blob: e7f8f6d6fb3c82a74ad3c1a0e37e0481bc111055 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.app;
18
Tor Norbye80756e32015-03-02 09:39:27 -080019import android.annotation.ColorInt;
Tor Norbye7b9c9122013-05-30 16:48:33 -070020import android.annotation.DrawableRes;
Tor Norbyed9273d62013-05-30 15:59:53 -070021import android.annotation.IntDef;
Daniel Sandler01df1c62014-06-09 10:54:01 -040022import android.annotation.SdkConstant;
23import android.annotation.SdkConstant.SdkConstantType;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080024import android.content.Context;
25import android.content.Intent;
Kenny Guy77320062014-08-27 21:37:15 +010026import android.content.pm.ApplicationInfo;
Christoph Studer4600f9b2014-07-22 22:44:43 +020027import android.content.pm.PackageManager.NameNotFoundException;
Jorim Jaggief72a192014-08-26 21:57:46 +020028import android.content.res.ColorStateList;
Joe Onoratoef1e7762010-09-17 18:38:38 -040029import android.graphics.Bitmap;
Kenny Guy8a0101b2014-05-08 23:34:12 +010030import android.graphics.Canvas;
Jorim Jaggi5c2d8462014-03-21 17:37:00 +010031import android.graphics.PorterDuff;
Kenny Guy8a0101b2014-05-08 23:34:12 +010032import android.graphics.drawable.Drawable;
John Spurlockc0650f022014-07-19 13:22:39 -040033import android.media.AudioAttributes;
Jeff Sharkey098d5802012-04-26 17:30:34 -070034import android.media.AudioManager;
Jeff Browndba34ba2014-06-24 20:46:03 -070035import android.media.session.MediaSession;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080036import android.net.Uri;
Daniel Sandlerdcbaf662013-04-26 16:23:09 -040037import android.os.BadParcelableException;
Christoph Studer239f8352014-08-25 15:13:18 +020038import android.os.Build;
Daniel Sandler2561b0b2012-02-13 21:04:12 -050039import android.os.Bundle;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080040import android.os.Parcel;
41import android.os.Parcelable;
Daniel Sandlera2985ed2012-04-03 16:42:00 -040042import android.os.SystemClock;
Jeff Sharkey6d515712012-09-20 16:06:08 -070043import android.os.UserHandle;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080044import android.text.TextUtils;
Daniel Sandlerdcbaf662013-04-26 16:23:09 -040045import android.util.Log;
Jorim Jaggi445d3c02014-08-19 22:33:42 +020046import android.util.MathUtils;
Daniel Sandler9f7936a2012-05-21 16:14:28 -040047import android.util.TypedValue;
Griff Hazen61a9e862014-05-22 16:05:19 -070048import android.view.Gravity;
Joe Onorato8595a3d2010-11-19 18:12:07 -080049import android.view.View;
Jeff Sharkey1c400132011-08-05 14:50:13 -070050import android.widget.ProgressBar;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080051import android.widget.RemoteViews;
52
Griff Hazen959591e2014-05-15 22:26:18 -070053import com.android.internal.R;
Griff Hazenc091ba82014-05-16 10:13:26 -070054import com.android.internal.util.NotificationColorUtil;
Griff Hazen959591e2014-05-15 22:26:18 -070055
Tor Norbyed9273d62013-05-30 15:59:53 -070056import java.lang.annotation.Retention;
57import java.lang.annotation.RetentionPolicy;
Christoph Studer4600f9b2014-07-22 22:44:43 +020058import java.lang.reflect.Constructor;
Andy Stadler110988c2010-12-03 14:29:16 -080059import java.text.NumberFormat;
Daniel Sandler2561b0b2012-02-13 21:04:12 -050060import java.util.ArrayList;
Griff Hazen61a9e862014-05-22 16:05:19 -070061import java.util.Arrays;
Griff Hazen5cadc3b2014-05-20 09:55:39 -070062import java.util.Collections;
Griff Hazen61a9e862014-05-22 16:05:19 -070063import java.util.List;
Joe Onorato561d3852010-11-20 18:09:34 -080064
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080065/**
66 * A class that represents how a persistent notification is to be presented to
67 * the user using the {@link android.app.NotificationManager}.
68 *
Joe Onoratocb109a02011-01-18 17:57:41 -080069 * <p>The {@link Notification.Builder Notification.Builder} has been added to make it
70 * easier to construct Notifications.</p>
71 *
Joe Fernandez558459f2011-10-13 16:47:36 -070072 * <div class="special reference">
73 * <h3>Developer Guides</h3>
74 * <p>For a guide to creating notifications, read the
75 * <a href="{@docRoot}guide/topics/ui/notifiers/notifications.html">Status Bar Notifications</a>
76 * developer guide.</p>
77 * </div>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080078 */
79public class Notification implements Parcelable
80{
Daniel Sandlerdcbaf662013-04-26 16:23:09 -040081 private static final String TAG = "Notification";
82
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080083 /**
Daniel Sandler01df1c62014-06-09 10:54:01 -040084 * An activity that provides a user interface for adjusting notification preferences for its
85 * containing application. Optional but recommended for apps that post
86 * {@link android.app.Notification Notifications}.
87 */
88 @SdkConstant(SdkConstantType.INTENT_CATEGORY)
89 public static final String INTENT_CATEGORY_NOTIFICATION_PREFERENCES
90 = "android.intent.category.NOTIFICATION_PREFERENCES";
91
92 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080093 * Use all default values (where applicable).
94 */
95 public static final int DEFAULT_ALL = ~0;
Daniel Sandler2561b0b2012-02-13 21:04:12 -050096
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080097 /**
98 * Use the default notification sound. This will ignore any given
99 * {@link #sound}.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500100 *
Chris Wren47c20a12014-06-18 17:27:29 -0400101 * <p>
102 * A notification that is noisy is more likely to be presented as a heads-up notification.
103 * </p>
104 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800105 * @see #defaults
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500106 */
107
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800108 public static final int DEFAULT_SOUND = 1;
109
110 /**
111 * Use the default notification vibrate. This will ignore any given
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500112 * {@link #vibrate}. Using phone vibration requires the
Scott Mainb8b36452009-04-26 15:50:49 -0700113 * {@link android.Manifest.permission#VIBRATE VIBRATE} permission.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500114 *
Chris Wren47c20a12014-06-18 17:27:29 -0400115 * <p>
116 * A notification that vibrates is more likely to be presented as a heads-up notification.
117 * </p>
118 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800119 * @see #defaults
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500120 */
121
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800122 public static final int DEFAULT_VIBRATE = 2;
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500123
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800124 /**
125 * Use the default notification lights. This will ignore the
126 * {@link #FLAG_SHOW_LIGHTS} bit, and {@link #ledARGB}, {@link #ledOffMS}, or
127 * {@link #ledOnMS}.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500128 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800129 * @see #defaults
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500130 */
131
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800132 public static final int DEFAULT_LIGHTS = 4;
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500133
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800134 /**
Christoph Studer535ec612014-09-03 15:47:47 +0200135 * Maximum length of CharSequences accepted by Builder and friends.
136 *
137 * <p>
138 * Avoids spamming the system with overly large strings such as full e-mails.
139 */
140 private static final int MAX_CHARSEQUENCE_LENGTH = 5 * 1024;
141
142 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500143 * A timestamp related to this notification, in milliseconds since the epoch.
Joe Malin8d40d042012-11-05 11:36:40 -0800144 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500145 * Default value: {@link System#currentTimeMillis() Now}.
146 *
147 * Choose a timestamp that will be most relevant to the user. For most finite events, this
148 * corresponds to the time the event happened (or will happen, in the case of events that have
149 * yet to occur but about which the user is being informed). Indefinite events should be
Joe Malin8d40d042012-11-05 11:36:40 -0800150 * timestamped according to when the activity began.
151 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500152 * Some examples:
Joe Malin8d40d042012-11-05 11:36:40 -0800153 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500154 * <ul>
155 * <li>Notification of a new chat message should be stamped when the message was received.</li>
156 * <li>Notification of an ongoing file download (with a progress bar, for example) should be stamped when the download started.</li>
157 * <li>Notification of a completed file download should be stamped when the download finished.</li>
158 * <li>Notification of an upcoming meeting should be stamped with the time the meeting will begin (that is, in the future).</li>
159 * <li>Notification of an ongoing stopwatch (increasing timer) should be stamped with the watch's start time.
160 * <li>Notification of an ongoing countdown timer should be stamped with the timer's end time.
Joe Malin8d40d042012-11-05 11:36:40 -0800161 * </ul>
162 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800163 */
164 public long when;
165
166 /**
167 * The resource id of a drawable to use as the icon in the status bar.
Daniel Sandlerd952dae2011-02-07 16:33:36 -0500168 * This is required; notifications with an invalid icon resource will not be shown.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800169 */
Tor Norbye7b9c9122013-05-30 16:48:33 -0700170 @DrawableRes
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800171 public int icon;
172
173 /**
Joe Onorato46439ce2010-11-19 13:56:21 -0800174 * If the icon in the status bar is to have more than one level, you can set this. Otherwise,
175 * leave it at its default value of 0.
176 *
177 * @see android.widget.ImageView#setImageLevel
Griff Hazen959591e2014-05-15 22:26:18 -0700178 * @see android.graphics.drawable.Drawable#setLevel
Joe Onorato46439ce2010-11-19 13:56:21 -0800179 */
180 public int iconLevel;
181
182 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500183 * The number of events that this notification represents. For example, in a new mail
184 * notification, this could be the number of unread messages.
Joe Malin8d40d042012-11-05 11:36:40 -0800185 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500186 * The system may or may not use this field to modify the appearance of the notification. For
187 * example, before {@link android.os.Build.VERSION_CODES#HONEYCOMB}, this number was
188 * superimposed over the icon in the status bar. Starting with
189 * {@link android.os.Build.VERSION_CODES#HONEYCOMB}, the template used by
190 * {@link Notification.Builder} has displayed the number in the expanded notification view.
Joe Malin8d40d042012-11-05 11:36:40 -0800191 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500192 * If the number is 0 or negative, it is never shown.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800193 */
194 public int number;
195
196 /**
197 * The intent to execute when the expanded status entry is clicked. If
198 * this is an activity, it must include the
199 * {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} flag, which requires
Scott Main7aee61f2011-02-08 11:25:01 -0800200 * that you take care of task management as described in the
201 * <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back
Dianne Hackborn6ceca582012-01-10 15:24:26 -0800202 * Stack</a> document. In particular, make sure to read the notification section
203 * <a href="{@docRoot}guide/topics/ui/notifiers/notifications.html#HandlingNotifications">Handling
204 * Notifications</a> for the correct ways to launch an application from a
205 * notification.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800206 */
207 public PendingIntent contentIntent;
208
209 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500210 * The intent to execute when the notification is explicitly dismissed by the user, either with
211 * the "Clear All" button or by swiping it away individually.
212 *
213 * This probably shouldn't be launching an activity since several of those will be sent
214 * at the same time.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800215 */
216 public PendingIntent deleteIntent;
217
218 /**
Dianne Hackborn170bae72010-09-03 15:14:28 -0700219 * An intent to launch instead of posting the notification to the status bar.
Joe Onoratocb109a02011-01-18 17:57:41 -0800220 *
Chris Wren47c20a12014-06-18 17:27:29 -0400221 * <p>
222 * The system UI may choose to display a heads-up notification, instead of
223 * launching this intent, while the user is using the device.
224 * </p>
225 *
Joe Onoratocb109a02011-01-18 17:57:41 -0800226 * @see Notification.Builder#setFullScreenIntent
Daniel Sandlere46cbd32010-06-17 10:35:26 -0400227 */
228 public PendingIntent fullScreenIntent;
229
230 /**
Dan Sandler5fcdf6e2014-07-18 11:31:15 -0400231 * Text that summarizes this notification for accessibility services.
232 *
233 * As of the L release, this text is no longer shown on screen, but it is still useful to
234 * accessibility services (where it serves as an audible announcement of the notification's
235 * appearance).
Joe Onoratoef1e7762010-09-17 18:38:38 -0400236 *
Joe Onorato46439ce2010-11-19 13:56:21 -0800237 * @see #tickerView
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800238 */
239 public CharSequence tickerText;
240
241 /**
Dan Sandler5fcdf6e2014-07-18 11:31:15 -0400242 * Formerly, a view showing the {@link #tickerText}.
243 *
244 * No longer displayed in the status bar as of API 21.
Joe Onoratoef1e7762010-09-17 18:38:38 -0400245 */
Dan Sandler5fcdf6e2014-07-18 11:31:15 -0400246 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -0800247 public RemoteViews tickerView;
Joe Onoratoef1e7762010-09-17 18:38:38 -0400248
249 /**
Daniel Sandlere46cbd32010-06-17 10:35:26 -0400250 * The view that will represent this notification in the expanded status bar.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800251 */
252 public RemoteViews contentView;
253
Daniel Sandlera0a938c2012-03-15 08:42:37 -0400254 /**
Daniel Sandler4dfbe832012-04-11 14:51:46 -0400255 * A large-format version of {@link #contentView}, giving the Notification an
Daniel Sandlerf3b73432012-03-27 15:01:25 -0400256 * opportunity to show more detail. The system UI may choose to show this
257 * instead of the normal content view at its discretion.
Daniel Sandlerf3b73432012-03-27 15:01:25 -0400258 */
259 public RemoteViews bigContentView;
260
Chris Wren8fd39ec2014-02-27 17:43:26 -0500261
262 /**
Chris Wren47c20a12014-06-18 17:27:29 -0400263 * A medium-format version of {@link #contentView}, providing the Notification an
264 * opportunity to add action buttons to contentView. At its discretion, the system UI may
265 * choose to show this as a heads-up notification, which will pop up so the user can see
266 * it without leaving their current activity.
Chris Wren8fd39ec2014-02-27 17:43:26 -0500267 */
268 public RemoteViews headsUpContentView;
269
Daniel Sandlerf3b73432012-03-27 15:01:25 -0400270 /**
Joe Onorato46439ce2010-11-19 13:56:21 -0800271 * The bitmap that may escape the bounds of the panel and bar.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800272 */
Joe Onorato46439ce2010-11-19 13:56:21 -0800273 public Bitmap largeIcon;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800274
275 /**
276 * The sound to play.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500277 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800278 * <p>
Chris Wren47c20a12014-06-18 17:27:29 -0400279 * A notification that is noisy is more likely to be presented as a heads-up notification.
280 * </p>
281 *
282 * <p>
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500283 * To play the default notification sound, see {@link #defaults}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800284 * </p>
285 */
286 public Uri sound;
287
288 /**
289 * Use this constant as the value for audioStreamType to request that
290 * the default stream type for notifications be used. Currently the
Jeff Sharkey098d5802012-04-26 17:30:34 -0700291 * default stream type is {@link AudioManager#STREAM_NOTIFICATION}.
John Spurlockc0650f022014-07-19 13:22:39 -0400292 *
293 * @deprecated Use {@link #audioAttributes} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800294 */
Jean-Michel Trivi81f871e2014-08-06 16:32:38 -0700295 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800296 public static final int STREAM_DEFAULT = -1;
297
298 /**
299 * The audio stream type to use when playing the sound.
300 * Should be one of the STREAM_ constants from
301 * {@link android.media.AudioManager}.
John Spurlockc0650f022014-07-19 13:22:39 -0400302 *
303 * @deprecated Use {@link #audioAttributes} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800304 */
Jean-Michel Trivi81f871e2014-08-06 16:32:38 -0700305 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800306 public int audioStreamType = STREAM_DEFAULT;
307
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800308 /**
John Spurlockc0650f022014-07-19 13:22:39 -0400309 * The default value of {@link #audioAttributes}.
310 */
311 public static final AudioAttributes AUDIO_ATTRIBUTES_DEFAULT = new AudioAttributes.Builder()
312 .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
313 .setUsage(AudioAttributes.USAGE_NOTIFICATION)
314 .build();
315
316 /**
317 * The {@link AudioAttributes audio attributes} to use when playing the sound.
318 */
319 public AudioAttributes audioAttributes = AUDIO_ATTRIBUTES_DEFAULT;
320
321 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500322 * The pattern with which to vibrate.
323 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800324 * <p>
325 * To vibrate the default pattern, see {@link #defaults}.
326 * </p>
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500327 *
Chris Wren47c20a12014-06-18 17:27:29 -0400328 * <p>
329 * A notification that vibrates is more likely to be presented as a heads-up notification.
330 * </p>
331 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800332 * @see android.os.Vibrator#vibrate(long[],int)
333 */
334 public long[] vibrate;
335
336 /**
337 * The color of the led. The hardware will do its best approximation.
338 *
339 * @see #FLAG_SHOW_LIGHTS
340 * @see #flags
341 */
Tor Norbye80756e32015-03-02 09:39:27 -0800342 @ColorInt
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800343 public int ledARGB;
344
345 /**
346 * The number of milliseconds for the LED to be on while it's flashing.
347 * The hardware will do its best approximation.
348 *
349 * @see #FLAG_SHOW_LIGHTS
350 * @see #flags
351 */
352 public int ledOnMS;
353
354 /**
355 * The number of milliseconds for the LED to be off while it's flashing.
356 * The hardware will do its best approximation.
357 *
358 * @see #FLAG_SHOW_LIGHTS
359 * @see #flags
360 */
361 public int ledOffMS;
362
363 /**
364 * Specifies which values should be taken from the defaults.
365 * <p>
366 * To set, OR the desired from {@link #DEFAULT_SOUND},
367 * {@link #DEFAULT_VIBRATE}, {@link #DEFAULT_LIGHTS}. For all default
368 * values, use {@link #DEFAULT_ALL}.
369 * </p>
370 */
371 public int defaults;
372
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800373 /**
374 * Bit to be bitwise-ored into the {@link #flags} field that should be
375 * set if you want the LED on for this notification.
376 * <ul>
377 * <li>To turn the LED off, pass 0 in the alpha channel for colorARGB
378 * or 0 for both ledOnMS and ledOffMS.</li>
379 * <li>To turn the LED on, pass 1 for ledOnMS and 0 for ledOffMS.</li>
380 * <li>To flash the LED, pass the number of milliseconds that it should
381 * be on and off to ledOnMS and ledOffMS.</li>
382 * </ul>
383 * <p>
384 * Since hardware varies, you are not guaranteed that any of the values
385 * you pass are honored exactly. Use the system defaults (TODO) if possible
386 * because they will be set to values that work on any given hardware.
387 * <p>
388 * The alpha channel must be set for forward compatibility.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500389 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800390 */
391 public static final int FLAG_SHOW_LIGHTS = 0x00000001;
392
393 /**
394 * Bit to be bitwise-ored into the {@link #flags} field that should be
395 * set if this notification is in reference to something that is ongoing,
396 * like a phone call. It should not be set if this notification is in
397 * reference to something that happened at a particular point in time,
398 * like a missed phone call.
399 */
400 public static final int FLAG_ONGOING_EVENT = 0x00000002;
401
402 /**
403 * Bit to be bitwise-ored into the {@link #flags} field that if set,
Scott Mainb8b36452009-04-26 15:50:49 -0700404 * the audio will be repeated until the notification is
405 * cancelled or the notification window is opened.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800406 */
407 public static final int FLAG_INSISTENT = 0x00000004;
408
409 /**
410 * Bit to be bitwise-ored into the {@link #flags} field that should be
Griff Hazen293977b2014-04-28 08:37:20 -0700411 * set if you would only like the sound, vibrate and ticker to be played
412 * if the notification was not already showing.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800413 */
414 public static final int FLAG_ONLY_ALERT_ONCE = 0x00000008;
415
416 /**
417 * Bit to be bitwise-ored into the {@link #flags} field that should be
418 * set if the notification should be canceled when it is clicked by the
Daniel Sandler8aa9ae62012-12-04 23:31:47 -0500419 * user.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800420 */
421 public static final int FLAG_AUTO_CANCEL = 0x00000010;
422
423 /**
424 * Bit to be bitwise-ored into the {@link #flags} field that should be
425 * set if the notification should not be canceled when the user clicks
426 * the Clear all button.
427 */
428 public static final int FLAG_NO_CLEAR = 0x00000020;
429
Dianne Hackbornd8a43f62009-08-17 23:33:56 -0700430 /**
431 * Bit to be bitwise-ored into the {@link #flags} field that should be
432 * set if this notification represents a currently running service. This
433 * will normally be set for you by {@link Service#startForeground}.
434 */
435 public static final int FLAG_FOREGROUND_SERVICE = 0x00000040;
436
Daniel Sandlere46cbd32010-06-17 10:35:26 -0400437 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500438 * Obsolete flag indicating high-priority notifications; use the priority field instead.
Joe Malin8d40d042012-11-05 11:36:40 -0800439 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500440 * @deprecated Use {@link #priority} with a positive value.
Daniel Sandlere46cbd32010-06-17 10:35:26 -0400441 */
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500442 public static final int FLAG_HIGH_PRIORITY = 0x00000080;
Daniel Sandlere46cbd32010-06-17 10:35:26 -0400443
Griff Hazendfcb0802014-02-11 12:00:00 -0800444 /**
445 * Bit to be bitswise-ored into the {@link #flags} field that should be
446 * set if this notification is relevant to the current device only
447 * and it is not recommended that it bridge to other devices.
448 */
449 public static final int FLAG_LOCAL_ONLY = 0x00000100;
450
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700451 /**
452 * Bit to be bitswise-ored into the {@link #flags} field that should be
453 * set if this notification is the group summary for a group of notifications.
454 * Grouped notifications may display in a cluster or stack on devices which
455 * support such rendering. Requires a group key also be set using {@link Builder#setGroup}.
456 */
457 public static final int FLAG_GROUP_SUMMARY = 0x00000200;
458
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800459 public int flags;
460
Tor Norbyed9273d62013-05-30 15:59:53 -0700461 /** @hide */
462 @IntDef({PRIORITY_DEFAULT,PRIORITY_LOW,PRIORITY_MIN,PRIORITY_HIGH,PRIORITY_MAX})
463 @Retention(RetentionPolicy.SOURCE)
464 public @interface Priority {}
465
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800466 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500467 * Default notification {@link #priority}. If your application does not prioritize its own
468 * notifications, use this value for all notifications.
469 */
470 public static final int PRIORITY_DEFAULT = 0;
471
472 /**
473 * Lower {@link #priority}, for items that are less important. The UI may choose to show these
474 * items smaller, or at a different position in the list, compared with your app's
475 * {@link #PRIORITY_DEFAULT} items.
476 */
477 public static final int PRIORITY_LOW = -1;
478
479 /**
480 * Lowest {@link #priority}; these items might not be shown to the user except under special
481 * circumstances, such as detailed notification logs.
482 */
483 public static final int PRIORITY_MIN = -2;
484
485 /**
486 * Higher {@link #priority}, for more important notifications or alerts. The UI may choose to
487 * show these items larger, or at a different position in notification lists, compared with
488 * your app's {@link #PRIORITY_DEFAULT} items.
489 */
490 public static final int PRIORITY_HIGH = 1;
491
492 /**
493 * Highest {@link #priority}, for your application's most important items that require the
494 * user's prompt attention or input.
495 */
496 public static final int PRIORITY_MAX = 2;
497
498 /**
499 * Relative priority for this notification.
Joe Malin8d40d042012-11-05 11:36:40 -0800500 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500501 * Priority is an indication of how much of the user's valuable attention should be consumed by
502 * this notification. Low-priority notifications may be hidden from the user in certain
503 * situations, while the user might be interrupted for a higher-priority notification. The
Daniel Sandler6738eee2012-11-16 12:03:32 -0500504 * system will make a determination about how to interpret this priority when presenting
505 * the notification.
Chris Wren47c20a12014-06-18 17:27:29 -0400506 *
507 * <p>
508 * A notification that is at least {@link #PRIORITY_HIGH} is more likely to be presented
509 * as a heads-up notification.
510 * </p>
511 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500512 */
Tor Norbyed9273d62013-05-30 15:59:53 -0700513 @Priority
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500514 public int priority;
Joe Malin8d40d042012-11-05 11:36:40 -0800515
Dan Sandler26e81cf2014-05-06 10:01:27 -0400516 /**
517 * Accent color (an ARGB integer like the constants in {@link android.graphics.Color})
518 * to be applied by the standard Style templates when presenting this notification.
519 *
520 * The current template design constructs a colorful header image by overlaying the
521 * {@link #icon} image (stenciled in white) atop a field of this color. Alpha components are
522 * ignored.
523 */
Tor Norbye80756e32015-03-02 09:39:27 -0800524 @ColorInt
Dan Sandler26e81cf2014-05-06 10:01:27 -0400525 public int color = COLOR_DEFAULT;
526
527 /**
528 * Special value of {@link #color} telling the system not to decorate this notification with
529 * any special color but instead use default colors when presenting this notification.
530 */
Tor Norbye80756e32015-03-02 09:39:27 -0800531 @ColorInt
Dan Sandler26e81cf2014-05-06 10:01:27 -0400532 public static final int COLOR_DEFAULT = 0; // AKA Color.TRANSPARENT
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600533
534 /**
535 * Sphere of visibility of this notification, which affects how and when the SystemUI reveals
536 * the notification's presence and contents in untrusted situations (namely, on the secure
537 * lockscreen).
538 *
539 * The default level, {@link #VISIBILITY_PRIVATE}, behaves exactly as notifications have always
540 * done on Android: The notification's {@link #icon} and {@link #tickerText} (if available) are
541 * shown in all situations, but the contents are only available if the device is unlocked for
542 * the appropriate user.
543 *
544 * A more permissive policy can be expressed by {@link #VISIBILITY_PUBLIC}; such a notification
545 * can be read even in an "insecure" context (that is, above a secure lockscreen).
546 * To modify the public version of this notification—for example, to redact some portions—see
547 * {@link Builder#setPublicVersion(Notification)}.
548 *
549 * Finally, a notification can be made {@link #VISIBILITY_SECRET}, which will suppress its icon
550 * and ticker until the user has bypassed the lockscreen.
551 */
552 public int visibility;
553
Griff Hazenfc3922d2014-08-20 11:56:44 -0700554 /**
555 * Notification visibility: Show this notification in its entirety on all lockscreens.
556 *
557 * {@see #visibility}
558 */
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600559 public static final int VISIBILITY_PUBLIC = 1;
Griff Hazenfc3922d2014-08-20 11:56:44 -0700560
561 /**
562 * Notification visibility: Show this notification on all lockscreens, but conceal sensitive or
563 * private information on secure lockscreens.
564 *
565 * {@see #visibility}
566 */
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600567 public static final int VISIBILITY_PRIVATE = 0;
Griff Hazenfc3922d2014-08-20 11:56:44 -0700568
569 /**
570 * Notification visibility: Do not reveal any part of this notification on a secure lockscreen.
571 *
572 * {@see #visibility}
573 */
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600574 public static final int VISIBILITY_SECRET = -1;
575
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500576 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400577 * Notification category: incoming call (voice or video) or similar synchronous communication request.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500578 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400579 public static final String CATEGORY_CALL = "call";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500580
581 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400582 * Notification category: incoming direct message (SMS, instant message, etc.).
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500583 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400584 public static final String CATEGORY_MESSAGE = "msg";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500585
586 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400587 * Notification category: asynchronous bulk message (email).
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500588 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400589 public static final String CATEGORY_EMAIL = "email";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500590
591 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400592 * Notification category: calendar event.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500593 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400594 public static final String CATEGORY_EVENT = "event";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500595
596 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400597 * Notification category: promotion or advertisement.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500598 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400599 public static final String CATEGORY_PROMO = "promo";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500600
601 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400602 * Notification category: alarm or timer.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500603 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400604 public static final String CATEGORY_ALARM = "alarm";
605
606 /**
607 * Notification category: progress of a long-running background operation.
608 */
609 public static final String CATEGORY_PROGRESS = "progress";
610
611 /**
612 * Notification category: social network or sharing update.
613 */
614 public static final String CATEGORY_SOCIAL = "social";
615
616 /**
617 * Notification category: error in background operation or authentication status.
618 */
619 public static final String CATEGORY_ERROR = "err";
620
621 /**
622 * Notification category: media transport control for playback.
623 */
624 public static final String CATEGORY_TRANSPORT = "transport";
625
626 /**
627 * Notification category: system or device status update. Reserved for system use.
628 */
629 public static final String CATEGORY_SYSTEM = "sys";
630
631 /**
632 * Notification category: indication of running background service.
633 */
634 public static final String CATEGORY_SERVICE = "service";
635
636 /**
John Spurlock0a69c8c2014-03-21 13:30:57 -0400637 * Notification category: a specific, timely recommendation for a single thing.
638 * For example, a news app might want to recommend a news story it believes the user will
639 * want to read next.
640 */
641 public static final String CATEGORY_RECOMMENDATION = "recommendation";
642
643 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400644 * Notification category: ongoing information about device or contextual status.
645 */
646 public static final String CATEGORY_STATUS = "status";
647
648 /**
John Spurlock24d3dad2015-04-02 12:24:02 -0400649 * Notification category: user-scheduled reminder.
650 */
651 public static final String CATEGORY_REMINDER = "reminder";
652
653 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400654 * One of the predefined notification categories (see the <code>CATEGORY_*</code> constants)
655 * that best describes this Notification. May be used by the system for ranking and filtering.
656 */
657 public String category;
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500658
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700659 private String mGroupKey;
660
661 /**
662 * Get the key used to group this notification into a cluster or stack
663 * with other notifications on devices which support such rendering.
664 */
665 public String getGroup() {
666 return mGroupKey;
667 }
668
669 private String mSortKey;
670
671 /**
672 * Get a sort key that orders this notification among other notifications from the
673 * same package. This can be useful if an external sort was already applied and an app
674 * would like to preserve this. Notifications will be sorted lexicographically using this
675 * value, although providing different priorities in addition to providing sort key may
676 * cause this value to be ignored.
677 *
678 * <p>This sort key can also be used to order members of a notification group. See
679 * {@link Builder#setGroup}.
680 *
681 * @see String#compareTo(String)
682 */
683 public String getSortKey() {
684 return mSortKey;
685 }
686
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500687 /**
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400688 * Additional semantic data to be carried around with this Notification.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400689 * <p>
690 * The extras keys defined here are intended to capture the original inputs to {@link Builder}
691 * APIs, and are intended to be used by
692 * {@link android.service.notification.NotificationListenerService} implementations to extract
693 * detailed information from notification objects.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500694 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400695 public Bundle extras = new Bundle();
696
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400697 /**
698 * {@link #extras} key: this is the title of the notification,
699 * as supplied to {@link Builder#setContentTitle(CharSequence)}.
700 */
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -0500701 public static final String EXTRA_TITLE = "android.title";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400702
703 /**
704 * {@link #extras} key: this is the title of the notification when shown in expanded form,
705 * e.g. as supplied to {@link BigTextStyle#setBigContentTitle(CharSequence)}.
706 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400707 public static final String EXTRA_TITLE_BIG = EXTRA_TITLE + ".big";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400708
709 /**
710 * {@link #extras} key: this is the main text payload, as supplied to
711 * {@link Builder#setContentText(CharSequence)}.
712 */
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -0500713 public static final String EXTRA_TEXT = "android.text";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400714
715 /**
716 * {@link #extras} key: this is a third line of text, as supplied to
717 * {@link Builder#setSubText(CharSequence)}.
718 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400719 public static final String EXTRA_SUB_TEXT = "android.subText";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400720
721 /**
722 * {@link #extras} key: this is a small piece of additional text as supplied to
723 * {@link Builder#setContentInfo(CharSequence)}.
724 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400725 public static final String EXTRA_INFO_TEXT = "android.infoText";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400726
727 /**
728 * {@link #extras} key: this is a line of summary information intended to be shown
729 * alongside expanded notifications, as supplied to (e.g.)
730 * {@link BigTextStyle#setSummaryText(CharSequence)}.
731 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400732 public static final String EXTRA_SUMMARY_TEXT = "android.summaryText";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400733
734 /**
Christoph Studer4600f9b2014-07-22 22:44:43 +0200735 * {@link #extras} key: this is the longer text shown in the big form of a
736 * {@link BigTextStyle} notification, as supplied to
737 * {@link BigTextStyle#bigText(CharSequence)}.
738 */
739 public static final String EXTRA_BIG_TEXT = "android.bigText";
740
741 /**
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400742 * {@link #extras} key: this is the resource ID of the notification's main small icon, as
743 * supplied to {@link Builder#setSmallIcon(int)}.
744 */
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -0500745 public static final String EXTRA_SMALL_ICON = "android.icon";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400746
747 /**
748 * {@link #extras} key: this is a bitmap to be used instead of the small icon when showing the
749 * notification payload, as
750 * supplied to {@link Builder#setLargeIcon(android.graphics.Bitmap)}.
751 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400752 public static final String EXTRA_LARGE_ICON = "android.largeIcon";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400753
754 /**
755 * {@link #extras} key: this is a bitmap to be used instead of the one from
756 * {@link Builder#setLargeIcon(android.graphics.Bitmap)} when the notification is
757 * shown in its expanded form, as supplied to
758 * {@link BigPictureStyle#bigLargeIcon(android.graphics.Bitmap)}.
759 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400760 public static final String EXTRA_LARGE_ICON_BIG = EXTRA_LARGE_ICON + ".big";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400761
762 /**
763 * {@link #extras} key: this is the progress value supplied to
764 * {@link Builder#setProgress(int, int, boolean)}.
765 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400766 public static final String EXTRA_PROGRESS = "android.progress";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400767
768 /**
769 * {@link #extras} key: this is the maximum value supplied to
770 * {@link Builder#setProgress(int, int, boolean)}.
771 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400772 public static final String EXTRA_PROGRESS_MAX = "android.progressMax";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400773
774 /**
775 * {@link #extras} key: whether the progress bar is indeterminate, supplied to
776 * {@link Builder#setProgress(int, int, boolean)}.
777 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400778 public static final String EXTRA_PROGRESS_INDETERMINATE = "android.progressIndeterminate";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400779
780 /**
781 * {@link #extras} key: whether {@link #when} should be shown as a count-up timer (specifically
782 * a {@link android.widget.Chronometer}) instead of a timestamp, as supplied to
783 * {@link Builder#setUsesChronometer(boolean)}.
784 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400785 public static final String EXTRA_SHOW_CHRONOMETER = "android.showChronometer";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400786
787 /**
788 * {@link #extras} key: whether {@link #when} should be shown,
789 * as supplied to {@link Builder#setShowWhen(boolean)}.
790 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400791 public static final String EXTRA_SHOW_WHEN = "android.showWhen";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400792
793 /**
794 * {@link #extras} key: this is a bitmap to be shown in {@link BigPictureStyle} expanded
795 * notifications, supplied to {@link BigPictureStyle#bigPicture(android.graphics.Bitmap)}.
796 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400797 public static final String EXTRA_PICTURE = "android.picture";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400798
799 /**
800 * {@link #extras} key: An array of CharSequences to show in {@link InboxStyle} expanded
801 * notifications, each of which was supplied to {@link InboxStyle#addLine(CharSequence)}.
802 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400803 public static final String EXTRA_TEXT_LINES = "android.textLines";
Dan Sandler842dd772014-05-15 09:36:47 -0400804
805 /**
806 * {@link #extras} key: A string representing the name of the specific
807 * {@link android.app.Notification.Style} used to create this notification.
808 */
Chris Wren91ad5632013-06-05 15:05:57 -0400809 public static final String EXTRA_TEMPLATE = "android.template";
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400810
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400811 /**
Chris Wrene6c48932014-09-29 17:19:27 -0400812 * {@link #extras} key: A String array containing the people that this notification relates to,
813 * each of which was supplied to {@link Builder#addPerson(String)}.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400814 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400815 public static final String EXTRA_PEOPLE = "android.people";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500816
817 /**
Chris Wren47c20a12014-06-18 17:27:29 -0400818 * {@link #extras} key: used to provide hints about the appropriateness of
819 * displaying this notification as a heads-up notification.
Chris Wren51c75102013-07-16 20:49:17 -0400820 * @hide
821 */
822 public static final String EXTRA_AS_HEADS_UP = "headsup";
823
824 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400825 * Allow certain system-generated notifications to appear before the device is provisioned.
826 * Only available to notifications coming from the android package.
827 * @hide
828 */
829 public static final String EXTRA_ALLOW_DURING_SETUP = "android.allowDuringSetup";
830
831 /**
Jose Limae9e3b3b2014-05-18 23:44:50 -0700832 * {@link #extras} key: A
833 * {@link android.content.ContentUris content URI} pointing to an image that can be displayed
834 * in the background when the notification is selected. The URI must point to an image stream
835 * suitable for passing into
836 * {@link android.graphics.BitmapFactory#decodeStream(java.io.InputStream)
837 * BitmapFactory.decodeStream}; all other content types will be ignored. The content provider
838 * URI used for this purpose must require no permissions to read the image data.
839 */
840 public static final String EXTRA_BACKGROUND_IMAGE_URI = "android.backgroundImageUri";
841
842 /**
Dan Sandler842dd772014-05-15 09:36:47 -0400843 * {@link #extras} key: A
Jeff Browndba34ba2014-06-24 20:46:03 -0700844 * {@link android.media.session.MediaSession.Token} associated with a
Dan Sandler842dd772014-05-15 09:36:47 -0400845 * {@link android.app.Notification.MediaStyle} notification.
846 */
847 public static final String EXTRA_MEDIA_SESSION = "android.mediaSession";
848
849 /**
Bryan Mawhinneye191f902014-07-22 12:50:09 +0100850 * {@link #extras} key: the indices of actions to be shown in the compact view,
851 * as supplied to (e.g.) {@link MediaStyle#setShowActionsInCompactView(int...)}.
852 */
853 public static final String EXTRA_COMPACT_ACTIONS = "android.compactActions";
854
Christoph Studer943aa672014-08-03 20:31:16 +0200855 /**
Kenny Guy8942bcd2014-09-08 21:09:47 +0100856 * {@link #extras} key: the user that built the notification.
857 *
858 * @hide
859 */
860 public static final String EXTRA_ORIGINATING_USERID = "android.originatingUserId";
861
862 /**
Chris Wren47c20a12014-06-18 17:27:29 -0400863 * Value for {@link #EXTRA_AS_HEADS_UP} that indicates this notification should not be
864 * displayed in the heads up space.
865 *
866 * <p>
867 * If this notification has a {@link #fullScreenIntent}, then it will always launch the
868 * full-screen intent when posted.
869 * </p>
Chris Wren51c75102013-07-16 20:49:17 -0400870 * @hide
871 */
872 public static final int HEADS_UP_NEVER = 0;
873
874 /**
Chris Wren47c20a12014-06-18 17:27:29 -0400875 * Default value for {@link #EXTRA_AS_HEADS_UP} that indicates this notification may be
876 * displayed as a heads up.
Chris Wren51c75102013-07-16 20:49:17 -0400877 * @hide
878 */
879 public static final int HEADS_UP_ALLOWED = 1;
880
881 /**
Chris Wren47c20a12014-06-18 17:27:29 -0400882 * Value for {@link #EXTRA_AS_HEADS_UP} that indicates this notification is a
883 * good candidate for display as a heads up.
Chris Wren51c75102013-07-16 20:49:17 -0400884 * @hide
885 */
886 public static final int HEADS_UP_REQUESTED = 2;
887
888 /**
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400889 * Structure to encapsulate a named action that can be shown as part of this notification.
890 * It must include an icon, a label, and a {@link PendingIntent} to be fired when the action is
891 * selected by the user.
892 * <p>
Griff Hazen959591e2014-05-15 22:26:18 -0700893 * Apps should use {@link Notification.Builder#addAction(int, CharSequence, PendingIntent)}
894 * or {@link Notification.Builder#addAction(Notification.Action)}
895 * to attach actions.
Daniel Sandlera0a938c2012-03-15 08:42:37 -0400896 */
Daniel Sandlerea2a3172013-02-20 22:24:20 -0500897 public static class Action implements Parcelable {
Griff Hazen959591e2014-05-15 22:26:18 -0700898 private final Bundle mExtras;
Griff Hazen61a9e862014-05-22 16:05:19 -0700899 private final RemoteInput[] mRemoteInputs;
Griff Hazen959591e2014-05-15 22:26:18 -0700900
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400901 /**
902 * Small icon representing the action.
903 */
Daniel Sandlera0a938c2012-03-15 08:42:37 -0400904 public int icon;
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700905
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400906 /**
907 * Title of the action.
908 */
Daniel Sandlera0a938c2012-03-15 08:42:37 -0400909 public CharSequence title;
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700910
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400911 /**
912 * Intent to send when the user invokes this action. May be null, in which case the action
913 * may be rendered in a disabled presentation by the system UI.
914 */
Daniel Sandlera0a938c2012-03-15 08:42:37 -0400915 public PendingIntent actionIntent;
Griff Hazen959591e2014-05-15 22:26:18 -0700916
Daniel Sandlera0a938c2012-03-15 08:42:37 -0400917 private Action(Parcel in) {
918 icon = in.readInt();
919 title = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
920 if (in.readInt() == 1) {
921 actionIntent = PendingIntent.CREATOR.createFromParcel(in);
922 }
Griff Hazen959591e2014-05-15 22:26:18 -0700923 mExtras = in.readBundle();
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700924 mRemoteInputs = in.createTypedArray(RemoteInput.CREATOR);
Daniel Sandlera0a938c2012-03-15 08:42:37 -0400925 }
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700926
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400927 /**
Griff Hazen959591e2014-05-15 22:26:18 -0700928 * Use {@link Notification.Builder#addAction(int, CharSequence, PendingIntent)}.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400929 */
930 public Action(int icon, CharSequence title, PendingIntent intent) {
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700931 this(icon, title, intent, new Bundle(), null);
Griff Hazen959591e2014-05-15 22:26:18 -0700932 }
933
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700934 private Action(int icon, CharSequence title, PendingIntent intent, Bundle extras,
935 RemoteInput[] remoteInputs) {
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400936 this.icon = icon;
937 this.title = title;
938 this.actionIntent = intent;
Griff Hazen959591e2014-05-15 22:26:18 -0700939 this.mExtras = extras != null ? extras : new Bundle();
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700940 this.mRemoteInputs = remoteInputs;
Griff Hazen959591e2014-05-15 22:26:18 -0700941 }
942
943 /**
944 * Get additional metadata carried around with this Action.
945 */
946 public Bundle getExtras() {
947 return mExtras;
948 }
949
950 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700951 * Get the list of inputs to be collected from the user when this action is sent.
952 * May return null if no remote inputs were added.
953 */
954 public RemoteInput[] getRemoteInputs() {
955 return mRemoteInputs;
956 }
957
958 /**
Griff Hazen959591e2014-05-15 22:26:18 -0700959 * Builder class for {@link Action} objects.
960 */
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700961 public static final class Builder {
Griff Hazen959591e2014-05-15 22:26:18 -0700962 private final int mIcon;
963 private final CharSequence mTitle;
964 private final PendingIntent mIntent;
965 private final Bundle mExtras;
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700966 private ArrayList<RemoteInput> mRemoteInputs;
Griff Hazen959591e2014-05-15 22:26:18 -0700967
968 /**
969 * Construct a new builder for {@link Action} object.
970 * @param icon icon to show for this action
971 * @param title the title of the action
972 * @param intent the {@link PendingIntent} to fire when users trigger this action
973 */
974 public Builder(int icon, CharSequence title, PendingIntent intent) {
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700975 this(icon, title, intent, new Bundle(), null);
Griff Hazen959591e2014-05-15 22:26:18 -0700976 }
977
978 /**
979 * Construct a new builder for {@link Action} object using the fields from an
980 * {@link Action}.
981 * @param action the action to read fields from.
982 */
983 public Builder(Action action) {
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700984 this(action.icon, action.title, action.actionIntent, new Bundle(action.mExtras),
985 action.getRemoteInputs());
Griff Hazen959591e2014-05-15 22:26:18 -0700986 }
987
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700988 private Builder(int icon, CharSequence title, PendingIntent intent, Bundle extras,
989 RemoteInput[] remoteInputs) {
Griff Hazen959591e2014-05-15 22:26:18 -0700990 mIcon = icon;
991 mTitle = title;
992 mIntent = intent;
993 mExtras = extras;
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700994 if (remoteInputs != null) {
995 mRemoteInputs = new ArrayList<RemoteInput>(remoteInputs.length);
996 Collections.addAll(mRemoteInputs, remoteInputs);
997 }
Griff Hazen959591e2014-05-15 22:26:18 -0700998 }
999
1000 /**
1001 * Merge additional metadata into this builder.
1002 *
1003 * <p>Values within the Bundle will replace existing extras values in this Builder.
1004 *
1005 * @see Notification.Action#extras
1006 */
1007 public Builder addExtras(Bundle extras) {
1008 if (extras != null) {
1009 mExtras.putAll(extras);
1010 }
1011 return this;
1012 }
1013
1014 /**
1015 * Get the metadata Bundle used by this Builder.
1016 *
1017 * <p>The returned Bundle is shared with this Builder.
1018 */
1019 public Bundle getExtras() {
1020 return mExtras;
1021 }
1022
1023 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001024 * Add an input to be collected from the user when this action is sent.
1025 * Response values can be retrieved from the fired intent by using the
1026 * {@link RemoteInput#getResultsFromIntent} function.
1027 * @param remoteInput a {@link RemoteInput} to add to the action
1028 * @return this object for method chaining
1029 */
1030 public Builder addRemoteInput(RemoteInput remoteInput) {
1031 if (mRemoteInputs == null) {
1032 mRemoteInputs = new ArrayList<RemoteInput>();
1033 }
1034 mRemoteInputs.add(remoteInput);
1035 return this;
1036 }
1037
1038 /**
1039 * Apply an extender to this action builder. Extenders may be used to add
1040 * metadata or change options on this builder.
1041 */
Griff Hazen61a9e862014-05-22 16:05:19 -07001042 public Builder extend(Extender extender) {
1043 extender.extend(this);
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001044 return this;
1045 }
1046
1047 /**
Griff Hazen959591e2014-05-15 22:26:18 -07001048 * Combine all of the options that have been set and return a new {@link Action}
1049 * object.
1050 * @return the built action
1051 */
1052 public Action build() {
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001053 RemoteInput[] remoteInputs = mRemoteInputs != null
1054 ? mRemoteInputs.toArray(new RemoteInput[mRemoteInputs.size()]) : null;
1055 return new Action(mIcon, mTitle, mIntent, mExtras, remoteInputs);
Griff Hazen959591e2014-05-15 22:26:18 -07001056 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001057 }
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001058
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001059 @Override
1060 public Action clone() {
1061 return new Action(
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001062 icon,
1063 title,
1064 actionIntent, // safe to alias
1065 new Bundle(mExtras),
1066 getRemoteInputs());
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001067 }
1068 @Override
1069 public int describeContents() {
1070 return 0;
1071 }
1072 @Override
1073 public void writeToParcel(Parcel out, int flags) {
1074 out.writeInt(icon);
1075 TextUtils.writeToParcel(title, out, flags);
1076 if (actionIntent != null) {
1077 out.writeInt(1);
1078 actionIntent.writeToParcel(out, flags);
1079 } else {
1080 out.writeInt(0);
1081 }
Griff Hazen959591e2014-05-15 22:26:18 -07001082 out.writeBundle(mExtras);
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001083 out.writeTypedArray(mRemoteInputs, flags);
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001084 }
Griff Hazen959591e2014-05-15 22:26:18 -07001085 public static final Parcelable.Creator<Action> CREATOR =
1086 new Parcelable.Creator<Action>() {
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001087 public Action createFromParcel(Parcel in) {
1088 return new Action(in);
1089 }
1090 public Action[] newArray(int size) {
1091 return new Action[size];
1092 }
1093 };
Griff Hazen61a9e862014-05-22 16:05:19 -07001094
1095 /**
1096 * Extender interface for use with {@link Builder#extend}. Extenders may be used to add
1097 * metadata or change options on an action builder.
1098 */
1099 public interface Extender {
1100 /**
1101 * Apply this extender to a notification action builder.
1102 * @param builder the builder to be modified.
1103 * @return the build object for chaining.
1104 */
1105 public Builder extend(Builder builder);
1106 }
1107
1108 /**
1109 * Wearable extender for notification actions. To add extensions to an action,
1110 * create a new {@link android.app.Notification.Action.WearableExtender} object using
1111 * the {@code WearableExtender()} constructor and apply it to a
1112 * {@link android.app.Notification.Action.Builder} using
1113 * {@link android.app.Notification.Action.Builder#extend}.
1114 *
1115 * <pre class="prettyprint">
1116 * Notification.Action action = new Notification.Action.Builder(
1117 * R.drawable.archive_all, "Archive all", actionIntent)
Griff Hazen14f57992014-05-26 09:07:14 -07001118 * .extend(new Notification.Action.WearableExtender()
Griff Hazen61a9e862014-05-22 16:05:19 -07001119 * .setAvailableOffline(false))
Griff Hazen14f57992014-05-26 09:07:14 -07001120 * .build();</pre>
Griff Hazen61a9e862014-05-22 16:05:19 -07001121 */
1122 public static final class WearableExtender implements Extender {
1123 /** Notification action extra which contains wearable extensions */
1124 private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS";
1125
Pete Gastaf6781d2014-10-07 15:17:05 -04001126 // Keys within EXTRA_WEARABLE_EXTENSIONS for wearable options.
Griff Hazen61a9e862014-05-22 16:05:19 -07001127 private static final String KEY_FLAGS = "flags";
Pete Gastaf6781d2014-10-07 15:17:05 -04001128 private static final String KEY_IN_PROGRESS_LABEL = "inProgressLabel";
1129 private static final String KEY_CONFIRM_LABEL = "confirmLabel";
1130 private static final String KEY_CANCEL_LABEL = "cancelLabel";
Griff Hazen61a9e862014-05-22 16:05:19 -07001131
1132 // Flags bitwise-ored to mFlags
1133 private static final int FLAG_AVAILABLE_OFFLINE = 0x1;
1134
1135 // Default value for flags integer
1136 private static final int DEFAULT_FLAGS = FLAG_AVAILABLE_OFFLINE;
1137
1138 private int mFlags = DEFAULT_FLAGS;
1139
Pete Gastaf6781d2014-10-07 15:17:05 -04001140 private CharSequence mInProgressLabel;
1141 private CharSequence mConfirmLabel;
1142 private CharSequence mCancelLabel;
1143
Griff Hazen61a9e862014-05-22 16:05:19 -07001144 /**
1145 * Create a {@link android.app.Notification.Action.WearableExtender} with default
1146 * options.
1147 */
1148 public WearableExtender() {
1149 }
1150
1151 /**
1152 * Create a {@link android.app.Notification.Action.WearableExtender} by reading
1153 * wearable options present in an existing notification action.
1154 * @param action the notification action to inspect.
1155 */
1156 public WearableExtender(Action action) {
1157 Bundle wearableBundle = action.getExtras().getBundle(EXTRA_WEARABLE_EXTENSIONS);
1158 if (wearableBundle != null) {
1159 mFlags = wearableBundle.getInt(KEY_FLAGS, DEFAULT_FLAGS);
Pete Gastaf6781d2014-10-07 15:17:05 -04001160 mInProgressLabel = wearableBundle.getCharSequence(KEY_IN_PROGRESS_LABEL);
1161 mConfirmLabel = wearableBundle.getCharSequence(KEY_CONFIRM_LABEL);
1162 mCancelLabel = wearableBundle.getCharSequence(KEY_CANCEL_LABEL);
Griff Hazen61a9e862014-05-22 16:05:19 -07001163 }
1164 }
1165
1166 /**
1167 * Apply wearable extensions to a notification action that is being built. This is
1168 * typically called by the {@link android.app.Notification.Action.Builder#extend}
1169 * method of {@link android.app.Notification.Action.Builder}.
1170 */
1171 @Override
1172 public Action.Builder extend(Action.Builder builder) {
1173 Bundle wearableBundle = new Bundle();
1174
1175 if (mFlags != DEFAULT_FLAGS) {
1176 wearableBundle.putInt(KEY_FLAGS, mFlags);
1177 }
Pete Gastaf6781d2014-10-07 15:17:05 -04001178 if (mInProgressLabel != null) {
1179 wearableBundle.putCharSequence(KEY_IN_PROGRESS_LABEL, mInProgressLabel);
1180 }
1181 if (mConfirmLabel != null) {
1182 wearableBundle.putCharSequence(KEY_CONFIRM_LABEL, mConfirmLabel);
1183 }
1184 if (mCancelLabel != null) {
1185 wearableBundle.putCharSequence(KEY_CANCEL_LABEL, mCancelLabel);
1186 }
Griff Hazen61a9e862014-05-22 16:05:19 -07001187
1188 builder.getExtras().putBundle(EXTRA_WEARABLE_EXTENSIONS, wearableBundle);
1189 return builder;
1190 }
1191
1192 @Override
1193 public WearableExtender clone() {
1194 WearableExtender that = new WearableExtender();
1195 that.mFlags = this.mFlags;
Pete Gastaf6781d2014-10-07 15:17:05 -04001196 that.mInProgressLabel = this.mInProgressLabel;
1197 that.mConfirmLabel = this.mConfirmLabel;
1198 that.mCancelLabel = this.mCancelLabel;
Griff Hazen61a9e862014-05-22 16:05:19 -07001199 return that;
1200 }
1201
1202 /**
1203 * Set whether this action is available when the wearable device is not connected to
1204 * a companion device. The user can still trigger this action when the wearable device is
1205 * offline, but a visual hint will indicate that the action may not be available.
1206 * Defaults to true.
1207 */
1208 public WearableExtender setAvailableOffline(boolean availableOffline) {
1209 setFlag(FLAG_AVAILABLE_OFFLINE, availableOffline);
1210 return this;
1211 }
1212
1213 /**
1214 * Get whether this action is available when the wearable device is not connected to
1215 * a companion device. The user can still trigger this action when the wearable device is
1216 * offline, but a visual hint will indicate that the action may not be available.
1217 * Defaults to true.
1218 */
1219 public boolean isAvailableOffline() {
1220 return (mFlags & FLAG_AVAILABLE_OFFLINE) != 0;
1221 }
1222
1223 private void setFlag(int mask, boolean value) {
1224 if (value) {
1225 mFlags |= mask;
1226 } else {
1227 mFlags &= ~mask;
1228 }
1229 }
Pete Gastaf6781d2014-10-07 15:17:05 -04001230
1231 /**
1232 * Set a label to display while the wearable is preparing to automatically execute the
1233 * action. This is usually a 'ing' verb ending in ellipsis like "Sending..."
1234 *
1235 * @param label the label to display while the action is being prepared to execute
1236 * @return this object for method chaining
1237 */
1238 public WearableExtender setInProgressLabel(CharSequence label) {
1239 mInProgressLabel = label;
1240 return this;
1241 }
1242
1243 /**
1244 * Get the label to display while the wearable is preparing to automatically execute
1245 * the action. This is usually a 'ing' verb ending in ellipsis like "Sending..."
1246 *
1247 * @return the label to display while the action is being prepared to execute
1248 */
1249 public CharSequence getInProgressLabel() {
1250 return mInProgressLabel;
1251 }
1252
1253 /**
1254 * Set a label to display to confirm that the action should be executed.
1255 * This is usually an imperative verb like "Send".
1256 *
1257 * @param label the label to confirm the action should be executed
1258 * @return this object for method chaining
1259 */
1260 public WearableExtender setConfirmLabel(CharSequence label) {
1261 mConfirmLabel = label;
1262 return this;
1263 }
1264
1265 /**
1266 * Get the label to display to confirm that the action should be executed.
1267 * This is usually an imperative verb like "Send".
1268 *
1269 * @return the label to confirm the action should be executed
1270 */
1271 public CharSequence getConfirmLabel() {
1272 return mConfirmLabel;
1273 }
1274
1275 /**
1276 * Set a label to display to cancel the action.
1277 * This is usually an imperative verb, like "Cancel".
1278 *
1279 * @param label the label to display to cancel the action
1280 * @return this object for method chaining
1281 */
1282 public WearableExtender setCancelLabel(CharSequence label) {
1283 mCancelLabel = label;
1284 return this;
1285 }
1286
1287 /**
1288 * Get the label to display to cancel the action.
1289 * This is usually an imperative verb like "Cancel".
1290 *
1291 * @return the label to display to cancel the action
1292 */
1293 public CharSequence getCancelLabel() {
1294 return mCancelLabel;
1295 }
Griff Hazen61a9e862014-05-22 16:05:19 -07001296 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001297 }
1298
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001299 /**
1300 * Array of all {@link Action} structures attached to this notification by
1301 * {@link Builder#addAction(int, CharSequence, PendingIntent)}. Mostly useful for instances of
1302 * {@link android.service.notification.NotificationListenerService} that provide an alternative
1303 * interface for invoking actions.
1304 */
Daniel Sandlerea2a3172013-02-20 22:24:20 -05001305 public Action[] actions;
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001306
1307 /**
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001308 * Replacement version of this notification whose content will be shown
1309 * in an insecure context such as atop a secure keyguard. See {@link #visibility}
1310 * and {@link #VISIBILITY_PUBLIC}.
1311 */
1312 public Notification publicVersion;
1313
1314 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001315 * Constructs a Notification object with default values.
Joe Onorato46439ce2010-11-19 13:56:21 -08001316 * You might want to consider using {@link Builder} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001317 */
1318 public Notification()
1319 {
1320 this.when = System.currentTimeMillis();
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001321 this.priority = PRIORITY_DEFAULT;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001322 }
1323
1324 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001325 * @hide
1326 */
1327 public Notification(Context context, int icon, CharSequence tickerText, long when,
1328 CharSequence contentTitle, CharSequence contentText, Intent contentIntent)
1329 {
1330 this.when = when;
1331 this.icon = icon;
1332 this.tickerText = tickerText;
1333 setLatestEventInfo(context, contentTitle, contentText,
1334 PendingIntent.getActivity(context, 0, contentIntent, 0));
1335 }
1336
1337 /**
1338 * Constructs a Notification object with the information needed to
1339 * have a status bar icon without the standard expanded view.
1340 *
1341 * @param icon The resource id of the icon to put in the status bar.
1342 * @param tickerText The text that flows by in the status bar when the notification first
1343 * activates.
1344 * @param when The time to show in the time field. In the System.currentTimeMillis
1345 * timebase.
Joe Onorato46439ce2010-11-19 13:56:21 -08001346 *
1347 * @deprecated Use {@link Builder} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001348 */
Joe Onorato46439ce2010-11-19 13:56:21 -08001349 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001350 public Notification(int icon, CharSequence tickerText, long when)
1351 {
1352 this.icon = icon;
1353 this.tickerText = tickerText;
1354 this.when = when;
1355 }
1356
1357 /**
1358 * Unflatten the notification from a parcel.
1359 */
1360 public Notification(Parcel parcel)
1361 {
1362 int version = parcel.readInt();
1363
1364 when = parcel.readLong();
1365 icon = parcel.readInt();
1366 number = parcel.readInt();
1367 if (parcel.readInt() != 0) {
1368 contentIntent = PendingIntent.CREATOR.createFromParcel(parcel);
1369 }
1370 if (parcel.readInt() != 0) {
1371 deleteIntent = PendingIntent.CREATOR.createFromParcel(parcel);
1372 }
1373 if (parcel.readInt() != 0) {
1374 tickerText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
1375 }
1376 if (parcel.readInt() != 0) {
Joe Onorato46439ce2010-11-19 13:56:21 -08001377 tickerView = RemoteViews.CREATOR.createFromParcel(parcel);
Joe Onoratoef1e7762010-09-17 18:38:38 -04001378 }
1379 if (parcel.readInt() != 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001380 contentView = RemoteViews.CREATOR.createFromParcel(parcel);
1381 }
Joe Onorato561d3852010-11-20 18:09:34 -08001382 if (parcel.readInt() != 0) {
1383 largeIcon = Bitmap.CREATOR.createFromParcel(parcel);
1384 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001385 defaults = parcel.readInt();
1386 flags = parcel.readInt();
1387 if (parcel.readInt() != 0) {
1388 sound = Uri.CREATOR.createFromParcel(parcel);
1389 }
1390
1391 audioStreamType = parcel.readInt();
John Spurlockc0650f022014-07-19 13:22:39 -04001392 if (parcel.readInt() != 0) {
1393 audioAttributes = AudioAttributes.CREATOR.createFromParcel(parcel);
1394 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001395 vibrate = parcel.createLongArray();
1396 ledARGB = parcel.readInt();
1397 ledOnMS = parcel.readInt();
1398 ledOffMS = parcel.readInt();
1399 iconLevel = parcel.readInt();
Daniel Sandlere46cbd32010-06-17 10:35:26 -04001400
1401 if (parcel.readInt() != 0) {
1402 fullScreenIntent = PendingIntent.CREATOR.createFromParcel(parcel);
1403 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001404
1405 priority = parcel.readInt();
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001406
John Spurlockfd7f1e02014-03-18 16:41:57 -04001407 category = parcel.readString();
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001408
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001409 mGroupKey = parcel.readString();
1410
1411 mSortKey = parcel.readString();
1412
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001413 extras = parcel.readBundle(); // may be null
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001414
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001415 actions = parcel.createTypedArray(Action.CREATOR); // may be null
1416
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001417 if (parcel.readInt() != 0) {
Daniel Sandlerf3b73432012-03-27 15:01:25 -04001418 bigContentView = RemoteViews.CREATOR.createFromParcel(parcel);
1419 }
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001420
Chris Wren8fd39ec2014-02-27 17:43:26 -05001421 if (parcel.readInt() != 0) {
1422 headsUpContentView = RemoteViews.CREATOR.createFromParcel(parcel);
1423 }
1424
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001425 visibility = parcel.readInt();
1426
1427 if (parcel.readInt() != 0) {
1428 publicVersion = Notification.CREATOR.createFromParcel(parcel);
1429 }
Dan Sandler26e81cf2014-05-06 10:01:27 -04001430
1431 color = parcel.readInt();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001432 }
1433
Andy Stadler110988c2010-12-03 14:29:16 -08001434 @Override
Joe Onorato18e69df2010-05-17 22:26:12 -07001435 public Notification clone() {
1436 Notification that = new Notification();
Daniel Sandler1a497d32013-04-18 14:52:45 -04001437 cloneInto(that, true);
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001438 return that;
1439 }
Joe Onorato18e69df2010-05-17 22:26:12 -07001440
Daniel Sandler1a497d32013-04-18 14:52:45 -04001441 /**
1442 * Copy all (or if heavy is false, all except Bitmaps and RemoteViews) members
1443 * of this into that.
1444 * @hide
1445 */
1446 public void cloneInto(Notification that, boolean heavy) {
Joe Onorato18e69df2010-05-17 22:26:12 -07001447 that.when = this.when;
1448 that.icon = this.icon;
1449 that.number = this.number;
1450
1451 // PendingIntents are global, so there's no reason (or way) to clone them.
1452 that.contentIntent = this.contentIntent;
1453 that.deleteIntent = this.deleteIntent;
Daniel Sandlere46cbd32010-06-17 10:35:26 -04001454 that.fullScreenIntent = this.fullScreenIntent;
Joe Onorato18e69df2010-05-17 22:26:12 -07001455
1456 if (this.tickerText != null) {
1457 that.tickerText = this.tickerText.toString();
1458 }
Daniel Sandler1a497d32013-04-18 14:52:45 -04001459 if (heavy && this.tickerView != null) {
Joe Onorato46439ce2010-11-19 13:56:21 -08001460 that.tickerView = this.tickerView.clone();
Joe Onoratoef1e7762010-09-17 18:38:38 -04001461 }
Daniel Sandler1a497d32013-04-18 14:52:45 -04001462 if (heavy && this.contentView != null) {
Joe Onorato18e69df2010-05-17 22:26:12 -07001463 that.contentView = this.contentView.clone();
1464 }
Daniel Sandler1a497d32013-04-18 14:52:45 -04001465 if (heavy && this.largeIcon != null) {
Joe Onorato561d3852010-11-20 18:09:34 -08001466 that.largeIcon = Bitmap.createBitmap(this.largeIcon);
1467 }
Jozef BABJAKa8b91832011-02-22 08:05:08 +01001468 that.iconLevel = this.iconLevel;
Joe Onorato18e69df2010-05-17 22:26:12 -07001469 that.sound = this.sound; // android.net.Uri is immutable
1470 that.audioStreamType = this.audioStreamType;
John Spurlockc0650f022014-07-19 13:22:39 -04001471 if (this.audioAttributes != null) {
1472 that.audioAttributes = new AudioAttributes.Builder(this.audioAttributes).build();
1473 }
Joe Onorato18e69df2010-05-17 22:26:12 -07001474
1475 final long[] vibrate = this.vibrate;
1476 if (vibrate != null) {
1477 final int N = vibrate.length;
1478 final long[] vib = that.vibrate = new long[N];
1479 System.arraycopy(vibrate, 0, vib, 0, N);
1480 }
1481
1482 that.ledARGB = this.ledARGB;
1483 that.ledOnMS = this.ledOnMS;
1484 that.ledOffMS = this.ledOffMS;
1485 that.defaults = this.defaults;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001486
Joe Onorato18e69df2010-05-17 22:26:12 -07001487 that.flags = this.flags;
1488
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001489 that.priority = this.priority;
Joe Malin8d40d042012-11-05 11:36:40 -08001490
John Spurlockfd7f1e02014-03-18 16:41:57 -04001491 that.category = this.category;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001492
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001493 that.mGroupKey = this.mGroupKey;
1494
1495 that.mSortKey = this.mSortKey;
1496
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001497 if (this.extras != null) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04001498 try {
1499 that.extras = new Bundle(this.extras);
1500 // will unparcel
1501 that.extras.size();
1502 } catch (BadParcelableException e) {
1503 Log.e(TAG, "could not unparcel extras from notification: " + this, e);
1504 that.extras = null;
1505 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001506 }
1507
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001508 if (this.actions != null) {
1509 that.actions = new Action[this.actions.length];
1510 for(int i=0; i<this.actions.length; i++) {
1511 that.actions[i] = this.actions[i].clone();
1512 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001513 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001514
Daniel Sandler1a497d32013-04-18 14:52:45 -04001515 if (heavy && this.bigContentView != null) {
Daniel Sandlerf3b73432012-03-27 15:01:25 -04001516 that.bigContentView = this.bigContentView.clone();
1517 }
Daniel Sandler1a497d32013-04-18 14:52:45 -04001518
Chris Wren8fd39ec2014-02-27 17:43:26 -05001519 if (heavy && this.headsUpContentView != null) {
1520 that.headsUpContentView = this.headsUpContentView.clone();
1521 }
1522
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001523 that.visibility = this.visibility;
1524
1525 if (this.publicVersion != null) {
1526 that.publicVersion = new Notification();
1527 this.publicVersion.cloneInto(that.publicVersion, heavy);
1528 }
1529
Dan Sandler26e81cf2014-05-06 10:01:27 -04001530 that.color = this.color;
1531
Daniel Sandler1a497d32013-04-18 14:52:45 -04001532 if (!heavy) {
1533 that.lightenPayload(); // will clean out extras
1534 }
1535 }
1536
1537 /**
1538 * Removes heavyweight parts of the Notification object for archival or for sending to
1539 * listeners when the full contents are not necessary.
1540 * @hide
1541 */
1542 public final void lightenPayload() {
1543 tickerView = null;
1544 contentView = null;
1545 bigContentView = null;
Chris Wren8fd39ec2014-02-27 17:43:26 -05001546 headsUpContentView = null;
Daniel Sandler1a497d32013-04-18 14:52:45 -04001547 largeIcon = null;
1548 if (extras != null) {
1549 extras.remove(Notification.EXTRA_LARGE_ICON);
1550 extras.remove(Notification.EXTRA_LARGE_ICON_BIG);
1551 extras.remove(Notification.EXTRA_PICTURE);
Christoph Studer223f44e2014-09-02 14:59:32 +02001552 extras.remove(Notification.EXTRA_BIG_TEXT);
Christoph Studerb82bc782014-08-20 14:29:43 +02001553 // Prevent light notifications from being rebuilt.
1554 extras.remove(Builder.EXTRA_NEEDS_REBUILD);
Daniel Sandler1a497d32013-04-18 14:52:45 -04001555 }
Joe Onorato18e69df2010-05-17 22:26:12 -07001556 }
1557
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04001558 /**
1559 * Make sure this CharSequence is safe to put into a bundle, which basically
1560 * means it had better not be some custom Parcelable implementation.
1561 * @hide
1562 */
1563 public static CharSequence safeCharSequence(CharSequence cs) {
Christoph Studer535ec612014-09-03 15:47:47 +02001564 if (cs == null) return cs;
1565 if (cs.length() > MAX_CHARSEQUENCE_LENGTH) {
1566 cs = cs.subSequence(0, MAX_CHARSEQUENCE_LENGTH);
1567 }
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04001568 if (cs instanceof Parcelable) {
1569 Log.e(TAG, "warning: " + cs.getClass().getCanonicalName()
1570 + " instance is a custom Parcelable and not allowed in Notification");
1571 return cs.toString();
1572 }
1573
1574 return cs;
1575 }
1576
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001577 public int describeContents() {
1578 return 0;
1579 }
1580
1581 /**
1582 * Flatten this notification from a parcel.
1583 */
1584 public void writeToParcel(Parcel parcel, int flags)
1585 {
1586 parcel.writeInt(1);
1587
1588 parcel.writeLong(when);
1589 parcel.writeInt(icon);
1590 parcel.writeInt(number);
1591 if (contentIntent != null) {
1592 parcel.writeInt(1);
1593 contentIntent.writeToParcel(parcel, 0);
1594 } else {
1595 parcel.writeInt(0);
1596 }
1597 if (deleteIntent != null) {
1598 parcel.writeInt(1);
1599 deleteIntent.writeToParcel(parcel, 0);
1600 } else {
1601 parcel.writeInt(0);
1602 }
1603 if (tickerText != null) {
1604 parcel.writeInt(1);
1605 TextUtils.writeToParcel(tickerText, parcel, flags);
1606 } else {
1607 parcel.writeInt(0);
1608 }
Joe Onorato46439ce2010-11-19 13:56:21 -08001609 if (tickerView != null) {
Joe Onoratoef1e7762010-09-17 18:38:38 -04001610 parcel.writeInt(1);
Joe Onorato46439ce2010-11-19 13:56:21 -08001611 tickerView.writeToParcel(parcel, 0);
Joe Onoratoef1e7762010-09-17 18:38:38 -04001612 } else {
1613 parcel.writeInt(0);
1614 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001615 if (contentView != null) {
1616 parcel.writeInt(1);
1617 contentView.writeToParcel(parcel, 0);
1618 } else {
1619 parcel.writeInt(0);
1620 }
Joe Onorato561d3852010-11-20 18:09:34 -08001621 if (largeIcon != null) {
1622 parcel.writeInt(1);
1623 largeIcon.writeToParcel(parcel, 0);
1624 } else {
1625 parcel.writeInt(0);
1626 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001627
1628 parcel.writeInt(defaults);
1629 parcel.writeInt(this.flags);
1630
1631 if (sound != null) {
1632 parcel.writeInt(1);
1633 sound.writeToParcel(parcel, 0);
1634 } else {
1635 parcel.writeInt(0);
1636 }
1637 parcel.writeInt(audioStreamType);
John Spurlockc0650f022014-07-19 13:22:39 -04001638
1639 if (audioAttributes != null) {
1640 parcel.writeInt(1);
1641 audioAttributes.writeToParcel(parcel, 0);
1642 } else {
1643 parcel.writeInt(0);
1644 }
1645
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001646 parcel.writeLongArray(vibrate);
1647 parcel.writeInt(ledARGB);
1648 parcel.writeInt(ledOnMS);
1649 parcel.writeInt(ledOffMS);
1650 parcel.writeInt(iconLevel);
Daniel Sandlere46cbd32010-06-17 10:35:26 -04001651
1652 if (fullScreenIntent != null) {
1653 parcel.writeInt(1);
1654 fullScreenIntent.writeToParcel(parcel, 0);
1655 } else {
1656 parcel.writeInt(0);
1657 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001658
1659 parcel.writeInt(priority);
Joe Malin8d40d042012-11-05 11:36:40 -08001660
John Spurlockfd7f1e02014-03-18 16:41:57 -04001661 parcel.writeString(category);
Joe Malin8d40d042012-11-05 11:36:40 -08001662
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001663 parcel.writeString(mGroupKey);
1664
1665 parcel.writeString(mSortKey);
1666
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001667 parcel.writeBundle(extras); // null ok
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001668
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001669 parcel.writeTypedArray(actions, 0); // null ok
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001670
Daniel Sandlerf3b73432012-03-27 15:01:25 -04001671 if (bigContentView != null) {
1672 parcel.writeInt(1);
1673 bigContentView.writeToParcel(parcel, 0);
1674 } else {
1675 parcel.writeInt(0);
1676 }
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001677
Chris Wren8fd39ec2014-02-27 17:43:26 -05001678 if (headsUpContentView != null) {
1679 parcel.writeInt(1);
1680 headsUpContentView.writeToParcel(parcel, 0);
1681 } else {
1682 parcel.writeInt(0);
1683 }
1684
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001685 parcel.writeInt(visibility);
1686
1687 if (publicVersion != null) {
1688 parcel.writeInt(1);
1689 publicVersion.writeToParcel(parcel, 0);
1690 } else {
1691 parcel.writeInt(0);
1692 }
Dan Sandler26e81cf2014-05-06 10:01:27 -04001693
1694 parcel.writeInt(color);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001695 }
1696
1697 /**
1698 * Parcelable.Creator that instantiates Notification objects
1699 */
1700 public static final Parcelable.Creator<Notification> CREATOR
1701 = new Parcelable.Creator<Notification>()
1702 {
1703 public Notification createFromParcel(Parcel parcel)
1704 {
1705 return new Notification(parcel);
1706 }
1707
1708 public Notification[] newArray(int size)
1709 {
1710 return new Notification[size];
1711 }
1712 };
1713
1714 /**
1715 * Sets the {@link #contentView} field to be a view with the standard "Latest Event"
1716 * layout.
1717 *
1718 * <p>Uses the {@link #icon} and {@link #when} fields to set the icon and time fields
1719 * in the view.</p>
1720 * @param context The context for your application / activity.
1721 * @param contentTitle The title that goes in the expanded entry.
1722 * @param contentText The text that goes in the expanded entry.
1723 * @param contentIntent The intent to launch when the user clicks the expanded notification.
1724 * If this is an activity, it must include the
1725 * {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} flag, which requires
Scott Main7aee61f2011-02-08 11:25:01 -08001726 * that you take care of task management as described in the
1727 * <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back
1728 * Stack</a> document.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001729 *
Joe Onorato46439ce2010-11-19 13:56:21 -08001730 * @deprecated Use {@link Builder} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001731 */
Joe Onorato46439ce2010-11-19 13:56:21 -08001732 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001733 public void setLatestEventInfo(Context context,
1734 CharSequence contentTitle, CharSequence contentText, PendingIntent contentIntent) {
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001735 Notification.Builder builder = new Notification.Builder(context);
1736
1737 // First, ensure that key pieces of information that may have been set directly
1738 // are preserved
1739 builder.setWhen(this.when);
1740 builder.setSmallIcon(this.icon);
1741 builder.setPriority(this.priority);
1742 builder.setTicker(this.tickerText);
1743 builder.setNumber(this.number);
Selim Cinek255dd042014-08-19 22:29:02 +02001744 builder.setColor(this.color);
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001745 builder.mFlags = this.flags;
1746 builder.setSound(this.sound, this.audioStreamType);
1747 builder.setDefaults(this.defaults);
1748 builder.setVibrate(this.vibrate);
Sean Barbeau6ae0b1b2014-07-19 21:31:41 -07001749 builder.setDeleteIntent(this.deleteIntent);
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001750
1751 // now apply the latestEventInfo fields
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001752 if (contentTitle != null) {
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001753 builder.setContentTitle(contentTitle);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001754 }
1755 if (contentText != null) {
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001756 builder.setContentText(contentText);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001757 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001758 builder.setContentIntent(contentIntent);
1759 builder.buildInto(this);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001760 }
1761
1762 @Override
1763 public String toString() {
1764 StringBuilder sb = new StringBuilder();
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001765 sb.append("Notification(pri=");
1766 sb.append(priority);
1767 sb.append(" contentView=");
Joe Onoratoc9596d62011-01-12 17:03:11 -08001768 if (contentView != null) {
1769 sb.append(contentView.getPackage());
1770 sb.append("/0x");
1771 sb.append(Integer.toHexString(contentView.getLayoutId()));
1772 } else {
1773 sb.append("null");
1774 }
1775 sb.append(" vibrate=");
Daniel Sandler6738eee2012-11-16 12:03:32 -05001776 if ((this.defaults & DEFAULT_VIBRATE) != 0) {
1777 sb.append("default");
1778 } else if (this.vibrate != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001779 int N = this.vibrate.length-1;
1780 sb.append("[");
1781 for (int i=0; i<N; i++) {
1782 sb.append(this.vibrate[i]);
1783 sb.append(',');
1784 }
Simon Schoar8cf97d92009-06-10 22:08:37 +02001785 if (N != -1) {
1786 sb.append(this.vibrate[N]);
1787 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001788 sb.append("]");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001789 } else {
1790 sb.append("null");
1791 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001792 sb.append(" sound=");
Daniel Sandler6738eee2012-11-16 12:03:32 -05001793 if ((this.defaults & DEFAULT_SOUND) != 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001794 sb.append("default");
Daniel Sandler6738eee2012-11-16 12:03:32 -05001795 } else if (this.sound != null) {
1796 sb.append(this.sound.toString());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001797 } else {
1798 sb.append("null");
1799 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001800 sb.append(" defaults=0x");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001801 sb.append(Integer.toHexString(this.defaults));
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001802 sb.append(" flags=0x");
Daniel Sandlere46cbd32010-06-17 10:35:26 -04001803 sb.append(Integer.toHexString(this.flags));
Dan Sandler26e81cf2014-05-06 10:01:27 -04001804 sb.append(String.format(" color=0x%08x", this.color));
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001805 if (this.category != null) {
1806 sb.append(" category=");
1807 sb.append(this.category);
1808 }
1809 if (this.mGroupKey != null) {
1810 sb.append(" groupKey=");
1811 sb.append(this.mGroupKey);
1812 }
1813 if (this.mSortKey != null) {
1814 sb.append(" sortKey=");
1815 sb.append(this.mSortKey);
1816 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001817 if (actions != null) {
Dan Sandler1b718782014-07-18 12:43:45 -04001818 sb.append(" actions=");
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001819 sb.append(actions.length);
Dan Sandler1b718782014-07-18 12:43:45 -04001820 }
1821 sb.append(" vis=");
1822 sb.append(visibilityToString(this.visibility));
1823 if (this.publicVersion != null) {
1824 sb.append(" publicVersion=");
1825 sb.append(publicVersion.toString());
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001826 }
1827 sb.append(")");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001828 return sb.toString();
1829 }
Joe Onorato46439ce2010-11-19 13:56:21 -08001830
Dan Sandler1b718782014-07-18 12:43:45 -04001831 /**
1832 * {@hide}
1833 */
1834 public static String visibilityToString(int vis) {
1835 switch (vis) {
1836 case VISIBILITY_PRIVATE:
1837 return "PRIVATE";
1838 case VISIBILITY_PUBLIC:
1839 return "PUBLIC";
1840 case VISIBILITY_SECRET:
1841 return "SECRET";
1842 default:
1843 return "UNKNOWN(" + String.valueOf(vis) + ")";
1844 }
1845 }
1846
Joe Onoratocb109a02011-01-18 17:57:41 -08001847 /**
John Spurlock1d881a12015-03-18 19:21:54 -04001848 * {@hide}
1849 */
1850 public static String priorityToString(@Priority int pri) {
1851 switch (pri) {
1852 case PRIORITY_MIN:
1853 return "MIN";
1854 case PRIORITY_LOW:
1855 return "LOW";
1856 case PRIORITY_DEFAULT:
1857 return "DEFAULT";
1858 case PRIORITY_HIGH:
1859 return "HIGH";
1860 case PRIORITY_MAX:
1861 return "MAX";
1862 default:
1863 return "UNKNOWN(" + String.valueOf(pri) + ")";
1864 }
1865 }
1866
1867 /**
Christoph Studer4600f9b2014-07-22 22:44:43 +02001868 * @hide
1869 */
1870 public boolean isValid() {
1871 // Would like to check for icon!=0 here, too, but NotificationManagerService accepts that
1872 // for legacy reasons.
1873 return contentView != null || extras.getBoolean(Builder.EXTRA_REBUILD_CONTENT_VIEW);
1874 }
1875
1876 /**
Christoph Studerc8db24b2014-07-25 17:50:30 +02001877 * @hide
1878 */
1879 public boolean isGroupSummary() {
1880 return mGroupKey != null && (flags & FLAG_GROUP_SUMMARY) != 0;
1881 }
1882
1883 /**
1884 * @hide
1885 */
1886 public boolean isGroupChild() {
1887 return mGroupKey != null && (flags & FLAG_GROUP_SUMMARY) == 0;
1888 }
1889
1890 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001891 * Builder class for {@link Notification} objects.
Joe Malin8d40d042012-11-05 11:36:40 -08001892 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001893 * Provides a convenient way to set the various fields of a {@link Notification} and generate
Scott Main183bf112012-08-13 19:12:13 -07001894 * content views using the platform's notification layout template. If your app supports
1895 * versions of Android as old as API level 4, you can instead use
1896 * {@link android.support.v4.app.NotificationCompat.Builder NotificationCompat.Builder},
1897 * available in the <a href="{@docRoot}tools/extras/support-library.html">Android Support
1898 * library</a>.
Joe Malin8d40d042012-11-05 11:36:40 -08001899 *
Scott Main183bf112012-08-13 19:12:13 -07001900 * <p>Example:
Joe Malin8d40d042012-11-05 11:36:40 -08001901 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001902 * <pre class="prettyprint">
Scott Main183bf112012-08-13 19:12:13 -07001903 * Notification noti = new Notification.Builder(mContext)
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001904 * .setContentTitle(&quot;New mail from &quot; + sender.toString())
1905 * .setContentText(subject)
1906 * .setSmallIcon(R.drawable.new_mail)
1907 * .setLargeIcon(aBitmap)
Chris Wrenfbd96ba2012-05-01 12:03:58 -04001908 * .build();
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001909 * </pre>
Joe Onoratocb109a02011-01-18 17:57:41 -08001910 */
Joe Onorato46439ce2010-11-19 13:56:21 -08001911 public static class Builder {
Daniel Sandler602ad1c2012-06-12 16:06:27 -04001912 private static final int MAX_ACTION_BUTTONS = 3;
Jorim Jaggi445d3c02014-08-19 22:33:42 +02001913 private static final float LARGE_TEXT_SCALE = 1.3f;
Daniel Sandler8680bf82012-05-15 16:52:52 -04001914
Christoph Studer4600f9b2014-07-22 22:44:43 +02001915 /**
1916 * @hide
1917 */
1918 public static final String EXTRA_NEEDS_REBUILD = "android.rebuild";
1919
1920 /**
1921 * @hide
1922 */
1923 public static final String EXTRA_REBUILD_LARGE_ICON = "android.rebuild.largeIcon";
1924 /**
1925 * @hide
1926 */
1927 public static final String EXTRA_REBUILD_CONTENT_VIEW = "android.rebuild.contentView";
1928 /**
1929 * @hide
1930 */
1931 public static final String EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT =
1932 "android.rebuild.contentViewActionCount";
1933 /**
1934 * @hide
1935 */
1936 public static final String EXTRA_REBUILD_BIG_CONTENT_VIEW
1937 = "android.rebuild.bigView";
1938 /**
1939 * @hide
1940 */
1941 public static final String EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT
1942 = "android.rebuild.bigViewActionCount";
1943 /**
1944 * @hide
1945 */
1946 public static final String EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW
1947 = "android.rebuild.hudView";
1948 /**
1949 * @hide
1950 */
1951 public static final String EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT
1952 = "android.rebuild.hudViewActionCount";
1953
1954 /**
Kenny Guy77320062014-08-27 21:37:15 +01001955 * The ApplicationInfo of the package that created the notification, used to create
1956 * a context to rebuild the notification via a Builder.
1957 * @hide
Christoph Studer4600f9b2014-07-22 22:44:43 +02001958 */
Kenny Guy77320062014-08-27 21:37:15 +01001959 private static final String EXTRA_REBUILD_CONTEXT_APPLICATION_INFO =
1960 "android.rebuild.applicationInfo";
Christoph Studer4600f9b2014-07-22 22:44:43 +02001961
1962 // Whether to enable stripping (at post time) & rebuilding (at listener receive time) of
1963 // memory intensive resources.
1964 private static final boolean STRIP_AND_REBUILD = true;
1965
Joe Onorato46439ce2010-11-19 13:56:21 -08001966 private Context mContext;
1967
1968 private long mWhen;
1969 private int mSmallIcon;
1970 private int mSmallIconLevel;
Joe Onorato8595a3d2010-11-19 18:12:07 -08001971 private int mNumber;
Joe Onorato46439ce2010-11-19 13:56:21 -08001972 private CharSequence mContentTitle;
1973 private CharSequence mContentText;
1974 private CharSequence mContentInfo;
Daniel Sandlerf3b73432012-03-27 15:01:25 -04001975 private CharSequence mSubText;
Joe Onorato46439ce2010-11-19 13:56:21 -08001976 private PendingIntent mContentIntent;
1977 private RemoteViews mContentView;
1978 private PendingIntent mDeleteIntent;
1979 private PendingIntent mFullScreenIntent;
1980 private CharSequence mTickerText;
1981 private RemoteViews mTickerView;
1982 private Bitmap mLargeIcon;
1983 private Uri mSound;
1984 private int mAudioStreamType;
John Spurlockc0650f022014-07-19 13:22:39 -04001985 private AudioAttributes mAudioAttributes;
Joe Onorato46439ce2010-11-19 13:56:21 -08001986 private long[] mVibrate;
1987 private int mLedArgb;
1988 private int mLedOnMs;
1989 private int mLedOffMs;
1990 private int mDefaults;
1991 private int mFlags;
Jeff Sharkey1c400132011-08-05 14:50:13 -07001992 private int mProgressMax;
1993 private int mProgress;
1994 private boolean mProgressIndeterminate;
John Spurlockfd7f1e02014-03-18 16:41:57 -04001995 private String mCategory;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001996 private String mGroupKey;
1997 private String mSortKey;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001998 private Bundle mExtras;
1999 private int mPriority;
Daniel Sandler8680bf82012-05-15 16:52:52 -04002000 private ArrayList<Action> mActions = new ArrayList<Action>(MAX_ACTION_BUTTONS);
Daniel Sandlera2985ed2012-04-03 16:42:00 -04002001 private boolean mUseChronometer;
Chris Wrenfbd96ba2012-05-01 12:03:58 -04002002 private Style mStyle;
Daniel Sandler0c890492012-09-12 17:23:10 -07002003 private boolean mShowWhen = true;
Dan Sandler0bf2ed82013-12-21 23:33:41 -06002004 private int mVisibility = VISIBILITY_PRIVATE;
2005 private Notification mPublicVersion = null;
Dan Sandler26e81cf2014-05-06 10:01:27 -04002006 private final NotificationColorUtil mColorUtil;
Chris Wrendde75302014-03-26 17:24:15 -04002007 private ArrayList<String> mPeople;
Dan Sandler26e81cf2014-05-06 10:01:27 -04002008 private int mColor = COLOR_DEFAULT;
Christoph Studer7ac80e62014-08-04 16:01:57 +02002009
2010 /**
Kenny Guy8942bcd2014-09-08 21:09:47 +01002011 * The user that built the notification originally.
2012 */
2013 private int mOriginatingUserId;
2014
2015 /**
Christoph Studer4600f9b2014-07-22 22:44:43 +02002016 * Contains extras related to rebuilding during the build phase.
2017 */
2018 private Bundle mRebuildBundle = new Bundle();
2019 /**
2020 * Contains the notification to rebuild when this Builder is in "rebuild" mode.
2021 * Null otherwise.
2022 */
2023 private Notification mRebuildNotification = null;
2024
Joe Onoratocb109a02011-01-18 17:57:41 -08002025 /**
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02002026 * Whether the build notification has three lines. This is used to make the top padding for
2027 * both the contracted and expanded layout consistent.
2028 *
2029 * <p>
2030 * This field is only valid during the build phase.
2031 */
2032 private boolean mHasThreeLines;
2033
2034 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002035 * Constructs a new Builder with the defaults:
Joe Onoratocb109a02011-01-18 17:57:41 -08002036 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002037
2038 * <table>
2039 * <tr><th align=right>priority</th>
2040 * <td>{@link #PRIORITY_DEFAULT}</td></tr>
2041 * <tr><th align=right>when</th>
2042 * <td>now ({@link System#currentTimeMillis()})</td></tr>
2043 * <tr><th align=right>audio stream</th>
2044 * <td>{@link #STREAM_DEFAULT}</td></tr>
2045 * </table>
Joe Onoratocb109a02011-01-18 17:57:41 -08002046 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002047
2048 * @param context
2049 * A {@link Context} that will be used by the Builder to construct the
2050 * RemoteViews. The Context will not be held past the lifetime of this Builder
2051 * object.
Joe Onoratocb109a02011-01-18 17:57:41 -08002052 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002053 public Builder(Context context) {
Adam Powellbf06fa02014-05-30 12:32:18 -07002054 /*
2055 * Important compatibility note!
2056 * Some apps out in the wild create a Notification.Builder in their Activity subclass
2057 * constructor for later use. At this point Activities - themselves subclasses of
2058 * ContextWrapper - do not have their inner Context populated yet. This means that
2059 * any calls to Context methods from within this constructor can cause NPEs in existing
2060 * apps. Any data populated from mContext should therefore be populated lazily to
2061 * preserve compatibility.
2062 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002063 mContext = context;
Andy Stadler110988c2010-12-03 14:29:16 -08002064
2065 // Set defaults to match the defaults of a Notification
Joe Onorato46439ce2010-11-19 13:56:21 -08002066 mWhen = System.currentTimeMillis();
Andy Stadler110988c2010-12-03 14:29:16 -08002067 mAudioStreamType = STREAM_DEFAULT;
John Spurlockc0650f022014-07-19 13:22:39 -04002068 mAudioAttributes = AUDIO_ATTRIBUTES_DEFAULT;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002069 mPriority = PRIORITY_DEFAULT;
Chris Wrendde75302014-03-26 17:24:15 -04002070 mPeople = new ArrayList<String>();
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01002071
Dianne Hackborn955d8d62014-10-07 20:17:19 -07002072 mColorUtil = context.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.LOLLIPOP ?
Dan Sandler05c362d2014-09-03 00:16:27 +02002073 NotificationColorUtil.getInstance(mContext) : null;
Joe Onorato46439ce2010-11-19 13:56:21 -08002074 }
2075
Joe Onoratocb109a02011-01-18 17:57:41 -08002076 /**
Christoph Studer4600f9b2014-07-22 22:44:43 +02002077 * Creates a Builder for rebuilding the given Notification.
2078 * <p>
2079 * Call {@link #rebuild()} to retrieve the rebuilt version of 'n'.
2080 */
2081 private Builder(Context context, Notification n) {
2082 this(context);
2083 mRebuildNotification = n;
2084 restoreFromNotification(n);
2085
2086 Style style = null;
2087 Bundle extras = n.extras;
2088 String templateClass = extras.getString(EXTRA_TEMPLATE);
2089 if (!TextUtils.isEmpty(templateClass)) {
2090 Class<? extends Style> styleClass = getNotificationStyleClass(templateClass);
2091 if (styleClass == null) {
2092 Log.d(TAG, "Unknown style class: " + styleClass);
2093 return;
2094 }
2095
2096 try {
2097 Constructor<? extends Style> constructor = styleClass.getConstructor();
2098 style = constructor.newInstance();
2099 style.restoreFromExtras(extras);
2100 } catch (Throwable t) {
2101 Log.e(TAG, "Could not create Style", t);
2102 return;
2103 }
2104 }
2105 if (style != null) {
2106 setStyle(style);
2107 }
2108 }
2109
2110 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002111 * Add a timestamp pertaining to the notification (usually the time the event occurred).
Daniel Sandler0c890492012-09-12 17:23:10 -07002112 * It will be shown in the notification content view by default; use
Griff Hazen50c11652014-05-16 09:46:31 -07002113 * {@link #setShowWhen(boolean) setShowWhen} to control this.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002114 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002115 * @see Notification#when
Joe Onoratocb109a02011-01-18 17:57:41 -08002116 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002117 public Builder setWhen(long when) {
2118 mWhen = when;
2119 return this;
2120 }
2121
Joe Onoratocb109a02011-01-18 17:57:41 -08002122 /**
Griff Hazen50c11652014-05-16 09:46:31 -07002123 * Control whether the timestamp set with {@link #setWhen(long) setWhen} is shown
Daniel Sandler0c890492012-09-12 17:23:10 -07002124 * in the content view.
2125 */
2126 public Builder setShowWhen(boolean show) {
2127 mShowWhen = show;
2128 return this;
2129 }
2130
2131 /**
Daniel Sandlerd33b8032012-05-10 11:41:48 -04002132 * Show the {@link Notification#when} field as a stopwatch.
Joe Malin8d40d042012-11-05 11:36:40 -08002133 *
2134 * Instead of presenting <code>when</code> as a timestamp, the notification will show an
Daniel Sandlerd33b8032012-05-10 11:41:48 -04002135 * automatically updating display of the minutes and seconds since <code>when</code>.
Daniel Sandlera2985ed2012-04-03 16:42:00 -04002136 *
Daniel Sandlerd33b8032012-05-10 11:41:48 -04002137 * Useful when showing an elapsed time (like an ongoing phone call).
2138 *
2139 * @see android.widget.Chronometer
Daniel Sandlera2985ed2012-04-03 16:42:00 -04002140 * @see Notification#when
2141 */
2142 public Builder setUsesChronometer(boolean b) {
2143 mUseChronometer = b;
2144 return this;
2145 }
2146
2147 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002148 * Set the small icon resource, which will be used to represent the notification in the
2149 * status bar.
Joe Onoratocb109a02011-01-18 17:57:41 -08002150 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002151
2152 * The platform template for the expanded view will draw this icon in the left, unless a
2153 * {@link #setLargeIcon(Bitmap) large icon} has also been specified, in which case the small
2154 * icon will be moved to the right-hand side.
2155 *
2156
2157 * @param icon
2158 * A resource ID in the application's package of the drawable to use.
2159 * @see Notification#icon
Joe Onoratocb109a02011-01-18 17:57:41 -08002160 */
Tor Norbye7b9c9122013-05-30 16:48:33 -07002161 public Builder setSmallIcon(@DrawableRes int icon) {
Joe Onorato46439ce2010-11-19 13:56:21 -08002162 mSmallIcon = icon;
2163 return this;
2164 }
2165
Joe Onoratocb109a02011-01-18 17:57:41 -08002166 /**
2167 * A variant of {@link #setSmallIcon(int) setSmallIcon(int)} that takes an additional
2168 * level parameter for when the icon is a {@link android.graphics.drawable.LevelListDrawable
2169 * LevelListDrawable}.
2170 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002171 * @param icon A resource ID in the application's package of the drawable to use.
Joe Onoratocb109a02011-01-18 17:57:41 -08002172 * @param level The level to use for the icon.
2173 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002174 * @see Notification#icon
2175 * @see Notification#iconLevel
Joe Onoratocb109a02011-01-18 17:57:41 -08002176 */
Tor Norbye7b9c9122013-05-30 16:48:33 -07002177 public Builder setSmallIcon(@DrawableRes int icon, int level) {
Joe Onorato46439ce2010-11-19 13:56:21 -08002178 mSmallIcon = icon;
2179 mSmallIconLevel = level;
2180 return this;
2181 }
2182
Joe Onoratocb109a02011-01-18 17:57:41 -08002183 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002184 * Set the first line of text in the platform notification template.
Joe Onoratocb109a02011-01-18 17:57:41 -08002185 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002186 public Builder setContentTitle(CharSequence title) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04002187 mContentTitle = safeCharSequence(title);
Joe Onorato46439ce2010-11-19 13:56:21 -08002188 return this;
2189 }
2190
Joe Onoratocb109a02011-01-18 17:57:41 -08002191 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002192 * Set the second line of text in the platform notification template.
Joe Onoratocb109a02011-01-18 17:57:41 -08002193 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002194 public Builder setContentText(CharSequence text) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04002195 mContentText = safeCharSequence(text);
Joe Onorato46439ce2010-11-19 13:56:21 -08002196 return this;
2197 }
2198
Joe Onoratocb109a02011-01-18 17:57:41 -08002199 /**
Joe Malin8d40d042012-11-05 11:36:40 -08002200 * Set the third line of text in the platform notification template.
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04002201 * Don't use if you're also using {@link #setProgress(int, int, boolean)}; they occupy the
2202 * same location in the standard template.
Daniel Sandlerf3b73432012-03-27 15:01:25 -04002203 */
2204 public Builder setSubText(CharSequence text) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04002205 mSubText = safeCharSequence(text);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04002206 return this;
2207 }
2208
2209 /**
Joe Onoratocb109a02011-01-18 17:57:41 -08002210 * Set the large number at the right-hand side of the notification. This is
2211 * equivalent to setContentInfo, although it might show the number in a different
2212 * font size for readability.
2213 */
Joe Onorato8595a3d2010-11-19 18:12:07 -08002214 public Builder setNumber(int number) {
2215 mNumber = number;
2216 return this;
2217 }
2218
Joe Onoratocb109a02011-01-18 17:57:41 -08002219 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002220 * A small piece of additional information pertaining to this notification.
2221 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002222 * The platform template will draw this on the last line of the notification, at the far
2223 * right (to the right of a smallIcon if it has been placed there).
Joe Onoratocb109a02011-01-18 17:57:41 -08002224 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002225 public Builder setContentInfo(CharSequence info) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04002226 mContentInfo = safeCharSequence(info);
Joe Onorato46439ce2010-11-19 13:56:21 -08002227 return this;
2228 }
2229
Joe Onoratocb109a02011-01-18 17:57:41 -08002230 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002231 * Set the progress this notification represents.
2232 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002233 * The platform template will represent this using a {@link ProgressBar}.
Jeff Sharkey1c400132011-08-05 14:50:13 -07002234 */
2235 public Builder setProgress(int max, int progress, boolean indeterminate) {
2236 mProgressMax = max;
2237 mProgress = progress;
2238 mProgressIndeterminate = indeterminate;
2239 return this;
2240 }
2241
2242 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002243 * Supply a custom RemoteViews to use instead of the platform template.
2244 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002245 * @see Notification#contentView
Joe Onoratocb109a02011-01-18 17:57:41 -08002246 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002247 public Builder setContent(RemoteViews views) {
2248 mContentView = views;
2249 return this;
2250 }
2251
Joe Onoratocb109a02011-01-18 17:57:41 -08002252 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002253 * Supply a {@link PendingIntent} to be sent when the notification is clicked.
2254 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002255 * As of {@link android.os.Build.VERSION_CODES#HONEYCOMB}, if this field is unset and you
2256 * have specified a custom RemoteViews with {@link #setContent(RemoteViews)}, you can use
2257 * {@link RemoteViews#setOnClickPendingIntent RemoteViews.setOnClickPendingIntent(int,PendingIntent)}
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002258 * to assign PendingIntents to individual views in that custom layout (i.e., to create
Daniel Sandlerf3b73432012-03-27 15:01:25 -04002259 * clickable buttons inside the notification view).
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002260 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002261 * @see Notification#contentIntent Notification.contentIntent
Joe Onoratocb109a02011-01-18 17:57:41 -08002262 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002263 public Builder setContentIntent(PendingIntent intent) {
2264 mContentIntent = intent;
2265 return this;
2266 }
2267
Joe Onoratocb109a02011-01-18 17:57:41 -08002268 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002269 * Supply a {@link PendingIntent} to send when the notification is cleared explicitly by the user.
2270 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002271 * @see Notification#deleteIntent
Joe Onoratocb109a02011-01-18 17:57:41 -08002272 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002273 public Builder setDeleteIntent(PendingIntent intent) {
2274 mDeleteIntent = intent;
2275 return this;
2276 }
2277
Joe Onoratocb109a02011-01-18 17:57:41 -08002278 /**
2279 * An intent to launch instead of posting the notification to the status bar.
2280 * Only for use with extremely high-priority notifications demanding the user's
2281 * <strong>immediate</strong> attention, such as an incoming phone call or
2282 * alarm clock that the user has explicitly set to a particular time.
2283 * If this facility is used for something else, please give the user an option
2284 * to turn it off and use a normal notification, as this can be extremely
2285 * disruptive.
2286 *
Chris Wren47c20a12014-06-18 17:27:29 -04002287 * <p>
2288 * The system UI may choose to display a heads-up notification, instead of
2289 * launching this intent, while the user is using the device.
2290 * </p>
2291 *
Joe Onoratocb109a02011-01-18 17:57:41 -08002292 * @param intent The pending intent to launch.
2293 * @param highPriority Passing true will cause this notification to be sent
2294 * even if other notifications are suppressed.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002295 *
2296 * @see Notification#fullScreenIntent
Joe Onoratocb109a02011-01-18 17:57:41 -08002297 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002298 public Builder setFullScreenIntent(PendingIntent intent, boolean highPriority) {
2299 mFullScreenIntent = intent;
2300 setFlag(FLAG_HIGH_PRIORITY, highPriority);
2301 return this;
2302 }
2303
Joe Onoratocb109a02011-01-18 17:57:41 -08002304 /**
Dan Sandler5fcdf6e2014-07-18 11:31:15 -04002305 * Set the "ticker" text which is sent to accessibility services.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002306 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002307 * @see Notification#tickerText
Joe Onoratocb109a02011-01-18 17:57:41 -08002308 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002309 public Builder setTicker(CharSequence tickerText) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04002310 mTickerText = safeCharSequence(tickerText);
Joe Onorato46439ce2010-11-19 13:56:21 -08002311 return this;
2312 }
2313
Joe Onoratocb109a02011-01-18 17:57:41 -08002314 /**
Dan Sandler5fcdf6e2014-07-18 11:31:15 -04002315 * Obsolete version of {@link #setTicker(CharSequence)}.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002316 *
Joe Onoratocb109a02011-01-18 17:57:41 -08002317 */
Dan Sandler5fcdf6e2014-07-18 11:31:15 -04002318 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08002319 public Builder setTicker(CharSequence tickerText, RemoteViews views) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04002320 mTickerText = safeCharSequence(tickerText);
Dan Sandler5fcdf6e2014-07-18 11:31:15 -04002321 mTickerView = views; // we'll save it for you anyway
Joe Onorato46439ce2010-11-19 13:56:21 -08002322 return this;
2323 }
2324
Joe Onoratocb109a02011-01-18 17:57:41 -08002325 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002326 * Add a large icon to the notification (and the ticker on some devices).
2327 *
2328 * In the platform template, this image will be shown on the left of the notification view
2329 * in place of the {@link #setSmallIcon(int) small icon} (which will move to the right side).
2330 *
2331 * @see Notification#largeIcon
Joe Onoratocb109a02011-01-18 17:57:41 -08002332 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002333 public Builder setLargeIcon(Bitmap icon) {
2334 mLargeIcon = icon;
2335 return this;
2336 }
2337
Joe Onoratocb109a02011-01-18 17:57:41 -08002338 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002339 * Set the sound to play.
2340 *
John Spurlockc0650f022014-07-19 13:22:39 -04002341 * It will be played using the {@link #AUDIO_ATTRIBUTES_DEFAULT default audio attributes}
2342 * for notifications.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002343 *
Chris Wren47c20a12014-06-18 17:27:29 -04002344 * <p>
2345 * A notification that is noisy is more likely to be presented as a heads-up notification.
2346 * </p>
2347 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002348 * @see Notification#sound
Joe Onoratocb109a02011-01-18 17:57:41 -08002349 */
Joe Onorato52f80cd2010-11-21 15:34:48 -08002350 public Builder setSound(Uri sound) {
2351 mSound = sound;
John Spurlockc0650f022014-07-19 13:22:39 -04002352 mAudioAttributes = AUDIO_ATTRIBUTES_DEFAULT;
Joe Onorato52f80cd2010-11-21 15:34:48 -08002353 return this;
2354 }
2355
Joe Onoratocb109a02011-01-18 17:57:41 -08002356 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002357 * Set the sound to play, along with a specific stream on which to play it.
Joe Onoratocb109a02011-01-18 17:57:41 -08002358 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002359 * See {@link android.media.AudioManager} for the <code>STREAM_</code> constants.
2360 *
Chris Wren47c20a12014-06-18 17:27:29 -04002361 * <p>
2362 * A notification that is noisy is more likely to be presented as a heads-up notification.
2363 * </p>
John Spurlockc0650f022014-07-19 13:22:39 -04002364 * @deprecated use {@link #setSound(Uri, AudioAttributes)} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002365 * @see Notification#sound
Joe Onoratocb109a02011-01-18 17:57:41 -08002366 */
Jean-Michel Trivi81f871e2014-08-06 16:32:38 -07002367 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08002368 public Builder setSound(Uri sound, int streamType) {
2369 mSound = sound;
2370 mAudioStreamType = streamType;
2371 return this;
2372 }
2373
Joe Onoratocb109a02011-01-18 17:57:41 -08002374 /**
John Spurlockc0650f022014-07-19 13:22:39 -04002375 * Set the sound to play, along with specific {@link AudioAttributes audio attributes} to
2376 * use during playback.
2377 *
2378 * <p>
2379 * A notification that is noisy is more likely to be presented as a heads-up notification.
2380 * </p>
2381 *
2382 * @see Notification#sound
2383 */
2384 public Builder setSound(Uri sound, AudioAttributes audioAttributes) {
2385 mSound = sound;
2386 mAudioAttributes = audioAttributes;
2387 return this;
2388 }
2389
2390 /**
Joe Onoratocb109a02011-01-18 17:57:41 -08002391 * Set the vibration pattern to use.
2392 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002393 * See {@link android.os.Vibrator#vibrate(long[], int)} for a discussion of the
2394 * <code>pattern</code> parameter.
2395 *
Chris Wren47c20a12014-06-18 17:27:29 -04002396 * <p>
2397 * A notification that vibrates is more likely to be presented as a heads-up notification.
2398 * </p>
2399 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002400 * @see Notification#vibrate
Joe Onoratocb109a02011-01-18 17:57:41 -08002401 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002402 public Builder setVibrate(long[] pattern) {
2403 mVibrate = pattern;
2404 return this;
2405 }
2406
Joe Onoratocb109a02011-01-18 17:57:41 -08002407 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002408 * Set the desired color for the indicator LED on the device, as well as the
2409 * blink duty cycle (specified in milliseconds).
2410 *
2411
2412 * Not all devices will honor all (or even any) of these values.
2413 *
2414
2415 * @see Notification#ledARGB
2416 * @see Notification#ledOnMS
2417 * @see Notification#ledOffMS
Joe Onoratocb109a02011-01-18 17:57:41 -08002418 */
Tor Norbye80756e32015-03-02 09:39:27 -08002419 public Builder setLights(@ColorInt int argb, int onMs, int offMs) {
Joe Onorato46439ce2010-11-19 13:56:21 -08002420 mLedArgb = argb;
2421 mLedOnMs = onMs;
2422 mLedOffMs = offMs;
Joe Onorato46439ce2010-11-19 13:56:21 -08002423 return this;
2424 }
2425
Joe Onoratocb109a02011-01-18 17:57:41 -08002426 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002427 * Set whether this is an "ongoing" notification.
Joe Onoratocb109a02011-01-18 17:57:41 -08002428 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002429
2430 * Ongoing notifications cannot be dismissed by the user, so your application or service
2431 * must take care of canceling them.
2432 *
2433
2434 * They are typically used to indicate a background task that the user is actively engaged
2435 * with (e.g., playing music) or is pending in some way and therefore occupying the device
2436 * (e.g., a file download, sync operation, active network connection).
2437 *
2438
2439 * @see Notification#FLAG_ONGOING_EVENT
2440 * @see Service#setForeground(boolean)
Joe Onoratocb109a02011-01-18 17:57:41 -08002441 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002442 public Builder setOngoing(boolean ongoing) {
2443 setFlag(FLAG_ONGOING_EVENT, ongoing);
2444 return this;
2445 }
2446
Joe Onoratocb109a02011-01-18 17:57:41 -08002447 /**
2448 * Set this flag if you would only like the sound, vibrate
2449 * and ticker to be played if the notification is not already showing.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002450 *
2451 * @see Notification#FLAG_ONLY_ALERT_ONCE
Joe Onoratocb109a02011-01-18 17:57:41 -08002452 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002453 public Builder setOnlyAlertOnce(boolean onlyAlertOnce) {
2454 setFlag(FLAG_ONLY_ALERT_ONCE, onlyAlertOnce);
2455 return this;
2456 }
2457
Joe Onoratocb109a02011-01-18 17:57:41 -08002458 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002459 * Make this notification automatically dismissed when the user touches it. The
2460 * PendingIntent set with {@link #setDeleteIntent} will be sent when this happens.
2461 *
2462 * @see Notification#FLAG_AUTO_CANCEL
Joe Onoratocb109a02011-01-18 17:57:41 -08002463 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002464 public Builder setAutoCancel(boolean autoCancel) {
Joe Onorato281d83f2011-01-04 17:13:10 -08002465 setFlag(FLAG_AUTO_CANCEL, autoCancel);
Joe Onorato46439ce2010-11-19 13:56:21 -08002466 return this;
2467 }
2468
Joe Onoratocb109a02011-01-18 17:57:41 -08002469 /**
Griff Hazendfcb0802014-02-11 12:00:00 -08002470 * Set whether or not this notification should not bridge to other devices.
2471 *
2472 * <p>Some notifications can be bridged to other devices for remote display.
2473 * This hint can be set to recommend this notification not be bridged.
2474 */
2475 public Builder setLocalOnly(boolean localOnly) {
2476 setFlag(FLAG_LOCAL_ONLY, localOnly);
2477 return this;
2478 }
2479
2480 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002481 * Set which notification properties will be inherited from system defaults.
Joe Onoratocb109a02011-01-18 17:57:41 -08002482 * <p>
2483 * The value should be one or more of the following fields combined with
2484 * bitwise-or:
2485 * {@link #DEFAULT_SOUND}, {@link #DEFAULT_VIBRATE}, {@link #DEFAULT_LIGHTS}.
2486 * <p>
2487 * For all default values, use {@link #DEFAULT_ALL}.
2488 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002489 public Builder setDefaults(int defaults) {
2490 mDefaults = defaults;
2491 return this;
2492 }
2493
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002494 /**
2495 * Set the priority of this notification.
2496 *
2497 * @see Notification#priority
2498 */
Tor Norbyed9273d62013-05-30 15:59:53 -07002499 public Builder setPriority(@Priority int pri) {
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002500 mPriority = pri;
2501 return this;
2502 }
Joe Malin8d40d042012-11-05 11:36:40 -08002503
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002504 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -04002505 * Set the notification category.
Joe Malin8d40d042012-11-05 11:36:40 -08002506 *
John Spurlockfd7f1e02014-03-18 16:41:57 -04002507 * @see Notification#category
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002508 */
John Spurlockfd7f1e02014-03-18 16:41:57 -04002509 public Builder setCategory(String category) {
2510 mCategory = category;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002511 return this;
2512 }
2513
2514 /**
Chris Wrendde75302014-03-26 17:24:15 -04002515 * Add a person that is relevant to this notification.
2516 *
Chris Wrene6c48932014-09-29 17:19:27 -04002517 * <P>
2518 * Depending on user preferences, this annotation may allow the notification to pass
2519 * through interruption filters, and to appear more prominently in the user interface.
2520 * </P>
2521 *
2522 * <P>
2523 * The person should be specified by the {@code String} representation of a
2524 * {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI}.
2525 * </P>
2526 *
2527 * <P>The system will also attempt to resolve {@code mailto:} and {@code tel:} schema
2528 * URIs. The path part of these URIs must exist in the contacts database, in the
2529 * appropriate column, or the reference will be discarded as invalid. Telephone schema
2530 * URIs will be resolved by {@link android.provider.ContactsContract.PhoneLookup}.
2531 * </P>
2532 *
2533 * @param uri A URI for the person.
Chris Wrendde75302014-03-26 17:24:15 -04002534 * @see Notification#EXTRA_PEOPLE
2535 */
Chris Wrene6c48932014-09-29 17:19:27 -04002536 public Builder addPerson(String uri) {
2537 mPeople.add(uri);
Chris Wrendde75302014-03-26 17:24:15 -04002538 return this;
2539 }
2540
2541 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002542 * Set this notification to be part of a group of notifications sharing the same key.
2543 * Grouped notifications may display in a cluster or stack on devices which
2544 * support such rendering.
2545 *
2546 * <p>To make this notification the summary for its group, also call
2547 * {@link #setGroupSummary}. A sort order can be specified for group members by using
2548 * {@link #setSortKey}.
2549 * @param groupKey The group key of the group.
2550 * @return this object for method chaining
2551 */
2552 public Builder setGroup(String groupKey) {
2553 mGroupKey = groupKey;
2554 return this;
2555 }
2556
2557 /**
2558 * Set this notification to be the group summary for a group of notifications.
2559 * Grouped notifications may display in a cluster or stack on devices which
2560 * support such rendering. Requires a group key also be set using {@link #setGroup}.
2561 * @param isGroupSummary Whether this notification should be a group summary.
2562 * @return this object for method chaining
2563 */
2564 public Builder setGroupSummary(boolean isGroupSummary) {
2565 setFlag(FLAG_GROUP_SUMMARY, isGroupSummary);
2566 return this;
2567 }
2568
2569 /**
2570 * Set a sort key that orders this notification among other notifications from the
2571 * same package. This can be useful if an external sort was already applied and an app
2572 * would like to preserve this. Notifications will be sorted lexicographically using this
2573 * value, although providing different priorities in addition to providing sort key may
2574 * cause this value to be ignored.
2575 *
2576 * <p>This sort key can also be used to order members of a notification group. See
Griff Hazen9e1379f2014-05-20 12:50:51 -07002577 * {@link #setGroup}.
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002578 *
2579 * @see String#compareTo(String)
2580 */
2581 public Builder setSortKey(String sortKey) {
2582 mSortKey = sortKey;
2583 return this;
2584 }
2585
2586 /**
Griff Hazen720042b2014-02-24 15:46:56 -08002587 * Merge additional metadata into this notification.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002588 *
Griff Hazen720042b2014-02-24 15:46:56 -08002589 * <p>Values within the Bundle will replace existing extras values in this Builder.
2590 *
2591 * @see Notification#extras
2592 */
Griff Hazen959591e2014-05-15 22:26:18 -07002593 public Builder addExtras(Bundle extras) {
2594 if (extras != null) {
2595 if (mExtras == null) {
2596 mExtras = new Bundle(extras);
2597 } else {
2598 mExtras.putAll(extras);
2599 }
Griff Hazen720042b2014-02-24 15:46:56 -08002600 }
2601 return this;
2602 }
2603
2604 /**
2605 * Set metadata for this notification.
2606 *
2607 * <p>A reference to the Bundle is held for the lifetime of this Builder, and the Bundle's
Chris Wrenfbd96ba2012-05-01 12:03:58 -04002608 * current contents are copied into the Notification each time {@link #build()} is
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002609 * called.
2610 *
Griff Hazen720042b2014-02-24 15:46:56 -08002611 * <p>Replaces any existing extras values with those from the provided Bundle.
2612 * Use {@link #addExtras} to merge in metadata instead.
2613 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002614 * @see Notification#extras
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002615 */
Griff Hazen959591e2014-05-15 22:26:18 -07002616 public Builder setExtras(Bundle extras) {
2617 mExtras = extras;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002618 return this;
2619 }
2620
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002621 /**
Griff Hazen720042b2014-02-24 15:46:56 -08002622 * Get the current metadata Bundle used by this notification Builder.
2623 *
2624 * <p>The returned Bundle is shared with this Builder.
2625 *
2626 * <p>The current contents of this Bundle are copied into the Notification each time
2627 * {@link #build()} is called.
2628 *
2629 * @see Notification#extras
2630 */
2631 public Bundle getExtras() {
2632 if (mExtras == null) {
2633 mExtras = new Bundle();
2634 }
2635 return mExtras;
2636 }
2637
2638 /**
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002639 * Add an action to this notification. Actions are typically displayed by
2640 * the system as a button adjacent to the notification content.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04002641 * <p>
2642 * Every action must have an icon (32dp square and matching the
2643 * <a href="{@docRoot}design/style/iconography.html#action-bar">Holo
2644 * Dark action bar</a> visual style), a textual label, and a {@link PendingIntent}.
2645 * <p>
2646 * A notification in its expanded form can display up to 3 actions, from left to right in
2647 * the order they were added. Actions will not be displayed when the notification is
2648 * collapsed, however, so be sure that any essential functions may be accessed by the user
2649 * in some other way (for example, in the Activity pointed to by {@link #contentIntent}).
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002650 *
2651 * @param icon Resource ID of a drawable that represents the action.
2652 * @param title Text describing the action.
2653 * @param intent PendingIntent to be fired when the action is invoked.
2654 */
2655 public Builder addAction(int icon, CharSequence title, PendingIntent intent) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04002656 mActions.add(new Action(icon, safeCharSequence(title), intent));
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002657 return this;
2658 }
2659
Chris Wrenfbd96ba2012-05-01 12:03:58 -04002660 /**
Griff Hazen959591e2014-05-15 22:26:18 -07002661 * Add an action to this notification. Actions are typically displayed by
2662 * the system as a button adjacent to the notification content.
2663 * <p>
2664 * Every action must have an icon (32dp square and matching the
2665 * <a href="{@docRoot}design/style/iconography.html#action-bar">Holo
2666 * Dark action bar</a> visual style), a textual label, and a {@link PendingIntent}.
2667 * <p>
2668 * A notification in its expanded form can display up to 3 actions, from left to right in
2669 * the order they were added. Actions will not be displayed when the notification is
2670 * collapsed, however, so be sure that any essential functions may be accessed by the user
2671 * in some other way (for example, in the Activity pointed to by {@link #contentIntent}).
2672 *
2673 * @param action The action to add.
2674 */
2675 public Builder addAction(Action action) {
2676 mActions.add(action);
2677 return this;
2678 }
2679
2680 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04002681 * Add a rich notification style to be applied at build time.
2682 *
2683 * @param style Object responsible for modifying the notification style.
2684 */
2685 public Builder setStyle(Style style) {
2686 if (mStyle != style) {
2687 mStyle = style;
Daniel Sandlerc08dea22012-06-28 08:35:24 -07002688 if (mStyle != null) {
2689 mStyle.setBuilder(this);
2690 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04002691 }
2692 return this;
2693 }
2694
Dan Sandler0bf2ed82013-12-21 23:33:41 -06002695 /**
2696 * Specify the value of {@link #visibility}.
Griff Hazenb720abe2014-05-20 13:15:30 -07002697 *
Dan Sandler0bf2ed82013-12-21 23:33:41 -06002698 * @param visibility One of {@link #VISIBILITY_PRIVATE} (the default),
2699 * {@link #VISIBILITY_SECRET}, or {@link #VISIBILITY_PUBLIC}.
2700 *
2701 * @return The same Builder.
2702 */
2703 public Builder setVisibility(int visibility) {
2704 mVisibility = visibility;
2705 return this;
2706 }
2707
2708 /**
2709 * Supply a replacement Notification whose contents should be shown in insecure contexts
2710 * (i.e. atop the secure lockscreen). See {@link #visibility} and {@link #VISIBILITY_PUBLIC}.
2711 * @param n A replacement notification, presumably with some or all info redacted.
2712 * @return The same Builder.
2713 */
2714 public Builder setPublicVersion(Notification n) {
2715 mPublicVersion = n;
2716 return this;
2717 }
2718
Griff Hazenb720abe2014-05-20 13:15:30 -07002719 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002720 * Apply an extender to this notification builder. Extenders may be used to add
2721 * metadata or change options on this builder.
2722 */
Griff Hazen61a9e862014-05-22 16:05:19 -07002723 public Builder extend(Extender extender) {
2724 extender.extend(this);
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002725 return this;
2726 }
2727
Joe Onorato46439ce2010-11-19 13:56:21 -08002728 private void setFlag(int mask, boolean value) {
2729 if (value) {
2730 mFlags |= mask;
2731 } else {
2732 mFlags &= ~mask;
2733 }
2734 }
2735
Dan Sandler26e81cf2014-05-06 10:01:27 -04002736 /**
2737 * Sets {@link Notification#color}.
2738 *
2739 * @param argb The accent color to use
2740 *
2741 * @return The same Builder.
2742 */
Tor Norbye80756e32015-03-02 09:39:27 -08002743 public Builder setColor(@ColorInt int argb) {
Dan Sandler26e81cf2014-05-06 10:01:27 -04002744 mColor = argb;
2745 return this;
2746 }
2747
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02002748 private Drawable getProfileBadgeDrawable() {
Christoph Studer7ac80e62014-08-04 16:01:57 +02002749 // Note: This assumes that the current user can read the profile badge of the
2750 // originating user.
Svetoslavc7d62f02014-09-04 15:39:54 -07002751 return mContext.getPackageManager().getUserBadgeForDensity(
Kenny Guy8942bcd2014-09-08 21:09:47 +01002752 new UserHandle(mOriginatingUserId), 0);
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02002753 }
2754
2755 private Bitmap getProfileBadge() {
2756 Drawable badge = getProfileBadgeDrawable();
Kenny Guy8a0101b2014-05-08 23:34:12 +01002757 if (badge == null) {
2758 return null;
2759 }
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02002760 final int size = mContext.getResources().getDimensionPixelSize(
2761 R.dimen.notification_badge_size);
2762 Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
Kenny Guy8a0101b2014-05-08 23:34:12 +01002763 Canvas canvas = new Canvas(bitmap);
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02002764 badge.setBounds(0, 0, size, size);
Kenny Guy8a0101b2014-05-08 23:34:12 +01002765 badge.draw(canvas);
2766 return bitmap;
2767 }
2768
Kenny Guy98193ea2014-07-24 19:54:37 +01002769 private boolean addProfileBadge(RemoteViews contentView, int resId) {
2770 Bitmap profileBadge = getProfileBadge();
2771
2772 contentView.setViewVisibility(R.id.profile_badge_large_template, View.GONE);
2773 contentView.setViewVisibility(R.id.profile_badge_line2, View.GONE);
2774 contentView.setViewVisibility(R.id.profile_badge_line3, View.GONE);
2775
2776 if (profileBadge != null) {
2777 contentView.setImageViewBitmap(resId, profileBadge);
2778 contentView.setViewVisibility(resId, View.VISIBLE);
2779
2780 // Make sure Line 3 is visible. As badge will be here if there
2781 // is no text to display.
2782 if (resId == R.id.profile_badge_line3) {
2783 contentView.setViewVisibility(R.id.line3, View.VISIBLE);
2784 }
2785 return true;
2786 }
2787 return false;
2788 }
2789
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02002790 private void shrinkLine3Text(RemoteViews contentView) {
2791 float subTextSize = mContext.getResources().getDimensionPixelSize(
2792 R.dimen.notification_subtext_size);
2793 contentView.setTextViewTextSize(R.id.text, TypedValue.COMPLEX_UNIT_PX, subTextSize);
2794 }
2795
Christoph Studerfe718432014-09-01 18:21:18 +02002796 private void unshrinkLine3Text(RemoteViews contentView) {
2797 float regularTextSize = mContext.getResources().getDimensionPixelSize(
2798 com.android.internal.R.dimen.notification_text_size);
2799 contentView.setTextViewTextSize(R.id.text, TypedValue.COMPLEX_UNIT_PX, regularTextSize);
2800 }
2801
2802 private void resetStandardTemplate(RemoteViews contentView) {
2803 removeLargeIconBackground(contentView);
2804 contentView.setViewPadding(R.id.icon, 0, 0, 0, 0);
2805 contentView.setImageViewResource(R.id.icon, 0);
2806 contentView.setInt(R.id.icon, "setBackgroundResource", 0);
2807 contentView.setViewVisibility(R.id.right_icon, View.GONE);
2808 contentView.setInt(R.id.right_icon, "setBackgroundResource", 0);
2809 contentView.setImageViewResource(R.id.right_icon, 0);
2810 contentView.setImageViewResource(R.id.icon, 0);
2811 contentView.setTextViewText(R.id.title, null);
2812 contentView.setTextViewText(R.id.text, null);
2813 unshrinkLine3Text(contentView);
2814 contentView.setTextViewText(R.id.text2, null);
2815 contentView.setViewVisibility(R.id.text2, View.GONE);
2816 contentView.setViewVisibility(R.id.info, View.GONE);
2817 contentView.setViewVisibility(R.id.time, View.GONE);
2818 contentView.setViewVisibility(R.id.line3, View.GONE);
2819 contentView.setViewVisibility(R.id.overflow_divider, View.GONE);
2820 contentView.setViewVisibility(R.id.progress, View.GONE);
Christoph Studerca1db712014-09-10 17:31:33 +02002821 contentView.setViewVisibility(R.id.chronometer, View.GONE);
2822 contentView.setViewVisibility(R.id.time, View.GONE);
Christoph Studerfe718432014-09-01 18:21:18 +02002823 }
2824
Jorim Jaggi445d3c02014-08-19 22:33:42 +02002825 private RemoteViews applyStandardTemplate(int resId) {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02002826 return applyStandardTemplate(resId, true /* hasProgress */);
2827 }
2828
2829 /**
2830 * @param hasProgress whether the progress bar should be shown and set
2831 */
2832 private RemoteViews applyStandardTemplate(int resId, boolean hasProgress) {
Kenny Guy77320062014-08-27 21:37:15 +01002833 RemoteViews contentView = new BuilderRemoteViews(mContext.getApplicationInfo(), resId);
Dan Sandler539aad42014-08-04 00:43:39 -04002834
Christoph Studerfe718432014-09-01 18:21:18 +02002835 resetStandardTemplate(contentView);
2836
Daniel Sandler9f7936a2012-05-21 16:14:28 -04002837 boolean showLine3 = false;
2838 boolean showLine2 = false;
Kenny Guy98193ea2014-07-24 19:54:37 +01002839 boolean contentTextInLine2 = false;
Dan Sandler190d58d2014-05-15 09:33:39 -04002840
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01002841 if (mLargeIcon != null) {
2842 contentView.setImageViewBitmap(R.id.icon, mLargeIcon);
Christoph Studer239f8352014-08-25 15:13:18 +02002843 processLargeLegacyIcon(mLargeIcon, contentView);
Dan Sandler190d58d2014-05-15 09:33:39 -04002844 contentView.setImageViewResource(R.id.right_icon, mSmallIcon);
2845 contentView.setViewVisibility(R.id.right_icon, View.VISIBLE);
2846 processSmallRightIcon(mSmallIcon, contentView);
2847 } else { // small icon at left
2848 contentView.setImageViewResource(R.id.icon, mSmallIcon);
2849 contentView.setViewVisibility(R.id.icon, View.VISIBLE);
2850 processSmallIconAsLarge(mSmallIcon, contentView);
Joe Onorato561d3852010-11-20 18:09:34 -08002851 }
2852 if (mContentTitle != null) {
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01002853 contentView.setTextViewText(R.id.title, processLegacyText(mContentTitle));
Joe Onorato561d3852010-11-20 18:09:34 -08002854 }
2855 if (mContentText != null) {
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01002856 contentView.setTextViewText(R.id.text, processLegacyText(mContentText));
Daniel Sandler9f7936a2012-05-21 16:14:28 -04002857 showLine3 = true;
Joe Onorato561d3852010-11-20 18:09:34 -08002858 }
2859 if (mContentInfo != null) {
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01002860 contentView.setTextViewText(R.id.info, processLegacyText(mContentInfo));
Jeff Sharkey1c400132011-08-05 14:50:13 -07002861 contentView.setViewVisibility(R.id.info, View.VISIBLE);
Daniel Sandler9f7936a2012-05-21 16:14:28 -04002862 showLine3 = true;
Joe Onorato561d3852010-11-20 18:09:34 -08002863 } else if (mNumber > 0) {
Daniel Sandlerebce0112011-06-16 16:44:51 -04002864 final int tooBig = mContext.getResources().getInteger(
2865 R.integer.status_bar_notification_info_maxnum);
2866 if (mNumber > tooBig) {
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01002867 contentView.setTextViewText(R.id.info, processLegacyText(
2868 mContext.getResources().getString(
2869 R.string.status_bar_notification_info_overflow)));
Joe Onorato059a2f82011-01-04 10:27:01 -08002870 } else {
2871 NumberFormat f = NumberFormat.getIntegerInstance();
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01002872 contentView.setTextViewText(R.id.info, processLegacyText(f.format(mNumber)));
Joe Onorato059a2f82011-01-04 10:27:01 -08002873 }
Jeff Sharkey1c400132011-08-05 14:50:13 -07002874 contentView.setViewVisibility(R.id.info, View.VISIBLE);
Daniel Sandler9f7936a2012-05-21 16:14:28 -04002875 showLine3 = true;
Joe Onorato561d3852010-11-20 18:09:34 -08002876 } else {
2877 contentView.setViewVisibility(R.id.info, View.GONE);
2878 }
Daniel Sandlerf3b73432012-03-27 15:01:25 -04002879
Daniel Sandler9f7936a2012-05-21 16:14:28 -04002880 // Need to show three lines?
Daniel Sandlerf3b73432012-03-27 15:01:25 -04002881 if (mSubText != null) {
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01002882 contentView.setTextViewText(R.id.text, processLegacyText(mSubText));
Daniel Sandler9f7936a2012-05-21 16:14:28 -04002883 if (mContentText != null) {
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01002884 contentView.setTextViewText(R.id.text2, processLegacyText(mContentText));
Daniel Sandler9f7936a2012-05-21 16:14:28 -04002885 contentView.setViewVisibility(R.id.text2, View.VISIBLE);
2886 showLine2 = true;
Kenny Guy98193ea2014-07-24 19:54:37 +01002887 contentTextInLine2 = true;
Daniel Sandler9f7936a2012-05-21 16:14:28 -04002888 } else {
2889 contentView.setViewVisibility(R.id.text2, View.GONE);
2890 }
Jeff Sharkey1c400132011-08-05 14:50:13 -07002891 } else {
Daniel Sandlerf3b73432012-03-27 15:01:25 -04002892 contentView.setViewVisibility(R.id.text2, View.GONE);
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02002893 if (hasProgress && (mProgressMax != 0 || mProgressIndeterminate)) {
Christoph Studeraca4b252014-09-09 15:58:41 +02002894 contentView.setViewVisibility(R.id.progress, View.VISIBLE);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04002895 contentView.setProgressBar(
2896 R.id.progress, mProgressMax, mProgress, mProgressIndeterminate);
Jorim Jaggief72a192014-08-26 21:57:46 +02002897 contentView.setProgressBackgroundTintList(
Alan Viverette4a357cd2015-03-18 18:37:18 -07002898 R.id.progress, ColorStateList.valueOf(mContext.getColor(
Jorim Jaggief72a192014-08-26 21:57:46 +02002899 R.color.notification_progress_background_color)));
2900 if (mColor != COLOR_DEFAULT) {
2901 ColorStateList colorStateList = ColorStateList.valueOf(mColor);
2902 contentView.setProgressTintList(R.id.progress, colorStateList);
2903 contentView.setProgressIndeterminateTintList(R.id.progress, colorStateList);
2904 }
Daniel Sandler9f7936a2012-05-21 16:14:28 -04002905 showLine2 = true;
Daniel Sandlerf3b73432012-03-27 15:01:25 -04002906 } else {
2907 contentView.setViewVisibility(R.id.progress, View.GONE);
2908 }
Jeff Sharkey1c400132011-08-05 14:50:13 -07002909 }
Daniel Sandler9f7936a2012-05-21 16:14:28 -04002910 if (showLine2) {
Jorim Jaggi445d3c02014-08-19 22:33:42 +02002911
2912 // need to shrink all the type to make sure everything fits
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02002913 shrinkLine3Text(contentView);
Daniel Sandler9f7936a2012-05-21 16:14:28 -04002914 }
2915
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02002916 if (showsTimeOrChronometer()) {
Daniel Sandlera2985ed2012-04-03 16:42:00 -04002917 if (mUseChronometer) {
2918 contentView.setViewVisibility(R.id.chronometer, View.VISIBLE);
2919 contentView.setLong(R.id.chronometer, "setBase",
2920 mWhen + (SystemClock.elapsedRealtime() - System.currentTimeMillis()));
2921 contentView.setBoolean(R.id.chronometer, "setStarted", true);
2922 } else {
2923 contentView.setViewVisibility(R.id.time, View.VISIBLE);
2924 contentView.setLong(R.id.time, "setTime", mWhen);
2925 }
Joe Onorato561d3852010-11-20 18:09:34 -08002926 }
Daniel Sandler0c890492012-09-12 17:23:10 -07002927
Jorim Jaggi445d3c02014-08-19 22:33:42 +02002928 // Adjust padding depending on line count and font size.
2929 contentView.setViewPadding(R.id.line1, 0, calculateTopPadding(mContext,
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02002930 mHasThreeLines, mContext.getResources().getConfiguration().fontScale),
Jorim Jaggi445d3c02014-08-19 22:33:42 +02002931 0, 0);
2932
Kenny Guy98193ea2014-07-24 19:54:37 +01002933 // We want to add badge to first line of text.
2934 boolean addedBadge = addProfileBadge(contentView,
2935 contentTextInLine2 ? R.id.profile_badge_line2 : R.id.profile_badge_line3);
2936 // If we added the badge to line 3 then we should show line 3.
2937 if (addedBadge && !contentTextInLine2) {
2938 showLine3 = true;
2939 }
2940
2941 // Note getStandardView may hide line 3 again.
Daniel Sandler9f7936a2012-05-21 16:14:28 -04002942 contentView.setViewVisibility(R.id.line3, showLine3 ? View.VISIBLE : View.GONE);
Daniel Sandler6387d2f2012-05-22 13:44:09 -04002943 contentView.setViewVisibility(R.id.overflow_divider, showLine3 ? View.VISIBLE : View.GONE);
Joe Onorato561d3852010-11-20 18:09:34 -08002944 return contentView;
2945 }
2946
Jorim Jaggi445d3c02014-08-19 22:33:42 +02002947 /**
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02002948 * @return true if the built notification will show the time or the chronometer; false
2949 * otherwise
2950 */
2951 private boolean showsTimeOrChronometer() {
2952 return mWhen != 0 && mShowWhen;
2953 }
2954
2955 /**
Jorim Jaggi445d3c02014-08-19 22:33:42 +02002956 * Logic to find out whether the notification is going to have three lines in the contracted
2957 * layout. This is used to adjust the top padding.
2958 *
2959 * @return true if the notification is going to have three lines; false if the notification
2960 * is going to have one or two lines
2961 */
2962 private boolean hasThreeLines() {
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02002963 boolean contentTextInLine2 = mSubText != null && mContentText != null;
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02002964 boolean hasProgress = mStyle == null || mStyle.hasProgress();
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02002965 // If we have content text in line 2, badge goes into line 2, or line 3 otherwise
2966 boolean badgeInLine3 = getProfileBadgeDrawable() != null && !contentTextInLine2;
2967 boolean hasLine3 = mContentText != null || mContentInfo != null || mNumber > 0
2968 || badgeInLine3;
Jorim Jaggi445d3c02014-08-19 22:33:42 +02002969 boolean hasLine2 = (mSubText != null && mContentText != null) ||
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02002970 (hasProgress && mSubText == null
2971 && (mProgressMax != 0 || mProgressIndeterminate));
Jorim Jaggi445d3c02014-08-19 22:33:42 +02002972 return hasLine2 && hasLine3;
2973 }
2974
2975 /**
2976 * @hide
2977 */
2978 public static int calculateTopPadding(Context ctx, boolean hasThreeLines,
2979 float fontScale) {
2980 int padding = ctx.getResources().getDimensionPixelSize(hasThreeLines
2981 ? R.dimen.notification_top_pad_narrow
2982 : R.dimen.notification_top_pad);
2983 int largePadding = ctx.getResources().getDimensionPixelSize(hasThreeLines
2984 ? R.dimen.notification_top_pad_large_text_narrow
2985 : R.dimen.notification_top_pad_large_text);
2986 float largeFactor = (MathUtils.constrain(fontScale, 1.0f, LARGE_TEXT_SCALE) - 1f)
2987 / (LARGE_TEXT_SCALE - 1f);
2988
2989 // Linearly interpolate the padding between large and normal with the font scale ranging
2990 // from 1f to LARGE_TEXT_SCALE
2991 return Math.round((1 - largeFactor) * padding + largeFactor * largePadding);
2992 }
2993
Christoph Studerfe718432014-09-01 18:21:18 +02002994 private void resetStandardTemplateWithActions(RemoteViews big) {
2995 big.setViewVisibility(R.id.actions, View.GONE);
2996 big.setViewVisibility(R.id.action_divider, View.GONE);
2997 big.removeAllViews(R.id.actions);
2998 }
2999
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003000 private RemoteViews applyStandardTemplateWithActions(int layoutId) {
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003001 RemoteViews big = applyStandardTemplate(layoutId);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003002
Christoph Studerfe718432014-09-01 18:21:18 +02003003 resetStandardTemplateWithActions(big);
3004
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003005 int N = mActions.size();
3006 if (N > 0) {
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003007 big.setViewVisibility(R.id.actions, View.VISIBLE);
Daniel Sandler6387d2f2012-05-22 13:44:09 -04003008 big.setViewVisibility(R.id.action_divider, View.VISIBLE);
Daniel Sandler8680bf82012-05-15 16:52:52 -04003009 if (N>MAX_ACTION_BUTTONS) N=MAX_ACTION_BUTTONS;
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003010 for (int i=0; i<N; i++) {
3011 final RemoteViews button = generateActionButton(mActions.get(i));
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003012 big.addView(R.id.actions, button);
3013 }
3014 }
3015 return big;
3016 }
3017
Joe Onorato46439ce2010-11-19 13:56:21 -08003018 private RemoteViews makeContentView() {
3019 if (mContentView != null) {
3020 return mContentView;
3021 } else {
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003022 return applyStandardTemplate(getBaseLayoutResource());
Joe Onorato46439ce2010-11-19 13:56:21 -08003023 }
3024 }
3025
3026 private RemoteViews makeTickerView() {
3027 if (mTickerView != null) {
3028 return mTickerView;
Joe Onorato46439ce2010-11-19 13:56:21 -08003029 }
Dan Sandler5fcdf6e2014-07-18 11:31:15 -04003030 return null; // tickers are not created by default anymore
Joe Onorato46439ce2010-11-19 13:56:21 -08003031 }
3032
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003033 private RemoteViews makeBigContentView() {
3034 if (mActions.size() == 0) return null;
3035
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003036 return applyStandardTemplateWithActions(getBigBaseLayoutResource());
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003037 }
3038
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003039 private RemoteViews makeHeadsUpContentView() {
Chris Wren8fd39ec2014-02-27 17:43:26 -05003040 if (mActions.size() == 0) return null;
3041
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003042 return applyStandardTemplateWithActions(getBigBaseLayoutResource());
Chris Wren8fd39ec2014-02-27 17:43:26 -05003043 }
3044
3045
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003046 private RemoteViews generateActionButton(Action action) {
Daniel Sandler8680bf82012-05-15 16:52:52 -04003047 final boolean tombstone = (action.actionIntent == null);
Joe Malin8d40d042012-11-05 11:36:40 -08003048 RemoteViews button = new RemoteViews(mContext.getPackageName(),
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003049 tombstone ? getActionTombstoneLayoutResource()
3050 : getActionLayoutResource());
Chris Wren8749ac8a2013-12-03 14:31:01 -05003051 button.setTextViewCompoundDrawablesRelative(R.id.action0, action.icon, 0, 0, 0);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003052 button.setTextViewText(R.id.action0, processLegacyText(action.title));
Daniel Sandler8680bf82012-05-15 16:52:52 -04003053 if (!tombstone) {
Daniel Sandlere5518842012-05-10 16:20:40 -04003054 button.setOnClickPendingIntent(R.id.action0, action.actionIntent);
Daniel Sandlere5518842012-05-10 16:20:40 -04003055 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003056 button.setContentDescription(R.id.action0, action.title);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003057 processLegacyAction(action, button);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003058 return button;
3059 }
3060
Joe Onoratocb109a02011-01-18 17:57:41 -08003061 /**
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003062 * @return Whether we are currently building a notification from a legacy (an app that
Alan Viverette3cb07a462014-06-06 14:19:53 -07003063 * doesn't create material notifications by itself) app.
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003064 */
3065 private boolean isLegacy() {
Dan Sandler26e81cf2014-05-06 10:01:27 -04003066 return mColorUtil != null;
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003067 }
3068
3069 private void processLegacyAction(Action action, RemoteViews button) {
Dan Sandler05c362d2014-09-03 00:16:27 +02003070 if (!isLegacy() || mColorUtil.isGrayscaleIcon(mContext, action.icon)) {
Christoph Studer239f8352014-08-25 15:13:18 +02003071 button.setTextViewCompoundDrawablesRelativeColorFilter(R.id.action0, 0,
Alan Viverette4a357cd2015-03-18 18:37:18 -07003072 mContext.getColor(R.color.notification_action_color_filter),
Christoph Studer239f8352014-08-25 15:13:18 +02003073 PorterDuff.Mode.MULTIPLY);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003074 }
3075 }
3076
3077 private CharSequence processLegacyText(CharSequence charSequence) {
3078 if (isLegacy()) {
Dan Sandler26e81cf2014-05-06 10:01:27 -04003079 return mColorUtil.invertCharSequenceColors(charSequence);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003080 } else {
3081 return charSequence;
3082 }
3083 }
3084
Dan Sandler26e81cf2014-05-06 10:01:27 -04003085 /**
3086 * Apply any necessary background to smallIcons being used in the largeIcon spot.
3087 */
3088 private void processSmallIconAsLarge(int largeIconId, RemoteViews contentView) {
Jorim Jaggi92df1f22014-12-16 19:44:41 +01003089 if (!isLegacy()) {
3090 contentView.setDrawableParameters(R.id.icon, false, -1,
3091 0xFFFFFFFF,
3092 PorterDuff.Mode.SRC_ATOP, -1);
3093 }
Dan Sandler05c362d2014-09-03 00:16:27 +02003094 if (!isLegacy() || mColorUtil.isGrayscaleIcon(mContext, largeIconId)) {
Dan Sandler26e81cf2014-05-06 10:01:27 -04003095 applyLargeIconBackground(contentView);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003096 }
3097 }
3098
Dan Sandler26e81cf2014-05-06 10:01:27 -04003099 /**
3100 * Apply any necessary background to a largeIcon if it's a fake smallIcon (that is,
3101 * if it's grayscale).
3102 */
3103 // TODO: also check bounds, transparency, that sort of thing.
Christoph Studer239f8352014-08-25 15:13:18 +02003104 private void processLargeLegacyIcon(Bitmap largeIcon, RemoteViews contentView) {
Dan Sandler05c362d2014-09-03 00:16:27 +02003105 if (isLegacy() && mColorUtil.isGrayscaleIcon(largeIcon)) {
Dan Sandler26e81cf2014-05-06 10:01:27 -04003106 applyLargeIconBackground(contentView);
Dan Sandler190d58d2014-05-15 09:33:39 -04003107 } else {
3108 removeLargeIconBackground(contentView);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003109 }
3110 }
3111
Dan Sandler26e81cf2014-05-06 10:01:27 -04003112 /**
3113 * Add a colored circle behind the largeIcon slot.
3114 */
3115 private void applyLargeIconBackground(RemoteViews contentView) {
3116 contentView.setInt(R.id.icon, "setBackgroundResource",
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003117 R.drawable.notification_icon_legacy_bg);
Dan Sandler26e81cf2014-05-06 10:01:27 -04003118
3119 contentView.setDrawableParameters(
3120 R.id.icon,
3121 true,
3122 -1,
Jorim Jaggi74419312014-06-10 20:57:21 +02003123 resolveColor(),
Dan Sandler26e81cf2014-05-06 10:01:27 -04003124 PorterDuff.Mode.SRC_ATOP,
3125 -1);
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003126
3127 int padding = mContext.getResources().getDimensionPixelSize(
3128 R.dimen.notification_large_icon_circle_padding);
3129 contentView.setViewPadding(R.id.icon, padding, padding, padding, padding);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003130 }
3131
Dan Sandler190d58d2014-05-15 09:33:39 -04003132 private void removeLargeIconBackground(RemoteViews contentView) {
3133 contentView.setInt(R.id.icon, "setBackgroundResource", 0);
3134 }
3135
Dan Sandler26e81cf2014-05-06 10:01:27 -04003136 /**
3137 * Recolor small icons when used in the R.id.right_icon slot.
3138 */
Dan Sandler190d58d2014-05-15 09:33:39 -04003139 private void processSmallRightIcon(int smallIconDrawableId,
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003140 RemoteViews contentView) {
Jorim Jaggi92df1f22014-12-16 19:44:41 +01003141 if (!isLegacy()) {
Dan Sandler190d58d2014-05-15 09:33:39 -04003142 contentView.setDrawableParameters(R.id.right_icon, false, -1,
3143 0xFFFFFFFF,
3144 PorterDuff.Mode.SRC_ATOP, -1);
Jorim Jaggi92df1f22014-12-16 19:44:41 +01003145 }
3146 if (!isLegacy() || mColorUtil.isGrayscaleIcon(mContext, smallIconDrawableId)) {
Dan Sandler190d58d2014-05-15 09:33:39 -04003147 contentView.setInt(R.id.right_icon,
3148 "setBackgroundResource",
3149 R.drawable.notification_icon_legacy_bg);
3150
3151 contentView.setDrawableParameters(
3152 R.id.right_icon,
3153 true,
3154 -1,
Jorim Jaggi74419312014-06-10 20:57:21 +02003155 resolveColor(),
Dan Sandler190d58d2014-05-15 09:33:39 -04003156 PorterDuff.Mode.SRC_ATOP,
3157 -1);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003158 }
3159 }
3160
Jorim Jaggi74419312014-06-10 20:57:21 +02003161 private int sanitizeColor() {
3162 if (mColor != COLOR_DEFAULT) {
3163 mColor |= 0xFF000000; // no alpha for custom colors
3164 }
3165 return mColor;
3166 }
3167
Dan Sandler26e81cf2014-05-06 10:01:27 -04003168 private int resolveColor() {
3169 if (mColor == COLOR_DEFAULT) {
Alan Viverette4a357cd2015-03-18 18:37:18 -07003170 return mContext.getColor(R.color.notification_icon_bg_color);
Dan Sandler26e81cf2014-05-06 10:01:27 -04003171 }
3172 return mColor;
3173 }
3174
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003175 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003176 * Apply the unstyled operations and return a new {@link Notification} object.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04003177 * @hide
Joe Onoratocb109a02011-01-18 17:57:41 -08003178 */
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04003179 public Notification buildUnstyled() {
Joe Onorato46439ce2010-11-19 13:56:21 -08003180 Notification n = new Notification();
3181 n.when = mWhen;
3182 n.icon = mSmallIcon;
3183 n.iconLevel = mSmallIconLevel;
Joe Onorato8595a3d2010-11-19 18:12:07 -08003184 n.number = mNumber;
Dan Sandler26e81cf2014-05-06 10:01:27 -04003185
Jorim Jaggi74419312014-06-10 20:57:21 +02003186 n.color = sanitizeColor();
Dan Sandler26e81cf2014-05-06 10:01:27 -04003187
Christoph Studer4600f9b2014-07-22 22:44:43 +02003188 setBuilderContentView(n, makeContentView());
Joe Onorato46439ce2010-11-19 13:56:21 -08003189 n.contentIntent = mContentIntent;
3190 n.deleteIntent = mDeleteIntent;
3191 n.fullScreenIntent = mFullScreenIntent;
3192 n.tickerText = mTickerText;
3193 n.tickerView = makeTickerView();
3194 n.largeIcon = mLargeIcon;
3195 n.sound = mSound;
3196 n.audioStreamType = mAudioStreamType;
John Spurlockc0650f022014-07-19 13:22:39 -04003197 n.audioAttributes = mAudioAttributes;
Joe Onorato46439ce2010-11-19 13:56:21 -08003198 n.vibrate = mVibrate;
3199 n.ledARGB = mLedArgb;
3200 n.ledOnMS = mLedOnMs;
3201 n.ledOffMS = mLedOffMs;
3202 n.defaults = mDefaults;
3203 n.flags = mFlags;
Christoph Studer4600f9b2014-07-22 22:44:43 +02003204 setBuilderBigContentView(n, makeBigContentView());
3205 setBuilderHeadsUpContentView(n, makeHeadsUpContentView());
Daniel Sandler26c13432013-04-04 11:01:04 -04003206 if (mLedOnMs != 0 || mLedOffMs != 0) {
Joe Onorato8d0b6552010-11-22 16:09:29 -08003207 n.flags |= FLAG_SHOW_LIGHTS;
3208 }
3209 if ((mDefaults & DEFAULT_LIGHTS) != 0) {
3210 n.flags |= FLAG_SHOW_LIGHTS;
3211 }
John Spurlockfd7f1e02014-03-18 16:41:57 -04003212 n.category = mCategory;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003213 n.mGroupKey = mGroupKey;
3214 n.mSortKey = mSortKey;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003215 n.priority = mPriority;
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003216 if (mActions.size() > 0) {
3217 n.actions = new Action[mActions.size()];
3218 mActions.toArray(n.actions);
3219 }
Dan Sandler0bf2ed82013-12-21 23:33:41 -06003220 n.visibility = mVisibility;
3221
3222 if (mPublicVersion != null) {
3223 n.publicVersion = new Notification();
3224 mPublicVersion.cloneInto(n.publicVersion, true);
3225 }
Christoph Studer4600f9b2014-07-22 22:44:43 +02003226 // Note: If you're adding new fields, also update restoreFromNotitification().
Joe Onorato46439ce2010-11-19 13:56:21 -08003227 return n;
3228 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003229
3230 /**
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003231 * Capture, in the provided bundle, semantic information used in the construction of
3232 * this Notification object.
3233 * @hide
3234 */
Griff Hazen720042b2014-02-24 15:46:56 -08003235 public void populateExtras(Bundle extras) {
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003236 // Store original information used in the construction of this object
Kenny Guy8942bcd2014-09-08 21:09:47 +01003237 extras.putInt(EXTRA_ORIGINATING_USERID, mOriginatingUserId);
Kenny Guy77320062014-08-27 21:37:15 +01003238 extras.putParcelable(EXTRA_REBUILD_CONTEXT_APPLICATION_INFO,
3239 mContext.getApplicationInfo());
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003240 extras.putCharSequence(EXTRA_TITLE, mContentTitle);
3241 extras.putCharSequence(EXTRA_TEXT, mContentText);
3242 extras.putCharSequence(EXTRA_SUB_TEXT, mSubText);
3243 extras.putCharSequence(EXTRA_INFO_TEXT, mContentInfo);
3244 extras.putInt(EXTRA_SMALL_ICON, mSmallIcon);
3245 extras.putInt(EXTRA_PROGRESS, mProgress);
3246 extras.putInt(EXTRA_PROGRESS_MAX, mProgressMax);
3247 extras.putBoolean(EXTRA_PROGRESS_INDETERMINATE, mProgressIndeterminate);
3248 extras.putBoolean(EXTRA_SHOW_CHRONOMETER, mUseChronometer);
3249 extras.putBoolean(EXTRA_SHOW_WHEN, mShowWhen);
John Spurlockac08a472013-06-10 11:37:08 -04003250 if (mLargeIcon != null) {
3251 extras.putParcelable(EXTRA_LARGE_ICON, mLargeIcon);
3252 }
Chris Wrendde75302014-03-26 17:24:15 -04003253 if (!mPeople.isEmpty()) {
3254 extras.putStringArray(EXTRA_PEOPLE, mPeople.toArray(new String[mPeople.size()]));
3255 }
Christoph Studer4600f9b2014-07-22 22:44:43 +02003256 // NOTE: If you're adding new extras also update restoreFromNotification().
3257 }
3258
3259
3260 /**
3261 * @hide
3262 */
3263 public static void stripForDelivery(Notification n) {
3264 if (!STRIP_AND_REBUILD) {
3265 return;
3266 }
3267
3268 String templateClass = n.extras.getString(EXTRA_TEMPLATE);
3269 // Only strip views for known Styles because we won't know how to
3270 // re-create them otherwise.
3271 boolean stripViews = TextUtils.isEmpty(templateClass) ||
3272 getNotificationStyleClass(templateClass) != null;
3273
3274 boolean isStripped = false;
3275
3276 if (n.largeIcon != null && n.extras.containsKey(EXTRA_LARGE_ICON)) {
3277 // TODO: Would like to check for equality here, but if the notification
3278 // has been cloned, we can't.
3279 n.largeIcon = null;
3280 n.extras.putBoolean(EXTRA_REBUILD_LARGE_ICON, true);
3281 isStripped = true;
3282 }
3283 // Get rid of unmodified BuilderRemoteViews.
3284
3285 if (stripViews &&
3286 n.contentView instanceof BuilderRemoteViews &&
3287 n.extras.getInt(EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT, -1) ==
3288 n.contentView.getSequenceNumber()) {
3289 n.contentView = null;
3290 n.extras.putBoolean(EXTRA_REBUILD_CONTENT_VIEW, true);
3291 n.extras.remove(EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT);
3292 isStripped = true;
3293 }
3294 if (stripViews &&
3295 n.bigContentView instanceof BuilderRemoteViews &&
3296 n.extras.getInt(EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT, -1) ==
3297 n.bigContentView.getSequenceNumber()) {
3298 n.bigContentView = null;
3299 n.extras.putBoolean(EXTRA_REBUILD_BIG_CONTENT_VIEW, true);
3300 n.extras.remove(EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT);
3301 isStripped = true;
3302 }
3303 if (stripViews &&
3304 n.headsUpContentView instanceof BuilderRemoteViews &&
3305 n.extras.getInt(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT, -1) ==
3306 n.headsUpContentView.getSequenceNumber()) {
3307 n.headsUpContentView = null;
3308 n.extras.putBoolean(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW, true);
3309 n.extras.remove(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT);
3310 isStripped = true;
3311 }
3312
3313 if (isStripped) {
3314 n.extras.putBoolean(EXTRA_NEEDS_REBUILD, true);
3315 }
3316 }
3317
3318 /**
3319 * @hide
3320 */
3321 public static Notification rebuild(Context context, Notification n) {
3322 Bundle extras = n.extras;
3323 if (!extras.getBoolean(EXTRA_NEEDS_REBUILD)) return n;
3324 extras.remove(EXTRA_NEEDS_REBUILD);
3325
3326 // Re-create notification context so we can access app resources.
Kenny Guy77320062014-08-27 21:37:15 +01003327 ApplicationInfo applicationInfo = extras.getParcelable(
3328 EXTRA_REBUILD_CONTEXT_APPLICATION_INFO);
Christoph Studer4600f9b2014-07-22 22:44:43 +02003329 Context builderContext;
3330 try {
Kenny Guy77320062014-08-27 21:37:15 +01003331 builderContext = context.createApplicationContext(applicationInfo,
Christoph Studer4600f9b2014-07-22 22:44:43 +02003332 Context.CONTEXT_RESTRICTED);
3333 } catch (NameNotFoundException e) {
Kenny Guy77320062014-08-27 21:37:15 +01003334 Log.e(TAG, "ApplicationInfo " + applicationInfo + " not found");
Christoph Studer4600f9b2014-07-22 22:44:43 +02003335 builderContext = context; // try with our context
3336 }
3337
3338 Builder b = new Builder(builderContext, n);
3339 return b.rebuild();
3340 }
3341
3342 /**
3343 * Rebuilds the notification passed in to the rebuild-constructor
3344 * {@link #Builder(Context, Notification)}.
3345 *
3346 * <p>
3347 * Throws IllegalStateException when invoked on a Builder that isn't in rebuild mode.
3348 *
3349 * @hide
3350 */
3351 private Notification rebuild() {
3352 if (mRebuildNotification == null) {
3353 throw new IllegalStateException("rebuild() only valid when in 'rebuild' mode.");
3354 }
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003355 mHasThreeLines = hasThreeLines();
Christoph Studer4600f9b2014-07-22 22:44:43 +02003356
3357 Bundle extras = mRebuildNotification.extras;
3358
3359 if (extras.getBoolean(EXTRA_REBUILD_LARGE_ICON)) {
3360 mRebuildNotification.largeIcon = extras.getParcelable(EXTRA_LARGE_ICON);
3361 }
3362 extras.remove(EXTRA_REBUILD_LARGE_ICON);
3363
3364 if (extras.getBoolean(EXTRA_REBUILD_CONTENT_VIEW)) {
3365 setBuilderContentView(mRebuildNotification, makeContentView());
3366 if (mStyle != null) {
3367 mStyle.populateContentView(mRebuildNotification);
3368 }
3369 }
3370 extras.remove(EXTRA_REBUILD_CONTENT_VIEW);
3371
3372 if (extras.getBoolean(EXTRA_REBUILD_BIG_CONTENT_VIEW)) {
3373 setBuilderBigContentView(mRebuildNotification, makeBigContentView());
3374 if (mStyle != null) {
3375 mStyle.populateBigContentView(mRebuildNotification);
3376 }
3377 }
3378 extras.remove(EXTRA_REBUILD_BIG_CONTENT_VIEW);
3379
3380 if (extras.getBoolean(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW)) {
3381 setBuilderHeadsUpContentView(mRebuildNotification, makeHeadsUpContentView());
3382 if (mStyle != null) {
3383 mStyle.populateHeadsUpContentView(mRebuildNotification);
3384 }
3385 }
3386 extras.remove(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW);
3387
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003388 mHasThreeLines = false;
Christoph Studer4600f9b2014-07-22 22:44:43 +02003389 return mRebuildNotification;
3390 }
3391
3392 private static Class<? extends Style> getNotificationStyleClass(String templateClass) {
3393 Class<? extends Style>[] classes = new Class[]{
3394 BigTextStyle.class, BigPictureStyle.class, InboxStyle.class, MediaStyle.class};
3395 for (Class<? extends Style> innerClass : classes) {
3396 if (templateClass.equals(innerClass.getName())) {
3397 return innerClass;
3398 }
3399 }
3400 return null;
3401 }
3402
3403 private void setBuilderContentView(Notification n, RemoteViews contentView) {
3404 n.contentView = contentView;
3405 if (contentView instanceof BuilderRemoteViews) {
3406 mRebuildBundle.putInt(Builder.EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT,
3407 contentView.getSequenceNumber());
3408 }
3409 }
3410
3411 private void setBuilderBigContentView(Notification n, RemoteViews bigContentView) {
3412 n.bigContentView = bigContentView;
3413 if (bigContentView instanceof BuilderRemoteViews) {
3414 mRebuildBundle.putInt(Builder.EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT,
3415 bigContentView.getSequenceNumber());
3416 }
3417 }
3418
3419 private void setBuilderHeadsUpContentView(Notification n,
3420 RemoteViews headsUpContentView) {
3421 n.headsUpContentView = headsUpContentView;
3422 if (headsUpContentView instanceof BuilderRemoteViews) {
3423 mRebuildBundle.putInt(Builder.EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT,
3424 headsUpContentView.getSequenceNumber());
3425 }
3426 }
3427
3428 private void restoreFromNotification(Notification n) {
3429
3430 // Notification fields.
3431 mWhen = n.when;
3432 mSmallIcon = n.icon;
3433 mSmallIconLevel = n.iconLevel;
3434 mNumber = n.number;
3435
3436 mColor = n.color;
3437
3438 mContentView = n.contentView;
3439 mDeleteIntent = n.deleteIntent;
3440 mFullScreenIntent = n.fullScreenIntent;
3441 mTickerText = n.tickerText;
3442 mTickerView = n.tickerView;
3443 mLargeIcon = n.largeIcon;
3444 mSound = n.sound;
3445 mAudioStreamType = n.audioStreamType;
3446 mAudioAttributes = n.audioAttributes;
3447
3448 mVibrate = n.vibrate;
3449 mLedArgb = n.ledARGB;
3450 mLedOnMs = n.ledOnMS;
3451 mLedOffMs = n.ledOffMS;
3452 mDefaults = n.defaults;
3453 mFlags = n.flags;
3454
3455 mCategory = n.category;
3456 mGroupKey = n.mGroupKey;
3457 mSortKey = n.mSortKey;
3458 mPriority = n.priority;
3459 mActions.clear();
3460 if (n.actions != null) {
3461 Collections.addAll(mActions, n.actions);
3462 }
3463 mVisibility = n.visibility;
3464
3465 mPublicVersion = n.publicVersion;
3466
3467 // Extras.
3468 Bundle extras = n.extras;
Kenny Guy8942bcd2014-09-08 21:09:47 +01003469 mOriginatingUserId = extras.getInt(EXTRA_ORIGINATING_USERID);
Christoph Studer4600f9b2014-07-22 22:44:43 +02003470 mContentTitle = extras.getCharSequence(EXTRA_TITLE);
3471 mContentText = extras.getCharSequence(EXTRA_TEXT);
3472 mSubText = extras.getCharSequence(EXTRA_SUB_TEXT);
3473 mContentInfo = extras.getCharSequence(EXTRA_INFO_TEXT);
3474 mSmallIcon = extras.getInt(EXTRA_SMALL_ICON);
3475 mProgress = extras.getInt(EXTRA_PROGRESS);
3476 mProgressMax = extras.getInt(EXTRA_PROGRESS_MAX);
3477 mProgressIndeterminate = extras.getBoolean(EXTRA_PROGRESS_INDETERMINATE);
3478 mUseChronometer = extras.getBoolean(EXTRA_SHOW_CHRONOMETER);
3479 mShowWhen = extras.getBoolean(EXTRA_SHOW_WHEN);
3480 if (extras.containsKey(EXTRA_LARGE_ICON)) {
3481 mLargeIcon = extras.getParcelable(EXTRA_LARGE_ICON);
3482 }
3483 if (extras.containsKey(EXTRA_PEOPLE)) {
3484 mPeople.clear();
3485 Collections.addAll(mPeople, extras.getStringArray(EXTRA_PEOPLE));
3486 }
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003487 }
3488
3489 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003490 * @deprecated Use {@link #build()} instead.
3491 */
3492 @Deprecated
3493 public Notification getNotification() {
3494 return build();
3495 }
3496
3497 /**
3498 * Combine all of the options that have been set and return a new {@link Notification}
3499 * object.
3500 */
3501 public Notification build() {
Kenny Guy8942bcd2014-09-08 21:09:47 +01003502 mOriginatingUserId = mContext.getUserId();
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003503 mHasThreeLines = hasThreeLines();
Christoph Studer943aa672014-08-03 20:31:16 +02003504
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04003505 Notification n = buildUnstyled();
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003506
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003507 if (mStyle != null) {
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04003508 n = mStyle.buildStyled(n);
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003509 }
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003510
Christoph Studer4600f9b2014-07-22 22:44:43 +02003511 if (mExtras != null) {
3512 n.extras.putAll(mExtras);
3513 }
3514
3515 if (mRebuildBundle.size() > 0) {
3516 n.extras.putAll(mRebuildBundle);
3517 mRebuildBundle.clear();
3518 }
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003519
Griff Hazen720042b2014-02-24 15:46:56 -08003520 populateExtras(n.extras);
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003521 if (mStyle != null) {
3522 mStyle.addExtras(n.extras);
3523 }
3524
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003525 mHasThreeLines = false;
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003526 return n;
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003527 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05003528
3529 /**
3530 * Apply this Builder to an existing {@link Notification} object.
3531 *
3532 * @hide
3533 */
3534 public Notification buildInto(Notification n) {
Daniel Sandler1a497d32013-04-18 14:52:45 -04003535 build().cloneInto(n, true);
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05003536 return n;
3537 }
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003538
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003539 private int getBaseLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07003540 return R.layout.notification_template_material_base;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003541 }
3542
3543 private int getBigBaseLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07003544 return R.layout.notification_template_material_big_base;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003545 }
3546
3547 private int getBigPictureLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07003548 return R.layout.notification_template_material_big_picture;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003549 }
3550
3551 private int getBigTextLayoutResource() {
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003552 return R.layout.notification_template_material_big_text;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003553 }
3554
3555 private int getInboxLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07003556 return R.layout.notification_template_material_inbox;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003557 }
3558
3559 private int getActionLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07003560 return R.layout.notification_material_action;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003561 }
3562
3563 private int getActionTombstoneLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07003564 return R.layout.notification_material_action_tombstone;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003565 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003566 }
3567
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003568 /**
3569 * An object that can apply a rich notification style to a {@link Notification.Builder}
3570 * object.
3571 */
Griff Hazendfcb0802014-02-11 12:00:00 -08003572 public static abstract class Style {
Chris Wrend6297db2012-05-03 16:20:13 -04003573 private CharSequence mBigContentTitle;
Jorim Jaggi457a10d2014-09-08 16:18:23 +02003574
3575 /**
3576 * @hide
3577 */
3578 protected CharSequence mSummaryText = null;
3579
3580 /**
3581 * @hide
3582 */
3583 protected boolean mSummaryTextSet = false;
Chris Wrend6297db2012-05-03 16:20:13 -04003584
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003585 protected Builder mBuilder;
3586
Chris Wrend6297db2012-05-03 16:20:13 -04003587 /**
3588 * Overrides ContentTitle in the big form of the template.
3589 * This defaults to the value passed to setContentTitle().
3590 */
3591 protected void internalSetBigContentTitle(CharSequence title) {
3592 mBigContentTitle = title;
3593 }
3594
3595 /**
3596 * Set the first line of text after the detail section in the big form of the template.
3597 */
3598 protected void internalSetSummaryText(CharSequence cs) {
3599 mSummaryText = cs;
Daniel Sandler619738c2012-06-07 16:33:08 -04003600 mSummaryTextSet = true;
Chris Wrend6297db2012-05-03 16:20:13 -04003601 }
3602
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003603 public void setBuilder(Builder builder) {
3604 if (mBuilder != builder) {
3605 mBuilder = builder;
Daniel Sandlerc08dea22012-06-28 08:35:24 -07003606 if (mBuilder != null) {
3607 mBuilder.setStyle(this);
3608 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003609 }
3610 }
3611
Chris Wrend6297db2012-05-03 16:20:13 -04003612 protected void checkBuilder() {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003613 if (mBuilder == null) {
3614 throw new IllegalArgumentException("Style requires a valid Builder object");
3615 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003616 }
Chris Wrend6297db2012-05-03 16:20:13 -04003617
3618 protected RemoteViews getStandardView(int layoutId) {
3619 checkBuilder();
3620
Christoph Studer4600f9b2014-07-22 22:44:43 +02003621 // Nasty.
3622 CharSequence oldBuilderContentTitle = mBuilder.mContentTitle;
Chris Wrend6297db2012-05-03 16:20:13 -04003623 if (mBigContentTitle != null) {
3624 mBuilder.setContentTitle(mBigContentTitle);
3625 }
3626
Chris Wrend6297db2012-05-03 16:20:13 -04003627 RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(layoutId);
3628
Christoph Studer4600f9b2014-07-22 22:44:43 +02003629 mBuilder.mContentTitle = oldBuilderContentTitle;
3630
Chris Wrend6297db2012-05-03 16:20:13 -04003631 if (mBigContentTitle != null && mBigContentTitle.equals("")) {
3632 contentView.setViewVisibility(R.id.line1, View.GONE);
Chris Wren67dc9a02012-05-16 01:03:20 -04003633 } else {
3634 contentView.setViewVisibility(R.id.line1, View.VISIBLE);
Chris Wrend6297db2012-05-03 16:20:13 -04003635 }
3636
Daniel Sandler619738c2012-06-07 16:33:08 -04003637 // The last line defaults to the subtext, but can be replaced by mSummaryText
3638 final CharSequence overflowText =
3639 mSummaryTextSet ? mSummaryText
3640 : mBuilder.mSubText;
3641 if (overflowText != null) {
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003642 contentView.setTextViewText(R.id.text, mBuilder.processLegacyText(overflowText));
Daniel Sandler619738c2012-06-07 16:33:08 -04003643 contentView.setViewVisibility(R.id.overflow_divider, View.VISIBLE);
Daniel Sandler9f7936a2012-05-21 16:14:28 -04003644 contentView.setViewVisibility(R.id.line3, View.VISIBLE);
Daniel Sandler916ad912012-06-13 12:17:07 -04003645 } else {
Kenny Guy98193ea2014-07-24 19:54:37 +01003646 // Clear text in case we use the line to show the profile badge.
3647 contentView.setTextViewText(R.id.text, "");
Daniel Sandler916ad912012-06-13 12:17:07 -04003648 contentView.setViewVisibility(R.id.overflow_divider, View.GONE);
3649 contentView.setViewVisibility(R.id.line3, View.GONE);
Chris Wrend6297db2012-05-03 16:20:13 -04003650 }
3651
3652 return contentView;
3653 }
3654
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003655 /**
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003656 * Changes the padding of the first line such that the big and small content view have the
3657 * same top padding.
3658 *
3659 * @hide
3660 */
3661 protected void applyTopPadding(RemoteViews contentView) {
3662 int topPadding = Builder.calculateTopPadding(mBuilder.mContext,
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003663 mBuilder.mHasThreeLines,
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003664 mBuilder.mContext.getResources().getConfiguration().fontScale);
3665 contentView.setViewPadding(R.id.line1, 0, topPadding, 0, 0);
3666 }
3667
3668 /**
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003669 * @hide
3670 */
3671 public void addExtras(Bundle extras) {
3672 if (mSummaryTextSet) {
3673 extras.putCharSequence(EXTRA_SUMMARY_TEXT, mSummaryText);
3674 }
3675 if (mBigContentTitle != null) {
3676 extras.putCharSequence(EXTRA_TITLE_BIG, mBigContentTitle);
3677 }
Chris Wren91ad5632013-06-05 15:05:57 -04003678 extras.putString(EXTRA_TEMPLATE, this.getClass().getName());
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003679 }
3680
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04003681 /**
3682 * @hide
3683 */
Christoph Studer4600f9b2014-07-22 22:44:43 +02003684 protected void restoreFromExtras(Bundle extras) {
3685 if (extras.containsKey(EXTRA_SUMMARY_TEXT)) {
3686 mSummaryText = extras.getCharSequence(EXTRA_SUMMARY_TEXT);
3687 mSummaryTextSet = true;
3688 }
3689 if (extras.containsKey(EXTRA_TITLE_BIG)) {
3690 mBigContentTitle = extras.getCharSequence(EXTRA_TITLE_BIG);
3691 }
3692 }
3693
3694
3695 /**
3696 * @hide
3697 */
3698 public Notification buildStyled(Notification wip) {
3699 populateTickerView(wip);
3700 populateContentView(wip);
3701 populateBigContentView(wip);
3702 populateHeadsUpContentView(wip);
3703 return wip;
3704 }
3705
3706 // The following methods are split out so we can re-create notification partially.
3707 /**
3708 * @hide
3709 */
3710 protected void populateTickerView(Notification wip) {}
3711 /**
3712 * @hide
3713 */
3714 protected void populateContentView(Notification wip) {}
3715
3716 /**
3717 * @hide
3718 */
3719 protected void populateBigContentView(Notification wip) {}
3720
3721 /**
3722 * @hide
3723 */
3724 protected void populateHeadsUpContentView(Notification wip) {}
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04003725
3726 /**
3727 * Calls {@link android.app.Notification.Builder#build()} on the Builder this Style is
3728 * attached to.
3729 *
3730 * @return the fully constructed Notification.
3731 */
3732 public Notification build() {
3733 checkBuilder();
3734 return mBuilder.build();
3735 }
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02003736
3737 /**
3738 * @hide
3739 * @return true if the style positions the progress bar on the second line; false if the
3740 * style hides the progress bar
3741 */
3742 protected boolean hasProgress() {
3743 return true;
3744 }
Joe Onorato46439ce2010-11-19 13:56:21 -08003745 }
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003746
3747 /**
Daniel Sandler4dfbe832012-04-11 14:51:46 -04003748 * Helper class for generating large-format notifications that include a large image attachment.
Joe Malin8d40d042012-11-05 11:36:40 -08003749 *
Robert Ly91c5ce32014-06-08 15:37:00 -07003750 * Here's how you'd set the <code>BigPictureStyle</code> on a notification:
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003751 * <pre class="prettyprint">
Robert Ly91c5ce32014-06-08 15:37:00 -07003752 * Notification notif = new Notification.Builder(mContext)
3753 * .setContentTitle(&quot;New photo from &quot; + sender.toString())
3754 * .setContentText(subject)
3755 * .setSmallIcon(R.drawable.new_post)
3756 * .setLargeIcon(aBitmap)
3757 * .setStyle(new Notification.BigPictureStyle()
3758 * .bigPicture(aBigBitmap))
3759 * .build();
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003760 * </pre>
Joe Malin8d40d042012-11-05 11:36:40 -08003761 *
Daniel Sandler4dfbe832012-04-11 14:51:46 -04003762 * @see Notification#bigContentView
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003763 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003764 public static class BigPictureStyle extends Style {
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003765 private Bitmap mPicture;
Chris Wren3745a3d2012-05-22 15:11:52 -04003766 private Bitmap mBigLargeIcon;
3767 private boolean mBigLargeIconSet = false;
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003768
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003769 public BigPictureStyle() {
3770 }
3771
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003772 public BigPictureStyle(Builder builder) {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003773 setBuilder(builder);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003774 }
3775
Chris Wrend6297db2012-05-03 16:20:13 -04003776 /**
3777 * Overrides ContentTitle in the big form of the template.
3778 * This defaults to the value passed to setContentTitle().
3779 */
3780 public BigPictureStyle setBigContentTitle(CharSequence title) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04003781 internalSetBigContentTitle(safeCharSequence(title));
Chris Wrend6297db2012-05-03 16:20:13 -04003782 return this;
3783 }
3784
3785 /**
3786 * Set the first line of text after the detail section in the big form of the template.
3787 */
3788 public BigPictureStyle setSummaryText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04003789 internalSetSummaryText(safeCharSequence(cs));
Chris Wrend6297db2012-05-03 16:20:13 -04003790 return this;
3791 }
3792
Chris Wren0bd664d2012-08-01 13:56:56 -04003793 /**
3794 * Provide the bitmap to be used as the payload for the BigPicture notification.
3795 */
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003796 public BigPictureStyle bigPicture(Bitmap b) {
3797 mPicture = b;
3798 return this;
3799 }
3800
Chris Wren3745a3d2012-05-22 15:11:52 -04003801 /**
Chris Wren3745a3d2012-05-22 15:11:52 -04003802 * Override the large icon when the big notification is shown.
3803 */
3804 public BigPictureStyle bigLargeIcon(Bitmap b) {
3805 mBigLargeIconSet = true;
3806 mBigLargeIcon = b;
3807 return this;
3808 }
3809
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003810 private RemoteViews makeBigContentView() {
Christoph Studer5c510ee2014-12-15 16:32:27 +01003811
3812 // Replace mLargeIcon with mBigLargeIcon if mBigLargeIconSet
3813 // This covers the following cases:
3814 // 1. mBigLargeIconSet -> mBigLargeIcon (null or non-null) applies, overrides
3815 // mLargeIcon
3816 // 2. !mBigLargeIconSet -> mLargeIcon applies
3817 Bitmap oldLargeIcon = null;
3818 if (mBigLargeIconSet) {
3819 oldLargeIcon = mBuilder.mLargeIcon;
3820 mBuilder.mLargeIcon = mBigLargeIcon;
3821 }
3822
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003823 RemoteViews contentView = getStandardView(mBuilder.getBigPictureLayoutResource());
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003824
Christoph Studer5c510ee2014-12-15 16:32:27 +01003825 if (mBigLargeIconSet) {
3826 mBuilder.mLargeIcon = oldLargeIcon;
3827 }
3828
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003829 contentView.setImageViewBitmap(R.id.big_picture, mPicture);
3830
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003831 applyTopPadding(contentView);
3832
Kenny Guy98193ea2014-07-24 19:54:37 +01003833 boolean twoTextLines = mBuilder.mSubText != null && mBuilder.mContentText != null;
3834 mBuilder.addProfileBadge(contentView,
3835 twoTextLines ? R.id.profile_badge_line2 : R.id.profile_badge_line3);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003836 return contentView;
3837 }
3838
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003839 /**
3840 * @hide
3841 */
3842 public void addExtras(Bundle extras) {
3843 super.addExtras(extras);
3844
3845 if (mBigLargeIconSet) {
3846 extras.putParcelable(EXTRA_LARGE_ICON_BIG, mBigLargeIcon);
3847 }
3848 extras.putParcelable(EXTRA_PICTURE, mPicture);
3849 }
3850
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04003851 /**
3852 * @hide
3853 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003854 @Override
Christoph Studer4600f9b2014-07-22 22:44:43 +02003855 protected void restoreFromExtras(Bundle extras) {
3856 super.restoreFromExtras(extras);
3857
3858 if (extras.containsKey(EXTRA_LARGE_ICON_BIG)) {
Christoph Studer5c510ee2014-12-15 16:32:27 +01003859 mBigLargeIconSet = true;
Christoph Studer4600f9b2014-07-22 22:44:43 +02003860 mBigLargeIcon = extras.getParcelable(EXTRA_LARGE_ICON_BIG);
Chris Wren3745a3d2012-05-22 15:11:52 -04003861 }
Christoph Studer4600f9b2014-07-22 22:44:43 +02003862 mPicture = extras.getParcelable(EXTRA_PICTURE);
3863 }
3864
3865 /**
3866 * @hide
3867 */
3868 @Override
3869 public void populateBigContentView(Notification wip) {
3870 mBuilder.setBuilderBigContentView(wip, makeBigContentView());
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003871 }
3872 }
3873
3874 /**
Daniel Sandler4dfbe832012-04-11 14:51:46 -04003875 * Helper class for generating large-format notifications that include a lot of text.
Joe Malin8d40d042012-11-05 11:36:40 -08003876 *
Robert Ly91c5ce32014-06-08 15:37:00 -07003877 * Here's how you'd set the <code>BigTextStyle</code> on a notification:
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003878 * <pre class="prettyprint">
Robert Ly91c5ce32014-06-08 15:37:00 -07003879 * Notification notif = new Notification.Builder(mContext)
3880 * .setContentTitle(&quot;New mail from &quot; + sender.toString())
3881 * .setContentText(subject)
3882 * .setSmallIcon(R.drawable.new_mail)
3883 * .setLargeIcon(aBitmap)
3884 * .setStyle(new Notification.BigTextStyle()
3885 * .bigText(aVeryLongString))
3886 * .build();
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003887 * </pre>
Joe Malin8d40d042012-11-05 11:36:40 -08003888 *
Daniel Sandler4dfbe832012-04-11 14:51:46 -04003889 * @see Notification#bigContentView
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003890 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003891 public static class BigTextStyle extends Style {
Jorim Jaggi457a10d2014-09-08 16:18:23 +02003892
3893 private static final int MAX_LINES = 13;
3894 private static final int LINES_CONSUMED_BY_ACTIONS = 3;
3895 private static final int LINES_CONSUMED_BY_SUMMARY = 2;
3896
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003897 private CharSequence mBigText;
3898
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003899 public BigTextStyle() {
3900 }
3901
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003902 public BigTextStyle(Builder builder) {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003903 setBuilder(builder);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003904 }
3905
Chris Wrend6297db2012-05-03 16:20:13 -04003906 /**
3907 * Overrides ContentTitle in the big form of the template.
3908 * This defaults to the value passed to setContentTitle().
3909 */
3910 public BigTextStyle setBigContentTitle(CharSequence title) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04003911 internalSetBigContentTitle(safeCharSequence(title));
Chris Wrend6297db2012-05-03 16:20:13 -04003912 return this;
3913 }
3914
3915 /**
3916 * Set the first line of text after the detail section in the big form of the template.
3917 */
3918 public BigTextStyle setSummaryText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04003919 internalSetSummaryText(safeCharSequence(cs));
Chris Wrend6297db2012-05-03 16:20:13 -04003920 return this;
3921 }
3922
Chris Wren0bd664d2012-08-01 13:56:56 -04003923 /**
3924 * Provide the longer text to be displayed in the big form of the
3925 * template in place of the content text.
3926 */
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003927 public BigTextStyle bigText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04003928 mBigText = safeCharSequence(cs);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003929 return this;
3930 }
3931
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003932 /**
3933 * @hide
3934 */
3935 public void addExtras(Bundle extras) {
3936 super.addExtras(extras);
3937
Christoph Studer4600f9b2014-07-22 22:44:43 +02003938 extras.putCharSequence(EXTRA_BIG_TEXT, mBigText);
3939 }
3940
3941 /**
3942 * @hide
3943 */
3944 @Override
3945 protected void restoreFromExtras(Bundle extras) {
3946 super.restoreFromExtras(extras);
3947
3948 mBigText = extras.getCharSequence(EXTRA_BIG_TEXT);
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003949 }
3950
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003951 private RemoteViews makeBigContentView() {
Christoph Studer4600f9b2014-07-22 22:44:43 +02003952
3953 // Nasty
3954 CharSequence oldBuilderContentText = mBuilder.mContentText;
Daniel Sandler6387d2f2012-05-22 13:44:09 -04003955 mBuilder.mContentText = null;
Daniel Sandler916ad912012-06-13 12:17:07 -04003956
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003957 RemoteViews contentView = getStandardView(mBuilder.getBigTextLayoutResource());
Joe Malin8d40d042012-11-05 11:36:40 -08003958
Christoph Studer4600f9b2014-07-22 22:44:43 +02003959 mBuilder.mContentText = oldBuilderContentText;
3960
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003961 contentView.setTextViewText(R.id.big_text, mBuilder.processLegacyText(mBigText));
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003962 contentView.setViewVisibility(R.id.big_text, View.VISIBLE);
Jorim Jaggi457a10d2014-09-08 16:18:23 +02003963 contentView.setInt(R.id.big_text, "setMaxLines", calculateMaxLines());
Chris Wren3c5f92432012-05-04 16:31:17 -04003964 contentView.setViewVisibility(R.id.text2, View.GONE);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003965
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003966 applyTopPadding(contentView);
3967
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003968 mBuilder.shrinkLine3Text(contentView);
3969
Kenny Guy98193ea2014-07-24 19:54:37 +01003970 mBuilder.addProfileBadge(contentView, R.id.profile_badge_large_template);
3971
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003972 return contentView;
3973 }
3974
Jorim Jaggi457a10d2014-09-08 16:18:23 +02003975 private int calculateMaxLines() {
3976 int lineCount = MAX_LINES;
3977 boolean hasActions = mBuilder.mActions.size() > 0;
3978 boolean hasSummary = (mSummaryTextSet ? mSummaryText : mBuilder.mSubText) != null;
3979 if (hasActions) {
3980 lineCount -= LINES_CONSUMED_BY_ACTIONS;
3981 }
3982 if (hasSummary) {
3983 lineCount -= LINES_CONSUMED_BY_SUMMARY;
3984 }
3985
3986 // If we have less top padding at the top, we can fit less lines.
3987 if (!mBuilder.mHasThreeLines) {
3988 lineCount--;
3989 }
3990 return lineCount;
3991 }
3992
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04003993 /**
3994 * @hide
3995 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003996 @Override
Christoph Studer4600f9b2014-07-22 22:44:43 +02003997 public void populateBigContentView(Notification wip) {
3998 mBuilder.setBuilderBigContentView(wip, makeBigContentView());
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003999 }
4000 }
Daniel Sandler879c5e02012-04-17 16:46:51 -04004001
4002 /**
4003 * Helper class for generating large-format notifications that include a list of (up to 5) strings.
Joe Malin8d40d042012-11-05 11:36:40 -08004004 *
Robert Ly91c5ce32014-06-08 15:37:00 -07004005 * Here's how you'd set the <code>InboxStyle</code> on a notification:
Daniel Sandler879c5e02012-04-17 16:46:51 -04004006 * <pre class="prettyprint">
Robert Ly91c5ce32014-06-08 15:37:00 -07004007 * Notification notif = new Notification.Builder(mContext)
4008 * .setContentTitle(&quot;5 New mails from &quot; + sender.toString())
4009 * .setContentText(subject)
4010 * .setSmallIcon(R.drawable.new_mail)
4011 * .setLargeIcon(aBitmap)
4012 * .setStyle(new Notification.InboxStyle()
4013 * .addLine(str1)
4014 * .addLine(str2)
4015 * .setContentTitle(&quot;&quot;)
4016 * .setSummaryText(&quot;+3 more&quot;))
4017 * .build();
Daniel Sandler879c5e02012-04-17 16:46:51 -04004018 * </pre>
Joe Malin8d40d042012-11-05 11:36:40 -08004019 *
Daniel Sandler879c5e02012-04-17 16:46:51 -04004020 * @see Notification#bigContentView
4021 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004022 public static class InboxStyle extends Style {
Daniel Sandler879c5e02012-04-17 16:46:51 -04004023 private ArrayList<CharSequence> mTexts = new ArrayList<CharSequence>(5);
4024
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004025 public InboxStyle() {
4026 }
4027
Daniel Sandler879c5e02012-04-17 16:46:51 -04004028 public InboxStyle(Builder builder) {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004029 setBuilder(builder);
Daniel Sandler879c5e02012-04-17 16:46:51 -04004030 }
4031
Chris Wrend6297db2012-05-03 16:20:13 -04004032 /**
4033 * Overrides ContentTitle in the big form of the template.
4034 * This defaults to the value passed to setContentTitle().
4035 */
4036 public InboxStyle setBigContentTitle(CharSequence title) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04004037 internalSetBigContentTitle(safeCharSequence(title));
Chris Wrend6297db2012-05-03 16:20:13 -04004038 return this;
4039 }
4040
4041 /**
4042 * Set the first line of text after the detail section in the big form of the template.
4043 */
4044 public InboxStyle setSummaryText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04004045 internalSetSummaryText(safeCharSequence(cs));
Chris Wrend6297db2012-05-03 16:20:13 -04004046 return this;
4047 }
4048
Chris Wren0bd664d2012-08-01 13:56:56 -04004049 /**
4050 * Append a line to the digest section of the Inbox notification.
4051 */
Daniel Sandler879c5e02012-04-17 16:46:51 -04004052 public InboxStyle addLine(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04004053 mTexts.add(safeCharSequence(cs));
Daniel Sandler879c5e02012-04-17 16:46:51 -04004054 return this;
4055 }
4056
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004057 /**
4058 * @hide
4059 */
4060 public void addExtras(Bundle extras) {
4061 super.addExtras(extras);
Christoph Studer4600f9b2014-07-22 22:44:43 +02004062
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004063 CharSequence[] a = new CharSequence[mTexts.size()];
4064 extras.putCharSequenceArray(EXTRA_TEXT_LINES, mTexts.toArray(a));
4065 }
4066
Christoph Studer4600f9b2014-07-22 22:44:43 +02004067 /**
4068 * @hide
4069 */
4070 @Override
4071 protected void restoreFromExtras(Bundle extras) {
4072 super.restoreFromExtras(extras);
4073
4074 mTexts.clear();
4075 if (extras.containsKey(EXTRA_TEXT_LINES)) {
4076 Collections.addAll(mTexts, extras.getCharSequenceArray(EXTRA_TEXT_LINES));
4077 }
4078 }
4079
Daniel Sandler879c5e02012-04-17 16:46:51 -04004080 private RemoteViews makeBigContentView() {
Daniel Sandler619738c2012-06-07 16:33:08 -04004081 // Remove the content text so line3 disappears unless you have a summary
Christoph Studer4600f9b2014-07-22 22:44:43 +02004082
4083 // Nasty
4084 CharSequence oldBuilderContentText = mBuilder.mContentText;
Daniel Sandler619738c2012-06-07 16:33:08 -04004085 mBuilder.mContentText = null;
Christoph Studer4600f9b2014-07-22 22:44:43 +02004086
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004087 RemoteViews contentView = getStandardView(mBuilder.getInboxLayoutResource());
Daniel Sandler619738c2012-06-07 16:33:08 -04004088
Christoph Studer4600f9b2014-07-22 22:44:43 +02004089 mBuilder.mContentText = oldBuilderContentText;
4090
Chris Wrend6297db2012-05-03 16:20:13 -04004091 contentView.setViewVisibility(R.id.text2, View.GONE);
Daniel Sandler879c5e02012-04-17 16:46:51 -04004092
Chris Wrend6297db2012-05-03 16:20:13 -04004093 int[] rowIds = {R.id.inbox_text0, R.id.inbox_text1, R.id.inbox_text2, R.id.inbox_text3,
Chris Wren29bb6d92012-05-17 18:09:42 -04004094 R.id.inbox_text4, R.id.inbox_text5, R.id.inbox_text6};
Chris Wrend6297db2012-05-03 16:20:13 -04004095
Chris Wren4ed80d52012-05-17 09:30:03 -04004096 // Make sure all rows are gone in case we reuse a view.
4097 for (int rowId : rowIds) {
4098 contentView.setViewVisibility(rowId, View.GONE);
4099 }
4100
Jorim Jaggi445d3c02014-08-19 22:33:42 +02004101 final boolean largeText =
4102 mBuilder.mContext.getResources().getConfiguration().fontScale > 1f;
4103 final float subTextSize = mBuilder.mContext.getResources().getDimensionPixelSize(
4104 R.dimen.notification_subtext_size);
Daniel Sandler879c5e02012-04-17 16:46:51 -04004105 int i=0;
4106 while (i < mTexts.size() && i < rowIds.length) {
4107 CharSequence str = mTexts.get(i);
4108 if (str != null && !str.equals("")) {
4109 contentView.setViewVisibility(rowIds[i], View.VISIBLE);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004110 contentView.setTextViewText(rowIds[i], mBuilder.processLegacyText(str));
Jorim Jaggi445d3c02014-08-19 22:33:42 +02004111 if (largeText) {
4112 contentView.setTextViewTextSize(rowIds[i], TypedValue.COMPLEX_UNIT_PX,
4113 subTextSize);
4114 }
Daniel Sandler879c5e02012-04-17 16:46:51 -04004115 }
4116 i++;
4117 }
4118
Chris Wren683ab002012-09-20 10:35:54 -04004119 contentView.setViewVisibility(R.id.inbox_end_pad,
4120 mTexts.size() > 0 ? View.VISIBLE : View.GONE);
4121
4122 contentView.setViewVisibility(R.id.inbox_more,
4123 mTexts.size() > rowIds.length ? View.VISIBLE : View.GONE);
Chris Wren29bb6d92012-05-17 18:09:42 -04004124
Jorim Jaggi445d3c02014-08-19 22:33:42 +02004125 applyTopPadding(contentView);
4126
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02004127 mBuilder.shrinkLine3Text(contentView);
4128
Kenny Guy98193ea2014-07-24 19:54:37 +01004129 mBuilder.addProfileBadge(contentView, R.id.profile_badge_large_template);
4130
Daniel Sandler879c5e02012-04-17 16:46:51 -04004131 return contentView;
4132 }
4133
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04004134 /**
4135 * @hide
4136 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004137 @Override
Christoph Studer4600f9b2014-07-22 22:44:43 +02004138 public void populateBigContentView(Notification wip) {
4139 mBuilder.setBuilderBigContentView(wip, makeBigContentView());
Daniel Sandler879c5e02012-04-17 16:46:51 -04004140 }
4141 }
Dan Sandler842dd772014-05-15 09:36:47 -04004142
4143 /**
4144 * Notification style for media playback notifications.
4145 *
4146 * In the expanded form, {@link Notification#bigContentView}, up to 5
4147 * {@link Notification.Action}s specified with
4148 * {@link Notification.Builder#addAction(int, CharSequence, PendingIntent) addAction} will be
4149 * shown as icon-only pushbuttons, suitable for transport controls. The Bitmap given to
4150 * {@link Notification.Builder#setLargeIcon(android.graphics.Bitmap) setLargeIcon()} will be
4151 * treated as album artwork.
4152 *
4153 * Unlike the other styles provided here, MediaStyle can also modify the standard-size
4154 * {@link Notification#contentView}; by providing action indices to
Christoph Studerfde6f4d2014-12-12 13:23:26 +01004155 * {@link #setShowActionsInCompactView(int...)} you can promote up to 3 actions to be displayed
Dan Sandler842dd772014-05-15 09:36:47 -04004156 * in the standard view alongside the usual content.
4157 *
Bryan Mawhinney6be8de32014-07-18 10:35:12 +01004158 * Notifications created with MediaStyle will have their category set to
4159 * {@link Notification#CATEGORY_TRANSPORT CATEGORY_TRANSPORT} unless you set a different
4160 * category using {@link Notification.Builder#setCategory(String) setCategory()}.
4161 *
Jeff Browndba34ba2014-06-24 20:46:03 -07004162 * Finally, if you attach a {@link android.media.session.MediaSession.Token} using
4163 * {@link android.app.Notification.MediaStyle#setMediaSession(MediaSession.Token)},
Dan Sandler842dd772014-05-15 09:36:47 -04004164 * the System UI can identify this as a notification representing an active media session
4165 * and respond accordingly (by showing album artwork in the lockscreen, for example).
4166 *
4167 * To use this style with your Notification, feed it to
4168 * {@link Notification.Builder#setStyle(android.app.Notification.Style)} like so:
4169 * <pre class="prettyprint">
4170 * Notification noti = new Notification.Builder()
4171 * .setSmallIcon(R.drawable.ic_stat_player)
Christoph Studere935fe92014-11-24 14:18:06 +01004172 * .setContentTitle(&quot;Track title&quot;)
4173 * .setContentText(&quot;Artist - Album&quot;)
4174 * .setLargeIcon(albumArtBitmap))
Bryan Mawhinney6be8de32014-07-18 10:35:12 +01004175 * .setStyle(<b>new Notification.MediaStyle()</b>
4176 * .setMediaSession(mySession))
Dan Sandler842dd772014-05-15 09:36:47 -04004177 * .build();
4178 * </pre>
4179 *
4180 * @see Notification#bigContentView
4181 */
4182 public static class MediaStyle extends Style {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004183 static final int MAX_MEDIA_BUTTONS_IN_COMPACT = 3;
Dan Sandler842dd772014-05-15 09:36:47 -04004184 static final int MAX_MEDIA_BUTTONS = 5;
4185
4186 private int[] mActionsToShowInCompact = null;
Jeff Browndba34ba2014-06-24 20:46:03 -07004187 private MediaSession.Token mToken;
Dan Sandler842dd772014-05-15 09:36:47 -04004188
4189 public MediaStyle() {
4190 }
4191
4192 public MediaStyle(Builder builder) {
4193 setBuilder(builder);
4194 }
4195
4196 /**
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004197 * Request up to 3 actions (by index in the order of addition) to be shown in the compact
Dan Sandler842dd772014-05-15 09:36:47 -04004198 * notification view.
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004199 *
4200 * @param actions the indices of the actions to show in the compact notification view
Dan Sandler842dd772014-05-15 09:36:47 -04004201 */
4202 public MediaStyle setShowActionsInCompactView(int...actions) {
4203 mActionsToShowInCompact = actions;
4204 return this;
4205 }
4206
4207 /**
Jeff Browndba34ba2014-06-24 20:46:03 -07004208 * Attach a {@link android.media.session.MediaSession.Token} to this Notification
4209 * to provide additional playback information and control to the SystemUI.
Dan Sandler842dd772014-05-15 09:36:47 -04004210 */
Jeff Browndba34ba2014-06-24 20:46:03 -07004211 public MediaStyle setMediaSession(MediaSession.Token token) {
Dan Sandler842dd772014-05-15 09:36:47 -04004212 mToken = token;
4213 return this;
4214 }
4215
Christoph Studer4600f9b2014-07-22 22:44:43 +02004216 /**
4217 * @hide
4218 */
Dan Sandler842dd772014-05-15 09:36:47 -04004219 @Override
4220 public Notification buildStyled(Notification wip) {
Christoph Studer4600f9b2014-07-22 22:44:43 +02004221 super.buildStyled(wip);
Bryan Mawhinney6be8de32014-07-18 10:35:12 +01004222 if (wip.category == null) {
4223 wip.category = Notification.CATEGORY_TRANSPORT;
4224 }
Dan Sandler842dd772014-05-15 09:36:47 -04004225 return wip;
4226 }
4227
Christoph Studer4600f9b2014-07-22 22:44:43 +02004228 /**
4229 * @hide
4230 */
4231 @Override
4232 public void populateContentView(Notification wip) {
4233 mBuilder.setBuilderContentView(wip, makeMediaContentView());
4234 }
4235
4236 /**
4237 * @hide
4238 */
4239 @Override
4240 public void populateBigContentView(Notification wip) {
4241 mBuilder.setBuilderBigContentView(wip, makeMediaBigContentView());
4242 }
4243
Dan Sandler842dd772014-05-15 09:36:47 -04004244 /** @hide */
4245 @Override
4246 public void addExtras(Bundle extras) {
4247 super.addExtras(extras);
4248
4249 if (mToken != null) {
4250 extras.putParcelable(EXTRA_MEDIA_SESSION, mToken);
4251 }
Bryan Mawhinneye191f902014-07-22 12:50:09 +01004252 if (mActionsToShowInCompact != null) {
4253 extras.putIntArray(EXTRA_COMPACT_ACTIONS, mActionsToShowInCompact);
4254 }
Dan Sandler842dd772014-05-15 09:36:47 -04004255 }
4256
Christoph Studer4600f9b2014-07-22 22:44:43 +02004257 /**
4258 * @hide
4259 */
4260 @Override
4261 protected void restoreFromExtras(Bundle extras) {
4262 super.restoreFromExtras(extras);
4263
4264 if (extras.containsKey(EXTRA_MEDIA_SESSION)) {
4265 mToken = extras.getParcelable(EXTRA_MEDIA_SESSION);
4266 }
4267 if (extras.containsKey(EXTRA_COMPACT_ACTIONS)) {
4268 mActionsToShowInCompact = extras.getIntArray(EXTRA_COMPACT_ACTIONS);
4269 }
4270 }
4271
Dan Sandler842dd772014-05-15 09:36:47 -04004272 private RemoteViews generateMediaActionButton(Action action) {
4273 final boolean tombstone = (action.actionIntent == null);
4274 RemoteViews button = new RemoteViews(mBuilder.mContext.getPackageName(),
Alan Viverette3cb07a462014-06-06 14:19:53 -07004275 R.layout.notification_material_media_action);
Dan Sandler842dd772014-05-15 09:36:47 -04004276 button.setImageViewResource(R.id.action0, action.icon);
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004277 button.setDrawableParameters(R.id.action0, false, -1,
4278 0xFFFFFFFF,
4279 PorterDuff.Mode.SRC_ATOP, -1);
Dan Sandler842dd772014-05-15 09:36:47 -04004280 if (!tombstone) {
4281 button.setOnClickPendingIntent(R.id.action0, action.actionIntent);
4282 }
4283 button.setContentDescription(R.id.action0, action.title);
4284 return button;
4285 }
4286
4287 private RemoteViews makeMediaContentView() {
4288 RemoteViews view = mBuilder.applyStandardTemplate(
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004289 R.layout.notification_template_material_media, false /* hasProgress */);
Dan Sandler842dd772014-05-15 09:36:47 -04004290
4291 final int numActions = mBuilder.mActions.size();
4292 final int N = mActionsToShowInCompact == null
4293 ? 0
4294 : Math.min(mActionsToShowInCompact.length, MAX_MEDIA_BUTTONS_IN_COMPACT);
4295 if (N > 0) {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004296 view.removeAllViews(com.android.internal.R.id.media_actions);
Dan Sandler842dd772014-05-15 09:36:47 -04004297 for (int i = 0; i < N; i++) {
4298 if (i >= numActions) {
4299 throw new IllegalArgumentException(String.format(
4300 "setShowActionsInCompactView: action %d out of bounds (max %d)",
4301 i, numActions - 1));
4302 }
4303
4304 final Action action = mBuilder.mActions.get(mActionsToShowInCompact[i]);
4305 final RemoteViews button = generateMediaActionButton(action);
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004306 view.addView(com.android.internal.R.id.media_actions, button);
Dan Sandler842dd772014-05-15 09:36:47 -04004307 }
4308 }
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004309 styleText(view);
4310 hideRightIcon(view);
Dan Sandler842dd772014-05-15 09:36:47 -04004311 return view;
4312 }
4313
4314 private RemoteViews makeMediaBigContentView() {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004315 final int actionCount = Math.min(mBuilder.mActions.size(), MAX_MEDIA_BUTTONS);
4316 RemoteViews big = mBuilder.applyStandardTemplate(getBigLayoutResource(actionCount),
4317 false /* hasProgress */);
Dan Sandler842dd772014-05-15 09:36:47 -04004318
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004319 if (actionCount > 0) {
4320 big.removeAllViews(com.android.internal.R.id.media_actions);
4321 for (int i = 0; i < actionCount; i++) {
Dan Sandler842dd772014-05-15 09:36:47 -04004322 final RemoteViews button = generateMediaActionButton(mBuilder.mActions.get(i));
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004323 big.addView(com.android.internal.R.id.media_actions, button);
Dan Sandler842dd772014-05-15 09:36:47 -04004324 }
4325 }
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004326 styleText(big);
4327 hideRightIcon(big);
4328 applyTopPadding(big);
4329 big.setViewVisibility(android.R.id.progress, View.GONE);
Dan Sandler842dd772014-05-15 09:36:47 -04004330 return big;
4331 }
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004332
4333 private int getBigLayoutResource(int actionCount) {
4334 if (actionCount <= 3) {
4335 return R.layout.notification_template_material_big_media_narrow;
4336 } else {
4337 return R.layout.notification_template_material_big_media;
4338 }
4339 }
4340
4341 private void hideRightIcon(RemoteViews contentView) {
4342 contentView.setViewVisibility(R.id.right_icon, View.GONE);
4343 }
4344
4345 /**
4346 * Applies the special text colors for media notifications to all text views.
4347 */
4348 private void styleText(RemoteViews contentView) {
Alan Viverette4a357cd2015-03-18 18:37:18 -07004349 int primaryColor = mBuilder.mContext.getColor(
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004350 R.color.notification_media_primary_color);
Alan Viverette4a357cd2015-03-18 18:37:18 -07004351 int secondaryColor = mBuilder.mContext.getColor(
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004352 R.color.notification_media_secondary_color);
4353 contentView.setTextColor(R.id.title, primaryColor);
4354 if (mBuilder.showsTimeOrChronometer()) {
4355 if (mBuilder.mUseChronometer) {
4356 contentView.setTextColor(R.id.chronometer, secondaryColor);
4357 } else {
4358 contentView.setTextColor(R.id.time, secondaryColor);
4359 }
4360 }
4361 contentView.setTextColor(R.id.text2, secondaryColor);
4362 contentView.setTextColor(R.id.text, secondaryColor);
4363 contentView.setTextColor(R.id.info, secondaryColor);
4364 }
4365
4366 /**
4367 * @hide
4368 */
4369 @Override
4370 protected boolean hasProgress() {
4371 return false;
4372 }
Dan Sandler842dd772014-05-15 09:36:47 -04004373 }
Griff Hazen61a9e862014-05-22 16:05:19 -07004374
Christoph Studer4600f9b2014-07-22 22:44:43 +02004375 // When adding a new Style subclass here, don't forget to update
4376 // Builder.getNotificationStyleClass.
4377
Griff Hazen61a9e862014-05-22 16:05:19 -07004378 /**
4379 * Extender interface for use with {@link Builder#extend}. Extenders may be used to add
4380 * metadata or change options on a notification builder.
4381 */
4382 public interface Extender {
4383 /**
4384 * Apply this extender to a notification builder.
4385 * @param builder the builder to be modified.
4386 * @return the build object for chaining.
4387 */
4388 public Builder extend(Builder builder);
4389 }
4390
4391 /**
4392 * Helper class to add wearable extensions to notifications.
4393 * <p class="note"> See
4394 * <a href="{@docRoot}wear/notifications/creating.html">Creating Notifications
4395 * for Android Wear</a> for more information on how to use this class.
4396 * <p>
4397 * To create a notification with wearable extensions:
4398 * <ol>
4399 * <li>Create a {@link android.app.Notification.Builder}, setting any desired
4400 * properties.
4401 * <li>Create a {@link android.app.Notification.WearableExtender}.
4402 * <li>Set wearable-specific properties using the
4403 * {@code add} and {@code set} methods of {@link android.app.Notification.WearableExtender}.
4404 * <li>Call {@link android.app.Notification.Builder#extend} to apply the extensions to a
4405 * notification.
4406 * <li>Post the notification to the notification system with the
4407 * {@code NotificationManager.notify(...)} methods.
4408 * </ol>
4409 *
4410 * <pre class="prettyprint">
4411 * Notification notif = new Notification.Builder(mContext)
4412 * .setContentTitle(&quot;New mail from &quot; + sender.toString())
4413 * .setContentText(subject)
4414 * .setSmallIcon(R.drawable.new_mail)
4415 * .extend(new Notification.WearableExtender()
4416 * .setContentIcon(R.drawable.new_mail))
4417 * .build();
4418 * NotificationManager notificationManger =
4419 * (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
4420 * notificationManger.notify(0, notif);</pre>
4421 *
4422 * <p>Wearable extensions can be accessed on an existing notification by using the
4423 * {@code WearableExtender(Notification)} constructor,
4424 * and then using the {@code get} methods to access values.
4425 *
4426 * <pre class="prettyprint">
4427 * Notification.WearableExtender wearableExtender = new Notification.WearableExtender(
4428 * notification);
Griff Hazen14f57992014-05-26 09:07:14 -07004429 * List&lt;Notification&gt; pages = wearableExtender.getPages();</pre>
Griff Hazen61a9e862014-05-22 16:05:19 -07004430 */
4431 public static final class WearableExtender implements Extender {
4432 /**
4433 * Sentinel value for an action index that is unset.
4434 */
4435 public static final int UNSET_ACTION_INDEX = -1;
4436
4437 /**
4438 * Size value for use with {@link #setCustomSizePreset} to show this notification with
4439 * default sizing.
4440 * <p>For custom display notifications created using {@link #setDisplayIntent},
4441 * the default is {@link #SIZE_LARGE}. All other notifications size automatically based
4442 * on their content.
4443 */
4444 public static final int SIZE_DEFAULT = 0;
4445
4446 /**
4447 * Size value for use with {@link #setCustomSizePreset} to show this notification
4448 * with an extra small size.
4449 * <p>This value is only applicable for custom display notifications created using
4450 * {@link #setDisplayIntent}.
4451 */
4452 public static final int SIZE_XSMALL = 1;
4453
4454 /**
4455 * Size value for use with {@link #setCustomSizePreset} to show this notification
4456 * with a small size.
4457 * <p>This value is only applicable for custom display notifications created using
4458 * {@link #setDisplayIntent}.
4459 */
4460 public static final int SIZE_SMALL = 2;
4461
4462 /**
4463 * Size value for use with {@link #setCustomSizePreset} to show this notification
4464 * with a medium size.
4465 * <p>This value is only applicable for custom display notifications created using
4466 * {@link #setDisplayIntent}.
4467 */
4468 public static final int SIZE_MEDIUM = 3;
4469
4470 /**
4471 * Size value for use with {@link #setCustomSizePreset} to show this notification
4472 * with a large size.
4473 * <p>This value is only applicable for custom display notifications created using
4474 * {@link #setDisplayIntent}.
4475 */
4476 public static final int SIZE_LARGE = 4;
4477
Griff Hazend5f11f92014-05-27 15:40:09 -07004478 /**
4479 * Size value for use with {@link #setCustomSizePreset} to show this notification
4480 * full screen.
4481 * <p>This value is only applicable for custom display notifications created using
4482 * {@link #setDisplayIntent}.
4483 */
4484 public static final int SIZE_FULL_SCREEN = 5;
4485
Griff Hazen5f2edfc2014-09-29 16:28:44 -07004486 /**
4487 * Sentinel value for use with {@link #setHintScreenTimeout} to keep the screen on for a
4488 * short amount of time when this notification is displayed on the screen. This
4489 * is the default value.
4490 */
4491 public static final int SCREEN_TIMEOUT_SHORT = 0;
4492
4493 /**
4494 * Sentinel value for use with {@link #setHintScreenTimeout} to keep the screen on
4495 * for a longer amount of time when this notification is displayed on the screen.
4496 */
4497 public static final int SCREEN_TIMEOUT_LONG = -1;
4498
Griff Hazen61a9e862014-05-22 16:05:19 -07004499 /** Notification extra which contains wearable extensions */
4500 private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS";
4501
Pete Gastaf6781d2014-10-07 15:17:05 -04004502 // Keys within EXTRA_WEARABLE_EXTENSIONS for wearable options.
Griff Hazen61a9e862014-05-22 16:05:19 -07004503 private static final String KEY_ACTIONS = "actions";
4504 private static final String KEY_FLAGS = "flags";
4505 private static final String KEY_DISPLAY_INTENT = "displayIntent";
4506 private static final String KEY_PAGES = "pages";
4507 private static final String KEY_BACKGROUND = "background";
4508 private static final String KEY_CONTENT_ICON = "contentIcon";
4509 private static final String KEY_CONTENT_ICON_GRAVITY = "contentIconGravity";
4510 private static final String KEY_CONTENT_ACTION_INDEX = "contentActionIndex";
4511 private static final String KEY_CUSTOM_SIZE_PRESET = "customSizePreset";
4512 private static final String KEY_CUSTOM_CONTENT_HEIGHT = "customContentHeight";
4513 private static final String KEY_GRAVITY = "gravity";
Griff Hazen5f2edfc2014-09-29 16:28:44 -07004514 private static final String KEY_HINT_SCREEN_TIMEOUT = "hintScreenTimeout";
Griff Hazen61a9e862014-05-22 16:05:19 -07004515
4516 // Flags bitwise-ored to mFlags
4517 private static final int FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE = 0x1;
4518 private static final int FLAG_HINT_HIDE_ICON = 1 << 1;
4519 private static final int FLAG_HINT_SHOW_BACKGROUND_ONLY = 1 << 2;
4520 private static final int FLAG_START_SCROLL_BOTTOM = 1 << 3;
Griff Hazen5f2edfc2014-09-29 16:28:44 -07004521 private static final int FLAG_HINT_AVOID_BACKGROUND_CLIPPING = 1 << 4;
Griff Hazen61a9e862014-05-22 16:05:19 -07004522
4523 // Default value for flags integer
4524 private static final int DEFAULT_FLAGS = FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE;
4525
4526 private static final int DEFAULT_CONTENT_ICON_GRAVITY = Gravity.END;
4527 private static final int DEFAULT_GRAVITY = Gravity.BOTTOM;
4528
4529 private ArrayList<Action> mActions = new ArrayList<Action>();
4530 private int mFlags = DEFAULT_FLAGS;
4531 private PendingIntent mDisplayIntent;
4532 private ArrayList<Notification> mPages = new ArrayList<Notification>();
4533 private Bitmap mBackground;
4534 private int mContentIcon;
4535 private int mContentIconGravity = DEFAULT_CONTENT_ICON_GRAVITY;
4536 private int mContentActionIndex = UNSET_ACTION_INDEX;
4537 private int mCustomSizePreset = SIZE_DEFAULT;
4538 private int mCustomContentHeight;
4539 private int mGravity = DEFAULT_GRAVITY;
Griff Hazen5f2edfc2014-09-29 16:28:44 -07004540 private int mHintScreenTimeout;
Griff Hazen61a9e862014-05-22 16:05:19 -07004541
4542 /**
4543 * Create a {@link android.app.Notification.WearableExtender} with default
4544 * options.
4545 */
4546 public WearableExtender() {
4547 }
4548
4549 public WearableExtender(Notification notif) {
4550 Bundle wearableBundle = notif.extras.getBundle(EXTRA_WEARABLE_EXTENSIONS);
4551 if (wearableBundle != null) {
4552 List<Action> actions = wearableBundle.getParcelableArrayList(KEY_ACTIONS);
4553 if (actions != null) {
4554 mActions.addAll(actions);
4555 }
4556
4557 mFlags = wearableBundle.getInt(KEY_FLAGS, DEFAULT_FLAGS);
4558 mDisplayIntent = wearableBundle.getParcelable(KEY_DISPLAY_INTENT);
4559
4560 Notification[] pages = getNotificationArrayFromBundle(
4561 wearableBundle, KEY_PAGES);
4562 if (pages != null) {
4563 Collections.addAll(mPages, pages);
4564 }
4565
4566 mBackground = wearableBundle.getParcelable(KEY_BACKGROUND);
4567 mContentIcon = wearableBundle.getInt(KEY_CONTENT_ICON);
4568 mContentIconGravity = wearableBundle.getInt(KEY_CONTENT_ICON_GRAVITY,
4569 DEFAULT_CONTENT_ICON_GRAVITY);
4570 mContentActionIndex = wearableBundle.getInt(KEY_CONTENT_ACTION_INDEX,
4571 UNSET_ACTION_INDEX);
4572 mCustomSizePreset = wearableBundle.getInt(KEY_CUSTOM_SIZE_PRESET,
4573 SIZE_DEFAULT);
4574 mCustomContentHeight = wearableBundle.getInt(KEY_CUSTOM_CONTENT_HEIGHT);
4575 mGravity = wearableBundle.getInt(KEY_GRAVITY, DEFAULT_GRAVITY);
Griff Hazen5f2edfc2014-09-29 16:28:44 -07004576 mHintScreenTimeout = wearableBundle.getInt(KEY_HINT_SCREEN_TIMEOUT);
Griff Hazen61a9e862014-05-22 16:05:19 -07004577 }
4578 }
4579
4580 /**
4581 * Apply wearable extensions to a notification that is being built. This is typically
4582 * called by the {@link android.app.Notification.Builder#extend} method of
4583 * {@link android.app.Notification.Builder}.
4584 */
4585 @Override
4586 public Notification.Builder extend(Notification.Builder builder) {
4587 Bundle wearableBundle = new Bundle();
4588
4589 if (!mActions.isEmpty()) {
4590 wearableBundle.putParcelableArrayList(KEY_ACTIONS, mActions);
4591 }
4592 if (mFlags != DEFAULT_FLAGS) {
4593 wearableBundle.putInt(KEY_FLAGS, mFlags);
4594 }
4595 if (mDisplayIntent != null) {
4596 wearableBundle.putParcelable(KEY_DISPLAY_INTENT, mDisplayIntent);
4597 }
4598 if (!mPages.isEmpty()) {
4599 wearableBundle.putParcelableArray(KEY_PAGES, mPages.toArray(
4600 new Notification[mPages.size()]));
4601 }
4602 if (mBackground != null) {
4603 wearableBundle.putParcelable(KEY_BACKGROUND, mBackground);
4604 }
4605 if (mContentIcon != 0) {
4606 wearableBundle.putInt(KEY_CONTENT_ICON, mContentIcon);
4607 }
4608 if (mContentIconGravity != DEFAULT_CONTENT_ICON_GRAVITY) {
4609 wearableBundle.putInt(KEY_CONTENT_ICON_GRAVITY, mContentIconGravity);
4610 }
4611 if (mContentActionIndex != UNSET_ACTION_INDEX) {
4612 wearableBundle.putInt(KEY_CONTENT_ACTION_INDEX,
4613 mContentActionIndex);
4614 }
4615 if (mCustomSizePreset != SIZE_DEFAULT) {
4616 wearableBundle.putInt(KEY_CUSTOM_SIZE_PRESET, mCustomSizePreset);
4617 }
4618 if (mCustomContentHeight != 0) {
4619 wearableBundle.putInt(KEY_CUSTOM_CONTENT_HEIGHT, mCustomContentHeight);
4620 }
4621 if (mGravity != DEFAULT_GRAVITY) {
4622 wearableBundle.putInt(KEY_GRAVITY, mGravity);
4623 }
Griff Hazen5f2edfc2014-09-29 16:28:44 -07004624 if (mHintScreenTimeout != 0) {
4625 wearableBundle.putInt(KEY_HINT_SCREEN_TIMEOUT, mHintScreenTimeout);
4626 }
Griff Hazen61a9e862014-05-22 16:05:19 -07004627
4628 builder.getExtras().putBundle(EXTRA_WEARABLE_EXTENSIONS, wearableBundle);
4629 return builder;
4630 }
4631
4632 @Override
4633 public WearableExtender clone() {
4634 WearableExtender that = new WearableExtender();
4635 that.mActions = new ArrayList<Action>(this.mActions);
4636 that.mFlags = this.mFlags;
4637 that.mDisplayIntent = this.mDisplayIntent;
4638 that.mPages = new ArrayList<Notification>(this.mPages);
4639 that.mBackground = this.mBackground;
4640 that.mContentIcon = this.mContentIcon;
4641 that.mContentIconGravity = this.mContentIconGravity;
4642 that.mContentActionIndex = this.mContentActionIndex;
4643 that.mCustomSizePreset = this.mCustomSizePreset;
4644 that.mCustomContentHeight = this.mCustomContentHeight;
4645 that.mGravity = this.mGravity;
Griff Hazen5f2edfc2014-09-29 16:28:44 -07004646 that.mHintScreenTimeout = this.mHintScreenTimeout;
Griff Hazen61a9e862014-05-22 16:05:19 -07004647 return that;
4648 }
4649
4650 /**
4651 * Add a wearable action to this notification.
4652 *
4653 * <p>When wearable actions are added using this method, the set of actions that
4654 * show on a wearable device splits from devices that only show actions added
4655 * using {@link android.app.Notification.Builder#addAction}. This allows for customization
4656 * of which actions display on different devices.
4657 *
4658 * @param action the action to add to this notification
4659 * @return this object for method chaining
4660 * @see android.app.Notification.Action
4661 */
4662 public WearableExtender addAction(Action action) {
4663 mActions.add(action);
4664 return this;
4665 }
4666
4667 /**
4668 * Adds wearable actions to this notification.
4669 *
4670 * <p>When wearable actions are added using this method, the set of actions that
4671 * show on a wearable device splits from devices that only show actions added
4672 * using {@link android.app.Notification.Builder#addAction}. This allows for customization
4673 * of which actions display on different devices.
4674 *
4675 * @param actions the actions to add to this notification
4676 * @return this object for method chaining
4677 * @see android.app.Notification.Action
4678 */
4679 public WearableExtender addActions(List<Action> actions) {
4680 mActions.addAll(actions);
4681 return this;
4682 }
4683
4684 /**
4685 * Clear all wearable actions present on this builder.
4686 * @return this object for method chaining.
4687 * @see #addAction
4688 */
4689 public WearableExtender clearActions() {
4690 mActions.clear();
4691 return this;
4692 }
4693
4694 /**
4695 * Get the wearable actions present on this notification.
4696 */
4697 public List<Action> getActions() {
4698 return mActions;
4699 }
4700
4701 /**
4702 * Set an intent to launch inside of an activity view when displaying
Griff Hazen14f57992014-05-26 09:07:14 -07004703 * this notification. The {@link PendingIntent} provided should be for an activity.
4704 *
4705 * <pre class="prettyprint">
4706 * Intent displayIntent = new Intent(context, MyDisplayActivity.class);
4707 * PendingIntent displayPendingIntent = PendingIntent.getActivity(context,
4708 * 0, displayIntent, PendingIntent.FLAG_UPDATE_CURRENT);
4709 * Notification notif = new Notification.Builder(context)
4710 * .extend(new Notification.WearableExtender()
4711 * .setDisplayIntent(displayPendingIntent)
4712 * .setCustomSizePreset(Notification.WearableExtender.SIZE_MEDIUM))
4713 * .build();</pre>
4714 *
4715 * <p>The activity to launch needs to allow embedding, must be exported, and
Griff Hazen831ca9d2014-06-17 00:38:38 -07004716 * should have an empty task affinity. It is also recommended to use the device
4717 * default light theme.
Griff Hazen14f57992014-05-26 09:07:14 -07004718 *
4719 * <p>Example AndroidManifest.xml entry:
4720 * <pre class="prettyprint">
4721 * &lt;activity android:name=&quot;com.example.MyDisplayActivity&quot;
4722 * android:exported=&quot;true&quot;
4723 * android:allowEmbedded=&quot;true&quot;
Griff Hazen831ca9d2014-06-17 00:38:38 -07004724 * android:taskAffinity=&quot;&quot;
4725 * android:theme=&quot;@android:style/Theme.DeviceDefault.Light&quot; /&gt;</pre>
Griff Hazen61a9e862014-05-22 16:05:19 -07004726 *
4727 * @param intent the {@link PendingIntent} for an activity
4728 * @return this object for method chaining
4729 * @see android.app.Notification.WearableExtender#getDisplayIntent
4730 */
4731 public WearableExtender setDisplayIntent(PendingIntent intent) {
4732 mDisplayIntent = intent;
4733 return this;
4734 }
4735
4736 /**
4737 * Get the intent to launch inside of an activity view when displaying this
4738 * notification. This {@code PendingIntent} should be for an activity.
4739 */
4740 public PendingIntent getDisplayIntent() {
4741 return mDisplayIntent;
4742 }
4743
4744 /**
4745 * Add an additional page of content to display with this notification. The current
4746 * notification forms the first page, and pages added using this function form
4747 * subsequent pages. This field can be used to separate a notification into multiple
4748 * sections.
4749 *
4750 * @param page the notification to add as another page
4751 * @return this object for method chaining
4752 * @see android.app.Notification.WearableExtender#getPages
4753 */
4754 public WearableExtender addPage(Notification page) {
4755 mPages.add(page);
4756 return this;
4757 }
4758
4759 /**
4760 * Add additional pages of content to display with this notification. The current
4761 * notification forms the first page, and pages added using this function form
4762 * subsequent pages. This field can be used to separate a notification into multiple
4763 * sections.
4764 *
4765 * @param pages a list of notifications
4766 * @return this object for method chaining
4767 * @see android.app.Notification.WearableExtender#getPages
4768 */
4769 public WearableExtender addPages(List<Notification> pages) {
4770 mPages.addAll(pages);
4771 return this;
4772 }
4773
4774 /**
4775 * Clear all additional pages present on this builder.
4776 * @return this object for method chaining.
4777 * @see #addPage
4778 */
4779 public WearableExtender clearPages() {
4780 mPages.clear();
4781 return this;
4782 }
4783
4784 /**
4785 * Get the array of additional pages of content for displaying this notification. The
4786 * current notification forms the first page, and elements within this array form
4787 * subsequent pages. This field can be used to separate a notification into multiple
4788 * sections.
4789 * @return the pages for this notification
4790 */
4791 public List<Notification> getPages() {
4792 return mPages;
4793 }
4794
4795 /**
4796 * Set a background image to be displayed behind the notification content.
4797 * Contrary to the {@link android.app.Notification.BigPictureStyle}, this background
4798 * will work with any notification style.
4799 *
4800 * @param background the background bitmap
4801 * @return this object for method chaining
4802 * @see android.app.Notification.WearableExtender#getBackground
4803 */
4804 public WearableExtender setBackground(Bitmap background) {
4805 mBackground = background;
4806 return this;
4807 }
4808
4809 /**
4810 * Get a background image to be displayed behind the notification content.
4811 * Contrary to the {@link android.app.Notification.BigPictureStyle}, this background
4812 * will work with any notification style.
4813 *
4814 * @return the background image
4815 * @see android.app.Notification.WearableExtender#setBackground
4816 */
4817 public Bitmap getBackground() {
4818 return mBackground;
4819 }
4820
4821 /**
4822 * Set an icon that goes with the content of this notification.
4823 */
4824 public WearableExtender setContentIcon(int icon) {
4825 mContentIcon = icon;
4826 return this;
4827 }
4828
4829 /**
4830 * Get an icon that goes with the content of this notification.
4831 */
4832 public int getContentIcon() {
4833 return mContentIcon;
4834 }
4835
4836 /**
4837 * Set the gravity that the content icon should have within the notification display.
4838 * Supported values include {@link android.view.Gravity#START} and
4839 * {@link android.view.Gravity#END}. The default value is {@link android.view.Gravity#END}.
4840 * @see #setContentIcon
4841 */
4842 public WearableExtender setContentIconGravity(int contentIconGravity) {
4843 mContentIconGravity = contentIconGravity;
4844 return this;
4845 }
4846
4847 /**
4848 * Get the gravity that the content icon should have within the notification display.
4849 * Supported values include {@link android.view.Gravity#START} and
4850 * {@link android.view.Gravity#END}. The default value is {@link android.view.Gravity#END}.
4851 * @see #getContentIcon
4852 */
4853 public int getContentIconGravity() {
4854 return mContentIconGravity;
4855 }
4856
4857 /**
4858 * Set an action from this notification's actions to be clickable with the content of
Griff Hazen14f57992014-05-26 09:07:14 -07004859 * this notification. This action will no longer display separately from the
4860 * notification's content.
4861 *
Griff Hazenca48d352014-05-28 22:37:13 -07004862 * <p>For notifications with multiple pages, child pages can also have content actions
Griff Hazen14f57992014-05-26 09:07:14 -07004863 * set, although the list of available actions comes from the main notification and not
4864 * from the child page's notification.
4865 *
4866 * @param actionIndex The index of the action to hoist onto the current notification page.
4867 * If wearable actions were added to the main notification, this index
4868 * will apply to that list, otherwise it will apply to the regular
4869 * actions list.
Griff Hazen61a9e862014-05-22 16:05:19 -07004870 */
4871 public WearableExtender setContentAction(int actionIndex) {
4872 mContentActionIndex = actionIndex;
4873 return this;
4874 }
4875
4876 /**
Griff Hazenca48d352014-05-28 22:37:13 -07004877 * Get the index of the notification action, if any, that was specified as being clickable
4878 * with the content of this notification. This action will no longer display separately
Griff Hazen14f57992014-05-26 09:07:14 -07004879 * from the notification's content.
Griff Hazen61a9e862014-05-22 16:05:19 -07004880 *
Griff Hazenca48d352014-05-28 22:37:13 -07004881 * <p>For notifications with multiple pages, child pages can also have content actions
Griff Hazen14f57992014-05-26 09:07:14 -07004882 * set, although the list of available actions comes from the main notification and not
4883 * from the child page's notification.
4884 *
4885 * <p>If wearable specific actions were added to the main notification, this index will
4886 * apply to that list, otherwise it will apply to the regular actions list.
Griff Hazenca48d352014-05-28 22:37:13 -07004887 *
4888 * @return the action index or {@link #UNSET_ACTION_INDEX} if no action was selected.
Griff Hazen61a9e862014-05-22 16:05:19 -07004889 */
4890 public int getContentAction() {
4891 return mContentActionIndex;
4892 }
4893
4894 /**
4895 * Set the gravity that this notification should have within the available viewport space.
4896 * Supported values include {@link android.view.Gravity#TOP},
4897 * {@link android.view.Gravity#CENTER_VERTICAL} and {@link android.view.Gravity#BOTTOM}.
4898 * The default value is {@link android.view.Gravity#BOTTOM}.
4899 */
4900 public WearableExtender setGravity(int gravity) {
4901 mGravity = gravity;
4902 return this;
4903 }
4904
4905 /**
4906 * Get the gravity that this notification should have within the available viewport space.
4907 * Supported values include {@link android.view.Gravity#TOP},
4908 * {@link android.view.Gravity#CENTER_VERTICAL} and {@link android.view.Gravity#BOTTOM}.
4909 * The default value is {@link android.view.Gravity#BOTTOM}.
4910 */
4911 public int getGravity() {
4912 return mGravity;
4913 }
4914
4915 /**
4916 * Set the custom size preset for the display of this notification out of the available
4917 * presets found in {@link android.app.Notification.WearableExtender}, e.g.
4918 * {@link #SIZE_LARGE}.
4919 * <p>Some custom size presets are only applicable for custom display notifications created
4920 * using {@link android.app.Notification.WearableExtender#setDisplayIntent}. Check the
4921 * documentation for the preset in question. See also
4922 * {@link #setCustomContentHeight} and {@link #getCustomSizePreset}.
4923 */
4924 public WearableExtender setCustomSizePreset(int sizePreset) {
4925 mCustomSizePreset = sizePreset;
4926 return this;
4927 }
4928
4929 /**
4930 * Get the custom size preset for the display of this notification out of the available
4931 * presets found in {@link android.app.Notification.WearableExtender}, e.g.
4932 * {@link #SIZE_LARGE}.
4933 * <p>Some custom size presets are only applicable for custom display notifications created
4934 * using {@link #setDisplayIntent}. Check the documentation for the preset in question.
4935 * See also {@link #setCustomContentHeight} and {@link #setCustomSizePreset}.
4936 */
4937 public int getCustomSizePreset() {
4938 return mCustomSizePreset;
4939 }
4940
4941 /**
4942 * Set the custom height in pixels for the display of this notification's content.
4943 * <p>This option is only available for custom display notifications created
4944 * using {@link android.app.Notification.WearableExtender#setDisplayIntent}. See also
4945 * {@link android.app.Notification.WearableExtender#setCustomSizePreset} and
4946 * {@link #getCustomContentHeight}.
4947 */
4948 public WearableExtender setCustomContentHeight(int height) {
4949 mCustomContentHeight = height;
4950 return this;
4951 }
4952
4953 /**
4954 * Get the custom height in pixels for the display of this notification's content.
4955 * <p>This option is only available for custom display notifications created
4956 * using {@link #setDisplayIntent}. See also {@link #setCustomSizePreset} and
4957 * {@link #setCustomContentHeight}.
4958 */
4959 public int getCustomContentHeight() {
4960 return mCustomContentHeight;
4961 }
4962
4963 /**
4964 * Set whether the scrolling position for the contents of this notification should start
4965 * at the bottom of the contents instead of the top when the contents are too long to
4966 * display within the screen. Default is false (start scroll at the top).
4967 */
4968 public WearableExtender setStartScrollBottom(boolean startScrollBottom) {
4969 setFlag(FLAG_START_SCROLL_BOTTOM, startScrollBottom);
4970 return this;
4971 }
4972
4973 /**
4974 * Get whether the scrolling position for the contents of this notification should start
4975 * at the bottom of the contents instead of the top when the contents are too long to
4976 * display within the screen. Default is false (start scroll at the top).
4977 */
4978 public boolean getStartScrollBottom() {
4979 return (mFlags & FLAG_START_SCROLL_BOTTOM) != 0;
4980 }
4981
4982 /**
4983 * Set whether the content intent is available when the wearable device is not connected
4984 * to a companion device. The user can still trigger this intent when the wearable device
4985 * is offline, but a visual hint will indicate that the content intent may not be available.
4986 * Defaults to true.
4987 */
4988 public WearableExtender setContentIntentAvailableOffline(
4989 boolean contentIntentAvailableOffline) {
4990 setFlag(FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE, contentIntentAvailableOffline);
4991 return this;
4992 }
4993
4994 /**
4995 * Get whether the content intent is available when the wearable device is not connected
4996 * to a companion device. The user can still trigger this intent when the wearable device
4997 * is offline, but a visual hint will indicate that the content intent may not be available.
4998 * Defaults to true.
4999 */
5000 public boolean getContentIntentAvailableOffline() {
5001 return (mFlags & FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE) != 0;
5002 }
5003
5004 /**
5005 * Set a hint that this notification's icon should not be displayed.
5006 * @param hintHideIcon {@code true} to hide the icon, {@code false} otherwise.
5007 * @return this object for method chaining
5008 */
5009 public WearableExtender setHintHideIcon(boolean hintHideIcon) {
5010 setFlag(FLAG_HINT_HIDE_ICON, hintHideIcon);
5011 return this;
5012 }
5013
5014 /**
5015 * Get a hint that this notification's icon should not be displayed.
5016 * @return {@code true} if this icon should not be displayed, false otherwise.
5017 * The default value is {@code false} if this was never set.
5018 */
5019 public boolean getHintHideIcon() {
5020 return (mFlags & FLAG_HINT_HIDE_ICON) != 0;
5021 }
5022
5023 /**
5024 * Set a visual hint that only the background image of this notification should be
5025 * displayed, and other semantic content should be hidden. This hint is only applicable
5026 * to sub-pages added using {@link #addPage}.
5027 */
5028 public WearableExtender setHintShowBackgroundOnly(boolean hintShowBackgroundOnly) {
5029 setFlag(FLAG_HINT_SHOW_BACKGROUND_ONLY, hintShowBackgroundOnly);
5030 return this;
5031 }
5032
5033 /**
5034 * Get a visual hint that only the background image of this notification should be
5035 * displayed, and other semantic content should be hidden. This hint is only applicable
5036 * to sub-pages added using {@link android.app.Notification.WearableExtender#addPage}.
5037 */
5038 public boolean getHintShowBackgroundOnly() {
5039 return (mFlags & FLAG_HINT_SHOW_BACKGROUND_ONLY) != 0;
5040 }
5041
Griff Hazen5f2edfc2014-09-29 16:28:44 -07005042 /**
Griff Hazen9c5be4e2014-11-17 17:47:50 -08005043 * Set a hint that this notification's background should not be clipped if possible,
5044 * and should instead be resized to fully display on the screen, retaining the aspect
5045 * ratio of the image. This can be useful for images like barcodes or qr codes.
Griff Hazen5f2edfc2014-09-29 16:28:44 -07005046 * @param hintAvoidBackgroundClipping {@code true} to avoid clipping if possible.
5047 * @return this object for method chaining
5048 */
5049 public WearableExtender setHintAvoidBackgroundClipping(
5050 boolean hintAvoidBackgroundClipping) {
5051 setFlag(FLAG_HINT_AVOID_BACKGROUND_CLIPPING, hintAvoidBackgroundClipping);
5052 return this;
5053 }
5054
5055 /**
Griff Hazen9c5be4e2014-11-17 17:47:50 -08005056 * Get a hint that this notification's background should not be clipped if possible,
5057 * and should instead be resized to fully display on the screen, retaining the aspect
5058 * ratio of the image. This can be useful for images like barcodes or qr codes.
Griff Hazen5f2edfc2014-09-29 16:28:44 -07005059 * @return {@code true} if it's ok if the background is clipped on the screen, false
5060 * otherwise. The default value is {@code false} if this was never set.
5061 */
5062 public boolean getHintAvoidBackgroundClipping() {
5063 return (mFlags & FLAG_HINT_AVOID_BACKGROUND_CLIPPING) != 0;
5064 }
5065
5066 /**
5067 * Set a hint that the screen should remain on for at least this duration when
5068 * this notification is displayed on the screen.
5069 * @param timeout The requested screen timeout in milliseconds. Can also be either
5070 * {@link #SCREEN_TIMEOUT_SHORT} or {@link #SCREEN_TIMEOUT_LONG}.
5071 * @return this object for method chaining
5072 */
5073 public WearableExtender setHintScreenTimeout(int timeout) {
5074 mHintScreenTimeout = timeout;
5075 return this;
5076 }
5077
5078 /**
5079 * Get the duration, in milliseconds, that the screen should remain on for
5080 * when this notification is displayed.
5081 * @return the duration in milliseconds if > 0, or either one of the sentinel values
5082 * {@link #SCREEN_TIMEOUT_SHORT} or {@link #SCREEN_TIMEOUT_LONG}.
5083 */
5084 public int getHintScreenTimeout() {
5085 return mHintScreenTimeout;
5086 }
5087
Griff Hazen61a9e862014-05-22 16:05:19 -07005088 private void setFlag(int mask, boolean value) {
5089 if (value) {
5090 mFlags |= mask;
5091 } else {
5092 mFlags &= ~mask;
5093 }
5094 }
5095 }
5096
5097 /**
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08005098 * <p>Helper class to add Android Auto extensions to notifications. To create a notification
5099 * with car extensions:
5100 *
5101 * <ol>
5102 * <li>Create an {@link Notification.Builder}, setting any desired
5103 * properties.
5104 * <li>Create a {@link CarExtender}.
5105 * <li>Set car-specific properties using the {@code add} and {@code set} methods of
5106 * {@link CarExtender}.
5107 * <li>Call {@link Notification.Builder#extend(Notification.Extender)}
5108 * to apply the extensions to a notification.
5109 * </ol>
5110 *
5111 * <pre class="prettyprint">
5112 * Notification notification = new Notification.Builder(context)
5113 * ...
5114 * .extend(new CarExtender()
5115 * .set*(...))
5116 * .build();
5117 * </pre>
5118 *
5119 * <p>Car extensions can be accessed on an existing notification by using the
5120 * {@code CarExtender(Notification)} constructor, and then using the {@code get} methods
5121 * to access values.
5122 */
5123 public static final class CarExtender implements Extender {
5124 private static final String TAG = "CarExtender";
5125
5126 private static final String EXTRA_CAR_EXTENDER = "android.car.EXTENSIONS";
5127 private static final String EXTRA_LARGE_ICON = "large_icon";
5128 private static final String EXTRA_CONVERSATION = "car_conversation";
5129 private static final String EXTRA_COLOR = "app_color";
5130
5131 private Bitmap mLargeIcon;
5132 private UnreadConversation mUnreadConversation;
5133 private int mColor = Notification.COLOR_DEFAULT;
5134
5135 /**
5136 * Create a {@link CarExtender} with default options.
5137 */
5138 public CarExtender() {
5139 }
5140
5141 /**
5142 * Create a {@link CarExtender} from the CarExtender options of an existing Notification.
5143 *
5144 * @param notif The notification from which to copy options.
5145 */
5146 public CarExtender(Notification notif) {
5147 Bundle carBundle = notif.extras == null ?
5148 null : notif.extras.getBundle(EXTRA_CAR_EXTENDER);
5149 if (carBundle != null) {
5150 mLargeIcon = carBundle.getParcelable(EXTRA_LARGE_ICON);
5151 mColor = carBundle.getInt(EXTRA_COLOR, Notification.COLOR_DEFAULT);
5152
5153 Bundle b = carBundle.getBundle(EXTRA_CONVERSATION);
5154 mUnreadConversation = UnreadConversation.getUnreadConversationFromBundle(b);
5155 }
5156 }
5157
5158 /**
5159 * Apply car extensions to a notification that is being built. This is typically called by
5160 * the {@link Notification.Builder#extend(Notification.Extender)}
5161 * method of {@link Notification.Builder}.
5162 */
5163 @Override
5164 public Notification.Builder extend(Notification.Builder builder) {
5165 Bundle carExtensions = new Bundle();
5166
5167 if (mLargeIcon != null) {
5168 carExtensions.putParcelable(EXTRA_LARGE_ICON, mLargeIcon);
5169 }
5170 if (mColor != Notification.COLOR_DEFAULT) {
5171 carExtensions.putInt(EXTRA_COLOR, mColor);
5172 }
5173
5174 if (mUnreadConversation != null) {
5175 Bundle b = mUnreadConversation.getBundleForUnreadConversation();
5176 carExtensions.putBundle(EXTRA_CONVERSATION, b);
5177 }
5178
5179 builder.getExtras().putBundle(EXTRA_CAR_EXTENDER, carExtensions);
5180 return builder;
5181 }
5182
5183 /**
5184 * Sets the accent color to use when Android Auto presents the notification.
5185 *
5186 * Android Auto uses the color set with {@link Notification.Builder#setColor(int)}
5187 * to accent the displayed notification. However, not all colors are acceptable in an
5188 * automotive setting. This method can be used to override the color provided in the
5189 * notification in such a situation.
5190 */
Tor Norbye80756e32015-03-02 09:39:27 -08005191 public CarExtender setColor(@ColorInt int color) {
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08005192 mColor = color;
5193 return this;
5194 }
5195
5196 /**
5197 * Gets the accent color.
5198 *
5199 * @see setColor
5200 */
Tor Norbye80756e32015-03-02 09:39:27 -08005201 @ColorInt
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08005202 public int getColor() {
5203 return mColor;
5204 }
5205
5206 /**
5207 * Sets the large icon of the car notification.
5208 *
5209 * If no large icon is set in the extender, Android Auto will display the icon
5210 * specified by {@link Notification.Builder#setLargeIcon(android.graphics.Bitmap)}
5211 *
5212 * @param largeIcon The large icon to use in the car notification.
5213 * @return This object for method chaining.
5214 */
5215 public CarExtender setLargeIcon(Bitmap largeIcon) {
5216 mLargeIcon = largeIcon;
5217 return this;
5218 }
5219
5220 /**
5221 * Gets the large icon used in this car notification, or null if no icon has been set.
5222 *
5223 * @return The large icon for the car notification.
5224 * @see CarExtender#setLargeIcon
5225 */
5226 public Bitmap getLargeIcon() {
5227 return mLargeIcon;
5228 }
5229
5230 /**
5231 * Sets the unread conversation in a message notification.
5232 *
5233 * @param unreadConversation The unread part of the conversation this notification conveys.
5234 * @return This object for method chaining.
5235 */
5236 public CarExtender setUnreadConversation(UnreadConversation unreadConversation) {
5237 mUnreadConversation = unreadConversation;
5238 return this;
5239 }
5240
5241 /**
5242 * Returns the unread conversation conveyed by this notification.
5243 * @see #setUnreadConversation(UnreadConversation)
5244 */
5245 public UnreadConversation getUnreadConversation() {
5246 return mUnreadConversation;
5247 }
5248
5249 /**
5250 * A class which holds the unread messages from a conversation.
5251 */
5252 public static class UnreadConversation {
5253 private static final String KEY_AUTHOR = "author";
5254 private static final String KEY_TEXT = "text";
5255 private static final String KEY_MESSAGES = "messages";
5256 private static final String KEY_REMOTE_INPUT = "remote_input";
5257 private static final String KEY_ON_REPLY = "on_reply";
5258 private static final String KEY_ON_READ = "on_read";
5259 private static final String KEY_PARTICIPANTS = "participants";
5260 private static final String KEY_TIMESTAMP = "timestamp";
5261
5262 private final String[] mMessages;
5263 private final RemoteInput mRemoteInput;
5264 private final PendingIntent mReplyPendingIntent;
5265 private final PendingIntent mReadPendingIntent;
5266 private final String[] mParticipants;
5267 private final long mLatestTimestamp;
5268
5269 UnreadConversation(String[] messages, RemoteInput remoteInput,
5270 PendingIntent replyPendingIntent, PendingIntent readPendingIntent,
5271 String[] participants, long latestTimestamp) {
5272 mMessages = messages;
5273 mRemoteInput = remoteInput;
5274 mReadPendingIntent = readPendingIntent;
5275 mReplyPendingIntent = replyPendingIntent;
5276 mParticipants = participants;
5277 mLatestTimestamp = latestTimestamp;
5278 }
5279
5280 /**
5281 * Gets the list of messages conveyed by this notification.
5282 */
5283 public String[] getMessages() {
5284 return mMessages;
5285 }
5286
5287 /**
5288 * Gets the remote input that will be used to convey the response to a message list, or
5289 * null if no such remote input exists.
5290 */
5291 public RemoteInput getRemoteInput() {
5292 return mRemoteInput;
5293 }
5294
5295 /**
5296 * Gets the pending intent that will be triggered when the user replies to this
5297 * notification.
5298 */
5299 public PendingIntent getReplyPendingIntent() {
5300 return mReplyPendingIntent;
5301 }
5302
5303 /**
5304 * Gets the pending intent that Android Auto will send after it reads aloud all messages
5305 * in this object's message list.
5306 */
5307 public PendingIntent getReadPendingIntent() {
5308 return mReadPendingIntent;
5309 }
5310
5311 /**
5312 * Gets the participants in the conversation.
5313 */
5314 public String[] getParticipants() {
5315 return mParticipants;
5316 }
5317
5318 /**
5319 * Gets the firs participant in the conversation.
5320 */
5321 public String getParticipant() {
5322 return mParticipants.length > 0 ? mParticipants[0] : null;
5323 }
5324
5325 /**
5326 * Gets the timestamp of the conversation.
5327 */
5328 public long getLatestTimestamp() {
5329 return mLatestTimestamp;
5330 }
5331
5332 Bundle getBundleForUnreadConversation() {
5333 Bundle b = new Bundle();
5334 String author = null;
5335 if (mParticipants != null && mParticipants.length > 1) {
5336 author = mParticipants[0];
5337 }
5338 Parcelable[] messages = new Parcelable[mMessages.length];
5339 for (int i = 0; i < messages.length; i++) {
5340 Bundle m = new Bundle();
5341 m.putString(KEY_TEXT, mMessages[i]);
5342 m.putString(KEY_AUTHOR, author);
5343 messages[i] = m;
5344 }
5345 b.putParcelableArray(KEY_MESSAGES, messages);
5346 if (mRemoteInput != null) {
5347 b.putParcelable(KEY_REMOTE_INPUT, mRemoteInput);
5348 }
5349 b.putParcelable(KEY_ON_REPLY, mReplyPendingIntent);
5350 b.putParcelable(KEY_ON_READ, mReadPendingIntent);
5351 b.putStringArray(KEY_PARTICIPANTS, mParticipants);
5352 b.putLong(KEY_TIMESTAMP, mLatestTimestamp);
5353 return b;
5354 }
5355
5356 static UnreadConversation getUnreadConversationFromBundle(Bundle b) {
5357 if (b == null) {
5358 return null;
5359 }
5360 Parcelable[] parcelableMessages = b.getParcelableArray(KEY_MESSAGES);
5361 String[] messages = null;
5362 if (parcelableMessages != null) {
5363 String[] tmp = new String[parcelableMessages.length];
5364 boolean success = true;
5365 for (int i = 0; i < tmp.length; i++) {
5366 if (!(parcelableMessages[i] instanceof Bundle)) {
5367 success = false;
5368 break;
5369 }
5370 tmp[i] = ((Bundle) parcelableMessages[i]).getString(KEY_TEXT);
5371 if (tmp[i] == null) {
5372 success = false;
5373 break;
5374 }
5375 }
5376 if (success) {
5377 messages = tmp;
5378 } else {
5379 return null;
5380 }
5381 }
5382
5383 PendingIntent onRead = b.getParcelable(KEY_ON_READ);
5384 PendingIntent onReply = b.getParcelable(KEY_ON_REPLY);
5385
5386 RemoteInput remoteInput = b.getParcelable(KEY_REMOTE_INPUT);
5387
5388 String[] participants = b.getStringArray(KEY_PARTICIPANTS);
5389 if (participants == null || participants.length != 1) {
5390 return null;
5391 }
5392
5393 return new UnreadConversation(messages,
5394 remoteInput,
5395 onReply,
5396 onRead,
5397 participants, b.getLong(KEY_TIMESTAMP));
5398 }
5399 };
5400
5401 /**
5402 * Builder class for {@link CarExtender.UnreadConversation} objects.
5403 */
5404 public static class Builder {
5405 private final List<String> mMessages = new ArrayList<String>();
5406 private final String mParticipant;
5407 private RemoteInput mRemoteInput;
5408 private PendingIntent mReadPendingIntent;
5409 private PendingIntent mReplyPendingIntent;
5410 private long mLatestTimestamp;
5411
5412 /**
5413 * Constructs a new builder for {@link CarExtender.UnreadConversation}.
5414 *
5415 * @param name The name of the other participant in the conversation.
5416 */
5417 public Builder(String name) {
5418 mParticipant = name;
5419 }
5420
5421 /**
5422 * Appends a new unread message to the list of messages for this conversation.
5423 *
5424 * The messages should be added from oldest to newest.
5425 *
5426 * @param message The text of the new unread message.
5427 * @return This object for method chaining.
5428 */
5429 public Builder addMessage(String message) {
5430 mMessages.add(message);
5431 return this;
5432 }
5433
5434 /**
5435 * Sets the pending intent and remote input which will convey the reply to this
5436 * notification.
5437 *
5438 * @param pendingIntent The pending intent which will be triggered on a reply.
5439 * @param remoteInput The remote input parcelable which will carry the reply.
5440 * @return This object for method chaining.
5441 *
5442 * @see CarExtender.UnreadConversation#getRemoteInput
5443 * @see CarExtender.UnreadConversation#getReplyPendingIntent
5444 */
5445 public Builder setReplyAction(
5446 PendingIntent pendingIntent, RemoteInput remoteInput) {
5447 mRemoteInput = remoteInput;
5448 mReplyPendingIntent = pendingIntent;
5449
5450 return this;
5451 }
5452
5453 /**
5454 * Sets the pending intent that will be sent once the messages in this notification
5455 * are read.
5456 *
5457 * @param pendingIntent The pending intent to use.
5458 * @return This object for method chaining.
5459 */
5460 public Builder setReadPendingIntent(PendingIntent pendingIntent) {
5461 mReadPendingIntent = pendingIntent;
5462 return this;
5463 }
5464
5465 /**
5466 * Sets the timestamp of the most recent message in an unread conversation.
5467 *
5468 * If a messaging notification has been posted by your application and has not
5469 * yet been cancelled, posting a later notification with the same id and tag
5470 * but without a newer timestamp may result in Android Auto not displaying a
5471 * heads up notification for the later notification.
5472 *
5473 * @param timestamp The timestamp of the most recent message in the conversation.
5474 * @return This object for method chaining.
5475 */
5476 public Builder setLatestTimestamp(long timestamp) {
5477 mLatestTimestamp = timestamp;
5478 return this;
5479 }
5480
5481 /**
5482 * Builds a new unread conversation object.
5483 *
5484 * @return The new unread conversation object.
5485 */
5486 public UnreadConversation build() {
5487 String[] messages = mMessages.toArray(new String[mMessages.size()]);
5488 String[] participants = { mParticipant };
5489 return new UnreadConversation(messages, mRemoteInput, mReplyPendingIntent,
5490 mReadPendingIntent, participants, mLatestTimestamp);
5491 }
5492 }
5493 }
5494
5495 /**
Jose Lima77c39392015-02-23 17:09:03 -08005496 * <p>
5497 * Helper class to add content info extensions to notifications. To create a notification with
5498 * content info extensions:
5499 * <ol>
5500 * <li>Create an {@link Notification.Builder}, setting any desired properties.
5501 * <li>Create a {@link ContentInfoExtender}.
5502 * <li>Set content info specific properties using the {@code add} and {@code set} methods of
5503 * {@link ContentInfoExtender}.
5504 * <li>Call {@link Notification.Builder#extend(Notification.Extender)} to apply the extensions
5505 * to a notification.
5506 * </ol>
5507 *
5508 * <pre class="prettyprint">Notification notification = new Notification.Builder(context) * ... * .extend(new ContentInfoExtender() * .set*(...)) * .build(); * </pre>
5509 * <p>
5510 * Content info extensions can be accessed on an existing notification by using the
5511 * {@code ContentInfoExtender(Notification)} constructor, and then using the {@code get} methods
5512 * to access values.
5513 */
5514 public static final class ContentInfoExtender implements Extender {
5515 private static final String TAG = "ContentInfoExtender";
5516
5517 // Key for the Content info extensions bundle in the main Notification extras bundle
5518 private static final String EXTRA_CONTENT_INFO_EXTENDER = "android.CONTENT_INFO_EXTENSIONS";
5519
5520 // Keys within EXTRA_CONTENT_INFO_EXTENDER for individual content info options.
5521
5522 private static final String KEY_CONTENT_TYPE = "android.contentType";
5523
5524 private static final String KEY_CONTENT_GENRES = "android.contentGenre";
5525
5526 private static final String KEY_CONTENT_PRICING_TYPE = "android.contentPricing.type";
5527
5528 private static final String KEY_CONTENT_PRICING_VALUE = "android.contentPricing.value";
5529
5530 private static final String KEY_CONTENT_STATUS = "android.contentStatus";
5531
5532 private static final String KEY_CONTENT_MATURITY_RATING = "android.contentMaturity";
5533
5534 private static final String KEY_CONTENT_RUN_LENGTH = "android.contentLength";
5535
5536
5537 /**
5538 * Value to be used with {@link #setContentTypes} to indicate that the content referred by
5539 * the notification item is a video clip.
5540 */
5541 public static final String CONTENT_TYPE_VIDEO = "android.contentType.video";
5542
5543 /**
5544 * Value to be used with {@link #setContentTypes} to indicate that the content referred by
5545 * the notification item is a movie.
5546 */
5547 public static final String CONTENT_TYPE_MOVIE = "android.contentType.movie";
5548
5549 /**
5550 * Value to be used with {@link #setContentTypes} to indicate that the content referred by
5551 * the notification item is a trailer.
5552 */
5553 public static final String CONTENT_TYPE_TRAILER = "android.contentType.trailer";
5554
5555 /**
5556 * Value to be used with {@link #setContentTypes} to indicate that the content referred by
5557 * the notification item is serial. It can refer to an entire show, a single season or
5558 * series, or a single episode.
5559 */
5560 public static final String CONTENT_TYPE_SERIAL = "android.contentType.serial";
5561
5562 /**
5563 * Value to be used with {@link #setContentTypes} to indicate that the content referred by
5564 * the notification item is a song or album.
5565 */
5566 public static final String CONTENT_TYPE_MUSIC = "android.contentType.music";
5567
5568 /**
5569 * Value to be used with {@link #setContentTypes} to indicate that the content referred by
5570 * the notification item is a radio station.
5571 */
5572 public static final String CONTENT_TYPE_RADIO = "android.contentType.radio";
5573
5574 /**
5575 * Value to be used with {@link #setContentTypes} to indicate that the content referred by
5576 * the notification item is a podcast.
5577 */
5578 public static final String CONTENT_TYPE_PODCAST = "android.contentType.podcast";
5579
5580 /**
5581 * Value to be used with {@link #setContentTypes} to indicate that the content referred by
5582 * the notification item is a news item.
5583 */
5584 public static final String CONTENT_TYPE_NEWS = "android.contentType.news";
5585
5586 /**
5587 * Value to be used with {@link #setContentTypes} to indicate that the content referred by
5588 * the notification item is sports.
5589 */
5590 public static final String CONTENT_TYPE_SPORTS = "android.contentType.sports";
5591
5592 /**
5593 * Value to be used with {@link #setContentTypes} to indicate that the content referred by
5594 * the notification item is an application.
5595 */
5596 public static final String CONTENT_TYPE_APP = "android.contentType.app";
5597
5598 /**
5599 * Value to be used with {@link #setContentTypes} to indicate that the content referred by
5600 * the notification item is a game.
5601 */
5602 public static final String CONTENT_TYPE_GAME = "android.contentType.game";
5603
5604 /**
5605 * Value to be used with {@link #setContentTypes} to indicate that the content referred by
5606 * the notification item is a book.
5607 */
5608 public static final String CONTENT_TYPE_BOOK = "android.contentType.book";
5609
5610 /**
5611 * Value to be used with {@link #setContentTypes} to indicate that the content referred by
5612 * the notification item is a comic book.
5613 */
5614 public static final String CONTENT_TYPE_COMIC = "android.contentType.comic";
5615
5616 /**
5617 * Value to be used with {@link #setContentTypes} to indicate that the content referred by
5618 * the notification item is a magazine.
5619 */
5620 public static final String CONTENT_TYPE_MAGAZINE = "android.contentType.magazine";
5621
5622 /**
5623 * Value to be used with {@link #setContentTypes} to indicate that the content referred by
5624 * the notification item is a website.
5625 */
5626 public static final String CONTENT_TYPE_WEBSITE = "android.contentType.website";
5627
5628
5629 /**
5630 * Value to be used with {@link #setPricingInformation} to indicate that the content
5631 * referred by the notification item is free to consume.
5632 */
5633 public static final String CONTENT_PRICING_FREE = "android.contentPrice.free";
5634
5635 /**
5636 * Value to be used with {@link #setPricingInformation} to indicate that the content
5637 * referred by the notification item is available as a rental, and the price value provided
5638 * is the rental price for the item.
5639 */
5640 public static final String CONTENT_PRICING_RENTAL = "android.contentPrice.rental";
5641
5642 /**
5643 * Value to be used with {@link #setPricingInformation} to indicate that the content
5644 * referred by the notification item is available for purchase, and the price value provided
5645 * is the purchase price for the item.
5646 */
5647 public static final String CONTENT_PRICING_PURCHASE = "android.contentPrice.purchase";
5648
5649 /**
5650 * Value to be used with {@link #setPricingInformation} to indicate that the content
5651 * referred by the notification item is available as part of a subscription based service,
5652 * and the price value provided is the subscription price for the service.
5653 */
5654 public static final String CONTENT_PRICING_SUBSCRIPTION =
5655 "android.contentPrice.subscription";
5656
5657 /**
5658 * Value to be used with {@link #setStatus} to indicate that the content referred by the
5659 * notification is available and ready to be consumed immediately.
5660 */
5661 public static final int CONTENT_STATUS_READY = 0;
5662
5663 /**
5664 * Value to be used with {@link #setStatus} to indicate that the content referred by the
5665 * notification is pending, waiting on either a download or purchase operation to complete
5666 * before it can be consumed.
5667 */
5668 public static final int CONTENT_STATUS_PENDING = 1;
5669
5670 /**
5671 * Value to be used with {@link #setStatus} to indicate that the content referred by the
5672 * notification is available, but needs to be first purchased, rented, subscribed or
5673 * downloaded before it can be consumed.
5674 */
5675 public static final int CONTENT_STATUS_AVAILABLE = 2;
5676
5677 /**
5678 * Value to be used with {@link #setStatus} to indicate that the content referred by the
5679 * notification is not available. This could be content not available in a certain region or
5680 * incompatible with the device in use.
5681 */
5682 public static final int CONTENT_STATUS_UNAVAILABLE = 3;
5683
5684 /**
5685 * Value to be used with {@link #setMaturityRating} to indicate that the content referred by
5686 * the notification is suitable for all audiences.
5687 */
5688 public static final String CONTENT_MATURITY_ALL = "android.contentMaturity.all";
5689
5690 /**
5691 * Value to be used with {@link #setMaturityRating} to indicate that the content
5692 * referred by the notification is suitable for audiences of low maturity and above.
5693 */
5694 public static final String CONTENT_MATURITY_LOW = "android.contentMaturity.low";
5695
5696 /**
5697 * Value to be used with {@link #setMaturityRating} to indicate that the content
5698 * referred by the notification is suitable for audiences of medium maturity and above.
5699 */
5700 public static final String CONTENT_MATURITY_MEDIUM = "android.contentMaturity.medium";
5701
5702 /**
5703 * Value to be used with {@link #setMaturityRating} to indicate that the content
5704 * referred by the notification is suitable for audiences of high maturity and above.
5705 */
5706 public static final String CONTENT_MATURITY_HIGH = "android.contentMaturity.high";
5707
5708 private String[] mTypes;
5709 private String[] mGenres;
5710 private String mPricingType;
5711 private String mPricingValue;
5712 private int mContentStatus = -1;
5713 private String mMaturityRating;
5714 private long mRunLength = -1;
5715
5716 /**
5717 * Create a {@link ContentInfoExtender} with default options.
5718 */
5719 public ContentInfoExtender() {
5720 }
5721
5722 /**
5723 * Create a {@link ContentInfoExtender} from the ContentInfoExtender options of an existing
5724 * Notification.
5725 *
5726 * @param notif The notification from which to copy options.
5727 */
5728 public ContentInfoExtender(Notification notif) {
5729 Bundle contentBundle = notif.extras == null ?
5730 null : notif.extras.getBundle(EXTRA_CONTENT_INFO_EXTENDER);
5731 if (contentBundle != null) {
5732 mTypes = contentBundle.getStringArray(KEY_CONTENT_TYPE);
5733 mGenres = contentBundle.getStringArray(KEY_CONTENT_GENRES);
5734 mPricingType = contentBundle.getString(KEY_CONTENT_PRICING_TYPE);
5735 mPricingValue = contentBundle.getString(KEY_CONTENT_PRICING_VALUE);
5736 mContentStatus = contentBundle.getInt(KEY_CONTENT_STATUS, -1);
5737 mMaturityRating = contentBundle.getString(KEY_CONTENT_MATURITY_RATING);
5738 mRunLength = contentBundle.getLong(KEY_CONTENT_RUN_LENGTH, -1);
5739 }
5740 }
5741
5742 /**
5743 * Apply content extensions to a notification that is being built. This is typically called
5744 * by the {@link Notification.Builder#extend(Notification.Extender)} method of
5745 * {@link Notification.Builder}.
5746 */
5747 @Override
5748 public Notification.Builder extend(Notification.Builder builder) {
5749 Bundle contentBundle = new Bundle();
5750
5751 if (mTypes != null) {
5752 contentBundle.putStringArray(KEY_CONTENT_TYPE, mTypes);
5753 }
5754 if (mGenres != null) {
5755 contentBundle.putStringArray(KEY_CONTENT_GENRES, mGenres);
5756 }
5757 if (mPricingType != null) {
5758 contentBundle.putString(KEY_CONTENT_PRICING_TYPE, mPricingType);
5759 }
5760 if (mPricingValue != null) {
5761 contentBundle.putString(KEY_CONTENT_PRICING_VALUE, mPricingValue);
5762 }
5763 if (mContentStatus != -1) {
5764 contentBundle.putInt(KEY_CONTENT_STATUS, mContentStatus);
5765 }
5766 if (mMaturityRating != null) {
5767 contentBundle.putString(KEY_CONTENT_MATURITY_RATING, mMaturityRating);
5768 }
5769 if (mRunLength > 0) {
5770 contentBundle.putLong(KEY_CONTENT_RUN_LENGTH, mRunLength);
5771 }
5772
5773 builder.getExtras().putBundle(EXTRA_CONTENT_INFO_EXTENDER, contentBundle);
5774 return builder;
5775 }
5776
5777 /**
5778 * Sets the content types associated with the notification content. The first tag entry will
5779 * be considered the primary type for the content and will be used for ranking purposes.
5780 * Other secondary type tags may be provided, if applicable, and may be used for filtering
5781 * purposes.
5782 *
5783 * @param types Array of predefined type tags (see the <code>CONTENT_TYPE_*</code>
5784 * constants) that describe the content referred to by a notification.
5785 */
5786 public ContentInfoExtender setContentTypes(String[] types) {
5787 mTypes = types;
5788 return this;
5789 }
5790
5791 /**
5792 * Returns an array containing the content types that describe the content associated with
5793 * the notification. The first tag entry is considered the primary type for the content, and
5794 * is used for content ranking purposes.
5795 *
5796 * @return An array of predefined type tags (see the <code>CONTENT_TYPE_*</code> constants)
5797 * that describe the content associated with the notification.
5798 * @see ContentInfoExtender#setContentTypes
5799 */
5800 public String[] getContentTypes() {
5801 return mTypes;
5802 }
5803
5804 /**
5805 * Returns the primary content type tag for the content associated with the notification.
5806 *
5807 * @return A predefined type tag (see the <code>CONTENT_TYPE_*</code> constants) indicating
5808 * the primary type for the content associated with the notification.
5809 * @see ContentInfoExtender#setContentTypes
5810 */
5811 public String getPrimaryContentType() {
5812 if (mTypes == null || mTypes.length == 0) {
5813 return null;
5814 }
5815 return mTypes[0];
5816 }
5817
5818 /**
5819 * Sets the content genres associated with the notification content. These genres may be
5820 * used for content ranking. Genres are open ended String tags.
5821 * <p>
5822 * Some examples: "comedy", "action", "dance", "electronica", "racing", etc.
5823 *
5824 * @param genres Array of genre string tags that describe the content referred to by a
5825 * notification.
5826 */
5827 public ContentInfoExtender setGenres(String[] genres) {
5828 mGenres = genres;
5829 return this;
5830 }
5831
5832 /**
5833 * Returns an array containing the content genres that describe the content associated with
5834 * the notification.
5835 *
5836 * @return An array of genre tags that describe the content associated with the
5837 * notification.
5838 * @see ContentInfoExtender#setGenres
5839 */
5840 public String[] getGenres() {
5841 return mGenres;
5842 }
5843
5844 /**
5845 * Sets the pricing and availability information for the content associated with the
5846 * notification. The provided information will indicate the access model for the content
5847 * (free, rental, purchase or subscription) and the price value (if not free).
5848 *
5849 * @param priceType Pricing type for this content. Must be one of the predefined pricing
5850 * type tags (see the <code>CONTENT_PRICING_*</code> constants).
5851 * @param priceValue A string containing a representation of the content price in the
5852 * current locale and currency.
5853 * @return This object for method chaining.
5854 */
5855 public ContentInfoExtender setPricingInformation(String priceType, String priceValue) {
5856 mPricingType = priceType;
5857 mPricingValue = priceValue;
5858 return this;
5859 }
5860
5861 /**
5862 * Gets the pricing type for the content associated with the notification.
5863 *
5864 * @return A predefined tag indicating the pricing type for the content (see the <code>
5865 * CONTENT_PRICING_*</code> constants).
5866 * @see ContentInfoExtender#setPricingInformation
5867 */
5868 public String getPricingType() {
5869 return mPricingType;
5870 }
5871
5872 /**
5873 * Gets the price value (when applicable) for the content associated with a notification.
5874 * The value will be provided as a String containing the price in the appropriate currency
5875 * for the current locale.
5876 *
5877 * @return A string containing a representation of the content price in the current locale
5878 * and currency.
5879 * @see ContentInfoExtender#setPricingInformation
5880 */
5881 public String getPricingValue() {
5882 if (mPricingType == null || CONTENT_PRICING_FREE.equals(mPricingType)) {
5883 return null;
5884 }
5885 return mPricingValue;
5886 }
5887
5888 /**
5889 * Sets the availability status for the content associated with the notification. This
5890 * status indicates whether the referred content is ready to be consumed on the device, or
5891 * if the user must first purchase, rent, subscribe to, or download the content.
5892 *
5893 * @param contentStatus The status value for this content. Must be one of the predefined
5894 * content status values (see the <code>CONTENT_STATUS_*</code> constants).
5895 */
5896 public ContentInfoExtender setStatus(int contentStatus) {
5897 mContentStatus = contentStatus;
5898 return this;
5899 }
5900
5901 /**
5902 * Returns status value for the content associated with the notification. This status
5903 * indicates whether the referred content is ready to be consumed on the device, or if the
5904 * user must first purchase, rent, subscribe to, or download the content.
5905 *
5906 * @return The status value for this content, or -1 is a valid status has not been specified
5907 * (see the <code>CONTENT_STATUS_*</code> for the defined valid status values).
5908 * @see ContentInfoExtender#setStatus
5909 */
5910 public int getStatus() {
5911 return mContentStatus;
5912 }
5913
5914 /**
5915 * Sets the maturity level rating for the content associated with the notification.
5916 *
5917 * @param maturityRating A tag indicating the maturity level rating for the content. This
5918 * tag must be one of the predefined maturity rating tags (see the <code>
5919 * CONTENT_MATURITY_*</code> constants).
5920 */
5921 public ContentInfoExtender setMaturityRating(String maturityRating) {
5922 mMaturityRating = maturityRating;
5923 return this;
5924 }
5925
5926 /**
5927 * Returns the maturity level rating for the content associated with the notification.
5928 *
5929 * @return returns a predefined tag indicating the maturity level rating for the content
5930 * (see the <code> CONTENT_MATURITY_*</code> constants).
5931 * @see ContentInfoExtender#setMaturityRating
5932 */
5933 public String getMaturityRating() {
5934 return mMaturityRating;
5935 }
5936
5937 /**
5938 * Sets the running time (when applicable) for the content associated with the notification.
5939 *
5940 * @param length The runing time, in seconds, of the content associated with the
5941 * notification.
5942 */
5943 public ContentInfoExtender setRunningTime(long length) {
5944 mRunLength = length;
5945 return this;
5946 }
5947
5948 /**
5949 * Returns the running time for the content associated with the notification.
5950 *
5951 * @return The running time, in seconds, of the content associated with the notification.
5952 * @see ContentInfoExtender#setRunningTime
5953 */
5954 public long getRunningTime() {
5955 return mRunLength;
5956 }
5957 }
5958
5959 /**
Griff Hazen61a9e862014-05-22 16:05:19 -07005960 * Get an array of Notification objects from a parcelable array bundle field.
5961 * Update the bundle to have a typed array so fetches in the future don't need
5962 * to do an array copy.
5963 */
5964 private static Notification[] getNotificationArrayFromBundle(Bundle bundle, String key) {
5965 Parcelable[] array = bundle.getParcelableArray(key);
5966 if (array instanceof Notification[] || array == null) {
5967 return (Notification[]) array;
5968 }
5969 Notification[] typedArray = Arrays.copyOf(array, array.length,
5970 Notification[].class);
5971 bundle.putParcelableArray(key, typedArray);
5972 return typedArray;
5973 }
Christoph Studer4600f9b2014-07-22 22:44:43 +02005974
5975 private static class BuilderRemoteViews extends RemoteViews {
5976 public BuilderRemoteViews(Parcel parcel) {
5977 super(parcel);
5978 }
5979
Kenny Guy77320062014-08-27 21:37:15 +01005980 public BuilderRemoteViews(ApplicationInfo appInfo, int layoutId) {
5981 super(appInfo, layoutId);
Christoph Studer4600f9b2014-07-22 22:44:43 +02005982 }
5983
5984 @Override
5985 public BuilderRemoteViews clone() {
5986 Parcel p = Parcel.obtain();
5987 writeToParcel(p, 0);
5988 p.setDataPosition(0);
5989 BuilderRemoteViews brv = new BuilderRemoteViews(p);
5990 p.recycle();
5991 return brv;
5992 }
5993 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005994}