blob: b592bb794a35d3be71ea111d013e5bea97bf360e [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;
Christoph Studer4600f9b2014-07-22 22:44:43 +020028import android.content.pm.PackageManager.NameNotFoundException;
Jorim Jaggief72a192014-08-26 21:57:46 +020029import android.content.res.ColorStateList;
Joe Onoratoef1e7762010-09-17 18:38:38 -040030import android.graphics.Bitmap;
Kenny Guy8a0101b2014-05-08 23:34:12 +010031import android.graphics.Canvas;
Jorim Jaggi5c2d8462014-03-21 17:37:00 +010032import android.graphics.PorterDuff;
Kenny Guy8a0101b2014-05-08 23:34:12 +010033import android.graphics.drawable.Drawable;
Dan Sandlerd63f9322015-05-06 15:18:49 -040034import android.graphics.drawable.Icon;
John Spurlockc0650f022014-07-19 13:22:39 -040035import android.media.AudioAttributes;
Jeff Sharkey098d5802012-04-26 17:30:34 -070036import android.media.AudioManager;
Jeff Browndba34ba2014-06-24 20:46:03 -070037import android.media.session.MediaSession;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080038import android.net.Uri;
Daniel Sandlerdcbaf662013-04-26 16:23:09 -040039import android.os.BadParcelableException;
Christoph Studer239f8352014-08-25 15:13:18 +020040import android.os.Build;
Daniel Sandler2561b0b2012-02-13 21:04:12 -050041import android.os.Bundle;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080042import android.os.Parcel;
43import android.os.Parcelable;
Daniel Sandlera2985ed2012-04-03 16:42:00 -040044import android.os.SystemClock;
Jeff Sharkey6d515712012-09-20 16:06:08 -070045import android.os.UserHandle;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080046import android.text.TextUtils;
Daniel Sandlerdcbaf662013-04-26 16:23:09 -040047import android.util.Log;
Jorim Jaggi445d3c02014-08-19 22:33:42 +020048import android.util.MathUtils;
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);
2972 contentView.setViewVisibility(R.id.profile_badge_line2, View.GONE);
2973 contentView.setViewVisibility(R.id.profile_badge_line3, View.GONE);
2974
2975 if (profileBadge != null) {
2976 contentView.setImageViewBitmap(resId, profileBadge);
2977 contentView.setViewVisibility(resId, View.VISIBLE);
2978
2979 // Make sure Line 3 is visible. As badge will be here if there
2980 // is no text to display.
2981 if (resId == R.id.profile_badge_line3) {
2982 contentView.setViewVisibility(R.id.line3, View.VISIBLE);
2983 }
2984 return true;
2985 }
2986 return false;
2987 }
2988
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02002989 private void shrinkLine3Text(RemoteViews contentView) {
2990 float subTextSize = mContext.getResources().getDimensionPixelSize(
2991 R.dimen.notification_subtext_size);
2992 contentView.setTextViewTextSize(R.id.text, TypedValue.COMPLEX_UNIT_PX, subTextSize);
2993 }
2994
Christoph Studerfe718432014-09-01 18:21:18 +02002995 private void unshrinkLine3Text(RemoteViews contentView) {
2996 float regularTextSize = mContext.getResources().getDimensionPixelSize(
2997 com.android.internal.R.dimen.notification_text_size);
2998 contentView.setTextViewTextSize(R.id.text, TypedValue.COMPLEX_UNIT_PX, regularTextSize);
2999 }
3000
3001 private void resetStandardTemplate(RemoteViews contentView) {
3002 removeLargeIconBackground(contentView);
3003 contentView.setViewPadding(R.id.icon, 0, 0, 0, 0);
3004 contentView.setImageViewResource(R.id.icon, 0);
3005 contentView.setInt(R.id.icon, "setBackgroundResource", 0);
3006 contentView.setViewVisibility(R.id.right_icon, View.GONE);
3007 contentView.setInt(R.id.right_icon, "setBackgroundResource", 0);
3008 contentView.setImageViewResource(R.id.right_icon, 0);
3009 contentView.setImageViewResource(R.id.icon, 0);
3010 contentView.setTextViewText(R.id.title, null);
3011 contentView.setTextViewText(R.id.text, null);
3012 unshrinkLine3Text(contentView);
3013 contentView.setTextViewText(R.id.text2, null);
3014 contentView.setViewVisibility(R.id.text2, View.GONE);
3015 contentView.setViewVisibility(R.id.info, View.GONE);
3016 contentView.setViewVisibility(R.id.time, View.GONE);
3017 contentView.setViewVisibility(R.id.line3, View.GONE);
3018 contentView.setViewVisibility(R.id.overflow_divider, View.GONE);
3019 contentView.setViewVisibility(R.id.progress, View.GONE);
Christoph Studerca1db712014-09-10 17:31:33 +02003020 contentView.setViewVisibility(R.id.chronometer, View.GONE);
Christoph Studerfe718432014-09-01 18:21:18 +02003021 }
3022
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003023 private RemoteViews applyStandardTemplate(int resId) {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02003024 return applyStandardTemplate(resId, true /* hasProgress */);
3025 }
3026
3027 /**
3028 * @param hasProgress whether the progress bar should be shown and set
3029 */
3030 private RemoteViews applyStandardTemplate(int resId, boolean hasProgress) {
Kenny Guy77320062014-08-27 21:37:15 +01003031 RemoteViews contentView = new BuilderRemoteViews(mContext.getApplicationInfo(), resId);
Dan Sandler539aad42014-08-04 00:43:39 -04003032
Christoph Studerfe718432014-09-01 18:21:18 +02003033 resetStandardTemplate(contentView);
3034
Daniel Sandler9f7936a2012-05-21 16:14:28 -04003035 boolean showLine3 = false;
3036 boolean showLine2 = false;
Kenny Guy98193ea2014-07-24 19:54:37 +01003037 boolean contentTextInLine2 = false;
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003038 final Bundle ex = mN.extras;
Dan Sandler190d58d2014-05-15 09:33:39 -04003039
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003040 if (mN.mLargeIcon != null) {
3041 contentView.setImageViewIcon(R.id.icon, mN.mLargeIcon);
3042 processLargeLegacyIcon(mN.mLargeIcon, contentView);
3043 contentView.setImageViewIcon(R.id.right_icon, mN.mSmallIcon);
Dan Sandler190d58d2014-05-15 09:33:39 -04003044 contentView.setViewVisibility(R.id.right_icon, View.VISIBLE);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003045 processSmallRightIcon(mN.mSmallIcon, contentView);
Dan Sandler190d58d2014-05-15 09:33:39 -04003046 } else { // small icon at left
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003047 contentView.setImageViewIcon(R.id.icon, mN.mSmallIcon);
Dan Sandler190d58d2014-05-15 09:33:39 -04003048 contentView.setViewVisibility(R.id.icon, View.VISIBLE);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003049 processSmallIconAsLarge(mN.mSmallIcon, contentView);
Joe Onorato561d3852010-11-20 18:09:34 -08003050 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003051 if (ex.getCharSequence(EXTRA_TITLE) != null) {
3052 contentView.setTextViewText(R.id.title,
3053 processLegacyText(ex.getCharSequence(EXTRA_TITLE)));
Joe Onorato561d3852010-11-20 18:09:34 -08003054 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003055 if (ex.getCharSequence(EXTRA_TEXT) != null) {
3056 contentView.setTextViewText(R.id.text,
3057 processLegacyText(ex.getCharSequence(EXTRA_TEXT)));
Daniel Sandler9f7936a2012-05-21 16:14:28 -04003058 showLine3 = true;
Joe Onorato561d3852010-11-20 18:09:34 -08003059 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003060 if (ex.getCharSequence(EXTRA_INFO_TEXT) != null) {
3061 contentView.setTextViewText(R.id.info,
3062 processLegacyText(ex.getCharSequence(EXTRA_INFO_TEXT)));
Jeff Sharkey1c400132011-08-05 14:50:13 -07003063 contentView.setViewVisibility(R.id.info, View.VISIBLE);
Daniel Sandler9f7936a2012-05-21 16:14:28 -04003064 showLine3 = true;
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003065 } else if (mN.number > 0) {
Daniel Sandlerebce0112011-06-16 16:44:51 -04003066 final int tooBig = mContext.getResources().getInteger(
3067 R.integer.status_bar_notification_info_maxnum);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003068 if (mN.number > tooBig) {
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003069 contentView.setTextViewText(R.id.info, processLegacyText(
3070 mContext.getResources().getString(
3071 R.string.status_bar_notification_info_overflow)));
Joe Onorato059a2f82011-01-04 10:27:01 -08003072 } else {
3073 NumberFormat f = NumberFormat.getIntegerInstance();
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003074 contentView.setTextViewText(R.id.info, processLegacyText(f.format(mN.number)));
Joe Onorato059a2f82011-01-04 10:27:01 -08003075 }
Jeff Sharkey1c400132011-08-05 14:50:13 -07003076 contentView.setViewVisibility(R.id.info, View.VISIBLE);
Daniel Sandler9f7936a2012-05-21 16:14:28 -04003077 showLine3 = true;
Joe Onorato561d3852010-11-20 18:09:34 -08003078 } else {
3079 contentView.setViewVisibility(R.id.info, View.GONE);
3080 }
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003081
Daniel Sandler9f7936a2012-05-21 16:14:28 -04003082 // Need to show three lines?
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003083 if (ex.getCharSequence(EXTRA_SUB_TEXT) != null) {
3084 contentView.setTextViewText(R.id.text,
3085 processLegacyText(ex.getCharSequence(EXTRA_SUB_TEXT)));
3086 if (ex.getCharSequence(EXTRA_TEXT) != null) {
3087 contentView.setTextViewText(R.id.text2,
3088 processLegacyText(ex.getCharSequence(EXTRA_TEXT)));
Daniel Sandler9f7936a2012-05-21 16:14:28 -04003089 contentView.setViewVisibility(R.id.text2, View.VISIBLE);
3090 showLine2 = true;
Kenny Guy98193ea2014-07-24 19:54:37 +01003091 contentTextInLine2 = true;
Daniel Sandler9f7936a2012-05-21 16:14:28 -04003092 } else {
3093 contentView.setViewVisibility(R.id.text2, View.GONE);
3094 }
Jeff Sharkey1c400132011-08-05 14:50:13 -07003095 } else {
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003096 contentView.setViewVisibility(R.id.text2, View.GONE);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003097 final int max = ex.getInt(EXTRA_PROGRESS_MAX, 0);
3098 final int progress = ex.getInt(EXTRA_PROGRESS, 0);
3099 final boolean ind = ex.getBoolean(EXTRA_PROGRESS_INDETERMINATE);
3100 if (hasProgress && (max != 0 || ind)) {
Christoph Studeraca4b252014-09-09 15:58:41 +02003101 contentView.setViewVisibility(R.id.progress, View.VISIBLE);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003102 contentView.setProgressBar(
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003103 R.id.progress, max, progress, ind);
Jorim Jaggief72a192014-08-26 21:57:46 +02003104 contentView.setProgressBackgroundTintList(
Alan Viverette4a357cd2015-03-18 18:37:18 -07003105 R.id.progress, ColorStateList.valueOf(mContext.getColor(
Jorim Jaggief72a192014-08-26 21:57:46 +02003106 R.color.notification_progress_background_color)));
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003107 if (mN.color != COLOR_DEFAULT) {
3108 ColorStateList colorStateList = ColorStateList.valueOf(mN.color);
Jorim Jaggief72a192014-08-26 21:57:46 +02003109 contentView.setProgressTintList(R.id.progress, colorStateList);
3110 contentView.setProgressIndeterminateTintList(R.id.progress, colorStateList);
3111 }
Daniel Sandler9f7936a2012-05-21 16:14:28 -04003112 showLine2 = true;
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003113 } else {
3114 contentView.setViewVisibility(R.id.progress, View.GONE);
3115 }
Jeff Sharkey1c400132011-08-05 14:50:13 -07003116 }
Daniel Sandler9f7936a2012-05-21 16:14:28 -04003117 if (showLine2) {
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003118
3119 // need to shrink all the type to make sure everything fits
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003120 shrinkLine3Text(contentView);
Daniel Sandler9f7936a2012-05-21 16:14:28 -04003121 }
3122
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02003123 if (showsTimeOrChronometer()) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003124 if (ex.getBoolean(EXTRA_SHOW_CHRONOMETER)) {
Daniel Sandlera2985ed2012-04-03 16:42:00 -04003125 contentView.setViewVisibility(R.id.chronometer, View.VISIBLE);
3126 contentView.setLong(R.id.chronometer, "setBase",
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003127 mN.when + (SystemClock.elapsedRealtime() - System.currentTimeMillis()));
Daniel Sandlera2985ed2012-04-03 16:42:00 -04003128 contentView.setBoolean(R.id.chronometer, "setStarted", true);
3129 } else {
3130 contentView.setViewVisibility(R.id.time, View.VISIBLE);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003131 contentView.setLong(R.id.time, "setTime", mN.when);
Daniel Sandlera2985ed2012-04-03 16:42:00 -04003132 }
Joe Onorato561d3852010-11-20 18:09:34 -08003133 }
Daniel Sandler0c890492012-09-12 17:23:10 -07003134
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003135 // Adjust padding depending on line count and font size.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003136 contentView.setViewPadding(R.id.line1, 0,
3137 calculateTopPadding(mContext, hasThreeLines(),
3138 mContext.getResources().getConfiguration().fontScale),
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003139 0, 0);
3140
Kenny Guy98193ea2014-07-24 19:54:37 +01003141 // We want to add badge to first line of text.
3142 boolean addedBadge = addProfileBadge(contentView,
3143 contentTextInLine2 ? R.id.profile_badge_line2 : R.id.profile_badge_line3);
3144 // If we added the badge to line 3 then we should show line 3.
3145 if (addedBadge && !contentTextInLine2) {
3146 showLine3 = true;
3147 }
3148
3149 // Note getStandardView may hide line 3 again.
Daniel Sandler9f7936a2012-05-21 16:14:28 -04003150 contentView.setViewVisibility(R.id.line3, showLine3 ? View.VISIBLE : View.GONE);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003151 contentView.setViewVisibility(R.id.overflow_divider,
3152 showLine3 ? View.VISIBLE : View.GONE);
Joe Onorato561d3852010-11-20 18:09:34 -08003153 return contentView;
3154 }
3155
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003156 /**
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02003157 * @return true if the built notification will show the time or the chronometer; false
3158 * otherwise
3159 */
3160 private boolean showsTimeOrChronometer() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003161 return mN.when != 0 && mN.extras.getBoolean(EXTRA_SHOW_WHEN);
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02003162 }
3163
3164 /**
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003165 * Logic to find out whether the notification is going to have three lines in the contracted
3166 * layout. This is used to adjust the top padding.
3167 *
3168 * @return true if the notification is going to have three lines; false if the notification
3169 * is going to have one or two lines
3170 */
3171 private boolean hasThreeLines() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003172 final CharSequence subText = mN.extras.getCharSequence(EXTRA_SUB_TEXT);
3173 final CharSequence text = mN.extras.getCharSequence(EXTRA_TEXT);
3174 boolean contentTextInLine2 = subText != null && text != null;
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02003175 boolean hasProgress = mStyle == null || mStyle.hasProgress();
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003176 // If we have content text in line 2, badge goes into line 2, or line 3 otherwise
3177 boolean badgeInLine3 = getProfileBadgeDrawable() != null && !contentTextInLine2;
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003178 boolean hasLine3 = text != null || mN.extras.getCharSequence(EXTRA_INFO_TEXT) != null
3179 || mN.number > 0 || badgeInLine3;
3180 final Bundle ex = mN.extras;
3181 final int max = ex.getInt(EXTRA_PROGRESS_MAX, 0);
3182 final boolean ind = ex.getBoolean(EXTRA_PROGRESS_INDETERMINATE);
3183 boolean hasLine2 = (subText != null && text != null) ||
3184 (hasProgress && subText == null && (max != 0 || ind));
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003185 return hasLine2 && hasLine3;
3186 }
3187
3188 /**
3189 * @hide
3190 */
3191 public static int calculateTopPadding(Context ctx, boolean hasThreeLines,
3192 float fontScale) {
3193 int padding = ctx.getResources().getDimensionPixelSize(hasThreeLines
3194 ? R.dimen.notification_top_pad_narrow
3195 : R.dimen.notification_top_pad);
3196 int largePadding = ctx.getResources().getDimensionPixelSize(hasThreeLines
3197 ? R.dimen.notification_top_pad_large_text_narrow
3198 : R.dimen.notification_top_pad_large_text);
3199 float largeFactor = (MathUtils.constrain(fontScale, 1.0f, LARGE_TEXT_SCALE) - 1f)
3200 / (LARGE_TEXT_SCALE - 1f);
3201
3202 // Linearly interpolate the padding between large and normal with the font scale ranging
3203 // from 1f to LARGE_TEXT_SCALE
3204 return Math.round((1 - largeFactor) * padding + largeFactor * largePadding);
3205 }
3206
Christoph Studerfe718432014-09-01 18:21:18 +02003207 private void resetStandardTemplateWithActions(RemoteViews big) {
3208 big.setViewVisibility(R.id.actions, View.GONE);
3209 big.setViewVisibility(R.id.action_divider, View.GONE);
3210 big.removeAllViews(R.id.actions);
3211 }
3212
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003213 private RemoteViews applyStandardTemplateWithActions(int layoutId) {
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003214 RemoteViews big = applyStandardTemplate(layoutId);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003215
Christoph Studerfe718432014-09-01 18:21:18 +02003216 resetStandardTemplateWithActions(big);
3217
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003218 int N = mActions.size();
3219 if (N > 0) {
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003220 big.setViewVisibility(R.id.actions, View.VISIBLE);
Daniel Sandler6387d2f2012-05-22 13:44:09 -04003221 big.setViewVisibility(R.id.action_divider, View.VISIBLE);
Daniel Sandler8680bf82012-05-15 16:52:52 -04003222 if (N>MAX_ACTION_BUTTONS) N=MAX_ACTION_BUTTONS;
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003223 for (int i=0; i<N; i++) {
3224 final RemoteViews button = generateActionButton(mActions.get(i));
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003225 big.addView(R.id.actions, button);
3226 }
3227 }
3228 return big;
3229 }
3230
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003231 /**
3232 * Construct a RemoteViews for the final 1U notification layout. In order:
3233 * 1. Custom contentView from the caller
3234 * 2. Style's proposed content view
3235 * 3. Standard template view
3236 */
3237 public RemoteViews makeContentView() {
3238 if (mN.contentView != null) {
3239 return mN.contentView;
3240 } else if (mStyle != null) {
3241 final RemoteViews styleView = mStyle.makeContentView();
3242 if (styleView != null) {
3243 return styleView;
3244 }
Joe Onorato46439ce2010-11-19 13:56:21 -08003245 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003246 return applyStandardTemplate(getBaseLayoutResource());
Joe Onorato46439ce2010-11-19 13:56:21 -08003247 }
3248
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003249 /**
3250 * Construct a RemoteViews for the final big notification layout.
3251 */
3252 public RemoteViews makeBigContentView() {
Julia Reynolds089e3e42015-11-18 09:59:57 -05003253 if (mN.bigContentView != null) {
3254 return mN.bigContentView;
3255 } else if (mStyle != null) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003256 final RemoteViews styleView = mStyle.makeBigContentView();
3257 if (styleView != null) {
3258 return styleView;
3259 }
Julia Reynolds089e3e42015-11-18 09:59:57 -05003260 } else if (mActions.size() == 0) {
3261 return null;
3262 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003263
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003264 return applyStandardTemplateWithActions(getBigBaseLayoutResource());
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003265 }
3266
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003267 /**
3268 * Construct a RemoteViews for the final heads-up notification layout.
3269 */
3270 public RemoteViews makeHeadsUpContentView() {
Julia Reynolds089e3e42015-11-18 09:59:57 -05003271 if (mN.headsUpContentView != null) {
3272 return mN.headsUpContentView;
3273 } else if (mStyle != null) {
3274 final RemoteViews styleView = mStyle.makeHeadsUpContentView();
3275 if (styleView != null) {
3276 return styleView;
3277 }
3278 } else if (mActions.size() == 0) {
3279 return null;
3280 }
3281
Chris Wren8fd39ec2014-02-27 17:43:26 -05003282
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003283 return applyStandardTemplateWithActions(getBigBaseLayoutResource());
Chris Wren8fd39ec2014-02-27 17:43:26 -05003284 }
3285
3286
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003287 private RemoteViews generateActionButton(Action action) {
Daniel Sandler8680bf82012-05-15 16:52:52 -04003288 final boolean tombstone = (action.actionIntent == null);
Selim Cinekf33b1112015-07-15 17:45:11 -07003289 RemoteViews button = new BuilderRemoteViews(mContext.getApplicationInfo(),
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003290 tombstone ? getActionTombstoneLayoutResource()
3291 : getActionLayoutResource());
Dan Sandler68079d52015-07-22 10:45:30 -04003292 final Icon ai = action.getIcon();
Dan Sandler912282e2015-07-28 22:49:30 -04003293 button.setTextViewCompoundDrawablesRelative(R.id.action0, ai, null, null, null);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003294 button.setTextViewText(R.id.action0, processLegacyText(action.title));
Daniel Sandler8680bf82012-05-15 16:52:52 -04003295 if (!tombstone) {
Daniel Sandlere5518842012-05-10 16:20:40 -04003296 button.setOnClickPendingIntent(R.id.action0, action.actionIntent);
Daniel Sandlere5518842012-05-10 16:20:40 -04003297 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003298 button.setContentDescription(R.id.action0, action.title);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003299 processLegacyAction(action, button);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003300 return button;
3301 }
3302
Joe Onoratocb109a02011-01-18 17:57:41 -08003303 /**
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003304 * @return Whether we are currently building a notification from a legacy (an app that
Alan Viverette3cb07a462014-06-06 14:19:53 -07003305 * doesn't create material notifications by itself) app.
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003306 */
3307 private boolean isLegacy() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003308 return getColorUtil() != null;
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003309 }
3310
3311 private void processLegacyAction(Action action, RemoteViews button) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003312 if (!isLegacy() || getColorUtil().isGrayscaleIcon(mContext, action.getIcon())) {
Christoph Studer239f8352014-08-25 15:13:18 +02003313 button.setTextViewCompoundDrawablesRelativeColorFilter(R.id.action0, 0,
Alan Viverette4a357cd2015-03-18 18:37:18 -07003314 mContext.getColor(R.color.notification_action_color_filter),
Christoph Studer239f8352014-08-25 15:13:18 +02003315 PorterDuff.Mode.MULTIPLY);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003316 }
3317 }
3318
3319 private CharSequence processLegacyText(CharSequence charSequence) {
3320 if (isLegacy()) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003321 return getColorUtil().invertCharSequenceColors(charSequence);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003322 } else {
3323 return charSequence;
3324 }
3325 }
3326
Dan Sandler26e81cf2014-05-06 10:01:27 -04003327 /**
3328 * Apply any necessary background to smallIcons being used in the largeIcon spot.
3329 */
Dan Sandlerd63f9322015-05-06 15:18:49 -04003330 private void processSmallIconAsLarge(Icon largeIcon, RemoteViews contentView) {
Jorim Jaggi92df1f22014-12-16 19:44:41 +01003331 if (!isLegacy()) {
3332 contentView.setDrawableParameters(R.id.icon, false, -1,
3333 0xFFFFFFFF,
3334 PorterDuff.Mode.SRC_ATOP, -1);
Dan Sandler26e81cf2014-05-06 10:01:27 -04003335 applyLargeIconBackground(contentView);
Dan Sandlerd63f9322015-05-06 15:18:49 -04003336 } else {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003337 if (getColorUtil().isGrayscaleIcon(mContext, largeIcon)) {
Dan Sandlerd63f9322015-05-06 15:18:49 -04003338 applyLargeIconBackground(contentView);
3339 }
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003340 }
3341 }
3342
Dan Sandler26e81cf2014-05-06 10:01:27 -04003343 /**
3344 * Apply any necessary background to a largeIcon if it's a fake smallIcon (that is,
3345 * if it's grayscale).
3346 */
3347 // TODO: also check bounds, transparency, that sort of thing.
Dan Sandlerd63f9322015-05-06 15:18:49 -04003348 private void processLargeLegacyIcon(Icon largeIcon, RemoteViews contentView) {
3349 if (largeIcon != null && isLegacy()
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003350 && getColorUtil().isGrayscaleIcon(mContext, largeIcon)) {
Dan Sandler26e81cf2014-05-06 10:01:27 -04003351 applyLargeIconBackground(contentView);
Dan Sandler190d58d2014-05-15 09:33:39 -04003352 } else {
3353 removeLargeIconBackground(contentView);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003354 }
3355 }
3356
Dan Sandler26e81cf2014-05-06 10:01:27 -04003357 /**
3358 * Add a colored circle behind the largeIcon slot.
3359 */
3360 private void applyLargeIconBackground(RemoteViews contentView) {
3361 contentView.setInt(R.id.icon, "setBackgroundResource",
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003362 R.drawable.notification_icon_legacy_bg);
Dan Sandler26e81cf2014-05-06 10:01:27 -04003363
3364 contentView.setDrawableParameters(
3365 R.id.icon,
3366 true,
3367 -1,
Jorim Jaggi74419312014-06-10 20:57:21 +02003368 resolveColor(),
Dan Sandler26e81cf2014-05-06 10:01:27 -04003369 PorterDuff.Mode.SRC_ATOP,
3370 -1);
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003371
3372 int padding = mContext.getResources().getDimensionPixelSize(
3373 R.dimen.notification_large_icon_circle_padding);
3374 contentView.setViewPadding(R.id.icon, padding, padding, padding, padding);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003375 }
3376
Dan Sandler190d58d2014-05-15 09:33:39 -04003377 private void removeLargeIconBackground(RemoteViews contentView) {
3378 contentView.setInt(R.id.icon, "setBackgroundResource", 0);
3379 }
3380
Dan Sandler26e81cf2014-05-06 10:01:27 -04003381 /**
3382 * Recolor small icons when used in the R.id.right_icon slot.
3383 */
Dan Sandlerd63f9322015-05-06 15:18:49 -04003384 private void processSmallRightIcon(Icon smallIcon, RemoteViews contentView) {
Jorim Jaggi92df1f22014-12-16 19:44:41 +01003385 if (!isLegacy()) {
Dan Sandler190d58d2014-05-15 09:33:39 -04003386 contentView.setDrawableParameters(R.id.right_icon, false, -1,
3387 0xFFFFFFFF,
3388 PorterDuff.Mode.SRC_ATOP, -1);
Jorim Jaggi92df1f22014-12-16 19:44:41 +01003389 }
Dan Sandlerd63f9322015-05-06 15:18:49 -04003390 final boolean gray = isLegacy()
3391 && smallIcon.getType() == Icon.TYPE_RESOURCE
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003392 && getColorUtil().isGrayscaleIcon(mContext, smallIcon.getResId());
Dan Sandlerd63f9322015-05-06 15:18:49 -04003393 if (!isLegacy() || gray) {
Dan Sandler190d58d2014-05-15 09:33:39 -04003394 contentView.setInt(R.id.right_icon,
3395 "setBackgroundResource",
3396 R.drawable.notification_icon_legacy_bg);
3397
3398 contentView.setDrawableParameters(
3399 R.id.right_icon,
3400 true,
3401 -1,
Jorim Jaggi74419312014-06-10 20:57:21 +02003402 resolveColor(),
Dan Sandler190d58d2014-05-15 09:33:39 -04003403 PorterDuff.Mode.SRC_ATOP,
3404 -1);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003405 }
3406 }
3407
Julia Reynolds10ee1fc2015-11-09 11:04:55 -05003408 private void sanitizeColor() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003409 if (mN.color != COLOR_DEFAULT) {
3410 mN.color |= 0xFF000000; // no alpha for custom colors
Jorim Jaggi74419312014-06-10 20:57:21 +02003411 }
Jorim Jaggi74419312014-06-10 20:57:21 +02003412 }
3413
Dan Sandler26e81cf2014-05-06 10:01:27 -04003414 private int resolveColor() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003415 if (mN.color == COLOR_DEFAULT) {
Alan Viverette4a357cd2015-03-18 18:37:18 -07003416 return mContext.getColor(R.color.notification_icon_bg_color);
Dan Sandler26e81cf2014-05-06 10:01:27 -04003417 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003418 return mN.color;
Dan Sandler26e81cf2014-05-06 10:01:27 -04003419 }
3420
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003421 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003422 * Apply the unstyled operations and return a new {@link Notification} object.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04003423 * @hide
Joe Onoratocb109a02011-01-18 17:57:41 -08003424 */
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04003425 public Notification buildUnstyled() {
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003426 if (mActions.size() > 0) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003427 mN.actions = new Action[mActions.size()];
3428 mActions.toArray(mN.actions);
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003429 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003430 if (!mPersonList.isEmpty()) {
3431 mN.extras.putStringArray(EXTRA_PEOPLE,
3432 mPersonList.toArray(new String[mPersonList.size()]));
Dan Sandler0bf2ed82013-12-21 23:33:41 -06003433 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003434 return mN;
Joe Onorato46439ce2010-11-19 13:56:21 -08003435 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003436
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003437 public static Notification.Builder recoverBuilder(Context context, Notification n) {
Christoph Studer4600f9b2014-07-22 22:44:43 +02003438 // Re-create notification context so we can access app resources.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003439 ApplicationInfo applicationInfo = n.extras.getParcelable(
3440 EXTRA_BUILDER_APPLICATION_INFO);
Christoph Studer4600f9b2014-07-22 22:44:43 +02003441 Context builderContext;
Julia Reynoldsda303542015-11-23 14:00:20 -05003442 if (applicationInfo != null) {
3443 try {
3444 builderContext = context.createApplicationContext(applicationInfo,
3445 Context.CONTEXT_RESTRICTED);
3446 } catch (NameNotFoundException e) {
3447 Log.e(TAG, "ApplicationInfo " + applicationInfo + " not found");
3448 builderContext = context; // try with our context
3449 }
3450 } else {
3451 builderContext = context; // try with given context
Christoph Studer4600f9b2014-07-22 22:44:43 +02003452 }
3453
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003454 return new Builder(builderContext, n);
Christoph Studer4600f9b2014-07-22 22:44:43 +02003455 }
3456
3457 private static Class<? extends Style> getNotificationStyleClass(String templateClass) {
3458 Class<? extends Style>[] classes = new Class[]{
3459 BigTextStyle.class, BigPictureStyle.class, InboxStyle.class, MediaStyle.class};
3460 for (Class<? extends Style> innerClass : classes) {
3461 if (templateClass.equals(innerClass.getName())) {
3462 return innerClass;
3463 }
3464 }
3465 return null;
3466 }
3467
3468 private void setBuilderContentView(Notification n, RemoteViews contentView) {
3469 n.contentView = contentView;
Christoph Studer4600f9b2014-07-22 22:44:43 +02003470 }
3471
3472 private void setBuilderBigContentView(Notification n, RemoteViews bigContentView) {
3473 n.bigContentView = bigContentView;
Christoph Studer4600f9b2014-07-22 22:44:43 +02003474 }
3475
3476 private void setBuilderHeadsUpContentView(Notification n,
3477 RemoteViews headsUpContentView) {
3478 n.headsUpContentView = headsUpContentView;
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003479 }
3480
3481 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003482 * @deprecated Use {@link #build()} instead.
3483 */
3484 @Deprecated
3485 public Notification getNotification() {
3486 return build();
3487 }
3488
3489 /**
3490 * Combine all of the options that have been set and return a new {@link Notification}
3491 * object.
3492 */
3493 public Notification build() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003494 // first, add any extras from the calling code
3495 if (mUserExtras != null) {
3496 mN.extras = getAllExtras();
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07003497 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003498
3499 // lazy stuff from mContext; see comment in Builder(Context, Notification)
Julia Reynoldsda303542015-11-23 14:00:20 -05003500 Notification.addFieldsFromContext(mContext, mN);
Christoph Studer943aa672014-08-03 20:31:16 +02003501
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003502 buildUnstyled();
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003503
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003504 if (mStyle != null) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003505 mStyle.buildStyled(mN);
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003506 }
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003507
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003508 return mN;
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003509 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05003510
3511 /**
3512 * Apply this Builder to an existing {@link Notification} object.
3513 *
3514 * @hide
3515 */
3516 public Notification buildInto(Notification n) {
Daniel Sandler1a497d32013-04-18 14:52:45 -04003517 build().cloneInto(n, true);
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05003518 return n;
3519 }
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003520
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003521 private int getBaseLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07003522 return R.layout.notification_template_material_base;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003523 }
3524
3525 private int getBigBaseLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07003526 return R.layout.notification_template_material_big_base;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003527 }
3528
3529 private int getBigPictureLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07003530 return R.layout.notification_template_material_big_picture;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003531 }
3532
3533 private int getBigTextLayoutResource() {
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003534 return R.layout.notification_template_material_big_text;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003535 }
3536
3537 private int getInboxLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07003538 return R.layout.notification_template_material_inbox;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003539 }
3540
3541 private int getActionLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07003542 return R.layout.notification_material_action;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003543 }
3544
3545 private int getActionTombstoneLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07003546 return R.layout.notification_material_action_tombstone;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003547 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003548 }
3549
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003550 /**
3551 * An object that can apply a rich notification style to a {@link Notification.Builder}
3552 * object.
3553 */
Griff Hazendfcb0802014-02-11 12:00:00 -08003554 public static abstract class Style {
Chris Wrend6297db2012-05-03 16:20:13 -04003555 private CharSequence mBigContentTitle;
Jorim Jaggi457a10d2014-09-08 16:18:23 +02003556
3557 /**
3558 * @hide
3559 */
3560 protected CharSequence mSummaryText = null;
3561
3562 /**
3563 * @hide
3564 */
3565 protected boolean mSummaryTextSet = false;
Chris Wrend6297db2012-05-03 16:20:13 -04003566
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003567 protected Builder mBuilder;
3568
Chris Wrend6297db2012-05-03 16:20:13 -04003569 /**
3570 * Overrides ContentTitle in the big form of the template.
3571 * This defaults to the value passed to setContentTitle().
3572 */
3573 protected void internalSetBigContentTitle(CharSequence title) {
3574 mBigContentTitle = title;
3575 }
3576
3577 /**
3578 * Set the first line of text after the detail section in the big form of the template.
3579 */
3580 protected void internalSetSummaryText(CharSequence cs) {
3581 mSummaryText = cs;
Daniel Sandler619738c2012-06-07 16:33:08 -04003582 mSummaryTextSet = true;
Chris Wrend6297db2012-05-03 16:20:13 -04003583 }
3584
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003585 public void setBuilder(Builder builder) {
3586 if (mBuilder != builder) {
3587 mBuilder = builder;
Daniel Sandlerc08dea22012-06-28 08:35:24 -07003588 if (mBuilder != null) {
3589 mBuilder.setStyle(this);
3590 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003591 }
3592 }
3593
Chris Wrend6297db2012-05-03 16:20:13 -04003594 protected void checkBuilder() {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003595 if (mBuilder == null) {
3596 throw new IllegalArgumentException("Style requires a valid Builder object");
3597 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003598 }
Chris Wrend6297db2012-05-03 16:20:13 -04003599
3600 protected RemoteViews getStandardView(int layoutId) {
3601 checkBuilder();
3602
Christoph Studer4600f9b2014-07-22 22:44:43 +02003603 // Nasty.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003604 CharSequence oldBuilderContentTitle =
3605 mBuilder.getAllExtras().getCharSequence(EXTRA_TITLE);
Chris Wrend6297db2012-05-03 16:20:13 -04003606 if (mBigContentTitle != null) {
3607 mBuilder.setContentTitle(mBigContentTitle);
3608 }
3609
Chris Wrend6297db2012-05-03 16:20:13 -04003610 RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(layoutId);
3611
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003612 mBuilder.getAllExtras().putCharSequence(EXTRA_TITLE, oldBuilderContentTitle);
Christoph Studer4600f9b2014-07-22 22:44:43 +02003613
Chris Wrend6297db2012-05-03 16:20:13 -04003614 if (mBigContentTitle != null && mBigContentTitle.equals("")) {
3615 contentView.setViewVisibility(R.id.line1, View.GONE);
Chris Wren67dc9a02012-05-16 01:03:20 -04003616 } else {
3617 contentView.setViewVisibility(R.id.line1, View.VISIBLE);
Chris Wrend6297db2012-05-03 16:20:13 -04003618 }
3619
Daniel Sandler619738c2012-06-07 16:33:08 -04003620 // The last line defaults to the subtext, but can be replaced by mSummaryText
3621 final CharSequence overflowText =
3622 mSummaryTextSet ? mSummaryText
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003623 : mBuilder.getAllExtras().getCharSequence(EXTRA_SUB_TEXT);
Daniel Sandler619738c2012-06-07 16:33:08 -04003624 if (overflowText != null) {
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003625 contentView.setTextViewText(R.id.text, mBuilder.processLegacyText(overflowText));
Daniel Sandler619738c2012-06-07 16:33:08 -04003626 contentView.setViewVisibility(R.id.overflow_divider, View.VISIBLE);
Daniel Sandler9f7936a2012-05-21 16:14:28 -04003627 contentView.setViewVisibility(R.id.line3, View.VISIBLE);
Daniel Sandler916ad912012-06-13 12:17:07 -04003628 } else {
Kenny Guy98193ea2014-07-24 19:54:37 +01003629 // Clear text in case we use the line to show the profile badge.
3630 contentView.setTextViewText(R.id.text, "");
Daniel Sandler916ad912012-06-13 12:17:07 -04003631 contentView.setViewVisibility(R.id.overflow_divider, View.GONE);
3632 contentView.setViewVisibility(R.id.line3, View.GONE);
Chris Wrend6297db2012-05-03 16:20:13 -04003633 }
3634
3635 return contentView;
3636 }
3637
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003638 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003639 * Construct a Style-specific RemoteViews for the final 1U notification layout.
3640 * The default implementation has nothing additional to add.
3641 * @hide
3642 */
3643 public RemoteViews makeContentView() {
3644 return null;
3645 }
3646
3647 /**
3648 * Construct a Style-specific RemoteViews for the final big notification layout.
3649 * @hide
3650 */
3651 public RemoteViews makeBigContentView() {
3652 return null;
3653 }
3654
3655 /**
3656 * Construct a Style-specific RemoteViews for the final HUN layout.
3657 * @hide
3658 */
3659 public RemoteViews makeHeadsUpContentView() {
3660 return null;
3661 }
3662
3663 /**
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003664 * Changes the padding of the first line such that the big and small content view have the
3665 * same top padding.
3666 *
3667 * @hide
3668 */
3669 protected void applyTopPadding(RemoteViews contentView) {
3670 int topPadding = Builder.calculateTopPadding(mBuilder.mContext,
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003671 mBuilder.hasThreeLines(),
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003672 mBuilder.mContext.getResources().getConfiguration().fontScale);
3673 contentView.setViewPadding(R.id.line1, 0, topPadding, 0, 0);
3674 }
3675
3676 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003677 * Apply any style-specific extras to this notification before shipping it out.
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003678 * @hide
3679 */
3680 public void addExtras(Bundle extras) {
3681 if (mSummaryTextSet) {
3682 extras.putCharSequence(EXTRA_SUMMARY_TEXT, mSummaryText);
3683 }
3684 if (mBigContentTitle != null) {
3685 extras.putCharSequence(EXTRA_TITLE_BIG, mBigContentTitle);
3686 }
Chris Wren91ad5632013-06-05 15:05:57 -04003687 extras.putString(EXTRA_TEMPLATE, this.getClass().getName());
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003688 }
3689
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04003690 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003691 * Reconstruct the internal state of this Style object from extras.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04003692 * @hide
3693 */
Christoph Studer4600f9b2014-07-22 22:44:43 +02003694 protected void restoreFromExtras(Bundle extras) {
3695 if (extras.containsKey(EXTRA_SUMMARY_TEXT)) {
3696 mSummaryText = extras.getCharSequence(EXTRA_SUMMARY_TEXT);
3697 mSummaryTextSet = true;
3698 }
3699 if (extras.containsKey(EXTRA_TITLE_BIG)) {
3700 mBigContentTitle = extras.getCharSequence(EXTRA_TITLE_BIG);
3701 }
3702 }
3703
3704
3705 /**
3706 * @hide
3707 */
3708 public Notification buildStyled(Notification wip) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003709 addExtras(wip.extras);
Christoph Studer4600f9b2014-07-22 22:44:43 +02003710 return wip;
3711 }
3712
Daniel Sandler0ec46202015-06-24 01:27:05 -04003713 /**
3714 * @hide
3715 */
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07003716 public void purgeResources() {}
3717
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04003718 /**
3719 * Calls {@link android.app.Notification.Builder#build()} on the Builder this Style is
3720 * attached to.
3721 *
3722 * @return the fully constructed Notification.
3723 */
3724 public Notification build() {
3725 checkBuilder();
3726 return mBuilder.build();
3727 }
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02003728
3729 /**
3730 * @hide
3731 * @return true if the style positions the progress bar on the second line; false if the
3732 * style hides the progress bar
3733 */
3734 protected boolean hasProgress() {
3735 return true;
3736 }
Joe Onorato46439ce2010-11-19 13:56:21 -08003737 }
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003738
3739 /**
Daniel Sandler4dfbe832012-04-11 14:51:46 -04003740 * Helper class for generating large-format notifications that include a large image attachment.
Joe Malin8d40d042012-11-05 11:36:40 -08003741 *
Robert Ly91c5ce32014-06-08 15:37:00 -07003742 * Here's how you'd set the <code>BigPictureStyle</code> on a notification:
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003743 * <pre class="prettyprint">
Robert Ly91c5ce32014-06-08 15:37:00 -07003744 * Notification notif = new Notification.Builder(mContext)
3745 * .setContentTitle(&quot;New photo from &quot; + sender.toString())
3746 * .setContentText(subject)
3747 * .setSmallIcon(R.drawable.new_post)
3748 * .setLargeIcon(aBitmap)
3749 * .setStyle(new Notification.BigPictureStyle()
3750 * .bigPicture(aBigBitmap))
3751 * .build();
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003752 * </pre>
Joe Malin8d40d042012-11-05 11:36:40 -08003753 *
Daniel Sandler4dfbe832012-04-11 14:51:46 -04003754 * @see Notification#bigContentView
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003755 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003756 public static class BigPictureStyle extends Style {
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003757 private Bitmap mPicture;
Dan Sandlerd63f9322015-05-06 15:18:49 -04003758 private Icon mBigLargeIcon;
Chris Wren3745a3d2012-05-22 15:11:52 -04003759 private boolean mBigLargeIconSet = false;
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003760
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003761 public BigPictureStyle() {
3762 }
3763
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003764 public BigPictureStyle(Builder builder) {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003765 setBuilder(builder);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003766 }
3767
Chris Wrend6297db2012-05-03 16:20:13 -04003768 /**
3769 * Overrides ContentTitle in the big form of the template.
3770 * This defaults to the value passed to setContentTitle().
3771 */
3772 public BigPictureStyle setBigContentTitle(CharSequence title) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04003773 internalSetBigContentTitle(safeCharSequence(title));
Chris Wrend6297db2012-05-03 16:20:13 -04003774 return this;
3775 }
3776
3777 /**
3778 * Set the first line of text after the detail section in the big form of the template.
3779 */
3780 public BigPictureStyle setSummaryText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04003781 internalSetSummaryText(safeCharSequence(cs));
Chris Wrend6297db2012-05-03 16:20:13 -04003782 return this;
3783 }
3784
Chris Wren0bd664d2012-08-01 13:56:56 -04003785 /**
3786 * Provide the bitmap to be used as the payload for the BigPicture notification.
3787 */
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003788 public BigPictureStyle bigPicture(Bitmap b) {
3789 mPicture = b;
3790 return this;
3791 }
3792
Chris Wren3745a3d2012-05-22 15:11:52 -04003793 /**
Chris Wren3745a3d2012-05-22 15:11:52 -04003794 * Override the large icon when the big notification is shown.
3795 */
3796 public BigPictureStyle bigLargeIcon(Bitmap b) {
Dan Sandlerd63f9322015-05-06 15:18:49 -04003797 return bigLargeIcon(b != null ? Icon.createWithBitmap(b) : null);
3798 }
3799
3800 /**
3801 * Override the large icon when the big notification is shown.
3802 */
3803 public BigPictureStyle bigLargeIcon(Icon icon) {
Chris Wren3745a3d2012-05-22 15:11:52 -04003804 mBigLargeIconSet = true;
Dan Sandlerd63f9322015-05-06 15:18:49 -04003805 mBigLargeIcon = icon;
Chris Wren3745a3d2012-05-22 15:11:52 -04003806 return this;
3807 }
3808
Riley Andrews0394a0c2015-11-03 23:36:52 -08003809 /** @hide */
3810 public static final int MIN_ASHMEM_BITMAP_SIZE = 128 * (1 << 10);
3811
Daniel Sandler0ec46202015-06-24 01:27:05 -04003812 /**
3813 * @hide
3814 */
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07003815 @Override
3816 public void purgeResources() {
3817 super.purgeResources();
Riley Andrews8cee7c12015-11-01 23:36:04 -08003818 if (mPicture != null &&
3819 mPicture.isMutable() &&
Riley Andrews0394a0c2015-11-03 23:36:52 -08003820 mPicture.getAllocationByteCount() >= MIN_ASHMEM_BITMAP_SIZE) {
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07003821 mPicture = mPicture.createAshmemBitmap();
3822 }
3823 if (mBigLargeIcon != null) {
3824 mBigLargeIcon.convertToAshmem();
3825 }
3826 }
Christoph Studer5c510ee2014-12-15 16:32:27 +01003827
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003828 /**
3829 * @hide
3830 */
3831 public RemoteViews makeBigContentView() {
3832 // Replace mN.mLargeIcon with mBigLargeIcon if mBigLargeIconSet
Christoph Studer5c510ee2014-12-15 16:32:27 +01003833 // This covers the following cases:
3834 // 1. mBigLargeIconSet -> mBigLargeIcon (null or non-null) applies, overrides
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003835 // mN.mLargeIcon
3836 // 2. !mBigLargeIconSet -> mN.mLargeIcon applies
Dan Sandlerd63f9322015-05-06 15:18:49 -04003837 Icon oldLargeIcon = null;
Christoph Studer5c510ee2014-12-15 16:32:27 +01003838 if (mBigLargeIconSet) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003839 oldLargeIcon = mBuilder.mN.mLargeIcon;
3840 mBuilder.mN.mLargeIcon = mBigLargeIcon;
Christoph Studer5c510ee2014-12-15 16:32:27 +01003841 }
3842
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003843 RemoteViews contentView = getStandardView(mBuilder.getBigPictureLayoutResource());
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003844
Christoph Studer5c510ee2014-12-15 16:32:27 +01003845 if (mBigLargeIconSet) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003846 mBuilder.mN.mLargeIcon = oldLargeIcon;
Christoph Studer5c510ee2014-12-15 16:32:27 +01003847 }
3848
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003849 contentView.setImageViewBitmap(R.id.big_picture, mPicture);
3850
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003851 applyTopPadding(contentView);
3852
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003853 boolean twoTextLines = mBuilder.getAllExtras().getCharSequence(EXTRA_SUB_TEXT) != null
3854 && mBuilder.getAllExtras().getCharSequence(EXTRA_TEXT) != null;
Kenny Guy98193ea2014-07-24 19:54:37 +01003855 mBuilder.addProfileBadge(contentView,
3856 twoTextLines ? R.id.profile_badge_line2 : R.id.profile_badge_line3);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003857 return contentView;
3858 }
3859
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003860 /**
3861 * @hide
3862 */
3863 public void addExtras(Bundle extras) {
3864 super.addExtras(extras);
3865
3866 if (mBigLargeIconSet) {
3867 extras.putParcelable(EXTRA_LARGE_ICON_BIG, mBigLargeIcon);
3868 }
3869 extras.putParcelable(EXTRA_PICTURE, mPicture);
3870 }
3871
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04003872 /**
3873 * @hide
3874 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003875 @Override
Christoph Studer4600f9b2014-07-22 22:44:43 +02003876 protected void restoreFromExtras(Bundle extras) {
3877 super.restoreFromExtras(extras);
3878
3879 if (extras.containsKey(EXTRA_LARGE_ICON_BIG)) {
Christoph Studer5c510ee2014-12-15 16:32:27 +01003880 mBigLargeIconSet = true;
Christoph Studer4600f9b2014-07-22 22:44:43 +02003881 mBigLargeIcon = extras.getParcelable(EXTRA_LARGE_ICON_BIG);
Chris Wren3745a3d2012-05-22 15:11:52 -04003882 }
Christoph Studer4600f9b2014-07-22 22:44:43 +02003883 mPicture = extras.getParcelable(EXTRA_PICTURE);
3884 }
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003885 }
3886
3887 /**
Daniel Sandler4dfbe832012-04-11 14:51:46 -04003888 * Helper class for generating large-format notifications that include a lot of text.
Joe Malin8d40d042012-11-05 11:36:40 -08003889 *
Robert Ly91c5ce32014-06-08 15:37:00 -07003890 * Here's how you'd set the <code>BigTextStyle</code> on a notification:
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003891 * <pre class="prettyprint">
Robert Ly91c5ce32014-06-08 15:37:00 -07003892 * Notification notif = new Notification.Builder(mContext)
3893 * .setContentTitle(&quot;New mail from &quot; + sender.toString())
3894 * .setContentText(subject)
3895 * .setSmallIcon(R.drawable.new_mail)
3896 * .setLargeIcon(aBitmap)
3897 * .setStyle(new Notification.BigTextStyle()
3898 * .bigText(aVeryLongString))
3899 * .build();
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003900 * </pre>
Joe Malin8d40d042012-11-05 11:36:40 -08003901 *
Daniel Sandler4dfbe832012-04-11 14:51:46 -04003902 * @see Notification#bigContentView
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003903 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003904 public static class BigTextStyle extends Style {
Jorim Jaggi457a10d2014-09-08 16:18:23 +02003905
3906 private static final int MAX_LINES = 13;
3907 private static final int LINES_CONSUMED_BY_ACTIONS = 3;
3908 private static final int LINES_CONSUMED_BY_SUMMARY = 2;
3909
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003910 private CharSequence mBigText;
3911
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003912 public BigTextStyle() {
3913 }
3914
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003915 public BigTextStyle(Builder builder) {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003916 setBuilder(builder);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003917 }
3918
Chris Wrend6297db2012-05-03 16:20:13 -04003919 /**
3920 * Overrides ContentTitle in the big form of the template.
3921 * This defaults to the value passed to setContentTitle().
3922 */
3923 public BigTextStyle setBigContentTitle(CharSequence title) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04003924 internalSetBigContentTitle(safeCharSequence(title));
Chris Wrend6297db2012-05-03 16:20:13 -04003925 return this;
3926 }
3927
3928 /**
3929 * Set the first line of text after the detail section in the big form of the template.
3930 */
3931 public BigTextStyle setSummaryText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04003932 internalSetSummaryText(safeCharSequence(cs));
Chris Wrend6297db2012-05-03 16:20:13 -04003933 return this;
3934 }
3935
Chris Wren0bd664d2012-08-01 13:56:56 -04003936 /**
3937 * Provide the longer text to be displayed in the big form of the
3938 * template in place of the content text.
3939 */
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003940 public BigTextStyle bigText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04003941 mBigText = safeCharSequence(cs);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003942 return this;
3943 }
3944
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003945 /**
3946 * @hide
3947 */
3948 public void addExtras(Bundle extras) {
3949 super.addExtras(extras);
3950
Christoph Studer4600f9b2014-07-22 22:44:43 +02003951 extras.putCharSequence(EXTRA_BIG_TEXT, mBigText);
3952 }
3953
3954 /**
3955 * @hide
3956 */
3957 @Override
3958 protected void restoreFromExtras(Bundle extras) {
3959 super.restoreFromExtras(extras);
3960
3961 mBigText = extras.getCharSequence(EXTRA_BIG_TEXT);
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003962 }
3963
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003964 /**
3965 * @hide
3966 */
3967 public RemoteViews makeBigContentView() {
Christoph Studer4600f9b2014-07-22 22:44:43 +02003968
3969 // Nasty
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003970 CharSequence oldBuilderContentText =
3971 mBuilder.getAllExtras().getCharSequence(EXTRA_TEXT);
3972 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, null);
Daniel Sandler916ad912012-06-13 12:17:07 -04003973
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003974 RemoteViews contentView = getStandardView(mBuilder.getBigTextLayoutResource());
Joe Malin8d40d042012-11-05 11:36:40 -08003975
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003976 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, oldBuilderContentText);
Christoph Studer4600f9b2014-07-22 22:44:43 +02003977
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003978 contentView.setTextViewText(R.id.big_text, mBuilder.processLegacyText(mBigText));
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003979 contentView.setViewVisibility(R.id.big_text, View.VISIBLE);
Jorim Jaggi457a10d2014-09-08 16:18:23 +02003980 contentView.setInt(R.id.big_text, "setMaxLines", calculateMaxLines());
Chris Wren3c5f92432012-05-04 16:31:17 -04003981 contentView.setViewVisibility(R.id.text2, View.GONE);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003982
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003983 applyTopPadding(contentView);
3984
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003985 mBuilder.shrinkLine3Text(contentView);
3986
Kenny Guy98193ea2014-07-24 19:54:37 +01003987 mBuilder.addProfileBadge(contentView, R.id.profile_badge_large_template);
3988
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003989 return contentView;
3990 }
3991
Jorim Jaggi457a10d2014-09-08 16:18:23 +02003992 private int calculateMaxLines() {
3993 int lineCount = MAX_LINES;
3994 boolean hasActions = mBuilder.mActions.size() > 0;
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003995 boolean hasSummary = (mSummaryTextSet ? mSummaryText
3996 : mBuilder.getAllExtras().getCharSequence(EXTRA_SUB_TEXT)) != null;
Jorim Jaggi457a10d2014-09-08 16:18:23 +02003997 if (hasActions) {
3998 lineCount -= LINES_CONSUMED_BY_ACTIONS;
3999 }
4000 if (hasSummary) {
4001 lineCount -= LINES_CONSUMED_BY_SUMMARY;
4002 }
4003
4004 // If we have less top padding at the top, we can fit less lines.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004005 if (!mBuilder.hasThreeLines()) {
Jorim Jaggi457a10d2014-09-08 16:18:23 +02004006 lineCount--;
4007 }
4008 return lineCount;
4009 }
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004010 }
Daniel Sandler879c5e02012-04-17 16:46:51 -04004011
4012 /**
4013 * Helper class for generating large-format notifications that include a list of (up to 5) strings.
Joe Malin8d40d042012-11-05 11:36:40 -08004014 *
Robert Ly91c5ce32014-06-08 15:37:00 -07004015 * Here's how you'd set the <code>InboxStyle</code> on a notification:
Daniel Sandler879c5e02012-04-17 16:46:51 -04004016 * <pre class="prettyprint">
Robert Ly91c5ce32014-06-08 15:37:00 -07004017 * Notification notif = new Notification.Builder(mContext)
4018 * .setContentTitle(&quot;5 New mails from &quot; + sender.toString())
4019 * .setContentText(subject)
4020 * .setSmallIcon(R.drawable.new_mail)
4021 * .setLargeIcon(aBitmap)
4022 * .setStyle(new Notification.InboxStyle()
4023 * .addLine(str1)
4024 * .addLine(str2)
4025 * .setContentTitle(&quot;&quot;)
4026 * .setSummaryText(&quot;+3 more&quot;))
4027 * .build();
Daniel Sandler879c5e02012-04-17 16:46:51 -04004028 * </pre>
Joe Malin8d40d042012-11-05 11:36:40 -08004029 *
Daniel Sandler879c5e02012-04-17 16:46:51 -04004030 * @see Notification#bigContentView
4031 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004032 public static class InboxStyle extends Style {
Daniel Sandler879c5e02012-04-17 16:46:51 -04004033 private ArrayList<CharSequence> mTexts = new ArrayList<CharSequence>(5);
4034
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004035 public InboxStyle() {
4036 }
4037
Daniel Sandler879c5e02012-04-17 16:46:51 -04004038 public InboxStyle(Builder builder) {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004039 setBuilder(builder);
Daniel Sandler879c5e02012-04-17 16:46:51 -04004040 }
4041
Chris Wrend6297db2012-05-03 16:20:13 -04004042 /**
4043 * Overrides ContentTitle in the big form of the template.
4044 * This defaults to the value passed to setContentTitle().
4045 */
4046 public InboxStyle setBigContentTitle(CharSequence title) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04004047 internalSetBigContentTitle(safeCharSequence(title));
Chris Wrend6297db2012-05-03 16:20:13 -04004048 return this;
4049 }
4050
4051 /**
4052 * Set the first line of text after the detail section in the big form of the template.
4053 */
4054 public InboxStyle setSummaryText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04004055 internalSetSummaryText(safeCharSequence(cs));
Chris Wrend6297db2012-05-03 16:20:13 -04004056 return this;
4057 }
4058
Chris Wren0bd664d2012-08-01 13:56:56 -04004059 /**
4060 * Append a line to the digest section of the Inbox notification.
4061 */
Daniel Sandler879c5e02012-04-17 16:46:51 -04004062 public InboxStyle addLine(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04004063 mTexts.add(safeCharSequence(cs));
Daniel Sandler879c5e02012-04-17 16:46:51 -04004064 return this;
4065 }
4066
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004067 /**
4068 * @hide
4069 */
4070 public void addExtras(Bundle extras) {
4071 super.addExtras(extras);
Christoph Studer4600f9b2014-07-22 22:44:43 +02004072
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004073 CharSequence[] a = new CharSequence[mTexts.size()];
4074 extras.putCharSequenceArray(EXTRA_TEXT_LINES, mTexts.toArray(a));
4075 }
4076
Christoph Studer4600f9b2014-07-22 22:44:43 +02004077 /**
4078 * @hide
4079 */
4080 @Override
4081 protected void restoreFromExtras(Bundle extras) {
4082 super.restoreFromExtras(extras);
4083
4084 mTexts.clear();
4085 if (extras.containsKey(EXTRA_TEXT_LINES)) {
4086 Collections.addAll(mTexts, extras.getCharSequenceArray(EXTRA_TEXT_LINES));
4087 }
4088 }
4089
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004090 /**
4091 * @hide
4092 */
4093 public RemoteViews makeBigContentView() {
Daniel Sandler619738c2012-06-07 16:33:08 -04004094 // Remove the content text so line3 disappears unless you have a summary
Christoph Studer4600f9b2014-07-22 22:44:43 +02004095 // Nasty
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004096 CharSequence oldBuilderContentText = mBuilder.mN.extras.getCharSequence(EXTRA_TEXT);
4097 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, null);
Christoph Studer4600f9b2014-07-22 22:44:43 +02004098
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004099 RemoteViews contentView = getStandardView(mBuilder.getInboxLayoutResource());
Daniel Sandler619738c2012-06-07 16:33:08 -04004100
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004101 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, oldBuilderContentText);
Christoph Studer4600f9b2014-07-22 22:44:43 +02004102
Chris Wrend6297db2012-05-03 16:20:13 -04004103 contentView.setViewVisibility(R.id.text2, View.GONE);
Daniel Sandler879c5e02012-04-17 16:46:51 -04004104
Chris Wrend6297db2012-05-03 16:20:13 -04004105 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 -04004106 R.id.inbox_text4, R.id.inbox_text5, R.id.inbox_text6};
Chris Wrend6297db2012-05-03 16:20:13 -04004107
Chris Wren4ed80d52012-05-17 09:30:03 -04004108 // Make sure all rows are gone in case we reuse a view.
4109 for (int rowId : rowIds) {
4110 contentView.setViewVisibility(rowId, View.GONE);
4111 }
4112
Jorim Jaggi445d3c02014-08-19 22:33:42 +02004113 final boolean largeText =
4114 mBuilder.mContext.getResources().getConfiguration().fontScale > 1f;
4115 final float subTextSize = mBuilder.mContext.getResources().getDimensionPixelSize(
4116 R.dimen.notification_subtext_size);
Daniel Sandler879c5e02012-04-17 16:46:51 -04004117 int i=0;
4118 while (i < mTexts.size() && i < rowIds.length) {
4119 CharSequence str = mTexts.get(i);
4120 if (str != null && !str.equals("")) {
4121 contentView.setViewVisibility(rowIds[i], View.VISIBLE);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004122 contentView.setTextViewText(rowIds[i], mBuilder.processLegacyText(str));
Jorim Jaggi445d3c02014-08-19 22:33:42 +02004123 if (largeText) {
4124 contentView.setTextViewTextSize(rowIds[i], TypedValue.COMPLEX_UNIT_PX,
4125 subTextSize);
4126 }
Daniel Sandler879c5e02012-04-17 16:46:51 -04004127 }
4128 i++;
4129 }
4130
Chris Wren683ab002012-09-20 10:35:54 -04004131 contentView.setViewVisibility(R.id.inbox_end_pad,
4132 mTexts.size() > 0 ? View.VISIBLE : View.GONE);
4133
4134 contentView.setViewVisibility(R.id.inbox_more,
4135 mTexts.size() > rowIds.length ? View.VISIBLE : View.GONE);
Chris Wren29bb6d92012-05-17 18:09:42 -04004136
Jorim Jaggi445d3c02014-08-19 22:33:42 +02004137 applyTopPadding(contentView);
4138
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02004139 mBuilder.shrinkLine3Text(contentView);
4140
Kenny Guy98193ea2014-07-24 19:54:37 +01004141 mBuilder.addProfileBadge(contentView, R.id.profile_badge_large_template);
4142
Daniel Sandler879c5e02012-04-17 16:46:51 -04004143 return contentView;
4144 }
Daniel Sandler879c5e02012-04-17 16:46:51 -04004145 }
Dan Sandler842dd772014-05-15 09:36:47 -04004146
4147 /**
4148 * Notification style for media playback notifications.
4149 *
4150 * In the expanded form, {@link Notification#bigContentView}, up to 5
4151 * {@link Notification.Action}s specified with
Dan Sandler86647982015-05-13 23:41:13 -04004152 * {@link Notification.Builder#addAction(Action) addAction} will be
Dan Sandler842dd772014-05-15 09:36:47 -04004153 * shown as icon-only pushbuttons, suitable for transport controls. The Bitmap given to
4154 * {@link Notification.Builder#setLargeIcon(android.graphics.Bitmap) setLargeIcon()} will be
4155 * treated as album artwork.
4156 *
4157 * Unlike the other styles provided here, MediaStyle can also modify the standard-size
4158 * {@link Notification#contentView}; by providing action indices to
Christoph Studerfde6f4d2014-12-12 13:23:26 +01004159 * {@link #setShowActionsInCompactView(int...)} you can promote up to 3 actions to be displayed
Dan Sandler842dd772014-05-15 09:36:47 -04004160 * in the standard view alongside the usual content.
4161 *
Bryan Mawhinney6be8de32014-07-18 10:35:12 +01004162 * Notifications created with MediaStyle will have their category set to
4163 * {@link Notification#CATEGORY_TRANSPORT CATEGORY_TRANSPORT} unless you set a different
4164 * category using {@link Notification.Builder#setCategory(String) setCategory()}.
4165 *
Jeff Browndba34ba2014-06-24 20:46:03 -07004166 * Finally, if you attach a {@link android.media.session.MediaSession.Token} using
4167 * {@link android.app.Notification.MediaStyle#setMediaSession(MediaSession.Token)},
Dan Sandler842dd772014-05-15 09:36:47 -04004168 * the System UI can identify this as a notification representing an active media session
4169 * and respond accordingly (by showing album artwork in the lockscreen, for example).
4170 *
4171 * To use this style with your Notification, feed it to
4172 * {@link Notification.Builder#setStyle(android.app.Notification.Style)} like so:
4173 * <pre class="prettyprint">
4174 * Notification noti = new Notification.Builder()
4175 * .setSmallIcon(R.drawable.ic_stat_player)
Christoph Studere935fe92014-11-24 14:18:06 +01004176 * .setContentTitle(&quot;Track title&quot;)
4177 * .setContentText(&quot;Artist - Album&quot;)
4178 * .setLargeIcon(albumArtBitmap))
Bryan Mawhinney6be8de32014-07-18 10:35:12 +01004179 * .setStyle(<b>new Notification.MediaStyle()</b>
4180 * .setMediaSession(mySession))
Dan Sandler842dd772014-05-15 09:36:47 -04004181 * .build();
4182 * </pre>
4183 *
4184 * @see Notification#bigContentView
4185 */
4186 public static class MediaStyle extends Style {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004187 static final int MAX_MEDIA_BUTTONS_IN_COMPACT = 3;
Dan Sandler842dd772014-05-15 09:36:47 -04004188 static final int MAX_MEDIA_BUTTONS = 5;
4189
4190 private int[] mActionsToShowInCompact = null;
Jeff Browndba34ba2014-06-24 20:46:03 -07004191 private MediaSession.Token mToken;
Dan Sandler842dd772014-05-15 09:36:47 -04004192
4193 public MediaStyle() {
4194 }
4195
4196 public MediaStyle(Builder builder) {
4197 setBuilder(builder);
4198 }
4199
4200 /**
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004201 * 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 -04004202 * notification view.
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004203 *
4204 * @param actions the indices of the actions to show in the compact notification view
Dan Sandler842dd772014-05-15 09:36:47 -04004205 */
4206 public MediaStyle setShowActionsInCompactView(int...actions) {
4207 mActionsToShowInCompact = actions;
4208 return this;
4209 }
4210
4211 /**
Jeff Browndba34ba2014-06-24 20:46:03 -07004212 * Attach a {@link android.media.session.MediaSession.Token} to this Notification
4213 * to provide additional playback information and control to the SystemUI.
Dan Sandler842dd772014-05-15 09:36:47 -04004214 */
Jeff Browndba34ba2014-06-24 20:46:03 -07004215 public MediaStyle setMediaSession(MediaSession.Token token) {
Dan Sandler842dd772014-05-15 09:36:47 -04004216 mToken = token;
4217 return this;
4218 }
4219
Christoph Studer4600f9b2014-07-22 22:44:43 +02004220 /**
4221 * @hide
4222 */
Dan Sandler842dd772014-05-15 09:36:47 -04004223 @Override
4224 public Notification buildStyled(Notification wip) {
Christoph Studer4600f9b2014-07-22 22:44:43 +02004225 super.buildStyled(wip);
Bryan Mawhinney6be8de32014-07-18 10:35:12 +01004226 if (wip.category == null) {
4227 wip.category = Notification.CATEGORY_TRANSPORT;
4228 }
Dan Sandler842dd772014-05-15 09:36:47 -04004229 return wip;
4230 }
4231
Christoph Studer4600f9b2014-07-22 22:44:43 +02004232 /**
4233 * @hide
4234 */
4235 @Override
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004236 public RemoteViews makeContentView() {
4237 return makeMediaContentView();
Christoph Studer4600f9b2014-07-22 22:44:43 +02004238 }
4239
4240 /**
4241 * @hide
4242 */
4243 @Override
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004244 public RemoteViews makeBigContentView() {
4245 return makeMediaBigContentView();
Christoph Studer4600f9b2014-07-22 22:44:43 +02004246 }
4247
Dan Sandler842dd772014-05-15 09:36:47 -04004248 /** @hide */
4249 @Override
4250 public void addExtras(Bundle extras) {
4251 super.addExtras(extras);
4252
4253 if (mToken != null) {
4254 extras.putParcelable(EXTRA_MEDIA_SESSION, mToken);
4255 }
Bryan Mawhinneye191f902014-07-22 12:50:09 +01004256 if (mActionsToShowInCompact != null) {
4257 extras.putIntArray(EXTRA_COMPACT_ACTIONS, mActionsToShowInCompact);
4258 }
Dan Sandler842dd772014-05-15 09:36:47 -04004259 }
4260
Christoph Studer4600f9b2014-07-22 22:44:43 +02004261 /**
4262 * @hide
4263 */
4264 @Override
4265 protected void restoreFromExtras(Bundle extras) {
4266 super.restoreFromExtras(extras);
4267
4268 if (extras.containsKey(EXTRA_MEDIA_SESSION)) {
4269 mToken = extras.getParcelable(EXTRA_MEDIA_SESSION);
4270 }
4271 if (extras.containsKey(EXTRA_COMPACT_ACTIONS)) {
4272 mActionsToShowInCompact = extras.getIntArray(EXTRA_COMPACT_ACTIONS);
4273 }
4274 }
4275
Dan Sandler842dd772014-05-15 09:36:47 -04004276 private RemoteViews generateMediaActionButton(Action action) {
4277 final boolean tombstone = (action.actionIntent == null);
Selim Cinekf33b1112015-07-15 17:45:11 -07004278 RemoteViews button = new BuilderRemoteViews(mBuilder.mContext.getApplicationInfo(),
Alan Viverette3cb07a462014-06-06 14:19:53 -07004279 R.layout.notification_material_media_action);
Dan Sandler68079d52015-07-22 10:45:30 -04004280 button.setImageViewIcon(R.id.action0, action.getIcon());
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004281 button.setDrawableParameters(R.id.action0, false, -1,
4282 0xFFFFFFFF,
4283 PorterDuff.Mode.SRC_ATOP, -1);
Dan Sandler842dd772014-05-15 09:36:47 -04004284 if (!tombstone) {
4285 button.setOnClickPendingIntent(R.id.action0, action.actionIntent);
4286 }
4287 button.setContentDescription(R.id.action0, action.title);
4288 return button;
4289 }
4290
4291 private RemoteViews makeMediaContentView() {
4292 RemoteViews view = mBuilder.applyStandardTemplate(
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004293 R.layout.notification_template_material_media, false /* hasProgress */);
Dan Sandler842dd772014-05-15 09:36:47 -04004294
4295 final int numActions = mBuilder.mActions.size();
4296 final int N = mActionsToShowInCompact == null
4297 ? 0
4298 : Math.min(mActionsToShowInCompact.length, MAX_MEDIA_BUTTONS_IN_COMPACT);
4299 if (N > 0) {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004300 view.removeAllViews(com.android.internal.R.id.media_actions);
Dan Sandler842dd772014-05-15 09:36:47 -04004301 for (int i = 0; i < N; i++) {
4302 if (i >= numActions) {
4303 throw new IllegalArgumentException(String.format(
4304 "setShowActionsInCompactView: action %d out of bounds (max %d)",
4305 i, numActions - 1));
4306 }
4307
4308 final Action action = mBuilder.mActions.get(mActionsToShowInCompact[i]);
4309 final RemoteViews button = generateMediaActionButton(action);
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004310 view.addView(com.android.internal.R.id.media_actions, button);
Dan Sandler842dd772014-05-15 09:36:47 -04004311 }
4312 }
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004313 styleText(view);
4314 hideRightIcon(view);
Dan Sandler842dd772014-05-15 09:36:47 -04004315 return view;
4316 }
4317
4318 private RemoteViews makeMediaBigContentView() {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004319 final int actionCount = Math.min(mBuilder.mActions.size(), MAX_MEDIA_BUTTONS);
4320 RemoteViews big = mBuilder.applyStandardTemplate(getBigLayoutResource(actionCount),
4321 false /* hasProgress */);
Dan Sandler842dd772014-05-15 09:36:47 -04004322
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004323 if (actionCount > 0) {
4324 big.removeAllViews(com.android.internal.R.id.media_actions);
4325 for (int i = 0; i < actionCount; i++) {
Dan Sandler842dd772014-05-15 09:36:47 -04004326 final RemoteViews button = generateMediaActionButton(mBuilder.mActions.get(i));
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004327 big.addView(com.android.internal.R.id.media_actions, button);
Dan Sandler842dd772014-05-15 09:36:47 -04004328 }
4329 }
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004330 styleText(big);
4331 hideRightIcon(big);
4332 applyTopPadding(big);
4333 big.setViewVisibility(android.R.id.progress, View.GONE);
Dan Sandler842dd772014-05-15 09:36:47 -04004334 return big;
4335 }
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004336
4337 private int getBigLayoutResource(int actionCount) {
4338 if (actionCount <= 3) {
4339 return R.layout.notification_template_material_big_media_narrow;
4340 } else {
4341 return R.layout.notification_template_material_big_media;
4342 }
4343 }
4344
4345 private void hideRightIcon(RemoteViews contentView) {
4346 contentView.setViewVisibility(R.id.right_icon, View.GONE);
4347 }
4348
4349 /**
4350 * Applies the special text colors for media notifications to all text views.
4351 */
4352 private void styleText(RemoteViews contentView) {
Alan Viverette4a357cd2015-03-18 18:37:18 -07004353 int primaryColor = mBuilder.mContext.getColor(
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004354 R.color.notification_media_primary_color);
Alan Viverette4a357cd2015-03-18 18:37:18 -07004355 int secondaryColor = mBuilder.mContext.getColor(
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004356 R.color.notification_media_secondary_color);
4357 contentView.setTextColor(R.id.title, primaryColor);
4358 if (mBuilder.showsTimeOrChronometer()) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004359 if (mBuilder.getAllExtras().getBoolean(EXTRA_SHOW_CHRONOMETER)) {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004360 contentView.setTextColor(R.id.chronometer, secondaryColor);
4361 } else {
4362 contentView.setTextColor(R.id.time, secondaryColor);
4363 }
4364 }
4365 contentView.setTextColor(R.id.text2, secondaryColor);
4366 contentView.setTextColor(R.id.text, secondaryColor);
4367 contentView.setTextColor(R.id.info, secondaryColor);
4368 }
4369
4370 /**
4371 * @hide
4372 */
4373 @Override
4374 protected boolean hasProgress() {
4375 return false;
4376 }
Dan Sandler842dd772014-05-15 09:36:47 -04004377 }
Griff Hazen61a9e862014-05-22 16:05:19 -07004378
Christoph Studer4600f9b2014-07-22 22:44:43 +02004379 // When adding a new Style subclass here, don't forget to update
4380 // Builder.getNotificationStyleClass.
4381
Griff Hazen61a9e862014-05-22 16:05:19 -07004382 /**
4383 * Extender interface for use with {@link Builder#extend}. Extenders may be used to add
4384 * metadata or change options on a notification builder.
4385 */
4386 public interface Extender {
4387 /**
4388 * Apply this extender to a notification builder.
4389 * @param builder the builder to be modified.
4390 * @return the build object for chaining.
4391 */
4392 public Builder extend(Builder builder);
4393 }
4394
4395 /**
4396 * Helper class to add wearable extensions to notifications.
4397 * <p class="note"> See
4398 * <a href="{@docRoot}wear/notifications/creating.html">Creating Notifications
4399 * for Android Wear</a> for more information on how to use this class.
4400 * <p>
4401 * To create a notification with wearable extensions:
4402 * <ol>
4403 * <li>Create a {@link android.app.Notification.Builder}, setting any desired
4404 * properties.
4405 * <li>Create a {@link android.app.Notification.WearableExtender}.
4406 * <li>Set wearable-specific properties using the
4407 * {@code add} and {@code set} methods of {@link android.app.Notification.WearableExtender}.
4408 * <li>Call {@link android.app.Notification.Builder#extend} to apply the extensions to a
4409 * notification.
4410 * <li>Post the notification to the notification system with the
4411 * {@code NotificationManager.notify(...)} methods.
4412 * </ol>
4413 *
4414 * <pre class="prettyprint">
4415 * Notification notif = new Notification.Builder(mContext)
4416 * .setContentTitle(&quot;New mail from &quot; + sender.toString())
4417 * .setContentText(subject)
4418 * .setSmallIcon(R.drawable.new_mail)
4419 * .extend(new Notification.WearableExtender()
4420 * .setContentIcon(R.drawable.new_mail))
4421 * .build();
4422 * NotificationManager notificationManger =
4423 * (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
4424 * notificationManger.notify(0, notif);</pre>
4425 *
4426 * <p>Wearable extensions can be accessed on an existing notification by using the
4427 * {@code WearableExtender(Notification)} constructor,
4428 * and then using the {@code get} methods to access values.
4429 *
4430 * <pre class="prettyprint">
4431 * Notification.WearableExtender wearableExtender = new Notification.WearableExtender(
4432 * notification);
Griff Hazen14f57992014-05-26 09:07:14 -07004433 * List&lt;Notification&gt; pages = wearableExtender.getPages();</pre>
Griff Hazen61a9e862014-05-22 16:05:19 -07004434 */
4435 public static final class WearableExtender implements Extender {
4436 /**
4437 * Sentinel value for an action index that is unset.
4438 */
4439 public static final int UNSET_ACTION_INDEX = -1;
4440
4441 /**
4442 * Size value for use with {@link #setCustomSizePreset} to show this notification with
4443 * default sizing.
4444 * <p>For custom display notifications created using {@link #setDisplayIntent},
Paul Soulosaa4f4bf2015-08-04 11:59:45 -07004445 * the default is {@link #SIZE_MEDIUM}. All other notifications size automatically based
Griff Hazen61a9e862014-05-22 16:05:19 -07004446 * on their content.
4447 */
4448 public static final int SIZE_DEFAULT = 0;
4449
4450 /**
4451 * Size value for use with {@link #setCustomSizePreset} to show this notification
4452 * with an extra small size.
4453 * <p>This value is only applicable for custom display notifications created using
4454 * {@link #setDisplayIntent}.
4455 */
4456 public static final int SIZE_XSMALL = 1;
4457
4458 /**
4459 * Size value for use with {@link #setCustomSizePreset} to show this notification
4460 * with a small size.
4461 * <p>This value is only applicable for custom display notifications created using
4462 * {@link #setDisplayIntent}.
4463 */
4464 public static final int SIZE_SMALL = 2;
4465
4466 /**
4467 * Size value for use with {@link #setCustomSizePreset} to show this notification
4468 * with a medium size.
4469 * <p>This value is only applicable for custom display notifications created using
4470 * {@link #setDisplayIntent}.
4471 */
4472 public static final int SIZE_MEDIUM = 3;
4473
4474 /**
4475 * Size value for use with {@link #setCustomSizePreset} to show this notification
4476 * with a large size.
4477 * <p>This value is only applicable for custom display notifications created using
4478 * {@link #setDisplayIntent}.
4479 */
4480 public static final int SIZE_LARGE = 4;
4481
Griff Hazend5f11f92014-05-27 15:40:09 -07004482 /**
4483 * Size value for use with {@link #setCustomSizePreset} to show this notification
4484 * full screen.
4485 * <p>This value is only applicable for custom display notifications created using
4486 * {@link #setDisplayIntent}.
4487 */
4488 public static final int SIZE_FULL_SCREEN = 5;
4489
Griff Hazen5f2edfc2014-09-29 16:28:44 -07004490 /**
4491 * Sentinel value for use with {@link #setHintScreenTimeout} to keep the screen on for a
4492 * short amount of time when this notification is displayed on the screen. This
4493 * is the default value.
4494 */
4495 public static final int SCREEN_TIMEOUT_SHORT = 0;
4496
4497 /**
4498 * Sentinel value for use with {@link #setHintScreenTimeout} to keep the screen on
4499 * for a longer amount of time when this notification is displayed on the screen.
4500 */
4501 public static final int SCREEN_TIMEOUT_LONG = -1;
4502
Griff Hazen61a9e862014-05-22 16:05:19 -07004503 /** Notification extra which contains wearable extensions */
4504 private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS";
4505
Pete Gastaf6781d2014-10-07 15:17:05 -04004506 // Keys within EXTRA_WEARABLE_EXTENSIONS for wearable options.
Griff Hazen61a9e862014-05-22 16:05:19 -07004507 private static final String KEY_ACTIONS = "actions";
4508 private static final String KEY_FLAGS = "flags";
4509 private static final String KEY_DISPLAY_INTENT = "displayIntent";
4510 private static final String KEY_PAGES = "pages";
4511 private static final String KEY_BACKGROUND = "background";
4512 private static final String KEY_CONTENT_ICON = "contentIcon";
4513 private static final String KEY_CONTENT_ICON_GRAVITY = "contentIconGravity";
4514 private static final String KEY_CONTENT_ACTION_INDEX = "contentActionIndex";
4515 private static final String KEY_CUSTOM_SIZE_PRESET = "customSizePreset";
4516 private static final String KEY_CUSTOM_CONTENT_HEIGHT = "customContentHeight";
4517 private static final String KEY_GRAVITY = "gravity";
Griff Hazen5f2edfc2014-09-29 16:28:44 -07004518 private static final String KEY_HINT_SCREEN_TIMEOUT = "hintScreenTimeout";
Griff Hazen61a9e862014-05-22 16:05:19 -07004519
4520 // Flags bitwise-ored to mFlags
4521 private static final int FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE = 0x1;
4522 private static final int FLAG_HINT_HIDE_ICON = 1 << 1;
4523 private static final int FLAG_HINT_SHOW_BACKGROUND_ONLY = 1 << 2;
4524 private static final int FLAG_START_SCROLL_BOTTOM = 1 << 3;
Griff Hazen5f2edfc2014-09-29 16:28:44 -07004525 private static final int FLAG_HINT_AVOID_BACKGROUND_CLIPPING = 1 << 4;
Griff Hazen61a9e862014-05-22 16:05:19 -07004526
4527 // Default value for flags integer
4528 private static final int DEFAULT_FLAGS = FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE;
4529
4530 private static final int DEFAULT_CONTENT_ICON_GRAVITY = Gravity.END;
4531 private static final int DEFAULT_GRAVITY = Gravity.BOTTOM;
4532
4533 private ArrayList<Action> mActions = new ArrayList<Action>();
4534 private int mFlags = DEFAULT_FLAGS;
4535 private PendingIntent mDisplayIntent;
4536 private ArrayList<Notification> mPages = new ArrayList<Notification>();
4537 private Bitmap mBackground;
4538 private int mContentIcon;
4539 private int mContentIconGravity = DEFAULT_CONTENT_ICON_GRAVITY;
4540 private int mContentActionIndex = UNSET_ACTION_INDEX;
4541 private int mCustomSizePreset = SIZE_DEFAULT;
4542 private int mCustomContentHeight;
4543 private int mGravity = DEFAULT_GRAVITY;
Griff Hazen5f2edfc2014-09-29 16:28:44 -07004544 private int mHintScreenTimeout;
Griff Hazen61a9e862014-05-22 16:05:19 -07004545
4546 /**
4547 * Create a {@link android.app.Notification.WearableExtender} with default
4548 * options.
4549 */
4550 public WearableExtender() {
4551 }
4552
4553 public WearableExtender(Notification notif) {
4554 Bundle wearableBundle = notif.extras.getBundle(EXTRA_WEARABLE_EXTENSIONS);
4555 if (wearableBundle != null) {
4556 List<Action> actions = wearableBundle.getParcelableArrayList(KEY_ACTIONS);
4557 if (actions != null) {
4558 mActions.addAll(actions);
4559 }
4560
4561 mFlags = wearableBundle.getInt(KEY_FLAGS, DEFAULT_FLAGS);
4562 mDisplayIntent = wearableBundle.getParcelable(KEY_DISPLAY_INTENT);
4563
4564 Notification[] pages = getNotificationArrayFromBundle(
4565 wearableBundle, KEY_PAGES);
4566 if (pages != null) {
4567 Collections.addAll(mPages, pages);
4568 }
4569
4570 mBackground = wearableBundle.getParcelable(KEY_BACKGROUND);
4571 mContentIcon = wearableBundle.getInt(KEY_CONTENT_ICON);
4572 mContentIconGravity = wearableBundle.getInt(KEY_CONTENT_ICON_GRAVITY,
4573 DEFAULT_CONTENT_ICON_GRAVITY);
4574 mContentActionIndex = wearableBundle.getInt(KEY_CONTENT_ACTION_INDEX,
4575 UNSET_ACTION_INDEX);
4576 mCustomSizePreset = wearableBundle.getInt(KEY_CUSTOM_SIZE_PRESET,
4577 SIZE_DEFAULT);
4578 mCustomContentHeight = wearableBundle.getInt(KEY_CUSTOM_CONTENT_HEIGHT);
4579 mGravity = wearableBundle.getInt(KEY_GRAVITY, DEFAULT_GRAVITY);
Griff Hazen5f2edfc2014-09-29 16:28:44 -07004580 mHintScreenTimeout = wearableBundle.getInt(KEY_HINT_SCREEN_TIMEOUT);
Griff Hazen61a9e862014-05-22 16:05:19 -07004581 }
4582 }
4583
4584 /**
4585 * Apply wearable extensions to a notification that is being built. This is typically
4586 * called by the {@link android.app.Notification.Builder#extend} method of
4587 * {@link android.app.Notification.Builder}.
4588 */
4589 @Override
4590 public Notification.Builder extend(Notification.Builder builder) {
4591 Bundle wearableBundle = new Bundle();
4592
4593 if (!mActions.isEmpty()) {
4594 wearableBundle.putParcelableArrayList(KEY_ACTIONS, mActions);
4595 }
4596 if (mFlags != DEFAULT_FLAGS) {
4597 wearableBundle.putInt(KEY_FLAGS, mFlags);
4598 }
4599 if (mDisplayIntent != null) {
4600 wearableBundle.putParcelable(KEY_DISPLAY_INTENT, mDisplayIntent);
4601 }
4602 if (!mPages.isEmpty()) {
4603 wearableBundle.putParcelableArray(KEY_PAGES, mPages.toArray(
4604 new Notification[mPages.size()]));
4605 }
4606 if (mBackground != null) {
4607 wearableBundle.putParcelable(KEY_BACKGROUND, mBackground);
4608 }
4609 if (mContentIcon != 0) {
4610 wearableBundle.putInt(KEY_CONTENT_ICON, mContentIcon);
4611 }
4612 if (mContentIconGravity != DEFAULT_CONTENT_ICON_GRAVITY) {
4613 wearableBundle.putInt(KEY_CONTENT_ICON_GRAVITY, mContentIconGravity);
4614 }
4615 if (mContentActionIndex != UNSET_ACTION_INDEX) {
4616 wearableBundle.putInt(KEY_CONTENT_ACTION_INDEX,
4617 mContentActionIndex);
4618 }
4619 if (mCustomSizePreset != SIZE_DEFAULT) {
4620 wearableBundle.putInt(KEY_CUSTOM_SIZE_PRESET, mCustomSizePreset);
4621 }
4622 if (mCustomContentHeight != 0) {
4623 wearableBundle.putInt(KEY_CUSTOM_CONTENT_HEIGHT, mCustomContentHeight);
4624 }
4625 if (mGravity != DEFAULT_GRAVITY) {
4626 wearableBundle.putInt(KEY_GRAVITY, mGravity);
4627 }
Griff Hazen5f2edfc2014-09-29 16:28:44 -07004628 if (mHintScreenTimeout != 0) {
4629 wearableBundle.putInt(KEY_HINT_SCREEN_TIMEOUT, mHintScreenTimeout);
4630 }
Griff Hazen61a9e862014-05-22 16:05:19 -07004631
4632 builder.getExtras().putBundle(EXTRA_WEARABLE_EXTENSIONS, wearableBundle);
4633 return builder;
4634 }
4635
4636 @Override
4637 public WearableExtender clone() {
4638 WearableExtender that = new WearableExtender();
4639 that.mActions = new ArrayList<Action>(this.mActions);
4640 that.mFlags = this.mFlags;
4641 that.mDisplayIntent = this.mDisplayIntent;
4642 that.mPages = new ArrayList<Notification>(this.mPages);
4643 that.mBackground = this.mBackground;
4644 that.mContentIcon = this.mContentIcon;
4645 that.mContentIconGravity = this.mContentIconGravity;
4646 that.mContentActionIndex = this.mContentActionIndex;
4647 that.mCustomSizePreset = this.mCustomSizePreset;
4648 that.mCustomContentHeight = this.mCustomContentHeight;
4649 that.mGravity = this.mGravity;
Griff Hazen5f2edfc2014-09-29 16:28:44 -07004650 that.mHintScreenTimeout = this.mHintScreenTimeout;
Griff Hazen61a9e862014-05-22 16:05:19 -07004651 return that;
4652 }
4653
4654 /**
4655 * Add a wearable action to this notification.
4656 *
4657 * <p>When wearable actions are added using this method, the set of actions that
4658 * show on a wearable device splits from devices that only show actions added
4659 * using {@link android.app.Notification.Builder#addAction}. This allows for customization
4660 * of which actions display on different devices.
4661 *
4662 * @param action the action to add to this notification
4663 * @return this object for method chaining
4664 * @see android.app.Notification.Action
4665 */
4666 public WearableExtender addAction(Action action) {
4667 mActions.add(action);
4668 return this;
4669 }
4670
4671 /**
4672 * Adds wearable actions to this notification.
4673 *
4674 * <p>When wearable actions are added using this method, the set of actions that
4675 * show on a wearable device splits from devices that only show actions added
4676 * using {@link android.app.Notification.Builder#addAction}. This allows for customization
4677 * of which actions display on different devices.
4678 *
4679 * @param actions the actions to add to this notification
4680 * @return this object for method chaining
4681 * @see android.app.Notification.Action
4682 */
4683 public WearableExtender addActions(List<Action> actions) {
4684 mActions.addAll(actions);
4685 return this;
4686 }
4687
4688 /**
4689 * Clear all wearable actions present on this builder.
4690 * @return this object for method chaining.
4691 * @see #addAction
4692 */
4693 public WearableExtender clearActions() {
4694 mActions.clear();
4695 return this;
4696 }
4697
4698 /**
4699 * Get the wearable actions present on this notification.
4700 */
4701 public List<Action> getActions() {
4702 return mActions;
4703 }
4704
4705 /**
4706 * Set an intent to launch inside of an activity view when displaying
Griff Hazen14f57992014-05-26 09:07:14 -07004707 * this notification. The {@link PendingIntent} provided should be for an activity.
4708 *
4709 * <pre class="prettyprint">
4710 * Intent displayIntent = new Intent(context, MyDisplayActivity.class);
4711 * PendingIntent displayPendingIntent = PendingIntent.getActivity(context,
4712 * 0, displayIntent, PendingIntent.FLAG_UPDATE_CURRENT);
4713 * Notification notif = new Notification.Builder(context)
4714 * .extend(new Notification.WearableExtender()
4715 * .setDisplayIntent(displayPendingIntent)
4716 * .setCustomSizePreset(Notification.WearableExtender.SIZE_MEDIUM))
4717 * .build();</pre>
4718 *
4719 * <p>The activity to launch needs to allow embedding, must be exported, and
Griff Hazen831ca9d2014-06-17 00:38:38 -07004720 * should have an empty task affinity. It is also recommended to use the device
4721 * default light theme.
Griff Hazen14f57992014-05-26 09:07:14 -07004722 *
4723 * <p>Example AndroidManifest.xml entry:
4724 * <pre class="prettyprint">
4725 * &lt;activity android:name=&quot;com.example.MyDisplayActivity&quot;
4726 * android:exported=&quot;true&quot;
4727 * android:allowEmbedded=&quot;true&quot;
Griff Hazen831ca9d2014-06-17 00:38:38 -07004728 * android:taskAffinity=&quot;&quot;
4729 * android:theme=&quot;@android:style/Theme.DeviceDefault.Light&quot; /&gt;</pre>
Griff Hazen61a9e862014-05-22 16:05:19 -07004730 *
4731 * @param intent the {@link PendingIntent} for an activity
4732 * @return this object for method chaining
4733 * @see android.app.Notification.WearableExtender#getDisplayIntent
4734 */
4735 public WearableExtender setDisplayIntent(PendingIntent intent) {
4736 mDisplayIntent = intent;
4737 return this;
4738 }
4739
4740 /**
4741 * Get the intent to launch inside of an activity view when displaying this
4742 * notification. This {@code PendingIntent} should be for an activity.
4743 */
4744 public PendingIntent getDisplayIntent() {
4745 return mDisplayIntent;
4746 }
4747
4748 /**
4749 * Add an additional page of content to display with this notification. The current
4750 * notification forms the first page, and pages added using this function form
4751 * subsequent pages. This field can be used to separate a notification into multiple
4752 * sections.
4753 *
4754 * @param page the notification to add as another page
4755 * @return this object for method chaining
4756 * @see android.app.Notification.WearableExtender#getPages
4757 */
4758 public WearableExtender addPage(Notification page) {
4759 mPages.add(page);
4760 return this;
4761 }
4762
4763 /**
4764 * Add additional pages of content to display with this notification. The current
4765 * notification forms the first page, and pages added using this function form
4766 * subsequent pages. This field can be used to separate a notification into multiple
4767 * sections.
4768 *
4769 * @param pages a list of notifications
4770 * @return this object for method chaining
4771 * @see android.app.Notification.WearableExtender#getPages
4772 */
4773 public WearableExtender addPages(List<Notification> pages) {
4774 mPages.addAll(pages);
4775 return this;
4776 }
4777
4778 /**
4779 * Clear all additional pages present on this builder.
4780 * @return this object for method chaining.
4781 * @see #addPage
4782 */
4783 public WearableExtender clearPages() {
4784 mPages.clear();
4785 return this;
4786 }
4787
4788 /**
4789 * Get the array of additional pages of content for displaying this notification. The
4790 * current notification forms the first page, and elements within this array form
4791 * subsequent pages. This field can be used to separate a notification into multiple
4792 * sections.
4793 * @return the pages for this notification
4794 */
4795 public List<Notification> getPages() {
4796 return mPages;
4797 }
4798
4799 /**
4800 * Set a background image to be displayed behind the notification content.
4801 * Contrary to the {@link android.app.Notification.BigPictureStyle}, this background
4802 * will work with any notification style.
4803 *
4804 * @param background the background bitmap
4805 * @return this object for method chaining
4806 * @see android.app.Notification.WearableExtender#getBackground
4807 */
4808 public WearableExtender setBackground(Bitmap background) {
4809 mBackground = background;
4810 return this;
4811 }
4812
4813 /**
4814 * Get a background image to be displayed behind the notification content.
4815 * Contrary to the {@link android.app.Notification.BigPictureStyle}, this background
4816 * will work with any notification style.
4817 *
4818 * @return the background image
4819 * @see android.app.Notification.WearableExtender#setBackground
4820 */
4821 public Bitmap getBackground() {
4822 return mBackground;
4823 }
4824
4825 /**
4826 * Set an icon that goes with the content of this notification.
4827 */
4828 public WearableExtender setContentIcon(int icon) {
4829 mContentIcon = icon;
4830 return this;
4831 }
4832
4833 /**
4834 * Get an icon that goes with the content of this notification.
4835 */
4836 public int getContentIcon() {
4837 return mContentIcon;
4838 }
4839
4840 /**
4841 * Set the gravity that the content icon should have within the notification display.
4842 * Supported values include {@link android.view.Gravity#START} and
4843 * {@link android.view.Gravity#END}. The default value is {@link android.view.Gravity#END}.
4844 * @see #setContentIcon
4845 */
4846 public WearableExtender setContentIconGravity(int contentIconGravity) {
4847 mContentIconGravity = contentIconGravity;
4848 return this;
4849 }
4850
4851 /**
4852 * Get the gravity that the content icon should have within the notification display.
4853 * Supported values include {@link android.view.Gravity#START} and
4854 * {@link android.view.Gravity#END}. The default value is {@link android.view.Gravity#END}.
4855 * @see #getContentIcon
4856 */
4857 public int getContentIconGravity() {
4858 return mContentIconGravity;
4859 }
4860
4861 /**
4862 * Set an action from this notification's actions to be clickable with the content of
Griff Hazen14f57992014-05-26 09:07:14 -07004863 * this notification. This action will no longer display separately from the
4864 * notification's content.
4865 *
Griff Hazenca48d352014-05-28 22:37:13 -07004866 * <p>For notifications with multiple pages, child pages can also have content actions
Griff Hazen14f57992014-05-26 09:07:14 -07004867 * set, although the list of available actions comes from the main notification and not
4868 * from the child page's notification.
4869 *
4870 * @param actionIndex The index of the action to hoist onto the current notification page.
4871 * If wearable actions were added to the main notification, this index
4872 * will apply to that list, otherwise it will apply to the regular
4873 * actions list.
Griff Hazen61a9e862014-05-22 16:05:19 -07004874 */
4875 public WearableExtender setContentAction(int actionIndex) {
4876 mContentActionIndex = actionIndex;
4877 return this;
4878 }
4879
4880 /**
Griff Hazenca48d352014-05-28 22:37:13 -07004881 * Get the index of the notification action, if any, that was specified as being clickable
4882 * with the content of this notification. This action will no longer display separately
Griff Hazen14f57992014-05-26 09:07:14 -07004883 * from the notification's content.
Griff Hazen61a9e862014-05-22 16:05:19 -07004884 *
Griff Hazenca48d352014-05-28 22:37:13 -07004885 * <p>For notifications with multiple pages, child pages can also have content actions
Griff Hazen14f57992014-05-26 09:07:14 -07004886 * set, although the list of available actions comes from the main notification and not
4887 * from the child page's notification.
4888 *
4889 * <p>If wearable specific actions were added to the main notification, this index will
4890 * apply to that list, otherwise it will apply to the regular actions list.
Griff Hazenca48d352014-05-28 22:37:13 -07004891 *
4892 * @return the action index or {@link #UNSET_ACTION_INDEX} if no action was selected.
Griff Hazen61a9e862014-05-22 16:05:19 -07004893 */
4894 public int getContentAction() {
4895 return mContentActionIndex;
4896 }
4897
4898 /**
4899 * Set the gravity that this notification should have within the available viewport space.
4900 * Supported values include {@link android.view.Gravity#TOP},
4901 * {@link android.view.Gravity#CENTER_VERTICAL} and {@link android.view.Gravity#BOTTOM}.
4902 * The default value is {@link android.view.Gravity#BOTTOM}.
4903 */
4904 public WearableExtender setGravity(int gravity) {
4905 mGravity = gravity;
4906 return this;
4907 }
4908
4909 /**
4910 * Get the gravity that this notification should have within the available viewport space.
4911 * Supported values include {@link android.view.Gravity#TOP},
4912 * {@link android.view.Gravity#CENTER_VERTICAL} and {@link android.view.Gravity#BOTTOM}.
4913 * The default value is {@link android.view.Gravity#BOTTOM}.
4914 */
4915 public int getGravity() {
4916 return mGravity;
4917 }
4918
4919 /**
4920 * Set the custom size preset for the display of this notification out of the available
4921 * presets found in {@link android.app.Notification.WearableExtender}, e.g.
4922 * {@link #SIZE_LARGE}.
4923 * <p>Some custom size presets are only applicable for custom display notifications created
4924 * using {@link android.app.Notification.WearableExtender#setDisplayIntent}. Check the
4925 * documentation for the preset in question. See also
4926 * {@link #setCustomContentHeight} and {@link #getCustomSizePreset}.
4927 */
4928 public WearableExtender setCustomSizePreset(int sizePreset) {
4929 mCustomSizePreset = sizePreset;
4930 return this;
4931 }
4932
4933 /**
4934 * Get the custom size preset for the display of this notification out of the available
4935 * presets found in {@link android.app.Notification.WearableExtender}, e.g.
4936 * {@link #SIZE_LARGE}.
4937 * <p>Some custom size presets are only applicable for custom display notifications created
4938 * using {@link #setDisplayIntent}. Check the documentation for the preset in question.
4939 * See also {@link #setCustomContentHeight} and {@link #setCustomSizePreset}.
4940 */
4941 public int getCustomSizePreset() {
4942 return mCustomSizePreset;
4943 }
4944
4945 /**
4946 * Set the custom height in pixels for the display of this notification's content.
4947 * <p>This option is only available for custom display notifications created
4948 * using {@link android.app.Notification.WearableExtender#setDisplayIntent}. See also
4949 * {@link android.app.Notification.WearableExtender#setCustomSizePreset} and
4950 * {@link #getCustomContentHeight}.
4951 */
4952 public WearableExtender setCustomContentHeight(int height) {
4953 mCustomContentHeight = height;
4954 return this;
4955 }
4956
4957 /**
4958 * Get the custom height in pixels for the display of this notification's content.
4959 * <p>This option is only available for custom display notifications created
4960 * using {@link #setDisplayIntent}. See also {@link #setCustomSizePreset} and
4961 * {@link #setCustomContentHeight}.
4962 */
4963 public int getCustomContentHeight() {
4964 return mCustomContentHeight;
4965 }
4966
4967 /**
4968 * Set whether the scrolling position for the contents of this notification should start
4969 * at the bottom of the contents instead of the top when the contents are too long to
4970 * display within the screen. Default is false (start scroll at the top).
4971 */
4972 public WearableExtender setStartScrollBottom(boolean startScrollBottom) {
4973 setFlag(FLAG_START_SCROLL_BOTTOM, startScrollBottom);
4974 return this;
4975 }
4976
4977 /**
4978 * Get whether the scrolling position for the contents of this notification should start
4979 * at the bottom of the contents instead of the top when the contents are too long to
4980 * display within the screen. Default is false (start scroll at the top).
4981 */
4982 public boolean getStartScrollBottom() {
4983 return (mFlags & FLAG_START_SCROLL_BOTTOM) != 0;
4984 }
4985
4986 /**
4987 * Set whether the content intent is available when the wearable device is not connected
4988 * to a companion device. The user can still trigger this intent when the wearable device
4989 * is offline, but a visual hint will indicate that the content intent may not be available.
4990 * Defaults to true.
4991 */
4992 public WearableExtender setContentIntentAvailableOffline(
4993 boolean contentIntentAvailableOffline) {
4994 setFlag(FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE, contentIntentAvailableOffline);
4995 return this;
4996 }
4997
4998 /**
4999 * Get whether the content intent is available when the wearable device is not connected
5000 * to a companion device. The user can still trigger this intent when the wearable device
5001 * is offline, but a visual hint will indicate that the content intent may not be available.
5002 * Defaults to true.
5003 */
5004 public boolean getContentIntentAvailableOffline() {
5005 return (mFlags & FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE) != 0;
5006 }
5007
5008 /**
5009 * Set a hint that this notification's icon should not be displayed.
5010 * @param hintHideIcon {@code true} to hide the icon, {@code false} otherwise.
5011 * @return this object for method chaining
5012 */
5013 public WearableExtender setHintHideIcon(boolean hintHideIcon) {
5014 setFlag(FLAG_HINT_HIDE_ICON, hintHideIcon);
5015 return this;
5016 }
5017
5018 /**
5019 * Get a hint that this notification's icon should not be displayed.
5020 * @return {@code true} if this icon should not be displayed, false otherwise.
5021 * The default value is {@code false} if this was never set.
5022 */
5023 public boolean getHintHideIcon() {
5024 return (mFlags & FLAG_HINT_HIDE_ICON) != 0;
5025 }
5026
5027 /**
5028 * Set a visual hint that only the background image of this notification should be
5029 * displayed, and other semantic content should be hidden. This hint is only applicable
5030 * to sub-pages added using {@link #addPage}.
5031 */
5032 public WearableExtender setHintShowBackgroundOnly(boolean hintShowBackgroundOnly) {
5033 setFlag(FLAG_HINT_SHOW_BACKGROUND_ONLY, hintShowBackgroundOnly);
5034 return this;
5035 }
5036
5037 /**
5038 * Get a visual hint that only the background image of this notification should be
5039 * displayed, and other semantic content should be hidden. This hint is only applicable
5040 * to sub-pages added using {@link android.app.Notification.WearableExtender#addPage}.
5041 */
5042 public boolean getHintShowBackgroundOnly() {
5043 return (mFlags & FLAG_HINT_SHOW_BACKGROUND_ONLY) != 0;
5044 }
5045
Griff Hazen5f2edfc2014-09-29 16:28:44 -07005046 /**
Griff Hazen9c5be4e2014-11-17 17:47:50 -08005047 * Set a hint that this notification's background should not be clipped if possible,
5048 * and should instead be resized to fully display on the screen, retaining the aspect
5049 * ratio of the image. This can be useful for images like barcodes or qr codes.
Griff Hazen5f2edfc2014-09-29 16:28:44 -07005050 * @param hintAvoidBackgroundClipping {@code true} to avoid clipping if possible.
5051 * @return this object for method chaining
5052 */
5053 public WearableExtender setHintAvoidBackgroundClipping(
5054 boolean hintAvoidBackgroundClipping) {
5055 setFlag(FLAG_HINT_AVOID_BACKGROUND_CLIPPING, hintAvoidBackgroundClipping);
5056 return this;
5057 }
5058
5059 /**
Griff Hazen9c5be4e2014-11-17 17:47:50 -08005060 * Get a hint that this notification's background should not be clipped if possible,
5061 * and should instead be resized to fully display on the screen, retaining the aspect
5062 * ratio of the image. This can be useful for images like barcodes or qr codes.
Griff Hazen5f2edfc2014-09-29 16:28:44 -07005063 * @return {@code true} if it's ok if the background is clipped on the screen, false
5064 * otherwise. The default value is {@code false} if this was never set.
5065 */
5066 public boolean getHintAvoidBackgroundClipping() {
5067 return (mFlags & FLAG_HINT_AVOID_BACKGROUND_CLIPPING) != 0;
5068 }
5069
5070 /**
5071 * Set a hint that the screen should remain on for at least this duration when
5072 * this notification is displayed on the screen.
5073 * @param timeout The requested screen timeout in milliseconds. Can also be either
5074 * {@link #SCREEN_TIMEOUT_SHORT} or {@link #SCREEN_TIMEOUT_LONG}.
5075 * @return this object for method chaining
5076 */
5077 public WearableExtender setHintScreenTimeout(int timeout) {
5078 mHintScreenTimeout = timeout;
5079 return this;
5080 }
5081
5082 /**
5083 * Get the duration, in milliseconds, that the screen should remain on for
5084 * when this notification is displayed.
5085 * @return the duration in milliseconds if > 0, or either one of the sentinel values
5086 * {@link #SCREEN_TIMEOUT_SHORT} or {@link #SCREEN_TIMEOUT_LONG}.
5087 */
5088 public int getHintScreenTimeout() {
5089 return mHintScreenTimeout;
5090 }
5091
Griff Hazen61a9e862014-05-22 16:05:19 -07005092 private void setFlag(int mask, boolean value) {
5093 if (value) {
5094 mFlags |= mask;
5095 } else {
5096 mFlags &= ~mask;
5097 }
5098 }
5099 }
5100
5101 /**
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08005102 * <p>Helper class to add Android Auto extensions to notifications. To create a notification
5103 * with car extensions:
5104 *
5105 * <ol>
5106 * <li>Create an {@link Notification.Builder}, setting any desired
5107 * properties.
5108 * <li>Create a {@link CarExtender}.
5109 * <li>Set car-specific properties using the {@code add} and {@code set} methods of
5110 * {@link CarExtender}.
5111 * <li>Call {@link Notification.Builder#extend(Notification.Extender)}
5112 * to apply the extensions to a notification.
5113 * </ol>
5114 *
5115 * <pre class="prettyprint">
5116 * Notification notification = new Notification.Builder(context)
5117 * ...
5118 * .extend(new CarExtender()
5119 * .set*(...))
5120 * .build();
5121 * </pre>
5122 *
5123 * <p>Car extensions can be accessed on an existing notification by using the
5124 * {@code CarExtender(Notification)} constructor, and then using the {@code get} methods
5125 * to access values.
5126 */
5127 public static final class CarExtender implements Extender {
5128 private static final String TAG = "CarExtender";
5129
5130 private static final String EXTRA_CAR_EXTENDER = "android.car.EXTENSIONS";
5131 private static final String EXTRA_LARGE_ICON = "large_icon";
5132 private static final String EXTRA_CONVERSATION = "car_conversation";
5133 private static final String EXTRA_COLOR = "app_color";
5134
5135 private Bitmap mLargeIcon;
5136 private UnreadConversation mUnreadConversation;
5137 private int mColor = Notification.COLOR_DEFAULT;
5138
5139 /**
5140 * Create a {@link CarExtender} with default options.
5141 */
5142 public CarExtender() {
5143 }
5144
5145 /**
5146 * Create a {@link CarExtender} from the CarExtender options of an existing Notification.
5147 *
5148 * @param notif The notification from which to copy options.
5149 */
5150 public CarExtender(Notification notif) {
5151 Bundle carBundle = notif.extras == null ?
5152 null : notif.extras.getBundle(EXTRA_CAR_EXTENDER);
5153 if (carBundle != null) {
5154 mLargeIcon = carBundle.getParcelable(EXTRA_LARGE_ICON);
5155 mColor = carBundle.getInt(EXTRA_COLOR, Notification.COLOR_DEFAULT);
5156
5157 Bundle b = carBundle.getBundle(EXTRA_CONVERSATION);
5158 mUnreadConversation = UnreadConversation.getUnreadConversationFromBundle(b);
5159 }
5160 }
5161
5162 /**
5163 * Apply car extensions to a notification that is being built. This is typically called by
5164 * the {@link Notification.Builder#extend(Notification.Extender)}
5165 * method of {@link Notification.Builder}.
5166 */
5167 @Override
5168 public Notification.Builder extend(Notification.Builder builder) {
5169 Bundle carExtensions = new Bundle();
5170
5171 if (mLargeIcon != null) {
5172 carExtensions.putParcelable(EXTRA_LARGE_ICON, mLargeIcon);
5173 }
5174 if (mColor != Notification.COLOR_DEFAULT) {
5175 carExtensions.putInt(EXTRA_COLOR, mColor);
5176 }
5177
5178 if (mUnreadConversation != null) {
5179 Bundle b = mUnreadConversation.getBundleForUnreadConversation();
5180 carExtensions.putBundle(EXTRA_CONVERSATION, b);
5181 }
5182
5183 builder.getExtras().putBundle(EXTRA_CAR_EXTENDER, carExtensions);
5184 return builder;
5185 }
5186
5187 /**
5188 * Sets the accent color to use when Android Auto presents the notification.
5189 *
5190 * Android Auto uses the color set with {@link Notification.Builder#setColor(int)}
5191 * to accent the displayed notification. However, not all colors are acceptable in an
5192 * automotive setting. This method can be used to override the color provided in the
5193 * notification in such a situation.
5194 */
Tor Norbye80756e32015-03-02 09:39:27 -08005195 public CarExtender setColor(@ColorInt int color) {
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08005196 mColor = color;
5197 return this;
5198 }
5199
5200 /**
5201 * Gets the accent color.
5202 *
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005203 * @see #setColor
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08005204 */
Tor Norbye80756e32015-03-02 09:39:27 -08005205 @ColorInt
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08005206 public int getColor() {
5207 return mColor;
5208 }
5209
5210 /**
5211 * Sets the large icon of the car notification.
5212 *
5213 * If no large icon is set in the extender, Android Auto will display the icon
5214 * specified by {@link Notification.Builder#setLargeIcon(android.graphics.Bitmap)}
5215 *
5216 * @param largeIcon The large icon to use in the car notification.
5217 * @return This object for method chaining.
5218 */
5219 public CarExtender setLargeIcon(Bitmap largeIcon) {
5220 mLargeIcon = largeIcon;
5221 return this;
5222 }
5223
5224 /**
5225 * Gets the large icon used in this car notification, or null if no icon has been set.
5226 *
5227 * @return The large icon for the car notification.
5228 * @see CarExtender#setLargeIcon
5229 */
5230 public Bitmap getLargeIcon() {
5231 return mLargeIcon;
5232 }
5233
5234 /**
5235 * Sets the unread conversation in a message notification.
5236 *
5237 * @param unreadConversation The unread part of the conversation this notification conveys.
5238 * @return This object for method chaining.
5239 */
5240 public CarExtender setUnreadConversation(UnreadConversation unreadConversation) {
5241 mUnreadConversation = unreadConversation;
5242 return this;
5243 }
5244
5245 /**
5246 * Returns the unread conversation conveyed by this notification.
5247 * @see #setUnreadConversation(UnreadConversation)
5248 */
5249 public UnreadConversation getUnreadConversation() {
5250 return mUnreadConversation;
5251 }
5252
5253 /**
5254 * A class which holds the unread messages from a conversation.
5255 */
5256 public static class UnreadConversation {
5257 private static final String KEY_AUTHOR = "author";
5258 private static final String KEY_TEXT = "text";
5259 private static final String KEY_MESSAGES = "messages";
5260 private static final String KEY_REMOTE_INPUT = "remote_input";
5261 private static final String KEY_ON_REPLY = "on_reply";
5262 private static final String KEY_ON_READ = "on_read";
5263 private static final String KEY_PARTICIPANTS = "participants";
5264 private static final String KEY_TIMESTAMP = "timestamp";
5265
5266 private final String[] mMessages;
5267 private final RemoteInput mRemoteInput;
5268 private final PendingIntent mReplyPendingIntent;
5269 private final PendingIntent mReadPendingIntent;
5270 private final String[] mParticipants;
5271 private final long mLatestTimestamp;
5272
5273 UnreadConversation(String[] messages, RemoteInput remoteInput,
5274 PendingIntent replyPendingIntent, PendingIntent readPendingIntent,
5275 String[] participants, long latestTimestamp) {
5276 mMessages = messages;
5277 mRemoteInput = remoteInput;
5278 mReadPendingIntent = readPendingIntent;
5279 mReplyPendingIntent = replyPendingIntent;
5280 mParticipants = participants;
5281 mLatestTimestamp = latestTimestamp;
5282 }
5283
5284 /**
5285 * Gets the list of messages conveyed by this notification.
5286 */
5287 public String[] getMessages() {
5288 return mMessages;
5289 }
5290
5291 /**
5292 * Gets the remote input that will be used to convey the response to a message list, or
5293 * null if no such remote input exists.
5294 */
5295 public RemoteInput getRemoteInput() {
5296 return mRemoteInput;
5297 }
5298
5299 /**
5300 * Gets the pending intent that will be triggered when the user replies to this
5301 * notification.
5302 */
5303 public PendingIntent getReplyPendingIntent() {
5304 return mReplyPendingIntent;
5305 }
5306
5307 /**
5308 * Gets the pending intent that Android Auto will send after it reads aloud all messages
5309 * in this object's message list.
5310 */
5311 public PendingIntent getReadPendingIntent() {
5312 return mReadPendingIntent;
5313 }
5314
5315 /**
5316 * Gets the participants in the conversation.
5317 */
5318 public String[] getParticipants() {
5319 return mParticipants;
5320 }
5321
5322 /**
5323 * Gets the firs participant in the conversation.
5324 */
5325 public String getParticipant() {
5326 return mParticipants.length > 0 ? mParticipants[0] : null;
5327 }
5328
5329 /**
5330 * Gets the timestamp of the conversation.
5331 */
5332 public long getLatestTimestamp() {
5333 return mLatestTimestamp;
5334 }
5335
5336 Bundle getBundleForUnreadConversation() {
5337 Bundle b = new Bundle();
5338 String author = null;
5339 if (mParticipants != null && mParticipants.length > 1) {
5340 author = mParticipants[0];
5341 }
5342 Parcelable[] messages = new Parcelable[mMessages.length];
5343 for (int i = 0; i < messages.length; i++) {
5344 Bundle m = new Bundle();
5345 m.putString(KEY_TEXT, mMessages[i]);
5346 m.putString(KEY_AUTHOR, author);
5347 messages[i] = m;
5348 }
5349 b.putParcelableArray(KEY_MESSAGES, messages);
5350 if (mRemoteInput != null) {
5351 b.putParcelable(KEY_REMOTE_INPUT, mRemoteInput);
5352 }
5353 b.putParcelable(KEY_ON_REPLY, mReplyPendingIntent);
5354 b.putParcelable(KEY_ON_READ, mReadPendingIntent);
5355 b.putStringArray(KEY_PARTICIPANTS, mParticipants);
5356 b.putLong(KEY_TIMESTAMP, mLatestTimestamp);
5357 return b;
5358 }
5359
5360 static UnreadConversation getUnreadConversationFromBundle(Bundle b) {
5361 if (b == null) {
5362 return null;
5363 }
5364 Parcelable[] parcelableMessages = b.getParcelableArray(KEY_MESSAGES);
5365 String[] messages = null;
5366 if (parcelableMessages != null) {
5367 String[] tmp = new String[parcelableMessages.length];
5368 boolean success = true;
5369 for (int i = 0; i < tmp.length; i++) {
5370 if (!(parcelableMessages[i] instanceof Bundle)) {
5371 success = false;
5372 break;
5373 }
5374 tmp[i] = ((Bundle) parcelableMessages[i]).getString(KEY_TEXT);
5375 if (tmp[i] == null) {
5376 success = false;
5377 break;
5378 }
5379 }
5380 if (success) {
5381 messages = tmp;
5382 } else {
5383 return null;
5384 }
5385 }
5386
5387 PendingIntent onRead = b.getParcelable(KEY_ON_READ);
5388 PendingIntent onReply = b.getParcelable(KEY_ON_REPLY);
5389
5390 RemoteInput remoteInput = b.getParcelable(KEY_REMOTE_INPUT);
5391
5392 String[] participants = b.getStringArray(KEY_PARTICIPANTS);
5393 if (participants == null || participants.length != 1) {
5394 return null;
5395 }
5396
5397 return new UnreadConversation(messages,
5398 remoteInput,
5399 onReply,
5400 onRead,
5401 participants, b.getLong(KEY_TIMESTAMP));
5402 }
5403 };
5404
5405 /**
5406 * Builder class for {@link CarExtender.UnreadConversation} objects.
5407 */
5408 public static class Builder {
5409 private final List<String> mMessages = new ArrayList<String>();
5410 private final String mParticipant;
5411 private RemoteInput mRemoteInput;
5412 private PendingIntent mReadPendingIntent;
5413 private PendingIntent mReplyPendingIntent;
5414 private long mLatestTimestamp;
5415
5416 /**
5417 * Constructs a new builder for {@link CarExtender.UnreadConversation}.
5418 *
5419 * @param name The name of the other participant in the conversation.
5420 */
5421 public Builder(String name) {
5422 mParticipant = name;
5423 }
5424
5425 /**
5426 * Appends a new unread message to the list of messages for this conversation.
5427 *
5428 * The messages should be added from oldest to newest.
5429 *
5430 * @param message The text of the new unread message.
5431 * @return This object for method chaining.
5432 */
5433 public Builder addMessage(String message) {
5434 mMessages.add(message);
5435 return this;
5436 }
5437
5438 /**
5439 * Sets the pending intent and remote input which will convey the reply to this
5440 * notification.
5441 *
5442 * @param pendingIntent The pending intent which will be triggered on a reply.
5443 * @param remoteInput The remote input parcelable which will carry the reply.
5444 * @return This object for method chaining.
5445 *
5446 * @see CarExtender.UnreadConversation#getRemoteInput
5447 * @see CarExtender.UnreadConversation#getReplyPendingIntent
5448 */
5449 public Builder setReplyAction(
5450 PendingIntent pendingIntent, RemoteInput remoteInput) {
5451 mRemoteInput = remoteInput;
5452 mReplyPendingIntent = pendingIntent;
5453
5454 return this;
5455 }
5456
5457 /**
5458 * Sets the pending intent that will be sent once the messages in this notification
5459 * are read.
5460 *
5461 * @param pendingIntent The pending intent to use.
5462 * @return This object for method chaining.
5463 */
5464 public Builder setReadPendingIntent(PendingIntent pendingIntent) {
5465 mReadPendingIntent = pendingIntent;
5466 return this;
5467 }
5468
5469 /**
5470 * Sets the timestamp of the most recent message in an unread conversation.
5471 *
5472 * If a messaging notification has been posted by your application and has not
5473 * yet been cancelled, posting a later notification with the same id and tag
5474 * but without a newer timestamp may result in Android Auto not displaying a
5475 * heads up notification for the later notification.
5476 *
5477 * @param timestamp The timestamp of the most recent message in the conversation.
5478 * @return This object for method chaining.
5479 */
5480 public Builder setLatestTimestamp(long timestamp) {
5481 mLatestTimestamp = timestamp;
5482 return this;
5483 }
5484
5485 /**
5486 * Builds a new unread conversation object.
5487 *
5488 * @return The new unread conversation object.
5489 */
5490 public UnreadConversation build() {
5491 String[] messages = mMessages.toArray(new String[mMessages.size()]);
5492 String[] participants = { mParticipant };
5493 return new UnreadConversation(messages, mRemoteInput, mReplyPendingIntent,
5494 mReadPendingIntent, participants, mLatestTimestamp);
5495 }
5496 }
5497 }
5498
5499 /**
Griff Hazen61a9e862014-05-22 16:05:19 -07005500 * Get an array of Notification objects from a parcelable array bundle field.
5501 * Update the bundle to have a typed array so fetches in the future don't need
5502 * to do an array copy.
5503 */
5504 private static Notification[] getNotificationArrayFromBundle(Bundle bundle, String key) {
5505 Parcelable[] array = bundle.getParcelableArray(key);
5506 if (array instanceof Notification[] || array == null) {
5507 return (Notification[]) array;
5508 }
5509 Notification[] typedArray = Arrays.copyOf(array, array.length,
5510 Notification[].class);
5511 bundle.putParcelableArray(key, typedArray);
5512 return typedArray;
5513 }
Christoph Studer4600f9b2014-07-22 22:44:43 +02005514
5515 private static class BuilderRemoteViews extends RemoteViews {
5516 public BuilderRemoteViews(Parcel parcel) {
5517 super(parcel);
5518 }
5519
Kenny Guy77320062014-08-27 21:37:15 +01005520 public BuilderRemoteViews(ApplicationInfo appInfo, int layoutId) {
5521 super(appInfo, layoutId);
Christoph Studer4600f9b2014-07-22 22:44:43 +02005522 }
5523
5524 @Override
5525 public BuilderRemoteViews clone() {
5526 Parcel p = Parcel.obtain();
5527 writeToParcel(p, 0);
5528 p.setDataPosition(0);
5529 BuilderRemoteViews brv = new BuilderRemoteViews(p);
5530 p.recycle();
5531 return brv;
5532 }
5533 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005534}