blob: b815eb1c2a5e37c99d126752f20624086f78a19e [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;
Julia Reynolds233a5f92015-10-19 13:51:23 -040024import android.annotation.SystemApi;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080025import android.content.Context;
26import android.content.Intent;
Kenny Guy77320062014-08-27 21:37:15 +010027import android.content.pm.ApplicationInfo;
Selim Cinek65b2e7c2015-10-26 14:11:31 -070028import android.content.pm.PackageManager;
Christoph Studer4600f9b2014-07-22 22:44:43 +020029import android.content.pm.PackageManager.NameNotFoundException;
Jorim Jaggief72a192014-08-26 21:57:46 +020030import android.content.res.ColorStateList;
Joe Onoratoef1e7762010-09-17 18:38:38 -040031import android.graphics.Bitmap;
Kenny Guy8a0101b2014-05-08 23:34:12 +010032import android.graphics.Canvas;
Jorim Jaggi5c2d8462014-03-21 17:37:00 +010033import android.graphics.PorterDuff;
Kenny Guy8a0101b2014-05-08 23:34:12 +010034import android.graphics.drawable.Drawable;
Dan Sandlerd63f9322015-05-06 15:18:49 -040035import android.graphics.drawable.Icon;
John Spurlockc0650f022014-07-19 13:22:39 -040036import android.media.AudioAttributes;
Jeff Sharkey098d5802012-04-26 17:30:34 -070037import android.media.AudioManager;
Jeff Browndba34ba2014-06-24 20:46:03 -070038import android.media.session.MediaSession;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080039import android.net.Uri;
Daniel Sandlerdcbaf662013-04-26 16:23:09 -040040import android.os.BadParcelableException;
Christoph Studer239f8352014-08-25 15:13:18 +020041import android.os.Build;
Daniel Sandler2561b0b2012-02-13 21:04:12 -050042import android.os.Bundle;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080043import android.os.Parcel;
44import android.os.Parcelable;
Daniel Sandlera2985ed2012-04-03 16:42:00 -040045import android.os.SystemClock;
Jeff Sharkey6d515712012-09-20 16:06:08 -070046import android.os.UserHandle;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080047import android.text.TextUtils;
Daniel Sandlerdcbaf662013-04-26 16:23:09 -040048import android.util.Log;
Daniel Sandler9f7936a2012-05-21 16:14:28 -040049import android.util.TypedValue;
Griff Hazen61a9e862014-05-22 16:05:19 -070050import android.view.Gravity;
Joe Onorato8595a3d2010-11-19 18:12:07 -080051import android.view.View;
Jeff Sharkey1c400132011-08-05 14:50:13 -070052import android.widget.ProgressBar;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080053import android.widget.RemoteViews;
54
Griff Hazen959591e2014-05-15 22:26:18 -070055import com.android.internal.R;
Griff Hazenc091ba82014-05-16 10:13:26 -070056import com.android.internal.util.NotificationColorUtil;
Griff Hazen959591e2014-05-15 22:26:18 -070057
Tor Norbyed9273d62013-05-30 15:59:53 -070058import java.lang.annotation.Retention;
59import java.lang.annotation.RetentionPolicy;
Christoph Studer4600f9b2014-07-22 22:44:43 +020060import java.lang.reflect.Constructor;
Andy Stadler110988c2010-12-03 14:29:16 -080061import java.text.NumberFormat;
Daniel Sandler2561b0b2012-02-13 21:04:12 -050062import java.util.ArrayList;
Griff Hazen61a9e862014-05-22 16:05:19 -070063import java.util.Arrays;
Griff Hazen5cadc3b2014-05-20 09:55:39 -070064import java.util.Collections;
Griff Hazen61a9e862014-05-22 16:05:19 -070065import java.util.List;
Julia Reynolds74303cf2015-10-16 11:37:55 -040066import java.util.Objects;
Joe Onorato561d3852010-11-20 18:09:34 -080067
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080068/**
69 * A class that represents how a persistent notification is to be presented to
70 * the user using the {@link android.app.NotificationManager}.
71 *
Joe Onoratocb109a02011-01-18 17:57:41 -080072 * <p>The {@link Notification.Builder Notification.Builder} has been added to make it
73 * easier to construct Notifications.</p>
74 *
Joe Fernandez558459f2011-10-13 16:47:36 -070075 * <div class="special reference">
76 * <h3>Developer Guides</h3>
77 * <p>For a guide to creating notifications, read the
78 * <a href="{@docRoot}guide/topics/ui/notifiers/notifications.html">Status Bar Notifications</a>
79 * developer guide.</p>
80 * </div>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080081 */
82public class Notification implements Parcelable
83{
Daniel Sandlerdcbaf662013-04-26 16:23:09 -040084 private static final String TAG = "Notification";
85
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080086 /**
Daniel Sandler01df1c62014-06-09 10:54:01 -040087 * An activity that provides a user interface for adjusting notification preferences for its
88 * containing application. Optional but recommended for apps that post
89 * {@link android.app.Notification Notifications}.
90 */
91 @SdkConstant(SdkConstantType.INTENT_CATEGORY)
92 public static final String INTENT_CATEGORY_NOTIFICATION_PREFERENCES
93 = "android.intent.category.NOTIFICATION_PREFERENCES";
94
95 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080096 * Use all default values (where applicable).
97 */
98 public static final int DEFAULT_ALL = ~0;
Daniel Sandler2561b0b2012-02-13 21:04:12 -050099
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800100 /**
101 * Use the default notification sound. This will ignore any given
102 * {@link #sound}.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500103 *
Chris Wren47c20a12014-06-18 17:27:29 -0400104 * <p>
105 * A notification that is noisy is more likely to be presented as a heads-up notification.
106 * </p>
107 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800108 * @see #defaults
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500109 */
110
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800111 public static final int DEFAULT_SOUND = 1;
112
113 /**
114 * Use the default notification vibrate. This will ignore any given
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500115 * {@link #vibrate}. Using phone vibration requires the
Scott Mainb8b36452009-04-26 15:50:49 -0700116 * {@link android.Manifest.permission#VIBRATE VIBRATE} permission.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500117 *
Chris Wren47c20a12014-06-18 17:27:29 -0400118 * <p>
119 * A notification that vibrates is more likely to be presented as a heads-up notification.
120 * </p>
121 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800122 * @see #defaults
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500123 */
124
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800125 public static final int DEFAULT_VIBRATE = 2;
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500126
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800127 /**
128 * Use the default notification lights. This will ignore the
129 * {@link #FLAG_SHOW_LIGHTS} bit, and {@link #ledARGB}, {@link #ledOffMS}, or
130 * {@link #ledOnMS}.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500131 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800132 * @see #defaults
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500133 */
134
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800135 public static final int DEFAULT_LIGHTS = 4;
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500136
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800137 /**
Christoph Studer535ec612014-09-03 15:47:47 +0200138 * Maximum length of CharSequences accepted by Builder and friends.
139 *
140 * <p>
141 * Avoids spamming the system with overly large strings such as full e-mails.
142 */
143 private static final int MAX_CHARSEQUENCE_LENGTH = 5 * 1024;
144
145 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500146 * A timestamp related to this notification, in milliseconds since the epoch.
Joe Malin8d40d042012-11-05 11:36:40 -0800147 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500148 * Default value: {@link System#currentTimeMillis() Now}.
149 *
150 * Choose a timestamp that will be most relevant to the user. For most finite events, this
151 * corresponds to the time the event happened (or will happen, in the case of events that have
152 * yet to occur but about which the user is being informed). Indefinite events should be
Joe Malin8d40d042012-11-05 11:36:40 -0800153 * timestamped according to when the activity began.
154 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500155 * Some examples:
Joe Malin8d40d042012-11-05 11:36:40 -0800156 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500157 * <ul>
158 * <li>Notification of a new chat message should be stamped when the message was received.</li>
159 * <li>Notification of an ongoing file download (with a progress bar, for example) should be stamped when the download started.</li>
160 * <li>Notification of a completed file download should be stamped when the download finished.</li>
161 * <li>Notification of an upcoming meeting should be stamped with the time the meeting will begin (that is, in the future).</li>
162 * <li>Notification of an ongoing stopwatch (increasing timer) should be stamped with the watch's start time.
163 * <li>Notification of an ongoing countdown timer should be stamped with the timer's end time.
Joe Malin8d40d042012-11-05 11:36:40 -0800164 * </ul>
165 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800166 */
167 public long when;
168
169 /**
170 * The resource id of a drawable to use as the icon in the status bar.
Dan Sandler86647982015-05-13 23:41:13 -0400171 *
172 * @deprecated Use {@link Builder#setSmallIcon(Icon)} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800173 */
Dan Sandler86647982015-05-13 23:41:13 -0400174 @Deprecated
Tor Norbye7b9c9122013-05-30 16:48:33 -0700175 @DrawableRes
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800176 public int icon;
177
178 /**
Joe Onorato46439ce2010-11-19 13:56:21 -0800179 * If the icon in the status bar is to have more than one level, you can set this. Otherwise,
180 * leave it at its default value of 0.
181 *
182 * @see android.widget.ImageView#setImageLevel
Griff Hazen959591e2014-05-15 22:26:18 -0700183 * @see android.graphics.drawable.Drawable#setLevel
Joe Onorato46439ce2010-11-19 13:56:21 -0800184 */
185 public int iconLevel;
186
187 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500188 * The number of events that this notification represents. For example, in a new mail
189 * notification, this could be the number of unread messages.
Joe Malin8d40d042012-11-05 11:36:40 -0800190 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500191 * The system may or may not use this field to modify the appearance of the notification. For
192 * example, before {@link android.os.Build.VERSION_CODES#HONEYCOMB}, this number was
193 * superimposed over the icon in the status bar. Starting with
194 * {@link android.os.Build.VERSION_CODES#HONEYCOMB}, the template used by
195 * {@link Notification.Builder} has displayed the number in the expanded notification view.
Joe Malin8d40d042012-11-05 11:36:40 -0800196 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500197 * If the number is 0 or negative, it is never shown.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800198 */
199 public int number;
200
201 /**
202 * The intent to execute when the expanded status entry is clicked. If
203 * this is an activity, it must include the
204 * {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} flag, which requires
Scott Main7aee61f2011-02-08 11:25:01 -0800205 * that you take care of task management as described in the
206 * <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back
Dianne Hackborn6ceca582012-01-10 15:24:26 -0800207 * Stack</a> document. In particular, make sure to read the notification section
208 * <a href="{@docRoot}guide/topics/ui/notifiers/notifications.html#HandlingNotifications">Handling
209 * Notifications</a> for the correct ways to launch an application from a
210 * notification.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800211 */
212 public PendingIntent contentIntent;
213
214 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500215 * The intent to execute when the notification is explicitly dismissed by the user, either with
216 * the "Clear All" button or by swiping it away individually.
217 *
218 * This probably shouldn't be launching an activity since several of those will be sent
219 * at the same time.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800220 */
221 public PendingIntent deleteIntent;
222
223 /**
Dianne Hackborn170bae72010-09-03 15:14:28 -0700224 * An intent to launch instead of posting the notification to the status bar.
Joe Onoratocb109a02011-01-18 17:57:41 -0800225 *
Chris Wren47c20a12014-06-18 17:27:29 -0400226 * <p>
227 * The system UI may choose to display a heads-up notification, instead of
228 * launching this intent, while the user is using the device.
229 * </p>
230 *
Joe Onoratocb109a02011-01-18 17:57:41 -0800231 * @see Notification.Builder#setFullScreenIntent
Daniel Sandlere46cbd32010-06-17 10:35:26 -0400232 */
233 public PendingIntent fullScreenIntent;
234
235 /**
Dan Sandler5fcdf6e2014-07-18 11:31:15 -0400236 * Text that summarizes this notification for accessibility services.
237 *
238 * As of the L release, this text is no longer shown on screen, but it is still useful to
239 * accessibility services (where it serves as an audible announcement of the notification's
240 * appearance).
Joe Onoratoef1e7762010-09-17 18:38:38 -0400241 *
Joe Onorato46439ce2010-11-19 13:56:21 -0800242 * @see #tickerView
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800243 */
244 public CharSequence tickerText;
245
246 /**
Dan Sandler5fcdf6e2014-07-18 11:31:15 -0400247 * Formerly, a view showing the {@link #tickerText}.
248 *
249 * No longer displayed in the status bar as of API 21.
Joe Onoratoef1e7762010-09-17 18:38:38 -0400250 */
Dan Sandler5fcdf6e2014-07-18 11:31:15 -0400251 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -0800252 public RemoteViews tickerView;
Joe Onoratoef1e7762010-09-17 18:38:38 -0400253
254 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400255 * The view that will represent this notification in the notification list (which is pulled
256 * down from the status bar).
257 *
258 * As of N, this field is not used. The notification view is determined by the inputs to
259 * {@link Notification.Builder}; a custom RemoteViews can optionally be
260 * supplied with {@link Notification.Builder#setCustomContentView(RemoteViews)}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800261 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400262 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800263 public RemoteViews contentView;
264
Daniel Sandlera0a938c2012-03-15 08:42:37 -0400265 /**
Daniel Sandler4dfbe832012-04-11 14:51:46 -0400266 * A large-format version of {@link #contentView}, giving the Notification an
Daniel Sandlerf3b73432012-03-27 15:01:25 -0400267 * opportunity to show more detail. The system UI may choose to show this
268 * instead of the normal content view at its discretion.
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400269 *
270 * As of N, this field is not used. The expanded notification view is determined by the
271 * inputs to {@link Notification.Builder}; a custom RemoteViews can optionally be
272 * supplied with {@link Notification.Builder#setCustomBigContentView(RemoteViews)}.
Daniel Sandlerf3b73432012-03-27 15:01:25 -0400273 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400274 @Deprecated
Daniel Sandlerf3b73432012-03-27 15:01:25 -0400275 public RemoteViews bigContentView;
276
Chris Wren8fd39ec2014-02-27 17:43:26 -0500277
278 /**
Chris Wren47c20a12014-06-18 17:27:29 -0400279 * A medium-format version of {@link #contentView}, providing the Notification an
280 * opportunity to add action buttons to contentView. At its discretion, the system UI may
281 * choose to show this as a heads-up notification, which will pop up so the user can see
282 * it without leaving their current activity.
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400283 *
284 * As of N, this field is not used. The heads-up notification view is determined by the
285 * inputs to {@link Notification.Builder}; a custom RemoteViews can optionally be
286 * supplied with {@link Notification.Builder#setCustomHeadsUpContentView(RemoteViews)}.
Chris Wren8fd39ec2014-02-27 17:43:26 -0500287 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400288 @Deprecated
Chris Wren8fd39ec2014-02-27 17:43:26 -0500289 public RemoteViews headsUpContentView;
290
Daniel Sandlerf3b73432012-03-27 15:01:25 -0400291 /**
Dan Sandler86647982015-05-13 23:41:13 -0400292 * A large bitmap to be shown in the notification content area.
293 *
294 * @deprecated Use {@link Builder#setLargeIcon(Icon)} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800295 */
Dan Sandler86647982015-05-13 23:41:13 -0400296 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -0800297 public Bitmap largeIcon;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800298
299 /**
300 * The sound to play.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500301 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800302 * <p>
Chris Wren47c20a12014-06-18 17:27:29 -0400303 * A notification that is noisy is more likely to be presented as a heads-up notification.
304 * </p>
305 *
306 * <p>
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500307 * To play the default notification sound, see {@link #defaults}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800308 * </p>
309 */
310 public Uri sound;
311
312 /**
313 * Use this constant as the value for audioStreamType to request that
314 * the default stream type for notifications be used. Currently the
Jeff Sharkey098d5802012-04-26 17:30:34 -0700315 * default stream type is {@link AudioManager#STREAM_NOTIFICATION}.
John Spurlockc0650f022014-07-19 13:22:39 -0400316 *
317 * @deprecated Use {@link #audioAttributes} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800318 */
Jean-Michel Trivi81f871e2014-08-06 16:32:38 -0700319 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800320 public static final int STREAM_DEFAULT = -1;
321
322 /**
323 * The audio stream type to use when playing the sound.
324 * Should be one of the STREAM_ constants from
325 * {@link android.media.AudioManager}.
John Spurlockc0650f022014-07-19 13:22:39 -0400326 *
327 * @deprecated Use {@link #audioAttributes} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800328 */
Jean-Michel Trivi81f871e2014-08-06 16:32:38 -0700329 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800330 public int audioStreamType = STREAM_DEFAULT;
331
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800332 /**
John Spurlockc0650f022014-07-19 13:22:39 -0400333 * The default value of {@link #audioAttributes}.
334 */
335 public static final AudioAttributes AUDIO_ATTRIBUTES_DEFAULT = new AudioAttributes.Builder()
336 .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
337 .setUsage(AudioAttributes.USAGE_NOTIFICATION)
338 .build();
339
340 /**
341 * The {@link AudioAttributes audio attributes} to use when playing the sound.
342 */
343 public AudioAttributes audioAttributes = AUDIO_ATTRIBUTES_DEFAULT;
344
345 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500346 * The pattern with which to vibrate.
347 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800348 * <p>
349 * To vibrate the default pattern, see {@link #defaults}.
350 * </p>
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500351 *
Chris Wren47c20a12014-06-18 17:27:29 -0400352 * <p>
353 * A notification that vibrates is more likely to be presented as a heads-up notification.
354 * </p>
355 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800356 * @see android.os.Vibrator#vibrate(long[],int)
357 */
358 public long[] vibrate;
359
360 /**
361 * The color of the led. The hardware will do its best approximation.
362 *
363 * @see #FLAG_SHOW_LIGHTS
364 * @see #flags
365 */
Tor Norbye80756e32015-03-02 09:39:27 -0800366 @ColorInt
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800367 public int ledARGB;
368
369 /**
370 * The number of milliseconds for the LED to be on while it's flashing.
371 * The hardware will do its best approximation.
372 *
373 * @see #FLAG_SHOW_LIGHTS
374 * @see #flags
375 */
376 public int ledOnMS;
377
378 /**
379 * The number of milliseconds for the LED to be off while it's flashing.
380 * The hardware will do its best approximation.
381 *
382 * @see #FLAG_SHOW_LIGHTS
383 * @see #flags
384 */
385 public int ledOffMS;
386
387 /**
388 * Specifies which values should be taken from the defaults.
389 * <p>
390 * To set, OR the desired from {@link #DEFAULT_SOUND},
391 * {@link #DEFAULT_VIBRATE}, {@link #DEFAULT_LIGHTS}. For all default
392 * values, use {@link #DEFAULT_ALL}.
393 * </p>
394 */
395 public int defaults;
396
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800397 /**
398 * Bit to be bitwise-ored into the {@link #flags} field that should be
399 * set if you want the LED on for this notification.
400 * <ul>
401 * <li>To turn the LED off, pass 0 in the alpha channel for colorARGB
402 * or 0 for both ledOnMS and ledOffMS.</li>
403 * <li>To turn the LED on, pass 1 for ledOnMS and 0 for ledOffMS.</li>
404 * <li>To flash the LED, pass the number of milliseconds that it should
405 * be on and off to ledOnMS and ledOffMS.</li>
406 * </ul>
407 * <p>
408 * Since hardware varies, you are not guaranteed that any of the values
409 * you pass are honored exactly. Use the system defaults (TODO) if possible
410 * because they will be set to values that work on any given hardware.
411 * <p>
412 * The alpha channel must be set for forward compatibility.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500413 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800414 */
415 public static final int FLAG_SHOW_LIGHTS = 0x00000001;
416
417 /**
418 * Bit to be bitwise-ored into the {@link #flags} field that should be
419 * set if this notification is in reference to something that is ongoing,
420 * like a phone call. It should not be set if this notification is in
421 * reference to something that happened at a particular point in time,
422 * like a missed phone call.
423 */
424 public static final int FLAG_ONGOING_EVENT = 0x00000002;
425
426 /**
427 * Bit to be bitwise-ored into the {@link #flags} field that if set,
Scott Mainb8b36452009-04-26 15:50:49 -0700428 * the audio will be repeated until the notification is
429 * cancelled or the notification window is opened.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800430 */
431 public static final int FLAG_INSISTENT = 0x00000004;
432
433 /**
434 * Bit to be bitwise-ored into the {@link #flags} field that should be
Griff Hazen293977b2014-04-28 08:37:20 -0700435 * set if you would only like the sound, vibrate and ticker to be played
436 * if the notification was not already showing.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800437 */
438 public static final int FLAG_ONLY_ALERT_ONCE = 0x00000008;
439
440 /**
441 * Bit to be bitwise-ored into the {@link #flags} field that should be
442 * set if the notification should be canceled when it is clicked by the
Daniel Sandler8aa9ae62012-12-04 23:31:47 -0500443 * user.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800444 */
445 public static final int FLAG_AUTO_CANCEL = 0x00000010;
446
447 /**
448 * Bit to be bitwise-ored into the {@link #flags} field that should be
449 * set if the notification should not be canceled when the user clicks
450 * the Clear all button.
451 */
452 public static final int FLAG_NO_CLEAR = 0x00000020;
453
Dianne Hackbornd8a43f62009-08-17 23:33:56 -0700454 /**
455 * Bit to be bitwise-ored into the {@link #flags} field that should be
456 * set if this notification represents a currently running service. This
457 * will normally be set for you by {@link Service#startForeground}.
458 */
459 public static final int FLAG_FOREGROUND_SERVICE = 0x00000040;
460
Daniel Sandlere46cbd32010-06-17 10:35:26 -0400461 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500462 * Obsolete flag indicating high-priority notifications; use the priority field instead.
Joe Malin8d40d042012-11-05 11:36:40 -0800463 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500464 * @deprecated Use {@link #priority} with a positive value.
Daniel Sandlere46cbd32010-06-17 10:35:26 -0400465 */
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500466 public static final int FLAG_HIGH_PRIORITY = 0x00000080;
Daniel Sandlere46cbd32010-06-17 10:35:26 -0400467
Griff Hazendfcb0802014-02-11 12:00:00 -0800468 /**
469 * Bit to be bitswise-ored into the {@link #flags} field that should be
470 * set if this notification is relevant to the current device only
471 * and it is not recommended that it bridge to other devices.
472 */
473 public static final int FLAG_LOCAL_ONLY = 0x00000100;
474
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700475 /**
476 * Bit to be bitswise-ored into the {@link #flags} field that should be
477 * set if this notification is the group summary for a group of notifications.
478 * Grouped notifications may display in a cluster or stack on devices which
479 * support such rendering. Requires a group key also be set using {@link Builder#setGroup}.
480 */
481 public static final int FLAG_GROUP_SUMMARY = 0x00000200;
482
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800483 public int flags;
484
Tor Norbyed9273d62013-05-30 15:59:53 -0700485 /** @hide */
486 @IntDef({PRIORITY_DEFAULT,PRIORITY_LOW,PRIORITY_MIN,PRIORITY_HIGH,PRIORITY_MAX})
487 @Retention(RetentionPolicy.SOURCE)
488 public @interface Priority {}
489
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800490 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500491 * Default notification {@link #priority}. If your application does not prioritize its own
492 * notifications, use this value for all notifications.
493 */
494 public static final int PRIORITY_DEFAULT = 0;
495
496 /**
497 * Lower {@link #priority}, for items that are less important. The UI may choose to show these
498 * items smaller, or at a different position in the list, compared with your app's
499 * {@link #PRIORITY_DEFAULT} items.
500 */
501 public static final int PRIORITY_LOW = -1;
502
503 /**
504 * Lowest {@link #priority}; these items might not be shown to the user except under special
505 * circumstances, such as detailed notification logs.
506 */
507 public static final int PRIORITY_MIN = -2;
508
509 /**
510 * Higher {@link #priority}, for more important notifications or alerts. The UI may choose to
511 * show these items larger, or at a different position in notification lists, compared with
512 * your app's {@link #PRIORITY_DEFAULT} items.
513 */
514 public static final int PRIORITY_HIGH = 1;
515
516 /**
517 * Highest {@link #priority}, for your application's most important items that require the
518 * user's prompt attention or input.
519 */
520 public static final int PRIORITY_MAX = 2;
521
522 /**
523 * Relative priority for this notification.
Joe Malin8d40d042012-11-05 11:36:40 -0800524 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500525 * Priority is an indication of how much of the user's valuable attention should be consumed by
526 * this notification. Low-priority notifications may be hidden from the user in certain
527 * situations, while the user might be interrupted for a higher-priority notification. The
Daniel Sandler6738eee2012-11-16 12:03:32 -0500528 * system will make a determination about how to interpret this priority when presenting
529 * the notification.
Chris Wren47c20a12014-06-18 17:27:29 -0400530 *
531 * <p>
532 * A notification that is at least {@link #PRIORITY_HIGH} is more likely to be presented
533 * as a heads-up notification.
534 * </p>
535 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500536 */
Tor Norbyed9273d62013-05-30 15:59:53 -0700537 @Priority
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500538 public int priority;
Joe Malin8d40d042012-11-05 11:36:40 -0800539
Dan Sandler26e81cf2014-05-06 10:01:27 -0400540 /**
541 * Accent color (an ARGB integer like the constants in {@link android.graphics.Color})
542 * to be applied by the standard Style templates when presenting this notification.
543 *
544 * The current template design constructs a colorful header image by overlaying the
545 * {@link #icon} image (stenciled in white) atop a field of this color. Alpha components are
546 * ignored.
547 */
Tor Norbye80756e32015-03-02 09:39:27 -0800548 @ColorInt
Dan Sandler26e81cf2014-05-06 10:01:27 -0400549 public int color = COLOR_DEFAULT;
550
551 /**
552 * Special value of {@link #color} telling the system not to decorate this notification with
553 * any special color but instead use default colors when presenting this notification.
554 */
Tor Norbye80756e32015-03-02 09:39:27 -0800555 @ColorInt
Dan Sandler26e81cf2014-05-06 10:01:27 -0400556 public static final int COLOR_DEFAULT = 0; // AKA Color.TRANSPARENT
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600557
558 /**
559 * Sphere of visibility of this notification, which affects how and when the SystemUI reveals
560 * the notification's presence and contents in untrusted situations (namely, on the secure
561 * lockscreen).
562 *
563 * The default level, {@link #VISIBILITY_PRIVATE}, behaves exactly as notifications have always
564 * done on Android: The notification's {@link #icon} and {@link #tickerText} (if available) are
565 * shown in all situations, but the contents are only available if the device is unlocked for
566 * the appropriate user.
567 *
568 * A more permissive policy can be expressed by {@link #VISIBILITY_PUBLIC}; such a notification
569 * can be read even in an "insecure" context (that is, above a secure lockscreen).
570 * To modify the public version of this notification—for example, to redact some portions—see
571 * {@link Builder#setPublicVersion(Notification)}.
572 *
573 * Finally, a notification can be made {@link #VISIBILITY_SECRET}, which will suppress its icon
574 * and ticker until the user has bypassed the lockscreen.
575 */
576 public int visibility;
577
Griff Hazenfc3922d2014-08-20 11:56:44 -0700578 /**
579 * Notification visibility: Show this notification in its entirety on all lockscreens.
580 *
581 * {@see #visibility}
582 */
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600583 public static final int VISIBILITY_PUBLIC = 1;
Griff Hazenfc3922d2014-08-20 11:56:44 -0700584
585 /**
586 * Notification visibility: Show this notification on all lockscreens, but conceal sensitive or
587 * private information on secure lockscreens.
588 *
589 * {@see #visibility}
590 */
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600591 public static final int VISIBILITY_PRIVATE = 0;
Griff Hazenfc3922d2014-08-20 11:56:44 -0700592
593 /**
594 * Notification visibility: Do not reveal any part of this notification on a secure lockscreen.
595 *
596 * {@see #visibility}
597 */
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600598 public static final int VISIBILITY_SECRET = -1;
599
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500600 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400601 * Notification category: incoming call (voice or video) or similar synchronous communication request.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500602 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400603 public static final String CATEGORY_CALL = "call";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500604
605 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400606 * Notification category: incoming direct message (SMS, instant message, etc.).
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500607 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400608 public static final String CATEGORY_MESSAGE = "msg";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500609
610 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400611 * Notification category: asynchronous bulk message (email).
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500612 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400613 public static final String CATEGORY_EMAIL = "email";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500614
615 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400616 * Notification category: calendar event.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500617 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400618 public static final String CATEGORY_EVENT = "event";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500619
620 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400621 * Notification category: promotion or advertisement.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500622 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400623 public static final String CATEGORY_PROMO = "promo";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500624
625 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400626 * Notification category: alarm or timer.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500627 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400628 public static final String CATEGORY_ALARM = "alarm";
629
630 /**
631 * Notification category: progress of a long-running background operation.
632 */
633 public static final String CATEGORY_PROGRESS = "progress";
634
635 /**
636 * Notification category: social network or sharing update.
637 */
638 public static final String CATEGORY_SOCIAL = "social";
639
640 /**
641 * Notification category: error in background operation or authentication status.
642 */
643 public static final String CATEGORY_ERROR = "err";
644
645 /**
646 * Notification category: media transport control for playback.
647 */
648 public static final String CATEGORY_TRANSPORT = "transport";
649
650 /**
651 * Notification category: system or device status update. Reserved for system use.
652 */
653 public static final String CATEGORY_SYSTEM = "sys";
654
655 /**
656 * Notification category: indication of running background service.
657 */
658 public static final String CATEGORY_SERVICE = "service";
659
660 /**
John Spurlock0a69c8c2014-03-21 13:30:57 -0400661 * Notification category: a specific, timely recommendation for a single thing.
662 * For example, a news app might want to recommend a news story it believes the user will
663 * want to read next.
664 */
665 public static final String CATEGORY_RECOMMENDATION = "recommendation";
666
667 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400668 * Notification category: ongoing information about device or contextual status.
669 */
670 public static final String CATEGORY_STATUS = "status";
671
672 /**
John Spurlock24d3dad2015-04-02 12:24:02 -0400673 * Notification category: user-scheduled reminder.
674 */
675 public static final String CATEGORY_REMINDER = "reminder";
676
677 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400678 * One of the predefined notification categories (see the <code>CATEGORY_*</code> constants)
679 * that best describes this Notification. May be used by the system for ranking and filtering.
680 */
681 public String category;
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500682
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700683 private String mGroupKey;
684
685 /**
686 * Get the key used to group this notification into a cluster or stack
687 * with other notifications on devices which support such rendering.
688 */
689 public String getGroup() {
690 return mGroupKey;
691 }
692
693 private String mSortKey;
694
695 /**
696 * Get a sort key that orders this notification among other notifications from the
697 * same package. This can be useful if an external sort was already applied and an app
698 * would like to preserve this. Notifications will be sorted lexicographically using this
699 * value, although providing different priorities in addition to providing sort key may
700 * cause this value to be ignored.
701 *
702 * <p>This sort key can also be used to order members of a notification group. See
703 * {@link Builder#setGroup}.
704 *
705 * @see String#compareTo(String)
706 */
707 public String getSortKey() {
708 return mSortKey;
709 }
710
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500711 /**
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400712 * Additional semantic data to be carried around with this Notification.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400713 * <p>
714 * The extras keys defined here are intended to capture the original inputs to {@link Builder}
715 * APIs, and are intended to be used by
716 * {@link android.service.notification.NotificationListenerService} implementations to extract
717 * detailed information from notification objects.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500718 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400719 public Bundle extras = new Bundle();
720
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400721 /**
722 * {@link #extras} key: this is the title of the notification,
723 * as supplied to {@link Builder#setContentTitle(CharSequence)}.
724 */
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -0500725 public static final String EXTRA_TITLE = "android.title";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400726
727 /**
728 * {@link #extras} key: this is the title of the notification when shown in expanded form,
729 * e.g. as supplied to {@link BigTextStyle#setBigContentTitle(CharSequence)}.
730 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400731 public static final String EXTRA_TITLE_BIG = EXTRA_TITLE + ".big";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400732
733 /**
734 * {@link #extras} key: this is the main text payload, as supplied to
735 * {@link Builder#setContentText(CharSequence)}.
736 */
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -0500737 public static final String EXTRA_TEXT = "android.text";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400738
739 /**
740 * {@link #extras} key: this is a third line of text, as supplied to
741 * {@link Builder#setSubText(CharSequence)}.
742 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400743 public static final String EXTRA_SUB_TEXT = "android.subText";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400744
745 /**
746 * {@link #extras} key: this is a small piece of additional text as supplied to
747 * {@link Builder#setContentInfo(CharSequence)}.
748 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400749 public static final String EXTRA_INFO_TEXT = "android.infoText";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400750
751 /**
752 * {@link #extras} key: this is a line of summary information intended to be shown
753 * alongside expanded notifications, as supplied to (e.g.)
754 * {@link BigTextStyle#setSummaryText(CharSequence)}.
755 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400756 public static final String EXTRA_SUMMARY_TEXT = "android.summaryText";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400757
758 /**
Christoph Studer4600f9b2014-07-22 22:44:43 +0200759 * {@link #extras} key: this is the longer text shown in the big form of a
760 * {@link BigTextStyle} notification, as supplied to
761 * {@link BigTextStyle#bigText(CharSequence)}.
762 */
763 public static final String EXTRA_BIG_TEXT = "android.bigText";
764
765 /**
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400766 * {@link #extras} key: this is the resource ID of the notification's main small icon, as
767 * supplied to {@link Builder#setSmallIcon(int)}.
768 */
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -0500769 public static final String EXTRA_SMALL_ICON = "android.icon";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400770
771 /**
772 * {@link #extras} key: this is a bitmap to be used instead of the small icon when showing the
773 * notification payload, as
774 * supplied to {@link Builder#setLargeIcon(android.graphics.Bitmap)}.
775 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400776 public static final String EXTRA_LARGE_ICON = "android.largeIcon";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400777
778 /**
779 * {@link #extras} key: this is a bitmap to be used instead of the one from
780 * {@link Builder#setLargeIcon(android.graphics.Bitmap)} when the notification is
781 * shown in its expanded form, as supplied to
782 * {@link BigPictureStyle#bigLargeIcon(android.graphics.Bitmap)}.
783 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400784 public static final String EXTRA_LARGE_ICON_BIG = EXTRA_LARGE_ICON + ".big";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400785
786 /**
787 * {@link #extras} key: this is the progress value supplied to
788 * {@link Builder#setProgress(int, int, boolean)}.
789 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400790 public static final String EXTRA_PROGRESS = "android.progress";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400791
792 /**
793 * {@link #extras} key: this is the maximum value supplied to
794 * {@link Builder#setProgress(int, int, boolean)}.
795 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400796 public static final String EXTRA_PROGRESS_MAX = "android.progressMax";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400797
798 /**
799 * {@link #extras} key: whether the progress bar is indeterminate, supplied to
800 * {@link Builder#setProgress(int, int, boolean)}.
801 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400802 public static final String EXTRA_PROGRESS_INDETERMINATE = "android.progressIndeterminate";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400803
804 /**
805 * {@link #extras} key: whether {@link #when} should be shown as a count-up timer (specifically
806 * a {@link android.widget.Chronometer}) instead of a timestamp, as supplied to
807 * {@link Builder#setUsesChronometer(boolean)}.
808 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400809 public static final String EXTRA_SHOW_CHRONOMETER = "android.showChronometer";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400810
811 /**
812 * {@link #extras} key: whether {@link #when} should be shown,
813 * as supplied to {@link Builder#setShowWhen(boolean)}.
814 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400815 public static final String EXTRA_SHOW_WHEN = "android.showWhen";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400816
817 /**
818 * {@link #extras} key: this is a bitmap to be shown in {@link BigPictureStyle} expanded
819 * notifications, supplied to {@link BigPictureStyle#bigPicture(android.graphics.Bitmap)}.
820 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400821 public static final String EXTRA_PICTURE = "android.picture";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400822
823 /**
824 * {@link #extras} key: An array of CharSequences to show in {@link InboxStyle} expanded
825 * notifications, each of which was supplied to {@link InboxStyle#addLine(CharSequence)}.
826 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400827 public static final String EXTRA_TEXT_LINES = "android.textLines";
Dan Sandler842dd772014-05-15 09:36:47 -0400828
829 /**
830 * {@link #extras} key: A string representing the name of the specific
831 * {@link android.app.Notification.Style} used to create this notification.
832 */
Chris Wren91ad5632013-06-05 15:05:57 -0400833 public static final String EXTRA_TEMPLATE = "android.template";
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400834
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400835 /**
Chris Wrene6c48932014-09-29 17:19:27 -0400836 * {@link #extras} key: A String array containing the people that this notification relates to,
837 * each of which was supplied to {@link Builder#addPerson(String)}.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400838 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400839 public static final String EXTRA_PEOPLE = "android.people";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500840
841 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400842 * Allow certain system-generated notifications to appear before the device is provisioned.
843 * Only available to notifications coming from the android package.
844 * @hide
845 */
846 public static final String EXTRA_ALLOW_DURING_SETUP = "android.allowDuringSetup";
847
848 /**
Jose Limae9e3b3b2014-05-18 23:44:50 -0700849 * {@link #extras} key: A
850 * {@link android.content.ContentUris content URI} pointing to an image that can be displayed
851 * in the background when the notification is selected. The URI must point to an image stream
852 * suitable for passing into
853 * {@link android.graphics.BitmapFactory#decodeStream(java.io.InputStream)
854 * BitmapFactory.decodeStream}; all other content types will be ignored. The content provider
855 * URI used for this purpose must require no permissions to read the image data.
856 */
857 public static final String EXTRA_BACKGROUND_IMAGE_URI = "android.backgroundImageUri";
858
859 /**
Dan Sandler842dd772014-05-15 09:36:47 -0400860 * {@link #extras} key: A
Jeff Browndba34ba2014-06-24 20:46:03 -0700861 * {@link android.media.session.MediaSession.Token} associated with a
Dan Sandler842dd772014-05-15 09:36:47 -0400862 * {@link android.app.Notification.MediaStyle} notification.
863 */
864 public static final String EXTRA_MEDIA_SESSION = "android.mediaSession";
865
866 /**
Bryan Mawhinneye191f902014-07-22 12:50:09 +0100867 * {@link #extras} key: the indices of actions to be shown in the compact view,
868 * as supplied to (e.g.) {@link MediaStyle#setShowActionsInCompactView(int...)}.
869 */
870 public static final String EXTRA_COMPACT_ACTIONS = "android.compactActions";
871
Christoph Studer943aa672014-08-03 20:31:16 +0200872 /**
Kenny Guy8942bcd2014-09-08 21:09:47 +0100873 * {@link #extras} key: the user that built the notification.
874 *
875 * @hide
876 */
877 public static final String EXTRA_ORIGINATING_USERID = "android.originatingUserId";
878
879 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400880 * @hide
881 */
882 public static final String EXTRA_BUILDER_APPLICATION_INFO = "android.appInfo";
883
Dan Sandlerd63f9322015-05-06 15:18:49 -0400884 private Icon mSmallIcon;
885 private Icon mLargeIcon;
886
Chris Wren51c75102013-07-16 20:49:17 -0400887 /**
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400888 * Structure to encapsulate a named action that can be shown as part of this notification.
889 * It must include an icon, a label, and a {@link PendingIntent} to be fired when the action is
890 * selected by the user.
891 * <p>
Griff Hazen959591e2014-05-15 22:26:18 -0700892 * Apps should use {@link Notification.Builder#addAction(int, CharSequence, PendingIntent)}
893 * or {@link Notification.Builder#addAction(Notification.Action)}
894 * to attach actions.
Daniel Sandlera0a938c2012-03-15 08:42:37 -0400895 */
Daniel Sandlerea2a3172013-02-20 22:24:20 -0500896 public static class Action implements Parcelable {
Griff Hazen959591e2014-05-15 22:26:18 -0700897 private final Bundle mExtras;
Dan Sandler86647982015-05-13 23:41:13 -0400898 private Icon mIcon;
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.
Dan Sandler86647982015-05-13 23:41:13 -0400903 *
904 * @deprecated Use {@link Action#getIcon()} instead.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400905 */
Dan Sandler86647982015-05-13 23:41:13 -0400906 @Deprecated
Daniel Sandlera0a938c2012-03-15 08:42:37 -0400907 public int icon;
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700908
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400909 /**
910 * Title of the action.
911 */
Daniel Sandlera0a938c2012-03-15 08:42:37 -0400912 public CharSequence title;
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700913
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400914 /**
915 * Intent to send when the user invokes this action. May be null, in which case the action
916 * may be rendered in a disabled presentation by the system UI.
917 */
Daniel Sandlera0a938c2012-03-15 08:42:37 -0400918 public PendingIntent actionIntent;
Griff Hazen959591e2014-05-15 22:26:18 -0700919
Daniel Sandlera0a938c2012-03-15 08:42:37 -0400920 private Action(Parcel in) {
Dan Sandler86647982015-05-13 23:41:13 -0400921 if (in.readInt() != 0) {
922 mIcon = Icon.CREATOR.createFromParcel(in);
Dan Sandler68079d52015-07-22 10:45:30 -0400923 if (mIcon.getType() == Icon.TYPE_RESOURCE) {
924 icon = mIcon.getResId();
925 }
Dan Sandler86647982015-05-13 23:41:13 -0400926 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -0400927 title = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
928 if (in.readInt() == 1) {
929 actionIntent = PendingIntent.CREATOR.createFromParcel(in);
930 }
Griff Hazen959591e2014-05-15 22:26:18 -0700931 mExtras = in.readBundle();
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700932 mRemoteInputs = in.createTypedArray(RemoteInput.CREATOR);
Daniel Sandlera0a938c2012-03-15 08:42:37 -0400933 }
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700934
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400935 /**
Dan Sandler86647982015-05-13 23:41:13 -0400936 * @deprecated Use {@link android.app.Notification.Action.Builder}.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400937 */
Dan Sandler86647982015-05-13 23:41:13 -0400938 @Deprecated
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400939 public Action(int icon, CharSequence title, PendingIntent intent) {
Dan Sandler86647982015-05-13 23:41:13 -0400940 this(Icon.createWithResource("", icon), title, intent, new Bundle(), null);
Griff Hazen959591e2014-05-15 22:26:18 -0700941 }
942
Dan Sandler86647982015-05-13 23:41:13 -0400943 private Action(Icon icon, CharSequence title, PendingIntent intent, Bundle extras,
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700944 RemoteInput[] remoteInputs) {
Dan Sandler86647982015-05-13 23:41:13 -0400945 this.mIcon = icon;
Gus Prevasf5bff46f2015-08-24 10:34:28 -0400946 if (icon != null && icon.getType() == Icon.TYPE_RESOURCE) {
947 this.icon = icon.getResId();
948 }
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400949 this.title = title;
950 this.actionIntent = intent;
Griff Hazen959591e2014-05-15 22:26:18 -0700951 this.mExtras = extras != null ? extras : new Bundle();
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700952 this.mRemoteInputs = remoteInputs;
Griff Hazen959591e2014-05-15 22:26:18 -0700953 }
954
955 /**
Dan Sandler86647982015-05-13 23:41:13 -0400956 * Return an icon representing the action.
957 */
958 public Icon getIcon() {
959 if (mIcon == null && icon != 0) {
960 // you snuck an icon in here without using the builder; let's try to keep it
961 mIcon = Icon.createWithResource("", icon);
962 }
963 return mIcon;
964 }
965
966 /**
Griff Hazen959591e2014-05-15 22:26:18 -0700967 * Get additional metadata carried around with this Action.
968 */
969 public Bundle getExtras() {
970 return mExtras;
971 }
972
973 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700974 * Get the list of inputs to be collected from the user when this action is sent.
975 * May return null if no remote inputs were added.
976 */
977 public RemoteInput[] getRemoteInputs() {
978 return mRemoteInputs;
979 }
980
981 /**
Griff Hazen959591e2014-05-15 22:26:18 -0700982 * Builder class for {@link Action} objects.
983 */
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700984 public static final class Builder {
Dan Sandler86647982015-05-13 23:41:13 -0400985 private final Icon mIcon;
Griff Hazen959591e2014-05-15 22:26:18 -0700986 private final CharSequence mTitle;
987 private final PendingIntent mIntent;
988 private final Bundle mExtras;
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700989 private ArrayList<RemoteInput> mRemoteInputs;
Griff Hazen959591e2014-05-15 22:26:18 -0700990
991 /**
992 * Construct a new builder for {@link Action} object.
993 * @param icon icon to show for this action
994 * @param title the title of the action
995 * @param intent the {@link PendingIntent} to fire when users trigger this action
996 */
Dan Sandler86647982015-05-13 23:41:13 -0400997 @Deprecated
Griff Hazen959591e2014-05-15 22:26:18 -0700998 public Builder(int icon, CharSequence title, PendingIntent intent) {
Dan Sandler86647982015-05-13 23:41:13 -0400999 this(Icon.createWithResource("", icon), title, intent, new Bundle(), null);
1000 }
1001
1002 /**
1003 * Construct a new builder for {@link Action} object.
1004 * @param icon icon to show for this action
1005 * @param title the title of the action
1006 * @param intent the {@link PendingIntent} to fire when users trigger this action
1007 */
1008 public Builder(Icon icon, CharSequence title, PendingIntent intent) {
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001009 this(icon, title, intent, new Bundle(), null);
Griff Hazen959591e2014-05-15 22:26:18 -07001010 }
1011
1012 /**
1013 * Construct a new builder for {@link Action} object using the fields from an
1014 * {@link Action}.
1015 * @param action the action to read fields from.
1016 */
1017 public Builder(Action action) {
Dan Sandler86647982015-05-13 23:41:13 -04001018 this(action.getIcon(), action.title, action.actionIntent, new Bundle(action.mExtras),
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001019 action.getRemoteInputs());
Griff Hazen959591e2014-05-15 22:26:18 -07001020 }
1021
Dan Sandler86647982015-05-13 23:41:13 -04001022 private Builder(Icon icon, CharSequence title, PendingIntent intent, Bundle extras,
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001023 RemoteInput[] remoteInputs) {
Griff Hazen959591e2014-05-15 22:26:18 -07001024 mIcon = icon;
1025 mTitle = title;
1026 mIntent = intent;
1027 mExtras = extras;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001028 if (remoteInputs != null) {
1029 mRemoteInputs = new ArrayList<RemoteInput>(remoteInputs.length);
1030 Collections.addAll(mRemoteInputs, remoteInputs);
1031 }
Griff Hazen959591e2014-05-15 22:26:18 -07001032 }
1033
1034 /**
1035 * Merge additional metadata into this builder.
1036 *
1037 * <p>Values within the Bundle will replace existing extras values in this Builder.
1038 *
1039 * @see Notification.Action#extras
1040 */
1041 public Builder addExtras(Bundle extras) {
1042 if (extras != null) {
1043 mExtras.putAll(extras);
1044 }
1045 return this;
1046 }
1047
1048 /**
1049 * Get the metadata Bundle used by this Builder.
1050 *
1051 * <p>The returned Bundle is shared with this Builder.
1052 */
1053 public Bundle getExtras() {
1054 return mExtras;
1055 }
1056
1057 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001058 * Add an input to be collected from the user when this action is sent.
1059 * Response values can be retrieved from the fired intent by using the
1060 * {@link RemoteInput#getResultsFromIntent} function.
1061 * @param remoteInput a {@link RemoteInput} to add to the action
1062 * @return this object for method chaining
1063 */
1064 public Builder addRemoteInput(RemoteInput remoteInput) {
1065 if (mRemoteInputs == null) {
1066 mRemoteInputs = new ArrayList<RemoteInput>();
1067 }
1068 mRemoteInputs.add(remoteInput);
1069 return this;
1070 }
1071
1072 /**
1073 * Apply an extender to this action builder. Extenders may be used to add
1074 * metadata or change options on this builder.
1075 */
Griff Hazen61a9e862014-05-22 16:05:19 -07001076 public Builder extend(Extender extender) {
1077 extender.extend(this);
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001078 return this;
1079 }
1080
1081 /**
Griff Hazen959591e2014-05-15 22:26:18 -07001082 * Combine all of the options that have been set and return a new {@link Action}
1083 * object.
1084 * @return the built action
1085 */
1086 public Action build() {
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001087 RemoteInput[] remoteInputs = mRemoteInputs != null
1088 ? mRemoteInputs.toArray(new RemoteInput[mRemoteInputs.size()]) : null;
1089 return new Action(mIcon, mTitle, mIntent, mExtras, remoteInputs);
Griff Hazen959591e2014-05-15 22:26:18 -07001090 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001091 }
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001092
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001093 @Override
1094 public Action clone() {
1095 return new Action(
Dan Sandler86647982015-05-13 23:41:13 -04001096 getIcon(),
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001097 title,
1098 actionIntent, // safe to alias
1099 new Bundle(mExtras),
1100 getRemoteInputs());
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001101 }
1102 @Override
1103 public int describeContents() {
1104 return 0;
1105 }
1106 @Override
1107 public void writeToParcel(Parcel out, int flags) {
Dan Sandler86647982015-05-13 23:41:13 -04001108 final Icon ic = getIcon();
1109 if (ic != null) {
1110 out.writeInt(1);
1111 ic.writeToParcel(out, 0);
1112 } else {
1113 out.writeInt(0);
1114 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001115 TextUtils.writeToParcel(title, out, flags);
1116 if (actionIntent != null) {
1117 out.writeInt(1);
1118 actionIntent.writeToParcel(out, flags);
1119 } else {
1120 out.writeInt(0);
1121 }
Griff Hazen959591e2014-05-15 22:26:18 -07001122 out.writeBundle(mExtras);
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001123 out.writeTypedArray(mRemoteInputs, flags);
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001124 }
Griff Hazen959591e2014-05-15 22:26:18 -07001125 public static final Parcelable.Creator<Action> CREATOR =
1126 new Parcelable.Creator<Action>() {
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001127 public Action createFromParcel(Parcel in) {
1128 return new Action(in);
1129 }
1130 public Action[] newArray(int size) {
1131 return new Action[size];
1132 }
1133 };
Griff Hazen61a9e862014-05-22 16:05:19 -07001134
1135 /**
1136 * Extender interface for use with {@link Builder#extend}. Extenders may be used to add
1137 * metadata or change options on an action builder.
1138 */
1139 public interface Extender {
1140 /**
1141 * Apply this extender to a notification action builder.
1142 * @param builder the builder to be modified.
1143 * @return the build object for chaining.
1144 */
1145 public Builder extend(Builder builder);
1146 }
1147
1148 /**
1149 * Wearable extender for notification actions. To add extensions to an action,
1150 * create a new {@link android.app.Notification.Action.WearableExtender} object using
1151 * the {@code WearableExtender()} constructor and apply it to a
1152 * {@link android.app.Notification.Action.Builder} using
1153 * {@link android.app.Notification.Action.Builder#extend}.
1154 *
1155 * <pre class="prettyprint">
1156 * Notification.Action action = new Notification.Action.Builder(
1157 * R.drawable.archive_all, "Archive all", actionIntent)
Griff Hazen14f57992014-05-26 09:07:14 -07001158 * .extend(new Notification.Action.WearableExtender()
Griff Hazen61a9e862014-05-22 16:05:19 -07001159 * .setAvailableOffline(false))
Griff Hazen14f57992014-05-26 09:07:14 -07001160 * .build();</pre>
Griff Hazen61a9e862014-05-22 16:05:19 -07001161 */
1162 public static final class WearableExtender implements Extender {
1163 /** Notification action extra which contains wearable extensions */
1164 private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS";
1165
Pete Gastaf6781d2014-10-07 15:17:05 -04001166 // Keys within EXTRA_WEARABLE_EXTENSIONS for wearable options.
Griff Hazen61a9e862014-05-22 16:05:19 -07001167 private static final String KEY_FLAGS = "flags";
Pete Gastaf6781d2014-10-07 15:17:05 -04001168 private static final String KEY_IN_PROGRESS_LABEL = "inProgressLabel";
1169 private static final String KEY_CONFIRM_LABEL = "confirmLabel";
1170 private static final String KEY_CANCEL_LABEL = "cancelLabel";
Griff Hazen61a9e862014-05-22 16:05:19 -07001171
1172 // Flags bitwise-ored to mFlags
1173 private static final int FLAG_AVAILABLE_OFFLINE = 0x1;
1174
1175 // Default value for flags integer
1176 private static final int DEFAULT_FLAGS = FLAG_AVAILABLE_OFFLINE;
1177
1178 private int mFlags = DEFAULT_FLAGS;
1179
Pete Gastaf6781d2014-10-07 15:17:05 -04001180 private CharSequence mInProgressLabel;
1181 private CharSequence mConfirmLabel;
1182 private CharSequence mCancelLabel;
1183
Griff Hazen61a9e862014-05-22 16:05:19 -07001184 /**
1185 * Create a {@link android.app.Notification.Action.WearableExtender} with default
1186 * options.
1187 */
1188 public WearableExtender() {
1189 }
1190
1191 /**
1192 * Create a {@link android.app.Notification.Action.WearableExtender} by reading
1193 * wearable options present in an existing notification action.
1194 * @param action the notification action to inspect.
1195 */
1196 public WearableExtender(Action action) {
1197 Bundle wearableBundle = action.getExtras().getBundle(EXTRA_WEARABLE_EXTENSIONS);
1198 if (wearableBundle != null) {
1199 mFlags = wearableBundle.getInt(KEY_FLAGS, DEFAULT_FLAGS);
Pete Gastaf6781d2014-10-07 15:17:05 -04001200 mInProgressLabel = wearableBundle.getCharSequence(KEY_IN_PROGRESS_LABEL);
1201 mConfirmLabel = wearableBundle.getCharSequence(KEY_CONFIRM_LABEL);
1202 mCancelLabel = wearableBundle.getCharSequence(KEY_CANCEL_LABEL);
Griff Hazen61a9e862014-05-22 16:05:19 -07001203 }
1204 }
1205
1206 /**
1207 * Apply wearable extensions to a notification action that is being built. This is
1208 * typically called by the {@link android.app.Notification.Action.Builder#extend}
1209 * method of {@link android.app.Notification.Action.Builder}.
1210 */
1211 @Override
1212 public Action.Builder extend(Action.Builder builder) {
1213 Bundle wearableBundle = new Bundle();
1214
1215 if (mFlags != DEFAULT_FLAGS) {
1216 wearableBundle.putInt(KEY_FLAGS, mFlags);
1217 }
Pete Gastaf6781d2014-10-07 15:17:05 -04001218 if (mInProgressLabel != null) {
1219 wearableBundle.putCharSequence(KEY_IN_PROGRESS_LABEL, mInProgressLabel);
1220 }
1221 if (mConfirmLabel != null) {
1222 wearableBundle.putCharSequence(KEY_CONFIRM_LABEL, mConfirmLabel);
1223 }
1224 if (mCancelLabel != null) {
1225 wearableBundle.putCharSequence(KEY_CANCEL_LABEL, mCancelLabel);
1226 }
Griff Hazen61a9e862014-05-22 16:05:19 -07001227
1228 builder.getExtras().putBundle(EXTRA_WEARABLE_EXTENSIONS, wearableBundle);
1229 return builder;
1230 }
1231
1232 @Override
1233 public WearableExtender clone() {
1234 WearableExtender that = new WearableExtender();
1235 that.mFlags = this.mFlags;
Pete Gastaf6781d2014-10-07 15:17:05 -04001236 that.mInProgressLabel = this.mInProgressLabel;
1237 that.mConfirmLabel = this.mConfirmLabel;
1238 that.mCancelLabel = this.mCancelLabel;
Griff Hazen61a9e862014-05-22 16:05:19 -07001239 return that;
1240 }
1241
1242 /**
1243 * Set whether this action is available when the wearable device is not connected to
1244 * a companion device. The user can still trigger this action when the wearable device is
1245 * offline, but a visual hint will indicate that the action may not be available.
1246 * Defaults to true.
1247 */
1248 public WearableExtender setAvailableOffline(boolean availableOffline) {
1249 setFlag(FLAG_AVAILABLE_OFFLINE, availableOffline);
1250 return this;
1251 }
1252
1253 /**
1254 * Get whether this action is available when the wearable device is not connected to
1255 * a companion device. The user can still trigger this action when the wearable device is
1256 * offline, but a visual hint will indicate that the action may not be available.
1257 * Defaults to true.
1258 */
1259 public boolean isAvailableOffline() {
1260 return (mFlags & FLAG_AVAILABLE_OFFLINE) != 0;
1261 }
1262
1263 private void setFlag(int mask, boolean value) {
1264 if (value) {
1265 mFlags |= mask;
1266 } else {
1267 mFlags &= ~mask;
1268 }
1269 }
Pete Gastaf6781d2014-10-07 15:17:05 -04001270
1271 /**
1272 * Set a label to display while the wearable is preparing to automatically execute the
1273 * action. This is usually a 'ing' verb ending in ellipsis like "Sending..."
1274 *
1275 * @param label the label to display while the action is being prepared to execute
1276 * @return this object for method chaining
1277 */
1278 public WearableExtender setInProgressLabel(CharSequence label) {
1279 mInProgressLabel = label;
1280 return this;
1281 }
1282
1283 /**
1284 * Get the label to display while the wearable is preparing to automatically execute
1285 * the action. This is usually a 'ing' verb ending in ellipsis like "Sending..."
1286 *
1287 * @return the label to display while the action is being prepared to execute
1288 */
1289 public CharSequence getInProgressLabel() {
1290 return mInProgressLabel;
1291 }
1292
1293 /**
1294 * Set a label to display to confirm that the action should be executed.
1295 * This is usually an imperative verb like "Send".
1296 *
1297 * @param label the label to confirm the action should be executed
1298 * @return this object for method chaining
1299 */
1300 public WearableExtender setConfirmLabel(CharSequence label) {
1301 mConfirmLabel = label;
1302 return this;
1303 }
1304
1305 /**
1306 * Get the label to display to confirm that the action should be executed.
1307 * This is usually an imperative verb like "Send".
1308 *
1309 * @return the label to confirm the action should be executed
1310 */
1311 public CharSequence getConfirmLabel() {
1312 return mConfirmLabel;
1313 }
1314
1315 /**
1316 * Set a label to display to cancel the action.
1317 * This is usually an imperative verb, like "Cancel".
1318 *
1319 * @param label the label to display to cancel the action
1320 * @return this object for method chaining
1321 */
1322 public WearableExtender setCancelLabel(CharSequence label) {
1323 mCancelLabel = label;
1324 return this;
1325 }
1326
1327 /**
1328 * Get the label to display to cancel the action.
1329 * This is usually an imperative verb like "Cancel".
1330 *
1331 * @return the label to display to cancel the action
1332 */
1333 public CharSequence getCancelLabel() {
1334 return mCancelLabel;
1335 }
Griff Hazen61a9e862014-05-22 16:05:19 -07001336 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001337 }
1338
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001339 /**
1340 * Array of all {@link Action} structures attached to this notification by
1341 * {@link Builder#addAction(int, CharSequence, PendingIntent)}. Mostly useful for instances of
1342 * {@link android.service.notification.NotificationListenerService} that provide an alternative
1343 * interface for invoking actions.
1344 */
Daniel Sandlerea2a3172013-02-20 22:24:20 -05001345 public Action[] actions;
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001346
1347 /**
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001348 * Replacement version of this notification whose content will be shown
1349 * in an insecure context such as atop a secure keyguard. See {@link #visibility}
1350 * and {@link #VISIBILITY_PUBLIC}.
1351 */
1352 public Notification publicVersion;
1353
1354 /**
Julia Reynolds74303cf2015-10-16 11:37:55 -04001355 * Structure to encapsulate a topic that is shown in Notification settings.
1356 * It must include an id and label.
1357 */
1358 public static class Topic implements Parcelable {
1359 private final String id;
1360 private final CharSequence label;
1361
1362 public Topic(String id, CharSequence label) {
1363 this.id = id;
1364 this.label = safeCharSequence(label);
1365 }
1366
1367 private Topic(Parcel in) {
1368 if (in.readInt() != 0) {
1369 id = in.readString();
1370 } else {
1371 id = null;
1372 }
1373 label = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
1374 }
1375
1376 public String getId() {
1377 return id;
1378 }
1379
1380 public CharSequence getLabel() {
1381 return label;
1382 }
1383
1384 @Override
1385 public String toString() {
1386 return new StringBuilder(Topic.class.getSimpleName()).append('[')
1387 .append("id=").append(id)
1388 .append(",label=").append(label)
1389 .append(']').toString();
1390 }
1391
1392 @Override
1393 public boolean equals(Object o) {
1394 if (!(o instanceof Topic)) return false;
1395 if (o == this) return true;
1396 final Topic other = (Topic) o;
1397 return Objects.equals(other.id, id)
1398 && Objects.equals(other.label, label);
1399 }
1400
1401 @Override
1402 public int hashCode() {
1403 return Objects.hash(id, label);
1404 }
1405
1406 @Override
1407 public Topic clone() {
1408 return new Topic(id, label);
1409 }
1410
1411 @Override
1412 public int describeContents() {
1413 return 0;
1414 }
1415
1416 @Override
1417 public void writeToParcel(Parcel out, int flags) {
1418 if (id != null) {
1419 out.writeInt(1);
1420 out.writeString(id);
1421 } else {
1422 out.writeInt(0);
1423 }
1424 TextUtils.writeToParcel(label, out, flags);
1425 }
1426 public static final Parcelable.Creator<Topic> CREATOR =
1427 new Parcelable.Creator<Topic>() {
1428 public Topic createFromParcel(Parcel in) {
1429 return new Topic(in);
1430 }
1431 public Topic[] newArray(int size) {
1432 return new Topic[size];
1433 }
1434 };
1435 }
1436
Julia Reynolds233a5f92015-10-19 13:51:23 -04001437 @SystemApi
1438 public static final String TOPIC_DEFAULT = "system_default_topic";
1439
Julia Reynolds054c5dc2015-11-17 15:36:30 -05001440 private Topic topic;
Julia Reynolds74303cf2015-10-16 11:37:55 -04001441
Julia Reynolds054c5dc2015-11-17 15:36:30 -05001442 public Topic getTopic() {
1443 return topic;
Julia Reynolds74303cf2015-10-16 11:37:55 -04001444 }
1445
1446 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001447 * Constructs a Notification object with default values.
Joe Onorato46439ce2010-11-19 13:56:21 -08001448 * You might want to consider using {@link Builder} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001449 */
1450 public Notification()
1451 {
1452 this.when = System.currentTimeMillis();
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001453 this.priority = PRIORITY_DEFAULT;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001454 }
1455
1456 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001457 * @hide
1458 */
1459 public Notification(Context context, int icon, CharSequence tickerText, long when,
1460 CharSequence contentTitle, CharSequence contentText, Intent contentIntent)
1461 {
Chris Wren1ce4b6d2015-06-11 10:19:43 -04001462 new Builder(context)
1463 .setWhen(when)
1464 .setSmallIcon(icon)
1465 .setTicker(tickerText)
1466 .setContentTitle(contentTitle)
1467 .setContentText(contentText)
1468 .setContentIntent(PendingIntent.getActivity(context, 0, contentIntent, 0))
1469 .buildInto(this);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001470 }
1471
1472 /**
1473 * Constructs a Notification object with the information needed to
1474 * have a status bar icon without the standard expanded view.
1475 *
1476 * @param icon The resource id of the icon to put in the status bar.
1477 * @param tickerText The text that flows by in the status bar when the notification first
1478 * activates.
1479 * @param when The time to show in the time field. In the System.currentTimeMillis
1480 * timebase.
Joe Onorato46439ce2010-11-19 13:56:21 -08001481 *
1482 * @deprecated Use {@link Builder} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001483 */
Joe Onorato46439ce2010-11-19 13:56:21 -08001484 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001485 public Notification(int icon, CharSequence tickerText, long when)
1486 {
1487 this.icon = icon;
1488 this.tickerText = tickerText;
1489 this.when = when;
1490 }
1491
1492 /**
1493 * Unflatten the notification from a parcel.
1494 */
1495 public Notification(Parcel parcel)
1496 {
1497 int version = parcel.readInt();
1498
1499 when = parcel.readLong();
Dan Sandler3936e7a2015-05-19 20:59:12 -04001500 if (parcel.readInt() != 0) {
1501 mSmallIcon = Icon.CREATOR.createFromParcel(parcel);
Dan Sandler4e787062015-06-17 15:09:48 -04001502 if (mSmallIcon.getType() == Icon.TYPE_RESOURCE) {
1503 icon = mSmallIcon.getResId();
1504 }
Dan Sandler3936e7a2015-05-19 20:59:12 -04001505 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001506 number = parcel.readInt();
1507 if (parcel.readInt() != 0) {
1508 contentIntent = PendingIntent.CREATOR.createFromParcel(parcel);
1509 }
1510 if (parcel.readInt() != 0) {
1511 deleteIntent = PendingIntent.CREATOR.createFromParcel(parcel);
1512 }
1513 if (parcel.readInt() != 0) {
1514 tickerText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
1515 }
1516 if (parcel.readInt() != 0) {
Joe Onorato46439ce2010-11-19 13:56:21 -08001517 tickerView = RemoteViews.CREATOR.createFromParcel(parcel);
Joe Onoratoef1e7762010-09-17 18:38:38 -04001518 }
1519 if (parcel.readInt() != 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001520 contentView = RemoteViews.CREATOR.createFromParcel(parcel);
1521 }
Joe Onorato561d3852010-11-20 18:09:34 -08001522 if (parcel.readInt() != 0) {
Dan Sandlerd63f9322015-05-06 15:18:49 -04001523 mLargeIcon = Icon.CREATOR.createFromParcel(parcel);
Joe Onorato561d3852010-11-20 18:09:34 -08001524 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001525 defaults = parcel.readInt();
1526 flags = parcel.readInt();
1527 if (parcel.readInt() != 0) {
1528 sound = Uri.CREATOR.createFromParcel(parcel);
1529 }
1530
1531 audioStreamType = parcel.readInt();
John Spurlockc0650f022014-07-19 13:22:39 -04001532 if (parcel.readInt() != 0) {
1533 audioAttributes = AudioAttributes.CREATOR.createFromParcel(parcel);
1534 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001535 vibrate = parcel.createLongArray();
1536 ledARGB = parcel.readInt();
1537 ledOnMS = parcel.readInt();
1538 ledOffMS = parcel.readInt();
1539 iconLevel = parcel.readInt();
Daniel Sandlere46cbd32010-06-17 10:35:26 -04001540
1541 if (parcel.readInt() != 0) {
1542 fullScreenIntent = PendingIntent.CREATOR.createFromParcel(parcel);
1543 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001544
1545 priority = parcel.readInt();
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001546
John Spurlockfd7f1e02014-03-18 16:41:57 -04001547 category = parcel.readString();
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001548
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001549 mGroupKey = parcel.readString();
1550
1551 mSortKey = parcel.readString();
1552
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001553 extras = parcel.readBundle(); // may be null
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001554
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001555 actions = parcel.createTypedArray(Action.CREATOR); // may be null
1556
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001557 if (parcel.readInt() != 0) {
Daniel Sandlerf3b73432012-03-27 15:01:25 -04001558 bigContentView = RemoteViews.CREATOR.createFromParcel(parcel);
1559 }
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001560
Chris Wren8fd39ec2014-02-27 17:43:26 -05001561 if (parcel.readInt() != 0) {
1562 headsUpContentView = RemoteViews.CREATOR.createFromParcel(parcel);
1563 }
1564
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001565 visibility = parcel.readInt();
1566
1567 if (parcel.readInt() != 0) {
1568 publicVersion = Notification.CREATOR.createFromParcel(parcel);
1569 }
Dan Sandler26e81cf2014-05-06 10:01:27 -04001570
1571 color = parcel.readInt();
Julia Reynolds74303cf2015-10-16 11:37:55 -04001572
Julia Reynolds054c5dc2015-11-17 15:36:30 -05001573 if (parcel.readInt() != 0) {
1574 topic = Topic.CREATOR.createFromParcel(parcel);
1575 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001576 }
1577
Andy Stadler110988c2010-12-03 14:29:16 -08001578 @Override
Joe Onorato18e69df2010-05-17 22:26:12 -07001579 public Notification clone() {
1580 Notification that = new Notification();
Daniel Sandler1a497d32013-04-18 14:52:45 -04001581 cloneInto(that, true);
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001582 return that;
1583 }
Joe Onorato18e69df2010-05-17 22:26:12 -07001584
Daniel Sandler1a497d32013-04-18 14:52:45 -04001585 /**
1586 * Copy all (or if heavy is false, all except Bitmaps and RemoteViews) members
1587 * of this into that.
1588 * @hide
1589 */
1590 public void cloneInto(Notification that, boolean heavy) {
Joe Onorato18e69df2010-05-17 22:26:12 -07001591 that.when = this.when;
Dan Sandlerd63f9322015-05-06 15:18:49 -04001592 that.mSmallIcon = this.mSmallIcon;
Joe Onorato18e69df2010-05-17 22:26:12 -07001593 that.number = this.number;
1594
1595 // PendingIntents are global, so there's no reason (or way) to clone them.
1596 that.contentIntent = this.contentIntent;
1597 that.deleteIntent = this.deleteIntent;
Daniel Sandlere46cbd32010-06-17 10:35:26 -04001598 that.fullScreenIntent = this.fullScreenIntent;
Joe Onorato18e69df2010-05-17 22:26:12 -07001599
1600 if (this.tickerText != null) {
1601 that.tickerText = this.tickerText.toString();
1602 }
Daniel Sandler1a497d32013-04-18 14:52:45 -04001603 if (heavy && this.tickerView != null) {
Joe Onorato46439ce2010-11-19 13:56:21 -08001604 that.tickerView = this.tickerView.clone();
Joe Onoratoef1e7762010-09-17 18:38:38 -04001605 }
Daniel Sandler1a497d32013-04-18 14:52:45 -04001606 if (heavy && this.contentView != null) {
Joe Onorato18e69df2010-05-17 22:26:12 -07001607 that.contentView = this.contentView.clone();
1608 }
Dan Sandlerd63f9322015-05-06 15:18:49 -04001609 if (heavy && this.mLargeIcon != null) {
1610 that.mLargeIcon = this.mLargeIcon;
Joe Onorato561d3852010-11-20 18:09:34 -08001611 }
Jozef BABJAKa8b91832011-02-22 08:05:08 +01001612 that.iconLevel = this.iconLevel;
Joe Onorato18e69df2010-05-17 22:26:12 -07001613 that.sound = this.sound; // android.net.Uri is immutable
1614 that.audioStreamType = this.audioStreamType;
John Spurlockc0650f022014-07-19 13:22:39 -04001615 if (this.audioAttributes != null) {
1616 that.audioAttributes = new AudioAttributes.Builder(this.audioAttributes).build();
1617 }
Joe Onorato18e69df2010-05-17 22:26:12 -07001618
1619 final long[] vibrate = this.vibrate;
1620 if (vibrate != null) {
1621 final int N = vibrate.length;
1622 final long[] vib = that.vibrate = new long[N];
1623 System.arraycopy(vibrate, 0, vib, 0, N);
1624 }
1625
1626 that.ledARGB = this.ledARGB;
1627 that.ledOnMS = this.ledOnMS;
1628 that.ledOffMS = this.ledOffMS;
1629 that.defaults = this.defaults;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001630
Joe Onorato18e69df2010-05-17 22:26:12 -07001631 that.flags = this.flags;
1632
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001633 that.priority = this.priority;
Joe Malin8d40d042012-11-05 11:36:40 -08001634
John Spurlockfd7f1e02014-03-18 16:41:57 -04001635 that.category = this.category;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001636
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001637 that.mGroupKey = this.mGroupKey;
1638
1639 that.mSortKey = this.mSortKey;
1640
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001641 if (this.extras != null) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04001642 try {
1643 that.extras = new Bundle(this.extras);
1644 // will unparcel
1645 that.extras.size();
1646 } catch (BadParcelableException e) {
1647 Log.e(TAG, "could not unparcel extras from notification: " + this, e);
1648 that.extras = null;
1649 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001650 }
1651
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001652 if (this.actions != null) {
1653 that.actions = new Action[this.actions.length];
1654 for(int i=0; i<this.actions.length; i++) {
1655 that.actions[i] = this.actions[i].clone();
1656 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001657 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001658
Daniel Sandler1a497d32013-04-18 14:52:45 -04001659 if (heavy && this.bigContentView != null) {
Daniel Sandlerf3b73432012-03-27 15:01:25 -04001660 that.bigContentView = this.bigContentView.clone();
1661 }
Daniel Sandler1a497d32013-04-18 14:52:45 -04001662
Chris Wren8fd39ec2014-02-27 17:43:26 -05001663 if (heavy && this.headsUpContentView != null) {
1664 that.headsUpContentView = this.headsUpContentView.clone();
1665 }
1666
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001667 that.visibility = this.visibility;
1668
1669 if (this.publicVersion != null) {
1670 that.publicVersion = new Notification();
1671 this.publicVersion.cloneInto(that.publicVersion, heavy);
1672 }
1673
Dan Sandler26e81cf2014-05-06 10:01:27 -04001674 that.color = this.color;
1675
Julia Reynolds054c5dc2015-11-17 15:36:30 -05001676 if (this.topic != null) {
1677 that.topic = this.topic.clone();
Julia Reynolds74303cf2015-10-16 11:37:55 -04001678 }
1679
Daniel Sandler1a497d32013-04-18 14:52:45 -04001680 if (!heavy) {
1681 that.lightenPayload(); // will clean out extras
1682 }
1683 }
1684
1685 /**
1686 * Removes heavyweight parts of the Notification object for archival or for sending to
1687 * listeners when the full contents are not necessary.
1688 * @hide
1689 */
1690 public final void lightenPayload() {
1691 tickerView = null;
1692 contentView = null;
1693 bigContentView = null;
Chris Wren8fd39ec2014-02-27 17:43:26 -05001694 headsUpContentView = null;
Dan Sandlerd63f9322015-05-06 15:18:49 -04001695 mLargeIcon = null;
Daniel Sandler1a497d32013-04-18 14:52:45 -04001696 if (extras != null) {
1697 extras.remove(Notification.EXTRA_LARGE_ICON);
1698 extras.remove(Notification.EXTRA_LARGE_ICON_BIG);
1699 extras.remove(Notification.EXTRA_PICTURE);
Christoph Studer223f44e2014-09-02 14:59:32 +02001700 extras.remove(Notification.EXTRA_BIG_TEXT);
Daniel Sandler1a497d32013-04-18 14:52:45 -04001701 }
Joe Onorato18e69df2010-05-17 22:26:12 -07001702 }
1703
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04001704 /**
1705 * Make sure this CharSequence is safe to put into a bundle, which basically
1706 * means it had better not be some custom Parcelable implementation.
1707 * @hide
1708 */
1709 public static CharSequence safeCharSequence(CharSequence cs) {
Christoph Studer535ec612014-09-03 15:47:47 +02001710 if (cs == null) return cs;
1711 if (cs.length() > MAX_CHARSEQUENCE_LENGTH) {
1712 cs = cs.subSequence(0, MAX_CHARSEQUENCE_LENGTH);
1713 }
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04001714 if (cs instanceof Parcelable) {
1715 Log.e(TAG, "warning: " + cs.getClass().getCanonicalName()
1716 + " instance is a custom Parcelable and not allowed in Notification");
1717 return cs.toString();
1718 }
1719
1720 return cs;
1721 }
1722
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001723 public int describeContents() {
1724 return 0;
1725 }
1726
1727 /**
Dan Sandler4e787062015-06-17 15:09:48 -04001728 * Flatten this notification into a parcel.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001729 */
1730 public void writeToParcel(Parcel parcel, int flags)
1731 {
1732 parcel.writeInt(1);
1733
1734 parcel.writeLong(when);
Dan Sandler4e787062015-06-17 15:09:48 -04001735 if (mSmallIcon == null && icon != 0) {
1736 // you snuck an icon in here without using the builder; let's try to keep it
1737 mSmallIcon = Icon.createWithResource("", icon);
1738 }
Dan Sandler3936e7a2015-05-19 20:59:12 -04001739 if (mSmallIcon != null) {
1740 parcel.writeInt(1);
1741 mSmallIcon.writeToParcel(parcel, 0);
1742 } else {
1743 parcel.writeInt(0);
1744 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001745 parcel.writeInt(number);
1746 if (contentIntent != null) {
1747 parcel.writeInt(1);
1748 contentIntent.writeToParcel(parcel, 0);
1749 } else {
1750 parcel.writeInt(0);
1751 }
1752 if (deleteIntent != null) {
1753 parcel.writeInt(1);
1754 deleteIntent.writeToParcel(parcel, 0);
1755 } else {
1756 parcel.writeInt(0);
1757 }
1758 if (tickerText != null) {
1759 parcel.writeInt(1);
1760 TextUtils.writeToParcel(tickerText, parcel, flags);
1761 } else {
1762 parcel.writeInt(0);
1763 }
Joe Onorato46439ce2010-11-19 13:56:21 -08001764 if (tickerView != null) {
Joe Onoratoef1e7762010-09-17 18:38:38 -04001765 parcel.writeInt(1);
Joe Onorato46439ce2010-11-19 13:56:21 -08001766 tickerView.writeToParcel(parcel, 0);
Joe Onoratoef1e7762010-09-17 18:38:38 -04001767 } else {
1768 parcel.writeInt(0);
1769 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001770 if (contentView != null) {
1771 parcel.writeInt(1);
1772 contentView.writeToParcel(parcel, 0);
1773 } else {
1774 parcel.writeInt(0);
1775 }
Dan Sandlerd63f9322015-05-06 15:18:49 -04001776 if (mLargeIcon != null) {
Joe Onorato561d3852010-11-20 18:09:34 -08001777 parcel.writeInt(1);
Dan Sandlerd63f9322015-05-06 15:18:49 -04001778 mLargeIcon.writeToParcel(parcel, 0);
Joe Onorato561d3852010-11-20 18:09:34 -08001779 } else {
1780 parcel.writeInt(0);
1781 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001782
1783 parcel.writeInt(defaults);
1784 parcel.writeInt(this.flags);
1785
1786 if (sound != null) {
1787 parcel.writeInt(1);
1788 sound.writeToParcel(parcel, 0);
1789 } else {
1790 parcel.writeInt(0);
1791 }
1792 parcel.writeInt(audioStreamType);
John Spurlockc0650f022014-07-19 13:22:39 -04001793
1794 if (audioAttributes != null) {
1795 parcel.writeInt(1);
1796 audioAttributes.writeToParcel(parcel, 0);
1797 } else {
1798 parcel.writeInt(0);
1799 }
1800
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001801 parcel.writeLongArray(vibrate);
1802 parcel.writeInt(ledARGB);
1803 parcel.writeInt(ledOnMS);
1804 parcel.writeInt(ledOffMS);
1805 parcel.writeInt(iconLevel);
Daniel Sandlere46cbd32010-06-17 10:35:26 -04001806
1807 if (fullScreenIntent != null) {
1808 parcel.writeInt(1);
1809 fullScreenIntent.writeToParcel(parcel, 0);
1810 } else {
1811 parcel.writeInt(0);
1812 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001813
1814 parcel.writeInt(priority);
Joe Malin8d40d042012-11-05 11:36:40 -08001815
John Spurlockfd7f1e02014-03-18 16:41:57 -04001816 parcel.writeString(category);
Joe Malin8d40d042012-11-05 11:36:40 -08001817
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001818 parcel.writeString(mGroupKey);
1819
1820 parcel.writeString(mSortKey);
1821
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001822 parcel.writeBundle(extras); // null ok
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001823
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001824 parcel.writeTypedArray(actions, 0); // null ok
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001825
Daniel Sandlerf3b73432012-03-27 15:01:25 -04001826 if (bigContentView != null) {
1827 parcel.writeInt(1);
1828 bigContentView.writeToParcel(parcel, 0);
1829 } else {
1830 parcel.writeInt(0);
1831 }
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001832
Chris Wren8fd39ec2014-02-27 17:43:26 -05001833 if (headsUpContentView != null) {
1834 parcel.writeInt(1);
1835 headsUpContentView.writeToParcel(parcel, 0);
1836 } else {
1837 parcel.writeInt(0);
1838 }
1839
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001840 parcel.writeInt(visibility);
1841
1842 if (publicVersion != null) {
1843 parcel.writeInt(1);
1844 publicVersion.writeToParcel(parcel, 0);
1845 } else {
1846 parcel.writeInt(0);
1847 }
Dan Sandler26e81cf2014-05-06 10:01:27 -04001848
1849 parcel.writeInt(color);
Julia Reynolds74303cf2015-10-16 11:37:55 -04001850
Julia Reynolds054c5dc2015-11-17 15:36:30 -05001851 if (topic != null) {
1852 parcel.writeInt(1);
1853 topic.writeToParcel(parcel, 0);
1854 } else {
1855 parcel.writeInt(0);
1856 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001857 }
1858
1859 /**
1860 * Parcelable.Creator that instantiates Notification objects
1861 */
1862 public static final Parcelable.Creator<Notification> CREATOR
1863 = new Parcelable.Creator<Notification>()
1864 {
1865 public Notification createFromParcel(Parcel parcel)
1866 {
1867 return new Notification(parcel);
1868 }
1869
1870 public Notification[] newArray(int size)
1871 {
1872 return new Notification[size];
1873 }
1874 };
1875
1876 /**
1877 * Sets the {@link #contentView} field to be a view with the standard "Latest Event"
1878 * layout.
1879 *
1880 * <p>Uses the {@link #icon} and {@link #when} fields to set the icon and time fields
1881 * in the view.</p>
1882 * @param context The context for your application / activity.
1883 * @param contentTitle The title that goes in the expanded entry.
1884 * @param contentText The text that goes in the expanded entry.
1885 * @param contentIntent The intent to launch when the user clicks the expanded notification.
1886 * If this is an activity, it must include the
1887 * {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} flag, which requires
Scott Main7aee61f2011-02-08 11:25:01 -08001888 * that you take care of task management as described in the
1889 * <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back
1890 * Stack</a> document.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001891 *
Joe Onorato46439ce2010-11-19 13:56:21 -08001892 * @deprecated Use {@link Builder} instead.
Chris Wrena05db382015-06-24 15:18:34 -04001893 * @removed
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001894 */
Joe Onorato46439ce2010-11-19 13:56:21 -08001895 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001896 public void setLatestEventInfo(Context context,
1897 CharSequence contentTitle, CharSequence contentText, PendingIntent contentIntent) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04001898 if (context.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1){
1899 Log.e(TAG, "setLatestEventInfo() is deprecated and you should feel deprecated.",
1900 new Throwable());
1901 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001902
Julia Reynoldsd9228f12015-10-20 10:37:27 -04001903 // ensure that any information already set directly is preserved
1904 final Notification.Builder builder = new Notification.Builder(context, this);
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001905
1906 // now apply the latestEventInfo fields
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001907 if (contentTitle != null) {
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001908 builder.setContentTitle(contentTitle);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001909 }
1910 if (contentText != null) {
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001911 builder.setContentText(contentText);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001912 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001913 builder.setContentIntent(contentIntent);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04001914
1915 builder.build(); // callers expect this notification to be ready to use
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001916 }
1917
Julia Reynoldsda303542015-11-23 14:00:20 -05001918 /**
1919 * @hide
1920 */
1921 public static void addFieldsFromContext(Context context, Notification notification) {
1922 if (notification.extras.getParcelable(EXTRA_BUILDER_APPLICATION_INFO) == null) {
1923 notification.extras.putParcelable(EXTRA_BUILDER_APPLICATION_INFO,
1924 context.getApplicationInfo());
1925 }
1926 if (!notification.extras.containsKey(EXTRA_ORIGINATING_USERID)) {
1927 notification.extras.putInt(EXTRA_ORIGINATING_USERID, context.getUserId());
1928 }
1929 }
1930
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001931 @Override
1932 public String toString() {
1933 StringBuilder sb = new StringBuilder();
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001934 sb.append("Notification(pri=");
1935 sb.append(priority);
1936 sb.append(" contentView=");
Joe Onoratoc9596d62011-01-12 17:03:11 -08001937 if (contentView != null) {
1938 sb.append(contentView.getPackage());
1939 sb.append("/0x");
1940 sb.append(Integer.toHexString(contentView.getLayoutId()));
1941 } else {
1942 sb.append("null");
1943 }
1944 sb.append(" vibrate=");
Daniel Sandler6738eee2012-11-16 12:03:32 -05001945 if ((this.defaults & DEFAULT_VIBRATE) != 0) {
1946 sb.append("default");
1947 } else if (this.vibrate != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001948 int N = this.vibrate.length-1;
1949 sb.append("[");
1950 for (int i=0; i<N; i++) {
1951 sb.append(this.vibrate[i]);
1952 sb.append(',');
1953 }
Simon Schoar8cf97d92009-06-10 22:08:37 +02001954 if (N != -1) {
1955 sb.append(this.vibrate[N]);
1956 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001957 sb.append("]");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001958 } else {
1959 sb.append("null");
1960 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001961 sb.append(" sound=");
Daniel Sandler6738eee2012-11-16 12:03:32 -05001962 if ((this.defaults & DEFAULT_SOUND) != 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001963 sb.append("default");
Daniel Sandler6738eee2012-11-16 12:03:32 -05001964 } else if (this.sound != null) {
1965 sb.append(this.sound.toString());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001966 } else {
1967 sb.append("null");
1968 }
Chris Wren365b6d32015-07-16 10:39:26 -04001969 if (this.tickerText != null) {
1970 sb.append(" tick");
1971 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001972 sb.append(" defaults=0x");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001973 sb.append(Integer.toHexString(this.defaults));
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001974 sb.append(" flags=0x");
Daniel Sandlere46cbd32010-06-17 10:35:26 -04001975 sb.append(Integer.toHexString(this.flags));
Dan Sandler26e81cf2014-05-06 10:01:27 -04001976 sb.append(String.format(" color=0x%08x", this.color));
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001977 if (this.category != null) {
1978 sb.append(" category=");
1979 sb.append(this.category);
1980 }
1981 if (this.mGroupKey != null) {
1982 sb.append(" groupKey=");
1983 sb.append(this.mGroupKey);
1984 }
1985 if (this.mSortKey != null) {
1986 sb.append(" sortKey=");
1987 sb.append(this.mSortKey);
1988 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001989 if (actions != null) {
Dan Sandler1b718782014-07-18 12:43:45 -04001990 sb.append(" actions=");
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001991 sb.append(actions.length);
Dan Sandler1b718782014-07-18 12:43:45 -04001992 }
1993 sb.append(" vis=");
1994 sb.append(visibilityToString(this.visibility));
1995 if (this.publicVersion != null) {
1996 sb.append(" publicVersion=");
1997 sb.append(publicVersion.toString());
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001998 }
Julia Reynolds054c5dc2015-11-17 15:36:30 -05001999 if (topic != null) {
2000 sb.append("topic=");
2001 sb.append(topic.toString());
Julia Reynolds74303cf2015-10-16 11:37:55 -04002002 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002003 sb.append(")");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002004 return sb.toString();
2005 }
Joe Onorato46439ce2010-11-19 13:56:21 -08002006
Dan Sandler1b718782014-07-18 12:43:45 -04002007 /**
2008 * {@hide}
2009 */
2010 public static String visibilityToString(int vis) {
2011 switch (vis) {
2012 case VISIBILITY_PRIVATE:
2013 return "PRIVATE";
2014 case VISIBILITY_PUBLIC:
2015 return "PUBLIC";
2016 case VISIBILITY_SECRET:
2017 return "SECRET";
2018 default:
2019 return "UNKNOWN(" + String.valueOf(vis) + ")";
2020 }
2021 }
2022
Joe Onoratocb109a02011-01-18 17:57:41 -08002023 /**
John Spurlock1d881a12015-03-18 19:21:54 -04002024 * {@hide}
2025 */
2026 public static String priorityToString(@Priority int pri) {
2027 switch (pri) {
2028 case PRIORITY_MIN:
2029 return "MIN";
2030 case PRIORITY_LOW:
2031 return "LOW";
2032 case PRIORITY_DEFAULT:
2033 return "DEFAULT";
2034 case PRIORITY_HIGH:
2035 return "HIGH";
2036 case PRIORITY_MAX:
2037 return "MAX";
2038 default:
2039 return "UNKNOWN(" + String.valueOf(pri) + ")";
2040 }
2041 }
2042
2043 /**
Dan Sandlerd63f9322015-05-06 15:18:49 -04002044 * The small icon representing this notification in the status bar and content view.
2045 *
2046 * @return the small icon representing this notification.
2047 *
2048 * @see Builder#getSmallIcon()
2049 * @see Builder#setSmallIcon(Icon)
2050 */
2051 public Icon getSmallIcon() {
2052 return mSmallIcon;
2053 }
2054
2055 /**
2056 * Used when notifying to clean up legacy small icons.
2057 * @hide
2058 */
2059 public void setSmallIcon(Icon icon) {
2060 mSmallIcon = icon;
2061 }
2062
2063 /**
2064 * The large icon shown in this notification's content view.
2065 * @see Builder#getLargeIcon()
2066 * @see Builder#setLargeIcon(Icon)
2067 */
2068 public Icon getLargeIcon() {
2069 return mLargeIcon;
2070 }
2071
2072 /**
Christoph Studer4600f9b2014-07-22 22:44:43 +02002073 * @hide
2074 */
Christoph Studerc8db24b2014-07-25 17:50:30 +02002075 public boolean isGroupSummary() {
2076 return mGroupKey != null && (flags & FLAG_GROUP_SUMMARY) != 0;
2077 }
2078
2079 /**
2080 * @hide
2081 */
2082 public boolean isGroupChild() {
2083 return mGroupKey != null && (flags & FLAG_GROUP_SUMMARY) == 0;
2084 }
2085
2086 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002087 * Builder class for {@link Notification} objects.
Joe Malin8d40d042012-11-05 11:36:40 -08002088 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002089 * Provides a convenient way to set the various fields of a {@link Notification} and generate
Scott Main183bf112012-08-13 19:12:13 -07002090 * content views using the platform's notification layout template. If your app supports
2091 * versions of Android as old as API level 4, you can instead use
2092 * {@link android.support.v4.app.NotificationCompat.Builder NotificationCompat.Builder},
2093 * available in the <a href="{@docRoot}tools/extras/support-library.html">Android Support
2094 * library</a>.
Joe Malin8d40d042012-11-05 11:36:40 -08002095 *
Scott Main183bf112012-08-13 19:12:13 -07002096 * <p>Example:
Joe Malin8d40d042012-11-05 11:36:40 -08002097 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002098 * <pre class="prettyprint">
Scott Main183bf112012-08-13 19:12:13 -07002099 * Notification noti = new Notification.Builder(mContext)
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002100 * .setContentTitle(&quot;New mail from &quot; + sender.toString())
2101 * .setContentText(subject)
2102 * .setSmallIcon(R.drawable.new_mail)
2103 * .setLargeIcon(aBitmap)
Chris Wrenfbd96ba2012-05-01 12:03:58 -04002104 * .build();
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002105 * </pre>
Joe Onoratocb109a02011-01-18 17:57:41 -08002106 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002107 public static class Builder {
Daniel Sandler602ad1c2012-06-12 16:06:27 -04002108 private static final int MAX_ACTION_BUTTONS = 3;
Jorim Jaggi445d3c02014-08-19 22:33:42 +02002109 private static final float LARGE_TEXT_SCALE = 1.3f;
Daniel Sandler8680bf82012-05-15 16:52:52 -04002110
Joe Onorato46439ce2010-11-19 13:56:21 -08002111 private Context mContext;
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002112 private Notification mN;
2113 private Bundle mUserExtras = new Bundle();
Chris Wrenfbd96ba2012-05-01 12:03:58 -04002114 private Style mStyle;
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002115 private ArrayList<Action> mActions = new ArrayList<Action>(MAX_ACTION_BUTTONS);
2116 private ArrayList<String> mPersonList = new ArrayList<String>();
2117 private NotificationColorUtil mColorUtil;
2118 private boolean mColorUtilInited = false;
Christoph Studer7ac80e62014-08-04 16:01:57 +02002119
2120 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002121 * Constructs a new Builder with the defaults:
Joe Onoratocb109a02011-01-18 17:57:41 -08002122 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002123
2124 * <table>
2125 * <tr><th align=right>priority</th>
2126 * <td>{@link #PRIORITY_DEFAULT}</td></tr>
2127 * <tr><th align=right>when</th>
2128 * <td>now ({@link System#currentTimeMillis()})</td></tr>
2129 * <tr><th align=right>audio stream</th>
2130 * <td>{@link #STREAM_DEFAULT}</td></tr>
2131 * </table>
Joe Onoratocb109a02011-01-18 17:57:41 -08002132 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002133
2134 * @param context
2135 * A {@link Context} that will be used by the Builder to construct the
2136 * RemoteViews. The Context will not be held past the lifetime of this Builder
2137 * object.
Joe Onoratocb109a02011-01-18 17:57:41 -08002138 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002139 public Builder(Context context) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002140 this(context, null);
Joe Onorato46439ce2010-11-19 13:56:21 -08002141 }
2142
Joe Onoratocb109a02011-01-18 17:57:41 -08002143 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002144 * @hide
Christoph Studer4600f9b2014-07-22 22:44:43 +02002145 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002146 public Builder(Context context, Notification toAdopt) {
2147 mContext = context;
Christoph Studer4600f9b2014-07-22 22:44:43 +02002148
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002149 if (toAdopt == null) {
2150 mN = new Notification();
2151 mN.extras.putBoolean(EXTRA_SHOW_WHEN, true);
2152 mN.priority = PRIORITY_DEFAULT;
2153 mN.visibility = VISIBILITY_PRIVATE;
2154 } else {
2155 mN = toAdopt;
2156 if (mN.actions != null) {
2157 Collections.addAll(mActions, mN.actions);
Christoph Studer4600f9b2014-07-22 22:44:43 +02002158 }
2159
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002160 if (mN.extras.containsKey(EXTRA_PEOPLE)) {
2161 Collections.addAll(mPersonList, mN.extras.getStringArray(EXTRA_PEOPLE));
2162 }
2163
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002164 String templateClass = mN.extras.getString(EXTRA_TEMPLATE);
2165 if (!TextUtils.isEmpty(templateClass)) {
2166 final Class<? extends Style> styleClass
2167 = getNotificationStyleClass(templateClass);
2168 if (styleClass == null) {
2169 Log.d(TAG, "Unknown style class: " + templateClass);
2170 } else {
2171 try {
2172 final Constructor<? extends Style> ctor = styleClass.getConstructor();
2173 ctor.setAccessible(true);
2174 final Style style = ctor.newInstance();
2175 style.restoreFromExtras(mN.extras);
2176
2177 if (style != null) {
2178 setStyle(style);
2179 }
2180 } catch (Throwable t) {
2181 Log.e(TAG, "Could not create Style", t);
2182 }
2183 }
2184 }
2185
2186 }
2187 }
2188
2189 private NotificationColorUtil getColorUtil() {
2190 if (!mColorUtilInited) {
2191 mColorUtilInited = true;
2192 if (mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.LOLLIPOP) {
2193 mColorUtil = NotificationColorUtil.getInstance(mContext);
Christoph Studer4600f9b2014-07-22 22:44:43 +02002194 }
2195 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002196 return mColorUtil;
Christoph Studer4600f9b2014-07-22 22:44:43 +02002197 }
2198
2199 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002200 * Add a timestamp pertaining to the notification (usually the time the event occurred).
Daniel Sandler0c890492012-09-12 17:23:10 -07002201 * It will be shown in the notification content view by default; use
Griff Hazen50c11652014-05-16 09:46:31 -07002202 * {@link #setShowWhen(boolean) setShowWhen} to control this.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002203 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002204 * @see Notification#when
Joe Onoratocb109a02011-01-18 17:57:41 -08002205 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002206 public Builder setWhen(long when) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002207 mN.when = when;
Joe Onorato46439ce2010-11-19 13:56:21 -08002208 return this;
2209 }
2210
Joe Onoratocb109a02011-01-18 17:57:41 -08002211 /**
Griff Hazen50c11652014-05-16 09:46:31 -07002212 * Control whether the timestamp set with {@link #setWhen(long) setWhen} is shown
Daniel Sandler0c890492012-09-12 17:23:10 -07002213 * in the content view.
2214 */
2215 public Builder setShowWhen(boolean show) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002216 mN.extras.putBoolean(EXTRA_SHOW_WHEN, show);
Daniel Sandler0c890492012-09-12 17:23:10 -07002217 return this;
2218 }
2219
2220 /**
Daniel Sandlerd33b8032012-05-10 11:41:48 -04002221 * Show the {@link Notification#when} field as a stopwatch.
Joe Malin8d40d042012-11-05 11:36:40 -08002222 *
2223 * Instead of presenting <code>when</code> as a timestamp, the notification will show an
Daniel Sandlerd33b8032012-05-10 11:41:48 -04002224 * automatically updating display of the minutes and seconds since <code>when</code>.
Daniel Sandlera2985ed2012-04-03 16:42:00 -04002225 *
Daniel Sandlerd33b8032012-05-10 11:41:48 -04002226 * Useful when showing an elapsed time (like an ongoing phone call).
2227 *
2228 * @see android.widget.Chronometer
Daniel Sandlera2985ed2012-04-03 16:42:00 -04002229 * @see Notification#when
2230 */
2231 public Builder setUsesChronometer(boolean b) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002232 mN.extras.putBoolean(EXTRA_SHOW_CHRONOMETER, b);
Daniel Sandlera2985ed2012-04-03 16:42:00 -04002233 return this;
2234 }
2235
2236 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002237 * Set the small icon resource, which will be used to represent the notification in the
2238 * status bar.
Joe Onoratocb109a02011-01-18 17:57:41 -08002239 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002240
2241 * The platform template for the expanded view will draw this icon in the left, unless a
2242 * {@link #setLargeIcon(Bitmap) large icon} has also been specified, in which case the small
2243 * icon will be moved to the right-hand side.
2244 *
2245
2246 * @param icon
2247 * A resource ID in the application's package of the drawable to use.
2248 * @see Notification#icon
Joe Onoratocb109a02011-01-18 17:57:41 -08002249 */
Tor Norbye7b9c9122013-05-30 16:48:33 -07002250 public Builder setSmallIcon(@DrawableRes int icon) {
Dan Sandlerd63f9322015-05-06 15:18:49 -04002251 return setSmallIcon(icon != 0
2252 ? Icon.createWithResource(mContext, icon)
2253 : null);
Joe Onorato46439ce2010-11-19 13:56:21 -08002254 }
2255
Joe Onoratocb109a02011-01-18 17:57:41 -08002256 /**
2257 * A variant of {@link #setSmallIcon(int) setSmallIcon(int)} that takes an additional
2258 * level parameter for when the icon is a {@link android.graphics.drawable.LevelListDrawable
2259 * LevelListDrawable}.
2260 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002261 * @param icon A resource ID in the application's package of the drawable to use.
Joe Onoratocb109a02011-01-18 17:57:41 -08002262 * @param level The level to use for the icon.
2263 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002264 * @see Notification#icon
2265 * @see Notification#iconLevel
Joe Onoratocb109a02011-01-18 17:57:41 -08002266 */
Tor Norbye7b9c9122013-05-30 16:48:33 -07002267 public Builder setSmallIcon(@DrawableRes int icon, int level) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002268 mN.iconLevel = level;
Dan Sandlerd63f9322015-05-06 15:18:49 -04002269 return setSmallIcon(icon);
2270 }
2271
2272 /**
2273 * Set the small icon, which will be used to represent the notification in the
2274 * status bar and content view (unless overriden there by a
2275 * {@link #setLargeIcon(Bitmap) large icon}).
2276 *
2277 * @param icon An Icon object to use.
2278 * @see Notification#icon
2279 */
2280 public Builder setSmallIcon(Icon icon) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002281 mN.setSmallIcon(icon);
2282 if (icon != null && icon.getType() == Icon.TYPE_RESOURCE) {
2283 mN.icon = icon.getResId();
2284 }
Joe Onorato46439ce2010-11-19 13:56:21 -08002285 return this;
2286 }
2287
Joe Onoratocb109a02011-01-18 17:57:41 -08002288 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002289 * Set the first line of text in the platform notification template.
Joe Onoratocb109a02011-01-18 17:57:41 -08002290 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002291 public Builder setContentTitle(CharSequence title) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002292 mN.extras.putCharSequence(EXTRA_TITLE, safeCharSequence(title));
Joe Onorato46439ce2010-11-19 13:56:21 -08002293 return this;
2294 }
2295
Joe Onoratocb109a02011-01-18 17:57:41 -08002296 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002297 * Set the second line of text in the platform notification template.
Joe Onoratocb109a02011-01-18 17:57:41 -08002298 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002299 public Builder setContentText(CharSequence text) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002300 mN.extras.putCharSequence(EXTRA_TEXT, safeCharSequence(text));
Joe Onorato46439ce2010-11-19 13:56:21 -08002301 return this;
2302 }
2303
Joe Onoratocb109a02011-01-18 17:57:41 -08002304 /**
Joe Malin8d40d042012-11-05 11:36:40 -08002305 * Set the third line of text in the platform notification template.
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04002306 * Don't use if you're also using {@link #setProgress(int, int, boolean)}; they occupy the
2307 * same location in the standard template.
Daniel Sandlerf3b73432012-03-27 15:01:25 -04002308 */
2309 public Builder setSubText(CharSequence text) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002310 mN.extras.putCharSequence(EXTRA_SUB_TEXT, safeCharSequence(text));
Daniel Sandlerf3b73432012-03-27 15:01:25 -04002311 return this;
2312 }
2313
2314 /**
Joe Onoratocb109a02011-01-18 17:57:41 -08002315 * Set the large number at the right-hand side of the notification. This is
2316 * equivalent to setContentInfo, although it might show the number in a different
2317 * font size for readability.
2318 */
Joe Onorato8595a3d2010-11-19 18:12:07 -08002319 public Builder setNumber(int number) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002320 mN.number = number;
Joe Onorato8595a3d2010-11-19 18:12:07 -08002321 return this;
2322 }
2323
Joe Onoratocb109a02011-01-18 17:57:41 -08002324 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002325 * A small piece of additional information pertaining to this notification.
2326 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002327 * The platform template will draw this on the last line of the notification, at the far
2328 * right (to the right of a smallIcon if it has been placed there).
Joe Onoratocb109a02011-01-18 17:57:41 -08002329 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002330 public Builder setContentInfo(CharSequence info) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002331 mN.extras.putCharSequence(EXTRA_INFO_TEXT, safeCharSequence(info));
Joe Onorato46439ce2010-11-19 13:56:21 -08002332 return this;
2333 }
2334
Joe Onoratocb109a02011-01-18 17:57:41 -08002335 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002336 * Set the progress this notification represents.
2337 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002338 * The platform template will represent this using a {@link ProgressBar}.
Jeff Sharkey1c400132011-08-05 14:50:13 -07002339 */
2340 public Builder setProgress(int max, int progress, boolean indeterminate) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002341 mN.extras.putInt(EXTRA_PROGRESS, progress);
2342 mN.extras.putInt(EXTRA_PROGRESS_MAX, max);
2343 mN.extras.putBoolean(EXTRA_PROGRESS_INDETERMINATE, indeterminate);
Jeff Sharkey1c400132011-08-05 14:50:13 -07002344 return this;
2345 }
2346
2347 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002348 * Supply a custom RemoteViews to use instead of the platform template.
2349 *
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002350 * Use {@link #setCustomContentView(RemoteViews)} instead.
Joe Onoratocb109a02011-01-18 17:57:41 -08002351 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002352 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08002353 public Builder setContent(RemoteViews views) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002354 return setCustomContentView(views);
2355 }
2356
2357 /**
2358 * Supply custom RemoteViews to use instead of the platform template.
2359 *
2360 * This will override the layout that would otherwise be constructed by this Builder
2361 * object.
2362 */
2363 public Builder setCustomContentView(RemoteViews contentView) {
2364 mN.contentView = contentView;
2365 return this;
2366 }
2367
2368 /**
2369 * Supply custom RemoteViews to use instead of the platform template in the expanded form.
2370 *
2371 * This will override the expanded layout that would otherwise be constructed by this
2372 * Builder object.
2373 */
2374 public Builder setCustomBigContentView(RemoteViews contentView) {
2375 mN.bigContentView = contentView;
2376 return this;
2377 }
2378
2379 /**
2380 * Supply custom RemoteViews to use instead of the platform template in the heads up dialog.
2381 *
2382 * This will override the heads-up layout that would otherwise be constructed by this
2383 * Builder object.
2384 */
2385 public Builder setCustomHeadsUpContentView(RemoteViews contentView) {
2386 mN.headsUpContentView = contentView;
Joe Onorato46439ce2010-11-19 13:56:21 -08002387 return this;
2388 }
2389
Joe Onoratocb109a02011-01-18 17:57:41 -08002390 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002391 * Supply a {@link PendingIntent} to be sent when the notification is clicked.
2392 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002393 * As of {@link android.os.Build.VERSION_CODES#HONEYCOMB}, if this field is unset and you
2394 * have specified a custom RemoteViews with {@link #setContent(RemoteViews)}, you can use
2395 * {@link RemoteViews#setOnClickPendingIntent RemoteViews.setOnClickPendingIntent(int,PendingIntent)}
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002396 * to assign PendingIntents to individual views in that custom layout (i.e., to create
Daniel Sandlerf3b73432012-03-27 15:01:25 -04002397 * clickable buttons inside the notification view).
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002398 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002399 * @see Notification#contentIntent Notification.contentIntent
Joe Onoratocb109a02011-01-18 17:57:41 -08002400 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002401 public Builder setContentIntent(PendingIntent intent) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002402 mN.contentIntent = intent;
Joe Onorato46439ce2010-11-19 13:56:21 -08002403 return this;
2404 }
2405
Joe Onoratocb109a02011-01-18 17:57:41 -08002406 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002407 * Supply a {@link PendingIntent} to send when the notification is cleared explicitly by the user.
2408 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002409 * @see Notification#deleteIntent
Joe Onoratocb109a02011-01-18 17:57:41 -08002410 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002411 public Builder setDeleteIntent(PendingIntent intent) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002412 mN.deleteIntent = intent;
Joe Onorato46439ce2010-11-19 13:56:21 -08002413 return this;
2414 }
2415
Joe Onoratocb109a02011-01-18 17:57:41 -08002416 /**
2417 * An intent to launch instead of posting the notification to the status bar.
2418 * Only for use with extremely high-priority notifications demanding the user's
2419 * <strong>immediate</strong> attention, such as an incoming phone call or
2420 * alarm clock that the user has explicitly set to a particular time.
2421 * If this facility is used for something else, please give the user an option
2422 * to turn it off and use a normal notification, as this can be extremely
2423 * disruptive.
2424 *
Chris Wren47c20a12014-06-18 17:27:29 -04002425 * <p>
2426 * The system UI may choose to display a heads-up notification, instead of
2427 * launching this intent, while the user is using the device.
2428 * </p>
2429 *
Joe Onoratocb109a02011-01-18 17:57:41 -08002430 * @param intent The pending intent to launch.
2431 * @param highPriority Passing true will cause this notification to be sent
2432 * even if other notifications are suppressed.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002433 *
2434 * @see Notification#fullScreenIntent
Joe Onoratocb109a02011-01-18 17:57:41 -08002435 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002436 public Builder setFullScreenIntent(PendingIntent intent, boolean highPriority) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002437 mN.fullScreenIntent = intent;
Joe Onorato46439ce2010-11-19 13:56:21 -08002438 setFlag(FLAG_HIGH_PRIORITY, highPriority);
2439 return this;
2440 }
2441
Joe Onoratocb109a02011-01-18 17:57:41 -08002442 /**
Dan Sandler5fcdf6e2014-07-18 11:31:15 -04002443 * Set the "ticker" text which is sent to accessibility services.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002444 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002445 * @see Notification#tickerText
Joe Onoratocb109a02011-01-18 17:57:41 -08002446 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002447 public Builder setTicker(CharSequence tickerText) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002448 mN.tickerText = safeCharSequence(tickerText);
Joe Onorato46439ce2010-11-19 13:56:21 -08002449 return this;
2450 }
2451
Joe Onoratocb109a02011-01-18 17:57:41 -08002452 /**
Dan Sandler5fcdf6e2014-07-18 11:31:15 -04002453 * Obsolete version of {@link #setTicker(CharSequence)}.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002454 *
Joe Onoratocb109a02011-01-18 17:57:41 -08002455 */
Dan Sandler5fcdf6e2014-07-18 11:31:15 -04002456 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08002457 public Builder setTicker(CharSequence tickerText, RemoteViews views) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002458 setTicker(tickerText);
2459 // views is ignored
Joe Onorato46439ce2010-11-19 13:56:21 -08002460 return this;
2461 }
2462
Joe Onoratocb109a02011-01-18 17:57:41 -08002463 /**
Dan Sandlerd63f9322015-05-06 15:18:49 -04002464 * Add a large icon to the notification content view.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002465 *
2466 * In the platform template, this image will be shown on the left of the notification view
Dan Sandlerd63f9322015-05-06 15:18:49 -04002467 * in place of the {@link #setSmallIcon(Icon) small icon} (which will be placed in a small
2468 * badge atop the large icon).
Dan Sandler08a04c12015-05-06 15:18:49 -04002469 */
Dan Sandlerd63f9322015-05-06 15:18:49 -04002470 public Builder setLargeIcon(Bitmap b) {
2471 return setLargeIcon(b != null ? Icon.createWithBitmap(b) : null);
2472 }
2473
2474 /**
2475 * Add a large icon to the notification content view.
2476 *
2477 * In the platform template, this image will be shown on the left of the notification view
2478 * in place of the {@link #setSmallIcon(Icon) small icon} (which will be placed in a small
2479 * badge atop the large icon).
2480 */
2481 public Builder setLargeIcon(Icon icon) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002482 mN.mLargeIcon = icon;
2483 mN.extras.putParcelable(EXTRA_LARGE_ICON, icon);
Joe Onorato46439ce2010-11-19 13:56:21 -08002484 return this;
2485 }
2486
Joe Onoratocb109a02011-01-18 17:57:41 -08002487 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002488 * Set the sound to play.
2489 *
John Spurlockc0650f022014-07-19 13:22:39 -04002490 * It will be played using the {@link #AUDIO_ATTRIBUTES_DEFAULT default audio attributes}
2491 * for notifications.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002492 *
Chris Wren47c20a12014-06-18 17:27:29 -04002493 * <p>
2494 * A notification that is noisy is more likely to be presented as a heads-up notification.
2495 * </p>
2496 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002497 * @see Notification#sound
Joe Onoratocb109a02011-01-18 17:57:41 -08002498 */
Joe Onorato52f80cd2010-11-21 15:34:48 -08002499 public Builder setSound(Uri sound) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002500 mN.sound = sound;
2501 mN.audioAttributes = AUDIO_ATTRIBUTES_DEFAULT;
Joe Onorato52f80cd2010-11-21 15:34:48 -08002502 return this;
2503 }
2504
Joe Onoratocb109a02011-01-18 17:57:41 -08002505 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002506 * Set the sound to play, along with a specific stream on which to play it.
Joe Onoratocb109a02011-01-18 17:57:41 -08002507 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002508 * See {@link android.media.AudioManager} for the <code>STREAM_</code> constants.
2509 *
Chris Wren47c20a12014-06-18 17:27:29 -04002510 * <p>
2511 * A notification that is noisy is more likely to be presented as a heads-up notification.
2512 * </p>
John Spurlockc0650f022014-07-19 13:22:39 -04002513 * @deprecated use {@link #setSound(Uri, AudioAttributes)} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002514 * @see Notification#sound
Joe Onoratocb109a02011-01-18 17:57:41 -08002515 */
Jean-Michel Trivi81f871e2014-08-06 16:32:38 -07002516 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08002517 public Builder setSound(Uri sound, int streamType) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002518 mN.sound = sound;
2519 mN.audioStreamType = streamType;
Joe Onorato46439ce2010-11-19 13:56:21 -08002520 return this;
2521 }
2522
Joe Onoratocb109a02011-01-18 17:57:41 -08002523 /**
John Spurlockc0650f022014-07-19 13:22:39 -04002524 * Set the sound to play, along with specific {@link AudioAttributes audio attributes} to
2525 * use during playback.
2526 *
2527 * <p>
2528 * A notification that is noisy is more likely to be presented as a heads-up notification.
2529 * </p>
2530 *
2531 * @see Notification#sound
2532 */
2533 public Builder setSound(Uri sound, AudioAttributes audioAttributes) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002534 mN.sound = sound;
2535 mN.audioAttributes = audioAttributes;
John Spurlockc0650f022014-07-19 13:22:39 -04002536 return this;
2537 }
2538
2539 /**
Joe Onoratocb109a02011-01-18 17:57:41 -08002540 * Set the vibration pattern to use.
2541 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002542 * See {@link android.os.Vibrator#vibrate(long[], int)} for a discussion of the
2543 * <code>pattern</code> parameter.
2544 *
Chris Wren47c20a12014-06-18 17:27:29 -04002545 * <p>
2546 * A notification that vibrates is more likely to be presented as a heads-up notification.
2547 * </p>
2548 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002549 * @see Notification#vibrate
Joe Onoratocb109a02011-01-18 17:57:41 -08002550 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002551 public Builder setVibrate(long[] pattern) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002552 mN.vibrate = pattern;
Joe Onorato46439ce2010-11-19 13:56:21 -08002553 return this;
2554 }
2555
Joe Onoratocb109a02011-01-18 17:57:41 -08002556 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002557 * Set the desired color for the indicator LED on the device, as well as the
2558 * blink duty cycle (specified in milliseconds).
2559 *
2560
2561 * Not all devices will honor all (or even any) of these values.
2562 *
2563
2564 * @see Notification#ledARGB
2565 * @see Notification#ledOnMS
2566 * @see Notification#ledOffMS
Joe Onoratocb109a02011-01-18 17:57:41 -08002567 */
Tor Norbye80756e32015-03-02 09:39:27 -08002568 public Builder setLights(@ColorInt int argb, int onMs, int offMs) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002569 mN.ledARGB = argb;
2570 mN.ledOnMS = onMs;
2571 mN.ledOffMS = offMs;
Julia Reynolds10ee1fc2015-11-09 11:04:55 -05002572 if (onMs != 0 || offMs != 0) {
2573 mN.flags |= FLAG_SHOW_LIGHTS;
2574 }
2575 if ((mN.defaults & DEFAULT_LIGHTS) != 0) {
2576 mN.flags |= FLAG_SHOW_LIGHTS;
2577 }
Joe Onorato46439ce2010-11-19 13:56:21 -08002578 return this;
2579 }
2580
Joe Onoratocb109a02011-01-18 17:57:41 -08002581 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002582 * Set whether this is an "ongoing" notification.
Joe Onoratocb109a02011-01-18 17:57:41 -08002583 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002584
2585 * Ongoing notifications cannot be dismissed by the user, so your application or service
2586 * must take care of canceling them.
2587 *
2588
2589 * They are typically used to indicate a background task that the user is actively engaged
2590 * with (e.g., playing music) or is pending in some way and therefore occupying the device
2591 * (e.g., a file download, sync operation, active network connection).
2592 *
2593
2594 * @see Notification#FLAG_ONGOING_EVENT
2595 * @see Service#setForeground(boolean)
Joe Onoratocb109a02011-01-18 17:57:41 -08002596 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002597 public Builder setOngoing(boolean ongoing) {
2598 setFlag(FLAG_ONGOING_EVENT, ongoing);
2599 return this;
2600 }
2601
Joe Onoratocb109a02011-01-18 17:57:41 -08002602 /**
2603 * Set this flag if you would only like the sound, vibrate
2604 * and ticker to be played if the notification is not already showing.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002605 *
2606 * @see Notification#FLAG_ONLY_ALERT_ONCE
Joe Onoratocb109a02011-01-18 17:57:41 -08002607 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002608 public Builder setOnlyAlertOnce(boolean onlyAlertOnce) {
2609 setFlag(FLAG_ONLY_ALERT_ONCE, onlyAlertOnce);
2610 return this;
2611 }
2612
Joe Onoratocb109a02011-01-18 17:57:41 -08002613 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002614 * Make this notification automatically dismissed when the user touches it. The
2615 * PendingIntent set with {@link #setDeleteIntent} will be sent when this happens.
2616 *
2617 * @see Notification#FLAG_AUTO_CANCEL
Joe Onoratocb109a02011-01-18 17:57:41 -08002618 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002619 public Builder setAutoCancel(boolean autoCancel) {
Joe Onorato281d83f2011-01-04 17:13:10 -08002620 setFlag(FLAG_AUTO_CANCEL, autoCancel);
Joe Onorato46439ce2010-11-19 13:56:21 -08002621 return this;
2622 }
2623
Joe Onoratocb109a02011-01-18 17:57:41 -08002624 /**
Griff Hazendfcb0802014-02-11 12:00:00 -08002625 * Set whether or not this notification should not bridge to other devices.
2626 *
2627 * <p>Some notifications can be bridged to other devices for remote display.
2628 * This hint can be set to recommend this notification not be bridged.
2629 */
2630 public Builder setLocalOnly(boolean localOnly) {
2631 setFlag(FLAG_LOCAL_ONLY, localOnly);
2632 return this;
2633 }
2634
2635 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002636 * Set which notification properties will be inherited from system defaults.
Joe Onoratocb109a02011-01-18 17:57:41 -08002637 * <p>
2638 * The value should be one or more of the following fields combined with
2639 * bitwise-or:
2640 * {@link #DEFAULT_SOUND}, {@link #DEFAULT_VIBRATE}, {@link #DEFAULT_LIGHTS}.
2641 * <p>
2642 * For all default values, use {@link #DEFAULT_ALL}.
2643 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002644 public Builder setDefaults(int defaults) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002645 mN.defaults = defaults;
Joe Onorato46439ce2010-11-19 13:56:21 -08002646 return this;
2647 }
2648
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002649 /**
2650 * Set the priority of this notification.
2651 *
2652 * @see Notification#priority
2653 */
Tor Norbyed9273d62013-05-30 15:59:53 -07002654 public Builder setPriority(@Priority int pri) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002655 mN.priority = pri;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002656 return this;
2657 }
Joe Malin8d40d042012-11-05 11:36:40 -08002658
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002659 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -04002660 * Set the notification category.
Joe Malin8d40d042012-11-05 11:36:40 -08002661 *
John Spurlockfd7f1e02014-03-18 16:41:57 -04002662 * @see Notification#category
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002663 */
John Spurlockfd7f1e02014-03-18 16:41:57 -04002664 public Builder setCategory(String category) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002665 mN.category = category;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002666 return this;
2667 }
2668
2669 /**
Chris Wrendde75302014-03-26 17:24:15 -04002670 * Add a person that is relevant to this notification.
2671 *
Chris Wrene6c48932014-09-29 17:19:27 -04002672 * <P>
2673 * Depending on user preferences, this annotation may allow the notification to pass
2674 * through interruption filters, and to appear more prominently in the user interface.
2675 * </P>
2676 *
2677 * <P>
2678 * The person should be specified by the {@code String} representation of a
2679 * {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI}.
2680 * </P>
2681 *
2682 * <P>The system will also attempt to resolve {@code mailto:} and {@code tel:} schema
2683 * URIs. The path part of these URIs must exist in the contacts database, in the
2684 * appropriate column, or the reference will be discarded as invalid. Telephone schema
2685 * URIs will be resolved by {@link android.provider.ContactsContract.PhoneLookup}.
2686 * </P>
2687 *
2688 * @param uri A URI for the person.
Chris Wrendde75302014-03-26 17:24:15 -04002689 * @see Notification#EXTRA_PEOPLE
2690 */
Chris Wrene6c48932014-09-29 17:19:27 -04002691 public Builder addPerson(String uri) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002692 mPersonList.add(uri);
Chris Wrendde75302014-03-26 17:24:15 -04002693 return this;
2694 }
2695
2696 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002697 * Set this notification to be part of a group of notifications sharing the same key.
2698 * Grouped notifications may display in a cluster or stack on devices which
2699 * support such rendering.
2700 *
2701 * <p>To make this notification the summary for its group, also call
2702 * {@link #setGroupSummary}. A sort order can be specified for group members by using
2703 * {@link #setSortKey}.
2704 * @param groupKey The group key of the group.
2705 * @return this object for method chaining
2706 */
2707 public Builder setGroup(String groupKey) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002708 mN.mGroupKey = groupKey;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002709 return this;
2710 }
2711
2712 /**
2713 * Set this notification to be the group summary for a group of notifications.
2714 * Grouped notifications may display in a cluster or stack on devices which
2715 * support such rendering. Requires a group key also be set using {@link #setGroup}.
2716 * @param isGroupSummary Whether this notification should be a group summary.
2717 * @return this object for method chaining
2718 */
2719 public Builder setGroupSummary(boolean isGroupSummary) {
2720 setFlag(FLAG_GROUP_SUMMARY, isGroupSummary);
2721 return this;
2722 }
2723
2724 /**
2725 * Set a sort key that orders this notification among other notifications from the
2726 * same package. This can be useful if an external sort was already applied and an app
2727 * would like to preserve this. Notifications will be sorted lexicographically using this
2728 * value, although providing different priorities in addition to providing sort key may
2729 * cause this value to be ignored.
2730 *
2731 * <p>This sort key can also be used to order members of a notification group. See
Griff Hazen9e1379f2014-05-20 12:50:51 -07002732 * {@link #setGroup}.
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002733 *
2734 * @see String#compareTo(String)
2735 */
2736 public Builder setSortKey(String sortKey) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002737 mN.mSortKey = sortKey;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002738 return this;
2739 }
2740
2741 /**
Griff Hazen720042b2014-02-24 15:46:56 -08002742 * Merge additional metadata into this notification.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002743 *
Griff Hazen720042b2014-02-24 15:46:56 -08002744 * <p>Values within the Bundle will replace existing extras values in this Builder.
2745 *
2746 * @see Notification#extras
2747 */
Griff Hazen959591e2014-05-15 22:26:18 -07002748 public Builder addExtras(Bundle extras) {
2749 if (extras != null) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002750 mUserExtras.putAll(extras);
Griff Hazen720042b2014-02-24 15:46:56 -08002751 }
2752 return this;
2753 }
2754
2755 /**
2756 * Set metadata for this notification.
2757 *
2758 * <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 -04002759 * current contents are copied into the Notification each time {@link #build()} is
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002760 * called.
2761 *
Griff Hazen720042b2014-02-24 15:46:56 -08002762 * <p>Replaces any existing extras values with those from the provided Bundle.
2763 * Use {@link #addExtras} to merge in metadata instead.
2764 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002765 * @see Notification#extras
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002766 */
Griff Hazen959591e2014-05-15 22:26:18 -07002767 public Builder setExtras(Bundle extras) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002768 if (extras != null) {
2769 mUserExtras = extras;
2770 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002771 return this;
2772 }
2773
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002774 /**
Griff Hazen720042b2014-02-24 15:46:56 -08002775 * Get the current metadata Bundle used by this notification Builder.
2776 *
2777 * <p>The returned Bundle is shared with this Builder.
2778 *
2779 * <p>The current contents of this Bundle are copied into the Notification each time
2780 * {@link #build()} is called.
2781 *
2782 * @see Notification#extras
2783 */
2784 public Bundle getExtras() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002785 return mUserExtras;
2786 }
2787
2788 private Bundle getAllExtras() {
2789 final Bundle saveExtras = (Bundle) mUserExtras.clone();
2790 saveExtras.putAll(mN.extras);
2791 return saveExtras;
Griff Hazen720042b2014-02-24 15:46:56 -08002792 }
2793
2794 /**
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002795 * Add an action to this notification. Actions are typically displayed by
2796 * the system as a button adjacent to the notification content.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04002797 * <p>
2798 * Every action must have an icon (32dp square and matching the
2799 * <a href="{@docRoot}design/style/iconography.html#action-bar">Holo
2800 * Dark action bar</a> visual style), a textual label, and a {@link PendingIntent}.
2801 * <p>
2802 * A notification in its expanded form can display up to 3 actions, from left to right in
2803 * the order they were added. Actions will not be displayed when the notification is
2804 * collapsed, however, so be sure that any essential functions may be accessed by the user
2805 * in some other way (for example, in the Activity pointed to by {@link #contentIntent}).
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002806 *
2807 * @param icon Resource ID of a drawable that represents the action.
2808 * @param title Text describing the action.
2809 * @param intent PendingIntent to be fired when the action is invoked.
Dan Sandler86647982015-05-13 23:41:13 -04002810 *
2811 * @deprecated Use {@link #addAction(Action)} instead.
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002812 */
Dan Sandler86647982015-05-13 23:41:13 -04002813 @Deprecated
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002814 public Builder addAction(int icon, CharSequence title, PendingIntent intent) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04002815 mActions.add(new Action(icon, safeCharSequence(title), intent));
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002816 return this;
2817 }
2818
Chris Wrenfbd96ba2012-05-01 12:03:58 -04002819 /**
Griff Hazen959591e2014-05-15 22:26:18 -07002820 * Add an action to this notification. Actions are typically displayed by
2821 * the system as a button adjacent to the notification content.
2822 * <p>
2823 * Every action must have an icon (32dp square and matching the
2824 * <a href="{@docRoot}design/style/iconography.html#action-bar">Holo
2825 * Dark action bar</a> visual style), a textual label, and a {@link PendingIntent}.
2826 * <p>
2827 * A notification in its expanded form can display up to 3 actions, from left to right in
2828 * the order they were added. Actions will not be displayed when the notification is
2829 * collapsed, however, so be sure that any essential functions may be accessed by the user
2830 * in some other way (for example, in the Activity pointed to by {@link #contentIntent}).
2831 *
2832 * @param action The action to add.
2833 */
2834 public Builder addAction(Action action) {
2835 mActions.add(action);
2836 return this;
2837 }
2838
2839 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002840 * Alter the complete list of actions attached to this notification.
2841 * @see #addAction(Action).
2842 *
2843 * @param actions
2844 * @return
2845 */
2846 public Builder setActions(Action... actions) {
2847 mActions.clear();
2848 for (int i = 0; i < actions.length; i++) {
2849 mActions.add(actions[i]);
2850 }
2851 return this;
2852 }
2853
2854 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04002855 * Add a rich notification style to be applied at build time.
2856 *
2857 * @param style Object responsible for modifying the notification style.
2858 */
2859 public Builder setStyle(Style style) {
2860 if (mStyle != style) {
2861 mStyle = style;
Daniel Sandlerc08dea22012-06-28 08:35:24 -07002862 if (mStyle != null) {
2863 mStyle.setBuilder(this);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002864 mN.extras.putString(EXTRA_TEMPLATE, style.getClass().getName());
2865 } else {
2866 mN.extras.remove(EXTRA_TEMPLATE);
Daniel Sandlerc08dea22012-06-28 08:35:24 -07002867 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04002868 }
2869 return this;
2870 }
2871
Dan Sandler0bf2ed82013-12-21 23:33:41 -06002872 /**
2873 * Specify the value of {@link #visibility}.
Griff Hazenb720abe2014-05-20 13:15:30 -07002874 *
Dan Sandler0bf2ed82013-12-21 23:33:41 -06002875 * @param visibility One of {@link #VISIBILITY_PRIVATE} (the default),
2876 * {@link #VISIBILITY_SECRET}, or {@link #VISIBILITY_PUBLIC}.
2877 *
2878 * @return The same Builder.
2879 */
2880 public Builder setVisibility(int visibility) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002881 mN.visibility = visibility;
Dan Sandler0bf2ed82013-12-21 23:33:41 -06002882 return this;
2883 }
2884
2885 /**
2886 * Supply a replacement Notification whose contents should be shown in insecure contexts
2887 * (i.e. atop the secure lockscreen). See {@link #visibility} and {@link #VISIBILITY_PUBLIC}.
2888 * @param n A replacement notification, presumably with some or all info redacted.
2889 * @return The same Builder.
2890 */
2891 public Builder setPublicVersion(Notification n) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002892 if (n != null) {
2893 mN.publicVersion = new Notification();
2894 n.cloneInto(mN.publicVersion, /*heavy=*/ true);
2895 } else {
2896 mN.publicVersion = null;
2897 }
Dan Sandler0bf2ed82013-12-21 23:33:41 -06002898 return this;
2899 }
2900
Griff Hazenb720abe2014-05-20 13:15:30 -07002901 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002902 * Apply an extender to this notification builder. Extenders may be used to add
2903 * metadata or change options on this builder.
2904 */
Griff Hazen61a9e862014-05-22 16:05:19 -07002905 public Builder extend(Extender extender) {
2906 extender.extend(this);
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002907 return this;
2908 }
2909
Dan Sandler4e787062015-06-17 15:09:48 -04002910 /**
2911 * @hide
2912 */
2913 public void setFlag(int mask, boolean value) {
Joe Onorato46439ce2010-11-19 13:56:21 -08002914 if (value) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002915 mN.flags |= mask;
Joe Onorato46439ce2010-11-19 13:56:21 -08002916 } else {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002917 mN.flags &= ~mask;
Joe Onorato46439ce2010-11-19 13:56:21 -08002918 }
2919 }
2920
Dan Sandler26e81cf2014-05-06 10:01:27 -04002921 /**
2922 * Sets {@link Notification#color}.
2923 *
2924 * @param argb The accent color to use
2925 *
2926 * @return The same Builder.
2927 */
Tor Norbye80756e32015-03-02 09:39:27 -08002928 public Builder setColor(@ColorInt int argb) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002929 mN.color = argb;
Julia Reynolds10ee1fc2015-11-09 11:04:55 -05002930 sanitizeColor();
Dan Sandler26e81cf2014-05-06 10:01:27 -04002931 return this;
2932 }
2933
Julia Reynolds74303cf2015-10-16 11:37:55 -04002934 /**
Julia Reynolds054c5dc2015-11-17 15:36:30 -05002935 * Sets the topic of this notification. Topics are typically displayed in Notification
Julia Reynolds74303cf2015-10-16 11:37:55 -04002936 * settings.
2937 * <p>
2938 * Every topic must have an id and a textual label.
2939 *
2940 * @param topic The topic to add.
2941 */
Julia Reynolds054c5dc2015-11-17 15:36:30 -05002942 public Builder setTopic(Topic topic) {
2943 mN.topic = topic;
Julia Reynolds74303cf2015-10-16 11:37:55 -04002944 return this;
2945 }
2946
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02002947 private Drawable getProfileBadgeDrawable() {
Christoph Studer7ac80e62014-08-04 16:01:57 +02002948 // Note: This assumes that the current user can read the profile badge of the
2949 // originating user.
Svetoslavc7d62f02014-09-04 15:39:54 -07002950 return mContext.getPackageManager().getUserBadgeForDensity(
Julia Reynoldsda303542015-11-23 14:00:20 -05002951 new UserHandle(mContext.getUserId()), 0);
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02002952 }
2953
2954 private Bitmap getProfileBadge() {
2955 Drawable badge = getProfileBadgeDrawable();
Kenny Guy8a0101b2014-05-08 23:34:12 +01002956 if (badge == null) {
2957 return null;
2958 }
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02002959 final int size = mContext.getResources().getDimensionPixelSize(
2960 R.dimen.notification_badge_size);
2961 Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
Kenny Guy8a0101b2014-05-08 23:34:12 +01002962 Canvas canvas = new Canvas(bitmap);
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02002963 badge.setBounds(0, 0, size, size);
Kenny Guy8a0101b2014-05-08 23:34:12 +01002964 badge.draw(canvas);
2965 return bitmap;
2966 }
2967
Kenny Guy98193ea2014-07-24 19:54:37 +01002968 private boolean addProfileBadge(RemoteViews contentView, int resId) {
2969 Bitmap profileBadge = getProfileBadge();
2970
2971 contentView.setViewVisibility(R.id.profile_badge_large_template, View.GONE);
Kenny Guy98193ea2014-07-24 19:54:37 +01002972 contentView.setViewVisibility(R.id.profile_badge_line3, View.GONE);
2973
2974 if (profileBadge != null) {
2975 contentView.setImageViewBitmap(resId, profileBadge);
2976 contentView.setViewVisibility(resId, View.VISIBLE);
2977
2978 // Make sure Line 3 is visible. As badge will be here if there
2979 // is no text to display.
2980 if (resId == R.id.profile_badge_line3) {
2981 contentView.setViewVisibility(R.id.line3, View.VISIBLE);
2982 }
2983 return true;
2984 }
2985 return false;
2986 }
2987
Christoph Studerfe718432014-09-01 18:21:18 +02002988 private void resetStandardTemplate(RemoteViews contentView) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07002989 resetHeader(contentView);
2990 resetContentMargins(contentView);
Christoph Studerfe718432014-09-01 18:21:18 +02002991 contentView.setViewVisibility(R.id.right_icon, View.GONE);
Christoph Studerfe718432014-09-01 18:21:18 +02002992 contentView.setTextViewText(R.id.title, null);
2993 contentView.setTextViewText(R.id.text, null);
Christoph Studerfe718432014-09-01 18:21:18 +02002994 contentView.setViewVisibility(R.id.line3, View.GONE);
Selim Cinek29603462015-11-17 19:04:39 -08002995 contentView.setViewVisibility(R.id.text_line_1, View.GONE);
Christoph Studerfe718432014-09-01 18:21:18 +02002996 contentView.setViewVisibility(R.id.progress, View.GONE);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07002997 }
2998
2999 private void resetHeader(RemoteViews contentView) {
3000 contentView.setImageViewResource(R.id.icon, 0);
3001 contentView.setTextViewText(R.id.app_name_text, null);
Christoph Studerca1db712014-09-10 17:31:33 +02003002 contentView.setViewVisibility(R.id.chronometer, View.GONE);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003003 contentView.setViewVisibility(R.id.expand_button, View.GONE);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003004 contentView.setViewVisibility(R.id.header_sub_text, View.GONE);
Selim Cinek29603462015-11-17 19:04:39 -08003005 contentView.setViewVisibility(R.id.header_content_info, View.GONE);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003006 contentView.setViewVisibility(R.id.number_of_children, View.GONE);
Selim Cinek29603462015-11-17 19:04:39 -08003007 contentView.setViewVisibility(R.id.sub_text_divider, View.GONE);
3008 contentView.setViewVisibility(R.id.content_info_divider, View.GONE);
3009 contentView.setViewVisibility(R.id.time_divider, View.GONE);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003010 }
3011
3012 private void resetContentMargins(RemoteViews contentView) {
3013 contentView.setViewLayoutMarginEnd(R.id.line1, 0);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003014 contentView.setViewLayoutMarginEnd(R.id.line3, 0);
Christoph Studerfe718432014-09-01 18:21:18 +02003015 }
3016
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003017 private RemoteViews applyStandardTemplate(int resId) {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02003018 return applyStandardTemplate(resId, true /* hasProgress */);
3019 }
3020
3021 /**
3022 * @param hasProgress whether the progress bar should be shown and set
3023 */
3024 private RemoteViews applyStandardTemplate(int resId, boolean hasProgress) {
Kenny Guy77320062014-08-27 21:37:15 +01003025 RemoteViews contentView = new BuilderRemoteViews(mContext.getApplicationInfo(), resId);
Dan Sandler539aad42014-08-04 00:43:39 -04003026
Christoph Studerfe718432014-09-01 18:21:18 +02003027 resetStandardTemplate(contentView);
3028
Daniel Sandler9f7936a2012-05-21 16:14:28 -04003029 boolean showLine3 = false;
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003030 final Bundle ex = mN.extras;
Dan Sandler190d58d2014-05-15 09:33:39 -04003031
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003032 bindNotificationHeader(contentView);
3033 bindLargeIcon(contentView);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003034 if (ex.getCharSequence(EXTRA_TITLE) != null) {
3035 contentView.setTextViewText(R.id.title,
3036 processLegacyText(ex.getCharSequence(EXTRA_TITLE)));
Joe Onorato561d3852010-11-20 18:09:34 -08003037 }
Selim Cinek29603462015-11-17 19:04:39 -08003038 boolean showProgress = handleProgressBar(hasProgress, contentView, ex);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003039 if (ex.getCharSequence(EXTRA_TEXT) != null) {
Selim Cinek29603462015-11-17 19:04:39 -08003040 contentView.setTextViewText(showProgress ? R.id.text_line_1 : R.id.text,
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003041 processLegacyText(ex.getCharSequence(EXTRA_TEXT)));
Selim Cinek29603462015-11-17 19:04:39 -08003042 if (showProgress) {
3043 contentView.setViewVisibility(R.id.text_line_1, View.VISIBLE);
Joe Onorato059a2f82011-01-04 10:27:01 -08003044 }
Selim Cinek29603462015-11-17 19:04:39 -08003045 showLine3 = !showProgress;
Joe Onorato561d3852010-11-20 18:09:34 -08003046 }
Selim Cinek29603462015-11-17 19:04:39 -08003047 // We want to add badge to first line of text.
3048 if (addProfileBadge(contentView, R.id.profile_badge_line3)) {
3049 showLine3 = true;
3050 }
3051 // Note getStandardView may hide line 3 again.
3052 contentView.setViewVisibility(R.id.line3, showLine3 ? View.VISIBLE : View.GONE);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003053
Selim Cinek29603462015-11-17 19:04:39 -08003054 return contentView;
3055 }
3056
3057 private boolean handleProgressBar(boolean hasProgress, RemoteViews contentView, Bundle ex) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003058 final int max = ex.getInt(EXTRA_PROGRESS_MAX, 0);
3059 final int progress = ex.getInt(EXTRA_PROGRESS, 0);
3060 final boolean ind = ex.getBoolean(EXTRA_PROGRESS_INDETERMINATE);
3061 if (hasProgress && (max != 0 || ind)) {
Selim Cinek29603462015-11-17 19:04:39 -08003062 contentView.setViewVisibility(com.android.internal.R.id.progress, View.VISIBLE);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003063 contentView.setProgressBar(
Selim Cinek29603462015-11-17 19:04:39 -08003064 R.id.progress, max, progress, ind);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003065 contentView.setProgressBackgroundTintList(
3066 R.id.progress, ColorStateList.valueOf(mContext.getColor(
3067 R.color.notification_progress_background_color)));
Selim Cinek29603462015-11-17 19:04:39 -08003068 if (mN.color != COLOR_DEFAULT) {
3069 ColorStateList colorStateList = ColorStateList.valueOf(mN.color);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003070 contentView.setProgressTintList(R.id.progress, colorStateList);
3071 contentView.setProgressIndeterminateTintList(R.id.progress, colorStateList);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003072 }
Selim Cinek29603462015-11-17 19:04:39 -08003073 return true;
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003074 } else {
3075 contentView.setViewVisibility(R.id.progress, View.GONE);
Selim Cinek29603462015-11-17 19:04:39 -08003076 return false;
Jeff Sharkey1c400132011-08-05 14:50:13 -07003077 }
Joe Onorato561d3852010-11-20 18:09:34 -08003078 }
3079
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003080 private void bindLargeIcon(RemoteViews contentView) {
3081 if (mN.mLargeIcon != null) {
3082 contentView.setViewVisibility(R.id.right_icon, View.VISIBLE);
3083 contentView.setImageViewIcon(R.id.right_icon, mN.mLargeIcon);
3084 processLargeLegacyIcon(mN.mLargeIcon, contentView);
3085 int endMargin = mContext.getResources().getDimensionPixelSize(
3086 R.dimen.notification_content_picture_margin);
3087 contentView.setViewLayoutMarginEnd(R.id.line1, endMargin);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003088 contentView.setViewLayoutMarginEnd(R.id.line3, endMargin);
Selim Cinek29603462015-11-17 19:04:39 -08003089 contentView.setViewLayoutMarginEnd(R.id.progress, endMargin);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003090 }
3091 }
3092
3093 private void bindNotificationHeader(RemoteViews contentView) {
3094 bindSmallIcon(contentView);
3095 bindHeaderAppName(contentView);
Selim Cinek03d0d652015-11-13 13:18:09 -05003096 bindHeaderSubText(contentView);
Selim Cinek29603462015-11-17 19:04:39 -08003097 bindContentInfo(contentView);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003098 bindHeaderChronometerAndTime(contentView);
3099 bindExpandButton(contentView);
3100 }
3101
Selim Cinek29603462015-11-17 19:04:39 -08003102 private void bindContentInfo(RemoteViews contentView) {
3103 boolean visible = false;
3104 if (mN.extras.getCharSequence(EXTRA_INFO_TEXT) != null) {
3105 contentView.setTextViewText(R.id.header_content_info,
3106 processLegacyText(mN.extras.getCharSequence(EXTRA_INFO_TEXT)));
3107 contentView.setViewVisibility(R.id.header_content_info, View.VISIBLE);
3108 visible = true;
3109 } else if (mN.number > 0) {
3110 final int tooBig = mContext.getResources().getInteger(
3111 R.integer.status_bar_notification_info_maxnum);
3112 if (mN.number > tooBig) {
3113 contentView.setTextViewText(R.id.header_content_info, processLegacyText(
3114 mContext.getResources().getString(
3115 R.string.status_bar_notification_info_overflow)));
3116 } else {
3117 contentView.setTextViewText(R.id.header_content_info,
3118 processLegacyText(String.valueOf(mN.number)));
3119 }
3120 contentView.setViewVisibility(R.id.header_content_info, View.VISIBLE);
3121 visible = true;
3122 }
3123 if (visible) {
3124 contentView.setViewVisibility(R.id.content_info_divider, View.VISIBLE);
3125 }
3126 }
3127
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003128 private void bindExpandButton(RemoteViews contentView) {
3129 contentView.setDrawableParameters(R.id.expand_button, false, -1, resolveColor(),
3130 PorterDuff.Mode.SRC_ATOP, -1);
3131 }
3132
3133 private void bindHeaderChronometerAndTime(RemoteViews contentView) {
3134 if (showsTimeOrChronometer()) {
Selim Cinek29603462015-11-17 19:04:39 -08003135 contentView.setViewVisibility(R.id.time_divider, View.VISIBLE);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003136 if (mN.extras.getBoolean(EXTRA_SHOW_CHRONOMETER)) {
3137 contentView.setViewVisibility(R.id.chronometer, View.VISIBLE);
3138 contentView.setLong(R.id.chronometer, "setBase",
3139 mN.when + (SystemClock.elapsedRealtime() - System.currentTimeMillis()));
3140 contentView.setBoolean(R.id.chronometer, "setStarted", true);
3141 } else {
3142 contentView.setViewVisibility(R.id.time, View.VISIBLE);
3143 contentView.setLong(R.id.time, "setTime", mN.when);
3144 }
3145 }
3146 }
3147
Selim Cinek03d0d652015-11-13 13:18:09 -05003148 private void bindHeaderSubText(RemoteViews contentView) {
3149 CharSequence subText = mN.extras.getCharSequence(EXTRA_SUB_TEXT);
3150 if (subText == null && mStyle != null && mStyle.mSummaryTextSet
3151 && mStyle.hasSummaryInHeader()) {
3152 subText = mStyle.mSummaryText;
3153 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003154 if (subText != null) {
3155 // TODO: Remove the span entirely to only have the string with propper formating.
3156 contentView.setTextViewText(R.id.header_sub_text, processLegacyText(subText));
3157 contentView.setViewVisibility(R.id.header_sub_text, View.VISIBLE);
Selim Cinek29603462015-11-17 19:04:39 -08003158 contentView.setViewVisibility(R.id.sub_text_divider, View.VISIBLE);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003159 }
3160 }
3161
3162 private void bindHeaderAppName(RemoteViews contentView) {
3163 PackageManager packageManager = mContext.getPackageManager();
3164 ApplicationInfo info = null;
3165 try {
3166 info = packageManager.getApplicationInfo(mContext.getApplicationInfo().packageName,
3167 0);
3168 } catch (final NameNotFoundException e) {
3169 return;
3170 }
3171 CharSequence appName = info != null ? packageManager.getApplicationLabel(info)
3172 : null;
3173 if (TextUtils.isEmpty(appName)) {
3174 return;
3175 }
3176 contentView.setTextViewText(R.id.app_name_text, appName);
3177 }
3178
3179 private void bindSmallIcon(RemoteViews contentView) {
3180 contentView.setImageViewIcon(R.id.icon, mN.mSmallIcon);
3181 processSmallIconColor(mN.mSmallIcon, contentView);
3182 }
3183
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003184 /**
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02003185 * @return true if the built notification will show the time or the chronometer; false
3186 * otherwise
3187 */
3188 private boolean showsTimeOrChronometer() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003189 return mN.when != 0 && mN.extras.getBoolean(EXTRA_SHOW_WHEN);
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02003190 }
3191
Christoph Studerfe718432014-09-01 18:21:18 +02003192 private void resetStandardTemplateWithActions(RemoteViews big) {
3193 big.setViewVisibility(R.id.actions, View.GONE);
3194 big.setViewVisibility(R.id.action_divider, View.GONE);
3195 big.removeAllViews(R.id.actions);
3196 }
3197
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003198 private RemoteViews applyStandardTemplateWithActions(int layoutId) {
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003199 RemoteViews big = applyStandardTemplate(layoutId);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003200
Christoph Studerfe718432014-09-01 18:21:18 +02003201 resetStandardTemplateWithActions(big);
3202
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003203 int N = mActions.size();
3204 if (N > 0) {
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003205 big.setViewVisibility(R.id.actions, View.VISIBLE);
Daniel Sandler6387d2f2012-05-22 13:44:09 -04003206 big.setViewVisibility(R.id.action_divider, View.VISIBLE);
Daniel Sandler8680bf82012-05-15 16:52:52 -04003207 if (N>MAX_ACTION_BUTTONS) N=MAX_ACTION_BUTTONS;
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003208 for (int i=0; i<N; i++) {
3209 final RemoteViews button = generateActionButton(mActions.get(i));
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003210 big.addView(R.id.actions, button);
3211 }
3212 }
3213 return big;
3214 }
3215
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003216 /**
3217 * Construct a RemoteViews for the final 1U notification layout. In order:
3218 * 1. Custom contentView from the caller
3219 * 2. Style's proposed content view
3220 * 3. Standard template view
3221 */
3222 public RemoteViews makeContentView() {
3223 if (mN.contentView != null) {
3224 return mN.contentView;
3225 } else if (mStyle != null) {
3226 final RemoteViews styleView = mStyle.makeContentView();
3227 if (styleView != null) {
3228 return styleView;
3229 }
Joe Onorato46439ce2010-11-19 13:56:21 -08003230 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003231 return applyStandardTemplate(getBaseLayoutResource());
Joe Onorato46439ce2010-11-19 13:56:21 -08003232 }
3233
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003234 /**
3235 * Construct a RemoteViews for the final big notification layout.
3236 */
3237 public RemoteViews makeBigContentView() {
Selim Cinek850a8542015-11-11 11:48:36 -05003238 RemoteViews result = null;
Julia Reynolds089e3e42015-11-18 09:59:57 -05003239 if (mN.bigContentView != null) {
3240 return mN.bigContentView;
3241 } else if (mStyle != null) {
Selim Cinek850a8542015-11-11 11:48:36 -05003242 result = mStyle.makeBigContentView();
Julia Reynolds089e3e42015-11-18 09:59:57 -05003243 } else if (mActions.size() == 0) {
3244 return null;
3245 }
Selim Cinek850a8542015-11-11 11:48:36 -05003246 if (result == null) {
3247 result = applyStandardTemplateWithActions(getBigBaseLayoutResource());
3248 }
3249 adaptNotificationHeaderForBigContentView(result);
Selim Cinek29603462015-11-17 19:04:39 -08003250 hideLine1Text(result);
Selim Cinek850a8542015-11-11 11:48:36 -05003251 return result;
3252 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003253
Selim Cinek29603462015-11-17 19:04:39 -08003254 private void hideLine1Text(RemoteViews result) {
3255 result.setViewVisibility(R.id.text_line_1, View.GONE);
3256 }
3257
Selim Cinek850a8542015-11-11 11:48:36 -05003258 private void adaptNotificationHeaderForBigContentView(RemoteViews result) {
3259 // We have to set the collapse button instead
3260 result.setImageViewResource(R.id.expand_button, R.drawable.ic_arrow_up_14dp);
3261 // Apply the color again
3262 result.setDrawableParameters(R.id.expand_button, false, -1, resolveColor(),
3263 PorterDuff.Mode.SRC_ATOP, -1);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003264 }
3265
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003266 /**
3267 * Construct a RemoteViews for the final heads-up notification layout.
3268 */
3269 public RemoteViews makeHeadsUpContentView() {
Julia Reynolds089e3e42015-11-18 09:59:57 -05003270 if (mN.headsUpContentView != null) {
3271 return mN.headsUpContentView;
3272 } else if (mStyle != null) {
3273 final RemoteViews styleView = mStyle.makeHeadsUpContentView();
3274 if (styleView != null) {
3275 return styleView;
3276 }
3277 } else if (mActions.size() == 0) {
3278 return null;
3279 }
3280
Chris Wren8fd39ec2014-02-27 17:43:26 -05003281
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003282 return applyStandardTemplateWithActions(getBigBaseLayoutResource());
Chris Wren8fd39ec2014-02-27 17:43:26 -05003283 }
3284
3285
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003286 private RemoteViews generateActionButton(Action action) {
Daniel Sandler8680bf82012-05-15 16:52:52 -04003287 final boolean tombstone = (action.actionIntent == null);
Selim Cinekf33b1112015-07-15 17:45:11 -07003288 RemoteViews button = new BuilderRemoteViews(mContext.getApplicationInfo(),
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003289 tombstone ? getActionTombstoneLayoutResource()
3290 : getActionLayoutResource());
Dan Sandler68079d52015-07-22 10:45:30 -04003291 final Icon ai = action.getIcon();
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003292 button.setTextViewText(R.id.action0, processLegacyText(action.title));
Daniel Sandler8680bf82012-05-15 16:52:52 -04003293 if (!tombstone) {
Daniel Sandlere5518842012-05-10 16:20:40 -04003294 button.setOnClickPendingIntent(R.id.action0, action.actionIntent);
Daniel Sandlere5518842012-05-10 16:20:40 -04003295 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003296 button.setContentDescription(R.id.action0, action.title);
Adrian Roosfe84e1f2015-11-04 15:55:39 -08003297 if (action.mRemoteInputs != null) {
3298 button.setRemoteInputs(R.id.action0, action.mRemoteInputs);
3299 }
3300 if (mN.color != COLOR_DEFAULT) {
3301 button.setTextColor(R.id.action0, mN.color);
3302 }
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003303 processLegacyAction(action, button);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003304 return button;
3305 }
3306
Joe Onoratocb109a02011-01-18 17:57:41 -08003307 /**
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003308 * @return Whether we are currently building a notification from a legacy (an app that
Alan Viverette3cb07a462014-06-06 14:19:53 -07003309 * doesn't create material notifications by itself) app.
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003310 */
3311 private boolean isLegacy() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003312 return getColorUtil() != null;
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003313 }
3314
3315 private void processLegacyAction(Action action, RemoteViews button) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003316 if (!isLegacy() || getColorUtil().isGrayscaleIcon(mContext, action.getIcon())) {
Christoph Studer239f8352014-08-25 15:13:18 +02003317 button.setTextViewCompoundDrawablesRelativeColorFilter(R.id.action0, 0,
Alan Viverette4a357cd2015-03-18 18:37:18 -07003318 mContext.getColor(R.color.notification_action_color_filter),
Christoph Studer239f8352014-08-25 15:13:18 +02003319 PorterDuff.Mode.MULTIPLY);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003320 }
3321 }
3322
3323 private CharSequence processLegacyText(CharSequence charSequence) {
3324 if (isLegacy()) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003325 return getColorUtil().invertCharSequenceColors(charSequence);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003326 } else {
3327 return charSequence;
3328 }
3329 }
3330
Dan Sandler26e81cf2014-05-06 10:01:27 -04003331 /**
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003332 * Apply any necessariy colors to the small icon
Dan Sandler26e81cf2014-05-06 10:01:27 -04003333 */
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003334 private void processSmallIconColor(Icon smallIcon, RemoteViews contentView) {
3335 if (!isLegacy() || getColorUtil().isGrayscaleIcon(mContext, smallIcon)) {
3336 contentView.setDrawableParameters(R.id.icon, false, -1, resolveColor(),
Jorim Jaggi92df1f22014-12-16 19:44:41 +01003337 PorterDuff.Mode.SRC_ATOP, -1);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003338 }
3339 }
3340
Dan Sandler26e81cf2014-05-06 10:01:27 -04003341 /**
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003342 * Make the largeIcon dark if it's a fake smallIcon (that is,
Dan Sandler26e81cf2014-05-06 10:01:27 -04003343 * if it's grayscale).
3344 */
3345 // TODO: also check bounds, transparency, that sort of thing.
Dan Sandlerd63f9322015-05-06 15:18:49 -04003346 private void processLargeLegacyIcon(Icon largeIcon, RemoteViews contentView) {
3347 if (largeIcon != null && isLegacy()
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003348 && getColorUtil().isGrayscaleIcon(mContext, largeIcon)) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003349 // resolve color will fall back to the default when legacy
3350 contentView.setDrawableParameters(R.id.icon, false, -1, resolveColor(),
Dan Sandler190d58d2014-05-15 09:33:39 -04003351 PorterDuff.Mode.SRC_ATOP, -1);
Jorim Jaggi92df1f22014-12-16 19:44:41 +01003352 }
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003353 }
3354
Julia Reynolds10ee1fc2015-11-09 11:04:55 -05003355 private void sanitizeColor() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003356 if (mN.color != COLOR_DEFAULT) {
3357 mN.color |= 0xFF000000; // no alpha for custom colors
Jorim Jaggi74419312014-06-10 20:57:21 +02003358 }
Jorim Jaggi74419312014-06-10 20:57:21 +02003359 }
3360
Selim Cinek5bf069a2015-11-10 19:14:27 -05003361 int resolveColor() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003362 if (mN.color == COLOR_DEFAULT) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003363 return mContext.getColor(R.color.notification_icon_default_color);
Dan Sandler26e81cf2014-05-06 10:01:27 -04003364 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003365 return mN.color;
Dan Sandler26e81cf2014-05-06 10:01:27 -04003366 }
3367
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003368 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003369 * Apply the unstyled operations and return a new {@link Notification} object.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04003370 * @hide
Joe Onoratocb109a02011-01-18 17:57:41 -08003371 */
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04003372 public Notification buildUnstyled() {
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003373 if (mActions.size() > 0) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003374 mN.actions = new Action[mActions.size()];
3375 mActions.toArray(mN.actions);
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003376 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003377 if (!mPersonList.isEmpty()) {
3378 mN.extras.putStringArray(EXTRA_PEOPLE,
3379 mPersonList.toArray(new String[mPersonList.size()]));
Dan Sandler0bf2ed82013-12-21 23:33:41 -06003380 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003381 return mN;
Joe Onorato46439ce2010-11-19 13:56:21 -08003382 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003383
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003384 public static Notification.Builder recoverBuilder(Context context, Notification n) {
Christoph Studer4600f9b2014-07-22 22:44:43 +02003385 // Re-create notification context so we can access app resources.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003386 ApplicationInfo applicationInfo = n.extras.getParcelable(
3387 EXTRA_BUILDER_APPLICATION_INFO);
Christoph Studer4600f9b2014-07-22 22:44:43 +02003388 Context builderContext;
Julia Reynoldsda303542015-11-23 14:00:20 -05003389 if (applicationInfo != null) {
3390 try {
3391 builderContext = context.createApplicationContext(applicationInfo,
3392 Context.CONTEXT_RESTRICTED);
3393 } catch (NameNotFoundException e) {
3394 Log.e(TAG, "ApplicationInfo " + applicationInfo + " not found");
3395 builderContext = context; // try with our context
3396 }
3397 } else {
3398 builderContext = context; // try with given context
Christoph Studer4600f9b2014-07-22 22:44:43 +02003399 }
3400
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003401 return new Builder(builderContext, n);
Christoph Studer4600f9b2014-07-22 22:44:43 +02003402 }
3403
3404 private static Class<? extends Style> getNotificationStyleClass(String templateClass) {
3405 Class<? extends Style>[] classes = new Class[]{
3406 BigTextStyle.class, BigPictureStyle.class, InboxStyle.class, MediaStyle.class};
3407 for (Class<? extends Style> innerClass : classes) {
3408 if (templateClass.equals(innerClass.getName())) {
3409 return innerClass;
3410 }
3411 }
3412 return null;
3413 }
3414
3415 private void setBuilderContentView(Notification n, RemoteViews contentView) {
3416 n.contentView = contentView;
Christoph Studer4600f9b2014-07-22 22:44:43 +02003417 }
3418
3419 private void setBuilderBigContentView(Notification n, RemoteViews bigContentView) {
3420 n.bigContentView = bigContentView;
Christoph Studer4600f9b2014-07-22 22:44:43 +02003421 }
3422
3423 private void setBuilderHeadsUpContentView(Notification n,
3424 RemoteViews headsUpContentView) {
3425 n.headsUpContentView = headsUpContentView;
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003426 }
3427
3428 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003429 * @deprecated Use {@link #build()} instead.
3430 */
3431 @Deprecated
3432 public Notification getNotification() {
3433 return build();
3434 }
3435
3436 /**
3437 * Combine all of the options that have been set and return a new {@link Notification}
3438 * object.
3439 */
3440 public Notification build() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003441 // first, add any extras from the calling code
3442 if (mUserExtras != null) {
3443 mN.extras = getAllExtras();
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07003444 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003445
3446 // lazy stuff from mContext; see comment in Builder(Context, Notification)
Julia Reynoldsda303542015-11-23 14:00:20 -05003447 Notification.addFieldsFromContext(mContext, mN);
Christoph Studer943aa672014-08-03 20:31:16 +02003448
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003449 buildUnstyled();
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003450
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003451 if (mStyle != null) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003452 mStyle.buildStyled(mN);
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003453 }
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003454
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003455 return mN;
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003456 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05003457
3458 /**
3459 * Apply this Builder to an existing {@link Notification} object.
3460 *
3461 * @hide
3462 */
3463 public Notification buildInto(Notification n) {
Daniel Sandler1a497d32013-04-18 14:52:45 -04003464 build().cloneInto(n, true);
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05003465 return n;
3466 }
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003467
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003468 private int getBaseLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07003469 return R.layout.notification_template_material_base;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003470 }
3471
3472 private int getBigBaseLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07003473 return R.layout.notification_template_material_big_base;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003474 }
3475
3476 private int getBigPictureLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07003477 return R.layout.notification_template_material_big_picture;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003478 }
3479
3480 private int getBigTextLayoutResource() {
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003481 return R.layout.notification_template_material_big_text;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003482 }
3483
3484 private int getInboxLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07003485 return R.layout.notification_template_material_inbox;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003486 }
3487
3488 private int getActionLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07003489 return R.layout.notification_material_action;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003490 }
3491
3492 private int getActionTombstoneLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07003493 return R.layout.notification_material_action_tombstone;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003494 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003495 }
3496
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003497 /**
3498 * An object that can apply a rich notification style to a {@link Notification.Builder}
3499 * object.
3500 */
Griff Hazendfcb0802014-02-11 12:00:00 -08003501 public static abstract class Style {
Chris Wrend6297db2012-05-03 16:20:13 -04003502 private CharSequence mBigContentTitle;
Jorim Jaggi457a10d2014-09-08 16:18:23 +02003503
3504 /**
3505 * @hide
3506 */
3507 protected CharSequence mSummaryText = null;
3508
3509 /**
3510 * @hide
3511 */
3512 protected boolean mSummaryTextSet = false;
Chris Wrend6297db2012-05-03 16:20:13 -04003513
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003514 protected Builder mBuilder;
3515
Chris Wrend6297db2012-05-03 16:20:13 -04003516 /**
3517 * Overrides ContentTitle in the big form of the template.
3518 * This defaults to the value passed to setContentTitle().
3519 */
3520 protected void internalSetBigContentTitle(CharSequence title) {
3521 mBigContentTitle = title;
3522 }
3523
3524 /**
3525 * Set the first line of text after the detail section in the big form of the template.
3526 */
3527 protected void internalSetSummaryText(CharSequence cs) {
3528 mSummaryText = cs;
Daniel Sandler619738c2012-06-07 16:33:08 -04003529 mSummaryTextSet = true;
Chris Wrend6297db2012-05-03 16:20:13 -04003530 }
3531
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003532 public void setBuilder(Builder builder) {
3533 if (mBuilder != builder) {
3534 mBuilder = builder;
Daniel Sandlerc08dea22012-06-28 08:35:24 -07003535 if (mBuilder != null) {
3536 mBuilder.setStyle(this);
3537 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003538 }
3539 }
3540
Chris Wrend6297db2012-05-03 16:20:13 -04003541 protected void checkBuilder() {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003542 if (mBuilder == null) {
3543 throw new IllegalArgumentException("Style requires a valid Builder object");
3544 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003545 }
Chris Wrend6297db2012-05-03 16:20:13 -04003546
3547 protected RemoteViews getStandardView(int layoutId) {
3548 checkBuilder();
3549
Christoph Studer4600f9b2014-07-22 22:44:43 +02003550 // Nasty.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003551 CharSequence oldBuilderContentTitle =
3552 mBuilder.getAllExtras().getCharSequence(EXTRA_TITLE);
Chris Wrend6297db2012-05-03 16:20:13 -04003553 if (mBigContentTitle != null) {
3554 mBuilder.setContentTitle(mBigContentTitle);
3555 }
3556
Chris Wrend6297db2012-05-03 16:20:13 -04003557 RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(layoutId);
3558
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003559 mBuilder.getAllExtras().putCharSequence(EXTRA_TITLE, oldBuilderContentTitle);
Christoph Studer4600f9b2014-07-22 22:44:43 +02003560
Chris Wrend6297db2012-05-03 16:20:13 -04003561 if (mBigContentTitle != null && mBigContentTitle.equals("")) {
3562 contentView.setViewVisibility(R.id.line1, View.GONE);
Chris Wren67dc9a02012-05-16 01:03:20 -04003563 } else {
3564 contentView.setViewVisibility(R.id.line1, View.VISIBLE);
Chris Wrend6297db2012-05-03 16:20:13 -04003565 }
3566
Selim Cinek9d9fc6e2015-11-12 15:49:14 -05003567 // Clear text in case we use the line to show the profile badge.
3568 contentView.setTextViewText(com.android.internal.R.id.text, "");
3569 contentView.setViewVisibility(com.android.internal.R.id.line3, View.GONE);
3570
Chris Wrend6297db2012-05-03 16:20:13 -04003571 return contentView;
3572 }
3573
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003574 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003575 * Construct a Style-specific RemoteViews for the final 1U notification layout.
3576 * The default implementation has nothing additional to add.
3577 * @hide
3578 */
3579 public RemoteViews makeContentView() {
3580 return null;
3581 }
3582
3583 /**
3584 * Construct a Style-specific RemoteViews for the final big notification layout.
3585 * @hide
3586 */
3587 public RemoteViews makeBigContentView() {
3588 return null;
3589 }
3590
3591 /**
3592 * Construct a Style-specific RemoteViews for the final HUN layout.
3593 * @hide
3594 */
3595 public RemoteViews makeHeadsUpContentView() {
3596 return null;
3597 }
3598
3599 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003600 * Apply any style-specific extras to this notification before shipping it out.
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003601 * @hide
3602 */
3603 public void addExtras(Bundle extras) {
3604 if (mSummaryTextSet) {
3605 extras.putCharSequence(EXTRA_SUMMARY_TEXT, mSummaryText);
3606 }
3607 if (mBigContentTitle != null) {
3608 extras.putCharSequence(EXTRA_TITLE_BIG, mBigContentTitle);
3609 }
Chris Wren91ad5632013-06-05 15:05:57 -04003610 extras.putString(EXTRA_TEMPLATE, this.getClass().getName());
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003611 }
3612
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04003613 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003614 * Reconstruct the internal state of this Style object from extras.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04003615 * @hide
3616 */
Christoph Studer4600f9b2014-07-22 22:44:43 +02003617 protected void restoreFromExtras(Bundle extras) {
3618 if (extras.containsKey(EXTRA_SUMMARY_TEXT)) {
3619 mSummaryText = extras.getCharSequence(EXTRA_SUMMARY_TEXT);
3620 mSummaryTextSet = true;
3621 }
3622 if (extras.containsKey(EXTRA_TITLE_BIG)) {
3623 mBigContentTitle = extras.getCharSequence(EXTRA_TITLE_BIG);
3624 }
3625 }
3626
3627
3628 /**
3629 * @hide
3630 */
3631 public Notification buildStyled(Notification wip) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003632 addExtras(wip.extras);
Christoph Studer4600f9b2014-07-22 22:44:43 +02003633 return wip;
3634 }
3635
Daniel Sandler0ec46202015-06-24 01:27:05 -04003636 /**
3637 * @hide
3638 */
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07003639 public void purgeResources() {}
3640
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04003641 /**
3642 * Calls {@link android.app.Notification.Builder#build()} on the Builder this Style is
3643 * attached to.
3644 *
3645 * @return the fully constructed Notification.
3646 */
3647 public Notification build() {
3648 checkBuilder();
3649 return mBuilder.build();
3650 }
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02003651
3652 /**
3653 * @hide
3654 * @return true if the style positions the progress bar on the second line; false if the
3655 * style hides the progress bar
3656 */
3657 protected boolean hasProgress() {
3658 return true;
3659 }
Selim Cinek03d0d652015-11-13 13:18:09 -05003660
3661 /**
3662 * @hide
3663 * @return Whether we should put the summary be put into the notification header
3664 */
3665 public boolean hasSummaryInHeader() {
3666 return true;
3667 }
Joe Onorato46439ce2010-11-19 13:56:21 -08003668 }
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003669
3670 /**
Daniel Sandler4dfbe832012-04-11 14:51:46 -04003671 * Helper class for generating large-format notifications that include a large image attachment.
Joe Malin8d40d042012-11-05 11:36:40 -08003672 *
Robert Ly91c5ce32014-06-08 15:37:00 -07003673 * Here's how you'd set the <code>BigPictureStyle</code> on a notification:
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003674 * <pre class="prettyprint">
Robert Ly91c5ce32014-06-08 15:37:00 -07003675 * Notification notif = new Notification.Builder(mContext)
3676 * .setContentTitle(&quot;New photo from &quot; + sender.toString())
3677 * .setContentText(subject)
3678 * .setSmallIcon(R.drawable.new_post)
3679 * .setLargeIcon(aBitmap)
3680 * .setStyle(new Notification.BigPictureStyle()
3681 * .bigPicture(aBigBitmap))
3682 * .build();
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003683 * </pre>
Joe Malin8d40d042012-11-05 11:36:40 -08003684 *
Daniel Sandler4dfbe832012-04-11 14:51:46 -04003685 * @see Notification#bigContentView
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003686 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003687 public static class BigPictureStyle extends Style {
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003688 private Bitmap mPicture;
Dan Sandlerd63f9322015-05-06 15:18:49 -04003689 private Icon mBigLargeIcon;
Chris Wren3745a3d2012-05-22 15:11:52 -04003690 private boolean mBigLargeIconSet = false;
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003691
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003692 public BigPictureStyle() {
3693 }
3694
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003695 public BigPictureStyle(Builder builder) {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003696 setBuilder(builder);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003697 }
3698
Chris Wrend6297db2012-05-03 16:20:13 -04003699 /**
3700 * Overrides ContentTitle in the big form of the template.
3701 * This defaults to the value passed to setContentTitle().
3702 */
3703 public BigPictureStyle setBigContentTitle(CharSequence title) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04003704 internalSetBigContentTitle(safeCharSequence(title));
Chris Wrend6297db2012-05-03 16:20:13 -04003705 return this;
3706 }
3707
3708 /**
3709 * Set the first line of text after the detail section in the big form of the template.
3710 */
3711 public BigPictureStyle setSummaryText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04003712 internalSetSummaryText(safeCharSequence(cs));
Chris Wrend6297db2012-05-03 16:20:13 -04003713 return this;
3714 }
3715
Chris Wren0bd664d2012-08-01 13:56:56 -04003716 /**
3717 * Provide the bitmap to be used as the payload for the BigPicture notification.
3718 */
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003719 public BigPictureStyle bigPicture(Bitmap b) {
3720 mPicture = b;
3721 return this;
3722 }
3723
Chris Wren3745a3d2012-05-22 15:11:52 -04003724 /**
Chris Wren3745a3d2012-05-22 15:11:52 -04003725 * Override the large icon when the big notification is shown.
3726 */
3727 public BigPictureStyle bigLargeIcon(Bitmap b) {
Dan Sandlerd63f9322015-05-06 15:18:49 -04003728 return bigLargeIcon(b != null ? Icon.createWithBitmap(b) : null);
3729 }
3730
3731 /**
3732 * Override the large icon when the big notification is shown.
3733 */
3734 public BigPictureStyle bigLargeIcon(Icon icon) {
Chris Wren3745a3d2012-05-22 15:11:52 -04003735 mBigLargeIconSet = true;
Dan Sandlerd63f9322015-05-06 15:18:49 -04003736 mBigLargeIcon = icon;
Chris Wren3745a3d2012-05-22 15:11:52 -04003737 return this;
3738 }
3739
Riley Andrews0394a0c2015-11-03 23:36:52 -08003740 /** @hide */
3741 public static final int MIN_ASHMEM_BITMAP_SIZE = 128 * (1 << 10);
3742
Daniel Sandler0ec46202015-06-24 01:27:05 -04003743 /**
3744 * @hide
3745 */
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07003746 @Override
3747 public void purgeResources() {
3748 super.purgeResources();
Riley Andrews8cee7c12015-11-01 23:36:04 -08003749 if (mPicture != null &&
3750 mPicture.isMutable() &&
Riley Andrews0394a0c2015-11-03 23:36:52 -08003751 mPicture.getAllocationByteCount() >= MIN_ASHMEM_BITMAP_SIZE) {
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07003752 mPicture = mPicture.createAshmemBitmap();
3753 }
3754 if (mBigLargeIcon != null) {
3755 mBigLargeIcon.convertToAshmem();
3756 }
3757 }
Christoph Studer5c510ee2014-12-15 16:32:27 +01003758
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003759 /**
3760 * @hide
3761 */
3762 public RemoteViews makeBigContentView() {
3763 // Replace mN.mLargeIcon with mBigLargeIcon if mBigLargeIconSet
Christoph Studer5c510ee2014-12-15 16:32:27 +01003764 // This covers the following cases:
3765 // 1. mBigLargeIconSet -> mBigLargeIcon (null or non-null) applies, overrides
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003766 // mN.mLargeIcon
3767 // 2. !mBigLargeIconSet -> mN.mLargeIcon applies
Dan Sandlerd63f9322015-05-06 15:18:49 -04003768 Icon oldLargeIcon = null;
Christoph Studer5c510ee2014-12-15 16:32:27 +01003769 if (mBigLargeIconSet) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003770 oldLargeIcon = mBuilder.mN.mLargeIcon;
3771 mBuilder.mN.mLargeIcon = mBigLargeIcon;
Christoph Studer5c510ee2014-12-15 16:32:27 +01003772 }
3773
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003774 RemoteViews contentView = getStandardView(mBuilder.getBigPictureLayoutResource());
Selim Cinek03d0d652015-11-13 13:18:09 -05003775 if (mSummaryTextSet) {
3776 contentView.setTextViewText(R.id.text, mBuilder.processLegacyText(mSummaryText));
3777 contentView.setViewVisibility(R.id.line3, View.VISIBLE);
3778 }
Selim Cinek53e64a42015-11-16 10:40:56 -08003779 int imageMinHeight = mBuilder.mContext.getResources().getDimensionPixelSize(
3780 R.dimen.notification_big_picture_content_min_height_with_picture);
3781 // We need to make space for the right image, so we're enforcing a minheight if there
3782 // is a picture.
3783 int minHeight = (mBuilder.mN.mLargeIcon == null) ? 0 : imageMinHeight;
3784 contentView.setInt(R.id.notification_main_column, "setMinimumHeight", minHeight);
3785
Christoph Studer5c510ee2014-12-15 16:32:27 +01003786 if (mBigLargeIconSet) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003787 mBuilder.mN.mLargeIcon = oldLargeIcon;
Christoph Studer5c510ee2014-12-15 16:32:27 +01003788 }
3789
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003790 contentView.setImageViewBitmap(R.id.big_picture, mPicture);
3791
Selim Cinek29603462015-11-17 19:04:39 -08003792 mBuilder.addProfileBadge(contentView, R.id.profile_badge_line3);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003793 return contentView;
3794 }
3795
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003796 /**
3797 * @hide
3798 */
3799 public void addExtras(Bundle extras) {
3800 super.addExtras(extras);
3801
3802 if (mBigLargeIconSet) {
3803 extras.putParcelable(EXTRA_LARGE_ICON_BIG, mBigLargeIcon);
3804 }
3805 extras.putParcelable(EXTRA_PICTURE, mPicture);
3806 }
3807
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04003808 /**
3809 * @hide
3810 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003811 @Override
Christoph Studer4600f9b2014-07-22 22:44:43 +02003812 protected void restoreFromExtras(Bundle extras) {
3813 super.restoreFromExtras(extras);
3814
3815 if (extras.containsKey(EXTRA_LARGE_ICON_BIG)) {
Christoph Studer5c510ee2014-12-15 16:32:27 +01003816 mBigLargeIconSet = true;
Christoph Studer4600f9b2014-07-22 22:44:43 +02003817 mBigLargeIcon = extras.getParcelable(EXTRA_LARGE_ICON_BIG);
Chris Wren3745a3d2012-05-22 15:11:52 -04003818 }
Christoph Studer4600f9b2014-07-22 22:44:43 +02003819 mPicture = extras.getParcelable(EXTRA_PICTURE);
3820 }
Selim Cinek03d0d652015-11-13 13:18:09 -05003821
3822 /**
3823 * @hide
3824 */
3825 @Override
3826 public boolean hasSummaryInHeader() {
3827 return false;
3828 }
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003829 }
3830
3831 /**
Daniel Sandler4dfbe832012-04-11 14:51:46 -04003832 * Helper class for generating large-format notifications that include a lot of text.
Joe Malin8d40d042012-11-05 11:36:40 -08003833 *
Robert Ly91c5ce32014-06-08 15:37:00 -07003834 * Here's how you'd set the <code>BigTextStyle</code> on a notification:
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003835 * <pre class="prettyprint">
Robert Ly91c5ce32014-06-08 15:37:00 -07003836 * Notification notif = new Notification.Builder(mContext)
3837 * .setContentTitle(&quot;New mail from &quot; + sender.toString())
3838 * .setContentText(subject)
3839 * .setSmallIcon(R.drawable.new_mail)
3840 * .setLargeIcon(aBitmap)
3841 * .setStyle(new Notification.BigTextStyle()
3842 * .bigText(aVeryLongString))
3843 * .build();
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003844 * </pre>
Joe Malin8d40d042012-11-05 11:36:40 -08003845 *
Daniel Sandler4dfbe832012-04-11 14:51:46 -04003846 * @see Notification#bigContentView
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003847 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003848 public static class BigTextStyle extends Style {
Jorim Jaggi457a10d2014-09-08 16:18:23 +02003849
3850 private static final int MAX_LINES = 13;
3851 private static final int LINES_CONSUMED_BY_ACTIONS = 3;
3852 private static final int LINES_CONSUMED_BY_SUMMARY = 2;
3853
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003854 private CharSequence mBigText;
3855
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003856 public BigTextStyle() {
3857 }
3858
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003859 public BigTextStyle(Builder builder) {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003860 setBuilder(builder);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003861 }
3862
Chris Wrend6297db2012-05-03 16:20:13 -04003863 /**
3864 * Overrides ContentTitle in the big form of the template.
3865 * This defaults to the value passed to setContentTitle().
3866 */
3867 public BigTextStyle setBigContentTitle(CharSequence title) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04003868 internalSetBigContentTitle(safeCharSequence(title));
Chris Wrend6297db2012-05-03 16:20:13 -04003869 return this;
3870 }
3871
3872 /**
3873 * Set the first line of text after the detail section in the big form of the template.
3874 */
3875 public BigTextStyle setSummaryText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04003876 internalSetSummaryText(safeCharSequence(cs));
Chris Wrend6297db2012-05-03 16:20:13 -04003877 return this;
3878 }
3879
Chris Wren0bd664d2012-08-01 13:56:56 -04003880 /**
3881 * Provide the longer text to be displayed in the big form of the
3882 * template in place of the content text.
3883 */
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003884 public BigTextStyle bigText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04003885 mBigText = safeCharSequence(cs);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003886 return this;
3887 }
3888
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003889 /**
3890 * @hide
3891 */
3892 public void addExtras(Bundle extras) {
3893 super.addExtras(extras);
3894
Christoph Studer4600f9b2014-07-22 22:44:43 +02003895 extras.putCharSequence(EXTRA_BIG_TEXT, mBigText);
3896 }
3897
3898 /**
3899 * @hide
3900 */
3901 @Override
3902 protected void restoreFromExtras(Bundle extras) {
3903 super.restoreFromExtras(extras);
3904
3905 mBigText = extras.getCharSequence(EXTRA_BIG_TEXT);
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003906 }
3907
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003908 /**
3909 * @hide
3910 */
3911 public RemoteViews makeBigContentView() {
Christoph Studer4600f9b2014-07-22 22:44:43 +02003912
3913 // Nasty
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003914 CharSequence oldBuilderContentText =
3915 mBuilder.getAllExtras().getCharSequence(EXTRA_TEXT);
3916 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, null);
Daniel Sandler916ad912012-06-13 12:17:07 -04003917
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003918 RemoteViews contentView = getStandardView(mBuilder.getBigTextLayoutResource());
Joe Malin8d40d042012-11-05 11:36:40 -08003919
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003920 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, oldBuilderContentText);
Christoph Studer4600f9b2014-07-22 22:44:43 +02003921
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003922 contentView.setTextViewText(R.id.big_text, mBuilder.processLegacyText(mBigText));
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003923 contentView.setViewVisibility(R.id.big_text, View.VISIBLE);
Jorim Jaggi457a10d2014-09-08 16:18:23 +02003924 contentView.setInt(R.id.big_text, "setMaxLines", calculateMaxLines());
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003925
Kenny Guy98193ea2014-07-24 19:54:37 +01003926 mBuilder.addProfileBadge(contentView, R.id.profile_badge_large_template);
3927
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003928 return contentView;
3929 }
3930
Jorim Jaggi457a10d2014-09-08 16:18:23 +02003931 private int calculateMaxLines() {
3932 int lineCount = MAX_LINES;
3933 boolean hasActions = mBuilder.mActions.size() > 0;
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003934 boolean hasSummary = (mSummaryTextSet ? mSummaryText
3935 : mBuilder.getAllExtras().getCharSequence(EXTRA_SUB_TEXT)) != null;
Jorim Jaggi457a10d2014-09-08 16:18:23 +02003936 if (hasActions) {
3937 lineCount -= LINES_CONSUMED_BY_ACTIONS;
3938 }
3939 if (hasSummary) {
3940 lineCount -= LINES_CONSUMED_BY_SUMMARY;
3941 }
Jorim Jaggi457a10d2014-09-08 16:18:23 +02003942 return lineCount;
3943 }
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003944 }
Daniel Sandler879c5e02012-04-17 16:46:51 -04003945
3946 /**
3947 * Helper class for generating large-format notifications that include a list of (up to 5) strings.
Joe Malin8d40d042012-11-05 11:36:40 -08003948 *
Robert Ly91c5ce32014-06-08 15:37:00 -07003949 * Here's how you'd set the <code>InboxStyle</code> on a notification:
Daniel Sandler879c5e02012-04-17 16:46:51 -04003950 * <pre class="prettyprint">
Robert Ly91c5ce32014-06-08 15:37:00 -07003951 * Notification notif = new Notification.Builder(mContext)
3952 * .setContentTitle(&quot;5 New mails from &quot; + sender.toString())
3953 * .setContentText(subject)
3954 * .setSmallIcon(R.drawable.new_mail)
3955 * .setLargeIcon(aBitmap)
3956 * .setStyle(new Notification.InboxStyle()
3957 * .addLine(str1)
3958 * .addLine(str2)
3959 * .setContentTitle(&quot;&quot;)
3960 * .setSummaryText(&quot;+3 more&quot;))
3961 * .build();
Daniel Sandler879c5e02012-04-17 16:46:51 -04003962 * </pre>
Joe Malin8d40d042012-11-05 11:36:40 -08003963 *
Daniel Sandler879c5e02012-04-17 16:46:51 -04003964 * @see Notification#bigContentView
3965 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003966 public static class InboxStyle extends Style {
Daniel Sandler879c5e02012-04-17 16:46:51 -04003967 private ArrayList<CharSequence> mTexts = new ArrayList<CharSequence>(5);
3968
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003969 public InboxStyle() {
3970 }
3971
Daniel Sandler879c5e02012-04-17 16:46:51 -04003972 public InboxStyle(Builder builder) {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003973 setBuilder(builder);
Daniel Sandler879c5e02012-04-17 16:46:51 -04003974 }
3975
Chris Wrend6297db2012-05-03 16:20:13 -04003976 /**
3977 * Overrides ContentTitle in the big form of the template.
3978 * This defaults to the value passed to setContentTitle().
3979 */
3980 public InboxStyle setBigContentTitle(CharSequence title) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04003981 internalSetBigContentTitle(safeCharSequence(title));
Chris Wrend6297db2012-05-03 16:20:13 -04003982 return this;
3983 }
3984
3985 /**
3986 * Set the first line of text after the detail section in the big form of the template.
3987 */
3988 public InboxStyle setSummaryText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04003989 internalSetSummaryText(safeCharSequence(cs));
Chris Wrend6297db2012-05-03 16:20:13 -04003990 return this;
3991 }
3992
Chris Wren0bd664d2012-08-01 13:56:56 -04003993 /**
3994 * Append a line to the digest section of the Inbox notification.
3995 */
Daniel Sandler879c5e02012-04-17 16:46:51 -04003996 public InboxStyle addLine(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04003997 mTexts.add(safeCharSequence(cs));
Daniel Sandler879c5e02012-04-17 16:46:51 -04003998 return this;
3999 }
4000
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004001 /**
4002 * @hide
4003 */
4004 public void addExtras(Bundle extras) {
4005 super.addExtras(extras);
Christoph Studer4600f9b2014-07-22 22:44:43 +02004006
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004007 CharSequence[] a = new CharSequence[mTexts.size()];
4008 extras.putCharSequenceArray(EXTRA_TEXT_LINES, mTexts.toArray(a));
4009 }
4010
Christoph Studer4600f9b2014-07-22 22:44:43 +02004011 /**
4012 * @hide
4013 */
4014 @Override
4015 protected void restoreFromExtras(Bundle extras) {
4016 super.restoreFromExtras(extras);
4017
4018 mTexts.clear();
4019 if (extras.containsKey(EXTRA_TEXT_LINES)) {
4020 Collections.addAll(mTexts, extras.getCharSequenceArray(EXTRA_TEXT_LINES));
4021 }
4022 }
4023
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004024 /**
4025 * @hide
4026 */
4027 public RemoteViews makeBigContentView() {
Daniel Sandler619738c2012-06-07 16:33:08 -04004028 // Remove the content text so line3 disappears unless you have a summary
Christoph Studer4600f9b2014-07-22 22:44:43 +02004029 // Nasty
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004030 CharSequence oldBuilderContentText = mBuilder.mN.extras.getCharSequence(EXTRA_TEXT);
4031 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, null);
Christoph Studer4600f9b2014-07-22 22:44:43 +02004032
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004033 RemoteViews contentView = getStandardView(mBuilder.getInboxLayoutResource());
Daniel Sandler619738c2012-06-07 16:33:08 -04004034
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004035 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, oldBuilderContentText);
Christoph Studer4600f9b2014-07-22 22:44:43 +02004036
Chris Wrend6297db2012-05-03 16:20:13 -04004037 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 -04004038 R.id.inbox_text4, R.id.inbox_text5, R.id.inbox_text6};
Chris Wrend6297db2012-05-03 16:20:13 -04004039
Chris Wren4ed80d52012-05-17 09:30:03 -04004040 // Make sure all rows are gone in case we reuse a view.
4041 for (int rowId : rowIds) {
4042 contentView.setViewVisibility(rowId, View.GONE);
4043 }
4044
Jorim Jaggi445d3c02014-08-19 22:33:42 +02004045 final boolean largeText =
4046 mBuilder.mContext.getResources().getConfiguration().fontScale > 1f;
4047 final float subTextSize = mBuilder.mContext.getResources().getDimensionPixelSize(
4048 R.dimen.notification_subtext_size);
Daniel Sandler879c5e02012-04-17 16:46:51 -04004049 int i=0;
Selim Cinek9d9fc6e2015-11-12 15:49:14 -05004050 final float density = mBuilder.mContext.getResources().getDisplayMetrics().density;
4051 int topPadding = (int) (5 * density);
4052 int bottomPadding = (int) (13 * density);
Daniel Sandler879c5e02012-04-17 16:46:51 -04004053 while (i < mTexts.size() && i < rowIds.length) {
4054 CharSequence str = mTexts.get(i);
4055 if (str != null && !str.equals("")) {
4056 contentView.setViewVisibility(rowIds[i], View.VISIBLE);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004057 contentView.setTextViewText(rowIds[i], mBuilder.processLegacyText(str));
Jorim Jaggi445d3c02014-08-19 22:33:42 +02004058 if (largeText) {
4059 contentView.setTextViewTextSize(rowIds[i], TypedValue.COMPLEX_UNIT_PX,
4060 subTextSize);
4061 }
Selim Cinek9d9fc6e2015-11-12 15:49:14 -05004062 contentView.setViewPadding(rowIds[i], 0, topPadding, 0,
4063 i == rowIds.length - 1 || i == mTexts.size() - 1 ? bottomPadding : 0);
Daniel Sandler879c5e02012-04-17 16:46:51 -04004064 }
4065 i++;
4066 }
4067
Kenny Guy98193ea2014-07-24 19:54:37 +01004068 mBuilder.addProfileBadge(contentView, R.id.profile_badge_large_template);
4069
Daniel Sandler879c5e02012-04-17 16:46:51 -04004070 return contentView;
4071 }
Daniel Sandler879c5e02012-04-17 16:46:51 -04004072 }
Dan Sandler842dd772014-05-15 09:36:47 -04004073
4074 /**
4075 * Notification style for media playback notifications.
4076 *
4077 * In the expanded form, {@link Notification#bigContentView}, up to 5
4078 * {@link Notification.Action}s specified with
Dan Sandler86647982015-05-13 23:41:13 -04004079 * {@link Notification.Builder#addAction(Action) addAction} will be
Dan Sandler842dd772014-05-15 09:36:47 -04004080 * shown as icon-only pushbuttons, suitable for transport controls. The Bitmap given to
4081 * {@link Notification.Builder#setLargeIcon(android.graphics.Bitmap) setLargeIcon()} will be
4082 * treated as album artwork.
4083 *
4084 * Unlike the other styles provided here, MediaStyle can also modify the standard-size
4085 * {@link Notification#contentView}; by providing action indices to
Christoph Studerfde6f4d2014-12-12 13:23:26 +01004086 * {@link #setShowActionsInCompactView(int...)} you can promote up to 3 actions to be displayed
Dan Sandler842dd772014-05-15 09:36:47 -04004087 * in the standard view alongside the usual content.
4088 *
Bryan Mawhinney6be8de32014-07-18 10:35:12 +01004089 * Notifications created with MediaStyle will have their category set to
4090 * {@link Notification#CATEGORY_TRANSPORT CATEGORY_TRANSPORT} unless you set a different
4091 * category using {@link Notification.Builder#setCategory(String) setCategory()}.
4092 *
Jeff Browndba34ba2014-06-24 20:46:03 -07004093 * Finally, if you attach a {@link android.media.session.MediaSession.Token} using
4094 * {@link android.app.Notification.MediaStyle#setMediaSession(MediaSession.Token)},
Dan Sandler842dd772014-05-15 09:36:47 -04004095 * the System UI can identify this as a notification representing an active media session
4096 * and respond accordingly (by showing album artwork in the lockscreen, for example).
4097 *
4098 * To use this style with your Notification, feed it to
4099 * {@link Notification.Builder#setStyle(android.app.Notification.Style)} like so:
4100 * <pre class="prettyprint">
4101 * Notification noti = new Notification.Builder()
4102 * .setSmallIcon(R.drawable.ic_stat_player)
Christoph Studere935fe92014-11-24 14:18:06 +01004103 * .setContentTitle(&quot;Track title&quot;)
4104 * .setContentText(&quot;Artist - Album&quot;)
4105 * .setLargeIcon(albumArtBitmap))
Bryan Mawhinney6be8de32014-07-18 10:35:12 +01004106 * .setStyle(<b>new Notification.MediaStyle()</b>
4107 * .setMediaSession(mySession))
Dan Sandler842dd772014-05-15 09:36:47 -04004108 * .build();
4109 * </pre>
4110 *
4111 * @see Notification#bigContentView
4112 */
4113 public static class MediaStyle extends Style {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004114 static final int MAX_MEDIA_BUTTONS_IN_COMPACT = 3;
Dan Sandler842dd772014-05-15 09:36:47 -04004115 static final int MAX_MEDIA_BUTTONS = 5;
4116
4117 private int[] mActionsToShowInCompact = null;
Jeff Browndba34ba2014-06-24 20:46:03 -07004118 private MediaSession.Token mToken;
Dan Sandler842dd772014-05-15 09:36:47 -04004119
4120 public MediaStyle() {
4121 }
4122
4123 public MediaStyle(Builder builder) {
4124 setBuilder(builder);
4125 }
4126
4127 /**
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004128 * 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 -04004129 * notification view.
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004130 *
4131 * @param actions the indices of the actions to show in the compact notification view
Dan Sandler842dd772014-05-15 09:36:47 -04004132 */
4133 public MediaStyle setShowActionsInCompactView(int...actions) {
4134 mActionsToShowInCompact = actions;
4135 return this;
4136 }
4137
4138 /**
Jeff Browndba34ba2014-06-24 20:46:03 -07004139 * Attach a {@link android.media.session.MediaSession.Token} to this Notification
4140 * to provide additional playback information and control to the SystemUI.
Dan Sandler842dd772014-05-15 09:36:47 -04004141 */
Jeff Browndba34ba2014-06-24 20:46:03 -07004142 public MediaStyle setMediaSession(MediaSession.Token token) {
Dan Sandler842dd772014-05-15 09:36:47 -04004143 mToken = token;
4144 return this;
4145 }
4146
Christoph Studer4600f9b2014-07-22 22:44:43 +02004147 /**
4148 * @hide
4149 */
Dan Sandler842dd772014-05-15 09:36:47 -04004150 @Override
4151 public Notification buildStyled(Notification wip) {
Christoph Studer4600f9b2014-07-22 22:44:43 +02004152 super.buildStyled(wip);
Bryan Mawhinney6be8de32014-07-18 10:35:12 +01004153 if (wip.category == null) {
4154 wip.category = Notification.CATEGORY_TRANSPORT;
4155 }
Dan Sandler842dd772014-05-15 09:36:47 -04004156 return wip;
4157 }
4158
Christoph Studer4600f9b2014-07-22 22:44:43 +02004159 /**
4160 * @hide
4161 */
4162 @Override
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004163 public RemoteViews makeContentView() {
4164 return makeMediaContentView();
Christoph Studer4600f9b2014-07-22 22:44:43 +02004165 }
4166
4167 /**
4168 * @hide
4169 */
4170 @Override
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004171 public RemoteViews makeBigContentView() {
4172 return makeMediaBigContentView();
Christoph Studer4600f9b2014-07-22 22:44:43 +02004173 }
4174
Dan Sandler842dd772014-05-15 09:36:47 -04004175 /** @hide */
4176 @Override
4177 public void addExtras(Bundle extras) {
4178 super.addExtras(extras);
4179
4180 if (mToken != null) {
4181 extras.putParcelable(EXTRA_MEDIA_SESSION, mToken);
4182 }
Bryan Mawhinneye191f902014-07-22 12:50:09 +01004183 if (mActionsToShowInCompact != null) {
4184 extras.putIntArray(EXTRA_COMPACT_ACTIONS, mActionsToShowInCompact);
4185 }
Dan Sandler842dd772014-05-15 09:36:47 -04004186 }
4187
Christoph Studer4600f9b2014-07-22 22:44:43 +02004188 /**
4189 * @hide
4190 */
4191 @Override
4192 protected void restoreFromExtras(Bundle extras) {
4193 super.restoreFromExtras(extras);
4194
4195 if (extras.containsKey(EXTRA_MEDIA_SESSION)) {
4196 mToken = extras.getParcelable(EXTRA_MEDIA_SESSION);
4197 }
4198 if (extras.containsKey(EXTRA_COMPACT_ACTIONS)) {
4199 mActionsToShowInCompact = extras.getIntArray(EXTRA_COMPACT_ACTIONS);
4200 }
4201 }
4202
Selim Cinek5bf069a2015-11-10 19:14:27 -05004203 private RemoteViews generateMediaActionButton(Action action, int color) {
Dan Sandler842dd772014-05-15 09:36:47 -04004204 final boolean tombstone = (action.actionIntent == null);
Selim Cinekf33b1112015-07-15 17:45:11 -07004205 RemoteViews button = new BuilderRemoteViews(mBuilder.mContext.getApplicationInfo(),
Alan Viverette3cb07a462014-06-06 14:19:53 -07004206 R.layout.notification_material_media_action);
Dan Sandler68079d52015-07-22 10:45:30 -04004207 button.setImageViewIcon(R.id.action0, action.getIcon());
Selim Cinek5bf069a2015-11-10 19:14:27 -05004208 button.setDrawableParameters(R.id.action0, false, -1, color, PorterDuff.Mode.SRC_ATOP,
4209 -1);
Dan Sandler842dd772014-05-15 09:36:47 -04004210 if (!tombstone) {
4211 button.setOnClickPendingIntent(R.id.action0, action.actionIntent);
4212 }
4213 button.setContentDescription(R.id.action0, action.title);
4214 return button;
4215 }
4216
4217 private RemoteViews makeMediaContentView() {
4218 RemoteViews view = mBuilder.applyStandardTemplate(
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004219 R.layout.notification_template_material_media, false /* hasProgress */);
Dan Sandler842dd772014-05-15 09:36:47 -04004220
4221 final int numActions = mBuilder.mActions.size();
4222 final int N = mActionsToShowInCompact == null
4223 ? 0
4224 : Math.min(mActionsToShowInCompact.length, MAX_MEDIA_BUTTONS_IN_COMPACT);
4225 if (N > 0) {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004226 view.removeAllViews(com.android.internal.R.id.media_actions);
Dan Sandler842dd772014-05-15 09:36:47 -04004227 for (int i = 0; i < N; i++) {
4228 if (i >= numActions) {
4229 throw new IllegalArgumentException(String.format(
4230 "setShowActionsInCompactView: action %d out of bounds (max %d)",
4231 i, numActions - 1));
4232 }
4233
4234 final Action action = mBuilder.mActions.get(mActionsToShowInCompact[i]);
Selim Cinek5bf069a2015-11-10 19:14:27 -05004235 final RemoteViews button = generateMediaActionButton(action,
4236 mBuilder.resolveColor());
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004237 view.addView(com.android.internal.R.id.media_actions, button);
Dan Sandler842dd772014-05-15 09:36:47 -04004238 }
4239 }
Selim Cinek5bf069a2015-11-10 19:14:27 -05004240 handleImage(view /* addPaddingToMainColumn */);
Dan Sandler842dd772014-05-15 09:36:47 -04004241 return view;
4242 }
4243
4244 private RemoteViews makeMediaBigContentView() {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004245 final int actionCount = Math.min(mBuilder.mActions.size(), MAX_MEDIA_BUTTONS);
Selim Cinek5bf069a2015-11-10 19:14:27 -05004246 RemoteViews big = mBuilder.applyStandardTemplate(
4247 R.layout.notification_template_material_big_media,
4248 false);
Dan Sandler842dd772014-05-15 09:36:47 -04004249
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004250 if (actionCount > 0) {
4251 big.removeAllViews(com.android.internal.R.id.media_actions);
4252 for (int i = 0; i < actionCount; i++) {
Selim Cinek5bf069a2015-11-10 19:14:27 -05004253 final RemoteViews button = generateMediaActionButton(mBuilder.mActions.get(i),
4254 mBuilder.resolveColor());
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004255 big.addView(com.android.internal.R.id.media_actions, button);
Dan Sandler842dd772014-05-15 09:36:47 -04004256 }
4257 }
Selim Cinek5bf069a2015-11-10 19:14:27 -05004258 handleImage(big);
Dan Sandler842dd772014-05-15 09:36:47 -04004259 return big;
4260 }
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004261
Selim Cinek5bf069a2015-11-10 19:14:27 -05004262 private void handleImage(RemoteViews contentView) {
4263 if (mBuilder.mN.mLargeIcon != null) {
4264 contentView.setViewLayoutMarginEnd(R.id.line1, 0);
Selim Cinek5bf069a2015-11-10 19:14:27 -05004265 contentView.setViewLayoutMarginEnd(R.id.line3, 0);
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004266 }
4267 }
4268
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004269 /**
4270 * @hide
4271 */
4272 @Override
4273 protected boolean hasProgress() {
4274 return false;
4275 }
Dan Sandler842dd772014-05-15 09:36:47 -04004276 }
Griff Hazen61a9e862014-05-22 16:05:19 -07004277
Christoph Studer4600f9b2014-07-22 22:44:43 +02004278 // When adding a new Style subclass here, don't forget to update
4279 // Builder.getNotificationStyleClass.
4280
Griff Hazen61a9e862014-05-22 16:05:19 -07004281 /**
4282 * Extender interface for use with {@link Builder#extend}. Extenders may be used to add
4283 * metadata or change options on a notification builder.
4284 */
4285 public interface Extender {
4286 /**
4287 * Apply this extender to a notification builder.
4288 * @param builder the builder to be modified.
4289 * @return the build object for chaining.
4290 */
4291 public Builder extend(Builder builder);
4292 }
4293
4294 /**
4295 * Helper class to add wearable extensions to notifications.
4296 * <p class="note"> See
4297 * <a href="{@docRoot}wear/notifications/creating.html">Creating Notifications
4298 * for Android Wear</a> for more information on how to use this class.
4299 * <p>
4300 * To create a notification with wearable extensions:
4301 * <ol>
4302 * <li>Create a {@link android.app.Notification.Builder}, setting any desired
4303 * properties.
4304 * <li>Create a {@link android.app.Notification.WearableExtender}.
4305 * <li>Set wearable-specific properties using the
4306 * {@code add} and {@code set} methods of {@link android.app.Notification.WearableExtender}.
4307 * <li>Call {@link android.app.Notification.Builder#extend} to apply the extensions to a
4308 * notification.
4309 * <li>Post the notification to the notification system with the
4310 * {@code NotificationManager.notify(...)} methods.
4311 * </ol>
4312 *
4313 * <pre class="prettyprint">
4314 * Notification notif = new Notification.Builder(mContext)
4315 * .setContentTitle(&quot;New mail from &quot; + sender.toString())
4316 * .setContentText(subject)
4317 * .setSmallIcon(R.drawable.new_mail)
4318 * .extend(new Notification.WearableExtender()
4319 * .setContentIcon(R.drawable.new_mail))
4320 * .build();
4321 * NotificationManager notificationManger =
4322 * (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
4323 * notificationManger.notify(0, notif);</pre>
4324 *
4325 * <p>Wearable extensions can be accessed on an existing notification by using the
4326 * {@code WearableExtender(Notification)} constructor,
4327 * and then using the {@code get} methods to access values.
4328 *
4329 * <pre class="prettyprint">
4330 * Notification.WearableExtender wearableExtender = new Notification.WearableExtender(
4331 * notification);
Griff Hazen14f57992014-05-26 09:07:14 -07004332 * List&lt;Notification&gt; pages = wearableExtender.getPages();</pre>
Griff Hazen61a9e862014-05-22 16:05:19 -07004333 */
4334 public static final class WearableExtender implements Extender {
4335 /**
4336 * Sentinel value for an action index that is unset.
4337 */
4338 public static final int UNSET_ACTION_INDEX = -1;
4339
4340 /**
4341 * Size value for use with {@link #setCustomSizePreset} to show this notification with
4342 * default sizing.
4343 * <p>For custom display notifications created using {@link #setDisplayIntent},
Paul Soulosaa4f4bf2015-08-04 11:59:45 -07004344 * the default is {@link #SIZE_MEDIUM}. All other notifications size automatically based
Griff Hazen61a9e862014-05-22 16:05:19 -07004345 * on their content.
4346 */
4347 public static final int SIZE_DEFAULT = 0;
4348
4349 /**
4350 * Size value for use with {@link #setCustomSizePreset} to show this notification
4351 * with an extra small size.
4352 * <p>This value is only applicable for custom display notifications created using
4353 * {@link #setDisplayIntent}.
4354 */
4355 public static final int SIZE_XSMALL = 1;
4356
4357 /**
4358 * Size value for use with {@link #setCustomSizePreset} to show this notification
4359 * with a small size.
4360 * <p>This value is only applicable for custom display notifications created using
4361 * {@link #setDisplayIntent}.
4362 */
4363 public static final int SIZE_SMALL = 2;
4364
4365 /**
4366 * Size value for use with {@link #setCustomSizePreset} to show this notification
4367 * with a medium size.
4368 * <p>This value is only applicable for custom display notifications created using
4369 * {@link #setDisplayIntent}.
4370 */
4371 public static final int SIZE_MEDIUM = 3;
4372
4373 /**
4374 * Size value for use with {@link #setCustomSizePreset} to show this notification
4375 * with a large size.
4376 * <p>This value is only applicable for custom display notifications created using
4377 * {@link #setDisplayIntent}.
4378 */
4379 public static final int SIZE_LARGE = 4;
4380
Griff Hazend5f11f92014-05-27 15:40:09 -07004381 /**
4382 * Size value for use with {@link #setCustomSizePreset} to show this notification
4383 * full screen.
4384 * <p>This value is only applicable for custom display notifications created using
4385 * {@link #setDisplayIntent}.
4386 */
4387 public static final int SIZE_FULL_SCREEN = 5;
4388
Griff Hazen5f2edfc2014-09-29 16:28:44 -07004389 /**
4390 * Sentinel value for use with {@link #setHintScreenTimeout} to keep the screen on for a
4391 * short amount of time when this notification is displayed on the screen. This
4392 * is the default value.
4393 */
4394 public static final int SCREEN_TIMEOUT_SHORT = 0;
4395
4396 /**
4397 * Sentinel value for use with {@link #setHintScreenTimeout} to keep the screen on
4398 * for a longer amount of time when this notification is displayed on the screen.
4399 */
4400 public static final int SCREEN_TIMEOUT_LONG = -1;
4401
Griff Hazen61a9e862014-05-22 16:05:19 -07004402 /** Notification extra which contains wearable extensions */
4403 private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS";
4404
Pete Gastaf6781d2014-10-07 15:17:05 -04004405 // Keys within EXTRA_WEARABLE_EXTENSIONS for wearable options.
Griff Hazen61a9e862014-05-22 16:05:19 -07004406 private static final String KEY_ACTIONS = "actions";
4407 private static final String KEY_FLAGS = "flags";
4408 private static final String KEY_DISPLAY_INTENT = "displayIntent";
4409 private static final String KEY_PAGES = "pages";
4410 private static final String KEY_BACKGROUND = "background";
4411 private static final String KEY_CONTENT_ICON = "contentIcon";
4412 private static final String KEY_CONTENT_ICON_GRAVITY = "contentIconGravity";
4413 private static final String KEY_CONTENT_ACTION_INDEX = "contentActionIndex";
4414 private static final String KEY_CUSTOM_SIZE_PRESET = "customSizePreset";
4415 private static final String KEY_CUSTOM_CONTENT_HEIGHT = "customContentHeight";
4416 private static final String KEY_GRAVITY = "gravity";
Griff Hazen5f2edfc2014-09-29 16:28:44 -07004417 private static final String KEY_HINT_SCREEN_TIMEOUT = "hintScreenTimeout";
Griff Hazen61a9e862014-05-22 16:05:19 -07004418
4419 // Flags bitwise-ored to mFlags
4420 private static final int FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE = 0x1;
4421 private static final int FLAG_HINT_HIDE_ICON = 1 << 1;
4422 private static final int FLAG_HINT_SHOW_BACKGROUND_ONLY = 1 << 2;
4423 private static final int FLAG_START_SCROLL_BOTTOM = 1 << 3;
Griff Hazen5f2edfc2014-09-29 16:28:44 -07004424 private static final int FLAG_HINT_AVOID_BACKGROUND_CLIPPING = 1 << 4;
Griff Hazen61a9e862014-05-22 16:05:19 -07004425
4426 // Default value for flags integer
4427 private static final int DEFAULT_FLAGS = FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE;
4428
4429 private static final int DEFAULT_CONTENT_ICON_GRAVITY = Gravity.END;
4430 private static final int DEFAULT_GRAVITY = Gravity.BOTTOM;
4431
4432 private ArrayList<Action> mActions = new ArrayList<Action>();
4433 private int mFlags = DEFAULT_FLAGS;
4434 private PendingIntent mDisplayIntent;
4435 private ArrayList<Notification> mPages = new ArrayList<Notification>();
4436 private Bitmap mBackground;
4437 private int mContentIcon;
4438 private int mContentIconGravity = DEFAULT_CONTENT_ICON_GRAVITY;
4439 private int mContentActionIndex = UNSET_ACTION_INDEX;
4440 private int mCustomSizePreset = SIZE_DEFAULT;
4441 private int mCustomContentHeight;
4442 private int mGravity = DEFAULT_GRAVITY;
Griff Hazen5f2edfc2014-09-29 16:28:44 -07004443 private int mHintScreenTimeout;
Griff Hazen61a9e862014-05-22 16:05:19 -07004444
4445 /**
4446 * Create a {@link android.app.Notification.WearableExtender} with default
4447 * options.
4448 */
4449 public WearableExtender() {
4450 }
4451
4452 public WearableExtender(Notification notif) {
4453 Bundle wearableBundle = notif.extras.getBundle(EXTRA_WEARABLE_EXTENSIONS);
4454 if (wearableBundle != null) {
4455 List<Action> actions = wearableBundle.getParcelableArrayList(KEY_ACTIONS);
4456 if (actions != null) {
4457 mActions.addAll(actions);
4458 }
4459
4460 mFlags = wearableBundle.getInt(KEY_FLAGS, DEFAULT_FLAGS);
4461 mDisplayIntent = wearableBundle.getParcelable(KEY_DISPLAY_INTENT);
4462
4463 Notification[] pages = getNotificationArrayFromBundle(
4464 wearableBundle, KEY_PAGES);
4465 if (pages != null) {
4466 Collections.addAll(mPages, pages);
4467 }
4468
4469 mBackground = wearableBundle.getParcelable(KEY_BACKGROUND);
4470 mContentIcon = wearableBundle.getInt(KEY_CONTENT_ICON);
4471 mContentIconGravity = wearableBundle.getInt(KEY_CONTENT_ICON_GRAVITY,
4472 DEFAULT_CONTENT_ICON_GRAVITY);
4473 mContentActionIndex = wearableBundle.getInt(KEY_CONTENT_ACTION_INDEX,
4474 UNSET_ACTION_INDEX);
4475 mCustomSizePreset = wearableBundle.getInt(KEY_CUSTOM_SIZE_PRESET,
4476 SIZE_DEFAULT);
4477 mCustomContentHeight = wearableBundle.getInt(KEY_CUSTOM_CONTENT_HEIGHT);
4478 mGravity = wearableBundle.getInt(KEY_GRAVITY, DEFAULT_GRAVITY);
Griff Hazen5f2edfc2014-09-29 16:28:44 -07004479 mHintScreenTimeout = wearableBundle.getInt(KEY_HINT_SCREEN_TIMEOUT);
Griff Hazen61a9e862014-05-22 16:05:19 -07004480 }
4481 }
4482
4483 /**
4484 * Apply wearable extensions to a notification that is being built. This is typically
4485 * called by the {@link android.app.Notification.Builder#extend} method of
4486 * {@link android.app.Notification.Builder}.
4487 */
4488 @Override
4489 public Notification.Builder extend(Notification.Builder builder) {
4490 Bundle wearableBundle = new Bundle();
4491
4492 if (!mActions.isEmpty()) {
4493 wearableBundle.putParcelableArrayList(KEY_ACTIONS, mActions);
4494 }
4495 if (mFlags != DEFAULT_FLAGS) {
4496 wearableBundle.putInt(KEY_FLAGS, mFlags);
4497 }
4498 if (mDisplayIntent != null) {
4499 wearableBundle.putParcelable(KEY_DISPLAY_INTENT, mDisplayIntent);
4500 }
4501 if (!mPages.isEmpty()) {
4502 wearableBundle.putParcelableArray(KEY_PAGES, mPages.toArray(
4503 new Notification[mPages.size()]));
4504 }
4505 if (mBackground != null) {
4506 wearableBundle.putParcelable(KEY_BACKGROUND, mBackground);
4507 }
4508 if (mContentIcon != 0) {
4509 wearableBundle.putInt(KEY_CONTENT_ICON, mContentIcon);
4510 }
4511 if (mContentIconGravity != DEFAULT_CONTENT_ICON_GRAVITY) {
4512 wearableBundle.putInt(KEY_CONTENT_ICON_GRAVITY, mContentIconGravity);
4513 }
4514 if (mContentActionIndex != UNSET_ACTION_INDEX) {
4515 wearableBundle.putInt(KEY_CONTENT_ACTION_INDEX,
4516 mContentActionIndex);
4517 }
4518 if (mCustomSizePreset != SIZE_DEFAULT) {
4519 wearableBundle.putInt(KEY_CUSTOM_SIZE_PRESET, mCustomSizePreset);
4520 }
4521 if (mCustomContentHeight != 0) {
4522 wearableBundle.putInt(KEY_CUSTOM_CONTENT_HEIGHT, mCustomContentHeight);
4523 }
4524 if (mGravity != DEFAULT_GRAVITY) {
4525 wearableBundle.putInt(KEY_GRAVITY, mGravity);
4526 }
Griff Hazen5f2edfc2014-09-29 16:28:44 -07004527 if (mHintScreenTimeout != 0) {
4528 wearableBundle.putInt(KEY_HINT_SCREEN_TIMEOUT, mHintScreenTimeout);
4529 }
Griff Hazen61a9e862014-05-22 16:05:19 -07004530
4531 builder.getExtras().putBundle(EXTRA_WEARABLE_EXTENSIONS, wearableBundle);
4532 return builder;
4533 }
4534
4535 @Override
4536 public WearableExtender clone() {
4537 WearableExtender that = new WearableExtender();
4538 that.mActions = new ArrayList<Action>(this.mActions);
4539 that.mFlags = this.mFlags;
4540 that.mDisplayIntent = this.mDisplayIntent;
4541 that.mPages = new ArrayList<Notification>(this.mPages);
4542 that.mBackground = this.mBackground;
4543 that.mContentIcon = this.mContentIcon;
4544 that.mContentIconGravity = this.mContentIconGravity;
4545 that.mContentActionIndex = this.mContentActionIndex;
4546 that.mCustomSizePreset = this.mCustomSizePreset;
4547 that.mCustomContentHeight = this.mCustomContentHeight;
4548 that.mGravity = this.mGravity;
Griff Hazen5f2edfc2014-09-29 16:28:44 -07004549 that.mHintScreenTimeout = this.mHintScreenTimeout;
Griff Hazen61a9e862014-05-22 16:05:19 -07004550 return that;
4551 }
4552
4553 /**
4554 * Add a wearable action to this notification.
4555 *
4556 * <p>When wearable actions are added using this method, the set of actions that
4557 * show on a wearable device splits from devices that only show actions added
4558 * using {@link android.app.Notification.Builder#addAction}. This allows for customization
4559 * of which actions display on different devices.
4560 *
4561 * @param action the action to add to this notification
4562 * @return this object for method chaining
4563 * @see android.app.Notification.Action
4564 */
4565 public WearableExtender addAction(Action action) {
4566 mActions.add(action);
4567 return this;
4568 }
4569
4570 /**
4571 * Adds wearable actions to this notification.
4572 *
4573 * <p>When wearable actions are added using this method, the set of actions that
4574 * show on a wearable device splits from devices that only show actions added
4575 * using {@link android.app.Notification.Builder#addAction}. This allows for customization
4576 * of which actions display on different devices.
4577 *
4578 * @param actions the actions to add to this notification
4579 * @return this object for method chaining
4580 * @see android.app.Notification.Action
4581 */
4582 public WearableExtender addActions(List<Action> actions) {
4583 mActions.addAll(actions);
4584 return this;
4585 }
4586
4587 /**
4588 * Clear all wearable actions present on this builder.
4589 * @return this object for method chaining.
4590 * @see #addAction
4591 */
4592 public WearableExtender clearActions() {
4593 mActions.clear();
4594 return this;
4595 }
4596
4597 /**
4598 * Get the wearable actions present on this notification.
4599 */
4600 public List<Action> getActions() {
4601 return mActions;
4602 }
4603
4604 /**
4605 * Set an intent to launch inside of an activity view when displaying
Griff Hazen14f57992014-05-26 09:07:14 -07004606 * this notification. The {@link PendingIntent} provided should be for an activity.
4607 *
4608 * <pre class="prettyprint">
4609 * Intent displayIntent = new Intent(context, MyDisplayActivity.class);
4610 * PendingIntent displayPendingIntent = PendingIntent.getActivity(context,
4611 * 0, displayIntent, PendingIntent.FLAG_UPDATE_CURRENT);
4612 * Notification notif = new Notification.Builder(context)
4613 * .extend(new Notification.WearableExtender()
4614 * .setDisplayIntent(displayPendingIntent)
4615 * .setCustomSizePreset(Notification.WearableExtender.SIZE_MEDIUM))
4616 * .build();</pre>
4617 *
4618 * <p>The activity to launch needs to allow embedding, must be exported, and
Griff Hazen831ca9d2014-06-17 00:38:38 -07004619 * should have an empty task affinity. It is also recommended to use the device
4620 * default light theme.
Griff Hazen14f57992014-05-26 09:07:14 -07004621 *
4622 * <p>Example AndroidManifest.xml entry:
4623 * <pre class="prettyprint">
4624 * &lt;activity android:name=&quot;com.example.MyDisplayActivity&quot;
4625 * android:exported=&quot;true&quot;
4626 * android:allowEmbedded=&quot;true&quot;
Griff Hazen831ca9d2014-06-17 00:38:38 -07004627 * android:taskAffinity=&quot;&quot;
4628 * android:theme=&quot;@android:style/Theme.DeviceDefault.Light&quot; /&gt;</pre>
Griff Hazen61a9e862014-05-22 16:05:19 -07004629 *
4630 * @param intent the {@link PendingIntent} for an activity
4631 * @return this object for method chaining
4632 * @see android.app.Notification.WearableExtender#getDisplayIntent
4633 */
4634 public WearableExtender setDisplayIntent(PendingIntent intent) {
4635 mDisplayIntent = intent;
4636 return this;
4637 }
4638
4639 /**
4640 * Get the intent to launch inside of an activity view when displaying this
4641 * notification. This {@code PendingIntent} should be for an activity.
4642 */
4643 public PendingIntent getDisplayIntent() {
4644 return mDisplayIntent;
4645 }
4646
4647 /**
4648 * Add an additional page of content to display with this notification. The current
4649 * notification forms the first page, and pages added using this function form
4650 * subsequent pages. This field can be used to separate a notification into multiple
4651 * sections.
4652 *
4653 * @param page the notification to add as another page
4654 * @return this object for method chaining
4655 * @see android.app.Notification.WearableExtender#getPages
4656 */
4657 public WearableExtender addPage(Notification page) {
4658 mPages.add(page);
4659 return this;
4660 }
4661
4662 /**
4663 * Add additional pages of content to display with this notification. The current
4664 * notification forms the first page, and pages added using this function form
4665 * subsequent pages. This field can be used to separate a notification into multiple
4666 * sections.
4667 *
4668 * @param pages a list of notifications
4669 * @return this object for method chaining
4670 * @see android.app.Notification.WearableExtender#getPages
4671 */
4672 public WearableExtender addPages(List<Notification> pages) {
4673 mPages.addAll(pages);
4674 return this;
4675 }
4676
4677 /**
4678 * Clear all additional pages present on this builder.
4679 * @return this object for method chaining.
4680 * @see #addPage
4681 */
4682 public WearableExtender clearPages() {
4683 mPages.clear();
4684 return this;
4685 }
4686
4687 /**
4688 * Get the array of additional pages of content for displaying this notification. The
4689 * current notification forms the first page, and elements within this array form
4690 * subsequent pages. This field can be used to separate a notification into multiple
4691 * sections.
4692 * @return the pages for this notification
4693 */
4694 public List<Notification> getPages() {
4695 return mPages;
4696 }
4697
4698 /**
4699 * Set a background image to be displayed behind the notification content.
4700 * Contrary to the {@link android.app.Notification.BigPictureStyle}, this background
4701 * will work with any notification style.
4702 *
4703 * @param background the background bitmap
4704 * @return this object for method chaining
4705 * @see android.app.Notification.WearableExtender#getBackground
4706 */
4707 public WearableExtender setBackground(Bitmap background) {
4708 mBackground = background;
4709 return this;
4710 }
4711
4712 /**
4713 * Get a background image to be displayed behind the notification content.
4714 * Contrary to the {@link android.app.Notification.BigPictureStyle}, this background
4715 * will work with any notification style.
4716 *
4717 * @return the background image
4718 * @see android.app.Notification.WearableExtender#setBackground
4719 */
4720 public Bitmap getBackground() {
4721 return mBackground;
4722 }
4723
4724 /**
4725 * Set an icon that goes with the content of this notification.
4726 */
4727 public WearableExtender setContentIcon(int icon) {
4728 mContentIcon = icon;
4729 return this;
4730 }
4731
4732 /**
4733 * Get an icon that goes with the content of this notification.
4734 */
4735 public int getContentIcon() {
4736 return mContentIcon;
4737 }
4738
4739 /**
4740 * Set the gravity that the content icon should have within the notification display.
4741 * Supported values include {@link android.view.Gravity#START} and
4742 * {@link android.view.Gravity#END}. The default value is {@link android.view.Gravity#END}.
4743 * @see #setContentIcon
4744 */
4745 public WearableExtender setContentIconGravity(int contentIconGravity) {
4746 mContentIconGravity = contentIconGravity;
4747 return this;
4748 }
4749
4750 /**
4751 * Get the gravity that the content icon should have within the notification display.
4752 * Supported values include {@link android.view.Gravity#START} and
4753 * {@link android.view.Gravity#END}. The default value is {@link android.view.Gravity#END}.
4754 * @see #getContentIcon
4755 */
4756 public int getContentIconGravity() {
4757 return mContentIconGravity;
4758 }
4759
4760 /**
4761 * Set an action from this notification's actions to be clickable with the content of
Griff Hazen14f57992014-05-26 09:07:14 -07004762 * this notification. This action will no longer display separately from the
4763 * notification's content.
4764 *
Griff Hazenca48d352014-05-28 22:37:13 -07004765 * <p>For notifications with multiple pages, child pages can also have content actions
Griff Hazen14f57992014-05-26 09:07:14 -07004766 * set, although the list of available actions comes from the main notification and not
4767 * from the child page's notification.
4768 *
4769 * @param actionIndex The index of the action to hoist onto the current notification page.
4770 * If wearable actions were added to the main notification, this index
4771 * will apply to that list, otherwise it will apply to the regular
4772 * actions list.
Griff Hazen61a9e862014-05-22 16:05:19 -07004773 */
4774 public WearableExtender setContentAction(int actionIndex) {
4775 mContentActionIndex = actionIndex;
4776 return this;
4777 }
4778
4779 /**
Griff Hazenca48d352014-05-28 22:37:13 -07004780 * Get the index of the notification action, if any, that was specified as being clickable
4781 * with the content of this notification. This action will no longer display separately
Griff Hazen14f57992014-05-26 09:07:14 -07004782 * from the notification's content.
Griff Hazen61a9e862014-05-22 16:05:19 -07004783 *
Griff Hazenca48d352014-05-28 22:37:13 -07004784 * <p>For notifications with multiple pages, child pages can also have content actions
Griff Hazen14f57992014-05-26 09:07:14 -07004785 * set, although the list of available actions comes from the main notification and not
4786 * from the child page's notification.
4787 *
4788 * <p>If wearable specific actions were added to the main notification, this index will
4789 * apply to that list, otherwise it will apply to the regular actions list.
Griff Hazenca48d352014-05-28 22:37:13 -07004790 *
4791 * @return the action index or {@link #UNSET_ACTION_INDEX} if no action was selected.
Griff Hazen61a9e862014-05-22 16:05:19 -07004792 */
4793 public int getContentAction() {
4794 return mContentActionIndex;
4795 }
4796
4797 /**
4798 * Set the gravity that this notification should have within the available viewport space.
4799 * Supported values include {@link android.view.Gravity#TOP},
4800 * {@link android.view.Gravity#CENTER_VERTICAL} and {@link android.view.Gravity#BOTTOM}.
4801 * The default value is {@link android.view.Gravity#BOTTOM}.
4802 */
4803 public WearableExtender setGravity(int gravity) {
4804 mGravity = gravity;
4805 return this;
4806 }
4807
4808 /**
4809 * Get the gravity that this notification should have within the available viewport space.
4810 * Supported values include {@link android.view.Gravity#TOP},
4811 * {@link android.view.Gravity#CENTER_VERTICAL} and {@link android.view.Gravity#BOTTOM}.
4812 * The default value is {@link android.view.Gravity#BOTTOM}.
4813 */
4814 public int getGravity() {
4815 return mGravity;
4816 }
4817
4818 /**
4819 * Set the custom size preset for the display of this notification out of the available
4820 * presets found in {@link android.app.Notification.WearableExtender}, e.g.
4821 * {@link #SIZE_LARGE}.
4822 * <p>Some custom size presets are only applicable for custom display notifications created
4823 * using {@link android.app.Notification.WearableExtender#setDisplayIntent}. Check the
4824 * documentation for the preset in question. See also
4825 * {@link #setCustomContentHeight} and {@link #getCustomSizePreset}.
4826 */
4827 public WearableExtender setCustomSizePreset(int sizePreset) {
4828 mCustomSizePreset = sizePreset;
4829 return this;
4830 }
4831
4832 /**
4833 * Get the custom size preset for the display of this notification out of the available
4834 * presets found in {@link android.app.Notification.WearableExtender}, e.g.
4835 * {@link #SIZE_LARGE}.
4836 * <p>Some custom size presets are only applicable for custom display notifications created
4837 * using {@link #setDisplayIntent}. Check the documentation for the preset in question.
4838 * See also {@link #setCustomContentHeight} and {@link #setCustomSizePreset}.
4839 */
4840 public int getCustomSizePreset() {
4841 return mCustomSizePreset;
4842 }
4843
4844 /**
4845 * Set the custom height in pixels for the display of this notification's content.
4846 * <p>This option is only available for custom display notifications created
4847 * using {@link android.app.Notification.WearableExtender#setDisplayIntent}. See also
4848 * {@link android.app.Notification.WearableExtender#setCustomSizePreset} and
4849 * {@link #getCustomContentHeight}.
4850 */
4851 public WearableExtender setCustomContentHeight(int height) {
4852 mCustomContentHeight = height;
4853 return this;
4854 }
4855
4856 /**
4857 * Get the custom height in pixels for the display of this notification's content.
4858 * <p>This option is only available for custom display notifications created
4859 * using {@link #setDisplayIntent}. See also {@link #setCustomSizePreset} and
4860 * {@link #setCustomContentHeight}.
4861 */
4862 public int getCustomContentHeight() {
4863 return mCustomContentHeight;
4864 }
4865
4866 /**
4867 * Set whether the scrolling position for the contents of this notification should start
4868 * at the bottom of the contents instead of the top when the contents are too long to
4869 * display within the screen. Default is false (start scroll at the top).
4870 */
4871 public WearableExtender setStartScrollBottom(boolean startScrollBottom) {
4872 setFlag(FLAG_START_SCROLL_BOTTOM, startScrollBottom);
4873 return this;
4874 }
4875
4876 /**
4877 * Get whether the scrolling position for the contents of this notification should start
4878 * at the bottom of the contents instead of the top when the contents are too long to
4879 * display within the screen. Default is false (start scroll at the top).
4880 */
4881 public boolean getStartScrollBottom() {
4882 return (mFlags & FLAG_START_SCROLL_BOTTOM) != 0;
4883 }
4884
4885 /**
4886 * Set whether the content intent is available when the wearable device is not connected
4887 * to a companion device. The user can still trigger this intent when the wearable device
4888 * is offline, but a visual hint will indicate that the content intent may not be available.
4889 * Defaults to true.
4890 */
4891 public WearableExtender setContentIntentAvailableOffline(
4892 boolean contentIntentAvailableOffline) {
4893 setFlag(FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE, contentIntentAvailableOffline);
4894 return this;
4895 }
4896
4897 /**
4898 * Get whether the content intent is available when the wearable device is not connected
4899 * to a companion device. The user can still trigger this intent when the wearable device
4900 * is offline, but a visual hint will indicate that the content intent may not be available.
4901 * Defaults to true.
4902 */
4903 public boolean getContentIntentAvailableOffline() {
4904 return (mFlags & FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE) != 0;
4905 }
4906
4907 /**
4908 * Set a hint that this notification's icon should not be displayed.
4909 * @param hintHideIcon {@code true} to hide the icon, {@code false} otherwise.
4910 * @return this object for method chaining
4911 */
4912 public WearableExtender setHintHideIcon(boolean hintHideIcon) {
4913 setFlag(FLAG_HINT_HIDE_ICON, hintHideIcon);
4914 return this;
4915 }
4916
4917 /**
4918 * Get a hint that this notification's icon should not be displayed.
4919 * @return {@code true} if this icon should not be displayed, false otherwise.
4920 * The default value is {@code false} if this was never set.
4921 */
4922 public boolean getHintHideIcon() {
4923 return (mFlags & FLAG_HINT_HIDE_ICON) != 0;
4924 }
4925
4926 /**
4927 * Set a visual hint that only the background image of this notification should be
4928 * displayed, and other semantic content should be hidden. This hint is only applicable
4929 * to sub-pages added using {@link #addPage}.
4930 */
4931 public WearableExtender setHintShowBackgroundOnly(boolean hintShowBackgroundOnly) {
4932 setFlag(FLAG_HINT_SHOW_BACKGROUND_ONLY, hintShowBackgroundOnly);
4933 return this;
4934 }
4935
4936 /**
4937 * Get a visual hint that only the background image of this notification should be
4938 * displayed, and other semantic content should be hidden. This hint is only applicable
4939 * to sub-pages added using {@link android.app.Notification.WearableExtender#addPage}.
4940 */
4941 public boolean getHintShowBackgroundOnly() {
4942 return (mFlags & FLAG_HINT_SHOW_BACKGROUND_ONLY) != 0;
4943 }
4944
Griff Hazen5f2edfc2014-09-29 16:28:44 -07004945 /**
Griff Hazen9c5be4e2014-11-17 17:47:50 -08004946 * Set a hint that this notification's background should not be clipped if possible,
4947 * and should instead be resized to fully display on the screen, retaining the aspect
4948 * ratio of the image. This can be useful for images like barcodes or qr codes.
Griff Hazen5f2edfc2014-09-29 16:28:44 -07004949 * @param hintAvoidBackgroundClipping {@code true} to avoid clipping if possible.
4950 * @return this object for method chaining
4951 */
4952 public WearableExtender setHintAvoidBackgroundClipping(
4953 boolean hintAvoidBackgroundClipping) {
4954 setFlag(FLAG_HINT_AVOID_BACKGROUND_CLIPPING, hintAvoidBackgroundClipping);
4955 return this;
4956 }
4957
4958 /**
Griff Hazen9c5be4e2014-11-17 17:47:50 -08004959 * Get a hint that this notification's background should not be clipped if possible,
4960 * and should instead be resized to fully display on the screen, retaining the aspect
4961 * ratio of the image. This can be useful for images like barcodes or qr codes.
Griff Hazen5f2edfc2014-09-29 16:28:44 -07004962 * @return {@code true} if it's ok if the background is clipped on the screen, false
4963 * otherwise. The default value is {@code false} if this was never set.
4964 */
4965 public boolean getHintAvoidBackgroundClipping() {
4966 return (mFlags & FLAG_HINT_AVOID_BACKGROUND_CLIPPING) != 0;
4967 }
4968
4969 /**
4970 * Set a hint that the screen should remain on for at least this duration when
4971 * this notification is displayed on the screen.
4972 * @param timeout The requested screen timeout in milliseconds. Can also be either
4973 * {@link #SCREEN_TIMEOUT_SHORT} or {@link #SCREEN_TIMEOUT_LONG}.
4974 * @return this object for method chaining
4975 */
4976 public WearableExtender setHintScreenTimeout(int timeout) {
4977 mHintScreenTimeout = timeout;
4978 return this;
4979 }
4980
4981 /**
4982 * Get the duration, in milliseconds, that the screen should remain on for
4983 * when this notification is displayed.
4984 * @return the duration in milliseconds if > 0, or either one of the sentinel values
4985 * {@link #SCREEN_TIMEOUT_SHORT} or {@link #SCREEN_TIMEOUT_LONG}.
4986 */
4987 public int getHintScreenTimeout() {
4988 return mHintScreenTimeout;
4989 }
4990
Griff Hazen61a9e862014-05-22 16:05:19 -07004991 private void setFlag(int mask, boolean value) {
4992 if (value) {
4993 mFlags |= mask;
4994 } else {
4995 mFlags &= ~mask;
4996 }
4997 }
4998 }
4999
5000 /**
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08005001 * <p>Helper class to add Android Auto extensions to notifications. To create a notification
5002 * with car extensions:
5003 *
5004 * <ol>
5005 * <li>Create an {@link Notification.Builder}, setting any desired
5006 * properties.
5007 * <li>Create a {@link CarExtender}.
5008 * <li>Set car-specific properties using the {@code add} and {@code set} methods of
5009 * {@link CarExtender}.
5010 * <li>Call {@link Notification.Builder#extend(Notification.Extender)}
5011 * to apply the extensions to a notification.
5012 * </ol>
5013 *
5014 * <pre class="prettyprint">
5015 * Notification notification = new Notification.Builder(context)
5016 * ...
5017 * .extend(new CarExtender()
5018 * .set*(...))
5019 * .build();
5020 * </pre>
5021 *
5022 * <p>Car extensions can be accessed on an existing notification by using the
5023 * {@code CarExtender(Notification)} constructor, and then using the {@code get} methods
5024 * to access values.
5025 */
5026 public static final class CarExtender implements Extender {
5027 private static final String TAG = "CarExtender";
5028
5029 private static final String EXTRA_CAR_EXTENDER = "android.car.EXTENSIONS";
5030 private static final String EXTRA_LARGE_ICON = "large_icon";
5031 private static final String EXTRA_CONVERSATION = "car_conversation";
5032 private static final String EXTRA_COLOR = "app_color";
5033
5034 private Bitmap mLargeIcon;
5035 private UnreadConversation mUnreadConversation;
5036 private int mColor = Notification.COLOR_DEFAULT;
5037
5038 /**
5039 * Create a {@link CarExtender} with default options.
5040 */
5041 public CarExtender() {
5042 }
5043
5044 /**
5045 * Create a {@link CarExtender} from the CarExtender options of an existing Notification.
5046 *
5047 * @param notif The notification from which to copy options.
5048 */
5049 public CarExtender(Notification notif) {
5050 Bundle carBundle = notif.extras == null ?
5051 null : notif.extras.getBundle(EXTRA_CAR_EXTENDER);
5052 if (carBundle != null) {
5053 mLargeIcon = carBundle.getParcelable(EXTRA_LARGE_ICON);
5054 mColor = carBundle.getInt(EXTRA_COLOR, Notification.COLOR_DEFAULT);
5055
5056 Bundle b = carBundle.getBundle(EXTRA_CONVERSATION);
5057 mUnreadConversation = UnreadConversation.getUnreadConversationFromBundle(b);
5058 }
5059 }
5060
5061 /**
5062 * Apply car extensions to a notification that is being built. This is typically called by
5063 * the {@link Notification.Builder#extend(Notification.Extender)}
5064 * method of {@link Notification.Builder}.
5065 */
5066 @Override
5067 public Notification.Builder extend(Notification.Builder builder) {
5068 Bundle carExtensions = new Bundle();
5069
5070 if (mLargeIcon != null) {
5071 carExtensions.putParcelable(EXTRA_LARGE_ICON, mLargeIcon);
5072 }
5073 if (mColor != Notification.COLOR_DEFAULT) {
5074 carExtensions.putInt(EXTRA_COLOR, mColor);
5075 }
5076
5077 if (mUnreadConversation != null) {
5078 Bundle b = mUnreadConversation.getBundleForUnreadConversation();
5079 carExtensions.putBundle(EXTRA_CONVERSATION, b);
5080 }
5081
5082 builder.getExtras().putBundle(EXTRA_CAR_EXTENDER, carExtensions);
5083 return builder;
5084 }
5085
5086 /**
5087 * Sets the accent color to use when Android Auto presents the notification.
5088 *
5089 * Android Auto uses the color set with {@link Notification.Builder#setColor(int)}
5090 * to accent the displayed notification. However, not all colors are acceptable in an
5091 * automotive setting. This method can be used to override the color provided in the
5092 * notification in such a situation.
5093 */
Tor Norbye80756e32015-03-02 09:39:27 -08005094 public CarExtender setColor(@ColorInt int color) {
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08005095 mColor = color;
5096 return this;
5097 }
5098
5099 /**
5100 * Gets the accent color.
5101 *
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005102 * @see #setColor
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08005103 */
Tor Norbye80756e32015-03-02 09:39:27 -08005104 @ColorInt
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08005105 public int getColor() {
5106 return mColor;
5107 }
5108
5109 /**
5110 * Sets the large icon of the car notification.
5111 *
5112 * If no large icon is set in the extender, Android Auto will display the icon
5113 * specified by {@link Notification.Builder#setLargeIcon(android.graphics.Bitmap)}
5114 *
5115 * @param largeIcon The large icon to use in the car notification.
5116 * @return This object for method chaining.
5117 */
5118 public CarExtender setLargeIcon(Bitmap largeIcon) {
5119 mLargeIcon = largeIcon;
5120 return this;
5121 }
5122
5123 /**
5124 * Gets the large icon used in this car notification, or null if no icon has been set.
5125 *
5126 * @return The large icon for the car notification.
5127 * @see CarExtender#setLargeIcon
5128 */
5129 public Bitmap getLargeIcon() {
5130 return mLargeIcon;
5131 }
5132
5133 /**
5134 * Sets the unread conversation in a message notification.
5135 *
5136 * @param unreadConversation The unread part of the conversation this notification conveys.
5137 * @return This object for method chaining.
5138 */
5139 public CarExtender setUnreadConversation(UnreadConversation unreadConversation) {
5140 mUnreadConversation = unreadConversation;
5141 return this;
5142 }
5143
5144 /**
5145 * Returns the unread conversation conveyed by this notification.
5146 * @see #setUnreadConversation(UnreadConversation)
5147 */
5148 public UnreadConversation getUnreadConversation() {
5149 return mUnreadConversation;
5150 }
5151
5152 /**
5153 * A class which holds the unread messages from a conversation.
5154 */
5155 public static class UnreadConversation {
5156 private static final String KEY_AUTHOR = "author";
5157 private static final String KEY_TEXT = "text";
5158 private static final String KEY_MESSAGES = "messages";
5159 private static final String KEY_REMOTE_INPUT = "remote_input";
5160 private static final String KEY_ON_REPLY = "on_reply";
5161 private static final String KEY_ON_READ = "on_read";
5162 private static final String KEY_PARTICIPANTS = "participants";
5163 private static final String KEY_TIMESTAMP = "timestamp";
5164
5165 private final String[] mMessages;
5166 private final RemoteInput mRemoteInput;
5167 private final PendingIntent mReplyPendingIntent;
5168 private final PendingIntent mReadPendingIntent;
5169 private final String[] mParticipants;
5170 private final long mLatestTimestamp;
5171
5172 UnreadConversation(String[] messages, RemoteInput remoteInput,
5173 PendingIntent replyPendingIntent, PendingIntent readPendingIntent,
5174 String[] participants, long latestTimestamp) {
5175 mMessages = messages;
5176 mRemoteInput = remoteInput;
5177 mReadPendingIntent = readPendingIntent;
5178 mReplyPendingIntent = replyPendingIntent;
5179 mParticipants = participants;
5180 mLatestTimestamp = latestTimestamp;
5181 }
5182
5183 /**
5184 * Gets the list of messages conveyed by this notification.
5185 */
5186 public String[] getMessages() {
5187 return mMessages;
5188 }
5189
5190 /**
5191 * Gets the remote input that will be used to convey the response to a message list, or
5192 * null if no such remote input exists.
5193 */
5194 public RemoteInput getRemoteInput() {
5195 return mRemoteInput;
5196 }
5197
5198 /**
5199 * Gets the pending intent that will be triggered when the user replies to this
5200 * notification.
5201 */
5202 public PendingIntent getReplyPendingIntent() {
5203 return mReplyPendingIntent;
5204 }
5205
5206 /**
5207 * Gets the pending intent that Android Auto will send after it reads aloud all messages
5208 * in this object's message list.
5209 */
5210 public PendingIntent getReadPendingIntent() {
5211 return mReadPendingIntent;
5212 }
5213
5214 /**
5215 * Gets the participants in the conversation.
5216 */
5217 public String[] getParticipants() {
5218 return mParticipants;
5219 }
5220
5221 /**
5222 * Gets the firs participant in the conversation.
5223 */
5224 public String getParticipant() {
5225 return mParticipants.length > 0 ? mParticipants[0] : null;
5226 }
5227
5228 /**
5229 * Gets the timestamp of the conversation.
5230 */
5231 public long getLatestTimestamp() {
5232 return mLatestTimestamp;
5233 }
5234
5235 Bundle getBundleForUnreadConversation() {
5236 Bundle b = new Bundle();
5237 String author = null;
5238 if (mParticipants != null && mParticipants.length > 1) {
5239 author = mParticipants[0];
5240 }
5241 Parcelable[] messages = new Parcelable[mMessages.length];
5242 for (int i = 0; i < messages.length; i++) {
5243 Bundle m = new Bundle();
5244 m.putString(KEY_TEXT, mMessages[i]);
5245 m.putString(KEY_AUTHOR, author);
5246 messages[i] = m;
5247 }
5248 b.putParcelableArray(KEY_MESSAGES, messages);
5249 if (mRemoteInput != null) {
5250 b.putParcelable(KEY_REMOTE_INPUT, mRemoteInput);
5251 }
5252 b.putParcelable(KEY_ON_REPLY, mReplyPendingIntent);
5253 b.putParcelable(KEY_ON_READ, mReadPendingIntent);
5254 b.putStringArray(KEY_PARTICIPANTS, mParticipants);
5255 b.putLong(KEY_TIMESTAMP, mLatestTimestamp);
5256 return b;
5257 }
5258
5259 static UnreadConversation getUnreadConversationFromBundle(Bundle b) {
5260 if (b == null) {
5261 return null;
5262 }
5263 Parcelable[] parcelableMessages = b.getParcelableArray(KEY_MESSAGES);
5264 String[] messages = null;
5265 if (parcelableMessages != null) {
5266 String[] tmp = new String[parcelableMessages.length];
5267 boolean success = true;
5268 for (int i = 0; i < tmp.length; i++) {
5269 if (!(parcelableMessages[i] instanceof Bundle)) {
5270 success = false;
5271 break;
5272 }
5273 tmp[i] = ((Bundle) parcelableMessages[i]).getString(KEY_TEXT);
5274 if (tmp[i] == null) {
5275 success = false;
5276 break;
5277 }
5278 }
5279 if (success) {
5280 messages = tmp;
5281 } else {
5282 return null;
5283 }
5284 }
5285
5286 PendingIntent onRead = b.getParcelable(KEY_ON_READ);
5287 PendingIntent onReply = b.getParcelable(KEY_ON_REPLY);
5288
5289 RemoteInput remoteInput = b.getParcelable(KEY_REMOTE_INPUT);
5290
5291 String[] participants = b.getStringArray(KEY_PARTICIPANTS);
5292 if (participants == null || participants.length != 1) {
5293 return null;
5294 }
5295
5296 return new UnreadConversation(messages,
5297 remoteInput,
5298 onReply,
5299 onRead,
5300 participants, b.getLong(KEY_TIMESTAMP));
5301 }
5302 };
5303
5304 /**
5305 * Builder class for {@link CarExtender.UnreadConversation} objects.
5306 */
5307 public static class Builder {
5308 private final List<String> mMessages = new ArrayList<String>();
5309 private final String mParticipant;
5310 private RemoteInput mRemoteInput;
5311 private PendingIntent mReadPendingIntent;
5312 private PendingIntent mReplyPendingIntent;
5313 private long mLatestTimestamp;
5314
5315 /**
5316 * Constructs a new builder for {@link CarExtender.UnreadConversation}.
5317 *
5318 * @param name The name of the other participant in the conversation.
5319 */
5320 public Builder(String name) {
5321 mParticipant = name;
5322 }
5323
5324 /**
5325 * Appends a new unread message to the list of messages for this conversation.
5326 *
5327 * The messages should be added from oldest to newest.
5328 *
5329 * @param message The text of the new unread message.
5330 * @return This object for method chaining.
5331 */
5332 public Builder addMessage(String message) {
5333 mMessages.add(message);
5334 return this;
5335 }
5336
5337 /**
5338 * Sets the pending intent and remote input which will convey the reply to this
5339 * notification.
5340 *
5341 * @param pendingIntent The pending intent which will be triggered on a reply.
5342 * @param remoteInput The remote input parcelable which will carry the reply.
5343 * @return This object for method chaining.
5344 *
5345 * @see CarExtender.UnreadConversation#getRemoteInput
5346 * @see CarExtender.UnreadConversation#getReplyPendingIntent
5347 */
5348 public Builder setReplyAction(
5349 PendingIntent pendingIntent, RemoteInput remoteInput) {
5350 mRemoteInput = remoteInput;
5351 mReplyPendingIntent = pendingIntent;
5352
5353 return this;
5354 }
5355
5356 /**
5357 * Sets the pending intent that will be sent once the messages in this notification
5358 * are read.
5359 *
5360 * @param pendingIntent The pending intent to use.
5361 * @return This object for method chaining.
5362 */
5363 public Builder setReadPendingIntent(PendingIntent pendingIntent) {
5364 mReadPendingIntent = pendingIntent;
5365 return this;
5366 }
5367
5368 /**
5369 * Sets the timestamp of the most recent message in an unread conversation.
5370 *
5371 * If a messaging notification has been posted by your application and has not
5372 * yet been cancelled, posting a later notification with the same id and tag
5373 * but without a newer timestamp may result in Android Auto not displaying a
5374 * heads up notification for the later notification.
5375 *
5376 * @param timestamp The timestamp of the most recent message in the conversation.
5377 * @return This object for method chaining.
5378 */
5379 public Builder setLatestTimestamp(long timestamp) {
5380 mLatestTimestamp = timestamp;
5381 return this;
5382 }
5383
5384 /**
5385 * Builds a new unread conversation object.
5386 *
5387 * @return The new unread conversation object.
5388 */
5389 public UnreadConversation build() {
5390 String[] messages = mMessages.toArray(new String[mMessages.size()]);
5391 String[] participants = { mParticipant };
5392 return new UnreadConversation(messages, mRemoteInput, mReplyPendingIntent,
5393 mReadPendingIntent, participants, mLatestTimestamp);
5394 }
5395 }
5396 }
5397
5398 /**
Griff Hazen61a9e862014-05-22 16:05:19 -07005399 * Get an array of Notification objects from a parcelable array bundle field.
5400 * Update the bundle to have a typed array so fetches in the future don't need
5401 * to do an array copy.
5402 */
5403 private static Notification[] getNotificationArrayFromBundle(Bundle bundle, String key) {
5404 Parcelable[] array = bundle.getParcelableArray(key);
5405 if (array instanceof Notification[] || array == null) {
5406 return (Notification[]) array;
5407 }
5408 Notification[] typedArray = Arrays.copyOf(array, array.length,
5409 Notification[].class);
5410 bundle.putParcelableArray(key, typedArray);
5411 return typedArray;
5412 }
Christoph Studer4600f9b2014-07-22 22:44:43 +02005413
5414 private static class BuilderRemoteViews extends RemoteViews {
5415 public BuilderRemoteViews(Parcel parcel) {
5416 super(parcel);
5417 }
5418
Kenny Guy77320062014-08-27 21:37:15 +01005419 public BuilderRemoteViews(ApplicationInfo appInfo, int layoutId) {
5420 super(appInfo, layoutId);
Christoph Studer4600f9b2014-07-22 22:44:43 +02005421 }
5422
5423 @Override
5424 public BuilderRemoteViews clone() {
5425 Parcel p = Parcel.obtain();
5426 writeToParcel(p, 0);
5427 p.setDataPosition(0);
5428 BuilderRemoteViews brv = new BuilderRemoteViews(p);
5429 p.recycle();
5430 return brv;
5431 }
5432 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005433}