blob: 4f9ed834318f86a9c283ff82ababa4922b70cf8b [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
Selim Cinek389edcd2017-05-11 19:16:44 -070019import static com.android.internal.util.NotificationColorUtil.satisfiesTextContrast;
20
Tor Norbye80756e32015-03-02 09:39:27 -080021import android.annotation.ColorInt;
Tor Norbye7b9c9122013-05-30 16:48:33 -070022import android.annotation.DrawableRes;
Tor Norbyed9273d62013-05-30 15:59:53 -070023import android.annotation.IntDef;
Alex Hillsfd590442016-10-07 09:52:44 -040024import android.annotation.NonNull;
Jeff Sharkeyd86b8fe2017-06-02 17:36:26 -060025import android.annotation.RequiresPermission;
Daniel Sandler01df1c62014-06-09 10:54:01 -040026import android.annotation.SdkConstant;
27import android.annotation.SdkConstant.SdkConstantType;
Julia Reynoldse46bb372016-03-17 11:05:58 -040028import android.annotation.SystemApi;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080029import android.content.Context;
30import android.content.Intent;
Kenny Guy77320062014-08-27 21:37:15 +010031import android.content.pm.ApplicationInfo;
Dan Sandler732bd6c2016-04-12 14:20:32 -040032import android.content.pm.PackageManager;
Christoph Studer4600f9b2014-07-22 22:44:43 +020033import android.content.pm.PackageManager.NameNotFoundException;
Julia Reynolds13d898c2017-02-02 12:22:05 -050034import android.content.pm.ShortcutInfo;
Jorim Jaggief72a192014-08-26 21:57:46 +020035import android.content.res.ColorStateList;
Joe Onoratoef1e7762010-09-17 18:38:38 -040036import android.graphics.Bitmap;
Kenny Guy8a0101b2014-05-08 23:34:12 +010037import android.graphics.Canvas;
Adrian Roosc1a80b02016-04-05 14:54:55 -070038import android.graphics.Color;
Jorim Jaggi5c2d8462014-03-21 17:37:00 +010039import android.graphics.PorterDuff;
Kenny Guy8a0101b2014-05-08 23:34:12 +010040import android.graphics.drawable.Drawable;
Dan Sandlerd63f9322015-05-06 15:18:49 -040041import android.graphics.drawable.Icon;
John Spurlockc0650f022014-07-19 13:22:39 -040042import android.media.AudioAttributes;
Jeff Sharkey098d5802012-04-26 17:30:34 -070043import android.media.AudioManager;
Jean-Michel Trivi2f7511f2016-11-28 15:40:27 -080044import android.media.PlayerBase;
Jeff Browndba34ba2014-06-24 20:46:03 -070045import android.media.session.MediaSession;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080046import android.net.Uri;
Daniel Sandlerdcbaf662013-04-26 16:23:09 -040047import android.os.BadParcelableException;
Christoph Studer239f8352014-08-25 15:13:18 +020048import android.os.Build;
Daniel Sandler2561b0b2012-02-13 21:04:12 -050049import android.os.Bundle;
Dianne Hackborn98305522017-05-05 17:53:53 -070050import android.os.IBinder;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080051import android.os.Parcel;
52import android.os.Parcelable;
Daniel Sandlera2985ed2012-04-03 16:42:00 -040053import android.os.SystemClock;
Selim Cinek6743c0b2017-01-18 18:24:01 -080054import android.os.SystemProperties;
Jeff Sharkey6d515712012-09-20 16:06:08 -070055import android.os.UserHandle;
Adrian Roosc1a80b02016-04-05 14:54:55 -070056import android.text.BidiFormatter;
Selim Cinek60a54252016-02-26 17:03:25 -080057import android.text.SpannableStringBuilder;
58import android.text.Spanned;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080059import android.text.TextUtils;
Selim Cinek60a54252016-02-26 17:03:25 -080060import android.text.style.AbsoluteSizeSpan;
Selim Cinek981962e2016-07-20 20:41:58 -070061import android.text.style.BackgroundColorSpan;
Selim Cinek89991a22016-03-07 19:51:54 -080062import android.text.style.CharacterStyle;
Selim Cinek981962e2016-07-20 20:41:58 -070063import android.text.style.ForegroundColorSpan;
Selim Cinek60a54252016-02-26 17:03:25 -080064import android.text.style.RelativeSizeSpan;
65import android.text.style.TextAppearanceSpan;
Svet Ganovddb94882016-06-23 19:55:24 -070066import android.util.ArraySet;
Daniel Sandlerdcbaf662013-04-26 16:23:09 -040067import android.util.Log;
Dan Sandler50128532015-12-08 15:42:41 -050068import android.util.SparseArray;
Griff Hazen61a9e862014-05-22 16:05:19 -070069import android.view.Gravity;
Selim Cinekea4bef72015-12-02 15:51:10 -080070import android.view.NotificationHeaderView;
Joe Onorato8595a3d2010-11-19 18:12:07 -080071import android.view.View;
Selim Cinek954cc232016-05-20 13:29:23 -070072import android.view.ViewGroup;
Jeff Sharkey1c400132011-08-05 14:50:13 -070073import android.widget.ProgressBar;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080074import android.widget.RemoteViews;
75
Griff Hazen959591e2014-05-15 22:26:18 -070076import com.android.internal.R;
Selim Cinek389edcd2017-05-11 19:16:44 -070077import com.android.internal.annotations.VisibleForTesting;
Svet Ganovddb94882016-06-23 19:55:24 -070078import com.android.internal.util.ArrayUtils;
Griff Hazenc091ba82014-05-16 10:13:26 -070079import com.android.internal.util.NotificationColorUtil;
Adrian Roos0bc3f6a2017-03-06 11:54:05 -080080import com.android.internal.util.Preconditions;
Griff Hazen959591e2014-05-15 22:26:18 -070081
Tor Norbyed9273d62013-05-30 15:59:53 -070082import java.lang.annotation.Retention;
83import java.lang.annotation.RetentionPolicy;
Christoph Studer4600f9b2014-07-22 22:44:43 +020084import java.lang.reflect.Constructor;
Daniel Sandler2561b0b2012-02-13 21:04:12 -050085import java.util.ArrayList;
Griff Hazen61a9e862014-05-22 16:05:19 -070086import java.util.Arrays;
Griff Hazen5cadc3b2014-05-20 09:55:39 -070087import java.util.Collections;
Griff Hazen61a9e862014-05-22 16:05:19 -070088import java.util.List;
Dan Sandler50128532015-12-08 15:42:41 -050089import java.util.Set;
Joe Onorato561d3852010-11-20 18:09:34 -080090
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080091/**
92 * A class that represents how a persistent notification is to be presented to
93 * the user using the {@link android.app.NotificationManager}.
94 *
Joe Onoratocb109a02011-01-18 17:57:41 -080095 * <p>The {@link Notification.Builder Notification.Builder} has been added to make it
96 * easier to construct Notifications.</p>
97 *
Joe Fernandez558459f2011-10-13 16:47:36 -070098 * <div class="special reference">
99 * <h3>Developer Guides</h3>
100 * <p>For a guide to creating notifications, read the
101 * <a href="{@docRoot}guide/topics/ui/notifiers/notifications.html">Status Bar Notifications</a>
102 * developer guide.</p>
103 * </div>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800104 */
105public class Notification implements Parcelable
106{
Daniel Sandlerdcbaf662013-04-26 16:23:09 -0400107 private static final String TAG = "Notification";
108
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800109 /**
Daniel Sandler01df1c62014-06-09 10:54:01 -0400110 * An activity that provides a user interface for adjusting notification preferences for its
Julia Reynolds3aedded2017-03-31 14:42:09 -0400111 * containing application.
Daniel Sandler01df1c62014-06-09 10:54:01 -0400112 */
113 @SdkConstant(SdkConstantType.INTENT_CATEGORY)
114 public static final String INTENT_CATEGORY_NOTIFICATION_PREFERENCES
115 = "android.intent.category.NOTIFICATION_PREFERENCES";
116
117 /**
Julia Reynolds2619b5e2017-02-09 09:58:15 -0500118 * Optional extra for {@link #INTENT_CATEGORY_NOTIFICATION_PREFERENCES}. If provided, will
119 * contain a {@link NotificationChannel#getId() channel id} that can be used to narrow down
Julia Reynolds3aedded2017-03-31 14:42:09 -0400120 * what settings should be shown in the target app.
Julia Reynolds2619b5e2017-02-09 09:58:15 -0500121 */
122 public static final String EXTRA_CHANNEL_ID = "android.intent.extra.CHANNEL_ID";
123
124 /**
Julia Reynolds3aedded2017-03-31 14:42:09 -0400125 * Optional extra for {@link #INTENT_CATEGORY_NOTIFICATION_PREFERENCES}. If provided, will
126 * contain the tag provided to {@link NotificationManager#notify(String, int, Notification)}
127 * that can be used to narrow down what settings should be shown in the target app.
128 */
129 public static final String EXTRA_NOTIFICATION_TAG = "android.intent.extra.NOTIFICATION_TAG";
130
131 /**
132 * Optional extra for {@link #INTENT_CATEGORY_NOTIFICATION_PREFERENCES}. If provided, will
133 * contain the id provided to {@link NotificationManager#notify(String, int, Notification)}
134 * that can be used to narrow down what settings should be shown in the target app.
135 */
136 public static final String EXTRA_NOTIFICATION_ID = "android.intent.extra.NOTIFICATION_ID";
137
138 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800139 * Use all default values (where applicable).
140 */
141 public static final int DEFAULT_ALL = ~0;
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500142
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800143 /**
144 * Use the default notification sound. This will ignore any given
145 * {@link #sound}.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500146 *
Chris Wren47c20a12014-06-18 17:27:29 -0400147 * <p>
148 * A notification that is noisy is more likely to be presented as a heads-up notification.
149 * </p>
150 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800151 * @see #defaults
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500152 */
153
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800154 public static final int DEFAULT_SOUND = 1;
155
156 /**
157 * Use the default notification vibrate. This will ignore any given
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500158 * {@link #vibrate}. Using phone vibration requires the
Scott Mainb8b36452009-04-26 15:50:49 -0700159 * {@link android.Manifest.permission#VIBRATE VIBRATE} permission.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500160 *
Chris Wren47c20a12014-06-18 17:27:29 -0400161 * <p>
162 * A notification that vibrates is more likely to be presented as a heads-up notification.
163 * </p>
164 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800165 * @see #defaults
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500166 */
167
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800168 public static final int DEFAULT_VIBRATE = 2;
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500169
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800170 /**
171 * Use the default notification lights. This will ignore the
172 * {@link #FLAG_SHOW_LIGHTS} bit, and {@link #ledARGB}, {@link #ledOffMS}, or
173 * {@link #ledOnMS}.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500174 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800175 * @see #defaults
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500176 */
177
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800178 public static final int DEFAULT_LIGHTS = 4;
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500179
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800180 /**
Christoph Studer535ec612014-09-03 15:47:47 +0200181 * Maximum length of CharSequences accepted by Builder and friends.
182 *
183 * <p>
184 * Avoids spamming the system with overly large strings such as full e-mails.
185 */
186 private static final int MAX_CHARSEQUENCE_LENGTH = 5 * 1024;
187
188 /**
Adrian Roose458aa82015-12-08 16:17:19 -0800189 * Maximum entries of reply text that are accepted by Builder and friends.
190 */
191 private static final int MAX_REPLY_HISTORY = 5;
192
193 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500194 * A timestamp related to this notification, in milliseconds since the epoch.
Joe Malin8d40d042012-11-05 11:36:40 -0800195 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500196 * Default value: {@link System#currentTimeMillis() Now}.
197 *
198 * Choose a timestamp that will be most relevant to the user. For most finite events, this
199 * corresponds to the time the event happened (or will happen, in the case of events that have
200 * yet to occur but about which the user is being informed). Indefinite events should be
Joe Malin8d40d042012-11-05 11:36:40 -0800201 * timestamped according to when the activity began.
202 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500203 * Some examples:
Joe Malin8d40d042012-11-05 11:36:40 -0800204 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500205 * <ul>
206 * <li>Notification of a new chat message should be stamped when the message was received.</li>
207 * <li>Notification of an ongoing file download (with a progress bar, for example) should be stamped when the download started.</li>
208 * <li>Notification of a completed file download should be stamped when the download finished.</li>
209 * <li>Notification of an upcoming meeting should be stamped with the time the meeting will begin (that is, in the future).</li>
210 * <li>Notification of an ongoing stopwatch (increasing timer) should be stamped with the watch's start time.
211 * <li>Notification of an ongoing countdown timer should be stamped with the timer's end time.
Joe Malin8d40d042012-11-05 11:36:40 -0800212 * </ul>
213 *
Selim Cinek0ff1ce602016-04-05 18:27:16 -0700214 * For apps targeting {@link android.os.Build.VERSION_CODES#N} and above, this time is not shown
215 * anymore by default and must be opted into by using
216 * {@link android.app.Notification.Builder#setShowWhen(boolean)}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800217 */
218 public long when;
219
220 /**
Selim Cinekb85f36fd2016-04-20 18:46:36 -0700221 * The creation time of the notification
222 */
223 private long creationTime;
224
225 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800226 * The resource id of a drawable to use as the icon in the status bar.
Dan Sandler86647982015-05-13 23:41:13 -0400227 *
228 * @deprecated Use {@link Builder#setSmallIcon(Icon)} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800229 */
Dan Sandler86647982015-05-13 23:41:13 -0400230 @Deprecated
Tor Norbye7b9c9122013-05-30 16:48:33 -0700231 @DrawableRes
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800232 public int icon;
233
234 /**
Joe Onorato46439ce2010-11-19 13:56:21 -0800235 * If the icon in the status bar is to have more than one level, you can set this. Otherwise,
236 * leave it at its default value of 0.
237 *
238 * @see android.widget.ImageView#setImageLevel
Griff Hazen959591e2014-05-15 22:26:18 -0700239 * @see android.graphics.drawable.Drawable#setLevel
Joe Onorato46439ce2010-11-19 13:56:21 -0800240 */
241 public int iconLevel;
242
243 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500244 * The number of events that this notification represents. For example, in a new mail
245 * notification, this could be the number of unread messages.
Joe Malin8d40d042012-11-05 11:36:40 -0800246 *
Julia Reynolds30331982017-04-27 10:12:50 -0400247 * The system may or may not use this field to modify the appearance of the notification.
Julia Reynolds13d898c2017-02-02 12:22:05 -0500248 * Starting with {@link android.os.Build.VERSION_CODES#O}, the number may be displayed as a
249 * badge icon in Launchers that support badging.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800250 */
Julia Reynolds30331982017-04-27 10:12:50 -0400251 public int number = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800252
253 /**
254 * The intent to execute when the expanded status entry is clicked. If
255 * this is an activity, it must include the
256 * {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} flag, which requires
Scott Main7aee61f2011-02-08 11:25:01 -0800257 * that you take care of task management as described in the
258 * <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back
Dianne Hackborn6ceca582012-01-10 15:24:26 -0800259 * Stack</a> document. In particular, make sure to read the notification section
260 * <a href="{@docRoot}guide/topics/ui/notifiers/notifications.html#HandlingNotifications">Handling
261 * Notifications</a> for the correct ways to launch an application from a
262 * notification.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800263 */
264 public PendingIntent contentIntent;
265
266 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500267 * The intent to execute when the notification is explicitly dismissed by the user, either with
268 * the "Clear All" button or by swiping it away individually.
269 *
270 * This probably shouldn't be launching an activity since several of those will be sent
271 * at the same time.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800272 */
273 public PendingIntent deleteIntent;
274
275 /**
Dianne Hackborn170bae72010-09-03 15:14:28 -0700276 * An intent to launch instead of posting the notification to the status bar.
Joe Onoratocb109a02011-01-18 17:57:41 -0800277 *
Chris Wren47c20a12014-06-18 17:27:29 -0400278 * <p>
279 * The system UI may choose to display a heads-up notification, instead of
280 * launching this intent, while the user is using the device.
281 * </p>
282 *
Joe Onoratocb109a02011-01-18 17:57:41 -0800283 * @see Notification.Builder#setFullScreenIntent
Daniel Sandlere46cbd32010-06-17 10:35:26 -0400284 */
285 public PendingIntent fullScreenIntent;
286
287 /**
Dan Sandler5fcdf6e2014-07-18 11:31:15 -0400288 * Text that summarizes this notification for accessibility services.
289 *
290 * As of the L release, this text is no longer shown on screen, but it is still useful to
291 * accessibility services (where it serves as an audible announcement of the notification's
292 * appearance).
Joe Onoratoef1e7762010-09-17 18:38:38 -0400293 *
Joe Onorato46439ce2010-11-19 13:56:21 -0800294 * @see #tickerView
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800295 */
296 public CharSequence tickerText;
297
298 /**
Dan Sandler5fcdf6e2014-07-18 11:31:15 -0400299 * Formerly, a view showing the {@link #tickerText}.
300 *
301 * No longer displayed in the status bar as of API 21.
Joe Onoratoef1e7762010-09-17 18:38:38 -0400302 */
Dan Sandler5fcdf6e2014-07-18 11:31:15 -0400303 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -0800304 public RemoteViews tickerView;
Joe Onoratoef1e7762010-09-17 18:38:38 -0400305
306 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400307 * The view that will represent this notification in the notification list (which is pulled
308 * down from the status bar).
309 *
Julia Reynoldsd4ea7412016-02-17 14:00:56 -0500310 * As of N, this field may be null. The notification view is determined by the inputs
311 * to {@link Notification.Builder}; a custom RemoteViews can optionally be
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400312 * supplied with {@link Notification.Builder#setCustomContentView(RemoteViews)}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800313 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400314 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800315 public RemoteViews contentView;
316
Daniel Sandlera0a938c2012-03-15 08:42:37 -0400317 /**
Daniel Sandler4dfbe832012-04-11 14:51:46 -0400318 * A large-format version of {@link #contentView}, giving the Notification an
Daniel Sandlerf3b73432012-03-27 15:01:25 -0400319 * opportunity to show more detail. The system UI may choose to show this
320 * instead of the normal content view at its discretion.
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400321 *
Julia Reynoldsd4ea7412016-02-17 14:00:56 -0500322 * As of N, this field may be null. The expanded notification view is determined by the
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400323 * inputs to {@link Notification.Builder}; a custom RemoteViews can optionally be
324 * supplied with {@link Notification.Builder#setCustomBigContentView(RemoteViews)}.
Daniel Sandlerf3b73432012-03-27 15:01:25 -0400325 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400326 @Deprecated
Daniel Sandlerf3b73432012-03-27 15:01:25 -0400327 public RemoteViews bigContentView;
328
Chris Wren8fd39ec2014-02-27 17:43:26 -0500329
330 /**
Chris Wren47c20a12014-06-18 17:27:29 -0400331 * A medium-format version of {@link #contentView}, providing the Notification an
332 * opportunity to add action buttons to contentView. At its discretion, the system UI may
333 * choose to show this as a heads-up notification, which will pop up so the user can see
334 * it without leaving their current activity.
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400335 *
Julia Reynoldsd4ea7412016-02-17 14:00:56 -0500336 * As of N, this field may be null. The heads-up notification view is determined by the
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400337 * inputs to {@link Notification.Builder}; a custom RemoteViews can optionally be
338 * supplied with {@link Notification.Builder#setCustomHeadsUpContentView(RemoteViews)}.
Chris Wren8fd39ec2014-02-27 17:43:26 -0500339 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400340 @Deprecated
Chris Wren8fd39ec2014-02-27 17:43:26 -0500341 public RemoteViews headsUpContentView;
342
Daniel Sandlerf3b73432012-03-27 15:01:25 -0400343 /**
Dan Sandler86647982015-05-13 23:41:13 -0400344 * A large bitmap to be shown in the notification content area.
345 *
346 * @deprecated Use {@link Builder#setLargeIcon(Icon)} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800347 */
Dan Sandler86647982015-05-13 23:41:13 -0400348 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -0800349 public Bitmap largeIcon;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800350
351 /**
352 * The sound to play.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500353 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800354 * <p>
Chris Wren47c20a12014-06-18 17:27:29 -0400355 * A notification that is noisy is more likely to be presented as a heads-up notification.
356 * </p>
357 *
358 * <p>
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500359 * To play the default notification sound, see {@link #defaults}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800360 * </p>
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500361 * @deprecated use {@link NotificationChannel#getSound()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800362 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500363 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800364 public Uri sound;
365
366 /**
367 * Use this constant as the value for audioStreamType to request that
368 * the default stream type for notifications be used. Currently the
Jeff Sharkey098d5802012-04-26 17:30:34 -0700369 * default stream type is {@link AudioManager#STREAM_NOTIFICATION}.
John Spurlockc0650f022014-07-19 13:22:39 -0400370 *
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500371 * @deprecated Use {@link NotificationChannel#getAudioAttributes()} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800372 */
Jean-Michel Trivi81f871e2014-08-06 16:32:38 -0700373 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800374 public static final int STREAM_DEFAULT = -1;
375
376 /**
377 * The audio stream type to use when playing the sound.
378 * Should be one of the STREAM_ constants from
379 * {@link android.media.AudioManager}.
John Spurlockc0650f022014-07-19 13:22:39 -0400380 *
381 * @deprecated Use {@link #audioAttributes} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800382 */
Jean-Michel Trivi81f871e2014-08-06 16:32:38 -0700383 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800384 public int audioStreamType = STREAM_DEFAULT;
385
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800386 /**
John Spurlockc0650f022014-07-19 13:22:39 -0400387 * The default value of {@link #audioAttributes}.
388 */
389 public static final AudioAttributes AUDIO_ATTRIBUTES_DEFAULT = new AudioAttributes.Builder()
390 .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
391 .setUsage(AudioAttributes.USAGE_NOTIFICATION)
392 .build();
393
394 /**
395 * The {@link AudioAttributes audio attributes} to use when playing the sound.
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500396 *
397 * @deprecated use {@link NotificationChannel#getAudioAttributes()} instead.
John Spurlockc0650f022014-07-19 13:22:39 -0400398 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500399 @Deprecated
John Spurlockc0650f022014-07-19 13:22:39 -0400400 public AudioAttributes audioAttributes = AUDIO_ATTRIBUTES_DEFAULT;
401
402 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500403 * The pattern with which to vibrate.
404 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800405 * <p>
406 * To vibrate the default pattern, see {@link #defaults}.
407 * </p>
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500408 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800409 * @see android.os.Vibrator#vibrate(long[],int)
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500410 * @deprecated use {@link NotificationChannel#getVibrationPattern()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800411 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500412 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800413 public long[] vibrate;
414
415 /**
416 * The color of the led. The hardware will do its best approximation.
417 *
418 * @see #FLAG_SHOW_LIGHTS
419 * @see #flags
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500420 * @deprecated use {@link NotificationChannel#shouldShowLights()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800421 */
Tor Norbye80756e32015-03-02 09:39:27 -0800422 @ColorInt
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500423 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800424 public int ledARGB;
425
426 /**
427 * The number of milliseconds for the LED to be on while it's flashing.
428 * The hardware will do its best approximation.
429 *
430 * @see #FLAG_SHOW_LIGHTS
431 * @see #flags
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500432 * @deprecated use {@link NotificationChannel#shouldShowLights()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800433 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500434 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800435 public int ledOnMS;
436
437 /**
438 * The number of milliseconds for the LED to be off while it's flashing.
439 * The hardware will do its best approximation.
440 *
441 * @see #FLAG_SHOW_LIGHTS
442 * @see #flags
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500443 *
444 * @deprecated use {@link NotificationChannel#shouldShowLights()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800445 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500446 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800447 public int ledOffMS;
448
449 /**
450 * Specifies which values should be taken from the defaults.
451 * <p>
452 * To set, OR the desired from {@link #DEFAULT_SOUND},
453 * {@link #DEFAULT_VIBRATE}, {@link #DEFAULT_LIGHTS}. For all default
454 * values, use {@link #DEFAULT_ALL}.
455 * </p>
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500456 *
457 * @deprecated use {@link NotificationChannel#getSound()} and
458 * {@link NotificationChannel#shouldShowLights()} and
459 * {@link NotificationChannel#shouldVibrate()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800460 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500461 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800462 public int defaults;
463
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800464 /**
465 * Bit to be bitwise-ored into the {@link #flags} field that should be
466 * set if you want the LED on for this notification.
467 * <ul>
468 * <li>To turn the LED off, pass 0 in the alpha channel for colorARGB
469 * or 0 for both ledOnMS and ledOffMS.</li>
470 * <li>To turn the LED on, pass 1 for ledOnMS and 0 for ledOffMS.</li>
471 * <li>To flash the LED, pass the number of milliseconds that it should
472 * be on and off to ledOnMS and ledOffMS.</li>
473 * </ul>
474 * <p>
475 * Since hardware varies, you are not guaranteed that any of the values
476 * you pass are honored exactly. Use the system defaults (TODO) if possible
477 * because they will be set to values that work on any given hardware.
478 * <p>
479 * The alpha channel must be set for forward compatibility.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500480 *
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500481 * @deprecated use {@link NotificationChannel#shouldShowLights()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800482 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500483 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800484 public static final int FLAG_SHOW_LIGHTS = 0x00000001;
485
486 /**
487 * Bit to be bitwise-ored into the {@link #flags} field that should be
488 * set if this notification is in reference to something that is ongoing,
489 * like a phone call. It should not be set if this notification is in
490 * reference to something that happened at a particular point in time,
491 * like a missed phone call.
492 */
493 public static final int FLAG_ONGOING_EVENT = 0x00000002;
494
495 /**
496 * Bit to be bitwise-ored into the {@link #flags} field that if set,
Scott Mainb8b36452009-04-26 15:50:49 -0700497 * the audio will be repeated until the notification is
498 * cancelled or the notification window is opened.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800499 */
500 public static final int FLAG_INSISTENT = 0x00000004;
501
502 /**
503 * Bit to be bitwise-ored into the {@link #flags} field that should be
Griff Hazen293977b2014-04-28 08:37:20 -0700504 * set if you would only like the sound, vibrate and ticker to be played
505 * if the notification was not already showing.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800506 */
507 public static final int FLAG_ONLY_ALERT_ONCE = 0x00000008;
508
509 /**
510 * Bit to be bitwise-ored into the {@link #flags} field that should be
511 * set if the notification should be canceled when it is clicked by the
Daniel Sandler8aa9ae62012-12-04 23:31:47 -0500512 * user.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800513 */
514 public static final int FLAG_AUTO_CANCEL = 0x00000010;
515
516 /**
517 * Bit to be bitwise-ored into the {@link #flags} field that should be
518 * set if the notification should not be canceled when the user clicks
519 * the Clear all button.
520 */
521 public static final int FLAG_NO_CLEAR = 0x00000020;
522
Dianne Hackbornd8a43f62009-08-17 23:33:56 -0700523 /**
524 * Bit to be bitwise-ored into the {@link #flags} field that should be
525 * set if this notification represents a currently running service. This
526 * will normally be set for you by {@link Service#startForeground}.
527 */
528 public static final int FLAG_FOREGROUND_SERVICE = 0x00000040;
529
Daniel Sandlere46cbd32010-06-17 10:35:26 -0400530 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500531 * Obsolete flag indicating high-priority notifications; use the priority field instead.
Joe Malin8d40d042012-11-05 11:36:40 -0800532 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500533 * @deprecated Use {@link #priority} with a positive value.
Daniel Sandlere46cbd32010-06-17 10:35:26 -0400534 */
Aurimas Liutikas514c5ef2016-05-24 15:22:55 -0700535 @Deprecated
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500536 public static final int FLAG_HIGH_PRIORITY = 0x00000080;
Daniel Sandlere46cbd32010-06-17 10:35:26 -0400537
Griff Hazendfcb0802014-02-11 12:00:00 -0800538 /**
539 * Bit to be bitswise-ored into the {@link #flags} field that should be
540 * set if this notification is relevant to the current device only
541 * and it is not recommended that it bridge to other devices.
542 */
543 public static final int FLAG_LOCAL_ONLY = 0x00000100;
544
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700545 /**
546 * Bit to be bitswise-ored into the {@link #flags} field that should be
547 * set if this notification is the group summary for a group of notifications.
548 * Grouped notifications may display in a cluster or stack on devices which
549 * support such rendering. Requires a group key also be set using {@link Builder#setGroup}.
550 */
551 public static final int FLAG_GROUP_SUMMARY = 0x00000200;
552
Julia Reynoldse46bb372016-03-17 11:05:58 -0400553 /**
554 * Bit to be bitswise-ored into the {@link #flags} field that should be
555 * set if this notification is the group summary for an auto-group of notifications.
556 *
557 * @hide
558 */
559 @SystemApi
560 public static final int FLAG_AUTOGROUP_SUMMARY = 0x00000400;
561
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800562 public int flags;
563
Tor Norbyed9273d62013-05-30 15:59:53 -0700564 /** @hide */
565 @IntDef({PRIORITY_DEFAULT,PRIORITY_LOW,PRIORITY_MIN,PRIORITY_HIGH,PRIORITY_MAX})
566 @Retention(RetentionPolicy.SOURCE)
567 public @interface Priority {}
568
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800569 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500570 * Default notification {@link #priority}. If your application does not prioritize its own
571 * notifications, use this value for all notifications.
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500572 *
573 * @deprecated use {@link NotificationManager#IMPORTANCE_DEFAULT} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500574 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500575 @Deprecated
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500576 public static final int PRIORITY_DEFAULT = 0;
577
578 /**
579 * Lower {@link #priority}, for items that are less important. The UI may choose to show these
580 * items smaller, or at a different position in the list, compared with your app's
581 * {@link #PRIORITY_DEFAULT} items.
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500582 *
583 * @deprecated use {@link NotificationManager#IMPORTANCE_LOW} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500584 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500585 @Deprecated
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500586 public static final int PRIORITY_LOW = -1;
587
588 /**
589 * Lowest {@link #priority}; these items might not be shown to the user except under special
590 * circumstances, such as detailed notification logs.
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500591 *
592 * @deprecated use {@link NotificationManager#IMPORTANCE_MIN} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500593 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500594 @Deprecated
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500595 public static final int PRIORITY_MIN = -2;
596
597 /**
598 * Higher {@link #priority}, for more important notifications or alerts. The UI may choose to
599 * show these items larger, or at a different position in notification lists, compared with
600 * your app's {@link #PRIORITY_DEFAULT} items.
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500601 *
602 * @deprecated use {@link NotificationManager#IMPORTANCE_HIGH} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500603 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500604 @Deprecated
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500605 public static final int PRIORITY_HIGH = 1;
606
607 /**
608 * Highest {@link #priority}, for your application's most important items that require the
609 * user's prompt attention or input.
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500610 *
611 * @deprecated use {@link NotificationManager#IMPORTANCE_HIGH} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500612 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500613 @Deprecated
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500614 public static final int PRIORITY_MAX = 2;
615
616 /**
617 * Relative priority for this notification.
Joe Malin8d40d042012-11-05 11:36:40 -0800618 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500619 * Priority is an indication of how much of the user's valuable attention should be consumed by
620 * this notification. Low-priority notifications may be hidden from the user in certain
621 * situations, while the user might be interrupted for a higher-priority notification. The
Daniel Sandler6738eee2012-11-16 12:03:32 -0500622 * system will make a determination about how to interpret this priority when presenting
623 * the notification.
Chris Wren47c20a12014-06-18 17:27:29 -0400624 *
625 * <p>
626 * A notification that is at least {@link #PRIORITY_HIGH} is more likely to be presented
627 * as a heads-up notification.
628 * </p>
629 *
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500630 * @deprecated use {@link NotificationChannel#getImportance()} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500631 */
Tor Norbyed9273d62013-05-30 15:59:53 -0700632 @Priority
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500633 @Deprecated
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500634 public int priority;
Joe Malin8d40d042012-11-05 11:36:40 -0800635
Dan Sandler26e81cf2014-05-06 10:01:27 -0400636 /**
637 * Accent color (an ARGB integer like the constants in {@link android.graphics.Color})
638 * to be applied by the standard Style templates when presenting this notification.
639 *
640 * The current template design constructs a colorful header image by overlaying the
641 * {@link #icon} image (stenciled in white) atop a field of this color. Alpha components are
642 * ignored.
643 */
Tor Norbye80756e32015-03-02 09:39:27 -0800644 @ColorInt
Dan Sandler26e81cf2014-05-06 10:01:27 -0400645 public int color = COLOR_DEFAULT;
646
647 /**
648 * Special value of {@link #color} telling the system not to decorate this notification with
649 * any special color but instead use default colors when presenting this notification.
650 */
Tor Norbye80756e32015-03-02 09:39:27 -0800651 @ColorInt
Dan Sandler26e81cf2014-05-06 10:01:27 -0400652 public static final int COLOR_DEFAULT = 0; // AKA Color.TRANSPARENT
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600653
654 /**
Adrian Roos4ff3b122016-02-01 12:26:13 -0800655 * Special value of {@link #color} used as a place holder for an invalid color.
Selim Cinek99104832017-01-25 14:47:33 -0800656 * @hide
Adrian Roos4ff3b122016-02-01 12:26:13 -0800657 */
658 @ColorInt
Selim Cinek99104832017-01-25 14:47:33 -0800659 public static final int COLOR_INVALID = 1;
Adrian Roos4ff3b122016-02-01 12:26:13 -0800660
661 /**
Adrian Roosc1a80b02016-04-05 14:54:55 -0700662 * Sphere of visibility of this notification, which affects how and when the SystemUI reveals
663 * the notification's presence and contents in untrusted situations (namely, on the secure
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600664 * lockscreen).
665 *
666 * The default level, {@link #VISIBILITY_PRIVATE}, behaves exactly as notifications have always
667 * done on Android: The notification's {@link #icon} and {@link #tickerText} (if available) are
668 * shown in all situations, but the contents are only available if the device is unlocked for
669 * the appropriate user.
670 *
671 * A more permissive policy can be expressed by {@link #VISIBILITY_PUBLIC}; such a notification
672 * can be read even in an "insecure" context (that is, above a secure lockscreen).
673 * To modify the public version of this notification—for example, to redact some portions—see
674 * {@link Builder#setPublicVersion(Notification)}.
675 *
676 * Finally, a notification can be made {@link #VISIBILITY_SECRET}, which will suppress its icon
677 * and ticker until the user has bypassed the lockscreen.
678 */
Jeff Sharkey30e06bb2017-04-24 11:18:03 -0600679 public @Visibility int visibility;
680
681 /** @hide */
682 @IntDef(prefix = { "VISIBILITY_" }, value = {
683 VISIBILITY_PUBLIC,
684 VISIBILITY_PRIVATE,
685 VISIBILITY_SECRET,
686 })
687 @Retention(RetentionPolicy.SOURCE)
688 public @interface Visibility {}
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600689
Griff Hazenfc3922d2014-08-20 11:56:44 -0700690 /**
691 * Notification visibility: Show this notification in its entirety on all lockscreens.
692 *
693 * {@see #visibility}
694 */
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600695 public static final int VISIBILITY_PUBLIC = 1;
Griff Hazenfc3922d2014-08-20 11:56:44 -0700696
697 /**
698 * Notification visibility: Show this notification on all lockscreens, but conceal sensitive or
699 * private information on secure lockscreens.
700 *
701 * {@see #visibility}
702 */
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600703 public static final int VISIBILITY_PRIVATE = 0;
Griff Hazenfc3922d2014-08-20 11:56:44 -0700704
705 /**
706 * Notification visibility: Do not reveal any part of this notification on a secure lockscreen.
707 *
708 * {@see #visibility}
709 */
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600710 public static final int VISIBILITY_SECRET = -1;
711
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500712 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400713 * Notification category: incoming call (voice or video) or similar synchronous communication request.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500714 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400715 public static final String CATEGORY_CALL = "call";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500716
717 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400718 * Notification category: incoming direct message (SMS, instant message, etc.).
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500719 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400720 public static final String CATEGORY_MESSAGE = "msg";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500721
722 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400723 * Notification category: asynchronous bulk message (email).
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500724 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400725 public static final String CATEGORY_EMAIL = "email";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500726
727 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400728 * Notification category: calendar event.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500729 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400730 public static final String CATEGORY_EVENT = "event";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500731
732 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400733 * Notification category: promotion or advertisement.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500734 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400735 public static final String CATEGORY_PROMO = "promo";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500736
737 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400738 * Notification category: alarm or timer.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500739 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400740 public static final String CATEGORY_ALARM = "alarm";
741
742 /**
743 * Notification category: progress of a long-running background operation.
744 */
745 public static final String CATEGORY_PROGRESS = "progress";
746
747 /**
748 * Notification category: social network or sharing update.
749 */
750 public static final String CATEGORY_SOCIAL = "social";
751
752 /**
753 * Notification category: error in background operation or authentication status.
754 */
755 public static final String CATEGORY_ERROR = "err";
756
757 /**
758 * Notification category: media transport control for playback.
759 */
760 public static final String CATEGORY_TRANSPORT = "transport";
761
762 /**
763 * Notification category: system or device status update. Reserved for system use.
764 */
765 public static final String CATEGORY_SYSTEM = "sys";
766
767 /**
768 * Notification category: indication of running background service.
769 */
770 public static final String CATEGORY_SERVICE = "service";
771
772 /**
John Spurlock0a69c8c2014-03-21 13:30:57 -0400773 * Notification category: a specific, timely recommendation for a single thing.
774 * For example, a news app might want to recommend a news story it believes the user will
775 * want to read next.
776 */
777 public static final String CATEGORY_RECOMMENDATION = "recommendation";
778
779 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400780 * Notification category: ongoing information about device or contextual status.
781 */
782 public static final String CATEGORY_STATUS = "status";
783
784 /**
John Spurlock24d3dad2015-04-02 12:24:02 -0400785 * Notification category: user-scheduled reminder.
786 */
787 public static final String CATEGORY_REMINDER = "reminder";
788
789 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400790 * One of the predefined notification categories (see the <code>CATEGORY_*</code> constants)
791 * that best describes this Notification. May be used by the system for ranking and filtering.
792 */
793 public String category;
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500794
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700795 private String mGroupKey;
796
797 /**
798 * Get the key used to group this notification into a cluster or stack
799 * with other notifications on devices which support such rendering.
800 */
801 public String getGroup() {
802 return mGroupKey;
803 }
804
805 private String mSortKey;
806
807 /**
808 * Get a sort key that orders this notification among other notifications from the
809 * same package. This can be useful if an external sort was already applied and an app
810 * would like to preserve this. Notifications will be sorted lexicographically using this
811 * value, although providing different priorities in addition to providing sort key may
812 * cause this value to be ignored.
813 *
814 * <p>This sort key can also be used to order members of a notification group. See
815 * {@link Builder#setGroup}.
816 *
817 * @see String#compareTo(String)
818 */
819 public String getSortKey() {
820 return mSortKey;
821 }
822
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500823 /**
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400824 * Additional semantic data to be carried around with this Notification.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400825 * <p>
826 * The extras keys defined here are intended to capture the original inputs to {@link Builder}
827 * APIs, and are intended to be used by
828 * {@link android.service.notification.NotificationListenerService} implementations to extract
829 * detailed information from notification objects.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500830 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400831 public Bundle extras = new Bundle();
832
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400833 /**
Felipe Lemedd85da62016-06-28 11:29:54 -0700834 * All pending intents in the notification as the system needs to be able to access them but
835 * touching the extras bundle in the system process is not safe because the bundle may contain
Svet Ganovddb94882016-06-23 19:55:24 -0700836 * custom parcelable objects.
837 *
838 * @hide
839 */
Felipe Lemedd85da62016-06-28 11:29:54 -0700840 public ArraySet<PendingIntent> allPendingIntents;
Svet Ganovddb94882016-06-23 19:55:24 -0700841
842 /**
Dianne Hackborn98305522017-05-05 17:53:53 -0700843 * Token identifying the notification that is applying doze/bgcheck whitelisting to the
844 * pending intents inside of it, so only those will get the behavior.
845 *
846 * @hide
847 */
848 static public IBinder whitelistToken;
849
850 /**
851 * Must be set by a process to start associating tokens with Notification objects
852 * coming in to it. This is set by NotificationManagerService.
853 *
854 * @hide
855 */
856 static public IBinder processWhitelistToken;
857
858 /**
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400859 * {@link #extras} key: this is the title of the notification,
860 * as supplied to {@link Builder#setContentTitle(CharSequence)}.
861 */
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -0500862 public static final String EXTRA_TITLE = "android.title";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400863
864 /**
865 * {@link #extras} key: this is the title of the notification when shown in expanded form,
866 * e.g. as supplied to {@link BigTextStyle#setBigContentTitle(CharSequence)}.
867 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400868 public static final String EXTRA_TITLE_BIG = EXTRA_TITLE + ".big";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400869
870 /**
871 * {@link #extras} key: this is the main text payload, as supplied to
872 * {@link Builder#setContentText(CharSequence)}.
873 */
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -0500874 public static final String EXTRA_TEXT = "android.text";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400875
876 /**
877 * {@link #extras} key: this is a third line of text, as supplied to
878 * {@link Builder#setSubText(CharSequence)}.
879 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400880 public static final String EXTRA_SUB_TEXT = "android.subText";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400881
882 /**
Adrian Roose458aa82015-12-08 16:17:19 -0800883 * {@link #extras} key: this is the remote input history, as supplied to
884 * {@link Builder#setRemoteInputHistory(CharSequence[])}.
Adrian Roos005e7742016-04-13 15:02:20 -0700885 *
886 * Apps can fill this through {@link Builder#setRemoteInputHistory(CharSequence[])}
887 * with the most recent inputs that have been sent through a {@link RemoteInput} of this
888 * Notification and are expected to clear it once the it is no longer relevant (e.g. for chat
889 * notifications once the other party has responded).
890 *
891 * The extra with this key is of type CharSequence[] and contains the most recent entry at
892 * the 0 index, the second most recent at the 1 index, etc.
893 *
894 * @see Builder#setRemoteInputHistory(CharSequence[])
Adrian Roose458aa82015-12-08 16:17:19 -0800895 */
896 public static final String EXTRA_REMOTE_INPUT_HISTORY = "android.remoteInputHistory";
897
898 /**
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400899 * {@link #extras} key: this is a small piece of additional text as supplied to
900 * {@link Builder#setContentInfo(CharSequence)}.
901 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400902 public static final String EXTRA_INFO_TEXT = "android.infoText";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400903
904 /**
905 * {@link #extras} key: this is a line of summary information intended to be shown
906 * alongside expanded notifications, as supplied to (e.g.)
907 * {@link BigTextStyle#setSummaryText(CharSequence)}.
908 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400909 public static final String EXTRA_SUMMARY_TEXT = "android.summaryText";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400910
911 /**
Christoph Studer4600f9b2014-07-22 22:44:43 +0200912 * {@link #extras} key: this is the longer text shown in the big form of a
913 * {@link BigTextStyle} notification, as supplied to
914 * {@link BigTextStyle#bigText(CharSequence)}.
915 */
916 public static final String EXTRA_BIG_TEXT = "android.bigText";
917
918 /**
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400919 * {@link #extras} key: this is the resource ID of the notification's main small icon, as
920 * supplied to {@link Builder#setSmallIcon(int)}.
Julia Reynoldse071abd2017-03-22 10:52:11 -0400921 *
922 * @deprecated Use {@link #getSmallIcon()}, which supports a wider variety of icon sources.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400923 */
Julia Reynoldse071abd2017-03-22 10:52:11 -0400924 @Deprecated
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -0500925 public static final String EXTRA_SMALL_ICON = "android.icon";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400926
927 /**
928 * {@link #extras} key: this is a bitmap to be used instead of the small icon when showing the
929 * notification payload, as
930 * supplied to {@link Builder#setLargeIcon(android.graphics.Bitmap)}.
Julia Reynoldse071abd2017-03-22 10:52:11 -0400931 *
932 * @deprecated Use {@link #getLargeIcon()}, which supports a wider variety of icon sources.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400933 */
Julia Reynoldse071abd2017-03-22 10:52:11 -0400934 @Deprecated
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400935 public static final String EXTRA_LARGE_ICON = "android.largeIcon";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400936
937 /**
938 * {@link #extras} key: this is a bitmap to be used instead of the one from
939 * {@link Builder#setLargeIcon(android.graphics.Bitmap)} when the notification is
940 * shown in its expanded form, as supplied to
941 * {@link BigPictureStyle#bigLargeIcon(android.graphics.Bitmap)}.
942 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400943 public static final String EXTRA_LARGE_ICON_BIG = EXTRA_LARGE_ICON + ".big";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400944
945 /**
946 * {@link #extras} key: this is the progress value supplied to
947 * {@link Builder#setProgress(int, int, boolean)}.
948 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400949 public static final String EXTRA_PROGRESS = "android.progress";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400950
951 /**
952 * {@link #extras} key: this is the maximum value supplied to
953 * {@link Builder#setProgress(int, int, boolean)}.
954 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400955 public static final String EXTRA_PROGRESS_MAX = "android.progressMax";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400956
957 /**
958 * {@link #extras} key: whether the progress bar is indeterminate, supplied to
959 * {@link Builder#setProgress(int, int, boolean)}.
960 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400961 public static final String EXTRA_PROGRESS_INDETERMINATE = "android.progressIndeterminate";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400962
963 /**
964 * {@link #extras} key: whether {@link #when} should be shown as a count-up timer (specifically
965 * a {@link android.widget.Chronometer}) instead of a timestamp, as supplied to
966 * {@link Builder#setUsesChronometer(boolean)}.
967 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400968 public static final String EXTRA_SHOW_CHRONOMETER = "android.showChronometer";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400969
970 /**
Selim Cinek81c23aa2016-02-25 16:23:13 -0800971 * {@link #extras} key: whether the chronometer set on the notification should count down
972 * instead of counting up. Is only relevant if key {@link #EXTRA_SHOW_CHRONOMETER} is present.
Adrian Roos96b7e202016-05-17 13:50:38 -0700973 * This extra is a boolean. The default is false.
Selim Cinek81c23aa2016-02-25 16:23:13 -0800974 */
Adrian Roos96b7e202016-05-17 13:50:38 -0700975 public static final String EXTRA_CHRONOMETER_COUNT_DOWN = "android.chronometerCountDown";
Selim Cinek81c23aa2016-02-25 16:23:13 -0800976
977 /**
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400978 * {@link #extras} key: whether {@link #when} should be shown,
979 * as supplied to {@link Builder#setShowWhen(boolean)}.
980 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400981 public static final String EXTRA_SHOW_WHEN = "android.showWhen";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400982
983 /**
984 * {@link #extras} key: this is a bitmap to be shown in {@link BigPictureStyle} expanded
985 * notifications, supplied to {@link BigPictureStyle#bigPicture(android.graphics.Bitmap)}.
986 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400987 public static final String EXTRA_PICTURE = "android.picture";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400988
989 /**
990 * {@link #extras} key: An array of CharSequences to show in {@link InboxStyle} expanded
991 * notifications, each of which was supplied to {@link InboxStyle#addLine(CharSequence)}.
992 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400993 public static final String EXTRA_TEXT_LINES = "android.textLines";
Dan Sandler842dd772014-05-15 09:36:47 -0400994
995 /**
996 * {@link #extras} key: A string representing the name of the specific
997 * {@link android.app.Notification.Style} used to create this notification.
998 */
Chris Wren91ad5632013-06-05 15:05:57 -0400999 public static final String EXTRA_TEMPLATE = "android.template";
Daniel Sandlerf45564e2013-04-15 15:05:08 -04001000
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001001 /**
Chris Wrene6c48932014-09-29 17:19:27 -04001002 * {@link #extras} key: A String array containing the people that this notification relates to,
1003 * each of which was supplied to {@link Builder#addPerson(String)}.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001004 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -04001005 public static final String EXTRA_PEOPLE = "android.people";
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001006
1007 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -04001008 * Allow certain system-generated notifications to appear before the device is provisioned.
1009 * Only available to notifications coming from the android package.
1010 * @hide
1011 */
Maurice Lam96c10032017-03-29 15:42:38 -07001012 @SystemApi
Jeff Sharkeyd86b8fe2017-06-02 17:36:26 -06001013 @RequiresPermission(android.Manifest.permission.NOTIFICATION_DURING_SETUP)
John Spurlockfd7f1e02014-03-18 16:41:57 -04001014 public static final String EXTRA_ALLOW_DURING_SETUP = "android.allowDuringSetup";
1015
1016 /**
Jose Limae9e3b3b2014-05-18 23:44:50 -07001017 * {@link #extras} key: A
1018 * {@link android.content.ContentUris content URI} pointing to an image that can be displayed
1019 * in the background when the notification is selected. The URI must point to an image stream
1020 * suitable for passing into
1021 * {@link android.graphics.BitmapFactory#decodeStream(java.io.InputStream)
1022 * BitmapFactory.decodeStream}; all other content types will be ignored. The content provider
1023 * URI used for this purpose must require no permissions to read the image data.
1024 */
1025 public static final String EXTRA_BACKGROUND_IMAGE_URI = "android.backgroundImageUri";
1026
1027 /**
Dan Sandler842dd772014-05-15 09:36:47 -04001028 * {@link #extras} key: A
Jeff Browndba34ba2014-06-24 20:46:03 -07001029 * {@link android.media.session.MediaSession.Token} associated with a
Dan Sandler842dd772014-05-15 09:36:47 -04001030 * {@link android.app.Notification.MediaStyle} notification.
1031 */
1032 public static final String EXTRA_MEDIA_SESSION = "android.mediaSession";
1033
1034 /**
Bryan Mawhinneye191f902014-07-22 12:50:09 +01001035 * {@link #extras} key: the indices of actions to be shown in the compact view,
1036 * as supplied to (e.g.) {@link MediaStyle#setShowActionsInCompactView(int...)}.
1037 */
1038 public static final String EXTRA_COMPACT_ACTIONS = "android.compactActions";
1039
Christoph Studer943aa672014-08-03 20:31:16 +02001040 /**
Alex Hillsfc737de2016-03-23 17:33:02 -04001041 * {@link #extras} key: the username to be displayed for all messages sent by the user including
1042 * direct replies
Adrian Roos96b7e202016-05-17 13:50:38 -07001043 * {@link android.app.Notification.MessagingStyle} notification. This extra is a
1044 * {@link CharSequence}
Alex Hillsfc737de2016-03-23 17:33:02 -04001045 */
1046 public static final String EXTRA_SELF_DISPLAY_NAME = "android.selfDisplayName";
1047
1048 /**
Adrian Roos96b7e202016-05-17 13:50:38 -07001049 * {@link #extras} key: a {@link CharSequence} to be displayed as the title to a conversation
Alex Hillsd9b04d92016-04-11 16:38:16 -04001050 * represented by a {@link android.app.Notification.MessagingStyle}
Alex Hillsfc737de2016-03-23 17:33:02 -04001051 */
Alex Hillsd9b04d92016-04-11 16:38:16 -04001052 public static final String EXTRA_CONVERSATION_TITLE = "android.conversationTitle";
Alex Hillsfc737de2016-03-23 17:33:02 -04001053
1054 /**
1055 * {@link #extras} key: an array of {@link android.app.Notification.MessagingStyle.Message}
1056 * bundles provided by a
Adrian Roos96b7e202016-05-17 13:50:38 -07001057 * {@link android.app.Notification.MessagingStyle} notification. This extra is a parcelable
1058 * array of bundles.
Alex Hillsfc737de2016-03-23 17:33:02 -04001059 */
1060 public static final String EXTRA_MESSAGES = "android.messages";
1061
1062 /**
Adrian Roos437cd562017-01-18 15:47:03 -08001063 * {@link #extras} key: an array of
1064 * {@link android.app.Notification.MessagingStyle#addHistoricMessage historic}
1065 * {@link android.app.Notification.MessagingStyle.Message} bundles provided by a
1066 * {@link android.app.Notification.MessagingStyle} notification. This extra is a parcelable
1067 * array of bundles.
1068 */
1069 public static final String EXTRA_HISTORIC_MESSAGES = "android.messages.historic";
1070
1071 /**
Selim Cinek7b9605b2017-01-19 17:36:00 -08001072 * {@link #extras} key: whether the notification should be colorized as
1073 * supplied to {@link Builder#setColorized(boolean)}}.
1074 */
1075 public static final String EXTRA_COLORIZED = "android.colorized";
1076
1077 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04001078 * @hide
1079 */
1080 public static final String EXTRA_BUILDER_APPLICATION_INFO = "android.appInfo";
1081
Selim Cinek247fa012016-02-18 09:50:48 -08001082 /**
1083 * @hide
1084 */
1085 public static final String EXTRA_CONTAINS_CUSTOM_VIEW = "android.contains.customView";
1086
Shane Brennan472a3b32016-12-12 15:28:10 -08001087 /**
1088 * {@link #extras} key: the audio contents of this notification.
1089 *
1090 * This is for use when rendering the notification on an audio-focused interface;
1091 * the audio contents are a complete sound sample that contains the contents/body of the
1092 * notification. This may be used in substitute of a Text-to-Speech reading of the
1093 * notification. For example if the notification represents a voice message this should point
1094 * to the audio of that message.
1095 *
1096 * The data stored under this key should be a String representation of a Uri that contains the
1097 * audio contents in one of the following formats: WAV, PCM 16-bit, AMR-WB.
1098 *
1099 * This extra is unnecessary if you are using {@code MessagingStyle} since each {@code Message}
1100 * has a field for holding data URI. That field can be used for audio.
1101 * See {@code Message#setData}.
1102 *
1103 * Example usage:
1104 * <pre>
1105 * {@code
1106 * Notification.Builder myBuilder = (build your Notification as normal);
1107 * myBuilder.getExtras().putString(EXTRA_AUDIO_CONTENTS_URI, myAudioUri.toString());
1108 * }
1109 * </pre>
1110 */
1111 public static final String EXTRA_AUDIO_CONTENTS_URI = "android.audioContents";
1112
Dan Sandler80eaa592016-04-14 23:34:54 -04001113 /** @hide */
1114 @SystemApi
Jeff Sharkeyd86b8fe2017-06-02 17:36:26 -06001115 @RequiresPermission(android.Manifest.permission.SUBSTITUTE_NOTIFICATION_APP_NAME)
Dan Sandler732bd6c2016-04-12 14:20:32 -04001116 public static final String EXTRA_SUBSTITUTE_APP_NAME = "android.substName";
1117
Dianne Hackbornfb5d4b52017-05-16 17:03:44 -07001118 /**
1119 * This is set on the notification shown by the activity manager about all apps
1120 * running in the background. It indicates that the notification should be shown
1121 * only if any of the given apps do not already have a {@link #FLAG_FOREGROUND_SERVICE}
1122 * notification currently visible to the user. This is a string array of all
1123 * package names of the apps.
1124 * @hide
1125 */
1126 public static final String EXTRA_FOREGROUND_APPS = "android.foregroundApps";
1127
Dan Sandlerd63f9322015-05-06 15:18:49 -04001128 private Icon mSmallIcon;
1129 private Icon mLargeIcon;
1130
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04001131 private String mChannelId;
Julia Reynolds2a128742016-11-28 14:29:25 -05001132 private long mTimeout;
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04001133
Julia Reynolds13d898c2017-02-02 12:22:05 -05001134 private String mShortcutId;
Julia Reynolds3aedded2017-03-31 14:42:09 -04001135 private CharSequence mSettingsText;
Julia Reynolds13d898c2017-02-02 12:22:05 -05001136
Julia Reynoldsa79c3712017-04-21 10:29:57 -04001137 /** @hide */
1138 @IntDef(prefix = { "GROUP_ALERT_" }, value = {
1139 GROUP_ALERT_ALL, GROUP_ALERT_CHILDREN, GROUP_ALERT_SUMMARY
1140 })
1141 @Retention(RetentionPolicy.SOURCE)
1142 public @interface GroupAlertBehavior {}
1143
1144 /**
1145 * Constant for {@link Builder#setGroupAlertBehavior(int)}, meaning that all notifications in a
1146 * group with sound or vibration ought to make sound or vibrate (respectively), so this
1147 * notification will not be muted when it is in a group.
1148 */
1149 public static final int GROUP_ALERT_ALL = 0;
1150
1151 /**
1152 * Constant for {@link Builder#setGroupAlertBehavior(int)}, meaning that all children
1153 * notification in a group should be silenced (no sound or vibration) even if they are posted
1154 * to a {@link NotificationChannel} that has sound and/or vibration. Use this constant to
1155 * mute this notification if this notification is a group child.
1156 *
1157 * <p> For example, you might want to use this constant if you post a number of children
1158 * notifications at once (say, after a periodic sync), and only need to notify the user
1159 * audibly once.
1160 */
1161 public static final int GROUP_ALERT_SUMMARY = 1;
1162
1163 /**
1164 * Constant for {@link Builder#setGroupAlertBehavior(int)}, meaning that the summary
1165 * notification in a group should be silenced (no sound or vibration) even if they are
1166 * posted to a {@link NotificationChannel} that has sound and/or vibration. Use this constant
1167 * to mute this notification if this notification is a group summary.
1168 *
1169 * <p>For example, you might want to use this constant if only the children notifications
1170 * in your group have content and the summary is only used to visually group notifications.
1171 */
1172 public static final int GROUP_ALERT_CHILDREN = 2;
1173
Julia Reynolds2f431e22017-06-07 14:12:09 +00001174 private int mGroupAlertBehavior = GROUP_ALERT_ALL;
Julia Reynoldsa79c3712017-04-21 10:29:57 -04001175
Julia Reynolds13d898c2017-02-02 12:22:05 -05001176 /**
1177 * If this notification is being shown as a badge, always show as a number.
1178 */
1179 public static final int BADGE_ICON_NONE = 0;
1180
1181 /**
1182 * If this notification is being shown as a badge, use the {@link #getSmallIcon()} to
1183 * represent this notification.
1184 */
1185 public static final int BADGE_ICON_SMALL = 1;
1186
1187 /**
1188 * If this notification is being shown as a badge, use the {@link #getLargeIcon()} to
1189 * represent this notification.
1190 */
1191 public static final int BADGE_ICON_LARGE = 2;
Julia Reynolds7d5b4da2017-03-20 10:18:49 -04001192 private int mBadgeIcon = BADGE_ICON_NONE;
Julia Reynolds13d898c2017-02-02 12:22:05 -05001193
Chris Wren51c75102013-07-16 20:49:17 -04001194 /**
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001195 * Structure to encapsulate a named action that can be shown as part of this notification.
1196 * It must include an icon, a label, and a {@link PendingIntent} to be fired when the action is
1197 * selected by the user.
1198 * <p>
Griff Hazen959591e2014-05-15 22:26:18 -07001199 * Apps should use {@link Notification.Builder#addAction(int, CharSequence, PendingIntent)}
1200 * or {@link Notification.Builder#addAction(Notification.Action)}
1201 * to attach actions.
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001202 */
Daniel Sandlerea2a3172013-02-20 22:24:20 -05001203 public static class Action implements Parcelable {
Shane Brennan472a3b32016-12-12 15:28:10 -08001204 /**
1205 * {@link #extras} key: Keys to a {@link Parcelable} {@link ArrayList} of
1206 * {@link RemoteInput}s.
1207 *
1208 * This is intended for {@link RemoteInput}s that only accept data, meaning
1209 * {@link RemoteInput#getAllowFreeFormInput} is false, {@link RemoteInput#getChoices}
1210 * is null or empty, and {@link RemoteInput#getAllowedDataTypes is non-null and not
1211 * empty. These {@link RemoteInput}s will be ignored by devices that do not
1212 * support non-text-based {@link RemoteInput}s. See {@link Builder#build}.
1213 *
1214 * You can test if a RemoteInput matches these constraints using
1215 * {@link RemoteInput#isDataOnly}.
1216 */
1217 private static final String EXTRA_DATA_ONLY_INPUTS = "android.extra.DATA_ONLY_INPUTS";
1218
Griff Hazen959591e2014-05-15 22:26:18 -07001219 private final Bundle mExtras;
Dan Sandler86647982015-05-13 23:41:13 -04001220 private Icon mIcon;
Griff Hazen61a9e862014-05-22 16:05:19 -07001221 private final RemoteInput[] mRemoteInputs;
Alex Hills1f27f882017-01-12 11:24:46 -05001222 private boolean mAllowGeneratedReplies = true;
Griff Hazen959591e2014-05-15 22:26:18 -07001223
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001224 /**
1225 * Small icon representing the action.
Dan Sandler86647982015-05-13 23:41:13 -04001226 *
1227 * @deprecated Use {@link Action#getIcon()} instead.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001228 */
Dan Sandler86647982015-05-13 23:41:13 -04001229 @Deprecated
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001230 public int icon;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001231
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001232 /**
1233 * Title of the action.
1234 */
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001235 public CharSequence title;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001236
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001237 /**
1238 * Intent to send when the user invokes this action. May be null, in which case the action
1239 * may be rendered in a disabled presentation by the system UI.
1240 */
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001241 public PendingIntent actionIntent;
Griff Hazen959591e2014-05-15 22:26:18 -07001242
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001243 private Action(Parcel in) {
Dan Sandler86647982015-05-13 23:41:13 -04001244 if (in.readInt() != 0) {
1245 mIcon = Icon.CREATOR.createFromParcel(in);
Dan Sandler68079d52015-07-22 10:45:30 -04001246 if (mIcon.getType() == Icon.TYPE_RESOURCE) {
1247 icon = mIcon.getResId();
1248 }
Dan Sandler86647982015-05-13 23:41:13 -04001249 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001250 title = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
1251 if (in.readInt() == 1) {
1252 actionIntent = PendingIntent.CREATOR.createFromParcel(in);
1253 }
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001254 mExtras = Bundle.setDefusable(in.readBundle(), true);
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001255 mRemoteInputs = in.createTypedArray(RemoteInput.CREATOR);
Alex Hills42b0c4d2016-04-26 13:35:36 -04001256 mAllowGeneratedReplies = in.readInt() == 1;
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001257 }
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001258
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001259 /**
Dan Sandler86647982015-05-13 23:41:13 -04001260 * @deprecated Use {@link android.app.Notification.Action.Builder}.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001261 */
Dan Sandler86647982015-05-13 23:41:13 -04001262 @Deprecated
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001263 public Action(int icon, CharSequence title, PendingIntent intent) {
Alex Hills1f27f882017-01-12 11:24:46 -05001264 this(Icon.createWithResource("", icon), title, intent, new Bundle(), null, true);
Griff Hazen959591e2014-05-15 22:26:18 -07001265 }
1266
Adrian Roos7af53622016-10-12 13:44:05 -07001267 /** Keep in sync with {@link Notification.Action.Builder#Builder(Action)}! */
Dan Sandler86647982015-05-13 23:41:13 -04001268 private Action(Icon icon, CharSequence title, PendingIntent intent, Bundle extras,
Alex Hills42b0c4d2016-04-26 13:35:36 -04001269 RemoteInput[] remoteInputs, boolean allowGeneratedReplies) {
Dan Sandler86647982015-05-13 23:41:13 -04001270 this.mIcon = icon;
Gus Prevasf5bff46f2015-08-24 10:34:28 -04001271 if (icon != null && icon.getType() == Icon.TYPE_RESOURCE) {
1272 this.icon = icon.getResId();
1273 }
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001274 this.title = title;
1275 this.actionIntent = intent;
Griff Hazen959591e2014-05-15 22:26:18 -07001276 this.mExtras = extras != null ? extras : new Bundle();
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001277 this.mRemoteInputs = remoteInputs;
Alex Hills42b0c4d2016-04-26 13:35:36 -04001278 this.mAllowGeneratedReplies = allowGeneratedReplies;
Griff Hazen959591e2014-05-15 22:26:18 -07001279 }
1280
1281 /**
Dan Sandler86647982015-05-13 23:41:13 -04001282 * Return an icon representing the action.
1283 */
1284 public Icon getIcon() {
1285 if (mIcon == null && icon != 0) {
1286 // you snuck an icon in here without using the builder; let's try to keep it
1287 mIcon = Icon.createWithResource("", icon);
1288 }
1289 return mIcon;
1290 }
1291
1292 /**
Griff Hazen959591e2014-05-15 22:26:18 -07001293 * Get additional metadata carried around with this Action.
1294 */
1295 public Bundle getExtras() {
1296 return mExtras;
1297 }
1298
1299 /**
Alex Hills42b0c4d2016-04-26 13:35:36 -04001300 * Return whether the platform should automatically generate possible replies for this
1301 * {@link Action}
1302 */
1303 public boolean getAllowGeneratedReplies() {
1304 return mAllowGeneratedReplies;
1305 }
1306
1307 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001308 * Get the list of inputs to be collected from the user when this action is sent.
Shane Brennan472a3b32016-12-12 15:28:10 -08001309 * May return null if no remote inputs were added. Only returns inputs which accept
1310 * a text input. For inputs which only accept data use {@link #getDataOnlyRemoteInputs}.
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001311 */
1312 public RemoteInput[] getRemoteInputs() {
1313 return mRemoteInputs;
1314 }
1315
1316 /**
Shane Brennan472a3b32016-12-12 15:28:10 -08001317 * Get the list of inputs to be collected from the user that ONLY accept data when this
1318 * action is sent. These remote inputs are guaranteed to return true on a call to
1319 * {@link RemoteInput#isDataOnly}.
1320 *
Shane Brennanf670d6c2017-04-25 13:05:45 -07001321 * Returns null if there are no data-only remote inputs.
Shane Brennan472a3b32016-12-12 15:28:10 -08001322 *
1323 * This method exists so that legacy RemoteInput collectors that pre-date the addition
1324 * of non-textual RemoteInputs do not access these remote inputs.
1325 */
1326 public RemoteInput[] getDataOnlyRemoteInputs() {
1327 return (RemoteInput[]) mExtras.getParcelableArray(EXTRA_DATA_ONLY_INPUTS);
1328 }
1329
1330 /**
Griff Hazen959591e2014-05-15 22:26:18 -07001331 * Builder class for {@link Action} objects.
1332 */
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001333 public static final class Builder {
Dan Sandler86647982015-05-13 23:41:13 -04001334 private final Icon mIcon;
Griff Hazen959591e2014-05-15 22:26:18 -07001335 private final CharSequence mTitle;
1336 private final PendingIntent mIntent;
Alex Hills1f27f882017-01-12 11:24:46 -05001337 private boolean mAllowGeneratedReplies = true;
Griff Hazen959591e2014-05-15 22:26:18 -07001338 private final Bundle mExtras;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001339 private ArrayList<RemoteInput> mRemoteInputs;
Griff Hazen959591e2014-05-15 22:26:18 -07001340
1341 /**
1342 * Construct a new builder for {@link Action} object.
1343 * @param icon icon to show for this action
1344 * @param title the title of the action
1345 * @param intent the {@link PendingIntent} to fire when users trigger this action
1346 */
Dan Sandler86647982015-05-13 23:41:13 -04001347 @Deprecated
Griff Hazen959591e2014-05-15 22:26:18 -07001348 public Builder(int icon, CharSequence title, PendingIntent intent) {
Adrian Roos7af53622016-10-12 13:44:05 -07001349 this(Icon.createWithResource("", icon), title, intent);
Dan Sandler86647982015-05-13 23:41:13 -04001350 }
1351
1352 /**
1353 * Construct a new builder for {@link Action} object.
1354 * @param icon icon to show for this action
1355 * @param title the title of the action
1356 * @param intent the {@link PendingIntent} to fire when users trigger this action
1357 */
1358 public Builder(Icon icon, CharSequence title, PendingIntent intent) {
Alex Hills1f27f882017-01-12 11:24:46 -05001359 this(icon, title, intent, new Bundle(), null, true);
Griff Hazen959591e2014-05-15 22:26:18 -07001360 }
1361
1362 /**
1363 * Construct a new builder for {@link Action} object using the fields from an
1364 * {@link Action}.
1365 * @param action the action to read fields from.
1366 */
1367 public Builder(Action action) {
Adrian Roos7af53622016-10-12 13:44:05 -07001368 this(action.getIcon(), action.title, action.actionIntent,
1369 new Bundle(action.mExtras), action.getRemoteInputs(),
1370 action.getAllowGeneratedReplies());
Griff Hazen959591e2014-05-15 22:26:18 -07001371 }
1372
Dan Sandler86647982015-05-13 23:41:13 -04001373 private Builder(Icon icon, CharSequence title, PendingIntent intent, Bundle extras,
Adrian Roos7af53622016-10-12 13:44:05 -07001374 RemoteInput[] remoteInputs, boolean allowGeneratedReplies) {
Griff Hazen959591e2014-05-15 22:26:18 -07001375 mIcon = icon;
1376 mTitle = title;
1377 mIntent = intent;
1378 mExtras = extras;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001379 if (remoteInputs != null) {
1380 mRemoteInputs = new ArrayList<RemoteInput>(remoteInputs.length);
1381 Collections.addAll(mRemoteInputs, remoteInputs);
1382 }
Adrian Roos7af53622016-10-12 13:44:05 -07001383 mAllowGeneratedReplies = allowGeneratedReplies;
Griff Hazen959591e2014-05-15 22:26:18 -07001384 }
1385
1386 /**
1387 * Merge additional metadata into this builder.
1388 *
1389 * <p>Values within the Bundle will replace existing extras values in this Builder.
1390 *
1391 * @see Notification.Action#extras
1392 */
1393 public Builder addExtras(Bundle extras) {
1394 if (extras != null) {
1395 mExtras.putAll(extras);
1396 }
1397 return this;
1398 }
1399
1400 /**
1401 * Get the metadata Bundle used by this Builder.
1402 *
1403 * <p>The returned Bundle is shared with this Builder.
1404 */
1405 public Bundle getExtras() {
1406 return mExtras;
1407 }
1408
1409 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001410 * Add an input to be collected from the user when this action is sent.
1411 * Response values can be retrieved from the fired intent by using the
1412 * {@link RemoteInput#getResultsFromIntent} function.
1413 * @param remoteInput a {@link RemoteInput} to add to the action
1414 * @return this object for method chaining
1415 */
1416 public Builder addRemoteInput(RemoteInput remoteInput) {
1417 if (mRemoteInputs == null) {
1418 mRemoteInputs = new ArrayList<RemoteInput>();
1419 }
1420 mRemoteInputs.add(remoteInput);
1421 return this;
1422 }
1423
1424 /**
Alex Hills42b0c4d2016-04-26 13:35:36 -04001425 * Set whether the platform should automatically generate possible replies to add to
1426 * {@link RemoteInput#getChoices()}. If the {@link Action} doesn't have a
1427 * {@link RemoteInput}, this has no effect.
1428 * @param allowGeneratedReplies {@code true} to allow generated replies, {@code false}
1429 * otherwise
1430 * @return this object for method chaining
Alex Hills1f27f882017-01-12 11:24:46 -05001431 * The default value is {@code true}
Alex Hills42b0c4d2016-04-26 13:35:36 -04001432 */
1433 public Builder setAllowGeneratedReplies(boolean allowGeneratedReplies) {
1434 mAllowGeneratedReplies = allowGeneratedReplies;
1435 return this;
1436 }
1437
1438 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001439 * Apply an extender to this action builder. Extenders may be used to add
1440 * metadata or change options on this builder.
1441 */
Griff Hazen61a9e862014-05-22 16:05:19 -07001442 public Builder extend(Extender extender) {
1443 extender.extend(this);
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001444 return this;
1445 }
1446
1447 /**
Griff Hazen959591e2014-05-15 22:26:18 -07001448 * Combine all of the options that have been set and return a new {@link Action}
1449 * object.
1450 * @return the built action
1451 */
1452 public Action build() {
Shane Brennan472a3b32016-12-12 15:28:10 -08001453 ArrayList<RemoteInput> dataOnlyInputs = new ArrayList<>();
1454 RemoteInput[] previousDataInputs =
1455 (RemoteInput[]) mExtras.getParcelableArray(EXTRA_DATA_ONLY_INPUTS);
Felipe Lemedfd11962017-01-18 18:38:46 -08001456 if (previousDataInputs != null) {
Shane Brennan472a3b32016-12-12 15:28:10 -08001457 for (RemoteInput input : previousDataInputs) {
1458 dataOnlyInputs.add(input);
1459 }
1460 }
1461 List<RemoteInput> textInputs = new ArrayList<>();
1462 if (mRemoteInputs != null) {
1463 for (RemoteInput input : mRemoteInputs) {
1464 if (input.isDataOnly()) {
1465 dataOnlyInputs.add(input);
1466 } else {
1467 textInputs.add(input);
1468 }
1469 }
1470 }
1471 if (!dataOnlyInputs.isEmpty()) {
1472 RemoteInput[] dataInputsArr =
1473 dataOnlyInputs.toArray(new RemoteInput[dataOnlyInputs.size()]);
1474 mExtras.putParcelableArray(EXTRA_DATA_ONLY_INPUTS, dataInputsArr);
1475 }
1476 RemoteInput[] textInputsArr = textInputs.isEmpty()
1477 ? null : textInputs.toArray(new RemoteInput[textInputs.size()]);
1478 return new Action(mIcon, mTitle, mIntent, mExtras, textInputsArr,
Alex Hills42b0c4d2016-04-26 13:35:36 -04001479 mAllowGeneratedReplies);
Griff Hazen959591e2014-05-15 22:26:18 -07001480 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001481 }
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001482
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001483 @Override
1484 public Action clone() {
1485 return new Action(
Dan Sandler86647982015-05-13 23:41:13 -04001486 getIcon(),
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001487 title,
1488 actionIntent, // safe to alias
Julia Reynolds53c934c2016-09-16 14:03:43 -04001489 mExtras == null ? new Bundle() : new Bundle(mExtras),
Alex Hills42b0c4d2016-04-26 13:35:36 -04001490 getRemoteInputs(),
1491 getAllowGeneratedReplies());
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001492 }
1493 @Override
1494 public int describeContents() {
1495 return 0;
1496 }
1497 @Override
1498 public void writeToParcel(Parcel out, int flags) {
Dan Sandler86647982015-05-13 23:41:13 -04001499 final Icon ic = getIcon();
1500 if (ic != null) {
1501 out.writeInt(1);
1502 ic.writeToParcel(out, 0);
1503 } else {
1504 out.writeInt(0);
1505 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001506 TextUtils.writeToParcel(title, out, flags);
1507 if (actionIntent != null) {
1508 out.writeInt(1);
1509 actionIntent.writeToParcel(out, flags);
1510 } else {
1511 out.writeInt(0);
1512 }
Griff Hazen959591e2014-05-15 22:26:18 -07001513 out.writeBundle(mExtras);
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001514 out.writeTypedArray(mRemoteInputs, flags);
Alex Hills42b0c4d2016-04-26 13:35:36 -04001515 out.writeInt(mAllowGeneratedReplies ? 1 : 0);
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001516 }
Griff Hazen959591e2014-05-15 22:26:18 -07001517 public static final Parcelable.Creator<Action> CREATOR =
1518 new Parcelable.Creator<Action>() {
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001519 public Action createFromParcel(Parcel in) {
1520 return new Action(in);
1521 }
1522 public Action[] newArray(int size) {
1523 return new Action[size];
1524 }
1525 };
Griff Hazen61a9e862014-05-22 16:05:19 -07001526
1527 /**
1528 * Extender interface for use with {@link Builder#extend}. Extenders may be used to add
1529 * metadata or change options on an action builder.
1530 */
1531 public interface Extender {
1532 /**
1533 * Apply this extender to a notification action builder.
1534 * @param builder the builder to be modified.
1535 * @return the build object for chaining.
1536 */
1537 public Builder extend(Builder builder);
1538 }
1539
1540 /**
1541 * Wearable extender for notification actions. To add extensions to an action,
1542 * create a new {@link android.app.Notification.Action.WearableExtender} object using
1543 * the {@code WearableExtender()} constructor and apply it to a
1544 * {@link android.app.Notification.Action.Builder} using
1545 * {@link android.app.Notification.Action.Builder#extend}.
1546 *
1547 * <pre class="prettyprint">
1548 * Notification.Action action = new Notification.Action.Builder(
1549 * R.drawable.archive_all, "Archive all", actionIntent)
Griff Hazen14f57992014-05-26 09:07:14 -07001550 * .extend(new Notification.Action.WearableExtender()
Griff Hazen61a9e862014-05-22 16:05:19 -07001551 * .setAvailableOffline(false))
Griff Hazen14f57992014-05-26 09:07:14 -07001552 * .build();</pre>
Griff Hazen61a9e862014-05-22 16:05:19 -07001553 */
1554 public static final class WearableExtender implements Extender {
1555 /** Notification action extra which contains wearable extensions */
1556 private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS";
1557
Pete Gastaf6781d2014-10-07 15:17:05 -04001558 // Keys within EXTRA_WEARABLE_EXTENSIONS for wearable options.
Griff Hazen61a9e862014-05-22 16:05:19 -07001559 private static final String KEY_FLAGS = "flags";
Pete Gastaf6781d2014-10-07 15:17:05 -04001560 private static final String KEY_IN_PROGRESS_LABEL = "inProgressLabel";
1561 private static final String KEY_CONFIRM_LABEL = "confirmLabel";
1562 private static final String KEY_CANCEL_LABEL = "cancelLabel";
Griff Hazen61a9e862014-05-22 16:05:19 -07001563
1564 // Flags bitwise-ored to mFlags
1565 private static final int FLAG_AVAILABLE_OFFLINE = 0x1;
Alex Hills9ab3a232016-04-05 14:54:56 -04001566 private static final int FLAG_HINT_LAUNCHES_ACTIVITY = 1 << 1;
Alex Hills9f087612016-06-07 09:08:59 -04001567 private static final int FLAG_HINT_DISPLAY_INLINE = 1 << 2;
Griff Hazen61a9e862014-05-22 16:05:19 -07001568
1569 // Default value for flags integer
1570 private static final int DEFAULT_FLAGS = FLAG_AVAILABLE_OFFLINE;
1571
1572 private int mFlags = DEFAULT_FLAGS;
1573
Pete Gastaf6781d2014-10-07 15:17:05 -04001574 private CharSequence mInProgressLabel;
1575 private CharSequence mConfirmLabel;
1576 private CharSequence mCancelLabel;
1577
Griff Hazen61a9e862014-05-22 16:05:19 -07001578 /**
1579 * Create a {@link android.app.Notification.Action.WearableExtender} with default
1580 * options.
1581 */
1582 public WearableExtender() {
1583 }
1584
1585 /**
1586 * Create a {@link android.app.Notification.Action.WearableExtender} by reading
1587 * wearable options present in an existing notification action.
1588 * @param action the notification action to inspect.
1589 */
1590 public WearableExtender(Action action) {
1591 Bundle wearableBundle = action.getExtras().getBundle(EXTRA_WEARABLE_EXTENSIONS);
1592 if (wearableBundle != null) {
1593 mFlags = wearableBundle.getInt(KEY_FLAGS, DEFAULT_FLAGS);
Pete Gastaf6781d2014-10-07 15:17:05 -04001594 mInProgressLabel = wearableBundle.getCharSequence(KEY_IN_PROGRESS_LABEL);
1595 mConfirmLabel = wearableBundle.getCharSequence(KEY_CONFIRM_LABEL);
1596 mCancelLabel = wearableBundle.getCharSequence(KEY_CANCEL_LABEL);
Griff Hazen61a9e862014-05-22 16:05:19 -07001597 }
1598 }
1599
1600 /**
1601 * Apply wearable extensions to a notification action that is being built. This is
1602 * typically called by the {@link android.app.Notification.Action.Builder#extend}
1603 * method of {@link android.app.Notification.Action.Builder}.
1604 */
1605 @Override
1606 public Action.Builder extend(Action.Builder builder) {
1607 Bundle wearableBundle = new Bundle();
1608
1609 if (mFlags != DEFAULT_FLAGS) {
1610 wearableBundle.putInt(KEY_FLAGS, mFlags);
1611 }
Pete Gastaf6781d2014-10-07 15:17:05 -04001612 if (mInProgressLabel != null) {
1613 wearableBundle.putCharSequence(KEY_IN_PROGRESS_LABEL, mInProgressLabel);
1614 }
1615 if (mConfirmLabel != null) {
1616 wearableBundle.putCharSequence(KEY_CONFIRM_LABEL, mConfirmLabel);
1617 }
1618 if (mCancelLabel != null) {
1619 wearableBundle.putCharSequence(KEY_CANCEL_LABEL, mCancelLabel);
1620 }
Griff Hazen61a9e862014-05-22 16:05:19 -07001621
1622 builder.getExtras().putBundle(EXTRA_WEARABLE_EXTENSIONS, wearableBundle);
1623 return builder;
1624 }
1625
1626 @Override
1627 public WearableExtender clone() {
1628 WearableExtender that = new WearableExtender();
1629 that.mFlags = this.mFlags;
Pete Gastaf6781d2014-10-07 15:17:05 -04001630 that.mInProgressLabel = this.mInProgressLabel;
1631 that.mConfirmLabel = this.mConfirmLabel;
1632 that.mCancelLabel = this.mCancelLabel;
Griff Hazen61a9e862014-05-22 16:05:19 -07001633 return that;
1634 }
1635
1636 /**
1637 * Set whether this action is available when the wearable device is not connected to
1638 * a companion device. The user can still trigger this action when the wearable device is
1639 * offline, but a visual hint will indicate that the action may not be available.
1640 * Defaults to true.
1641 */
1642 public WearableExtender setAvailableOffline(boolean availableOffline) {
1643 setFlag(FLAG_AVAILABLE_OFFLINE, availableOffline);
1644 return this;
1645 }
1646
1647 /**
1648 * Get whether this action is available when the wearable device is not connected to
1649 * a companion device. The user can still trigger this action when the wearable device is
1650 * offline, but a visual hint will indicate that the action may not be available.
1651 * Defaults to true.
1652 */
1653 public boolean isAvailableOffline() {
1654 return (mFlags & FLAG_AVAILABLE_OFFLINE) != 0;
1655 }
1656
1657 private void setFlag(int mask, boolean value) {
1658 if (value) {
1659 mFlags |= mask;
1660 } else {
1661 mFlags &= ~mask;
1662 }
1663 }
Pete Gastaf6781d2014-10-07 15:17:05 -04001664
1665 /**
1666 * Set a label to display while the wearable is preparing to automatically execute the
1667 * action. This is usually a 'ing' verb ending in ellipsis like "Sending..."
1668 *
1669 * @param label the label to display while the action is being prepared to execute
1670 * @return this object for method chaining
1671 */
1672 public WearableExtender setInProgressLabel(CharSequence label) {
1673 mInProgressLabel = label;
1674 return this;
1675 }
1676
1677 /**
1678 * Get the label to display while the wearable is preparing to automatically execute
1679 * the action. This is usually a 'ing' verb ending in ellipsis like "Sending..."
1680 *
1681 * @return the label to display while the action is being prepared to execute
1682 */
1683 public CharSequence getInProgressLabel() {
1684 return mInProgressLabel;
1685 }
1686
1687 /**
1688 * Set a label to display to confirm that the action should be executed.
1689 * This is usually an imperative verb like "Send".
1690 *
1691 * @param label the label to confirm the action should be executed
1692 * @return this object for method chaining
1693 */
1694 public WearableExtender setConfirmLabel(CharSequence label) {
1695 mConfirmLabel = label;
1696 return this;
1697 }
1698
1699 /**
1700 * Get the label to display to confirm that the action should be executed.
1701 * This is usually an imperative verb like "Send".
1702 *
1703 * @return the label to confirm the action should be executed
1704 */
1705 public CharSequence getConfirmLabel() {
1706 return mConfirmLabel;
1707 }
1708
1709 /**
1710 * Set a label to display to cancel the action.
1711 * This is usually an imperative verb, like "Cancel".
1712 *
1713 * @param label the label to display to cancel the action
1714 * @return this object for method chaining
1715 */
1716 public WearableExtender setCancelLabel(CharSequence label) {
1717 mCancelLabel = label;
1718 return this;
1719 }
1720
1721 /**
1722 * Get the label to display to cancel the action.
1723 * This is usually an imperative verb like "Cancel".
1724 *
1725 * @return the label to display to cancel the action
1726 */
1727 public CharSequence getCancelLabel() {
1728 return mCancelLabel;
1729 }
Alex Hills9ab3a232016-04-05 14:54:56 -04001730
1731 /**
1732 * Set a hint that this Action will launch an {@link Activity} directly, telling the
1733 * platform that it can generate the appropriate transitions.
1734 * @param hintLaunchesActivity {@code true} if the content intent will launch
1735 * an activity and transitions should be generated, false otherwise.
1736 * @return this object for method chaining
1737 */
Alex Hills4ec3ff42016-04-12 11:36:18 -04001738 public WearableExtender setHintLaunchesActivity(
Alex Hills9ab3a232016-04-05 14:54:56 -04001739 boolean hintLaunchesActivity) {
1740 setFlag(FLAG_HINT_LAUNCHES_ACTIVITY, hintLaunchesActivity);
1741 return this;
1742 }
1743
1744 /**
1745 * Get a hint that this Action will launch an {@link Activity} directly, telling the
1746 * platform that it can generate the appropriate transitions
1747 * @return {@code true} if the content intent will launch an activity and transitions
1748 * should be generated, false otherwise. The default value is {@code false} if this was
1749 * never set.
1750 */
Alex Hills4ec3ff42016-04-12 11:36:18 -04001751 public boolean getHintLaunchesActivity() {
Alex Hills9ab3a232016-04-05 14:54:56 -04001752 return (mFlags & FLAG_HINT_LAUNCHES_ACTIVITY) != 0;
1753 }
Alex Hills9f087612016-06-07 09:08:59 -04001754
1755 /**
1756 * Set a hint that this Action should be displayed inline.
1757 *
1758 * @param hintDisplayInline {@code true} if action should be displayed inline, false
1759 * otherwise
1760 * @return this object for method chaining
1761 */
1762 public WearableExtender setHintDisplayActionInline(
1763 boolean hintDisplayInline) {
1764 setFlag(FLAG_HINT_DISPLAY_INLINE, hintDisplayInline);
1765 return this;
1766 }
1767
1768 /**
1769 * Get a hint that this Action should be displayed inline.
1770 *
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08001771 * @return {@code true} if the Action should be displayed inline, {@code false}
Alex Hills9f087612016-06-07 09:08:59 -04001772 * otherwise. The default value is {@code false} if this was never set.
1773 */
1774 public boolean getHintDisplayActionInline() {
1775 return (mFlags & FLAG_HINT_DISPLAY_INLINE) != 0;
1776 }
Griff Hazen61a9e862014-05-22 16:05:19 -07001777 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001778 }
1779
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001780 /**
1781 * Array of all {@link Action} structures attached to this notification by
1782 * {@link Builder#addAction(int, CharSequence, PendingIntent)}. Mostly useful for instances of
1783 * {@link android.service.notification.NotificationListenerService} that provide an alternative
1784 * interface for invoking actions.
1785 */
Daniel Sandlerea2a3172013-02-20 22:24:20 -05001786 public Action[] actions;
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001787
1788 /**
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001789 * Replacement version of this notification whose content will be shown
1790 * in an insecure context such as atop a secure keyguard. See {@link #visibility}
1791 * and {@link #VISIBILITY_PUBLIC}.
1792 */
1793 public Notification publicVersion;
1794
1795 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001796 * Constructs a Notification object with default values.
Joe Onorato46439ce2010-11-19 13:56:21 -08001797 * You might want to consider using {@link Builder} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001798 */
1799 public Notification()
1800 {
1801 this.when = System.currentTimeMillis();
Selim Cinekb85f36fd2016-04-20 18:46:36 -07001802 this.creationTime = System.currentTimeMillis();
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001803 this.priority = PRIORITY_DEFAULT;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001804 }
1805
1806 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001807 * @hide
1808 */
1809 public Notification(Context context, int icon, CharSequence tickerText, long when,
1810 CharSequence contentTitle, CharSequence contentText, Intent contentIntent)
1811 {
Chris Wren1ce4b6d2015-06-11 10:19:43 -04001812 new Builder(context)
1813 .setWhen(when)
1814 .setSmallIcon(icon)
1815 .setTicker(tickerText)
1816 .setContentTitle(contentTitle)
1817 .setContentText(contentText)
1818 .setContentIntent(PendingIntent.getActivity(context, 0, contentIntent, 0))
1819 .buildInto(this);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001820 }
1821
1822 /**
1823 * Constructs a Notification object with the information needed to
1824 * have a status bar icon without the standard expanded view.
1825 *
1826 * @param icon The resource id of the icon to put in the status bar.
1827 * @param tickerText The text that flows by in the status bar when the notification first
1828 * activates.
1829 * @param when The time to show in the time field. In the System.currentTimeMillis
1830 * timebase.
Joe Onorato46439ce2010-11-19 13:56:21 -08001831 *
1832 * @deprecated Use {@link Builder} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001833 */
Joe Onorato46439ce2010-11-19 13:56:21 -08001834 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001835 public Notification(int icon, CharSequence tickerText, long when)
1836 {
1837 this.icon = icon;
1838 this.tickerText = tickerText;
1839 this.when = when;
Selim Cinekb85f36fd2016-04-20 18:46:36 -07001840 this.creationTime = System.currentTimeMillis();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001841 }
1842
1843 /**
1844 * Unflatten the notification from a parcel.
1845 */
Svet Ganovddb94882016-06-23 19:55:24 -07001846 @SuppressWarnings("unchecked")
1847 public Notification(Parcel parcel) {
1848 // IMPORTANT: Add unmarshaling code in readFromParcel as the pending
1849 // intents in extras are always written as the last entry.
1850 readFromParcelImpl(parcel);
1851 // Must be read last!
Felipe Lemedd85da62016-06-28 11:29:54 -07001852 allPendingIntents = (ArraySet<PendingIntent>) parcel.readArraySet(null);
Svet Ganovddb94882016-06-23 19:55:24 -07001853 }
1854
1855 private void readFromParcelImpl(Parcel parcel)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001856 {
1857 int version = parcel.readInt();
1858
Dianne Hackborn98305522017-05-05 17:53:53 -07001859 whitelistToken = parcel.readStrongBinder();
1860 if (whitelistToken == null) {
1861 whitelistToken = processWhitelistToken;
1862 }
1863 // Propagate this token to all pending intents that are unmarshalled from the parcel.
1864 parcel.setClassCookie(PendingIntent.class, whitelistToken);
1865
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001866 when = parcel.readLong();
Selim Cinekb85f36fd2016-04-20 18:46:36 -07001867 creationTime = parcel.readLong();
Dan Sandler3936e7a2015-05-19 20:59:12 -04001868 if (parcel.readInt() != 0) {
1869 mSmallIcon = Icon.CREATOR.createFromParcel(parcel);
Dan Sandler4e787062015-06-17 15:09:48 -04001870 if (mSmallIcon.getType() == Icon.TYPE_RESOURCE) {
1871 icon = mSmallIcon.getResId();
1872 }
Dan Sandler3936e7a2015-05-19 20:59:12 -04001873 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001874 number = parcel.readInt();
1875 if (parcel.readInt() != 0) {
1876 contentIntent = PendingIntent.CREATOR.createFromParcel(parcel);
1877 }
1878 if (parcel.readInt() != 0) {
1879 deleteIntent = PendingIntent.CREATOR.createFromParcel(parcel);
1880 }
1881 if (parcel.readInt() != 0) {
1882 tickerText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
1883 }
1884 if (parcel.readInt() != 0) {
Joe Onorato46439ce2010-11-19 13:56:21 -08001885 tickerView = RemoteViews.CREATOR.createFromParcel(parcel);
Joe Onoratoef1e7762010-09-17 18:38:38 -04001886 }
1887 if (parcel.readInt() != 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001888 contentView = RemoteViews.CREATOR.createFromParcel(parcel);
1889 }
Joe Onorato561d3852010-11-20 18:09:34 -08001890 if (parcel.readInt() != 0) {
Dan Sandlerd63f9322015-05-06 15:18:49 -04001891 mLargeIcon = Icon.CREATOR.createFromParcel(parcel);
Joe Onorato561d3852010-11-20 18:09:34 -08001892 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001893 defaults = parcel.readInt();
1894 flags = parcel.readInt();
1895 if (parcel.readInt() != 0) {
1896 sound = Uri.CREATOR.createFromParcel(parcel);
1897 }
1898
1899 audioStreamType = parcel.readInt();
John Spurlockc0650f022014-07-19 13:22:39 -04001900 if (parcel.readInt() != 0) {
1901 audioAttributes = AudioAttributes.CREATOR.createFromParcel(parcel);
1902 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001903 vibrate = parcel.createLongArray();
1904 ledARGB = parcel.readInt();
1905 ledOnMS = parcel.readInt();
1906 ledOffMS = parcel.readInt();
1907 iconLevel = parcel.readInt();
Daniel Sandlere46cbd32010-06-17 10:35:26 -04001908
1909 if (parcel.readInt() != 0) {
1910 fullScreenIntent = PendingIntent.CREATOR.createFromParcel(parcel);
1911 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001912
1913 priority = parcel.readInt();
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001914
John Spurlockfd7f1e02014-03-18 16:41:57 -04001915 category = parcel.readString();
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001916
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001917 mGroupKey = parcel.readString();
1918
1919 mSortKey = parcel.readString();
1920
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001921 extras = Bundle.setDefusable(parcel.readBundle(), true); // may be null
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001922
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001923 actions = parcel.createTypedArray(Action.CREATOR); // may be null
1924
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001925 if (parcel.readInt() != 0) {
Daniel Sandlerf3b73432012-03-27 15:01:25 -04001926 bigContentView = RemoteViews.CREATOR.createFromParcel(parcel);
1927 }
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001928
Chris Wren8fd39ec2014-02-27 17:43:26 -05001929 if (parcel.readInt() != 0) {
1930 headsUpContentView = RemoteViews.CREATOR.createFromParcel(parcel);
1931 }
1932
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001933 visibility = parcel.readInt();
1934
1935 if (parcel.readInt() != 0) {
1936 publicVersion = Notification.CREATOR.createFromParcel(parcel);
1937 }
Dan Sandler26e81cf2014-05-06 10:01:27 -04001938
1939 color = parcel.readInt();
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04001940
1941 if (parcel.readInt() != 0) {
1942 mChannelId = parcel.readString();
1943 }
Julia Reynolds2a128742016-11-28 14:29:25 -05001944 mTimeout = parcel.readLong();
Julia Reynolds13d898c2017-02-02 12:22:05 -05001945
1946 if (parcel.readInt() != 0) {
1947 mShortcutId = parcel.readString();
1948 }
1949
1950 mBadgeIcon = parcel.readInt();
Julia Reynolds3aedded2017-03-31 14:42:09 -04001951
1952 if (parcel.readInt() != 0) {
1953 mSettingsText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
1954 }
Julia Reynoldsa79c3712017-04-21 10:29:57 -04001955
1956 mGroupAlertBehavior = parcel.readInt();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001957 }
1958
Andy Stadler110988c2010-12-03 14:29:16 -08001959 @Override
Joe Onorato18e69df2010-05-17 22:26:12 -07001960 public Notification clone() {
1961 Notification that = new Notification();
Daniel Sandler1a497d32013-04-18 14:52:45 -04001962 cloneInto(that, true);
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001963 return that;
1964 }
Joe Onorato18e69df2010-05-17 22:26:12 -07001965
Daniel Sandler1a497d32013-04-18 14:52:45 -04001966 /**
1967 * Copy all (or if heavy is false, all except Bitmaps and RemoteViews) members
1968 * of this into that.
1969 * @hide
1970 */
1971 public void cloneInto(Notification that, boolean heavy) {
Dianne Hackborn98305522017-05-05 17:53:53 -07001972 that.whitelistToken = this.whitelistToken;
Joe Onorato18e69df2010-05-17 22:26:12 -07001973 that.when = this.when;
Selim Cinekb85f36fd2016-04-20 18:46:36 -07001974 that.creationTime = this.creationTime;
Dan Sandlerd63f9322015-05-06 15:18:49 -04001975 that.mSmallIcon = this.mSmallIcon;
Joe Onorato18e69df2010-05-17 22:26:12 -07001976 that.number = this.number;
1977
1978 // PendingIntents are global, so there's no reason (or way) to clone them.
1979 that.contentIntent = this.contentIntent;
1980 that.deleteIntent = this.deleteIntent;
Daniel Sandlere46cbd32010-06-17 10:35:26 -04001981 that.fullScreenIntent = this.fullScreenIntent;
Joe Onorato18e69df2010-05-17 22:26:12 -07001982
1983 if (this.tickerText != null) {
1984 that.tickerText = this.tickerText.toString();
1985 }
Daniel Sandler1a497d32013-04-18 14:52:45 -04001986 if (heavy && this.tickerView != null) {
Joe Onorato46439ce2010-11-19 13:56:21 -08001987 that.tickerView = this.tickerView.clone();
Joe Onoratoef1e7762010-09-17 18:38:38 -04001988 }
Daniel Sandler1a497d32013-04-18 14:52:45 -04001989 if (heavy && this.contentView != null) {
Joe Onorato18e69df2010-05-17 22:26:12 -07001990 that.contentView = this.contentView.clone();
1991 }
Dan Sandlerd63f9322015-05-06 15:18:49 -04001992 if (heavy && this.mLargeIcon != null) {
1993 that.mLargeIcon = this.mLargeIcon;
Joe Onorato561d3852010-11-20 18:09:34 -08001994 }
Jozef BABJAKa8b91832011-02-22 08:05:08 +01001995 that.iconLevel = this.iconLevel;
Joe Onorato18e69df2010-05-17 22:26:12 -07001996 that.sound = this.sound; // android.net.Uri is immutable
1997 that.audioStreamType = this.audioStreamType;
John Spurlockc0650f022014-07-19 13:22:39 -04001998 if (this.audioAttributes != null) {
1999 that.audioAttributes = new AudioAttributes.Builder(this.audioAttributes).build();
2000 }
Joe Onorato18e69df2010-05-17 22:26:12 -07002001
2002 final long[] vibrate = this.vibrate;
2003 if (vibrate != null) {
2004 final int N = vibrate.length;
2005 final long[] vib = that.vibrate = new long[N];
2006 System.arraycopy(vibrate, 0, vib, 0, N);
2007 }
2008
2009 that.ledARGB = this.ledARGB;
2010 that.ledOnMS = this.ledOnMS;
2011 that.ledOffMS = this.ledOffMS;
2012 that.defaults = this.defaults;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002013
Joe Onorato18e69df2010-05-17 22:26:12 -07002014 that.flags = this.flags;
2015
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002016 that.priority = this.priority;
Joe Malin8d40d042012-11-05 11:36:40 -08002017
John Spurlockfd7f1e02014-03-18 16:41:57 -04002018 that.category = this.category;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002019
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002020 that.mGroupKey = this.mGroupKey;
2021
2022 that.mSortKey = this.mSortKey;
2023
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002024 if (this.extras != null) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04002025 try {
2026 that.extras = new Bundle(this.extras);
2027 // will unparcel
2028 that.extras.size();
2029 } catch (BadParcelableException e) {
2030 Log.e(TAG, "could not unparcel extras from notification: " + this, e);
2031 that.extras = null;
2032 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002033 }
2034
Felipe Lemedd85da62016-06-28 11:29:54 -07002035 if (!ArrayUtils.isEmpty(allPendingIntents)) {
2036 that.allPendingIntents = new ArraySet<>(allPendingIntents);
Svet Ganovddb94882016-06-23 19:55:24 -07002037 }
2038
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002039 if (this.actions != null) {
2040 that.actions = new Action[this.actions.length];
2041 for(int i=0; i<this.actions.length; i++) {
liangweikang63b03b52017-03-16 19:22:15 +08002042 if ( this.actions[i] != null) {
2043 that.actions[i] = this.actions[i].clone();
2044 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002045 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002046 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002047
Daniel Sandler1a497d32013-04-18 14:52:45 -04002048 if (heavy && this.bigContentView != null) {
Daniel Sandlerf3b73432012-03-27 15:01:25 -04002049 that.bigContentView = this.bigContentView.clone();
2050 }
Daniel Sandler1a497d32013-04-18 14:52:45 -04002051
Chris Wren8fd39ec2014-02-27 17:43:26 -05002052 if (heavy && this.headsUpContentView != null) {
2053 that.headsUpContentView = this.headsUpContentView.clone();
2054 }
2055
Dan Sandler0bf2ed82013-12-21 23:33:41 -06002056 that.visibility = this.visibility;
2057
2058 if (this.publicVersion != null) {
2059 that.publicVersion = new Notification();
2060 this.publicVersion.cloneInto(that.publicVersion, heavy);
2061 }
2062
Dan Sandler26e81cf2014-05-06 10:01:27 -04002063 that.color = this.color;
2064
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04002065 that.mChannelId = this.mChannelId;
Julia Reynolds2a128742016-11-28 14:29:25 -05002066 that.mTimeout = this.mTimeout;
Julia Reynolds3aedded2017-03-31 14:42:09 -04002067 that.mShortcutId = this.mShortcutId;
2068 that.mBadgeIcon = this.mBadgeIcon;
2069 that.mSettingsText = this.mSettingsText;
Julia Reynoldsa79c3712017-04-21 10:29:57 -04002070 that.mGroupAlertBehavior = this.mGroupAlertBehavior;
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04002071
Daniel Sandler1a497d32013-04-18 14:52:45 -04002072 if (!heavy) {
2073 that.lightenPayload(); // will clean out extras
2074 }
2075 }
2076
2077 /**
2078 * Removes heavyweight parts of the Notification object for archival or for sending to
2079 * listeners when the full contents are not necessary.
2080 * @hide
2081 */
2082 public final void lightenPayload() {
2083 tickerView = null;
2084 contentView = null;
2085 bigContentView = null;
Chris Wren8fd39ec2014-02-27 17:43:26 -05002086 headsUpContentView = null;
Dan Sandlerd63f9322015-05-06 15:18:49 -04002087 mLargeIcon = null;
Dan Sandler50128532015-12-08 15:42:41 -05002088 if (extras != null && !extras.isEmpty()) {
2089 final Set<String> keyset = extras.keySet();
2090 final int N = keyset.size();
2091 final String[] keys = keyset.toArray(new String[N]);
2092 for (int i=0; i<N; i++) {
2093 final String key = keys[i];
Dmitri Plotnikov22281362017-01-30 11:16:21 -08002094 if (TvExtender.EXTRA_TV_EXTENDER.equals(key)) {
2095 continue;
2096 }
Dan Sandler50128532015-12-08 15:42:41 -05002097 final Object obj = extras.get(key);
2098 if (obj != null &&
2099 ( obj instanceof Parcelable
2100 || obj instanceof Parcelable[]
2101 || obj instanceof SparseArray
2102 || obj instanceof ArrayList)) {
2103 extras.remove(key);
2104 }
2105 }
Daniel Sandler1a497d32013-04-18 14:52:45 -04002106 }
Joe Onorato18e69df2010-05-17 22:26:12 -07002107 }
2108
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04002109 /**
2110 * Make sure this CharSequence is safe to put into a bundle, which basically
2111 * means it had better not be some custom Parcelable implementation.
2112 * @hide
2113 */
2114 public static CharSequence safeCharSequence(CharSequence cs) {
Christoph Studer535ec612014-09-03 15:47:47 +02002115 if (cs == null) return cs;
2116 if (cs.length() > MAX_CHARSEQUENCE_LENGTH) {
2117 cs = cs.subSequence(0, MAX_CHARSEQUENCE_LENGTH);
2118 }
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04002119 if (cs instanceof Parcelable) {
2120 Log.e(TAG, "warning: " + cs.getClass().getCanonicalName()
2121 + " instance is a custom Parcelable and not allowed in Notification");
2122 return cs.toString();
2123 }
Selim Cinek60a54252016-02-26 17:03:25 -08002124 return removeTextSizeSpans(cs);
2125 }
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04002126
Selim Cinek60a54252016-02-26 17:03:25 -08002127 private static CharSequence removeTextSizeSpans(CharSequence charSequence) {
2128 if (charSequence instanceof Spanned) {
2129 Spanned ss = (Spanned) charSequence;
2130 Object[] spans = ss.getSpans(0, ss.length(), Object.class);
2131 SpannableStringBuilder builder = new SpannableStringBuilder(ss.toString());
2132 for (Object span : spans) {
2133 Object resultSpan = span;
Selim Cinek89991a22016-03-07 19:51:54 -08002134 if (resultSpan instanceof CharacterStyle) {
2135 resultSpan = ((CharacterStyle) span).getUnderlying();
2136 }
2137 if (resultSpan instanceof TextAppearanceSpan) {
2138 TextAppearanceSpan originalSpan = (TextAppearanceSpan) resultSpan;
Selim Cinek60a54252016-02-26 17:03:25 -08002139 resultSpan = new TextAppearanceSpan(
2140 originalSpan.getFamily(),
2141 originalSpan.getTextStyle(),
2142 -1,
2143 originalSpan.getTextColor(),
2144 originalSpan.getLinkTextColor());
Selim Cinek89991a22016-03-07 19:51:54 -08002145 } else if (resultSpan instanceof RelativeSizeSpan
2146 || resultSpan instanceof AbsoluteSizeSpan) {
Selim Cinek60a54252016-02-26 17:03:25 -08002147 continue;
Selim Cinek89991a22016-03-07 19:51:54 -08002148 } else {
2149 resultSpan = span;
Selim Cinek60a54252016-02-26 17:03:25 -08002150 }
2151 builder.setSpan(resultSpan, ss.getSpanStart(span), ss.getSpanEnd(span),
2152 ss.getSpanFlags(span));
2153 }
2154 return builder;
2155 }
2156 return charSequence;
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04002157 }
2158
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002159 public int describeContents() {
2160 return 0;
2161 }
2162
2163 /**
Dan Sandler4e787062015-06-17 15:09:48 -04002164 * Flatten this notification into a parcel.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002165 */
Svet Ganovddb94882016-06-23 19:55:24 -07002166 public void writeToParcel(Parcel parcel, int flags) {
2167 // We need to mark all pending intents getting into the notification
2168 // system as being put there to later allow the notification ranker
2169 // to launch them and by doing so add the app to the battery saver white
2170 // list for a short period of time. The problem is that the system
2171 // cannot look into the extras as there may be parcelables there that
2172 // the platform does not know how to handle. To go around that we have
2173 // an explicit list of the pending intents in the extras bundle.
Felipe Lemedd85da62016-06-28 11:29:54 -07002174 final boolean collectPendingIntents = (allPendingIntents == null);
Svet Ganovddb94882016-06-23 19:55:24 -07002175 if (collectPendingIntents) {
2176 PendingIntent.setOnMarshaledListener(
2177 (PendingIntent intent, Parcel out, int outFlags) -> {
2178 if (parcel == out) {
Felipe Lemedd85da62016-06-28 11:29:54 -07002179 if (allPendingIntents == null) {
2180 allPendingIntents = new ArraySet<>();
Svet Ganovddb94882016-06-23 19:55:24 -07002181 }
Felipe Lemedd85da62016-06-28 11:29:54 -07002182 allPendingIntents.add(intent);
Svet Ganovddb94882016-06-23 19:55:24 -07002183 }
2184 });
2185 }
2186 try {
2187 // IMPORTANT: Add marshaling code in writeToParcelImpl as we
Julia Reynolds13d898c2017-02-02 12:22:05 -05002188 // want to intercept all pending events written to the parcel.
Svet Ganovddb94882016-06-23 19:55:24 -07002189 writeToParcelImpl(parcel, flags);
2190 // Must be written last!
Felipe Lemedd85da62016-06-28 11:29:54 -07002191 parcel.writeArraySet(allPendingIntents);
Svet Ganovddb94882016-06-23 19:55:24 -07002192 } finally {
2193 if (collectPendingIntents) {
2194 PendingIntent.setOnMarshaledListener(null);
2195 }
2196 }
2197 }
2198
2199 private void writeToParcelImpl(Parcel parcel, int flags) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002200 parcel.writeInt(1);
2201
Dianne Hackborn98305522017-05-05 17:53:53 -07002202 parcel.writeStrongBinder(whitelistToken);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002203 parcel.writeLong(when);
Selim Cinekb85f36fd2016-04-20 18:46:36 -07002204 parcel.writeLong(creationTime);
Dan Sandler4e787062015-06-17 15:09:48 -04002205 if (mSmallIcon == null && icon != 0) {
2206 // you snuck an icon in here without using the builder; let's try to keep it
2207 mSmallIcon = Icon.createWithResource("", icon);
2208 }
Dan Sandler3936e7a2015-05-19 20:59:12 -04002209 if (mSmallIcon != null) {
2210 parcel.writeInt(1);
2211 mSmallIcon.writeToParcel(parcel, 0);
2212 } else {
2213 parcel.writeInt(0);
2214 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002215 parcel.writeInt(number);
2216 if (contentIntent != null) {
2217 parcel.writeInt(1);
2218 contentIntent.writeToParcel(parcel, 0);
2219 } else {
2220 parcel.writeInt(0);
2221 }
2222 if (deleteIntent != null) {
2223 parcel.writeInt(1);
2224 deleteIntent.writeToParcel(parcel, 0);
2225 } else {
2226 parcel.writeInt(0);
2227 }
2228 if (tickerText != null) {
2229 parcel.writeInt(1);
2230 TextUtils.writeToParcel(tickerText, parcel, flags);
2231 } else {
2232 parcel.writeInt(0);
2233 }
Joe Onorato46439ce2010-11-19 13:56:21 -08002234 if (tickerView != null) {
Joe Onoratoef1e7762010-09-17 18:38:38 -04002235 parcel.writeInt(1);
Joe Onorato46439ce2010-11-19 13:56:21 -08002236 tickerView.writeToParcel(parcel, 0);
Joe Onoratoef1e7762010-09-17 18:38:38 -04002237 } else {
2238 parcel.writeInt(0);
2239 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002240 if (contentView != null) {
2241 parcel.writeInt(1);
2242 contentView.writeToParcel(parcel, 0);
2243 } else {
2244 parcel.writeInt(0);
2245 }
Selim Cinek279fa862016-06-14 10:57:25 -07002246 if (mLargeIcon == null && largeIcon != null) {
2247 // you snuck an icon in here without using the builder; let's try to keep it
2248 mLargeIcon = Icon.createWithBitmap(largeIcon);
2249 }
Dan Sandlerd63f9322015-05-06 15:18:49 -04002250 if (mLargeIcon != null) {
Joe Onorato561d3852010-11-20 18:09:34 -08002251 parcel.writeInt(1);
Dan Sandlerd63f9322015-05-06 15:18:49 -04002252 mLargeIcon.writeToParcel(parcel, 0);
Joe Onorato561d3852010-11-20 18:09:34 -08002253 } else {
2254 parcel.writeInt(0);
2255 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002256
2257 parcel.writeInt(defaults);
2258 parcel.writeInt(this.flags);
2259
2260 if (sound != null) {
2261 parcel.writeInt(1);
2262 sound.writeToParcel(parcel, 0);
2263 } else {
2264 parcel.writeInt(0);
2265 }
2266 parcel.writeInt(audioStreamType);
John Spurlockc0650f022014-07-19 13:22:39 -04002267
2268 if (audioAttributes != null) {
2269 parcel.writeInt(1);
2270 audioAttributes.writeToParcel(parcel, 0);
2271 } else {
2272 parcel.writeInt(0);
2273 }
2274
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002275 parcel.writeLongArray(vibrate);
2276 parcel.writeInt(ledARGB);
2277 parcel.writeInt(ledOnMS);
2278 parcel.writeInt(ledOffMS);
2279 parcel.writeInt(iconLevel);
Daniel Sandlere46cbd32010-06-17 10:35:26 -04002280
2281 if (fullScreenIntent != null) {
2282 parcel.writeInt(1);
2283 fullScreenIntent.writeToParcel(parcel, 0);
2284 } else {
2285 parcel.writeInt(0);
2286 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002287
2288 parcel.writeInt(priority);
Joe Malin8d40d042012-11-05 11:36:40 -08002289
John Spurlockfd7f1e02014-03-18 16:41:57 -04002290 parcel.writeString(category);
Joe Malin8d40d042012-11-05 11:36:40 -08002291
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002292 parcel.writeString(mGroupKey);
2293
2294 parcel.writeString(mSortKey);
2295
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002296 parcel.writeBundle(extras); // null ok
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002297
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002298 parcel.writeTypedArray(actions, 0); // null ok
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002299
Daniel Sandlerf3b73432012-03-27 15:01:25 -04002300 if (bigContentView != null) {
2301 parcel.writeInt(1);
2302 bigContentView.writeToParcel(parcel, 0);
2303 } else {
2304 parcel.writeInt(0);
2305 }
Dan Sandler0bf2ed82013-12-21 23:33:41 -06002306
Chris Wren8fd39ec2014-02-27 17:43:26 -05002307 if (headsUpContentView != null) {
2308 parcel.writeInt(1);
2309 headsUpContentView.writeToParcel(parcel, 0);
2310 } else {
2311 parcel.writeInt(0);
2312 }
2313
Dan Sandler0bf2ed82013-12-21 23:33:41 -06002314 parcel.writeInt(visibility);
2315
2316 if (publicVersion != null) {
2317 parcel.writeInt(1);
2318 publicVersion.writeToParcel(parcel, 0);
2319 } else {
2320 parcel.writeInt(0);
2321 }
Dan Sandler26e81cf2014-05-06 10:01:27 -04002322
2323 parcel.writeInt(color);
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04002324
2325 if (mChannelId != null) {
2326 parcel.writeInt(1);
2327 parcel.writeString(mChannelId);
2328 } else {
2329 parcel.writeInt(0);
2330 }
Julia Reynolds2a128742016-11-28 14:29:25 -05002331 parcel.writeLong(mTimeout);
Julia Reynolds13d898c2017-02-02 12:22:05 -05002332
2333 if (mShortcutId != null) {
2334 parcel.writeInt(1);
2335 parcel.writeString(mShortcutId);
2336 } else {
2337 parcel.writeInt(0);
2338 }
2339
2340 parcel.writeInt(mBadgeIcon);
Julia Reynolds3aedded2017-03-31 14:42:09 -04002341
2342 if (mSettingsText != null) {
2343 parcel.writeInt(1);
2344 TextUtils.writeToParcel(mSettingsText, parcel, flags);
2345 } else {
2346 parcel.writeInt(0);
2347 }
Julia Reynoldsa79c3712017-04-21 10:29:57 -04002348
2349 parcel.writeInt(mGroupAlertBehavior);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002350 }
2351
2352 /**
2353 * Parcelable.Creator that instantiates Notification objects
2354 */
2355 public static final Parcelable.Creator<Notification> CREATOR
2356 = new Parcelable.Creator<Notification>()
2357 {
2358 public Notification createFromParcel(Parcel parcel)
2359 {
2360 return new Notification(parcel);
2361 }
2362
2363 public Notification[] newArray(int size)
2364 {
2365 return new Notification[size];
2366 }
2367 };
2368
2369 /**
2370 * Sets the {@link #contentView} field to be a view with the standard "Latest Event"
2371 * layout.
2372 *
2373 * <p>Uses the {@link #icon} and {@link #when} fields to set the icon and time fields
2374 * in the view.</p>
2375 * @param context The context for your application / activity.
2376 * @param contentTitle The title that goes in the expanded entry.
2377 * @param contentText The text that goes in the expanded entry.
2378 * @param contentIntent The intent to launch when the user clicks the expanded notification.
2379 * If this is an activity, it must include the
2380 * {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} flag, which requires
Scott Main7aee61f2011-02-08 11:25:01 -08002381 * that you take care of task management as described in the
2382 * <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back
2383 * Stack</a> document.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002384 *
Joe Onorato46439ce2010-11-19 13:56:21 -08002385 * @deprecated Use {@link Builder} instead.
Chris Wrena05db382015-06-24 15:18:34 -04002386 * @removed
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002387 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002388 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002389 public void setLatestEventInfo(Context context,
2390 CharSequence contentTitle, CharSequence contentText, PendingIntent contentIntent) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002391 if (context.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1){
2392 Log.e(TAG, "setLatestEventInfo() is deprecated and you should feel deprecated.",
2393 new Throwable());
2394 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002395
Selim Cinek4ac6f602016-06-13 15:47:03 -07002396 if (context.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N) {
2397 extras.putBoolean(EXTRA_SHOW_WHEN, true);
2398 }
2399
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002400 // ensure that any information already set directly is preserved
2401 final Notification.Builder builder = new Notification.Builder(context, this);
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002402
2403 // now apply the latestEventInfo fields
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002404 if (contentTitle != null) {
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002405 builder.setContentTitle(contentTitle);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002406 }
2407 if (contentText != null) {
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002408 builder.setContentText(contentText);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002409 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002410 builder.setContentIntent(contentIntent);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002411
2412 builder.build(); // callers expect this notification to be ready to use
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002413 }
2414
Julia Reynoldsda303542015-11-23 14:00:20 -05002415 /**
2416 * @hide
2417 */
2418 public static void addFieldsFromContext(Context context, Notification notification) {
Julia Reynoldse071abd2017-03-22 10:52:11 -04002419 addFieldsFromContext(context.getApplicationInfo(), notification);
Jeff Sharkey012bc7b2016-04-11 16:30:27 -06002420 }
2421
2422 /**
2423 * @hide
2424 */
Julia Reynoldse071abd2017-03-22 10:52:11 -04002425 public static void addFieldsFromContext(ApplicationInfo ai, Notification notification) {
Jeff Sharkey012bc7b2016-04-11 16:30:27 -06002426 notification.extras.putParcelable(EXTRA_BUILDER_APPLICATION_INFO, ai);
Julia Reynoldsda303542015-11-23 14:00:20 -05002427 }
2428
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002429 @Override
2430 public String toString() {
2431 StringBuilder sb = new StringBuilder();
Julia Reynoldsb9e712e2017-04-17 10:31:03 -04002432 sb.append("Notification(channel=");
Julia Reynoldsbad42972017-04-25 13:52:49 -04002433 sb.append(getChannelId());
Julia Reynoldsb9e712e2017-04-17 10:31:03 -04002434 sb.append(" pri=");
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002435 sb.append(priority);
2436 sb.append(" contentView=");
Joe Onoratoc9596d62011-01-12 17:03:11 -08002437 if (contentView != null) {
2438 sb.append(contentView.getPackage());
2439 sb.append("/0x");
2440 sb.append(Integer.toHexString(contentView.getLayoutId()));
2441 } else {
2442 sb.append("null");
2443 }
2444 sb.append(" vibrate=");
Daniel Sandler6738eee2012-11-16 12:03:32 -05002445 if ((this.defaults & DEFAULT_VIBRATE) != 0) {
2446 sb.append("default");
2447 } else if (this.vibrate != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002448 int N = this.vibrate.length-1;
2449 sb.append("[");
2450 for (int i=0; i<N; i++) {
2451 sb.append(this.vibrate[i]);
2452 sb.append(',');
2453 }
Simon Schoar8cf97d92009-06-10 22:08:37 +02002454 if (N != -1) {
2455 sb.append(this.vibrate[N]);
2456 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002457 sb.append("]");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002458 } else {
2459 sb.append("null");
2460 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002461 sb.append(" sound=");
Daniel Sandler6738eee2012-11-16 12:03:32 -05002462 if ((this.defaults & DEFAULT_SOUND) != 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002463 sb.append("default");
Daniel Sandler6738eee2012-11-16 12:03:32 -05002464 } else if (this.sound != null) {
2465 sb.append(this.sound.toString());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002466 } else {
2467 sb.append("null");
2468 }
Chris Wren365b6d32015-07-16 10:39:26 -04002469 if (this.tickerText != null) {
2470 sb.append(" tick");
2471 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002472 sb.append(" defaults=0x");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002473 sb.append(Integer.toHexString(this.defaults));
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002474 sb.append(" flags=0x");
Daniel Sandlere46cbd32010-06-17 10:35:26 -04002475 sb.append(Integer.toHexString(this.flags));
Dan Sandler26e81cf2014-05-06 10:01:27 -04002476 sb.append(String.format(" color=0x%08x", this.color));
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002477 if (this.category != null) {
2478 sb.append(" category=");
2479 sb.append(this.category);
2480 }
2481 if (this.mGroupKey != null) {
2482 sb.append(" groupKey=");
2483 sb.append(this.mGroupKey);
2484 }
2485 if (this.mSortKey != null) {
2486 sb.append(" sortKey=");
2487 sb.append(this.mSortKey);
2488 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002489 if (actions != null) {
Dan Sandler1b718782014-07-18 12:43:45 -04002490 sb.append(" actions=");
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002491 sb.append(actions.length);
Dan Sandler1b718782014-07-18 12:43:45 -04002492 }
2493 sb.append(" vis=");
2494 sb.append(visibilityToString(this.visibility));
2495 if (this.publicVersion != null) {
2496 sb.append(" publicVersion=");
2497 sb.append(publicVersion.toString());
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002498 }
2499 sb.append(")");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002500 return sb.toString();
2501 }
Joe Onorato46439ce2010-11-19 13:56:21 -08002502
Dan Sandler1b718782014-07-18 12:43:45 -04002503 /**
2504 * {@hide}
2505 */
2506 public static String visibilityToString(int vis) {
2507 switch (vis) {
2508 case VISIBILITY_PRIVATE:
2509 return "PRIVATE";
2510 case VISIBILITY_PUBLIC:
2511 return "PUBLIC";
2512 case VISIBILITY_SECRET:
2513 return "SECRET";
2514 default:
2515 return "UNKNOWN(" + String.valueOf(vis) + ")";
2516 }
2517 }
2518
Joe Onoratocb109a02011-01-18 17:57:41 -08002519 /**
John Spurlock1d881a12015-03-18 19:21:54 -04002520 * {@hide}
2521 */
2522 public static String priorityToString(@Priority int pri) {
2523 switch (pri) {
2524 case PRIORITY_MIN:
2525 return "MIN";
2526 case PRIORITY_LOW:
2527 return "LOW";
2528 case PRIORITY_DEFAULT:
2529 return "DEFAULT";
2530 case PRIORITY_HIGH:
2531 return "HIGH";
2532 case PRIORITY_MAX:
2533 return "MAX";
2534 default:
2535 return "UNKNOWN(" + String.valueOf(pri) + ")";
2536 }
2537 }
2538
Jeff Sharkey000ce802017-04-29 13:13:27 -06002539 /** @removed */
2540 @Deprecated
Julia Reynolds37856052016-11-11 09:20:07 -05002541 public String getChannel() {
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04002542 return mChannelId;
2543 }
2544
2545 /**
Julia Reynoldsbad42972017-04-25 13:52:49 -04002546 * Returns the id of the channel this notification posts to.
2547 */
2548 public String getChannelId() {
2549 return mChannelId;
2550 }
2551
Jeff Sharkey000ce802017-04-29 13:13:27 -06002552 /** @removed */
2553 @Deprecated
Julia Reynolds2a128742016-11-28 14:29:25 -05002554 public long getTimeout() {
2555 return mTimeout;
2556 }
2557
2558 /**
Julia Reynoldsbad42972017-04-25 13:52:49 -04002559 * Returns the duration from posting after which this notification should be canceled by the
2560 * system, if it's not canceled already.
2561 */
2562 public long getTimeoutAfter() {
2563 return mTimeout;
2564 }
2565
2566 /**
Julia Reynoldse071abd2017-03-22 10:52:11 -04002567 * Returns what icon should be shown for this notification if it is being displayed in a
2568 * Launcher that supports badging. Will be one of {@link #BADGE_ICON_NONE},
2569 * {@link #BADGE_ICON_SMALL}, or {@link #BADGE_ICON_LARGE}.
2570 */
2571 public int getBadgeIconType() {
2572 return mBadgeIcon;
2573 }
2574
2575 /**
Julia Reynolds13d898c2017-02-02 12:22:05 -05002576 * Returns the {@link ShortcutInfo#getId() id} that this notification supersedes, if any.
Julia Reynoldsbad42972017-04-25 13:52:49 -04002577 *
2578 * <p>Used by some Launchers that display notification content to hide shortcuts that duplicate
2579 * notifications.
Julia Reynolds13d898c2017-02-02 12:22:05 -05002580 */
2581 public String getShortcutId() {
2582 return mShortcutId;
2583 }
2584
Julia Reynolds3aedded2017-03-31 14:42:09 -04002585
2586 /**
2587 * Returns the settings text provided to {@link Builder#setSettingsText(CharSequence)}.
2588 */
2589 public CharSequence getSettingsText() {
2590 return mSettingsText;
2591 }
2592
Julia Reynolds13d898c2017-02-02 12:22:05 -05002593 /**
Julia Reynoldsa79c3712017-04-21 10:29:57 -04002594 * Returns which type of notifications in a group are responsible for audibly alerting the
2595 * user. See {@link #GROUP_ALERT_ALL}, {@link #GROUP_ALERT_CHILDREN},
2596 * {@link #GROUP_ALERT_SUMMARY}.
2597 */
2598 public @GroupAlertBehavior int getGroupAlertBehavior() {
2599 return mGroupAlertBehavior;
2600 }
2601
2602 /**
Dan Sandlerd63f9322015-05-06 15:18:49 -04002603 * The small icon representing this notification in the status bar and content view.
2604 *
2605 * @return the small icon representing this notification.
2606 *
2607 * @see Builder#getSmallIcon()
2608 * @see Builder#setSmallIcon(Icon)
2609 */
2610 public Icon getSmallIcon() {
2611 return mSmallIcon;
2612 }
2613
2614 /**
2615 * Used when notifying to clean up legacy small icons.
2616 * @hide
2617 */
2618 public void setSmallIcon(Icon icon) {
2619 mSmallIcon = icon;
2620 }
2621
2622 /**
2623 * The large icon shown in this notification's content view.
2624 * @see Builder#getLargeIcon()
2625 * @see Builder#setLargeIcon(Icon)
2626 */
2627 public Icon getLargeIcon() {
2628 return mLargeIcon;
2629 }
2630
2631 /**
Christoph Studer4600f9b2014-07-22 22:44:43 +02002632 * @hide
2633 */
Christoph Studerc8db24b2014-07-25 17:50:30 +02002634 public boolean isGroupSummary() {
2635 return mGroupKey != null && (flags & FLAG_GROUP_SUMMARY) != 0;
2636 }
2637
2638 /**
2639 * @hide
2640 */
2641 public boolean isGroupChild() {
2642 return mGroupKey != null && (flags & FLAG_GROUP_SUMMARY) == 0;
2643 }
2644
2645 /**
Julia Reynolds30203152017-05-26 13:36:31 -04002646 * @hide
2647 */
2648 public boolean suppressAlertingDueToGrouping() {
2649 if (isGroupSummary()
2650 && getGroupAlertBehavior() == Notification.GROUP_ALERT_CHILDREN) {
2651 return true;
2652 } else if (isGroupChild()
2653 && getGroupAlertBehavior() == Notification.GROUP_ALERT_SUMMARY) {
2654 return true;
2655 }
2656 return false;
2657 }
2658
2659 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002660 * Builder class for {@link Notification} objects.
Joe Malin8d40d042012-11-05 11:36:40 -08002661 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002662 * Provides a convenient way to set the various fields of a {@link Notification} and generate
Scott Main183bf112012-08-13 19:12:13 -07002663 * content views using the platform's notification layout template. If your app supports
2664 * versions of Android as old as API level 4, you can instead use
2665 * {@link android.support.v4.app.NotificationCompat.Builder NotificationCompat.Builder},
2666 * available in the <a href="{@docRoot}tools/extras/support-library.html">Android Support
2667 * library</a>.
Joe Malin8d40d042012-11-05 11:36:40 -08002668 *
Scott Main183bf112012-08-13 19:12:13 -07002669 * <p>Example:
Joe Malin8d40d042012-11-05 11:36:40 -08002670 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002671 * <pre class="prettyprint">
Scott Main183bf112012-08-13 19:12:13 -07002672 * Notification noti = new Notification.Builder(mContext)
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002673 * .setContentTitle(&quot;New mail from &quot; + sender.toString())
2674 * .setContentText(subject)
2675 * .setSmallIcon(R.drawable.new_mail)
2676 * .setLargeIcon(aBitmap)
Chris Wrenfbd96ba2012-05-01 12:03:58 -04002677 * .build();
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002678 * </pre>
Joe Onoratocb109a02011-01-18 17:57:41 -08002679 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002680 public static class Builder {
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05002681 /**
2682 * @hide
2683 */
2684 public static final String EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT =
2685 "android.rebuild.contentViewActionCount";
2686 /**
2687 * @hide
2688 */
2689 public static final String EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT
2690 = "android.rebuild.bigViewActionCount";
2691 /**
2692 * @hide
2693 */
2694 public static final String EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT
2695 = "android.rebuild.hudViewActionCount";
2696
Daniel Sandler602ad1c2012-06-12 16:06:27 -04002697 private static final int MAX_ACTION_BUTTONS = 3;
Daniel Sandler8680bf82012-05-15 16:52:52 -04002698
Selim Cinek6743c0b2017-01-18 18:24:01 -08002699 private static final boolean USE_ONLY_TITLE_IN_LOW_PRIORITY_SUMMARY =
2700 SystemProperties.getBoolean("notifications.only_title", true);
2701
Selim Cinek389edcd2017-05-11 19:16:44 -07002702 /**
2703 * The lightness difference that has to be added to the primary text color to obtain the
2704 * secondary text color when the background is light.
2705 */
2706 private static final int LIGHTNESS_TEXT_DIFFERENCE_LIGHT = 20;
2707
2708 /**
2709 * The lightness difference that has to be added to the primary text color to obtain the
2710 * secondary text color when the background is dark.
2711 * A bit less then the above value, since it looks better on dark backgrounds.
2712 */
2713 private static final int LIGHTNESS_TEXT_DIFFERENCE_DARK = -10;
2714
Joe Onorato46439ce2010-11-19 13:56:21 -08002715 private Context mContext;
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002716 private Notification mN;
2717 private Bundle mUserExtras = new Bundle();
Chris Wrenfbd96ba2012-05-01 12:03:58 -04002718 private Style mStyle;
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002719 private ArrayList<Action> mActions = new ArrayList<Action>(MAX_ACTION_BUTTONS);
2720 private ArrayList<String> mPersonList = new ArrayList<String>();
2721 private NotificationColorUtil mColorUtil;
Selim Cinek7b9605b2017-01-19 17:36:00 -08002722 private boolean mIsLegacy;
2723 private boolean mIsLegacyInitialized;
Christoph Studer7ac80e62014-08-04 16:01:57 +02002724
2725 /**
Adrian Roos4ff3b122016-02-01 12:26:13 -08002726 * Caches a contrast-enhanced version of {@link #mCachedContrastColorIsFor}.
2727 */
2728 private int mCachedContrastColor = COLOR_INVALID;
2729 private int mCachedContrastColorIsFor = COLOR_INVALID;
Adrian Roos487374f2017-01-11 15:48:14 -08002730 /**
2731 * Caches a ambient version of {@link #mCachedContrastColorIsFor}.
2732 */
2733 private int mCachedAmbientColor = COLOR_INVALID;
2734 private int mCachedAmbientColorIsFor = COLOR_INVALID;
Adrian Roos4ff3b122016-02-01 12:26:13 -08002735
2736 /**
Adrian Roos70d7aa32017-01-11 15:39:06 -08002737 * Caches an instance of StandardTemplateParams. Note that this may have been used before,
2738 * so make sure to call {@link StandardTemplateParams#reset()} before using it.
2739 */
2740 StandardTemplateParams mParams = new StandardTemplateParams();
Selim Cinek7b9605b2017-01-19 17:36:00 -08002741 private int mTextColorsAreForBackground = COLOR_INVALID;
2742 private int mPrimaryTextColor = COLOR_INVALID;
2743 private int mSecondaryTextColor = COLOR_INVALID;
2744 private int mActionBarColor = COLOR_INVALID;
Selim Cinek5fb73f82017-04-20 16:55:38 -07002745 private int mBackgroundColor = COLOR_INVALID;
2746 private int mForegroundColor = COLOR_INVALID;
Selim Cinekac5f0272017-05-02 16:05:41 -07002747 private int mBackgroundColorHint = COLOR_INVALID;
Selim Cinek5d6ef8d2017-05-18 22:16:00 -07002748 /**
2749 * A temporary location where actions are stored. If != null the view originally has action
2750 * but doesn't have any for this inflation.
2751 */
2752 private ArrayList<Action> mOriginalActions;
Selim Cineka7679b62017-05-10 16:33:25 -07002753 private boolean mRebuildStyledRemoteViews;
Adrian Roos70d7aa32017-01-11 15:39:06 -08002754
2755 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002756 * Constructs a new Builder with the defaults:
Joe Onoratocb109a02011-01-18 17:57:41 -08002757 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002758 * @param context
2759 * A {@link Context} that will be used by the Builder to construct the
2760 * RemoteViews. The Context will not be held past the lifetime of this Builder
2761 * object.
Geoffrey Pitsch5caa2762017-01-12 09:35:54 -05002762 * @param channelId
2763 * The constructed Notification will be posted on this
2764 * {@link NotificationChannel}. To use a NotificationChannel, it must first be
2765 * created using {@link NotificationManager#createNotificationChannel}.
Joe Onoratocb109a02011-01-18 17:57:41 -08002766 */
Geoffrey Pitsch5caa2762017-01-12 09:35:54 -05002767 public Builder(Context context, String channelId) {
2768 this(context, (Notification) null);
2769 mN.mChannelId = channelId;
2770 }
2771
2772 /**
2773 * @deprecated use {@link Notification.Builder#Notification.Builder(Context, String)}
2774 * instead. All posted Notifications must specify a NotificationChannel Id.
2775 */
2776 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08002777 public Builder(Context context) {
Geoffrey Pitsch5caa2762017-01-12 09:35:54 -05002778 this(context, (Notification) null);
Joe Onorato46439ce2010-11-19 13:56:21 -08002779 }
2780
Joe Onoratocb109a02011-01-18 17:57:41 -08002781 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002782 * @hide
Christoph Studer4600f9b2014-07-22 22:44:43 +02002783 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002784 public Builder(Context context, Notification toAdopt) {
2785 mContext = context;
Christoph Studer4600f9b2014-07-22 22:44:43 +02002786
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002787 if (toAdopt == null) {
2788 mN = new Notification();
Selim Cinek0ff1ce602016-04-05 18:27:16 -07002789 if (context.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N) {
2790 mN.extras.putBoolean(EXTRA_SHOW_WHEN, true);
2791 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002792 mN.priority = PRIORITY_DEFAULT;
2793 mN.visibility = VISIBILITY_PRIVATE;
2794 } else {
2795 mN = toAdopt;
2796 if (mN.actions != null) {
2797 Collections.addAll(mActions, mN.actions);
Christoph Studer4600f9b2014-07-22 22:44:43 +02002798 }
2799
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002800 if (mN.extras.containsKey(EXTRA_PEOPLE)) {
2801 Collections.addAll(mPersonList, mN.extras.getStringArray(EXTRA_PEOPLE));
2802 }
2803
Selim Cinek4ac6f602016-06-13 15:47:03 -07002804 if (mN.getSmallIcon() == null && mN.icon != 0) {
2805 setSmallIcon(mN.icon);
2806 }
2807
2808 if (mN.getLargeIcon() == null && mN.largeIcon != null) {
2809 setLargeIcon(mN.largeIcon);
2810 }
2811
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002812 String templateClass = mN.extras.getString(EXTRA_TEMPLATE);
2813 if (!TextUtils.isEmpty(templateClass)) {
2814 final Class<? extends Style> styleClass
2815 = getNotificationStyleClass(templateClass);
2816 if (styleClass == null) {
2817 Log.d(TAG, "Unknown style class: " + templateClass);
2818 } else {
2819 try {
Adrian Roosc1a80b02016-04-05 14:54:55 -07002820 final Constructor<? extends Style> ctor =
2821 styleClass.getDeclaredConstructor();
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002822 ctor.setAccessible(true);
2823 final Style style = ctor.newInstance();
2824 style.restoreFromExtras(mN.extras);
2825
2826 if (style != null) {
2827 setStyle(style);
2828 }
2829 } catch (Throwable t) {
2830 Log.e(TAG, "Could not create Style", t);
2831 }
2832 }
2833 }
2834
2835 }
2836 }
2837
2838 private NotificationColorUtil getColorUtil() {
Selim Cinek99104832017-01-25 14:47:33 -08002839 if (mColorUtil == null) {
2840 mColorUtil = NotificationColorUtil.getInstance(mContext);
Christoph Studer4600f9b2014-07-22 22:44:43 +02002841 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002842 return mColorUtil;
Christoph Studer4600f9b2014-07-22 22:44:43 +02002843 }
2844
2845 /**
Julia Reynolds13d898c2017-02-02 12:22:05 -05002846 * If this notification is duplicative of a Launcher shortcut, sets the
2847 * {@link ShortcutInfo#getId() id} of the shortcut, in case the Launcher wants to hide
2848 * the shortcut.
2849 *
Julia Reynoldsbad42972017-04-25 13:52:49 -04002850 * This field will be ignored by Launchers that don't support badging, don't show
2851 * notification content, or don't show {@link android.content.pm.ShortcutManager shortcuts}.
Julia Reynolds13d898c2017-02-02 12:22:05 -05002852 *
2853 * @param shortcutId the {@link ShortcutInfo#getId() id} of the shortcut this notification
2854 * supersedes
2855 */
2856 public Builder setShortcutId(String shortcutId) {
2857 mN.mShortcutId = shortcutId;
2858 return this;
2859 }
2860
2861 /**
Julia Reynoldse071abd2017-03-22 10:52:11 -04002862 * Sets which icon to display as a badge for this notification.
2863 *
2864 * Must be one of {@link #BADGE_ICON_NONE}, {@link #BADGE_ICON_SMALL},
2865 * {@link #BADGE_ICON_LARGE}.
2866 *
2867 * Note: This value might be ignored, for launchers that don't support badge icons.
2868 */
Julia Reynolds612beb22017-03-30 10:48:30 -04002869 public Builder setBadgeIconType(int icon) {
Julia Reynoldse071abd2017-03-22 10:52:11 -04002870 mN.mBadgeIcon = icon;
2871 return this;
2872 }
2873
2874 /**
Julia Reynoldsa79c3712017-04-21 10:29:57 -04002875 * Sets the group alert behavior for this notification. Use this method to mute this
2876 * notification if alerts for this notification's group should be handled by a different
2877 * notification. This is only applicable for notifications that belong to a
2878 * {@link #setGroup(String) group}.
2879 *
2880 * <p> The default value is {@link #GROUP_ALERT_ALL}.</p>
2881 */
2882 public Builder setGroupAlertBehavior(@GroupAlertBehavior int groupAlertBehavior) {
2883 mN.mGroupAlertBehavior = groupAlertBehavior;
2884 return this;
2885 }
2886
Jeff Sharkey000ce802017-04-29 13:13:27 -06002887 /** @removed */
2888 @Deprecated
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04002889 public Builder setChannel(String channelId) {
2890 mN.mChannelId = channelId;
2891 return this;
2892 }
2893
2894 /**
Julia Reynoldsbad42972017-04-25 13:52:49 -04002895 * Specifies the channel the notification should be delivered on.
2896 */
2897 public Builder setChannelId(String channelId) {
2898 mN.mChannelId = channelId;
2899 return this;
2900 }
2901
Jeff Sharkey000ce802017-04-29 13:13:27 -06002902 /** @removed */
2903 @Deprecated
Julia Reynolds50989772017-02-23 14:32:16 -05002904 public Builder setTimeout(long durationMs) {
2905 mN.mTimeout = durationMs;
Julia Reynolds2a128742016-11-28 14:29:25 -05002906 return this;
2907 }
2908
2909 /**
Julia Reynoldsbad42972017-04-25 13:52:49 -04002910 * Specifies a duration in milliseconds after which this notification should be canceled,
2911 * if it is not already canceled.
2912 */
2913 public Builder setTimeoutAfter(long durationMs) {
2914 mN.mTimeout = durationMs;
2915 return this;
2916 }
2917
2918 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002919 * Add a timestamp pertaining to the notification (usually the time the event occurred).
Selim Cinek0ff1ce602016-04-05 18:27:16 -07002920 *
2921 * For apps targeting {@link android.os.Build.VERSION_CODES#N} and above, this time is not
2922 * shown anymore by default and must be opted into by using
2923 * {@link android.app.Notification.Builder#setShowWhen(boolean)}
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002924 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002925 * @see Notification#when
Joe Onoratocb109a02011-01-18 17:57:41 -08002926 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002927 public Builder setWhen(long when) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002928 mN.when = when;
Joe Onorato46439ce2010-11-19 13:56:21 -08002929 return this;
2930 }
2931
Joe Onoratocb109a02011-01-18 17:57:41 -08002932 /**
Griff Hazen50c11652014-05-16 09:46:31 -07002933 * Control whether the timestamp set with {@link #setWhen(long) setWhen} is shown
Daniel Sandler0c890492012-09-12 17:23:10 -07002934 * in the content view.
Selim Cinek0ff1ce602016-04-05 18:27:16 -07002935 * For apps targeting {@link android.os.Build.VERSION_CODES#N} and above, this defaults to
2936 * {@code false}. For earlier apps, the default is {@code true}.
Daniel Sandler0c890492012-09-12 17:23:10 -07002937 */
2938 public Builder setShowWhen(boolean show) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002939 mN.extras.putBoolean(EXTRA_SHOW_WHEN, show);
Daniel Sandler0c890492012-09-12 17:23:10 -07002940 return this;
2941 }
2942
2943 /**
Daniel Sandlerd33b8032012-05-10 11:41:48 -04002944 * Show the {@link Notification#when} field as a stopwatch.
Joe Malin8d40d042012-11-05 11:36:40 -08002945 *
2946 * Instead of presenting <code>when</code> as a timestamp, the notification will show an
Daniel Sandlerd33b8032012-05-10 11:41:48 -04002947 * automatically updating display of the minutes and seconds since <code>when</code>.
Daniel Sandlera2985ed2012-04-03 16:42:00 -04002948 *
Daniel Sandlerd33b8032012-05-10 11:41:48 -04002949 * Useful when showing an elapsed time (like an ongoing phone call).
2950 *
Selim Cinek81c23aa2016-02-25 16:23:13 -08002951 * The counter can also be set to count down to <code>when</code> when using
Adrian Roos96b7e202016-05-17 13:50:38 -07002952 * {@link #setChronometerCountDown(boolean)}.
Selim Cinek81c23aa2016-02-25 16:23:13 -08002953 *
Daniel Sandlerd33b8032012-05-10 11:41:48 -04002954 * @see android.widget.Chronometer
Daniel Sandlera2985ed2012-04-03 16:42:00 -04002955 * @see Notification#when
Adrian Roos96b7e202016-05-17 13:50:38 -07002956 * @see #setChronometerCountDown(boolean)
Daniel Sandlera2985ed2012-04-03 16:42:00 -04002957 */
2958 public Builder setUsesChronometer(boolean b) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002959 mN.extras.putBoolean(EXTRA_SHOW_CHRONOMETER, b);
Daniel Sandlera2985ed2012-04-03 16:42:00 -04002960 return this;
2961 }
2962
2963 /**
Selim Cinek81c23aa2016-02-25 16:23:13 -08002964 * Sets the Chronometer to count down instead of counting up.
2965 *
2966 * <p>This is only relevant if {@link #setUsesChronometer(boolean)} has been set to true.
2967 * If it isn't set the chronometer will count up.
2968 *
2969 * @see #setUsesChronometer(boolean)
2970 */
Adrian Roos96b7e202016-05-17 13:50:38 -07002971 public Builder setChronometerCountDown(boolean countDown) {
2972 mN.extras.putBoolean(EXTRA_CHRONOMETER_COUNT_DOWN, countDown);
Selim Cinek81c23aa2016-02-25 16:23:13 -08002973 return this;
2974 }
2975
2976 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002977 * Set the small icon resource, which will be used to represent the notification in the
2978 * status bar.
Joe Onoratocb109a02011-01-18 17:57:41 -08002979 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002980
2981 * The platform template for the expanded view will draw this icon in the left, unless a
2982 * {@link #setLargeIcon(Bitmap) large icon} has also been specified, in which case the small
2983 * icon will be moved to the right-hand side.
2984 *
2985
2986 * @param icon
2987 * A resource ID in the application's package of the drawable to use.
2988 * @see Notification#icon
Joe Onoratocb109a02011-01-18 17:57:41 -08002989 */
Tor Norbye7b9c9122013-05-30 16:48:33 -07002990 public Builder setSmallIcon(@DrawableRes int icon) {
Dan Sandlerd63f9322015-05-06 15:18:49 -04002991 return setSmallIcon(icon != 0
2992 ? Icon.createWithResource(mContext, icon)
2993 : null);
Joe Onorato46439ce2010-11-19 13:56:21 -08002994 }
2995
Joe Onoratocb109a02011-01-18 17:57:41 -08002996 /**
2997 * A variant of {@link #setSmallIcon(int) setSmallIcon(int)} that takes an additional
2998 * level parameter for when the icon is a {@link android.graphics.drawable.LevelListDrawable
2999 * LevelListDrawable}.
3000 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003001 * @param icon A resource ID in the application's package of the drawable to use.
Joe Onoratocb109a02011-01-18 17:57:41 -08003002 * @param level The level to use for the icon.
3003 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003004 * @see Notification#icon
3005 * @see Notification#iconLevel
Joe Onoratocb109a02011-01-18 17:57:41 -08003006 */
Tor Norbye7b9c9122013-05-30 16:48:33 -07003007 public Builder setSmallIcon(@DrawableRes int icon, int level) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003008 mN.iconLevel = level;
Dan Sandlerd63f9322015-05-06 15:18:49 -04003009 return setSmallIcon(icon);
3010 }
3011
3012 /**
3013 * Set the small icon, which will be used to represent the notification in the
3014 * status bar and content view (unless overriden there by a
3015 * {@link #setLargeIcon(Bitmap) large icon}).
3016 *
3017 * @param icon An Icon object to use.
3018 * @see Notification#icon
3019 */
3020 public Builder setSmallIcon(Icon icon) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003021 mN.setSmallIcon(icon);
3022 if (icon != null && icon.getType() == Icon.TYPE_RESOURCE) {
3023 mN.icon = icon.getResId();
3024 }
Joe Onorato46439ce2010-11-19 13:56:21 -08003025 return this;
3026 }
3027
Joe Onoratocb109a02011-01-18 17:57:41 -08003028 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003029 * Set the first line of text in the platform notification template.
Joe Onoratocb109a02011-01-18 17:57:41 -08003030 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003031 public Builder setContentTitle(CharSequence title) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003032 mN.extras.putCharSequence(EXTRA_TITLE, safeCharSequence(title));
Joe Onorato46439ce2010-11-19 13:56:21 -08003033 return this;
3034 }
3035
Joe Onoratocb109a02011-01-18 17:57:41 -08003036 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003037 * Set the second line of text in the platform notification template.
Joe Onoratocb109a02011-01-18 17:57:41 -08003038 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003039 public Builder setContentText(CharSequence text) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003040 mN.extras.putCharSequence(EXTRA_TEXT, safeCharSequence(text));
Joe Onorato46439ce2010-11-19 13:56:21 -08003041 return this;
3042 }
3043
Joe Onoratocb109a02011-01-18 17:57:41 -08003044 /**
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07003045 * This provides some additional information that is displayed in the notification. No
3046 * guarantees are given where exactly it is displayed.
3047 *
3048 * <p>This information should only be provided if it provides an essential
3049 * benefit to the understanding of the notification. The more text you provide the
3050 * less readable it becomes. For example, an email client should only provide the account
3051 * name here if more than one email account has been added.</p>
3052 *
3053 * <p>As of {@link android.os.Build.VERSION_CODES#N} this information is displayed in the
3054 * notification header area.
3055 *
3056 * On Android versions before {@link android.os.Build.VERSION_CODES#N}
3057 * this will be shown in the third line of text in the platform notification template.
3058 * You should not be using {@link #setProgress(int, int, boolean)} at the
3059 * same time on those versions; they occupy the same place.
3060 * </p>
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003061 */
3062 public Builder setSubText(CharSequence text) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003063 mN.extras.putCharSequence(EXTRA_SUB_TEXT, safeCharSequence(text));
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003064 return this;
3065 }
3066
3067 /**
Julia Reynolds3aedded2017-03-31 14:42:09 -04003068 * Provides text that will appear as a link to your application's settings.
3069 *
3070 * <p>This text does not appear within notification {@link Style templates} but may
3071 * appear when the user uses an affordance to learn more about the notification.
3072 * Additionally, this text will not appear unless you provide a valid link target by
3073 * handling {@link #INTENT_CATEGORY_NOTIFICATION_PREFERENCES}.
3074 *
3075 * <p>This text is meant to be concise description about what the user can customize
3076 * when they click on this link. The recommended maximum length is 40 characters.
3077 * @param text
3078 * @return
3079 */
3080 public Builder setSettingsText(CharSequence text) {
3081 mN.mSettingsText = safeCharSequence(text);
3082 return this;
3083 }
3084
3085 /**
Adrian Roose458aa82015-12-08 16:17:19 -08003086 * Set the remote input history.
3087 *
3088 * This should be set to the most recent inputs that have been sent
3089 * through a {@link RemoteInput} of this Notification and cleared once the it is no
3090 * longer relevant (e.g. for chat notifications once the other party has responded).
3091 *
3092 * The most recent input must be stored at the 0 index, the second most recent at the
3093 * 1 index, etc. Note that the system will limit both how far back the inputs will be shown
3094 * and how much of each individual input is shown.
3095 *
3096 * <p>Note: The reply text will only be shown on notifications that have least one action
3097 * with a {@code RemoteInput}.</p>
3098 */
3099 public Builder setRemoteInputHistory(CharSequence[] text) {
3100 if (text == null) {
3101 mN.extras.putCharSequenceArray(EXTRA_REMOTE_INPUT_HISTORY, null);
3102 } else {
3103 final int N = Math.min(MAX_REPLY_HISTORY, text.length);
3104 CharSequence[] safe = new CharSequence[N];
3105 for (int i = 0; i < N; i++) {
3106 safe[i] = safeCharSequence(text[i]);
3107 }
3108 mN.extras.putCharSequenceArray(EXTRA_REMOTE_INPUT_HISTORY, safe);
3109 }
3110 return this;
3111 }
3112
3113 /**
Julia Reynolds13d898c2017-02-02 12:22:05 -05003114 * Sets the number of items this notification represents. May be displayed as a badge count
3115 * for Launchers that support badging.
Joe Onoratocb109a02011-01-18 17:57:41 -08003116 */
Joe Onorato8595a3d2010-11-19 18:12:07 -08003117 public Builder setNumber(int number) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003118 mN.number = number;
Joe Onorato8595a3d2010-11-19 18:12:07 -08003119 return this;
3120 }
3121
Joe Onoratocb109a02011-01-18 17:57:41 -08003122 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003123 * A small piece of additional information pertaining to this notification.
3124 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003125 * The platform template will draw this on the last line of the notification, at the far
3126 * right (to the right of a smallIcon if it has been placed there).
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07003127 *
3128 * @deprecated use {@link #setSubText(CharSequence)} instead to set a text in the header.
3129 * For legacy apps targeting a version below {@link android.os.Build.VERSION_CODES#N} this
3130 * field will still show up, but the subtext will take precedence.
Joe Onoratocb109a02011-01-18 17:57:41 -08003131 */
Aurimas Liutikas514c5ef2016-05-24 15:22:55 -07003132 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08003133 public Builder setContentInfo(CharSequence info) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003134 mN.extras.putCharSequence(EXTRA_INFO_TEXT, safeCharSequence(info));
Joe Onorato46439ce2010-11-19 13:56:21 -08003135 return this;
3136 }
3137
Joe Onoratocb109a02011-01-18 17:57:41 -08003138 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003139 * Set the progress this notification represents.
3140 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003141 * The platform template will represent this using a {@link ProgressBar}.
Jeff Sharkey1c400132011-08-05 14:50:13 -07003142 */
3143 public Builder setProgress(int max, int progress, boolean indeterminate) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003144 mN.extras.putInt(EXTRA_PROGRESS, progress);
3145 mN.extras.putInt(EXTRA_PROGRESS_MAX, max);
3146 mN.extras.putBoolean(EXTRA_PROGRESS_INDETERMINATE, indeterminate);
Jeff Sharkey1c400132011-08-05 14:50:13 -07003147 return this;
3148 }
3149
3150 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003151 * Supply a custom RemoteViews to use instead of the platform template.
3152 *
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003153 * Use {@link #setCustomContentView(RemoteViews)} instead.
Joe Onoratocb109a02011-01-18 17:57:41 -08003154 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003155 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08003156 public Builder setContent(RemoteViews views) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003157 return setCustomContentView(views);
3158 }
3159
3160 /**
3161 * Supply custom RemoteViews to use instead of the platform template.
3162 *
3163 * This will override the layout that would otherwise be constructed by this Builder
3164 * object.
3165 */
3166 public Builder setCustomContentView(RemoteViews contentView) {
3167 mN.contentView = contentView;
3168 return this;
3169 }
3170
3171 /**
3172 * Supply custom RemoteViews to use instead of the platform template in the expanded form.
3173 *
3174 * This will override the expanded layout that would otherwise be constructed by this
3175 * Builder object.
3176 */
3177 public Builder setCustomBigContentView(RemoteViews contentView) {
3178 mN.bigContentView = contentView;
3179 return this;
3180 }
3181
3182 /**
3183 * Supply custom RemoteViews to use instead of the platform template in the heads up dialog.
3184 *
3185 * This will override the heads-up layout that would otherwise be constructed by this
3186 * Builder object.
3187 */
3188 public Builder setCustomHeadsUpContentView(RemoteViews contentView) {
3189 mN.headsUpContentView = contentView;
Joe Onorato46439ce2010-11-19 13:56:21 -08003190 return this;
3191 }
3192
Joe Onoratocb109a02011-01-18 17:57:41 -08003193 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003194 * Supply a {@link PendingIntent} to be sent when the notification is clicked.
3195 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003196 * As of {@link android.os.Build.VERSION_CODES#HONEYCOMB}, if this field is unset and you
3197 * have specified a custom RemoteViews with {@link #setContent(RemoteViews)}, you can use
3198 * {@link RemoteViews#setOnClickPendingIntent RemoteViews.setOnClickPendingIntent(int,PendingIntent)}
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003199 * to assign PendingIntents to individual views in that custom layout (i.e., to create
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003200 * clickable buttons inside the notification view).
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003201 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003202 * @see Notification#contentIntent Notification.contentIntent
Joe Onoratocb109a02011-01-18 17:57:41 -08003203 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003204 public Builder setContentIntent(PendingIntent intent) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003205 mN.contentIntent = intent;
Joe Onorato46439ce2010-11-19 13:56:21 -08003206 return this;
3207 }
3208
Joe Onoratocb109a02011-01-18 17:57:41 -08003209 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003210 * Supply a {@link PendingIntent} to send when the notification is cleared explicitly by the user.
3211 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003212 * @see Notification#deleteIntent
Joe Onoratocb109a02011-01-18 17:57:41 -08003213 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003214 public Builder setDeleteIntent(PendingIntent intent) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003215 mN.deleteIntent = intent;
Joe Onorato46439ce2010-11-19 13:56:21 -08003216 return this;
3217 }
3218
Joe Onoratocb109a02011-01-18 17:57:41 -08003219 /**
3220 * An intent to launch instead of posting the notification to the status bar.
3221 * Only for use with extremely high-priority notifications demanding the user's
3222 * <strong>immediate</strong> attention, such as an incoming phone call or
3223 * alarm clock that the user has explicitly set to a particular time.
3224 * If this facility is used for something else, please give the user an option
3225 * to turn it off and use a normal notification, as this can be extremely
3226 * disruptive.
3227 *
Chris Wren47c20a12014-06-18 17:27:29 -04003228 * <p>
3229 * The system UI may choose to display a heads-up notification, instead of
3230 * launching this intent, while the user is using the device.
3231 * </p>
3232 *
Joe Onoratocb109a02011-01-18 17:57:41 -08003233 * @param intent The pending intent to launch.
3234 * @param highPriority Passing true will cause this notification to be sent
3235 * even if other notifications are suppressed.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003236 *
3237 * @see Notification#fullScreenIntent
Joe Onoratocb109a02011-01-18 17:57:41 -08003238 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003239 public Builder setFullScreenIntent(PendingIntent intent, boolean highPriority) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003240 mN.fullScreenIntent = intent;
Joe Onorato46439ce2010-11-19 13:56:21 -08003241 setFlag(FLAG_HIGH_PRIORITY, highPriority);
3242 return this;
3243 }
3244
Joe Onoratocb109a02011-01-18 17:57:41 -08003245 /**
Dan Sandler5fcdf6e2014-07-18 11:31:15 -04003246 * Set the "ticker" text which is sent to accessibility services.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003247 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003248 * @see Notification#tickerText
Joe Onoratocb109a02011-01-18 17:57:41 -08003249 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003250 public Builder setTicker(CharSequence tickerText) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003251 mN.tickerText = safeCharSequence(tickerText);
Joe Onorato46439ce2010-11-19 13:56:21 -08003252 return this;
3253 }
3254
Joe Onoratocb109a02011-01-18 17:57:41 -08003255 /**
Dan Sandler5fcdf6e2014-07-18 11:31:15 -04003256 * Obsolete version of {@link #setTicker(CharSequence)}.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003257 *
Joe Onoratocb109a02011-01-18 17:57:41 -08003258 */
Dan Sandler5fcdf6e2014-07-18 11:31:15 -04003259 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08003260 public Builder setTicker(CharSequence tickerText, RemoteViews views) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003261 setTicker(tickerText);
3262 // views is ignored
Joe Onorato46439ce2010-11-19 13:56:21 -08003263 return this;
3264 }
3265
Joe Onoratocb109a02011-01-18 17:57:41 -08003266 /**
Dan Sandlerd63f9322015-05-06 15:18:49 -04003267 * Add a large icon to the notification content view.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003268 *
3269 * In the platform template, this image will be shown on the left of the notification view
Dan Sandlerd63f9322015-05-06 15:18:49 -04003270 * in place of the {@link #setSmallIcon(Icon) small icon} (which will be placed in a small
3271 * badge atop the large icon).
Dan Sandler08a04c12015-05-06 15:18:49 -04003272 */
Dan Sandlerd63f9322015-05-06 15:18:49 -04003273 public Builder setLargeIcon(Bitmap b) {
3274 return setLargeIcon(b != null ? Icon.createWithBitmap(b) : null);
3275 }
3276
3277 /**
3278 * Add a large icon to the notification content view.
3279 *
3280 * In the platform template, this image will be shown on the left of the notification view
3281 * in place of the {@link #setSmallIcon(Icon) small icon} (which will be placed in a small
3282 * badge atop the large icon).
3283 */
3284 public Builder setLargeIcon(Icon icon) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003285 mN.mLargeIcon = icon;
3286 mN.extras.putParcelable(EXTRA_LARGE_ICON, icon);
Joe Onorato46439ce2010-11-19 13:56:21 -08003287 return this;
3288 }
3289
Joe Onoratocb109a02011-01-18 17:57:41 -08003290 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003291 * Set the sound to play.
3292 *
John Spurlockc0650f022014-07-19 13:22:39 -04003293 * It will be played using the {@link #AUDIO_ATTRIBUTES_DEFAULT default audio attributes}
3294 * for notifications.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003295 *
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003296 * @deprecated use {@link NotificationChannel#setSound(Uri, AudioAttributes)} instead.
Joe Onoratocb109a02011-01-18 17:57:41 -08003297 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003298 @Deprecated
Joe Onorato52f80cd2010-11-21 15:34:48 -08003299 public Builder setSound(Uri sound) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003300 mN.sound = sound;
3301 mN.audioAttributes = AUDIO_ATTRIBUTES_DEFAULT;
Joe Onorato52f80cd2010-11-21 15:34:48 -08003302 return this;
3303 }
3304
Joe Onoratocb109a02011-01-18 17:57:41 -08003305 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003306 * Set the sound to play, along with a specific stream on which to play it.
Joe Onoratocb109a02011-01-18 17:57:41 -08003307 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003308 * See {@link android.media.AudioManager} for the <code>STREAM_</code> constants.
3309 *
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003310 * @deprecated use {@link NotificationChannel#setSound(Uri, AudioAttributes)}.
Joe Onoratocb109a02011-01-18 17:57:41 -08003311 */
Jean-Michel Trivi81f871e2014-08-06 16:32:38 -07003312 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08003313 public Builder setSound(Uri sound, int streamType) {
Jean-Michel Trivi2f7511f2016-11-28 15:40:27 -08003314 PlayerBase.deprecateStreamTypeForPlayback(streamType, "Notification", "setSound()");
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003315 mN.sound = sound;
3316 mN.audioStreamType = streamType;
Joe Onorato46439ce2010-11-19 13:56:21 -08003317 return this;
3318 }
3319
Joe Onoratocb109a02011-01-18 17:57:41 -08003320 /**
John Spurlockc0650f022014-07-19 13:22:39 -04003321 * Set the sound to play, along with specific {@link AudioAttributes audio attributes} to
3322 * use during playback.
3323 *
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003324 * @deprecated use {@link NotificationChannel#setSound(Uri, AudioAttributes)} instead.
John Spurlockc0650f022014-07-19 13:22:39 -04003325 * @see Notification#sound
3326 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003327 @Deprecated
John Spurlockc0650f022014-07-19 13:22:39 -04003328 public Builder setSound(Uri sound, AudioAttributes audioAttributes) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003329 mN.sound = sound;
3330 mN.audioAttributes = audioAttributes;
John Spurlockc0650f022014-07-19 13:22:39 -04003331 return this;
3332 }
3333
3334 /**
Joe Onoratocb109a02011-01-18 17:57:41 -08003335 * Set the vibration pattern to use.
3336 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003337 * See {@link android.os.Vibrator#vibrate(long[], int)} for a discussion of the
3338 * <code>pattern</code> parameter.
3339 *
Chris Wren47c20a12014-06-18 17:27:29 -04003340 * <p>
3341 * A notification that vibrates is more likely to be presented as a heads-up notification.
3342 * </p>
3343 *
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003344 * @deprecated use {@link NotificationChannel#setVibrationPattern(long[])} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003345 * @see Notification#vibrate
Joe Onoratocb109a02011-01-18 17:57:41 -08003346 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003347 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08003348 public Builder setVibrate(long[] pattern) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003349 mN.vibrate = pattern;
Joe Onorato46439ce2010-11-19 13:56:21 -08003350 return this;
3351 }
3352
Joe Onoratocb109a02011-01-18 17:57:41 -08003353 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003354 * Set the desired color for the indicator LED on the device, as well as the
3355 * blink duty cycle (specified in milliseconds).
3356 *
3357
3358 * Not all devices will honor all (or even any) of these values.
3359 *
Julia Reynolds529e3322017-02-06 08:33:01 -05003360 * @deprecated use {@link NotificationChannel#enableLights(boolean)} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003361 * @see Notification#ledARGB
3362 * @see Notification#ledOnMS
3363 * @see Notification#ledOffMS
Joe Onoratocb109a02011-01-18 17:57:41 -08003364 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003365 @Deprecated
Tor Norbye80756e32015-03-02 09:39:27 -08003366 public Builder setLights(@ColorInt int argb, int onMs, int offMs) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003367 mN.ledARGB = argb;
3368 mN.ledOnMS = onMs;
3369 mN.ledOffMS = offMs;
Julia Reynolds10ee1fc2015-11-09 11:04:55 -05003370 if (onMs != 0 || offMs != 0) {
3371 mN.flags |= FLAG_SHOW_LIGHTS;
3372 }
Joe Onorato46439ce2010-11-19 13:56:21 -08003373 return this;
3374 }
3375
Joe Onoratocb109a02011-01-18 17:57:41 -08003376 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003377 * Set whether this is an "ongoing" notification.
Joe Onoratocb109a02011-01-18 17:57:41 -08003378 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003379
3380 * Ongoing notifications cannot be dismissed by the user, so your application or service
3381 * must take care of canceling them.
3382 *
3383
3384 * They are typically used to indicate a background task that the user is actively engaged
3385 * with (e.g., playing music) or is pending in some way and therefore occupying the device
3386 * (e.g., a file download, sync operation, active network connection).
3387 *
3388
3389 * @see Notification#FLAG_ONGOING_EVENT
3390 * @see Service#setForeground(boolean)
Joe Onoratocb109a02011-01-18 17:57:41 -08003391 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003392 public Builder setOngoing(boolean ongoing) {
3393 setFlag(FLAG_ONGOING_EVENT, ongoing);
3394 return this;
3395 }
3396
Joe Onoratocb109a02011-01-18 17:57:41 -08003397 /**
Selim Cinek7b9605b2017-01-19 17:36:00 -08003398 * Set whether this notification should be colorized. When set, the color set with
3399 * {@link #setColor(int)} will be used as the background color of this notification.
3400 * <p>
Selim Cinek7b9605b2017-01-19 17:36:00 -08003401 * This should only be used for high priority ongoing tasks like navigation, an ongoing
3402 * call, or other similarly high-priority events for the user.
Selim Cinek99104832017-01-25 14:47:33 -08003403 * <p>
Selim Cinek22714f12017-04-13 16:23:53 -07003404 * For most styles, the coloring will only be applied if the notification is for a
3405 * foreground service notification.
Selim Cinek99104832017-01-25 14:47:33 -08003406 * However, for {@link MediaStyle} and {@link DecoratedMediaCustomViewStyle} notifications
Selim Cinek22714f12017-04-13 16:23:53 -07003407 * that have a media session attached there is no such requirement.
Selim Cinek7b9605b2017-01-19 17:36:00 -08003408 *
Selim Cinek7b9605b2017-01-19 17:36:00 -08003409 * @see Builder#setColor(int)
Selim Cinek99104832017-01-25 14:47:33 -08003410 * @see MediaStyle#setMediaSession(MediaSession.Token)
Selim Cinek7b9605b2017-01-19 17:36:00 -08003411 */
3412 public Builder setColorized(boolean colorize) {
3413 mN.extras.putBoolean(EXTRA_COLORIZED, colorize);
3414 return this;
3415 }
3416
3417 /**
Joe Onoratocb109a02011-01-18 17:57:41 -08003418 * Set this flag if you would only like the sound, vibrate
3419 * and ticker to be played if the notification is not already showing.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003420 *
3421 * @see Notification#FLAG_ONLY_ALERT_ONCE
Joe Onoratocb109a02011-01-18 17:57:41 -08003422 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003423 public Builder setOnlyAlertOnce(boolean onlyAlertOnce) {
3424 setFlag(FLAG_ONLY_ALERT_ONCE, onlyAlertOnce);
3425 return this;
3426 }
3427
Joe Onoratocb109a02011-01-18 17:57:41 -08003428 /**
Julia Reynolds04499532016-09-13 14:04:53 -04003429 * Make this notification automatically dismissed when the user touches it.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003430 *
3431 * @see Notification#FLAG_AUTO_CANCEL
Joe Onoratocb109a02011-01-18 17:57:41 -08003432 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003433 public Builder setAutoCancel(boolean autoCancel) {
Joe Onorato281d83f2011-01-04 17:13:10 -08003434 setFlag(FLAG_AUTO_CANCEL, autoCancel);
Joe Onorato46439ce2010-11-19 13:56:21 -08003435 return this;
3436 }
3437
Joe Onoratocb109a02011-01-18 17:57:41 -08003438 /**
Griff Hazendfcb0802014-02-11 12:00:00 -08003439 * Set whether or not this notification should not bridge to other devices.
3440 *
3441 * <p>Some notifications can be bridged to other devices for remote display.
3442 * This hint can be set to recommend this notification not be bridged.
3443 */
3444 public Builder setLocalOnly(boolean localOnly) {
3445 setFlag(FLAG_LOCAL_ONLY, localOnly);
3446 return this;
3447 }
3448
3449 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003450 * Set which notification properties will be inherited from system defaults.
Joe Onoratocb109a02011-01-18 17:57:41 -08003451 * <p>
3452 * The value should be one or more of the following fields combined with
3453 * bitwise-or:
3454 * {@link #DEFAULT_SOUND}, {@link #DEFAULT_VIBRATE}, {@link #DEFAULT_LIGHTS}.
3455 * <p>
3456 * For all default values, use {@link #DEFAULT_ALL}.
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003457 *
3458 * @deprecated use {@link NotificationChannel#enableVibration(boolean)} and
Julia Reynolds529e3322017-02-06 08:33:01 -05003459 * {@link NotificationChannel#enableLights(boolean)} and
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003460 * {@link NotificationChannel#setSound(Uri, AudioAttributes)} instead.
Joe Onoratocb109a02011-01-18 17:57:41 -08003461 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003462 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08003463 public Builder setDefaults(int defaults) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003464 mN.defaults = defaults;
Joe Onorato46439ce2010-11-19 13:56:21 -08003465 return this;
3466 }
3467
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003468 /**
3469 * Set the priority of this notification.
3470 *
3471 * @see Notification#priority
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003472 * @deprecated use {@link NotificationChannel#setImportance(int)} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003473 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003474 @Deprecated
Tor Norbyed9273d62013-05-30 15:59:53 -07003475 public Builder setPriority(@Priority int pri) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003476 mN.priority = pri;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003477 return this;
3478 }
Joe Malin8d40d042012-11-05 11:36:40 -08003479
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003480 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -04003481 * Set the notification category.
Joe Malin8d40d042012-11-05 11:36:40 -08003482 *
John Spurlockfd7f1e02014-03-18 16:41:57 -04003483 * @see Notification#category
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003484 */
John Spurlockfd7f1e02014-03-18 16:41:57 -04003485 public Builder setCategory(String category) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003486 mN.category = category;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003487 return this;
3488 }
3489
3490 /**
Chris Wrendde75302014-03-26 17:24:15 -04003491 * Add a person that is relevant to this notification.
3492 *
Chris Wrene6c48932014-09-29 17:19:27 -04003493 * <P>
3494 * Depending on user preferences, this annotation may allow the notification to pass
Julia Reynoldse071abd2017-03-22 10:52:11 -04003495 * through interruption filters, if this notification is of category {@link #CATEGORY_CALL}
3496 * or {@link #CATEGORY_MESSAGE}. The addition of people may also cause this notification to
3497 * appear more prominently in the user interface.
Chris Wrene6c48932014-09-29 17:19:27 -04003498 * </P>
3499 *
3500 * <P>
3501 * The person should be specified by the {@code String} representation of a
3502 * {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI}.
3503 * </P>
3504 *
3505 * <P>The system will also attempt to resolve {@code mailto:} and {@code tel:} schema
3506 * URIs. The path part of these URIs must exist in the contacts database, in the
3507 * appropriate column, or the reference will be discarded as invalid. Telephone schema
3508 * URIs will be resolved by {@link android.provider.ContactsContract.PhoneLookup}.
3509 * </P>
3510 *
3511 * @param uri A URI for the person.
Chris Wrendde75302014-03-26 17:24:15 -04003512 * @see Notification#EXTRA_PEOPLE
3513 */
Chris Wrene6c48932014-09-29 17:19:27 -04003514 public Builder addPerson(String uri) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003515 mPersonList.add(uri);
Chris Wrendde75302014-03-26 17:24:15 -04003516 return this;
3517 }
3518
3519 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003520 * Set this notification to be part of a group of notifications sharing the same key.
3521 * Grouped notifications may display in a cluster or stack on devices which
3522 * support such rendering.
3523 *
3524 * <p>To make this notification the summary for its group, also call
3525 * {@link #setGroupSummary}. A sort order can be specified for group members by using
3526 * {@link #setSortKey}.
3527 * @param groupKey The group key of the group.
3528 * @return this object for method chaining
3529 */
3530 public Builder setGroup(String groupKey) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003531 mN.mGroupKey = groupKey;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003532 return this;
3533 }
3534
3535 /**
3536 * Set this notification to be the group summary for a group of notifications.
3537 * Grouped notifications may display in a cluster or stack on devices which
Julia Reynolds04499532016-09-13 14:04:53 -04003538 * support such rendering. If thereRequires a group key also be set using {@link #setGroup}.
3539 * The group summary may be suppressed if too few notifications are included in the group.
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003540 * @param isGroupSummary Whether this notification should be a group summary.
3541 * @return this object for method chaining
3542 */
3543 public Builder setGroupSummary(boolean isGroupSummary) {
3544 setFlag(FLAG_GROUP_SUMMARY, isGroupSummary);
3545 return this;
3546 }
3547
3548 /**
3549 * Set a sort key that orders this notification among other notifications from the
3550 * same package. This can be useful if an external sort was already applied and an app
3551 * would like to preserve this. Notifications will be sorted lexicographically using this
3552 * value, although providing different priorities in addition to providing sort key may
3553 * cause this value to be ignored.
3554 *
3555 * <p>This sort key can also be used to order members of a notification group. See
Griff Hazen9e1379f2014-05-20 12:50:51 -07003556 * {@link #setGroup}.
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003557 *
3558 * @see String#compareTo(String)
3559 */
3560 public Builder setSortKey(String sortKey) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003561 mN.mSortKey = sortKey;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003562 return this;
3563 }
3564
3565 /**
Griff Hazen720042b2014-02-24 15:46:56 -08003566 * Merge additional metadata into this notification.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003567 *
Griff Hazen720042b2014-02-24 15:46:56 -08003568 * <p>Values within the Bundle will replace existing extras values in this Builder.
3569 *
3570 * @see Notification#extras
3571 */
Griff Hazen959591e2014-05-15 22:26:18 -07003572 public Builder addExtras(Bundle extras) {
3573 if (extras != null) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003574 mUserExtras.putAll(extras);
Griff Hazen720042b2014-02-24 15:46:56 -08003575 }
3576 return this;
3577 }
3578
3579 /**
3580 * Set metadata for this notification.
3581 *
3582 * <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 -04003583 * current contents are copied into the Notification each time {@link #build()} is
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003584 * called.
3585 *
Griff Hazen720042b2014-02-24 15:46:56 -08003586 * <p>Replaces any existing extras values with those from the provided Bundle.
3587 * Use {@link #addExtras} to merge in metadata instead.
3588 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003589 * @see Notification#extras
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003590 */
Griff Hazen959591e2014-05-15 22:26:18 -07003591 public Builder setExtras(Bundle extras) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003592 if (extras != null) {
3593 mUserExtras = extras;
3594 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003595 return this;
3596 }
3597
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003598 /**
Griff Hazen720042b2014-02-24 15:46:56 -08003599 * Get the current metadata Bundle used by this notification Builder.
3600 *
3601 * <p>The returned Bundle is shared with this Builder.
3602 *
3603 * <p>The current contents of this Bundle are copied into the Notification each time
3604 * {@link #build()} is called.
3605 *
3606 * @see Notification#extras
3607 */
3608 public Bundle getExtras() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003609 return mUserExtras;
3610 }
3611
3612 private Bundle getAllExtras() {
3613 final Bundle saveExtras = (Bundle) mUserExtras.clone();
3614 saveExtras.putAll(mN.extras);
3615 return saveExtras;
Griff Hazen720042b2014-02-24 15:46:56 -08003616 }
3617
3618 /**
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003619 * Add an action to this notification. Actions are typically displayed by
3620 * the system as a button adjacent to the notification content.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04003621 * <p>
3622 * Every action must have an icon (32dp square and matching the
3623 * <a href="{@docRoot}design/style/iconography.html#action-bar">Holo
3624 * Dark action bar</a> visual style), a textual label, and a {@link PendingIntent}.
3625 * <p>
3626 * A notification in its expanded form can display up to 3 actions, from left to right in
3627 * the order they were added. Actions will not be displayed when the notification is
3628 * collapsed, however, so be sure that any essential functions may be accessed by the user
3629 * in some other way (for example, in the Activity pointed to by {@link #contentIntent}).
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003630 *
3631 * @param icon Resource ID of a drawable that represents the action.
3632 * @param title Text describing the action.
3633 * @param intent PendingIntent to be fired when the action is invoked.
Dan Sandler86647982015-05-13 23:41:13 -04003634 *
3635 * @deprecated Use {@link #addAction(Action)} instead.
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003636 */
Dan Sandler86647982015-05-13 23:41:13 -04003637 @Deprecated
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003638 public Builder addAction(int icon, CharSequence title, PendingIntent intent) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04003639 mActions.add(new Action(icon, safeCharSequence(title), intent));
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003640 return this;
3641 }
3642
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003643 /**
Griff Hazen959591e2014-05-15 22:26:18 -07003644 * Add an action to this notification. Actions are typically displayed by
3645 * the system as a button adjacent to the notification content.
3646 * <p>
3647 * Every action must have an icon (32dp square and matching the
3648 * <a href="{@docRoot}design/style/iconography.html#action-bar">Holo
3649 * Dark action bar</a> visual style), a textual label, and a {@link PendingIntent}.
3650 * <p>
3651 * A notification in its expanded form can display up to 3 actions, from left to right in
3652 * the order they were added. Actions will not be displayed when the notification is
3653 * collapsed, however, so be sure that any essential functions may be accessed by the user
3654 * in some other way (for example, in the Activity pointed to by {@link #contentIntent}).
3655 *
3656 * @param action The action to add.
3657 */
3658 public Builder addAction(Action action) {
liangweikang63b03b52017-03-16 19:22:15 +08003659 if (action != null) {
3660 mActions.add(action);
3661 }
Griff Hazen959591e2014-05-15 22:26:18 -07003662 return this;
3663 }
3664
3665 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003666 * Alter the complete list of actions attached to this notification.
3667 * @see #addAction(Action).
3668 *
3669 * @param actions
3670 * @return
3671 */
3672 public Builder setActions(Action... actions) {
3673 mActions.clear();
3674 for (int i = 0; i < actions.length; i++) {
liangweikang63b03b52017-03-16 19:22:15 +08003675 if (actions[i] != null) {
3676 mActions.add(actions[i]);
3677 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003678 }
3679 return this;
3680 }
3681
3682 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003683 * Add a rich notification style to be applied at build time.
3684 *
3685 * @param style Object responsible for modifying the notification style.
3686 */
3687 public Builder setStyle(Style style) {
3688 if (mStyle != style) {
3689 mStyle = style;
Daniel Sandlerc08dea22012-06-28 08:35:24 -07003690 if (mStyle != null) {
3691 mStyle.setBuilder(this);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003692 mN.extras.putString(EXTRA_TEMPLATE, style.getClass().getName());
3693 } else {
3694 mN.extras.remove(EXTRA_TEMPLATE);
Daniel Sandlerc08dea22012-06-28 08:35:24 -07003695 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003696 }
3697 return this;
3698 }
3699
Dan Sandler0bf2ed82013-12-21 23:33:41 -06003700 /**
3701 * Specify the value of {@link #visibility}.
Griff Hazenb720abe2014-05-20 13:15:30 -07003702 *
Dan Sandler0bf2ed82013-12-21 23:33:41 -06003703 * @return The same Builder.
3704 */
Jeff Sharkey30e06bb2017-04-24 11:18:03 -06003705 public Builder setVisibility(@Visibility int visibility) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003706 mN.visibility = visibility;
Dan Sandler0bf2ed82013-12-21 23:33:41 -06003707 return this;
3708 }
3709
3710 /**
3711 * Supply a replacement Notification whose contents should be shown in insecure contexts
3712 * (i.e. atop the secure lockscreen). See {@link #visibility} and {@link #VISIBILITY_PUBLIC}.
3713 * @param n A replacement notification, presumably with some or all info redacted.
3714 * @return The same Builder.
3715 */
3716 public Builder setPublicVersion(Notification n) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003717 if (n != null) {
3718 mN.publicVersion = new Notification();
3719 n.cloneInto(mN.publicVersion, /*heavy=*/ true);
3720 } else {
3721 mN.publicVersion = null;
3722 }
Dan Sandler0bf2ed82013-12-21 23:33:41 -06003723 return this;
3724 }
3725
Griff Hazenb720abe2014-05-20 13:15:30 -07003726 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003727 * Apply an extender to this notification builder. Extenders may be used to add
3728 * metadata or change options on this builder.
3729 */
Griff Hazen61a9e862014-05-22 16:05:19 -07003730 public Builder extend(Extender extender) {
3731 extender.extend(this);
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003732 return this;
3733 }
3734
Dan Sandler4e787062015-06-17 15:09:48 -04003735 /**
3736 * @hide
3737 */
Julia Reynoldse46bb372016-03-17 11:05:58 -04003738 public Builder setFlag(int mask, boolean value) {
Joe Onorato46439ce2010-11-19 13:56:21 -08003739 if (value) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003740 mN.flags |= mask;
Joe Onorato46439ce2010-11-19 13:56:21 -08003741 } else {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003742 mN.flags &= ~mask;
Joe Onorato46439ce2010-11-19 13:56:21 -08003743 }
Julia Reynoldse46bb372016-03-17 11:05:58 -04003744 return this;
Joe Onorato46439ce2010-11-19 13:56:21 -08003745 }
3746
Dan Sandler26e81cf2014-05-06 10:01:27 -04003747 /**
3748 * Sets {@link Notification#color}.
3749 *
3750 * @param argb The accent color to use
3751 *
3752 * @return The same Builder.
3753 */
Tor Norbye80756e32015-03-02 09:39:27 -08003754 public Builder setColor(@ColorInt int argb) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003755 mN.color = argb;
Julia Reynolds10ee1fc2015-11-09 11:04:55 -05003756 sanitizeColor();
Dan Sandler26e81cf2014-05-06 10:01:27 -04003757 return this;
3758 }
3759
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003760 private Drawable getProfileBadgeDrawable() {
Chris Wren66619a22016-05-12 16:42:37 -04003761 if (mContext.getUserId() == UserHandle.USER_SYSTEM) {
3762 // This user can never be a badged profile,
3763 // and also includes USER_ALL system notifications.
3764 return null;
3765 }
Christoph Studer7ac80e62014-08-04 16:01:57 +02003766 // Note: This assumes that the current user can read the profile badge of the
3767 // originating user.
Selim Cineke6ff9462016-01-15 15:07:06 -08003768 return mContext.getPackageManager().getUserBadgeForDensityNoBackground(
Julia Reynoldsda303542015-11-23 14:00:20 -05003769 new UserHandle(mContext.getUserId()), 0);
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003770 }
3771
3772 private Bitmap getProfileBadge() {
3773 Drawable badge = getProfileBadgeDrawable();
Kenny Guy8a0101b2014-05-08 23:34:12 +01003774 if (badge == null) {
3775 return null;
3776 }
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003777 final int size = mContext.getResources().getDimensionPixelSize(
3778 R.dimen.notification_badge_size);
3779 Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
Kenny Guy8a0101b2014-05-08 23:34:12 +01003780 Canvas canvas = new Canvas(bitmap);
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003781 badge.setBounds(0, 0, size, size);
Kenny Guy8a0101b2014-05-08 23:34:12 +01003782 badge.draw(canvas);
3783 return bitmap;
3784 }
3785
Selim Cinekc848c3a2016-01-13 15:27:30 -08003786 private void bindProfileBadge(RemoteViews contentView) {
Kenny Guy98193ea2014-07-24 19:54:37 +01003787 Bitmap profileBadge = getProfileBadge();
3788
Kenny Guy98193ea2014-07-24 19:54:37 +01003789 if (profileBadge != null) {
Selim Cinekc848c3a2016-01-13 15:27:30 -08003790 contentView.setImageViewBitmap(R.id.profile_badge, profileBadge);
3791 contentView.setViewVisibility(R.id.profile_badge, View.VISIBLE);
Selim Cinek7b9605b2017-01-19 17:36:00 -08003792 if (isColorized()) {
3793 contentView.setDrawableParameters(R.id.profile_badge, false, -1,
3794 getPrimaryTextColor(), PorterDuff.Mode.SRC_ATOP, -1);
3795 }
Kenny Guy98193ea2014-07-24 19:54:37 +01003796 }
Kenny Guy98193ea2014-07-24 19:54:37 +01003797 }
3798
Christoph Studerfe718432014-09-01 18:21:18 +02003799 private void resetStandardTemplate(RemoteViews contentView) {
Selim Cinekeaa29ca2015-11-23 13:51:13 -08003800 resetNotificationHeader(contentView);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003801 resetContentMargins(contentView);
Christoph Studerfe718432014-09-01 18:21:18 +02003802 contentView.setViewVisibility(R.id.right_icon, View.GONE);
Selim Cinek860b6da2015-12-16 19:02:19 -08003803 contentView.setViewVisibility(R.id.title, View.GONE);
Christoph Studerfe718432014-09-01 18:21:18 +02003804 contentView.setTextViewText(R.id.title, null);
Selim Cinek41598732016-01-11 16:58:37 -08003805 contentView.setViewVisibility(R.id.text, View.GONE);
Christoph Studerfe718432014-09-01 18:21:18 +02003806 contentView.setTextViewText(R.id.text, null);
Selim Cinek29603462015-11-17 19:04:39 -08003807 contentView.setViewVisibility(R.id.text_line_1, View.GONE);
Selim Cinek41598732016-01-11 16:58:37 -08003808 contentView.setTextViewText(R.id.text_line_1, null);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003809 }
3810
Selim Cinekeaa29ca2015-11-23 13:51:13 -08003811 /**
3812 * Resets the notification header to its original state
3813 */
3814 private void resetNotificationHeader(RemoteViews contentView) {
Adrian Roosc4337a32016-08-02 18:30:34 -07003815 // Small icon doesn't need to be reset, as it's always set. Resetting would prevent
3816 // re-using the drawable when the notification is updated.
Selim Cinek7b836392015-12-04 20:02:59 -08003817 contentView.setBoolean(R.id.notification_header, "setExpanded", false);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003818 contentView.setTextViewText(R.id.app_name_text, null);
Christoph Studerca1db712014-09-10 17:31:33 +02003819 contentView.setViewVisibility(R.id.chronometer, View.GONE);
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07003820 contentView.setViewVisibility(R.id.header_text, View.GONE);
Adrian Roos9dfb78f2016-06-30 15:43:44 -07003821 contentView.setTextViewText(R.id.header_text, null);
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07003822 contentView.setViewVisibility(R.id.header_text_divider, View.GONE);
Selim Cinek29603462015-11-17 19:04:39 -08003823 contentView.setViewVisibility(R.id.time_divider, View.GONE);
Selim Cinekb85f36fd2016-04-20 18:46:36 -07003824 contentView.setViewVisibility(R.id.time, View.GONE);
Selim Cinekc848c3a2016-01-13 15:27:30 -08003825 contentView.setImageViewIcon(R.id.profile_badge, null);
3826 contentView.setViewVisibility(R.id.profile_badge, View.GONE);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003827 }
3828
3829 private void resetContentMargins(RemoteViews contentView) {
Adrian Roos2d5dbba2016-06-08 17:11:53 -07003830 contentView.setViewLayoutMarginEndDimen(R.id.line1, 0);
3831 contentView.setViewLayoutMarginEndDimen(R.id.text, 0);
Christoph Studerfe718432014-09-01 18:21:18 +02003832 }
3833
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003834 private RemoteViews applyStandardTemplate(int resId) {
Adrian Roos70d7aa32017-01-11 15:39:06 -08003835 return applyStandardTemplate(resId, mParams.reset().fillTextsFrom(this));
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02003836 }
3837
3838 /**
3839 * @param hasProgress whether the progress bar should be shown and set
3840 */
3841 private RemoteViews applyStandardTemplate(int resId, boolean hasProgress) {
Adrian Roos70d7aa32017-01-11 15:39:06 -08003842 return applyStandardTemplate(resId, mParams.reset().hasProgress(hasProgress)
3843 .fillTextsFrom(this));
Adrian Roosc1a80b02016-04-05 14:54:55 -07003844 }
3845
Adrian Roos70d7aa32017-01-11 15:39:06 -08003846 private RemoteViews applyStandardTemplate(int resId, StandardTemplateParams p) {
Kenny Guy77320062014-08-27 21:37:15 +01003847 RemoteViews contentView = new BuilderRemoteViews(mContext.getApplicationInfo(), resId);
Dan Sandler539aad42014-08-04 00:43:39 -04003848
Christoph Studerfe718432014-09-01 18:21:18 +02003849 resetStandardTemplate(contentView);
3850
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003851 final Bundle ex = mN.extras;
Selim Cinek7b9605b2017-01-19 17:36:00 -08003852 updateBackgroundColor(contentView);
Adrian Roos487374f2017-01-11 15:48:14 -08003853 bindNotificationHeader(contentView, p.ambient);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003854 bindLargeIcon(contentView);
Adrian Roos70d7aa32017-01-11 15:39:06 -08003855 boolean showProgress = handleProgressBar(p.hasProgress, contentView, ex);
3856 if (p.title != null) {
Selim Cinek860b6da2015-12-16 19:02:19 -08003857 contentView.setViewVisibility(R.id.title, View.VISIBLE);
Adrian Roos70d7aa32017-01-11 15:39:06 -08003858 contentView.setTextViewText(R.id.title, p.title);
Adrian Roos72171622017-01-27 10:32:06 -08003859 if (!p.ambient) {
3860 setTextViewColorPrimary(contentView, R.id.title);
3861 }
Selim Cinek954cc232016-05-20 13:29:23 -07003862 contentView.setViewLayoutWidth(R.id.title, showProgress
3863 ? ViewGroup.LayoutParams.WRAP_CONTENT
3864 : ViewGroup.LayoutParams.MATCH_PARENT);
Joe Onorato561d3852010-11-20 18:09:34 -08003865 }
Adrian Roos70d7aa32017-01-11 15:39:06 -08003866 if (p.text != null) {
Selim Cinek41598732016-01-11 16:58:37 -08003867 int textId = showProgress ? com.android.internal.R.id.text_line_1
3868 : com.android.internal.R.id.text;
Adrian Roos70d7aa32017-01-11 15:39:06 -08003869 contentView.setTextViewText(textId, p.text);
Adrian Roos72171622017-01-27 10:32:06 -08003870 if (!p.ambient) {
3871 setTextViewColorSecondary(contentView, textId);
3872 }
Selim Cinek41598732016-01-11 16:58:37 -08003873 contentView.setViewVisibility(textId, View.VISIBLE);
Joe Onorato561d3852010-11-20 18:09:34 -08003874 }
Selim Cinekc848c3a2016-01-13 15:27:30 -08003875
Selim Cinek279fa862016-06-14 10:57:25 -07003876 setContentMinHeight(contentView, showProgress || mN.hasLargeIcon());
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003877
Selim Cinek29603462015-11-17 19:04:39 -08003878 return contentView;
3879 }
3880
Selim Cinek7b9605b2017-01-19 17:36:00 -08003881 private void setTextViewColorPrimary(RemoteViews contentView, int id) {
3882 ensureColors();
3883 contentView.setTextColor(id, mPrimaryTextColor);
3884 }
3885
Selim Cinek389edcd2017-05-11 19:16:44 -07003886 /**
3887 * @return the primary text color
3888 * @hide
3889 */
3890 @VisibleForTesting
3891 public int getPrimaryTextColor() {
Selim Cinek7b9605b2017-01-19 17:36:00 -08003892 ensureColors();
3893 return mPrimaryTextColor;
3894 }
3895
Selim Cinek389edcd2017-05-11 19:16:44 -07003896 /**
3897 * @return the secondary text color
3898 * @hide
3899 */
3900 @VisibleForTesting
3901 public int getSecondaryTextColor() {
3902 ensureColors();
3903 return mSecondaryTextColor;
3904 }
3905
Selim Cinek7b9605b2017-01-19 17:36:00 -08003906 private int getActionBarColor() {
3907 ensureColors();
3908 return mActionBarColor;
3909 }
3910
Selim Cinek622c64a2017-04-17 17:10:05 -07003911 private int getActionBarColorDeEmphasized() {
3912 int backgroundColor = getBackgroundColor();
3913 return NotificationColorUtil.getShiftedColor(backgroundColor, 12);
3914 }
3915
Selim Cinek7b9605b2017-01-19 17:36:00 -08003916 private void setTextViewColorSecondary(RemoteViews contentView, int id) {
3917 ensureColors();
3918 contentView.setTextColor(id, mSecondaryTextColor);
3919 }
3920
3921 private void ensureColors() {
3922 int backgroundColor = getBackgroundColor();
3923 if (mPrimaryTextColor == COLOR_INVALID
3924 || mSecondaryTextColor == COLOR_INVALID
3925 || mActionBarColor == COLOR_INVALID
3926 || mTextColorsAreForBackground != backgroundColor) {
3927 mTextColorsAreForBackground = backgroundColor;
Selim Cinek5fb73f82017-04-20 16:55:38 -07003928 if (mForegroundColor == COLOR_INVALID || !isColorized()) {
3929 mPrimaryTextColor = NotificationColorUtil.resolvePrimaryColor(mContext,
3930 backgroundColor);
3931 mSecondaryTextColor = NotificationColorUtil.resolveSecondaryColor(mContext,
3932 backgroundColor);
Selim Cinekac5f0272017-05-02 16:05:41 -07003933 if (backgroundColor != COLOR_DEFAULT
3934 && (mBackgroundColorHint != COLOR_INVALID || isColorized())) {
3935 mPrimaryTextColor = NotificationColorUtil.findAlphaToMeetContrast(
3936 mPrimaryTextColor, backgroundColor, 4.5);
3937 mSecondaryTextColor = NotificationColorUtil.findAlphaToMeetContrast(
3938 mSecondaryTextColor, backgroundColor, 4.5);
3939 }
Selim Cinek5fb73f82017-04-20 16:55:38 -07003940 } else {
3941 double backLum = NotificationColorUtil.calculateLuminance(backgroundColor);
3942 double textLum = NotificationColorUtil.calculateLuminance(mForegroundColor);
3943 double contrast = NotificationColorUtil.calculateContrast(mForegroundColor,
3944 backgroundColor);
Selim Cinek389edcd2017-05-11 19:16:44 -07003945 // We only respect the given colors if worst case Black or White still has
3946 // contrast
3947 boolean backgroundLight = backLum > textLum
3948 && satisfiesTextContrast(backgroundColor, Color.BLACK)
3949 || backLum <= textLum
3950 && !satisfiesTextContrast(backgroundColor, Color.WHITE);
Selim Cinek5fb73f82017-04-20 16:55:38 -07003951 if (contrast < 4.5f) {
Selim Cinek389edcd2017-05-11 19:16:44 -07003952 if (backgroundLight) {
Selim Cinek5fb73f82017-04-20 16:55:38 -07003953 mSecondaryTextColor = NotificationColorUtil.findContrastColor(
3954 mForegroundColor,
3955 backgroundColor,
3956 true /* findFG */,
3957 4.5f);
3958 mPrimaryTextColor = NotificationColorUtil.changeColorLightness(
Selim Cinek389edcd2017-05-11 19:16:44 -07003959 mSecondaryTextColor, -LIGHTNESS_TEXT_DIFFERENCE_LIGHT);
Selim Cinek5fb73f82017-04-20 16:55:38 -07003960 } else {
3961 mSecondaryTextColor =
3962 NotificationColorUtil.findContrastColorAgainstDark(
3963 mForegroundColor,
3964 backgroundColor,
3965 true /* findFG */,
3966 4.5f);
3967 mPrimaryTextColor = NotificationColorUtil.changeColorLightness(
Selim Cinek389edcd2017-05-11 19:16:44 -07003968 mSecondaryTextColor, -LIGHTNESS_TEXT_DIFFERENCE_DARK);
Selim Cinek5fb73f82017-04-20 16:55:38 -07003969 }
3970 } else {
3971 mPrimaryTextColor = mForegroundColor;
3972 mSecondaryTextColor = NotificationColorUtil.changeColorLightness(
Selim Cinek389edcd2017-05-11 19:16:44 -07003973 mPrimaryTextColor, backgroundLight ? LIGHTNESS_TEXT_DIFFERENCE_LIGHT
3974 : LIGHTNESS_TEXT_DIFFERENCE_DARK);
Selim Cinek5fb73f82017-04-20 16:55:38 -07003975 if (NotificationColorUtil.calculateContrast(mSecondaryTextColor,
3976 backgroundColor) < 4.5f) {
3977 // oh well the secondary is not good enough
Selim Cinek389edcd2017-05-11 19:16:44 -07003978 if (backgroundLight) {
Selim Cinek5fb73f82017-04-20 16:55:38 -07003979 mSecondaryTextColor = NotificationColorUtil.findContrastColor(
3980 mSecondaryTextColor,
3981 backgroundColor,
3982 true /* findFG */,
3983 4.5f);
3984 } else {
3985 mSecondaryTextColor
3986 = NotificationColorUtil.findContrastColorAgainstDark(
3987 mSecondaryTextColor,
3988 backgroundColor,
3989 true /* findFG */,
3990 4.5f);
3991 }
3992 mPrimaryTextColor = NotificationColorUtil.changeColorLightness(
Selim Cinek389edcd2017-05-11 19:16:44 -07003993 mSecondaryTextColor, backgroundLight
3994 ? -LIGHTNESS_TEXT_DIFFERENCE_LIGHT
3995 : -LIGHTNESS_TEXT_DIFFERENCE_DARK);
Selim Cinek5fb73f82017-04-20 16:55:38 -07003996 }
3997 }
3998 }
Selim Cinek875ba9b2017-02-13 16:20:17 -08003999 mActionBarColor = NotificationColorUtil.resolveActionBarColor(mContext,
4000 backgroundColor);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004001 }
4002 }
4003
4004 private void updateBackgroundColor(RemoteViews contentView) {
4005 if (isColorized()) {
4006 contentView.setInt(R.id.status_bar_latest_event_content, "setBackgroundColor",
4007 getBackgroundColor());
4008 } else {
4009 // Clear it!
4010 contentView.setInt(R.id.status_bar_latest_event_content, "setBackgroundResource",
4011 0);
4012 }
4013 }
4014
Selim Cinek860b6da2015-12-16 19:02:19 -08004015 /**
4016 * @param remoteView the remote view to update the minheight in
4017 * @param hasMinHeight does it have a mimHeight
4018 * @hide
4019 */
4020 void setContentMinHeight(RemoteViews remoteView, boolean hasMinHeight) {
4021 int minHeight = 0;
4022 if (hasMinHeight) {
4023 // we need to set the minHeight of the notification
4024 minHeight = mContext.getResources().getDimensionPixelSize(
4025 com.android.internal.R.dimen.notification_min_content_height);
4026 }
4027 remoteView.setInt(R.id.notification_main_column, "setMinimumHeight", minHeight);
4028 }
4029
Selim Cinek29603462015-11-17 19:04:39 -08004030 private boolean handleProgressBar(boolean hasProgress, RemoteViews contentView, Bundle ex) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004031 final int max = ex.getInt(EXTRA_PROGRESS_MAX, 0);
4032 final int progress = ex.getInt(EXTRA_PROGRESS, 0);
4033 final boolean ind = ex.getBoolean(EXTRA_PROGRESS_INDETERMINATE);
4034 if (hasProgress && (max != 0 || ind)) {
Selim Cinek29603462015-11-17 19:04:39 -08004035 contentView.setViewVisibility(com.android.internal.R.id.progress, View.VISIBLE);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004036 contentView.setProgressBar(
Selim Cinek29603462015-11-17 19:04:39 -08004037 R.id.progress, max, progress, ind);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004038 contentView.setProgressBackgroundTintList(
4039 R.id.progress, ColorStateList.valueOf(mContext.getColor(
4040 R.color.notification_progress_background_color)));
Selim Cinek29603462015-11-17 19:04:39 -08004041 if (mN.color != COLOR_DEFAULT) {
Adrian Roos4ff3b122016-02-01 12:26:13 -08004042 ColorStateList colorStateList = ColorStateList.valueOf(resolveContrastColor());
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004043 contentView.setProgressTintList(R.id.progress, colorStateList);
4044 contentView.setProgressIndeterminateTintList(R.id.progress, colorStateList);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004045 }
Selim Cinek29603462015-11-17 19:04:39 -08004046 return true;
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004047 } else {
4048 contentView.setViewVisibility(R.id.progress, View.GONE);
Selim Cinek29603462015-11-17 19:04:39 -08004049 return false;
Jeff Sharkey1c400132011-08-05 14:50:13 -07004050 }
Joe Onorato561d3852010-11-20 18:09:34 -08004051 }
4052
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004053 private void bindLargeIcon(RemoteViews contentView) {
Selim Cinek279fa862016-06-14 10:57:25 -07004054 if (mN.mLargeIcon == null && mN.largeIcon != null) {
4055 mN.mLargeIcon = Icon.createWithBitmap(mN.largeIcon);
4056 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004057 if (mN.mLargeIcon != null) {
4058 contentView.setViewVisibility(R.id.right_icon, View.VISIBLE);
4059 contentView.setImageViewIcon(R.id.right_icon, mN.mLargeIcon);
4060 processLargeLegacyIcon(mN.mLargeIcon, contentView);
Adrian Roos2d5dbba2016-06-08 17:11:53 -07004061 int endMargin = R.dimen.notification_content_picture_margin;
4062 contentView.setViewLayoutMarginEndDimen(R.id.line1, endMargin);
4063 contentView.setViewLayoutMarginEndDimen(R.id.text, endMargin);
4064 contentView.setViewLayoutMarginEndDimen(R.id.progress, endMargin);
Selim Cinek5d6ef8d2017-05-18 22:16:00 -07004065 // Bind the reply action
4066 Action action = findReplyAction();
4067 contentView.setViewVisibility(R.id.reply_icon_action, action != null
4068 ? View.VISIBLE
4069 : View.GONE);
4070
4071 if (action != null) {
4072 int contrastColor = resolveContrastColor();
4073 contentView.setDrawableParameters(R.id.reply_icon_action,
4074 true /* targetBackground */,
4075 -1,
4076 contrastColor,
4077 PorterDuff.Mode.SRC_ATOP, -1);
4078 int iconColor = NotificationColorUtil.isColorLight(contrastColor)
4079 ? Color.BLACK : Color.WHITE;
4080 contentView.setDrawableParameters(R.id.reply_icon_action,
4081 false /* targetBackground */,
4082 -1,
4083 iconColor,
4084 PorterDuff.Mode.SRC_ATOP, -1);
4085 contentView.setOnClickPendingIntent(R.id.right_icon,
4086 action.actionIntent);
4087 contentView.setOnClickPendingIntent(R.id.reply_icon_action,
4088 action.actionIntent);
4089 contentView.setRemoteInputs(R.id.right_icon, action.mRemoteInputs);
4090 contentView.setRemoteInputs(R.id.reply_icon_action, action.mRemoteInputs);
4091
4092 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004093 }
Selim Cinek5d6ef8d2017-05-18 22:16:00 -07004094 contentView.setViewVisibility(R.id.right_icon_container, mN.mLargeIcon != null
4095 ? View.VISIBLE
4096 : View.GONE);
4097 }
4098
4099 private Action findReplyAction() {
4100 ArrayList<Action> actions = mActions;
4101 if (mOriginalActions != null) {
4102 actions = mOriginalActions;
4103 }
4104 int numActions = actions.size();
4105 for (int i = 0; i < numActions; i++) {
4106 Action action = actions.get(i);
4107 if (hasValidRemoteInput(action)) {
4108 return action;
4109 }
4110 }
4111 return null;
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004112 }
4113
Adrian Roos487374f2017-01-11 15:48:14 -08004114 private void bindNotificationHeader(RemoteViews contentView, boolean ambient) {
4115 bindSmallIcon(contentView, ambient);
4116 bindHeaderAppName(contentView, ambient);
4117 if (!ambient) {
4118 // Ambient view does not have these
4119 bindHeaderText(contentView);
4120 bindHeaderChronometerAndTime(contentView);
Adrian Roos487374f2017-01-11 15:48:14 -08004121 bindProfileBadge(contentView);
4122 }
Adrian Roosd83e9992017-03-16 15:17:57 -07004123 bindExpandButton(contentView);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004124 }
4125
4126 private void bindExpandButton(RemoteViews contentView) {
Selim Cinek99104832017-01-25 14:47:33 -08004127 int color = getPrimaryHighlightColor();
Selim Cinek7b9605b2017-01-19 17:36:00 -08004128 contentView.setDrawableParameters(R.id.expand_button, false, -1, color,
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004129 PorterDuff.Mode.SRC_ATOP, -1);
Selim Cinekea4bef72015-12-02 15:51:10 -08004130 contentView.setInt(R.id.notification_header, "setOriginalNotificationColor",
Selim Cinek7b9605b2017-01-19 17:36:00 -08004131 color);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004132 }
4133
Selim Cinek99104832017-01-25 14:47:33 -08004134 /**
4135 * @return the color that is used as the first primary highlight color. This is applied
4136 * in several places like the action buttons or the app name in the header.
4137 */
4138 private int getPrimaryHighlightColor() {
4139 return isColorized() ? getPrimaryTextColor() : resolveContrastColor();
4140 }
4141
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004142 private void bindHeaderChronometerAndTime(RemoteViews contentView) {
4143 if (showsTimeOrChronometer()) {
Selim Cinek29603462015-11-17 19:04:39 -08004144 contentView.setViewVisibility(R.id.time_divider, View.VISIBLE);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004145 setTextViewColorSecondary(contentView, R.id.time_divider);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004146 if (mN.extras.getBoolean(EXTRA_SHOW_CHRONOMETER)) {
4147 contentView.setViewVisibility(R.id.chronometer, View.VISIBLE);
4148 contentView.setLong(R.id.chronometer, "setBase",
4149 mN.when + (SystemClock.elapsedRealtime() - System.currentTimeMillis()));
4150 contentView.setBoolean(R.id.chronometer, "setStarted", true);
Adrian Roos96b7e202016-05-17 13:50:38 -07004151 boolean countsDown = mN.extras.getBoolean(EXTRA_CHRONOMETER_COUNT_DOWN);
Selim Cinekc3b752e2016-04-20 16:13:59 -07004152 contentView.setChronometerCountDown(R.id.chronometer, countsDown);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004153 setTextViewColorSecondary(contentView, R.id.chronometer);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004154 } else {
4155 contentView.setViewVisibility(R.id.time, View.VISIBLE);
4156 contentView.setLong(R.id.time, "setTime", mN.when);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004157 setTextViewColorSecondary(contentView, R.id.time);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004158 }
Selim Cinekb85f36fd2016-04-20 18:46:36 -07004159 } else {
4160 // We still want a time to be set but gone, such that we can show and hide it
4161 // on demand in case it's a child notification without anything in the header
4162 contentView.setLong(R.id.time, "setTime", mN.when != 0 ? mN.when : mN.creationTime);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004163 }
4164 }
4165
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07004166 private void bindHeaderText(RemoteViews contentView) {
4167 CharSequence headerText = mN.extras.getCharSequence(EXTRA_SUB_TEXT);
4168 if (headerText == null && mStyle != null && mStyle.mSummaryTextSet
Selim Cinek03d0d652015-11-13 13:18:09 -05004169 && mStyle.hasSummaryInHeader()) {
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07004170 headerText = mStyle.mSummaryText;
Selim Cinek03d0d652015-11-13 13:18:09 -05004171 }
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07004172 if (headerText == null
4173 && mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N
4174 && mN.extras.getCharSequence(EXTRA_INFO_TEXT) != null) {
4175 headerText = mN.extras.getCharSequence(EXTRA_INFO_TEXT);
4176 }
4177 if (headerText != null) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004178 // TODO: Remove the span entirely to only have the string with propper formating.
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07004179 contentView.setTextViewText(R.id.header_text, processLegacyText(headerText));
Selim Cinek7b9605b2017-01-19 17:36:00 -08004180 setTextViewColorSecondary(contentView, R.id.header_text);
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07004181 contentView.setViewVisibility(R.id.header_text, View.VISIBLE);
4182 contentView.setViewVisibility(R.id.header_text_divider, View.VISIBLE);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004183 setTextViewColorSecondary(contentView, R.id.header_text_divider);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004184 }
4185 }
4186
Adrian Rooseba05822016-04-22 17:09:27 -07004187 /**
4188 * @hide
4189 */
4190 public String loadHeaderAppName() {
Dan Sandler732bd6c2016-04-12 14:20:32 -04004191 CharSequence name = null;
4192 final PackageManager pm = mContext.getPackageManager();
4193 if (mN.extras.containsKey(EXTRA_SUBSTITUTE_APP_NAME)) {
4194 // only system packages which lump together a bunch of unrelated stuff
4195 // may substitute a different name to make the purpose of the
4196 // notification more clear. the correct package label should always
4197 // be accessible via SystemUI.
4198 final String pkg = mContext.getPackageName();
4199 final String subName = mN.extras.getString(EXTRA_SUBSTITUTE_APP_NAME);
4200 if (PackageManager.PERMISSION_GRANTED == pm.checkPermission(
4201 android.Manifest.permission.SUBSTITUTE_NOTIFICATION_APP_NAME, pkg)) {
4202 name = subName;
4203 } else {
4204 Log.w(TAG, "warning: pkg "
4205 + pkg + " attempting to substitute app name '" + subName
4206 + "' without holding perm "
4207 + android.Manifest.permission.SUBSTITUTE_NOTIFICATION_APP_NAME);
4208 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004209 }
Dan Sandler732bd6c2016-04-12 14:20:32 -04004210 if (TextUtils.isEmpty(name)) {
4211 name = pm.getApplicationLabel(mContext.getApplicationInfo());
4212 }
4213 if (TextUtils.isEmpty(name)) {
4214 // still nothing?
4215 return null;
4216 }
4217
4218 return String.valueOf(name);
4219 }
Adrian Roos487374f2017-01-11 15:48:14 -08004220 private void bindHeaderAppName(RemoteViews contentView, boolean ambient) {
Dan Sandler732bd6c2016-04-12 14:20:32 -04004221 contentView.setTextViewText(R.id.app_name_text, loadHeaderAppName());
Adrian Roos72171622017-01-27 10:32:06 -08004222 if (isColorized() && !ambient) {
Selim Cinek7b9605b2017-01-19 17:36:00 -08004223 setTextViewColorPrimary(contentView, R.id.app_name_text);
4224 } else {
4225 contentView.setTextColor(R.id.app_name_text,
4226 ambient ? resolveAmbientColor() : resolveContrastColor());
4227 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004228 }
4229
Adrian Roos487374f2017-01-11 15:48:14 -08004230 private void bindSmallIcon(RemoteViews contentView, boolean ambient) {
Selim Cinek279fa862016-06-14 10:57:25 -07004231 if (mN.mSmallIcon == null && mN.icon != 0) {
4232 mN.mSmallIcon = Icon.createWithResource(mContext, mN.icon);
4233 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004234 contentView.setImageViewIcon(R.id.icon, mN.mSmallIcon);
Adrian Roos9b45a152016-06-28 13:32:29 -07004235 contentView.setDrawableParameters(R.id.icon, false /* targetBackground */,
4236 -1 /* alpha */, -1 /* colorFilter */, null /* mode */, mN.iconLevel);
Adrian Roos487374f2017-01-11 15:48:14 -08004237 processSmallIconColor(mN.mSmallIcon, contentView, ambient);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004238 }
4239
Jorim Jaggi445d3c02014-08-19 22:33:42 +02004240 /**
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004241 * @return true if the built notification will show the time or the chronometer; false
4242 * otherwise
4243 */
4244 private boolean showsTimeOrChronometer() {
Selim Cinekc2c0b042016-05-18 17:13:46 -07004245 return mN.showsTime() || mN.showsChronometer();
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004246 }
4247
Christoph Studerfe718432014-09-01 18:21:18 +02004248 private void resetStandardTemplateWithActions(RemoteViews big) {
Adrian Roos4c1fcc82016-03-31 14:39:39 -07004249 // actions_container is only reset when there are no actions to avoid focus issues with
4250 // remote inputs.
Christoph Studerfe718432014-09-01 18:21:18 +02004251 big.setViewVisibility(R.id.actions, View.GONE);
Christoph Studerfe718432014-09-01 18:21:18 +02004252 big.removeAllViews(R.id.actions);
Adrian Roose458aa82015-12-08 16:17:19 -08004253
4254 big.setViewVisibility(R.id.notification_material_reply_container, View.GONE);
4255 big.setTextViewText(R.id.notification_material_reply_text_1, null);
4256
4257 big.setViewVisibility(R.id.notification_material_reply_text_2, View.GONE);
4258 big.setTextViewText(R.id.notification_material_reply_text_2, null);
4259 big.setViewVisibility(R.id.notification_material_reply_text_3, View.GONE);
4260 big.setTextViewText(R.id.notification_material_reply_text_3, null);
Adrian Roosf852a422016-06-03 13:33:43 -07004261
4262 big.setViewLayoutMarginBottomDimen(R.id.notification_action_list_margin_target, 0);
Christoph Studerfe718432014-09-01 18:21:18 +02004263 }
4264
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004265 private RemoteViews applyStandardTemplateWithActions(int layoutId) {
Adrian Roos70d7aa32017-01-11 15:39:06 -08004266 return applyStandardTemplateWithActions(layoutId, mParams.reset().fillTextsFrom(this));
Adrian Roos48d746a2016-04-12 14:57:28 -07004267 }
4268
Adrian Roos70d7aa32017-01-11 15:39:06 -08004269 private RemoteViews applyStandardTemplateWithActions(int layoutId,
4270 StandardTemplateParams p) {
4271 RemoteViews big = applyStandardTemplate(layoutId, p);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004272
Christoph Studerfe718432014-09-01 18:21:18 +02004273 resetStandardTemplateWithActions(big);
4274
Adrian Roose458aa82015-12-08 16:17:19 -08004275 boolean validRemoteInput = false;
4276
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004277 int N = mActions.size();
Adrian Roos487374f2017-01-11 15:48:14 -08004278 boolean emphazisedMode = mN.fullScreenIntent != null && !p.ambient;
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004279 big.setBoolean(R.id.actions, "setEmphasizedMode", emphazisedMode);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004280 if (N > 0) {
Adrian Roos7052de52016-03-03 15:53:34 -08004281 big.setViewVisibility(R.id.actions_container, View.VISIBLE);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004282 big.setViewVisibility(R.id.actions, View.VISIBLE);
Adrian Roos487374f2017-01-11 15:48:14 -08004283 if (p.ambient) {
4284 big.setInt(R.id.actions, "setBackgroundColor", Color.TRANSPARENT);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004285 } else if (isColorized()) {
4286 big.setInt(R.id.actions, "setBackgroundColor", getActionBarColor());
4287 } else {
4288 big.setInt(R.id.actions, "setBackgroundColor", mContext.getColor(
4289 R.color.notification_action_list));
Adrian Roos487374f2017-01-11 15:48:14 -08004290 }
Adrian Roosf852a422016-06-03 13:33:43 -07004291 big.setViewLayoutMarginBottomDimen(R.id.notification_action_list_margin_target,
4292 R.dimen.notification_action_list_height);
Daniel Sandler8680bf82012-05-15 16:52:52 -04004293 if (N>MAX_ACTION_BUTTONS) N=MAX_ACTION_BUTTONS;
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004294 for (int i=0; i<N; i++) {
Adrian Roose458aa82015-12-08 16:17:19 -08004295 Action action = mActions.get(i);
4296 validRemoteInput |= hasValidRemoteInput(action);
4297
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004298 final RemoteViews button = generateActionButton(action, emphazisedMode,
Adrian Roos487374f2017-01-11 15:48:14 -08004299 i % 2 != 0, p.ambient);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004300 big.addView(R.id.actions, button);
4301 }
Adrian Roos4c1fcc82016-03-31 14:39:39 -07004302 } else {
4303 big.setViewVisibility(R.id.actions_container, View.GONE);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004304 }
Adrian Roose458aa82015-12-08 16:17:19 -08004305
4306 CharSequence[] replyText = mN.extras.getCharSequenceArray(EXTRA_REMOTE_INPUT_HISTORY);
Adrian Roos487374f2017-01-11 15:48:14 -08004307 if (!p.ambient && validRemoteInput && replyText != null
Adrian Roose458aa82015-12-08 16:17:19 -08004308 && replyText.length > 0 && !TextUtils.isEmpty(replyText[0])) {
4309 big.setViewVisibility(R.id.notification_material_reply_container, View.VISIBLE);
4310 big.setTextViewText(R.id.notification_material_reply_text_1, replyText[0]);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004311 setTextViewColorSecondary(big, R.id.notification_material_reply_text_1);
Adrian Roose458aa82015-12-08 16:17:19 -08004312
4313 if (replyText.length > 1 && !TextUtils.isEmpty(replyText[1])) {
4314 big.setViewVisibility(R.id.notification_material_reply_text_2, View.VISIBLE);
4315 big.setTextViewText(R.id.notification_material_reply_text_2, replyText[1]);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004316 setTextViewColorSecondary(big, R.id.notification_material_reply_text_2);
Adrian Roose458aa82015-12-08 16:17:19 -08004317
4318 if (replyText.length > 2 && !TextUtils.isEmpty(replyText[2])) {
4319 big.setViewVisibility(
4320 R.id.notification_material_reply_text_3, View.VISIBLE);
4321 big.setTextViewText(R.id.notification_material_reply_text_3, replyText[2]);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004322 setTextViewColorSecondary(big, R.id.notification_material_reply_text_3);
Adrian Roose458aa82015-12-08 16:17:19 -08004323 }
4324 }
4325 }
4326
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004327 return big;
4328 }
4329
Adrian Roose458aa82015-12-08 16:17:19 -08004330 private boolean hasValidRemoteInput(Action action) {
4331 if (TextUtils.isEmpty(action.title) || action.actionIntent == null) {
4332 // Weird actions
4333 return false;
4334 }
4335
4336 RemoteInput[] remoteInputs = action.getRemoteInputs();
4337 if (remoteInputs == null) {
4338 return false;
4339 }
4340
4341 for (RemoteInput r : remoteInputs) {
4342 CharSequence[] choices = r.getChoices();
4343 if (r.getAllowFreeFormInput() || (choices != null && choices.length != 0)) {
4344 return true;
4345 }
4346 }
4347 return false;
4348 }
4349
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004350 /**
4351 * Construct a RemoteViews for the final 1U notification layout. In order:
4352 * 1. Custom contentView from the caller
4353 * 2. Style's proposed content view
4354 * 3. Standard template view
4355 */
Julia Reynolds3b848122016-02-26 10:45:32 -05004356 public RemoteViews createContentView() {
Selim Cinek7d1009b2017-01-25 15:28:28 -08004357 return createContentView(false /* increasedheight */ );
4358 }
4359
4360 /**
4361 * Construct a RemoteViews for the smaller content view.
4362 *
4363 * @param increasedHeight true if this layout be created with an increased height. Some
4364 * styles may support showing more then just that basic 1U size
4365 * and the system may decide to render important notifications
4366 * slightly bigger even when collapsed.
4367 *
4368 * @hide
4369 */
4370 public RemoteViews createContentView(boolean increasedHeight) {
Selim Cineka7679b62017-05-10 16:33:25 -07004371 if (mN.contentView != null && useExistingRemoteView()) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004372 return mN.contentView;
4373 } else if (mStyle != null) {
Selim Cinek7d1009b2017-01-25 15:28:28 -08004374 final RemoteViews styleView = mStyle.makeContentView(increasedHeight);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004375 if (styleView != null) {
4376 return styleView;
4377 }
Joe Onorato46439ce2010-11-19 13:56:21 -08004378 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004379 return applyStandardTemplate(getBaseLayoutResource());
Joe Onorato46439ce2010-11-19 13:56:21 -08004380 }
4381
Selim Cineka7679b62017-05-10 16:33:25 -07004382 private boolean useExistingRemoteView() {
4383 return mStyle == null || (!mStyle.displayCustomViewInline()
4384 && !mRebuildStyledRemoteViews);
4385 }
4386
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004387 /**
4388 * Construct a RemoteViews for the final big notification layout.
4389 */
Julia Reynolds3b848122016-02-26 10:45:32 -05004390 public RemoteViews createBigContentView() {
Selim Cinek850a8542015-11-11 11:48:36 -05004391 RemoteViews result = null;
Selim Cineka7679b62017-05-10 16:33:25 -07004392 if (mN.bigContentView != null && useExistingRemoteView()) {
Julia Reynolds089e3e42015-11-18 09:59:57 -05004393 return mN.bigContentView;
4394 } else if (mStyle != null) {
Selim Cinek850a8542015-11-11 11:48:36 -05004395 result = mStyle.makeBigContentView();
Selim Cinek90dcf6d2015-11-18 20:24:13 -08004396 hideLine1Text(result);
Selim Cinekcc10bfb2016-02-10 16:24:21 -08004397 } else if (mActions.size() != 0) {
4398 result = applyStandardTemplateWithActions(getBigBaseLayoutResource());
Selim Cinek850a8542015-11-11 11:48:36 -05004399 }
Selim Cinek6743c0b2017-01-18 18:24:01 -08004400 makeHeaderExpanded(result);
Selim Cinek850a8542015-11-11 11:48:36 -05004401 return result;
4402 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004403
Selim Cinekeaa29ca2015-11-23 13:51:13 -08004404 /**
Selim Cinek414ad332017-02-24 19:06:12 -08004405 * Construct a RemoteViews for the final notification header only. This will not be
4406 * colorized.
Selim Cinekeaa29ca2015-11-23 13:51:13 -08004407 *
Adrian Roos6f6e1592017-05-02 16:22:53 -07004408 * @param ambient if true, generate the header for the ambient display layout.
Selim Cinekeaa29ca2015-11-23 13:51:13 -08004409 * @hide
4410 */
Adrian Roos6f6e1592017-05-02 16:22:53 -07004411 public RemoteViews makeNotificationHeader(boolean ambient) {
Selim Cinek414ad332017-02-24 19:06:12 -08004412 Boolean colorized = (Boolean) mN.extras.get(EXTRA_COLORIZED);
4413 mN.extras.putBoolean(EXTRA_COLORIZED, false);
Selim Cinekeaa29ca2015-11-23 13:51:13 -08004414 RemoteViews header = new BuilderRemoteViews(mContext.getApplicationInfo(),
Adrian Roos6f6e1592017-05-02 16:22:53 -07004415 ambient ? R.layout.notification_template_ambient_header
4416 : R.layout.notification_template_header);
Selim Cinekeaa29ca2015-11-23 13:51:13 -08004417 resetNotificationHeader(header);
Adrian Roos6f6e1592017-05-02 16:22:53 -07004418 bindNotificationHeader(header, ambient);
Selim Cinek414ad332017-02-24 19:06:12 -08004419 if (colorized != null) {
4420 mN.extras.putBoolean(EXTRA_COLORIZED, colorized);
4421 } else {
4422 mN.extras.remove(EXTRA_COLORIZED);
4423 }
Selim Cinekeaa29ca2015-11-23 13:51:13 -08004424 return header;
4425 }
4426
Adrian Roos487374f2017-01-11 15:48:14 -08004427 /**
4428 * Construct a RemoteViews for the ambient version of the notification.
4429 *
4430 * @hide
4431 */
4432 public RemoteViews makeAmbientNotification() {
4433 RemoteViews ambient = applyStandardTemplateWithActions(
4434 R.layout.notification_template_material_ambient,
Adrian Roos0bc3f6a2017-03-06 11:54:05 -08004435 mParams.reset().ambient(true).fillTextsFrom(this).hasProgress(false));
Adrian Roos487374f2017-01-11 15:48:14 -08004436 return ambient;
4437 }
4438
Selim Cinek29603462015-11-17 19:04:39 -08004439 private void hideLine1Text(RemoteViews result) {
Selim Cinekcc10bfb2016-02-10 16:24:21 -08004440 if (result != null) {
4441 result.setViewVisibility(R.id.text_line_1, View.GONE);
4442 }
Selim Cinek29603462015-11-17 19:04:39 -08004443 }
4444
Selim Cinek6743c0b2017-01-18 18:24:01 -08004445 /**
4446 * Adapt the Notification header if this view is used as an expanded view.
4447 *
4448 * @hide
4449 */
4450 public static void makeHeaderExpanded(RemoteViews result) {
Selim Cinekcc10bfb2016-02-10 16:24:21 -08004451 if (result != null) {
4452 result.setBoolean(R.id.notification_header, "setExpanded", true);
4453 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004454 }
4455
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004456 /**
4457 * Construct a RemoteViews for the final heads-up notification layout.
Selim Cinek87ed69b2017-02-09 15:59:43 -08004458 *
4459 * @param increasedHeight true if this layout be created with an increased height. Some
4460 * styles may support showing more then just that basic 1U size
4461 * and the system may decide to render important notifications
4462 * slightly bigger even when collapsed.
4463 *
4464 * @hide
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004465 */
Selim Cinek87ed69b2017-02-09 15:59:43 -08004466 public RemoteViews createHeadsUpContentView(boolean increasedHeight) {
Selim Cineka7679b62017-05-10 16:33:25 -07004467 if (mN.headsUpContentView != null && useExistingRemoteView()) {
Julia Reynolds089e3e42015-11-18 09:59:57 -05004468 return mN.headsUpContentView;
4469 } else if (mStyle != null) {
Selim Cinek87ed69b2017-02-09 15:59:43 -08004470 final RemoteViews styleView = mStyle.makeHeadsUpContentView(increasedHeight);
4471 if (styleView != null) {
4472 return styleView;
4473 }
Julia Reynolds089e3e42015-11-18 09:59:57 -05004474 } else if (mActions.size() == 0) {
4475 return null;
4476 }
4477
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004478 return applyStandardTemplateWithActions(getBigBaseLayoutResource());
Chris Wren8fd39ec2014-02-27 17:43:26 -05004479 }
4480
Selim Cinek624c02db2015-12-14 21:00:02 -08004481 /**
Selim Cinek87ed69b2017-02-09 15:59:43 -08004482 * Construct a RemoteViews for the final heads-up notification layout.
4483 */
4484 public RemoteViews createHeadsUpContentView() {
4485 return createHeadsUpContentView(false /* useIncreasedHeight */);
4486 }
4487
4488 /**
Selim Cinek624c02db2015-12-14 21:00:02 -08004489 * Construct a RemoteViews for the display in public contexts like on the lockscreen.
4490 *
4491 * @hide
4492 */
4493 public RemoteViews makePublicContentView() {
Adrian Roos1a1ecfc2017-04-17 11:17:59 -07004494 return makePublicView(false /* ambient */);
4495 }
4496
4497 /**
4498 * Construct a RemoteViews for the display in public contexts like on the lockscreen.
4499 *
4500 * @hide
4501 */
4502 public RemoteViews makePublicAmbientNotification() {
4503 return makePublicView(true /* ambient */);
4504 }
4505
4506 private RemoteViews makePublicView(boolean ambient) {
Selim Cinek624c02db2015-12-14 21:00:02 -08004507 if (mN.publicVersion != null) {
4508 final Builder builder = recoverBuilder(mContext, mN.publicVersion);
Adrian Roos1a1ecfc2017-04-17 11:17:59 -07004509 return ambient ? builder.makeAmbientNotification() : builder.createContentView();
Selim Cinek624c02db2015-12-14 21:00:02 -08004510 }
4511 Bundle savedBundle = mN.extras;
4512 Style style = mStyle;
4513 mStyle = null;
4514 Icon largeIcon = mN.mLargeIcon;
4515 mN.mLargeIcon = null;
Selim Cinek279fa862016-06-14 10:57:25 -07004516 Bitmap largeIconLegacy = mN.largeIcon;
4517 mN.largeIcon = null;
Adrian Roosb19b06b2017-05-02 18:54:40 -07004518 ArrayList<Action> actions = mActions;
4519 mActions = new ArrayList<>();
Selim Cinek624c02db2015-12-14 21:00:02 -08004520 Bundle publicExtras = new Bundle();
4521 publicExtras.putBoolean(EXTRA_SHOW_WHEN,
4522 savedBundle.getBoolean(EXTRA_SHOW_WHEN));
4523 publicExtras.putBoolean(EXTRA_SHOW_CHRONOMETER,
4524 savedBundle.getBoolean(EXTRA_SHOW_CHRONOMETER));
Adrian Roos96b7e202016-05-17 13:50:38 -07004525 publicExtras.putBoolean(EXTRA_CHRONOMETER_COUNT_DOWN,
4526 savedBundle.getBoolean(EXTRA_CHRONOMETER_COUNT_DOWN));
Selim Cinek624c02db2015-12-14 21:00:02 -08004527 publicExtras.putCharSequence(EXTRA_TITLE,
Adrian Roos1a1ecfc2017-04-17 11:17:59 -07004528 mContext.getString(com.android.internal.R.string.notification_hidden_text));
Selim Cinek624c02db2015-12-14 21:00:02 -08004529 mN.extras = publicExtras;
Adrian Roos1a1ecfc2017-04-17 11:17:59 -07004530 final RemoteViews view = ambient ? makeAmbientNotification()
4531 : applyStandardTemplate(getBaseLayoutResource());
Selim Cinek624c02db2015-12-14 21:00:02 -08004532 mN.extras = savedBundle;
4533 mN.mLargeIcon = largeIcon;
Selim Cinek279fa862016-06-14 10:57:25 -07004534 mN.largeIcon = largeIconLegacy;
Adrian Roosb19b06b2017-05-02 18:54:40 -07004535 mActions = actions;
Selim Cinek624c02db2015-12-14 21:00:02 -08004536 mStyle = style;
Adrian Roos1a1ecfc2017-04-17 11:17:59 -07004537 return view;
Selim Cinek624c02db2015-12-14 21:00:02 -08004538 }
4539
Selim Cinek6743c0b2017-01-18 18:24:01 -08004540 /**
4541 * Construct a content view for the display when low - priority
4542 *
4543 * @param useRegularSubtext uses the normal subtext set if there is one available. Otherwise
4544 * a new subtext is created consisting of the content of the
4545 * notification.
4546 * @hide
4547 */
4548 public RemoteViews makeLowPriorityContentView(boolean useRegularSubtext) {
4549 int color = mN.color;
4550 mN.color = COLOR_DEFAULT;
4551 CharSequence summary = mN.extras.getCharSequence(EXTRA_SUB_TEXT);
4552 if (!useRegularSubtext || TextUtils.isEmpty(summary)) {
4553 CharSequence newSummary = createSummaryText();
4554 if (!TextUtils.isEmpty(newSummary)) {
4555 mN.extras.putCharSequence(EXTRA_SUB_TEXT, newSummary);
4556 }
4557 }
Selim Cinek875ba9b2017-02-13 16:20:17 -08004558
Adrian Roos6f6e1592017-05-02 16:22:53 -07004559 RemoteViews header = makeNotificationHeader(false /* ambient */);
Selim Cinek1b554392017-02-28 17:22:49 -08004560 header.setBoolean(R.id.notification_header, "setAcceptAllTouches", true);
Selim Cinek6743c0b2017-01-18 18:24:01 -08004561 if (summary != null) {
4562 mN.extras.putCharSequence(EXTRA_SUB_TEXT, summary);
4563 } else {
4564 mN.extras.remove(EXTRA_SUB_TEXT);
4565 }
4566 mN.color = color;
4567 return header;
4568 }
Selim Cinek624c02db2015-12-14 21:00:02 -08004569
Selim Cinek6743c0b2017-01-18 18:24:01 -08004570 private CharSequence createSummaryText() {
4571 CharSequence titleText = mN.extras.getCharSequence(Notification.EXTRA_TITLE);
4572 if (USE_ONLY_TITLE_IN_LOW_PRIORITY_SUMMARY) {
4573 return titleText;
4574 }
4575 SpannableStringBuilder summary = new SpannableStringBuilder();
4576 if (titleText == null) {
4577 titleText = mN.extras.getCharSequence(Notification.EXTRA_TITLE_BIG);
4578 }
4579 BidiFormatter bidi = BidiFormatter.getInstance();
4580 if (titleText != null) {
4581 summary.append(bidi.unicodeWrap(titleText));
4582 }
4583 CharSequence contentText = mN.extras.getCharSequence(Notification.EXTRA_TEXT);
4584 if (titleText != null && contentText != null) {
4585 summary.append(bidi.unicodeWrap(mContext.getText(
4586 R.string.notification_header_divider_symbol_with_spaces)));
4587 }
4588 if (contentText != null) {
4589 summary.append(bidi.unicodeWrap(contentText));
4590 }
4591 return summary;
4592 }
Chris Wren8fd39ec2014-02-27 17:43:26 -05004593
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004594 private RemoteViews generateActionButton(Action action, boolean emphazisedMode,
Adrian Roos487374f2017-01-11 15:48:14 -08004595 boolean oddAction, boolean ambient) {
Daniel Sandler8680bf82012-05-15 16:52:52 -04004596 final boolean tombstone = (action.actionIntent == null);
Selim Cinekf33b1112015-07-15 17:45:11 -07004597 RemoteViews button = new BuilderRemoteViews(mContext.getApplicationInfo(),
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004598 emphazisedMode ? getEmphasizedActionLayoutResource()
4599 : tombstone ? getActionTombstoneLayoutResource()
4600 : getActionLayoutResource());
Daniel Sandler8680bf82012-05-15 16:52:52 -04004601 if (!tombstone) {
Daniel Sandlere5518842012-05-10 16:20:40 -04004602 button.setOnClickPendingIntent(R.id.action0, action.actionIntent);
Daniel Sandlere5518842012-05-10 16:20:40 -04004603 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004604 button.setContentDescription(R.id.action0, action.title);
Adrian Roosfe84e1f2015-11-04 15:55:39 -08004605 if (action.mRemoteInputs != null) {
4606 button.setRemoteInputs(R.id.action0, action.mRemoteInputs);
4607 }
Selim Cinek7b9605b2017-01-19 17:36:00 -08004608 // TODO: handle emphasized mode / actions right
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004609 if (emphazisedMode) {
Selim Cinek981962e2016-07-20 20:41:58 -07004610 // change the background bgColor
Selim Cinek622c64a2017-04-17 17:10:05 -07004611 int bgColor;
4612 if (isColorized()) {
4613 bgColor = oddAction ? getActionBarColor() : getActionBarColorDeEmphasized();
4614 } else {
4615 bgColor = mContext.getColor(oddAction ? R.color.notification_action_list
4616 : R.color.notification_action_list_dark);
4617 }
Selim Cinek981962e2016-07-20 20:41:58 -07004618 button.setDrawableParameters(R.id.button_holder, true, -1, bgColor,
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004619 PorterDuff.Mode.SRC_ATOP, -1);
Selim Cinek981962e2016-07-20 20:41:58 -07004620 CharSequence title = action.title;
4621 ColorStateList[] outResultColor = null;
4622 if (isLegacy()) {
4623 title = clearColorSpans(title);
4624 } else {
4625 outResultColor = new ColorStateList[1];
4626 title = ensureColorSpanContrast(title, bgColor, outResultColor);
4627 }
4628 button.setTextViewText(R.id.action0, title);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004629 setTextViewColorPrimary(button, R.id.action0);
Selim Cinek981962e2016-07-20 20:41:58 -07004630 if (outResultColor != null && outResultColor[0] != null) {
4631 // We need to set the text color as well since changing a text to uppercase
4632 // clears its spans.
4633 button.setTextColor(R.id.action0, outResultColor[0]);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004634 } else if (mN.color != COLOR_DEFAULT && !isColorized()) {
Selim Cinek981962e2016-07-20 20:41:58 -07004635 button.setTextColor(R.id.action0,resolveContrastColor());
4636 }
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004637 } else {
Selim Cinek981962e2016-07-20 20:41:58 -07004638 button.setTextViewText(R.id.action0, processLegacyText(action.title));
Adrian Roos72171622017-01-27 10:32:06 -08004639 if (isColorized() && !ambient) {
Selim Cinek7b9605b2017-01-19 17:36:00 -08004640 setTextViewColorPrimary(button, R.id.action0);
4641 } else if (mN.color != COLOR_DEFAULT) {
Adrian Roos487374f2017-01-11 15:48:14 -08004642 button.setTextColor(R.id.action0,
4643 ambient ? resolveAmbientColor() : resolveContrastColor());
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004644 }
Adrian Roosfe84e1f2015-11-04 15:55:39 -08004645 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004646 return button;
4647 }
4648
Joe Onoratocb109a02011-01-18 17:57:41 -08004649 /**
Selim Cinek981962e2016-07-20 20:41:58 -07004650 * Clears all color spans of a text
4651 * @param charSequence the input text
4652 * @return the same text but without color spans
4653 */
4654 private CharSequence clearColorSpans(CharSequence charSequence) {
4655 if (charSequence instanceof Spanned) {
4656 Spanned ss = (Spanned) charSequence;
4657 Object[] spans = ss.getSpans(0, ss.length(), Object.class);
4658 SpannableStringBuilder builder = new SpannableStringBuilder(ss.toString());
4659 for (Object span : spans) {
4660 Object resultSpan = span;
4661 if (resultSpan instanceof CharacterStyle) {
4662 resultSpan = ((CharacterStyle) span).getUnderlying();
4663 }
4664 if (resultSpan instanceof TextAppearanceSpan) {
4665 TextAppearanceSpan originalSpan = (TextAppearanceSpan) resultSpan;
4666 if (originalSpan.getTextColor() != null) {
4667 resultSpan = new TextAppearanceSpan(
4668 originalSpan.getFamily(),
4669 originalSpan.getTextStyle(),
4670 originalSpan.getTextSize(),
4671 null,
4672 originalSpan.getLinkTextColor());
4673 }
4674 } else if (resultSpan instanceof ForegroundColorSpan
4675 || (resultSpan instanceof BackgroundColorSpan)) {
4676 continue;
4677 } else {
4678 resultSpan = span;
4679 }
4680 builder.setSpan(resultSpan, ss.getSpanStart(span), ss.getSpanEnd(span),
4681 ss.getSpanFlags(span));
4682 }
4683 return builder;
4684 }
4685 return charSequence;
4686 }
4687
4688 /**
4689 * Ensures contrast on color spans against a background color. also returns the color of the
4690 * text if a span was found that spans over the whole text.
4691 *
4692 * @param charSequence the charSequence on which the spans are
4693 * @param background the background color to ensure the contrast against
4694 * @param outResultColor an array in which a color will be returned as the first element if
4695 * there exists a full length color span.
4696 * @return the contrasted charSequence
4697 */
4698 private CharSequence ensureColorSpanContrast(CharSequence charSequence, int background,
4699 ColorStateList[] outResultColor) {
4700 if (charSequence instanceof Spanned) {
4701 Spanned ss = (Spanned) charSequence;
4702 Object[] spans = ss.getSpans(0, ss.length(), Object.class);
4703 SpannableStringBuilder builder = new SpannableStringBuilder(ss.toString());
4704 for (Object span : spans) {
4705 Object resultSpan = span;
4706 int spanStart = ss.getSpanStart(span);
4707 int spanEnd = ss.getSpanEnd(span);
4708 boolean fullLength = (spanEnd - spanStart) == charSequence.length();
4709 if (resultSpan instanceof CharacterStyle) {
4710 resultSpan = ((CharacterStyle) span).getUnderlying();
4711 }
4712 if (resultSpan instanceof TextAppearanceSpan) {
4713 TextAppearanceSpan originalSpan = (TextAppearanceSpan) resultSpan;
4714 ColorStateList textColor = originalSpan.getTextColor();
4715 if (textColor != null) {
4716 int[] colors = textColor.getColors();
4717 int[] newColors = new int[colors.length];
4718 for (int i = 0; i < newColors.length; i++) {
4719 newColors[i] = NotificationColorUtil.ensureLargeTextContrast(
4720 colors[i], background);
4721 }
4722 textColor = new ColorStateList(textColor.getStates().clone(),
4723 newColors);
4724 resultSpan = new TextAppearanceSpan(
4725 originalSpan.getFamily(),
4726 originalSpan.getTextStyle(),
4727 originalSpan.getTextSize(),
4728 textColor,
4729 originalSpan.getLinkTextColor());
4730 if (fullLength) {
4731 outResultColor[0] = new ColorStateList(
4732 textColor.getStates().clone(), newColors);
4733 }
4734 }
4735 } else if (resultSpan instanceof ForegroundColorSpan) {
4736 ForegroundColorSpan originalSpan = (ForegroundColorSpan) resultSpan;
4737 int foregroundColor = originalSpan.getForegroundColor();
4738 foregroundColor = NotificationColorUtil.ensureLargeTextContrast(
4739 foregroundColor, background);
4740 resultSpan = new ForegroundColorSpan(foregroundColor);
4741 if (fullLength) {
4742 outResultColor[0] = ColorStateList.valueOf(foregroundColor);
4743 }
4744 } else {
4745 resultSpan = span;
4746 }
4747
4748 builder.setSpan(resultSpan, spanStart, spanEnd, ss.getSpanFlags(span));
4749 }
4750 return builder;
4751 }
4752 return charSequence;
4753 }
4754
4755 /**
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004756 * @return Whether we are currently building a notification from a legacy (an app that
Alan Viverette3cb07a462014-06-06 14:19:53 -07004757 * doesn't create material notifications by itself) app.
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004758 */
4759 private boolean isLegacy() {
Selim Cinek7b9605b2017-01-19 17:36:00 -08004760 if (!mIsLegacyInitialized) {
4761 mIsLegacy = mContext.getApplicationInfo().targetSdkVersion
4762 < Build.VERSION_CODES.LOLLIPOP;
4763 mIsLegacyInitialized = true;
4764 }
4765 return mIsLegacy;
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004766 }
4767
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004768 private CharSequence processLegacyText(CharSequence charSequence) {
Adrian Roos0bc3f6a2017-03-06 11:54:05 -08004769 return processLegacyText(charSequence, false /* ambient */);
4770 }
4771
4772 private CharSequence processLegacyText(CharSequence charSequence, boolean ambient) {
4773 boolean isAlreadyLightText = isLegacy() || textColorsNeedInversion();
4774 boolean wantLightText = ambient;
4775 if (isAlreadyLightText != wantLightText) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004776 return getColorUtil().invertCharSequenceColors(charSequence);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004777 } else {
4778 return charSequence;
4779 }
4780 }
4781
Dan Sandler26e81cf2014-05-06 10:01:27 -04004782 /**
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004783 * Apply any necessariy colors to the small icon
Dan Sandler26e81cf2014-05-06 10:01:27 -04004784 */
Adrian Roos487374f2017-01-11 15:48:14 -08004785 private void processSmallIconColor(Icon smallIcon, RemoteViews contentView,
4786 boolean ambient) {
Selim Cinekea4bef72015-12-02 15:51:10 -08004787 boolean colorable = !isLegacy() || getColorUtil().isGrayscaleIcon(mContext, smallIcon);
Selim Cinek99104832017-01-25 14:47:33 -08004788 int color = ambient ? resolveAmbientColor() : getPrimaryHighlightColor();
Selim Cinekea4bef72015-12-02 15:51:10 -08004789 if (colorable) {
Adrian Roos487374f2017-01-11 15:48:14 -08004790 contentView.setDrawableParameters(R.id.icon, false, -1, color,
Jorim Jaggi92df1f22014-12-16 19:44:41 +01004791 PorterDuff.Mode.SRC_ATOP, -1);
Selim Cinekea4bef72015-12-02 15:51:10 -08004792
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004793 }
Selim Cinekea4bef72015-12-02 15:51:10 -08004794 contentView.setInt(R.id.notification_header, "setOriginalIconColor",
Adrian Roos487374f2017-01-11 15:48:14 -08004795 colorable ? color : NotificationHeaderView.NO_COLOR);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004796 }
4797
Dan Sandler26e81cf2014-05-06 10:01:27 -04004798 /**
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004799 * Make the largeIcon dark if it's a fake smallIcon (that is,
Dan Sandler26e81cf2014-05-06 10:01:27 -04004800 * if it's grayscale).
4801 */
4802 // TODO: also check bounds, transparency, that sort of thing.
Dan Sandlerd63f9322015-05-06 15:18:49 -04004803 private void processLargeLegacyIcon(Icon largeIcon, RemoteViews contentView) {
4804 if (largeIcon != null && isLegacy()
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004805 && getColorUtil().isGrayscaleIcon(mContext, largeIcon)) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004806 // resolve color will fall back to the default when legacy
Adrian Roos4ff3b122016-02-01 12:26:13 -08004807 contentView.setDrawableParameters(R.id.icon, false, -1, resolveContrastColor(),
Dan Sandler190d58d2014-05-15 09:33:39 -04004808 PorterDuff.Mode.SRC_ATOP, -1);
Jorim Jaggi92df1f22014-12-16 19:44:41 +01004809 }
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004810 }
4811
Julia Reynolds10ee1fc2015-11-09 11:04:55 -05004812 private void sanitizeColor() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004813 if (mN.color != COLOR_DEFAULT) {
4814 mN.color |= 0xFF000000; // no alpha for custom colors
Jorim Jaggi74419312014-06-10 20:57:21 +02004815 }
Jorim Jaggi74419312014-06-10 20:57:21 +02004816 }
4817
Adrian Roos4ff3b122016-02-01 12:26:13 -08004818 int resolveContrastColor() {
4819 if (mCachedContrastColorIsFor == mN.color && mCachedContrastColor != COLOR_INVALID) {
4820 return mCachedContrastColor;
Dan Sandler26e81cf2014-05-06 10:01:27 -04004821 }
Adrian Roos4ff3b122016-02-01 12:26:13 -08004822
Selim Cinekac5f0272017-05-02 16:05:41 -07004823 int color;
4824 int background = mBackgroundColorHint;
4825 if (mBackgroundColorHint == COLOR_INVALID) {
4826 background = mContext.getColor(
4827 com.android.internal.R.color.notification_material_background_color);
4828 }
4829 if (mN.color == COLOR_DEFAULT) {
4830 ensureColors();
4831 color = mSecondaryTextColor;
4832 } else {
4833 color = NotificationColorUtil.resolveContrastColor(mContext, mN.color,
4834 background);
4835 }
4836 if (Color.alpha(color) < 255) {
4837 // alpha doesn't go well for color filters, so let's blend it manually
4838 color = NotificationColorUtil.compositeColors(color, background);
4839 }
Adrian Roos4ff3b122016-02-01 12:26:13 -08004840 mCachedContrastColorIsFor = mN.color;
Selim Cinekac5f0272017-05-02 16:05:41 -07004841 return mCachedContrastColor = color;
Dan Sandler26e81cf2014-05-06 10:01:27 -04004842 }
4843
Adrian Roos487374f2017-01-11 15:48:14 -08004844 int resolveAmbientColor() {
4845 if (mCachedAmbientColorIsFor == mN.color && mCachedAmbientColorIsFor != COLOR_INVALID) {
4846 return mCachedAmbientColor;
4847 }
4848 final int contrasted = NotificationColorUtil.resolveAmbientColor(mContext, mN.color);
4849
4850 mCachedAmbientColorIsFor = mN.color;
4851 return mCachedAmbientColor = contrasted;
4852 }
4853
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004854 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004855 * Apply the unstyled operations and return a new {@link Notification} object.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04004856 * @hide
Joe Onoratocb109a02011-01-18 17:57:41 -08004857 */
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04004858 public Notification buildUnstyled() {
Daniel Sandlera0a938c2012-03-15 08:42:37 -04004859 if (mActions.size() > 0) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004860 mN.actions = new Action[mActions.size()];
4861 mActions.toArray(mN.actions);
Daniel Sandlera0a938c2012-03-15 08:42:37 -04004862 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004863 if (!mPersonList.isEmpty()) {
4864 mN.extras.putStringArray(EXTRA_PEOPLE,
4865 mPersonList.toArray(new String[mPersonList.size()]));
Dan Sandler0bf2ed82013-12-21 23:33:41 -06004866 }
Selim Cinek247fa012016-02-18 09:50:48 -08004867 if (mN.bigContentView != null || mN.contentView != null
4868 || mN.headsUpContentView != null) {
4869 mN.extras.putBoolean(EXTRA_CONTAINS_CUSTOM_VIEW, true);
4870 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004871 return mN;
Joe Onorato46439ce2010-11-19 13:56:21 -08004872 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004873
Julia Reynolds3b848122016-02-26 10:45:32 -05004874 /**
4875 * Creates a Builder from an existing notification so further changes can be made.
4876 * @param context The context for your application / activity.
4877 * @param n The notification to create a Builder from.
4878 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004879 public static Notification.Builder recoverBuilder(Context context, Notification n) {
Christoph Studer4600f9b2014-07-22 22:44:43 +02004880 // Re-create notification context so we can access app resources.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004881 ApplicationInfo applicationInfo = n.extras.getParcelable(
4882 EXTRA_BUILDER_APPLICATION_INFO);
Christoph Studer4600f9b2014-07-22 22:44:43 +02004883 Context builderContext;
Julia Reynoldsda303542015-11-23 14:00:20 -05004884 if (applicationInfo != null) {
4885 try {
4886 builderContext = context.createApplicationContext(applicationInfo,
4887 Context.CONTEXT_RESTRICTED);
4888 } catch (NameNotFoundException e) {
4889 Log.e(TAG, "ApplicationInfo " + applicationInfo + " not found");
4890 builderContext = context; // try with our context
4891 }
4892 } else {
4893 builderContext = context; // try with given context
Christoph Studer4600f9b2014-07-22 22:44:43 +02004894 }
4895
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004896 return new Builder(builderContext, n);
Christoph Studer4600f9b2014-07-22 22:44:43 +02004897 }
4898
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004899 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004900 * @deprecated Use {@link #build()} instead.
4901 */
4902 @Deprecated
4903 public Notification getNotification() {
4904 return build();
4905 }
4906
4907 /**
4908 * Combine all of the options that have been set and return a new {@link Notification}
4909 * object.
4910 */
4911 public Notification build() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004912 // first, add any extras from the calling code
4913 if (mUserExtras != null) {
4914 mN.extras = getAllExtras();
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07004915 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004916
Selim Cinekb85f36fd2016-04-20 18:46:36 -07004917 mN.creationTime = System.currentTimeMillis();
4918
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004919 // lazy stuff from mContext; see comment in Builder(Context, Notification)
Julia Reynoldsda303542015-11-23 14:00:20 -05004920 Notification.addFieldsFromContext(mContext, mN);
Christoph Studer943aa672014-08-03 20:31:16 +02004921
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004922 buildUnstyled();
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004923
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004924 if (mStyle != null) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004925 mStyle.buildStyled(mN);
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004926 }
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004927
Adrian Roos5081c0d2016-02-26 16:04:19 -08004928 if (mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N
Selim Cineka7679b62017-05-10 16:33:25 -07004929 && (useExistingRemoteView())) {
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004930 if (mN.contentView == null) {
Julia Reynolds3b848122016-02-26 10:45:32 -05004931 mN.contentView = createContentView();
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004932 mN.extras.putInt(EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT,
4933 mN.contentView.getSequenceNumber());
4934 }
4935 if (mN.bigContentView == null) {
Julia Reynolds3b848122016-02-26 10:45:32 -05004936 mN.bigContentView = createBigContentView();
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004937 if (mN.bigContentView != null) {
4938 mN.extras.putInt(EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT,
4939 mN.bigContentView.getSequenceNumber());
4940 }
4941 }
4942 if (mN.headsUpContentView == null) {
Julia Reynolds3b848122016-02-26 10:45:32 -05004943 mN.headsUpContentView = createHeadsUpContentView();
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004944 if (mN.headsUpContentView != null) {
4945 mN.extras.putInt(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT,
4946 mN.headsUpContentView.getSequenceNumber());
4947 }
4948 }
4949 }
4950
Julia Reynolds4c0c2022016-02-02 15:11:59 -05004951 if ((mN.defaults & DEFAULT_LIGHTS) != 0) {
4952 mN.flags |= FLAG_SHOW_LIGHTS;
4953 }
4954
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004955 return mN;
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004956 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05004957
4958 /**
4959 * Apply this Builder to an existing {@link Notification} object.
4960 *
4961 * @hide
4962 */
4963 public Notification buildInto(Notification n) {
Daniel Sandler1a497d32013-04-18 14:52:45 -04004964 build().cloneInto(n, true);
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05004965 return n;
4966 }
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004967
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004968 /**
Adrian Roos184bfe022016-03-03 13:41:44 -08004969 * Removes RemoteViews that were created for compatibility from {@param n}, if they did not
4970 * change.
4971 *
4972 * @return {@param n}, if no stripping is needed, otherwise a stripped clone of {@param n}.
4973 *
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004974 * @hide
4975 */
Adrian Roos184bfe022016-03-03 13:41:44 -08004976 public static Notification maybeCloneStrippedForDelivery(Notification n) {
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004977 String templateClass = n.extras.getString(EXTRA_TEMPLATE);
Adrian Roos184bfe022016-03-03 13:41:44 -08004978
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004979 // Only strip views for known Styles because we won't know how to
4980 // re-create them otherwise.
Adrian Roos184bfe022016-03-03 13:41:44 -08004981 if (!TextUtils.isEmpty(templateClass)
4982 && getNotificationStyleClass(templateClass) == null) {
4983 return n;
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004984 }
Adrian Roos184bfe022016-03-03 13:41:44 -08004985
4986 // Only strip unmodified BuilderRemoteViews.
4987 boolean stripContentView = n.contentView instanceof BuilderRemoteViews &&
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004988 n.extras.getInt(EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT, -1) ==
Adrian Roos184bfe022016-03-03 13:41:44 -08004989 n.contentView.getSequenceNumber();
4990 boolean stripBigContentView = n.bigContentView instanceof BuilderRemoteViews &&
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004991 n.extras.getInt(EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT, -1) ==
Adrian Roos184bfe022016-03-03 13:41:44 -08004992 n.bigContentView.getSequenceNumber();
4993 boolean stripHeadsUpContentView = n.headsUpContentView instanceof BuilderRemoteViews &&
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004994 n.extras.getInt(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT, -1) ==
Adrian Roos184bfe022016-03-03 13:41:44 -08004995 n.headsUpContentView.getSequenceNumber();
4996
4997 // Nothing to do here, no need to clone.
4998 if (!stripContentView && !stripBigContentView && !stripHeadsUpContentView) {
4999 return n;
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05005000 }
Adrian Roos184bfe022016-03-03 13:41:44 -08005001
5002 Notification clone = n.clone();
5003 if (stripContentView) {
5004 clone.contentView = null;
5005 clone.extras.remove(EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT);
5006 }
5007 if (stripBigContentView) {
5008 clone.bigContentView = null;
5009 clone.extras.remove(EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT);
5010 }
5011 if (stripHeadsUpContentView) {
5012 clone.headsUpContentView = null;
5013 clone.extras.remove(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT);
5014 }
5015 return clone;
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05005016 }
5017
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01005018 private int getBaseLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07005019 return R.layout.notification_template_material_base;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01005020 }
5021
5022 private int getBigBaseLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07005023 return R.layout.notification_template_material_big_base;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01005024 }
5025
5026 private int getBigPictureLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07005027 return R.layout.notification_template_material_big_picture;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01005028 }
5029
5030 private int getBigTextLayoutResource() {
Jorim Jaggi445d3c02014-08-19 22:33:42 +02005031 return R.layout.notification_template_material_big_text;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01005032 }
5033
5034 private int getInboxLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07005035 return R.layout.notification_template_material_inbox;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01005036 }
5037
Adrian Roosc1a80b02016-04-05 14:54:55 -07005038 private int getMessagingLayoutResource() {
5039 return R.layout.notification_template_material_messaging;
5040 }
5041
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01005042 private int getActionLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07005043 return R.layout.notification_material_action;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01005044 }
5045
Selim Cinek06e9e1f2016-07-08 17:14:16 -07005046 private int getEmphasizedActionLayoutResource() {
5047 return R.layout.notification_material_action_emphasized;
5048 }
5049
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01005050 private int getActionTombstoneLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07005051 return R.layout.notification_material_action_tombstone;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01005052 }
Selim Cinek7b9605b2017-01-19 17:36:00 -08005053
5054 private int getBackgroundColor() {
5055 if (isColorized()) {
Selim Cinek5fb73f82017-04-20 16:55:38 -07005056 return mBackgroundColor != COLOR_INVALID ? mBackgroundColor : mN.color;
Selim Cinek7b9605b2017-01-19 17:36:00 -08005057 } else {
Selim Cinekac5f0272017-05-02 16:05:41 -07005058 return mBackgroundColorHint != COLOR_INVALID ? mBackgroundColorHint
5059 : COLOR_DEFAULT;
Selim Cinek7b9605b2017-01-19 17:36:00 -08005060 }
5061 }
5062
5063 private boolean isColorized() {
5064 return mN.isColorized();
5065 }
Selim Cinek99104832017-01-25 14:47:33 -08005066
5067 private boolean textColorsNeedInversion() {
5068 if (mStyle == null || !MediaStyle.class.equals(mStyle.getClass())) {
5069 return false;
5070 }
5071 int targetSdkVersion = mContext.getApplicationInfo().targetSdkVersion;
5072 return targetSdkVersion > Build.VERSION_CODES.M
5073 && targetSdkVersion < Build.VERSION_CODES.O;
5074 }
Selim Cinek5fb73f82017-04-20 16:55:38 -07005075
5076 /**
5077 * Set a color palette to be used as the background and textColors
5078 *
5079 * @param backgroundColor the color to be used as the background
5080 * @param foregroundColor the color to be used as the foreground
5081 *
5082 * @hide
5083 */
5084 public void setColorPalette(int backgroundColor, int foregroundColor) {
5085 mBackgroundColor = backgroundColor;
5086 mForegroundColor = foregroundColor;
5087 mTextColorsAreForBackground = COLOR_INVALID;
5088 ensureColors();
5089 }
Selim Cinekac5f0272017-05-02 16:05:41 -07005090
5091 /**
5092 * Sets the background color for this notification to be a different one then the default.
5093 * This is mainly used to calculate contrast and won't necessarily be applied to the
5094 * background.
5095 *
5096 * @hide
5097 */
5098 public void setBackgroundColorHint(int backgroundColor) {
5099 mBackgroundColorHint = backgroundColor;
5100 }
Selim Cineka7679b62017-05-10 16:33:25 -07005101
5102
5103 /**
5104 * Forces all styled remoteViews to be built from scratch and not use any cached
5105 * RemoteViews.
5106 * This is needed for legacy apps that are baking in their remoteviews into the
5107 * notification.
5108 *
5109 * @hide
5110 */
5111 public void setRebuildStyledRemoteViews(boolean rebuild) {
5112 mRebuildStyledRemoteViews = rebuild;
5113 }
Selim Cinek7b9605b2017-01-19 17:36:00 -08005114 }
5115
5116 /**
Selim Cinek22714f12017-04-13 16:23:53 -07005117 * @return whether this notification is a foreground service notification
Selim Cinek7b9605b2017-01-19 17:36:00 -08005118 */
Selim Cinek22714f12017-04-13 16:23:53 -07005119 private boolean isForegroundService() {
5120 return (flags & Notification.FLAG_FOREGROUND_SERVICE) != 0;
Selim Cinek7b9605b2017-01-19 17:36:00 -08005121 }
5122
5123 /**
Selim Cinek99104832017-01-25 14:47:33 -08005124 * @return whether this notification has a media session attached
5125 * @hide
5126 */
5127 public boolean hasMediaSession() {
5128 return extras.getParcelable(Notification.EXTRA_MEDIA_SESSION) != null;
5129 }
5130
5131 /**
Selim Cinek7d1009b2017-01-25 15:28:28 -08005132 * @return the style class of this notification
5133 * @hide
5134 */
5135 public Class<? extends Notification.Style> getNotificationStyle() {
5136 String templateClass = extras.getString(Notification.EXTRA_TEMPLATE);
5137
5138 if (!TextUtils.isEmpty(templateClass)) {
5139 return Notification.getNotificationStyleClass(templateClass);
5140 }
5141 return null;
5142 }
5143
5144 /**
Selim Cinek22714f12017-04-13 16:23:53 -07005145 * @return true if this notification is colorized.
Selim Cinek7b9605b2017-01-19 17:36:00 -08005146 *
5147 * @hide
5148 */
5149 public boolean isColorized() {
Selim Cinek5fb73f82017-04-20 16:55:38 -07005150 if (isColorizedMedia()) {
5151 return true;
5152 }
5153 return extras.getBoolean(EXTRA_COLORIZED) && isForegroundService();
5154 }
5155
5156 /**
5157 * @return true if this notification is colorized and it is a media notification
5158 *
5159 * @hide
5160 */
5161 public boolean isColorizedMedia() {
Selim Cinek99104832017-01-25 14:47:33 -08005162 Class<? extends Style> style = getNotificationStyle();
5163 if (MediaStyle.class.equals(style)) {
5164 Boolean colorized = (Boolean) extras.get(EXTRA_COLORIZED);
5165 if ((colorized == null || colorized) && hasMediaSession()) {
5166 return true;
5167 }
5168 } else if (DecoratedMediaCustomViewStyle.class.equals(style)) {
5169 if (extras.getBoolean(EXTRA_COLORIZED) && hasMediaSession()) {
5170 return true;
5171 }
5172 }
Selim Cinek5fb73f82017-04-20 16:55:38 -07005173 return false;
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005174 }
5175
Selim Cinek0847acd2017-04-24 19:48:29 -07005176
5177 /**
5178 * @return true if this is a media notification
5179 *
5180 * @hide
5181 */
5182 public boolean isMediaNotification() {
5183 Class<? extends Style> style = getNotificationStyle();
5184 if (MediaStyle.class.equals(style)) {
5185 return true;
5186 } else if (DecoratedMediaCustomViewStyle.class.equals(style)) {
5187 return true;
5188 }
5189 return false;
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005190 }
5191
Selim Cinek279fa862016-06-14 10:57:25 -07005192 private boolean hasLargeIcon() {
5193 return mLargeIcon != null || largeIcon != null;
5194 }
5195
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005196 /**
Selim Cinekc2c0b042016-05-18 17:13:46 -07005197 * @return true if the notification will show the time; false otherwise
Selim Cinekb85f36fd2016-04-20 18:46:36 -07005198 * @hide
5199 */
Selim Cinekc2c0b042016-05-18 17:13:46 -07005200 public boolean showsTime() {
Selim Cinekb85f36fd2016-04-20 18:46:36 -07005201 return when != 0 && extras.getBoolean(EXTRA_SHOW_WHEN);
5202 }
5203
5204 /**
Selim Cinekc2c0b042016-05-18 17:13:46 -07005205 * @return true if the notification will show a chronometer; false otherwise
5206 * @hide
5207 */
5208 public boolean showsChronometer() {
5209 return when != 0 && extras.getBoolean(EXTRA_SHOW_CHRONOMETER);
5210 }
5211
5212 /**
Julia Reynolds4a02afb2016-12-13 13:39:52 -05005213 * @hide
5214 */
5215 @SystemApi
5216 public static Class<? extends Style> getNotificationStyleClass(String templateClass) {
5217 Class<? extends Style>[] classes = new Class[] {
5218 BigTextStyle.class, BigPictureStyle.class, InboxStyle.class, MediaStyle.class,
5219 DecoratedCustomViewStyle.class, DecoratedMediaCustomViewStyle.class,
5220 MessagingStyle.class };
5221 for (Class<? extends Style> innerClass : classes) {
5222 if (templateClass.equals(innerClass.getName())) {
5223 return innerClass;
5224 }
5225 }
5226 return null;
5227 }
5228
5229 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005230 * An object that can apply a rich notification style to a {@link Notification.Builder}
5231 * object.
5232 */
Griff Hazendfcb0802014-02-11 12:00:00 -08005233 public static abstract class Style {
Chris Wrend6297db2012-05-03 16:20:13 -04005234 private CharSequence mBigContentTitle;
Jorim Jaggi457a10d2014-09-08 16:18:23 +02005235
5236 /**
5237 * @hide
5238 */
5239 protected CharSequence mSummaryText = null;
5240
5241 /**
5242 * @hide
5243 */
5244 protected boolean mSummaryTextSet = false;
Chris Wrend6297db2012-05-03 16:20:13 -04005245
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005246 protected Builder mBuilder;
5247
Chris Wrend6297db2012-05-03 16:20:13 -04005248 /**
5249 * Overrides ContentTitle in the big form of the template.
5250 * This defaults to the value passed to setContentTitle().
5251 */
5252 protected void internalSetBigContentTitle(CharSequence title) {
5253 mBigContentTitle = title;
5254 }
5255
5256 /**
5257 * Set the first line of text after the detail section in the big form of the template.
5258 */
5259 protected void internalSetSummaryText(CharSequence cs) {
5260 mSummaryText = cs;
Daniel Sandler619738c2012-06-07 16:33:08 -04005261 mSummaryTextSet = true;
Chris Wrend6297db2012-05-03 16:20:13 -04005262 }
5263
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005264 public void setBuilder(Builder builder) {
5265 if (mBuilder != builder) {
5266 mBuilder = builder;
Daniel Sandlerc08dea22012-06-28 08:35:24 -07005267 if (mBuilder != null) {
5268 mBuilder.setStyle(this);
5269 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005270 }
5271 }
5272
Chris Wrend6297db2012-05-03 16:20:13 -04005273 protected void checkBuilder() {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005274 if (mBuilder == null) {
5275 throw new IllegalArgumentException("Style requires a valid Builder object");
5276 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005277 }
Chris Wrend6297db2012-05-03 16:20:13 -04005278
5279 protected RemoteViews getStandardView(int layoutId) {
5280 checkBuilder();
5281
Christoph Studer4600f9b2014-07-22 22:44:43 +02005282 // Nasty.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005283 CharSequence oldBuilderContentTitle =
5284 mBuilder.getAllExtras().getCharSequence(EXTRA_TITLE);
Chris Wrend6297db2012-05-03 16:20:13 -04005285 if (mBigContentTitle != null) {
5286 mBuilder.setContentTitle(mBigContentTitle);
5287 }
5288
Chris Wrend6297db2012-05-03 16:20:13 -04005289 RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(layoutId);
5290
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005291 mBuilder.getAllExtras().putCharSequence(EXTRA_TITLE, oldBuilderContentTitle);
Christoph Studer4600f9b2014-07-22 22:44:43 +02005292
Chris Wrend6297db2012-05-03 16:20:13 -04005293 if (mBigContentTitle != null && mBigContentTitle.equals("")) {
5294 contentView.setViewVisibility(R.id.line1, View.GONE);
Chris Wren67dc9a02012-05-16 01:03:20 -04005295 } else {
5296 contentView.setViewVisibility(R.id.line1, View.VISIBLE);
Chris Wrend6297db2012-05-03 16:20:13 -04005297 }
5298
Chris Wrend6297db2012-05-03 16:20:13 -04005299 return contentView;
5300 }
5301
Daniel Sandlerf45564e2013-04-15 15:05:08 -04005302 /**
Selim Cinek7d1009b2017-01-25 15:28:28 -08005303 * Construct a Style-specific RemoteViews for the collapsed notification layout.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005304 * The default implementation has nothing additional to add.
Selim Cinek7d1009b2017-01-25 15:28:28 -08005305 *
5306 * @param increasedHeight true if this layout be created with an increased height.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005307 * @hide
5308 */
Selim Cinek7d1009b2017-01-25 15:28:28 -08005309 public RemoteViews makeContentView(boolean increasedHeight) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005310 return null;
5311 }
5312
5313 /**
5314 * Construct a Style-specific RemoteViews for the final big notification layout.
5315 * @hide
5316 */
5317 public RemoteViews makeBigContentView() {
5318 return null;
5319 }
5320
5321 /**
5322 * Construct a Style-specific RemoteViews for the final HUN layout.
Selim Cinek87ed69b2017-02-09 15:59:43 -08005323 *
5324 * @param increasedHeight true if this layout be created with an increased height.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005325 * @hide
5326 */
Selim Cinek87ed69b2017-02-09 15:59:43 -08005327 public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005328 return null;
5329 }
5330
5331 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005332 * Apply any style-specific extras to this notification before shipping it out.
Daniel Sandlerf45564e2013-04-15 15:05:08 -04005333 * @hide
5334 */
5335 public void addExtras(Bundle extras) {
5336 if (mSummaryTextSet) {
5337 extras.putCharSequence(EXTRA_SUMMARY_TEXT, mSummaryText);
5338 }
5339 if (mBigContentTitle != null) {
5340 extras.putCharSequence(EXTRA_TITLE_BIG, mBigContentTitle);
5341 }
Chris Wren91ad5632013-06-05 15:05:57 -04005342 extras.putString(EXTRA_TEMPLATE, this.getClass().getName());
Daniel Sandlerf45564e2013-04-15 15:05:08 -04005343 }
5344
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04005345 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005346 * Reconstruct the internal state of this Style object from extras.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04005347 * @hide
5348 */
Christoph Studer4600f9b2014-07-22 22:44:43 +02005349 protected void restoreFromExtras(Bundle extras) {
5350 if (extras.containsKey(EXTRA_SUMMARY_TEXT)) {
5351 mSummaryText = extras.getCharSequence(EXTRA_SUMMARY_TEXT);
5352 mSummaryTextSet = true;
5353 }
5354 if (extras.containsKey(EXTRA_TITLE_BIG)) {
5355 mBigContentTitle = extras.getCharSequence(EXTRA_TITLE_BIG);
5356 }
5357 }
5358
5359
5360 /**
5361 * @hide
5362 */
5363 public Notification buildStyled(Notification wip) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005364 addExtras(wip.extras);
Christoph Studer4600f9b2014-07-22 22:44:43 +02005365 return wip;
5366 }
5367
Daniel Sandler0ec46202015-06-24 01:27:05 -04005368 /**
5369 * @hide
5370 */
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07005371 public void purgeResources() {}
5372
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04005373 /**
5374 * Calls {@link android.app.Notification.Builder#build()} on the Builder this Style is
5375 * attached to.
5376 *
5377 * @return the fully constructed Notification.
5378 */
5379 public Notification build() {
5380 checkBuilder();
5381 return mBuilder.build();
5382 }
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02005383
5384 /**
5385 * @hide
5386 * @return true if the style positions the progress bar on the second line; false if the
5387 * style hides the progress bar
5388 */
5389 protected boolean hasProgress() {
5390 return true;
5391 }
Selim Cinek03d0d652015-11-13 13:18:09 -05005392
5393 /**
5394 * @hide
5395 * @return Whether we should put the summary be put into the notification header
5396 */
5397 public boolean hasSummaryInHeader() {
5398 return true;
5399 }
Selim Cinek593610c2016-02-16 18:42:57 -08005400
5401 /**
5402 * @hide
5403 * @return Whether custom content views are displayed inline in the style
5404 */
5405 public boolean displayCustomViewInline() {
5406 return false;
5407 }
Joe Onorato46439ce2010-11-19 13:56:21 -08005408 }
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005409
5410 /**
Daniel Sandler4dfbe832012-04-11 14:51:46 -04005411 * Helper class for generating large-format notifications that include a large image attachment.
Joe Malin8d40d042012-11-05 11:36:40 -08005412 *
Robert Ly91c5ce32014-06-08 15:37:00 -07005413 * Here's how you'd set the <code>BigPictureStyle</code> on a notification:
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005414 * <pre class="prettyprint">
Robert Ly91c5ce32014-06-08 15:37:00 -07005415 * Notification notif = new Notification.Builder(mContext)
5416 * .setContentTitle(&quot;New photo from &quot; + sender.toString())
5417 * .setContentText(subject)
5418 * .setSmallIcon(R.drawable.new_post)
5419 * .setLargeIcon(aBitmap)
5420 * .setStyle(new Notification.BigPictureStyle()
5421 * .bigPicture(aBigBitmap))
5422 * .build();
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005423 * </pre>
Joe Malin8d40d042012-11-05 11:36:40 -08005424 *
Daniel Sandler4dfbe832012-04-11 14:51:46 -04005425 * @see Notification#bigContentView
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005426 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005427 public static class BigPictureStyle extends Style {
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005428 private Bitmap mPicture;
Dan Sandlerd63f9322015-05-06 15:18:49 -04005429 private Icon mBigLargeIcon;
Chris Wren3745a3d2012-05-22 15:11:52 -04005430 private boolean mBigLargeIconSet = false;
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005431
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005432 public BigPictureStyle() {
5433 }
5434
Adrian Roosf5faf9d2016-05-23 13:56:15 -07005435 /**
5436 * @deprecated use {@code BigPictureStyle()}.
5437 */
5438 @Deprecated
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005439 public BigPictureStyle(Builder builder) {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005440 setBuilder(builder);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005441 }
5442
Chris Wrend6297db2012-05-03 16:20:13 -04005443 /**
5444 * Overrides ContentTitle in the big form of the template.
5445 * This defaults to the value passed to setContentTitle().
5446 */
5447 public BigPictureStyle setBigContentTitle(CharSequence title) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04005448 internalSetBigContentTitle(safeCharSequence(title));
Chris Wrend6297db2012-05-03 16:20:13 -04005449 return this;
5450 }
5451
5452 /**
5453 * Set the first line of text after the detail section in the big form of the template.
5454 */
5455 public BigPictureStyle setSummaryText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04005456 internalSetSummaryText(safeCharSequence(cs));
Chris Wrend6297db2012-05-03 16:20:13 -04005457 return this;
5458 }
5459
Chris Wren0bd664d2012-08-01 13:56:56 -04005460 /**
5461 * Provide the bitmap to be used as the payload for the BigPicture notification.
5462 */
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005463 public BigPictureStyle bigPicture(Bitmap b) {
5464 mPicture = b;
5465 return this;
5466 }
5467
Chris Wren3745a3d2012-05-22 15:11:52 -04005468 /**
Chris Wren3745a3d2012-05-22 15:11:52 -04005469 * Override the large icon when the big notification is shown.
5470 */
5471 public BigPictureStyle bigLargeIcon(Bitmap b) {
Dan Sandlerd63f9322015-05-06 15:18:49 -04005472 return bigLargeIcon(b != null ? Icon.createWithBitmap(b) : null);
5473 }
5474
5475 /**
5476 * Override the large icon when the big notification is shown.
5477 */
5478 public BigPictureStyle bigLargeIcon(Icon icon) {
Chris Wren3745a3d2012-05-22 15:11:52 -04005479 mBigLargeIconSet = true;
Dan Sandlerd63f9322015-05-06 15:18:49 -04005480 mBigLargeIcon = icon;
Chris Wren3745a3d2012-05-22 15:11:52 -04005481 return this;
5482 }
5483
Riley Andrews0394a0c2015-11-03 23:36:52 -08005484 /** @hide */
5485 public static final int MIN_ASHMEM_BITMAP_SIZE = 128 * (1 << 10);
5486
Daniel Sandler0ec46202015-06-24 01:27:05 -04005487 /**
5488 * @hide
5489 */
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07005490 @Override
5491 public void purgeResources() {
5492 super.purgeResources();
Riley Andrews8cee7c12015-11-01 23:36:04 -08005493 if (mPicture != null &&
5494 mPicture.isMutable() &&
Riley Andrews0394a0c2015-11-03 23:36:52 -08005495 mPicture.getAllocationByteCount() >= MIN_ASHMEM_BITMAP_SIZE) {
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07005496 mPicture = mPicture.createAshmemBitmap();
5497 }
5498 if (mBigLargeIcon != null) {
5499 mBigLargeIcon.convertToAshmem();
5500 }
5501 }
Christoph Studer5c510ee2014-12-15 16:32:27 +01005502
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005503 /**
5504 * @hide
5505 */
5506 public RemoteViews makeBigContentView() {
5507 // Replace mN.mLargeIcon with mBigLargeIcon if mBigLargeIconSet
Christoph Studer5c510ee2014-12-15 16:32:27 +01005508 // This covers the following cases:
5509 // 1. mBigLargeIconSet -> mBigLargeIcon (null or non-null) applies, overrides
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005510 // mN.mLargeIcon
5511 // 2. !mBigLargeIconSet -> mN.mLargeIcon applies
Dan Sandlerd63f9322015-05-06 15:18:49 -04005512 Icon oldLargeIcon = null;
Selim Cineke99acb22016-08-04 12:55:48 -07005513 Bitmap largeIconLegacy = null;
Christoph Studer5c510ee2014-12-15 16:32:27 +01005514 if (mBigLargeIconSet) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005515 oldLargeIcon = mBuilder.mN.mLargeIcon;
5516 mBuilder.mN.mLargeIcon = mBigLargeIcon;
Selim Cineke99acb22016-08-04 12:55:48 -07005517 // The legacy largeIcon might not allow us to clear the image, as it's taken in
5518 // replacement if the other one is null. Because we're restoring these legacy icons
5519 // for old listeners, this is in general non-null.
5520 largeIconLegacy = mBuilder.mN.largeIcon;
5521 mBuilder.mN.largeIcon = null;
Christoph Studer5c510ee2014-12-15 16:32:27 +01005522 }
5523
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01005524 RemoteViews contentView = getStandardView(mBuilder.getBigPictureLayoutResource());
Selim Cinek03d0d652015-11-13 13:18:09 -05005525 if (mSummaryTextSet) {
5526 contentView.setTextViewText(R.id.text, mBuilder.processLegacyText(mSummaryText));
Selim Cinek7b9605b2017-01-19 17:36:00 -08005527 mBuilder.setTextViewColorSecondary(contentView, R.id.text);
Selim Cinekc848c3a2016-01-13 15:27:30 -08005528 contentView.setViewVisibility(R.id.text, View.VISIBLE);
Selim Cinek03d0d652015-11-13 13:18:09 -05005529 }
Selim Cinek279fa862016-06-14 10:57:25 -07005530 mBuilder.setContentMinHeight(contentView, mBuilder.mN.hasLargeIcon());
Selim Cinek53e64a42015-11-16 10:40:56 -08005531
Christoph Studer5c510ee2014-12-15 16:32:27 +01005532 if (mBigLargeIconSet) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005533 mBuilder.mN.mLargeIcon = oldLargeIcon;
Selim Cineke99acb22016-08-04 12:55:48 -07005534 mBuilder.mN.largeIcon = largeIconLegacy;
Christoph Studer5c510ee2014-12-15 16:32:27 +01005535 }
5536
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005537 contentView.setImageViewBitmap(R.id.big_picture, mPicture);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005538 return contentView;
5539 }
5540
Daniel Sandlerf45564e2013-04-15 15:05:08 -04005541 /**
5542 * @hide
5543 */
5544 public void addExtras(Bundle extras) {
5545 super.addExtras(extras);
5546
5547 if (mBigLargeIconSet) {
5548 extras.putParcelable(EXTRA_LARGE_ICON_BIG, mBigLargeIcon);
5549 }
5550 extras.putParcelable(EXTRA_PICTURE, mPicture);
5551 }
5552
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04005553 /**
5554 * @hide
5555 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005556 @Override
Christoph Studer4600f9b2014-07-22 22:44:43 +02005557 protected void restoreFromExtras(Bundle extras) {
5558 super.restoreFromExtras(extras);
5559
5560 if (extras.containsKey(EXTRA_LARGE_ICON_BIG)) {
Christoph Studer5c510ee2014-12-15 16:32:27 +01005561 mBigLargeIconSet = true;
Christoph Studer4600f9b2014-07-22 22:44:43 +02005562 mBigLargeIcon = extras.getParcelable(EXTRA_LARGE_ICON_BIG);
Chris Wren3745a3d2012-05-22 15:11:52 -04005563 }
Christoph Studer4600f9b2014-07-22 22:44:43 +02005564 mPicture = extras.getParcelable(EXTRA_PICTURE);
5565 }
Selim Cinek03d0d652015-11-13 13:18:09 -05005566
5567 /**
5568 * @hide
5569 */
5570 @Override
5571 public boolean hasSummaryInHeader() {
5572 return false;
5573 }
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005574 }
5575
5576 /**
Daniel Sandler4dfbe832012-04-11 14:51:46 -04005577 * Helper class for generating large-format notifications that include a lot of text.
Joe Malin8d40d042012-11-05 11:36:40 -08005578 *
Robert Ly91c5ce32014-06-08 15:37:00 -07005579 * Here's how you'd set the <code>BigTextStyle</code> on a notification:
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005580 * <pre class="prettyprint">
Robert Ly91c5ce32014-06-08 15:37:00 -07005581 * Notification notif = new Notification.Builder(mContext)
5582 * .setContentTitle(&quot;New mail from &quot; + sender.toString())
5583 * .setContentText(subject)
5584 * .setSmallIcon(R.drawable.new_mail)
5585 * .setLargeIcon(aBitmap)
5586 * .setStyle(new Notification.BigTextStyle()
5587 * .bigText(aVeryLongString))
5588 * .build();
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005589 * </pre>
Joe Malin8d40d042012-11-05 11:36:40 -08005590 *
Daniel Sandler4dfbe832012-04-11 14:51:46 -04005591 * @see Notification#bigContentView
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005592 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005593 public static class BigTextStyle extends Style {
Jorim Jaggi457a10d2014-09-08 16:18:23 +02005594
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005595 private CharSequence mBigText;
5596
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005597 public BigTextStyle() {
5598 }
5599
Adrian Roosf5faf9d2016-05-23 13:56:15 -07005600 /**
5601 * @deprecated use {@code BigTextStyle()}.
5602 */
5603 @Deprecated
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005604 public BigTextStyle(Builder builder) {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005605 setBuilder(builder);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005606 }
5607
Chris Wrend6297db2012-05-03 16:20:13 -04005608 /**
5609 * Overrides ContentTitle in the big form of the template.
5610 * This defaults to the value passed to setContentTitle().
5611 */
5612 public BigTextStyle setBigContentTitle(CharSequence title) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04005613 internalSetBigContentTitle(safeCharSequence(title));
Chris Wrend6297db2012-05-03 16:20:13 -04005614 return this;
5615 }
5616
5617 /**
5618 * Set the first line of text after the detail section in the big form of the template.
5619 */
5620 public BigTextStyle setSummaryText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04005621 internalSetSummaryText(safeCharSequence(cs));
Chris Wrend6297db2012-05-03 16:20:13 -04005622 return this;
5623 }
5624
Chris Wren0bd664d2012-08-01 13:56:56 -04005625 /**
5626 * Provide the longer text to be displayed in the big form of the
5627 * template in place of the content text.
5628 */
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005629 public BigTextStyle bigText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04005630 mBigText = safeCharSequence(cs);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005631 return this;
5632 }
5633
Daniel Sandlerf45564e2013-04-15 15:05:08 -04005634 /**
5635 * @hide
5636 */
5637 public void addExtras(Bundle extras) {
5638 super.addExtras(extras);
5639
Christoph Studer4600f9b2014-07-22 22:44:43 +02005640 extras.putCharSequence(EXTRA_BIG_TEXT, mBigText);
5641 }
5642
5643 /**
5644 * @hide
5645 */
5646 @Override
5647 protected void restoreFromExtras(Bundle extras) {
5648 super.restoreFromExtras(extras);
5649
5650 mBigText = extras.getCharSequence(EXTRA_BIG_TEXT);
Daniel Sandlerf45564e2013-04-15 15:05:08 -04005651 }
5652
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005653 /**
Selim Cinek7d1009b2017-01-25 15:28:28 -08005654 * @param increasedHeight true if this layout be created with an increased height.
5655 *
5656 * @hide
5657 */
5658 @Override
5659 public RemoteViews makeContentView(boolean increasedHeight) {
5660 if (increasedHeight) {
Selim Cinek5d6ef8d2017-05-18 22:16:00 -07005661 mBuilder.mOriginalActions = mBuilder.mActions;
Selim Cinek7d1009b2017-01-25 15:28:28 -08005662 mBuilder.mActions = new ArrayList<>();
5663 RemoteViews remoteViews = makeBigContentView();
Selim Cinek5d6ef8d2017-05-18 22:16:00 -07005664 mBuilder.mActions = mBuilder.mOriginalActions;
5665 mBuilder.mOriginalActions = null;
Selim Cinek7d1009b2017-01-25 15:28:28 -08005666 return remoteViews;
5667 }
5668 return super.makeContentView(increasedHeight);
5669 }
5670
5671 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005672 * @hide
5673 */
Selim Cinek87ed69b2017-02-09 15:59:43 -08005674 @Override
5675 public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
5676 if (increasedHeight && mBuilder.mActions.size() > 0) {
5677 return makeBigContentView();
5678 }
5679 return super.makeHeadsUpContentView(increasedHeight);
5680 }
5681
5682 /**
5683 * @hide
5684 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005685 public RemoteViews makeBigContentView() {
Christoph Studer4600f9b2014-07-22 22:44:43 +02005686
5687 // Nasty
Selim Cinek75998782016-04-26 10:39:17 -07005688 CharSequence text = mBuilder.getAllExtras().getCharSequence(EXTRA_TEXT);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005689 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, null);
Daniel Sandler916ad912012-06-13 12:17:07 -04005690
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01005691 RemoteViews contentView = getStandardView(mBuilder.getBigTextLayoutResource());
Joe Malin8d40d042012-11-05 11:36:40 -08005692
Selim Cinek75998782016-04-26 10:39:17 -07005693 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, text);
Christoph Studer4600f9b2014-07-22 22:44:43 +02005694
Selim Cinek3a2c4b92015-12-17 17:01:17 -08005695 CharSequence bigTextText = mBuilder.processLegacyText(mBigText);
Selim Cinek75998782016-04-26 10:39:17 -07005696 if (TextUtils.isEmpty(bigTextText)) {
5697 // In case the bigtext is null / empty fall back to the normal text to avoid a weird
5698 // experience
5699 bigTextText = mBuilder.processLegacyText(text);
5700 }
Adrian Roosb1f427c2016-05-26 12:27:15 -07005701 applyBigTextContentView(mBuilder, contentView, bigTextText);
Selim Cinek4fb12d32015-11-19 18:10:48 -08005702
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005703 return contentView;
5704 }
5705
Adrian Roosb1f427c2016-05-26 12:27:15 -07005706 static void applyBigTextContentView(Builder builder,
5707 RemoteViews contentView, CharSequence bigTextText) {
5708 contentView.setTextViewText(R.id.big_text, bigTextText);
Selim Cinek7b9605b2017-01-19 17:36:00 -08005709 builder.setTextViewColorSecondary(contentView, R.id.big_text);
Adrian Roosb1f427c2016-05-26 12:27:15 -07005710 contentView.setViewVisibility(R.id.big_text,
5711 TextUtils.isEmpty(bigTextText) ? View.GONE : View.VISIBLE);
Selim Cinek279fa862016-06-14 10:57:25 -07005712 contentView.setBoolean(R.id.big_text, "setHasImage", builder.mN.hasLargeIcon());
Adrian Roosb1f427c2016-05-26 12:27:15 -07005713 }
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005714 }
Daniel Sandler879c5e02012-04-17 16:46:51 -04005715
5716 /**
Alex Hillsfc737de2016-03-23 17:33:02 -04005717 * Helper class for generating large-format notifications that include multiple back-and-forth
5718 * messages of varying types between any number of people.
5719 *
5720 * <br>
5721 * If the platform does not provide large-format notifications, this method has no effect. The
5722 * user will always see the normal notification view.
5723 * <br>
5724 * This class is a "rebuilder": It attaches to a Builder object and modifies its behavior, like
5725 * so:
5726 * <pre class="prettyprint">
5727 *
5728 * Notification noti = new Notification.Builder()
5729 * .setContentTitle(&quot;2 new messages wtih &quot; + sender.toString())
5730 * .setContentText(subject)
5731 * .setSmallIcon(R.drawable.new_message)
5732 * .setLargeIcon(aBitmap)
5733 * .setStyle(new Notification.MessagingStyle(resources.getString(R.string.reply_name))
5734 * .addMessage(messages[0].getText(), messages[0].getTime(), messages[0].getSender())
5735 * .addMessage(messages[1].getText(), messages[1].getTime(), messages[1].getSender()))
5736 * .build();
5737 * </pre>
5738 */
5739 public static class MessagingStyle extends Style {
5740
5741 /**
5742 * The maximum number of messages that will be retained in the Notification itself (the
5743 * number displayed is up to the platform).
5744 */
5745 public static final int MAXIMUM_RETAINED_MESSAGES = 25;
5746
5747 CharSequence mUserDisplayName;
5748 CharSequence mConversationTitle;
Alex Hillsd9b04d92016-04-11 16:38:16 -04005749 List<Message> mMessages = new ArrayList<>();
Adrian Roos437cd562017-01-18 15:47:03 -08005750 List<Message> mHistoricMessages = new ArrayList<>();
Alex Hillsfc737de2016-03-23 17:33:02 -04005751
5752 MessagingStyle() {
5753 }
5754
5755 /**
Alex Hillsfd590442016-10-07 09:52:44 -04005756 * @param userDisplayName Required - the name to be displayed for any replies sent by the
5757 * user before the posting app reposts the notification with those messages after they've
5758 * been actually sent and in previous messages sent by the user added in
Alex Hillsfc737de2016-03-23 17:33:02 -04005759 * {@link #addMessage(Notification.MessagingStyle.Message)}
5760 */
Alex Hillsfd590442016-10-07 09:52:44 -04005761 public MessagingStyle(@NonNull CharSequence userDisplayName) {
Alex Hillsfc737de2016-03-23 17:33:02 -04005762 mUserDisplayName = userDisplayName;
5763 }
5764
5765 /**
5766 * Returns the name to be displayed for any replies sent by the user
5767 */
5768 public CharSequence getUserDisplayName() {
5769 return mUserDisplayName;
5770 }
5771
5772 /**
Alex Hillsfc737de2016-03-23 17:33:02 -04005773 * Sets the title to be displayed on this conversation. This should only be used for
5774 * group messaging and left unset for one-on-one conversations.
5775 * @param conversationTitle
5776 * @return this object for method chaining.
5777 */
5778 public MessagingStyle setConversationTitle(CharSequence conversationTitle) {
5779 mConversationTitle = conversationTitle;
5780 return this;
5781 }
5782
5783 /**
5784 * Return the title to be displayed on this conversation. Can be <code>null</code> and
5785 * should be for one-on-one conversations
5786 */
5787 public CharSequence getConversationTitle() {
5788 return mConversationTitle;
5789 }
5790
5791 /**
5792 * Adds a message for display by this notification. Convenience call for a simple
5793 * {@link Message} in {@link #addMessage(Notification.MessagingStyle.Message)}.
5794 * @param text A {@link CharSequence} to be displayed as the message content
5795 * @param timestamp Time at which the message arrived
5796 * @param sender A {@link CharSequence} to be used for displaying the name of the
5797 * sender. Should be <code>null</code> for messages by the current user, in which case
5798 * the platform will insert {@link #getUserDisplayName()}.
5799 * Should be unique amongst all individuals in the conversation, and should be
5800 * consistent during re-posts of the notification.
5801 *
5802 * @see Message#Message(CharSequence, long, CharSequence)
5803 *
5804 * @return this object for method chaining
5805 */
5806 public MessagingStyle addMessage(CharSequence text, long timestamp, CharSequence sender) {
Adrian Roos437cd562017-01-18 15:47:03 -08005807 return addMessage(new Message(text, timestamp, sender));
Alex Hillsfc737de2016-03-23 17:33:02 -04005808 }
5809
5810 /**
5811 * Adds a {@link Message} for display in this notification.
Adrian Roos437cd562017-01-18 15:47:03 -08005812 *
5813 * <p>The messages should be added in chronologic order, i.e. the oldest first,
5814 * the newest last.
5815 *
Alex Hillsfc737de2016-03-23 17:33:02 -04005816 * @param message The {@link Message} to be displayed
5817 * @return this object for method chaining
5818 */
5819 public MessagingStyle addMessage(Message message) {
5820 mMessages.add(message);
5821 if (mMessages.size() > MAXIMUM_RETAINED_MESSAGES) {
5822 mMessages.remove(0);
5823 }
5824 return this;
5825 }
5826
5827 /**
Adrian Roos437cd562017-01-18 15:47:03 -08005828 * Adds a {@link Message} for historic context in this notification.
5829 *
5830 * <p>Messages should be added as historic if they are not the main subject of the
5831 * notification but may give context to a conversation. The system may choose to present
5832 * them only when relevant, e.g. when replying to a message through a {@link RemoteInput}.
5833 *
5834 * <p>The messages should be added in chronologic order, i.e. the oldest first,
5835 * the newest last.
5836 *
5837 * @param message The historic {@link Message} to be added
5838 * @return this object for method chaining
5839 */
5840 public MessagingStyle addHistoricMessage(Message message) {
5841 mHistoricMessages.add(message);
5842 if (mHistoricMessages.size() > MAXIMUM_RETAINED_MESSAGES) {
5843 mHistoricMessages.remove(0);
5844 }
5845 return this;
5846 }
5847
5848 /**
Alex Hillsfc737de2016-03-23 17:33:02 -04005849 * Gets the list of {@code Message} objects that represent the notification
5850 */
5851 public List<Message> getMessages() {
5852 return mMessages;
5853 }
5854
5855 /**
Adrian Roos437cd562017-01-18 15:47:03 -08005856 * Gets the list of historic {@code Message}s in the notification.
5857 */
5858 public List<Message> getHistoricMessages() {
5859 return mHistoricMessages;
5860 }
5861
5862 /**
Alex Hillsfc737de2016-03-23 17:33:02 -04005863 * @hide
5864 */
5865 @Override
5866 public void addExtras(Bundle extras) {
5867 super.addExtras(extras);
5868 if (mUserDisplayName != null) {
5869 extras.putCharSequence(EXTRA_SELF_DISPLAY_NAME, mUserDisplayName);
5870 }
5871 if (mConversationTitle != null) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04005872 extras.putCharSequence(EXTRA_CONVERSATION_TITLE, mConversationTitle);
Alex Hillsfc737de2016-03-23 17:33:02 -04005873 }
Alex Hillsd9b04d92016-04-11 16:38:16 -04005874 if (!mMessages.isEmpty()) { extras.putParcelableArray(EXTRA_MESSAGES,
5875 Message.getBundleArrayForMessages(mMessages));
Alex Hillsfc737de2016-03-23 17:33:02 -04005876 }
Adrian Roos437cd562017-01-18 15:47:03 -08005877 if (!mHistoricMessages.isEmpty()) { extras.putParcelableArray(EXTRA_HISTORIC_MESSAGES,
5878 Message.getBundleArrayForMessages(mHistoricMessages));
5879 }
Adrian Roos33fbd2c2016-05-27 15:35:28 -07005880
5881 fixTitleAndTextExtras(extras);
5882 }
5883
5884 private void fixTitleAndTextExtras(Bundle extras) {
5885 Message m = findLatestIncomingMessage();
5886 CharSequence text = (m == null) ? null : m.mText;
5887 CharSequence sender = m == null ? null
5888 : TextUtils.isEmpty(m.mSender) ? mUserDisplayName : m.mSender;
5889 CharSequence title;
5890 if (!TextUtils.isEmpty(mConversationTitle)) {
5891 if (!TextUtils.isEmpty(sender)) {
5892 BidiFormatter bidi = BidiFormatter.getInstance();
5893 title = mBuilder.mContext.getString(
5894 com.android.internal.R.string.notification_messaging_title_template,
5895 bidi.unicodeWrap(mConversationTitle), bidi.unicodeWrap(m.mSender));
5896 } else {
5897 title = mConversationTitle;
5898 }
5899 } else {
5900 title = sender;
5901 }
5902
5903 if (title != null) {
5904 extras.putCharSequence(EXTRA_TITLE, title);
5905 }
5906 if (text != null) {
5907 extras.putCharSequence(EXTRA_TEXT, text);
5908 }
Alex Hillsfc737de2016-03-23 17:33:02 -04005909 }
5910
5911 /**
5912 * @hide
5913 */
5914 @Override
5915 protected void restoreFromExtras(Bundle extras) {
5916 super.restoreFromExtras(extras);
5917
5918 mMessages.clear();
Adrian Roos437cd562017-01-18 15:47:03 -08005919 mHistoricMessages.clear();
Adrian Roos96b7e202016-05-17 13:50:38 -07005920 mUserDisplayName = extras.getCharSequence(EXTRA_SELF_DISPLAY_NAME);
5921 mConversationTitle = extras.getCharSequence(EXTRA_CONVERSATION_TITLE);
Adrian Roos437cd562017-01-18 15:47:03 -08005922 Parcelable[] messages = extras.getParcelableArray(EXTRA_MESSAGES);
5923 if (messages != null && messages instanceof Parcelable[]) {
5924 mMessages = Message.getMessagesFromBundleArray(messages);
5925 }
5926 Parcelable[] histMessages = extras.getParcelableArray(EXTRA_HISTORIC_MESSAGES);
5927 if (histMessages != null && histMessages instanceof Parcelable[]) {
5928 mHistoricMessages = Message.getMessagesFromBundleArray(histMessages);
Alex Hillsfc737de2016-03-23 17:33:02 -04005929 }
5930 }
5931
5932 /**
5933 * @hide
5934 */
Adrian Roosc1a80b02016-04-05 14:54:55 -07005935 @Override
Selim Cinek7d1009b2017-01-25 15:28:28 -08005936 public RemoteViews makeContentView(boolean increasedHeight) {
5937 if (!increasedHeight) {
5938 Message m = findLatestIncomingMessage();
5939 CharSequence title = mConversationTitle != null
5940 ? mConversationTitle
5941 : (m == null) ? null : m.mSender;
5942 CharSequence text = (m == null)
5943 ? null
5944 : mConversationTitle != null ? makeMessageLine(m, mBuilder) : m.mText;
Alex Hillsfc737de2016-03-23 17:33:02 -04005945
Selim Cinek7d1009b2017-01-25 15:28:28 -08005946 return mBuilder.applyStandardTemplate(mBuilder.getBaseLayoutResource(),
5947 mBuilder.mParams.reset().hasProgress(false).title(title).text(text));
5948 } else {
Selim Cinek5d6ef8d2017-05-18 22:16:00 -07005949 mBuilder.mOriginalActions = mBuilder.mActions;
Selim Cinek7d1009b2017-01-25 15:28:28 -08005950 mBuilder.mActions = new ArrayList<>();
5951 RemoteViews remoteViews = makeBigContentView();
Selim Cinek5d6ef8d2017-05-18 22:16:00 -07005952 mBuilder.mActions = mBuilder.mOriginalActions;
5953 mBuilder.mOriginalActions = null;
Selim Cinek7d1009b2017-01-25 15:28:28 -08005954 return remoteViews;
5955 }
Adrian Roosc1a80b02016-04-05 14:54:55 -07005956 }
5957
5958 private Message findLatestIncomingMessage() {
5959 for (int i = mMessages.size() - 1; i >= 0; i--) {
5960 Message m = mMessages.get(i);
5961 // Incoming messages have a non-empty sender.
5962 if (!TextUtils.isEmpty(m.mSender)) {
5963 return m;
5964 }
5965 }
Adrian Roos33fbd2c2016-05-27 15:35:28 -07005966 if (!mMessages.isEmpty()) {
5967 // No incoming messages, fall back to outgoing message
5968 return mMessages.get(mMessages.size() - 1);
5969 }
Adrian Roosc1a80b02016-04-05 14:54:55 -07005970 return null;
5971 }
5972
5973 /**
5974 * @hide
5975 */
5976 @Override
5977 public RemoteViews makeBigContentView() {
5978 CharSequence title = !TextUtils.isEmpty(super.mBigContentTitle)
5979 ? super.mBigContentTitle
5980 : mConversationTitle;
5981 boolean hasTitle = !TextUtils.isEmpty(title);
5982
Adrian Roosfeafa052016-06-01 17:09:45 -07005983 if (mMessages.size() == 1) {
5984 // Special case for a single message: Use the big text style
5985 // so the collapsed and expanded versions match nicely.
5986 CharSequence bigTitle;
5987 CharSequence text;
5988 if (hasTitle) {
5989 bigTitle = title;
Selim Cinek7b9605b2017-01-19 17:36:00 -08005990 text = makeMessageLine(mMessages.get(0), mBuilder);
Adrian Roosfeafa052016-06-01 17:09:45 -07005991 } else {
5992 bigTitle = mMessages.get(0).mSender;
5993 text = mMessages.get(0).mText;
5994 }
Adrian Roosb1f427c2016-05-26 12:27:15 -07005995 RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(
5996 mBuilder.getBigTextLayoutResource(),
Adrian Roos70d7aa32017-01-11 15:39:06 -08005997 mBuilder.mParams.reset().hasProgress(false).title(bigTitle).text(null));
Adrian Roosb1f427c2016-05-26 12:27:15 -07005998 BigTextStyle.applyBigTextContentView(mBuilder, contentView, text);
5999 return contentView;
6000 }
6001
Adrian Roos48d746a2016-04-12 14:57:28 -07006002 RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(
Adrian Roosc1a80b02016-04-05 14:54:55 -07006003 mBuilder.getMessagingLayoutResource(),
Adrian Roos70d7aa32017-01-11 15:39:06 -08006004 mBuilder.mParams.reset().hasProgress(false).title(title).text(null));
Adrian Roosc1a80b02016-04-05 14:54:55 -07006005
6006 int[] rowIds = {R.id.inbox_text0, R.id.inbox_text1, R.id.inbox_text2, R.id.inbox_text3,
6007 R.id.inbox_text4, R.id.inbox_text5, R.id.inbox_text6};
6008
6009 // Make sure all rows are gone in case we reuse a view.
6010 for (int rowId : rowIds) {
6011 contentView.setViewVisibility(rowId, View.GONE);
6012 }
6013
6014 int i=0;
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006015 contentView.setViewLayoutMarginBottomDimen(R.id.line1,
6016 hasTitle ? R.dimen.notification_messaging_spacing : 0);
Adrian Roosc1a80b02016-04-05 14:54:55 -07006017 contentView.setInt(R.id.notification_messaging, "setNumIndentLines",
Selim Cinek279fa862016-06-14 10:57:25 -07006018 !mBuilder.mN.hasLargeIcon() ? 0 : (hasTitle ? 1 : 2));
Adrian Roosc1a80b02016-04-05 14:54:55 -07006019
Adrian Roosfeafa052016-06-01 17:09:45 -07006020 int contractedChildId = View.NO_ID;
6021 Message contractedMessage = findLatestIncomingMessage();
Adrian Roos437cd562017-01-18 15:47:03 -08006022 int firstHistoricMessage = Math.max(0, mHistoricMessages.size()
6023 - (rowIds.length - mMessages.size()));
6024 while (firstHistoricMessage + i < mHistoricMessages.size() && i < rowIds.length) {
6025 Message m = mHistoricMessages.get(firstHistoricMessage + i);
6026 int rowId = rowIds[i];
6027
Selim Cinek7b9605b2017-01-19 17:36:00 -08006028 contentView.setTextViewText(rowId, makeMessageLine(m, mBuilder));
Adrian Roos437cd562017-01-18 15:47:03 -08006029
6030 if (contractedMessage == m) {
6031 contractedChildId = rowId;
6032 }
6033
6034 i++;
6035 }
6036
Adrian Roosc1a80b02016-04-05 14:54:55 -07006037 int firstMessage = Math.max(0, mMessages.size() - rowIds.length);
6038 while (firstMessage + i < mMessages.size() && i < rowIds.length) {
6039 Message m = mMessages.get(firstMessage + i);
6040 int rowId = rowIds[i];
6041
6042 contentView.setViewVisibility(rowId, View.VISIBLE);
Selim Cinek7b9605b2017-01-19 17:36:00 -08006043 contentView.setTextViewText(rowId, makeMessageLine(m, mBuilder));
6044 mBuilder.setTextViewColorSecondary(contentView, rowId);
Adrian Roosc1a80b02016-04-05 14:54:55 -07006045
Adrian Roosfeafa052016-06-01 17:09:45 -07006046 if (contractedMessage == m) {
6047 contractedChildId = rowId;
6048 }
6049
Adrian Roosc1a80b02016-04-05 14:54:55 -07006050 i++;
6051 }
Adrian Roos437cd562017-01-18 15:47:03 -08006052 // Clear the remaining views for reapply. Ensures that historic message views can
6053 // reliably be identified as being GONE and having non-null text.
6054 while (i < rowIds.length) {
6055 int rowId = rowIds[i];
6056 contentView.setTextViewText(rowId, null);
6057 i++;
6058 }
6059
Adrian Roosfeafa052016-06-01 17:09:45 -07006060 // Record this here to allow transformation between the contracted and expanded views.
6061 contentView.setInt(R.id.notification_messaging, "setContractedChildId",
6062 contractedChildId);
Alex Hillsfc737de2016-03-23 17:33:02 -04006063 return contentView;
6064 }
6065
Selim Cinek7b9605b2017-01-19 17:36:00 -08006066 private CharSequence makeMessageLine(Message m, Builder builder) {
Adrian Roosc1a80b02016-04-05 14:54:55 -07006067 BidiFormatter bidi = BidiFormatter.getInstance();
6068 SpannableStringBuilder sb = new SpannableStringBuilder();
Selim Cinek7b9605b2017-01-19 17:36:00 -08006069 boolean colorize = builder.isColorized();
Adrian Roosc1a80b02016-04-05 14:54:55 -07006070 if (TextUtils.isEmpty(m.mSender)) {
6071 CharSequence replyName = mUserDisplayName == null ? "" : mUserDisplayName;
6072 sb.append(bidi.unicodeWrap(replyName),
Selim Cinek7b9605b2017-01-19 17:36:00 -08006073 makeFontColorSpan(colorize
6074 ? builder.getPrimaryTextColor()
6075 : mBuilder.resolveContrastColor()),
Adrian Roosc1a80b02016-04-05 14:54:55 -07006076 0 /* flags */);
6077 } else {
6078 sb.append(bidi.unicodeWrap(m.mSender),
Selim Cinek7b9605b2017-01-19 17:36:00 -08006079 makeFontColorSpan(colorize
6080 ? builder.getPrimaryTextColor()
6081 : Color.BLACK),
Adrian Roosc1a80b02016-04-05 14:54:55 -07006082 0 /* flags */);
6083 }
6084 CharSequence text = m.mText == null ? "" : m.mText;
6085 sb.append(" ").append(bidi.unicodeWrap(text));
6086 return sb;
6087 }
6088
Adrian Roosdedd1df2016-04-26 16:38:47 -07006089 /**
6090 * @hide
6091 */
6092 @Override
Selim Cinek87ed69b2017-02-09 15:59:43 -08006093 public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
6094 if (increasedHeight) {
6095 return makeBigContentView();
6096 }
Adrian Roosdedd1df2016-04-26 16:38:47 -07006097 Message m = findLatestIncomingMessage();
6098 CharSequence title = mConversationTitle != null
6099 ? mConversationTitle
6100 : (m == null) ? null : m.mSender;
6101 CharSequence text = (m == null)
6102 ? null
Selim Cinek7b9605b2017-01-19 17:36:00 -08006103 : mConversationTitle != null ? makeMessageLine(m, mBuilder) : m.mText;
Adrian Roosdedd1df2016-04-26 16:38:47 -07006104
6105 return mBuilder.applyStandardTemplateWithActions(mBuilder.getBigBaseLayoutResource(),
Adrian Roos70d7aa32017-01-11 15:39:06 -08006106 mBuilder.mParams.reset().hasProgress(false).title(title).text(text));
Adrian Roosdedd1df2016-04-26 16:38:47 -07006107 }
6108
Adrian Roosc1a80b02016-04-05 14:54:55 -07006109 private static TextAppearanceSpan makeFontColorSpan(int color) {
6110 return new TextAppearanceSpan(null, 0, 0,
6111 ColorStateList.valueOf(color), null);
6112 }
6113
Alex Hillsd9b04d92016-04-11 16:38:16 -04006114 public static final class Message {
6115
6116 static final String KEY_TEXT = "text";
6117 static final String KEY_TIMESTAMP = "time";
6118 static final String KEY_SENDER = "sender";
6119 static final String KEY_DATA_MIME_TYPE = "type";
6120 static final String KEY_DATA_URI= "uri";
Shane Brennan5a871862017-03-11 13:14:17 -08006121 static final String KEY_EXTRAS_BUNDLE = "extras";
Alex Hillsfc737de2016-03-23 17:33:02 -04006122
6123 private final CharSequence mText;
6124 private final long mTimestamp;
6125 private final CharSequence mSender;
6126
Shane Brennan5a871862017-03-11 13:14:17 -08006127 private Bundle mExtras = new Bundle();
Alex Hillsfc737de2016-03-23 17:33:02 -04006128 private String mDataMimeType;
6129 private Uri mDataUri;
6130
6131 /**
6132 * Constructor
6133 * @param text A {@link CharSequence} to be displayed as the message content
6134 * @param timestamp Time at which the message arrived
6135 * @param sender A {@link CharSequence} to be used for displaying the name of the
6136 * sender. Should be <code>null</code> for messages by the current user, in which case
6137 * the platform will insert {@link MessagingStyle#getUserDisplayName()}.
6138 * Should be unique amongst all individuals in the conversation, and should be
6139 * consistent during re-posts of the notification.
6140 */
6141 public Message(CharSequence text, long timestamp, CharSequence sender){
6142 mText = text;
6143 mTimestamp = timestamp;
6144 mSender = sender;
6145 }
6146
6147 /**
6148 * Sets a binary blob of data and an associated MIME type for a message. In the case
6149 * where the platform doesn't support the MIME type, the original text provided in the
6150 * constructor will be used.
6151 * @param dataMimeType The MIME type of the content. See
6152 * <a href="{@docRoot}notifications/messaging.html"> for the list of supported MIME
6153 * types on Android and Android Wear.
6154 * @param dataUri The uri containing the content whose type is given by the MIME type.
6155 * <p class="note">
6156 * <ol>
6157 * <li>Notification Listeners including the System UI need permission to access the
6158 * data the Uri points to. The recommended ways to do this are:</li>
6159 * <li>Store the data in your own ContentProvider, making sure that other apps have
6160 * the correct permission to access your provider. The preferred mechanism for
6161 * providing access is to use per-URI permissions which are temporary and only
6162 * grant access to the receiving application. An easy way to create a
6163 * ContentProvider like this is to use the FileProvider helper class.</li>
6164 * <li>Use the system MediaStore. The MediaStore is primarily aimed at video, audio
6165 * and image MIME types, however beginning with Android 3.0 (API level 11) it can
6166 * also store non-media types (see MediaStore.Files for more info). Files can be
6167 * inserted into the MediaStore using scanFile() after which a content:// style
6168 * Uri suitable for sharing is passed to the provided onScanCompleted() callback.
6169 * Note that once added to the system MediaStore the content is accessible to any
6170 * app on the device.</li>
6171 * </ol>
6172 * @return this object for method chaining
6173 */
6174 public Message setData(String dataMimeType, Uri dataUri) {
6175 mDataMimeType = dataMimeType;
6176 mDataUri = dataUri;
6177 return this;
6178 }
6179
Alex Hillsfc737de2016-03-23 17:33:02 -04006180 /**
6181 * Get the text to be used for this message, or the fallback text if a type and content
6182 * Uri have been set
6183 */
6184 public CharSequence getText() {
6185 return mText;
6186 }
6187
6188 /**
6189 * Get the time at which this message arrived
6190 */
6191 public long getTimestamp() {
6192 return mTimestamp;
6193 }
6194
6195 /**
Shane Brennan5a871862017-03-11 13:14:17 -08006196 * Get the extras Bundle for this message.
6197 */
6198 public Bundle getExtras() {
6199 return mExtras;
6200 }
6201
6202 /**
Alex Hillsfc737de2016-03-23 17:33:02 -04006203 * Get the text used to display the contact's name in the messaging experience
6204 */
6205 public CharSequence getSender() {
6206 return mSender;
6207 }
6208
6209 /**
6210 * Get the MIME type of the data pointed to by the Uri
6211 */
6212 public String getDataMimeType() {
6213 return mDataMimeType;
6214 }
6215
6216 /**
6217 * Get the the Uri pointing to the content of the message. Can be null, in which case
6218 * {@see #getText()} is used.
6219 */
6220 public Uri getDataUri() {
6221 return mDataUri;
6222 }
6223
Alex Hillsd9b04d92016-04-11 16:38:16 -04006224 private Bundle toBundle() {
6225 Bundle bundle = new Bundle();
Alex Hillsfc737de2016-03-23 17:33:02 -04006226 if (mText != null) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04006227 bundle.putCharSequence(KEY_TEXT, mText);
Alex Hillsfc737de2016-03-23 17:33:02 -04006228 }
Alex Hillsd9b04d92016-04-11 16:38:16 -04006229 bundle.putLong(KEY_TIMESTAMP, mTimestamp);
Alex Hillsfc737de2016-03-23 17:33:02 -04006230 if (mSender != null) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04006231 bundle.putCharSequence(KEY_SENDER, mSender);
Alex Hillsfc737de2016-03-23 17:33:02 -04006232 }
6233 if (mDataMimeType != null) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04006234 bundle.putString(KEY_DATA_MIME_TYPE, mDataMimeType);
Alex Hillsfc737de2016-03-23 17:33:02 -04006235 }
6236 if (mDataUri != null) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04006237 bundle.putParcelable(KEY_DATA_URI, mDataUri);
Alex Hillsfc737de2016-03-23 17:33:02 -04006238 }
Shane Brennan5a871862017-03-11 13:14:17 -08006239 if (mExtras != null) {
6240 bundle.putBundle(KEY_EXTRAS_BUNDLE, mExtras);
6241 }
Alex Hillsd9b04d92016-04-11 16:38:16 -04006242 return bundle;
Alex Hillsfc737de2016-03-23 17:33:02 -04006243 }
6244
Alex Hillsd9b04d92016-04-11 16:38:16 -04006245 static Bundle[] getBundleArrayForMessages(List<Message> messages) {
6246 Bundle[] bundles = new Bundle[messages.size()];
6247 final int N = messages.size();
6248 for (int i = 0; i < N; i++) {
6249 bundles[i] = messages.get(i).toBundle();
6250 }
6251 return bundles;
6252 }
6253
Adrian Roosdedd1df2016-04-26 16:38:47 -07006254 static List<Message> getMessagesFromBundleArray(Parcelable[] bundles) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04006255 List<Message> messages = new ArrayList<>(bundles.length);
6256 for (int i = 0; i < bundles.length; i++) {
Adrian Roosdedd1df2016-04-26 16:38:47 -07006257 if (bundles[i] instanceof Bundle) {
6258 Message message = getMessageFromBundle((Bundle)bundles[i]);
6259 if (message != null) {
6260 messages.add(message);
6261 }
Alex Hillsd9b04d92016-04-11 16:38:16 -04006262 }
6263 }
6264 return messages;
6265 }
6266
6267 static Message getMessageFromBundle(Bundle bundle) {
6268 try {
Adrian Roosfbddd2c2016-05-13 12:57:20 -07006269 if (!bundle.containsKey(KEY_TEXT) || !bundle.containsKey(KEY_TIMESTAMP)) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04006270 return null;
6271 } else {
6272 Message message = new Message(bundle.getCharSequence(KEY_TEXT),
6273 bundle.getLong(KEY_TIMESTAMP), bundle.getCharSequence(KEY_SENDER));
6274 if (bundle.containsKey(KEY_DATA_MIME_TYPE) &&
6275 bundle.containsKey(KEY_DATA_URI)) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04006276 message.setData(bundle.getString(KEY_DATA_MIME_TYPE),
6277 (Uri) bundle.getParcelable(KEY_DATA_URI));
Alex Hillsfc737de2016-03-23 17:33:02 -04006278 }
Shane Brennan5a871862017-03-11 13:14:17 -08006279 if (bundle.containsKey(KEY_EXTRAS_BUNDLE)) {
6280 message.getExtras().putAll(bundle.getBundle(KEY_EXTRAS_BUNDLE));
6281 }
Alex Hillsd9b04d92016-04-11 16:38:16 -04006282 return message;
6283 }
6284 } catch (ClassCastException e) {
6285 return null;
6286 }
6287 }
Alex Hillsfc737de2016-03-23 17:33:02 -04006288 }
6289 }
6290
6291 /**
Daniel Sandler879c5e02012-04-17 16:46:51 -04006292 * Helper class for generating large-format notifications that include a list of (up to 5) strings.
Joe Malin8d40d042012-11-05 11:36:40 -08006293 *
Robert Ly91c5ce32014-06-08 15:37:00 -07006294 * Here's how you'd set the <code>InboxStyle</code> on a notification:
Daniel Sandler879c5e02012-04-17 16:46:51 -04006295 * <pre class="prettyprint">
Robert Ly91c5ce32014-06-08 15:37:00 -07006296 * Notification notif = new Notification.Builder(mContext)
6297 * .setContentTitle(&quot;5 New mails from &quot; + sender.toString())
6298 * .setContentText(subject)
6299 * .setSmallIcon(R.drawable.new_mail)
6300 * .setLargeIcon(aBitmap)
6301 * .setStyle(new Notification.InboxStyle()
6302 * .addLine(str1)
6303 * .addLine(str2)
6304 * .setContentTitle(&quot;&quot;)
6305 * .setSummaryText(&quot;+3 more&quot;))
6306 * .build();
Daniel Sandler879c5e02012-04-17 16:46:51 -04006307 * </pre>
Joe Malin8d40d042012-11-05 11:36:40 -08006308 *
Daniel Sandler879c5e02012-04-17 16:46:51 -04006309 * @see Notification#bigContentView
6310 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04006311 public static class InboxStyle extends Style {
Daniel Sandler879c5e02012-04-17 16:46:51 -04006312 private ArrayList<CharSequence> mTexts = new ArrayList<CharSequence>(5);
6313
Chris Wrenfbd96ba2012-05-01 12:03:58 -04006314 public InboxStyle() {
6315 }
6316
Adrian Roosf5faf9d2016-05-23 13:56:15 -07006317 /**
6318 * @deprecated use {@code InboxStyle()}.
6319 */
6320 @Deprecated
Daniel Sandler879c5e02012-04-17 16:46:51 -04006321 public InboxStyle(Builder builder) {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04006322 setBuilder(builder);
Daniel Sandler879c5e02012-04-17 16:46:51 -04006323 }
6324
Chris Wrend6297db2012-05-03 16:20:13 -04006325 /**
6326 * Overrides ContentTitle in the big form of the template.
6327 * This defaults to the value passed to setContentTitle().
6328 */
6329 public InboxStyle setBigContentTitle(CharSequence title) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04006330 internalSetBigContentTitle(safeCharSequence(title));
Chris Wrend6297db2012-05-03 16:20:13 -04006331 return this;
6332 }
6333
6334 /**
6335 * Set the first line of text after the detail section in the big form of the template.
6336 */
6337 public InboxStyle setSummaryText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04006338 internalSetSummaryText(safeCharSequence(cs));
Chris Wrend6297db2012-05-03 16:20:13 -04006339 return this;
6340 }
6341
Chris Wren0bd664d2012-08-01 13:56:56 -04006342 /**
6343 * Append a line to the digest section of the Inbox notification.
6344 */
Daniel Sandler879c5e02012-04-17 16:46:51 -04006345 public InboxStyle addLine(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04006346 mTexts.add(safeCharSequence(cs));
Daniel Sandler879c5e02012-04-17 16:46:51 -04006347 return this;
6348 }
6349
Daniel Sandlerf45564e2013-04-15 15:05:08 -04006350 /**
6351 * @hide
6352 */
6353 public void addExtras(Bundle extras) {
6354 super.addExtras(extras);
Christoph Studer4600f9b2014-07-22 22:44:43 +02006355
Daniel Sandlerf45564e2013-04-15 15:05:08 -04006356 CharSequence[] a = new CharSequence[mTexts.size()];
6357 extras.putCharSequenceArray(EXTRA_TEXT_LINES, mTexts.toArray(a));
6358 }
6359
Christoph Studer4600f9b2014-07-22 22:44:43 +02006360 /**
6361 * @hide
6362 */
6363 @Override
6364 protected void restoreFromExtras(Bundle extras) {
6365 super.restoreFromExtras(extras);
6366
6367 mTexts.clear();
6368 if (extras.containsKey(EXTRA_TEXT_LINES)) {
6369 Collections.addAll(mTexts, extras.getCharSequenceArray(EXTRA_TEXT_LINES));
6370 }
6371 }
6372
Julia Reynoldsd9228f12015-10-20 10:37:27 -04006373 /**
6374 * @hide
6375 */
6376 public RemoteViews makeBigContentView() {
Selim Cinekc848c3a2016-01-13 15:27:30 -08006377 // Remove the content text so it disappears unless you have a summary
Christoph Studer4600f9b2014-07-22 22:44:43 +02006378 // Nasty
Julia Reynoldsd9228f12015-10-20 10:37:27 -04006379 CharSequence oldBuilderContentText = mBuilder.mN.extras.getCharSequence(EXTRA_TEXT);
6380 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, null);
Christoph Studer4600f9b2014-07-22 22:44:43 +02006381
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01006382 RemoteViews contentView = getStandardView(mBuilder.getInboxLayoutResource());
Daniel Sandler619738c2012-06-07 16:33:08 -04006383
Julia Reynoldsd9228f12015-10-20 10:37:27 -04006384 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, oldBuilderContentText);
Christoph Studer4600f9b2014-07-22 22:44:43 +02006385
Chris Wrend6297db2012-05-03 16:20:13 -04006386 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 -04006387 R.id.inbox_text4, R.id.inbox_text5, R.id.inbox_text6};
Chris Wrend6297db2012-05-03 16:20:13 -04006388
Chris Wren4ed80d52012-05-17 09:30:03 -04006389 // Make sure all rows are gone in case we reuse a view.
6390 for (int rowId : rowIds) {
6391 contentView.setViewVisibility(rowId, View.GONE);
6392 }
6393
Daniel Sandler879c5e02012-04-17 16:46:51 -04006394 int i=0;
Selim Cinek07c80172016-04-21 16:40:47 -07006395 int topPadding = mBuilder.mContext.getResources().getDimensionPixelSize(
6396 R.dimen.notification_inbox_item_top_padding);
Selim Cinek247fa012016-02-18 09:50:48 -08006397 boolean first = true;
Selim Cinek07c80172016-04-21 16:40:47 -07006398 int onlyViewId = 0;
6399 int maxRows = rowIds.length;
6400 if (mBuilder.mActions.size() > 0) {
6401 maxRows--;
6402 }
6403 while (i < mTexts.size() && i < maxRows) {
Daniel Sandler879c5e02012-04-17 16:46:51 -04006404 CharSequence str = mTexts.get(i);
Selim Cinek07c80172016-04-21 16:40:47 -07006405 if (!TextUtils.isEmpty(str)) {
Daniel Sandler879c5e02012-04-17 16:46:51 -04006406 contentView.setViewVisibility(rowIds[i], View.VISIBLE);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01006407 contentView.setTextViewText(rowIds[i], mBuilder.processLegacyText(str));
Selim Cinek7b9605b2017-01-19 17:36:00 -08006408 mBuilder.setTextViewColorSecondary(contentView, rowIds[i]);
Selim Cinek07c80172016-04-21 16:40:47 -07006409 contentView.setViewPadding(rowIds[i], 0, topPadding, 0, 0);
Selim Cinek247fa012016-02-18 09:50:48 -08006410 handleInboxImageMargin(contentView, rowIds[i], first);
Selim Cinek07c80172016-04-21 16:40:47 -07006411 if (first) {
6412 onlyViewId = rowIds[i];
6413 } else {
6414 onlyViewId = 0;
6415 }
Selim Cinek247fa012016-02-18 09:50:48 -08006416 first = false;
Daniel Sandler879c5e02012-04-17 16:46:51 -04006417 }
6418 i++;
6419 }
Selim Cinek07c80172016-04-21 16:40:47 -07006420 if (onlyViewId != 0) {
6421 // We only have 1 entry, lets make it look like the normal Text of a Bigtext
6422 topPadding = mBuilder.mContext.getResources().getDimensionPixelSize(
6423 R.dimen.notification_text_margin_top);
6424 contentView.setViewPadding(onlyViewId, 0, topPadding, 0, 0);
6425 }
Selim Cinek1e0bf612015-11-20 15:57:26 -08006426
Daniel Sandler879c5e02012-04-17 16:46:51 -04006427 return contentView;
6428 }
Selim Cinek1e0bf612015-11-20 15:57:26 -08006429
Selim Cinek247fa012016-02-18 09:50:48 -08006430 private void handleInboxImageMargin(RemoteViews contentView, int id, boolean first) {
Selim Cinek1e0bf612015-11-20 15:57:26 -08006431 int endMargin = 0;
Selim Cinek247fa012016-02-18 09:50:48 -08006432 if (first) {
6433 final int max = mBuilder.mN.extras.getInt(EXTRA_PROGRESS_MAX, 0);
6434 final boolean ind = mBuilder.mN.extras.getBoolean(EXTRA_PROGRESS_INDETERMINATE);
6435 boolean hasProgress = max != 0 || ind;
Selim Cinek279fa862016-06-14 10:57:25 -07006436 if (mBuilder.mN.hasLargeIcon() && !hasProgress) {
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006437 endMargin = R.dimen.notification_content_picture_margin;
Selim Cinek247fa012016-02-18 09:50:48 -08006438 }
Selim Cinek1e0bf612015-11-20 15:57:26 -08006439 }
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006440 contentView.setViewLayoutMarginEndDimen(id, endMargin);
Selim Cinek1e0bf612015-11-20 15:57:26 -08006441 }
Daniel Sandler879c5e02012-04-17 16:46:51 -04006442 }
Dan Sandler842dd772014-05-15 09:36:47 -04006443
6444 /**
6445 * Notification style for media playback notifications.
6446 *
6447 * In the expanded form, {@link Notification#bigContentView}, up to 5
6448 * {@link Notification.Action}s specified with
Dan Sandler86647982015-05-13 23:41:13 -04006449 * {@link Notification.Builder#addAction(Action) addAction} will be
Dan Sandler842dd772014-05-15 09:36:47 -04006450 * shown as icon-only pushbuttons, suitable for transport controls. The Bitmap given to
6451 * {@link Notification.Builder#setLargeIcon(android.graphics.Bitmap) setLargeIcon()} will be
6452 * treated as album artwork.
Selim Cinek99104832017-01-25 14:47:33 -08006453 * <p>
Dan Sandler842dd772014-05-15 09:36:47 -04006454 * Unlike the other styles provided here, MediaStyle can also modify the standard-size
6455 * {@link Notification#contentView}; by providing action indices to
Christoph Studerfde6f4d2014-12-12 13:23:26 +01006456 * {@link #setShowActionsInCompactView(int...)} you can promote up to 3 actions to be displayed
Dan Sandler842dd772014-05-15 09:36:47 -04006457 * in the standard view alongside the usual content.
Selim Cinek99104832017-01-25 14:47:33 -08006458 * <p>
Bryan Mawhinney6be8de32014-07-18 10:35:12 +01006459 * Notifications created with MediaStyle will have their category set to
6460 * {@link Notification#CATEGORY_TRANSPORT CATEGORY_TRANSPORT} unless you set a different
6461 * category using {@link Notification.Builder#setCategory(String) setCategory()}.
Selim Cinek99104832017-01-25 14:47:33 -08006462 * <p>
Jeff Browndba34ba2014-06-24 20:46:03 -07006463 * Finally, if you attach a {@link android.media.session.MediaSession.Token} using
6464 * {@link android.app.Notification.MediaStyle#setMediaSession(MediaSession.Token)},
Dan Sandler842dd772014-05-15 09:36:47 -04006465 * the System UI can identify this as a notification representing an active media session
6466 * and respond accordingly (by showing album artwork in the lockscreen, for example).
6467 *
Selim Cinek99104832017-01-25 14:47:33 -08006468 * <p>
6469 * Starting at {@link android.os.Build.VERSION_CODES#O Android O} any notification that has a
6470 * media session attached with {@link #setMediaSession(MediaSession.Token)} will be colorized.
6471 * You can opt-out of this behavior by using {@link Notification.Builder#setColorized(boolean)}.
6472 * <p>
6473 *
Dan Sandler842dd772014-05-15 09:36:47 -04006474 * To use this style with your Notification, feed it to
6475 * {@link Notification.Builder#setStyle(android.app.Notification.Style)} like so:
6476 * <pre class="prettyprint">
6477 * Notification noti = new Notification.Builder()
6478 * .setSmallIcon(R.drawable.ic_stat_player)
Christoph Studere935fe92014-11-24 14:18:06 +01006479 * .setContentTitle(&quot;Track title&quot;)
6480 * .setContentText(&quot;Artist - Album&quot;)
6481 * .setLargeIcon(albumArtBitmap))
Bryan Mawhinney6be8de32014-07-18 10:35:12 +01006482 * .setStyle(<b>new Notification.MediaStyle()</b>
6483 * .setMediaSession(mySession))
Dan Sandler842dd772014-05-15 09:36:47 -04006484 * .build();
6485 * </pre>
6486 *
6487 * @see Notification#bigContentView
Selim Cinek99104832017-01-25 14:47:33 -08006488 * @see Notification.Builder#setColorized(boolean)
Dan Sandler842dd772014-05-15 09:36:47 -04006489 */
6490 public static class MediaStyle extends Style {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006491 static final int MAX_MEDIA_BUTTONS_IN_COMPACT = 3;
Dan Sandler842dd772014-05-15 09:36:47 -04006492 static final int MAX_MEDIA_BUTTONS = 5;
6493
6494 private int[] mActionsToShowInCompact = null;
Jeff Browndba34ba2014-06-24 20:46:03 -07006495 private MediaSession.Token mToken;
Dan Sandler842dd772014-05-15 09:36:47 -04006496
6497 public MediaStyle() {
6498 }
6499
Adrian Roosf5faf9d2016-05-23 13:56:15 -07006500 /**
6501 * @deprecated use {@code MediaStyle()}.
6502 */
6503 @Deprecated
Dan Sandler842dd772014-05-15 09:36:47 -04006504 public MediaStyle(Builder builder) {
6505 setBuilder(builder);
6506 }
6507
6508 /**
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006509 * 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 -04006510 * notification view.
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006511 *
6512 * @param actions the indices of the actions to show in the compact notification view
Dan Sandler842dd772014-05-15 09:36:47 -04006513 */
6514 public MediaStyle setShowActionsInCompactView(int...actions) {
6515 mActionsToShowInCompact = actions;
6516 return this;
6517 }
6518
6519 /**
Jeff Browndba34ba2014-06-24 20:46:03 -07006520 * Attach a {@link android.media.session.MediaSession.Token} to this Notification
6521 * to provide additional playback information and control to the SystemUI.
Dan Sandler842dd772014-05-15 09:36:47 -04006522 */
Jeff Browndba34ba2014-06-24 20:46:03 -07006523 public MediaStyle setMediaSession(MediaSession.Token token) {
Dan Sandler842dd772014-05-15 09:36:47 -04006524 mToken = token;
6525 return this;
6526 }
6527
Christoph Studer4600f9b2014-07-22 22:44:43 +02006528 /**
6529 * @hide
6530 */
Dan Sandler842dd772014-05-15 09:36:47 -04006531 @Override
6532 public Notification buildStyled(Notification wip) {
Christoph Studer4600f9b2014-07-22 22:44:43 +02006533 super.buildStyled(wip);
Bryan Mawhinney6be8de32014-07-18 10:35:12 +01006534 if (wip.category == null) {
6535 wip.category = Notification.CATEGORY_TRANSPORT;
6536 }
Dan Sandler842dd772014-05-15 09:36:47 -04006537 return wip;
6538 }
6539
Christoph Studer4600f9b2014-07-22 22:44:43 +02006540 /**
6541 * @hide
6542 */
6543 @Override
Selim Cinek7d1009b2017-01-25 15:28:28 -08006544 public RemoteViews makeContentView(boolean increasedHeight) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04006545 return makeMediaContentView();
Christoph Studer4600f9b2014-07-22 22:44:43 +02006546 }
6547
6548 /**
6549 * @hide
6550 */
6551 @Override
Julia Reynoldsd9228f12015-10-20 10:37:27 -04006552 public RemoteViews makeBigContentView() {
6553 return makeMediaBigContentView();
Christoph Studer4600f9b2014-07-22 22:44:43 +02006554 }
6555
Selim Cinekcc10bfb2016-02-10 16:24:21 -08006556 /**
6557 * @hide
6558 */
6559 @Override
Selim Cinek87ed69b2017-02-09 15:59:43 -08006560 public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
Selim Cinekcc10bfb2016-02-10 16:24:21 -08006561 RemoteViews expanded = makeMediaBigContentView();
6562 return expanded != null ? expanded : makeMediaContentView();
6563 }
6564
Dan Sandler842dd772014-05-15 09:36:47 -04006565 /** @hide */
6566 @Override
6567 public void addExtras(Bundle extras) {
6568 super.addExtras(extras);
6569
6570 if (mToken != null) {
6571 extras.putParcelable(EXTRA_MEDIA_SESSION, mToken);
6572 }
Bryan Mawhinneye191f902014-07-22 12:50:09 +01006573 if (mActionsToShowInCompact != null) {
6574 extras.putIntArray(EXTRA_COMPACT_ACTIONS, mActionsToShowInCompact);
6575 }
Dan Sandler842dd772014-05-15 09:36:47 -04006576 }
6577
Christoph Studer4600f9b2014-07-22 22:44:43 +02006578 /**
6579 * @hide
6580 */
6581 @Override
6582 protected void restoreFromExtras(Bundle extras) {
6583 super.restoreFromExtras(extras);
6584
6585 if (extras.containsKey(EXTRA_MEDIA_SESSION)) {
6586 mToken = extras.getParcelable(EXTRA_MEDIA_SESSION);
6587 }
6588 if (extras.containsKey(EXTRA_COMPACT_ACTIONS)) {
6589 mActionsToShowInCompact = extras.getIntArray(EXTRA_COMPACT_ACTIONS);
6590 }
6591 }
6592
Selim Cinek5bf069a2015-11-10 19:14:27 -05006593 private RemoteViews generateMediaActionButton(Action action, int color) {
Dan Sandler842dd772014-05-15 09:36:47 -04006594 final boolean tombstone = (action.actionIntent == null);
Selim Cinekf33b1112015-07-15 17:45:11 -07006595 RemoteViews button = new BuilderRemoteViews(mBuilder.mContext.getApplicationInfo(),
Alan Viverette3cb07a462014-06-06 14:19:53 -07006596 R.layout.notification_material_media_action);
Dan Sandler68079d52015-07-22 10:45:30 -04006597 button.setImageViewIcon(R.id.action0, action.getIcon());
Selim Cinek5bf069a2015-11-10 19:14:27 -05006598 button.setDrawableParameters(R.id.action0, false, -1, color, PorterDuff.Mode.SRC_ATOP,
6599 -1);
Dan Sandler842dd772014-05-15 09:36:47 -04006600 if (!tombstone) {
6601 button.setOnClickPendingIntent(R.id.action0, action.actionIntent);
6602 }
6603 button.setContentDescription(R.id.action0, action.title);
6604 return button;
6605 }
6606
6607 private RemoteViews makeMediaContentView() {
6608 RemoteViews view = mBuilder.applyStandardTemplate(
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006609 R.layout.notification_template_material_media, false /* hasProgress */);
Dan Sandler842dd772014-05-15 09:36:47 -04006610
6611 final int numActions = mBuilder.mActions.size();
6612 final int N = mActionsToShowInCompact == null
6613 ? 0
6614 : Math.min(mActionsToShowInCompact.length, MAX_MEDIA_BUTTONS_IN_COMPACT);
6615 if (N > 0) {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006616 view.removeAllViews(com.android.internal.R.id.media_actions);
Dan Sandler842dd772014-05-15 09:36:47 -04006617 for (int i = 0; i < N; i++) {
6618 if (i >= numActions) {
6619 throw new IllegalArgumentException(String.format(
6620 "setShowActionsInCompactView: action %d out of bounds (max %d)",
6621 i, numActions - 1));
6622 }
6623
6624 final Action action = mBuilder.mActions.get(mActionsToShowInCompact[i]);
Selim Cinek5bf069a2015-11-10 19:14:27 -05006625 final RemoteViews button = generateMediaActionButton(action,
Selim Cinek99104832017-01-25 14:47:33 -08006626 getPrimaryHighlightColor());
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006627 view.addView(com.android.internal.R.id.media_actions, button);
Dan Sandler842dd772014-05-15 09:36:47 -04006628 }
6629 }
Selim Cinekfdc738f2016-01-27 20:04:27 -08006630 handleImage(view);
6631 // handle the content margin
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006632 int endMargin = R.dimen.notification_content_margin_end;
Selim Cinek279fa862016-06-14 10:57:25 -07006633 if (mBuilder.mN.hasLargeIcon()) {
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006634 endMargin = R.dimen.notification_content_plus_picture_margin_end;
Selim Cinekfdc738f2016-01-27 20:04:27 -08006635 }
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006636 view.setViewLayoutMarginEndDimen(R.id.notification_main_column, endMargin);
Dan Sandler842dd772014-05-15 09:36:47 -04006637 return view;
6638 }
6639
Selim Cinek99104832017-01-25 14:47:33 -08006640 private int getPrimaryHighlightColor() {
6641 return mBuilder.getPrimaryHighlightColor();
6642 }
6643
Dan Sandler842dd772014-05-15 09:36:47 -04006644 private RemoteViews makeMediaBigContentView() {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006645 final int actionCount = Math.min(mBuilder.mActions.size(), MAX_MEDIA_BUTTONS);
Selim Cinekcc10bfb2016-02-10 16:24:21 -08006646 // Dont add an expanded view if there is no more content to be revealed
6647 int actionsInCompact = mActionsToShowInCompact == null
6648 ? 0
6649 : Math.min(mActionsToShowInCompact.length, MAX_MEDIA_BUTTONS_IN_COMPACT);
Selim Cinek279fa862016-06-14 10:57:25 -07006650 if (!mBuilder.mN.hasLargeIcon() && actionCount <= actionsInCompact) {
Selim Cinekcc10bfb2016-02-10 16:24:21 -08006651 return null;
6652 }
Selim Cinek5bf069a2015-11-10 19:14:27 -05006653 RemoteViews big = mBuilder.applyStandardTemplate(
6654 R.layout.notification_template_material_big_media,
6655 false);
Dan Sandler842dd772014-05-15 09:36:47 -04006656
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006657 if (actionCount > 0) {
6658 big.removeAllViews(com.android.internal.R.id.media_actions);
6659 for (int i = 0; i < actionCount; i++) {
Selim Cinek5bf069a2015-11-10 19:14:27 -05006660 final RemoteViews button = generateMediaActionButton(mBuilder.mActions.get(i),
Selim Cinek99104832017-01-25 14:47:33 -08006661 getPrimaryHighlightColor());
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006662 big.addView(com.android.internal.R.id.media_actions, button);
Dan Sandler842dd772014-05-15 09:36:47 -04006663 }
6664 }
Selim Cinek5bf069a2015-11-10 19:14:27 -05006665 handleImage(big);
Dan Sandler842dd772014-05-15 09:36:47 -04006666 return big;
6667 }
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006668
Selim Cinek5bf069a2015-11-10 19:14:27 -05006669 private void handleImage(RemoteViews contentView) {
Selim Cinek279fa862016-06-14 10:57:25 -07006670 if (mBuilder.mN.hasLargeIcon()) {
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006671 contentView.setViewLayoutMarginEndDimen(R.id.line1, 0);
6672 contentView.setViewLayoutMarginEndDimen(R.id.text, 0);
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006673 }
6674 }
6675
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006676 /**
6677 * @hide
6678 */
6679 @Override
6680 protected boolean hasProgress() {
6681 return false;
6682 }
Dan Sandler842dd772014-05-15 09:36:47 -04006683 }
Griff Hazen61a9e862014-05-22 16:05:19 -07006684
Selim Cinek593610c2016-02-16 18:42:57 -08006685 /**
6686 * Notification style for custom views that are decorated by the system
6687 *
6688 * <p>Instead of providing a notification that is completely custom, a developer can set this
6689 * style and still obtain system decorations like the notification header with the expand
6690 * affordance and actions.
6691 *
6692 * <p>Use {@link android.app.Notification.Builder#setCustomContentView(RemoteViews)},
6693 * {@link android.app.Notification.Builder#setCustomBigContentView(RemoteViews)} and
6694 * {@link android.app.Notification.Builder#setCustomHeadsUpContentView(RemoteViews)} to set the
6695 * corresponding custom views to display.
6696 *
6697 * To use this style with your Notification, feed it to
6698 * {@link Notification.Builder#setStyle(android.app.Notification.Style)} like so:
6699 * <pre class="prettyprint">
6700 * Notification noti = new Notification.Builder()
6701 * .setSmallIcon(R.drawable.ic_stat_player)
6702 * .setLargeIcon(albumArtBitmap))
6703 * .setCustomContentView(contentView);
6704 * .setStyle(<b>new Notification.DecoratedCustomViewStyle()</b>)
6705 * .build();
6706 * </pre>
6707 */
6708 public static class DecoratedCustomViewStyle extends Style {
6709
6710 public DecoratedCustomViewStyle() {
6711 }
6712
Selim Cinek593610c2016-02-16 18:42:57 -08006713 /**
6714 * @hide
6715 */
6716 public boolean displayCustomViewInline() {
6717 return true;
6718 }
6719
6720 /**
6721 * @hide
6722 */
6723 @Override
Selim Cinek7d1009b2017-01-25 15:28:28 -08006724 public RemoteViews makeContentView(boolean increasedHeight) {
Selim Cinek593610c2016-02-16 18:42:57 -08006725 return makeStandardTemplateWithCustomContent(mBuilder.mN.contentView);
6726 }
6727
6728 /**
6729 * @hide
6730 */
6731 @Override
6732 public RemoteViews makeBigContentView() {
6733 return makeDecoratedBigContentView();
6734 }
6735
6736 /**
6737 * @hide
6738 */
6739 @Override
Selim Cinek87ed69b2017-02-09 15:59:43 -08006740 public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
Selim Cinek593610c2016-02-16 18:42:57 -08006741 return makeDecoratedHeadsUpContentView();
6742 }
6743
Selim Cinek593610c2016-02-16 18:42:57 -08006744 private RemoteViews makeDecoratedHeadsUpContentView() {
6745 RemoteViews headsUpContentView = mBuilder.mN.headsUpContentView == null
6746 ? mBuilder.mN.contentView
6747 : mBuilder.mN.headsUpContentView;
6748 if (mBuilder.mActions.size() == 0) {
6749 return makeStandardTemplateWithCustomContent(headsUpContentView);
6750 }
6751 RemoteViews remoteViews = mBuilder.applyStandardTemplateWithActions(
6752 mBuilder.getBigBaseLayoutResource());
Selim Cinek247fa012016-02-18 09:50:48 -08006753 buildIntoRemoteViewContent(remoteViews, headsUpContentView);
Selim Cinek593610c2016-02-16 18:42:57 -08006754 return remoteViews;
6755 }
6756
Selim Cinek593610c2016-02-16 18:42:57 -08006757 private RemoteViews makeStandardTemplateWithCustomContent(RemoteViews customContent) {
6758 RemoteViews remoteViews = mBuilder.applyStandardTemplate(
6759 mBuilder.getBaseLayoutResource());
Selim Cinek247fa012016-02-18 09:50:48 -08006760 buildIntoRemoteViewContent(remoteViews, customContent);
Selim Cinek593610c2016-02-16 18:42:57 -08006761 return remoteViews;
6762 }
6763
Selim Cinek593610c2016-02-16 18:42:57 -08006764 private RemoteViews makeDecoratedBigContentView() {
6765 RemoteViews bigContentView = mBuilder.mN.bigContentView == null
6766 ? mBuilder.mN.contentView
6767 : mBuilder.mN.bigContentView;
6768 if (mBuilder.mActions.size() == 0) {
6769 return makeStandardTemplateWithCustomContent(bigContentView);
6770 }
6771 RemoteViews remoteViews = mBuilder.applyStandardTemplateWithActions(
6772 mBuilder.getBigBaseLayoutResource());
Selim Cinek247fa012016-02-18 09:50:48 -08006773 buildIntoRemoteViewContent(remoteViews, bigContentView);
Selim Cinek593610c2016-02-16 18:42:57 -08006774 return remoteViews;
6775 }
Selim Cinek247fa012016-02-18 09:50:48 -08006776
6777 private void buildIntoRemoteViewContent(RemoteViews remoteViews,
6778 RemoteViews customContent) {
Adrian Roos5081c0d2016-02-26 16:04:19 -08006779 if (customContent != null) {
Selim Cinekf91017e2016-03-14 12:25:09 -07006780 // Need to clone customContent before adding, because otherwise it can no longer be
6781 // parceled independently of remoteViews.
Adrian Roos5081c0d2016-02-26 16:04:19 -08006782 customContent = customContent.clone();
Anthony Chen8f5f3582017-04-11 11:18:37 -07006783 remoteViews.removeAllViewsExceptId(R.id.notification_main_column, R.id.progress);
6784 remoteViews.addView(R.id.notification_main_column, customContent, 0 /* index */);
Adrian Roos5081c0d2016-02-26 16:04:19 -08006785 }
Selim Cinek247fa012016-02-18 09:50:48 -08006786 // also update the end margin if there is an image
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006787 int endMargin = R.dimen.notification_content_margin_end;
Selim Cinek279fa862016-06-14 10:57:25 -07006788 if (mBuilder.mN.hasLargeIcon()) {
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006789 endMargin = R.dimen.notification_content_plus_picture_margin_end;
Selim Cinek247fa012016-02-18 09:50:48 -08006790 }
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006791 remoteViews.setViewLayoutMarginEndDimen(R.id.notification_main_column, endMargin);
Selim Cinek247fa012016-02-18 09:50:48 -08006792 }
Selim Cinek593610c2016-02-16 18:42:57 -08006793 }
6794
Selim Cinek03eb3b72016-02-18 10:39:45 -08006795 /**
6796 * Notification style for media custom views that are decorated by the system
6797 *
6798 * <p>Instead of providing a media notification that is completely custom, a developer can set
6799 * this style and still obtain system decorations like the notification header with the expand
6800 * affordance and actions.
6801 *
6802 * <p>Use {@link android.app.Notification.Builder#setCustomContentView(RemoteViews)},
6803 * {@link android.app.Notification.Builder#setCustomBigContentView(RemoteViews)} and
6804 * {@link android.app.Notification.Builder#setCustomHeadsUpContentView(RemoteViews)} to set the
6805 * corresponding custom views to display.
Selim Cinek99104832017-01-25 14:47:33 -08006806 * <p>
6807 * Contrary to {@link MediaStyle} a developer has to opt-in to the colorizing of the
6808 * notification by using {@link Notification.Builder#setColorized(boolean)}.
6809 * <p>
Selim Cinek03eb3b72016-02-18 10:39:45 -08006810 * To use this style with your Notification, feed it to
6811 * {@link Notification.Builder#setStyle(android.app.Notification.Style)} like so:
6812 * <pre class="prettyprint">
6813 * Notification noti = new Notification.Builder()
6814 * .setSmallIcon(R.drawable.ic_stat_player)
6815 * .setLargeIcon(albumArtBitmap))
6816 * .setCustomContentView(contentView);
6817 * .setStyle(<b>new Notification.DecoratedMediaCustomViewStyle()</b>
6818 * .setMediaSession(mySession))
6819 * .build();
6820 * </pre>
6821 *
6822 * @see android.app.Notification.DecoratedCustomViewStyle
6823 * @see android.app.Notification.MediaStyle
6824 */
6825 public static class DecoratedMediaCustomViewStyle extends MediaStyle {
6826
6827 public DecoratedMediaCustomViewStyle() {
6828 }
6829
Selim Cinek03eb3b72016-02-18 10:39:45 -08006830 /**
6831 * @hide
6832 */
6833 public boolean displayCustomViewInline() {
6834 return true;
6835 }
6836
6837 /**
6838 * @hide
6839 */
6840 @Override
Selim Cinek7d1009b2017-01-25 15:28:28 -08006841 public RemoteViews makeContentView(boolean increasedHeight) {
6842 RemoteViews remoteViews = super.makeContentView(false /* increasedHeight */);
Selim Cinek03eb3b72016-02-18 10:39:45 -08006843 return buildIntoRemoteView(remoteViews, R.id.notification_content_container,
6844 mBuilder.mN.contentView);
6845 }
6846
6847 /**
6848 * @hide
6849 */
6850 @Override
6851 public RemoteViews makeBigContentView() {
6852 RemoteViews customRemoteView = mBuilder.mN.bigContentView != null
6853 ? mBuilder.mN.bigContentView
6854 : mBuilder.mN.contentView;
6855 return makeBigContentViewWithCustomContent(customRemoteView);
6856 }
6857
6858 private RemoteViews makeBigContentViewWithCustomContent(RemoteViews customRemoteView) {
6859 RemoteViews remoteViews = super.makeBigContentView();
6860 if (remoteViews != null) {
6861 return buildIntoRemoteView(remoteViews, R.id.notification_main_column,
6862 customRemoteView);
6863 } else if (customRemoteView != mBuilder.mN.contentView){
Selim Cinek7d1009b2017-01-25 15:28:28 -08006864 remoteViews = super.makeContentView(false /* increasedHeight */);
Selim Cinek03eb3b72016-02-18 10:39:45 -08006865 return buildIntoRemoteView(remoteViews, R.id.notification_content_container,
6866 customRemoteView);
6867 } else {
6868 return null;
6869 }
6870 }
6871
6872 /**
6873 * @hide
6874 */
6875 @Override
Selim Cinek87ed69b2017-02-09 15:59:43 -08006876 public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
Selim Cinek03eb3b72016-02-18 10:39:45 -08006877 RemoteViews customRemoteView = mBuilder.mN.headsUpContentView != null
6878 ? mBuilder.mN.headsUpContentView
6879 : mBuilder.mN.contentView;
6880 return makeBigContentViewWithCustomContent(customRemoteView);
6881 }
6882
6883 private RemoteViews buildIntoRemoteView(RemoteViews remoteViews, int id,
6884 RemoteViews customContent) {
Adrian Roos5081c0d2016-02-26 16:04:19 -08006885 if (customContent != null) {
Selim Cinekf91017e2016-03-14 12:25:09 -07006886 // Need to clone customContent before adding, because otherwise it can no longer be
6887 // parceled independently of remoteViews.
Adrian Roos5081c0d2016-02-26 16:04:19 -08006888 customContent = customContent.clone();
Selim Cinekf91017e2016-03-14 12:25:09 -07006889 remoteViews.removeAllViews(id);
6890 remoteViews.addView(id, customContent);
Adrian Roos5081c0d2016-02-26 16:04:19 -08006891 }
Selim Cinek03eb3b72016-02-18 10:39:45 -08006892 return remoteViews;
6893 }
6894 }
6895
Christoph Studer4600f9b2014-07-22 22:44:43 +02006896 // When adding a new Style subclass here, don't forget to update
6897 // Builder.getNotificationStyleClass.
6898
Griff Hazen61a9e862014-05-22 16:05:19 -07006899 /**
6900 * Extender interface for use with {@link Builder#extend}. Extenders may be used to add
6901 * metadata or change options on a notification builder.
6902 */
6903 public interface Extender {
6904 /**
6905 * Apply this extender to a notification builder.
6906 * @param builder the builder to be modified.
6907 * @return the build object for chaining.
6908 */
6909 public Builder extend(Builder builder);
6910 }
6911
6912 /**
6913 * Helper class to add wearable extensions to notifications.
6914 * <p class="note"> See
6915 * <a href="{@docRoot}wear/notifications/creating.html">Creating Notifications
6916 * for Android Wear</a> for more information on how to use this class.
6917 * <p>
6918 * To create a notification with wearable extensions:
6919 * <ol>
6920 * <li>Create a {@link android.app.Notification.Builder}, setting any desired
6921 * properties.
6922 * <li>Create a {@link android.app.Notification.WearableExtender}.
6923 * <li>Set wearable-specific properties using the
6924 * {@code add} and {@code set} methods of {@link android.app.Notification.WearableExtender}.
6925 * <li>Call {@link android.app.Notification.Builder#extend} to apply the extensions to a
6926 * notification.
6927 * <li>Post the notification to the notification system with the
6928 * {@code NotificationManager.notify(...)} methods.
6929 * </ol>
6930 *
6931 * <pre class="prettyprint">
6932 * Notification notif = new Notification.Builder(mContext)
6933 * .setContentTitle(&quot;New mail from &quot; + sender.toString())
6934 * .setContentText(subject)
6935 * .setSmallIcon(R.drawable.new_mail)
6936 * .extend(new Notification.WearableExtender()
6937 * .setContentIcon(R.drawable.new_mail))
6938 * .build();
6939 * NotificationManager notificationManger =
6940 * (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
6941 * notificationManger.notify(0, notif);</pre>
6942 *
6943 * <p>Wearable extensions can be accessed on an existing notification by using the
6944 * {@code WearableExtender(Notification)} constructor,
6945 * and then using the {@code get} methods to access values.
6946 *
6947 * <pre class="prettyprint">
6948 * Notification.WearableExtender wearableExtender = new Notification.WearableExtender(
6949 * notification);
Griff Hazen14f57992014-05-26 09:07:14 -07006950 * List&lt;Notification&gt; pages = wearableExtender.getPages();</pre>
Griff Hazen61a9e862014-05-22 16:05:19 -07006951 */
6952 public static final class WearableExtender implements Extender {
6953 /**
6954 * Sentinel value for an action index that is unset.
6955 */
6956 public static final int UNSET_ACTION_INDEX = -1;
6957
6958 /**
6959 * Size value for use with {@link #setCustomSizePreset} to show this notification with
6960 * default sizing.
6961 * <p>For custom display notifications created using {@link #setDisplayIntent},
Paul Soulosaa4f4bf2015-08-04 11:59:45 -07006962 * the default is {@link #SIZE_MEDIUM}. All other notifications size automatically based
Griff Hazen61a9e862014-05-22 16:05:19 -07006963 * on their content.
6964 */
6965 public static final int SIZE_DEFAULT = 0;
6966
6967 /**
6968 * Size value for use with {@link #setCustomSizePreset} to show this notification
6969 * with an extra small size.
6970 * <p>This value is only applicable for custom display notifications created using
6971 * {@link #setDisplayIntent}.
6972 */
6973 public static final int SIZE_XSMALL = 1;
6974
6975 /**
6976 * Size value for use with {@link #setCustomSizePreset} to show this notification
6977 * with a small size.
6978 * <p>This value is only applicable for custom display notifications created using
6979 * {@link #setDisplayIntent}.
6980 */
6981 public static final int SIZE_SMALL = 2;
6982
6983 /**
6984 * Size value for use with {@link #setCustomSizePreset} to show this notification
6985 * with a medium size.
6986 * <p>This value is only applicable for custom display notifications created using
6987 * {@link #setDisplayIntent}.
6988 */
6989 public static final int SIZE_MEDIUM = 3;
6990
6991 /**
6992 * Size value for use with {@link #setCustomSizePreset} to show this notification
6993 * with a large size.
6994 * <p>This value is only applicable for custom display notifications created using
6995 * {@link #setDisplayIntent}.
6996 */
6997 public static final int SIZE_LARGE = 4;
6998
Griff Hazend5f11f92014-05-27 15:40:09 -07006999 /**
7000 * Size value for use with {@link #setCustomSizePreset} to show this notification
7001 * full screen.
7002 * <p>This value is only applicable for custom display notifications created using
7003 * {@link #setDisplayIntent}.
7004 */
7005 public static final int SIZE_FULL_SCREEN = 5;
7006
Griff Hazen5f2edfc2014-09-29 16:28:44 -07007007 /**
7008 * Sentinel value for use with {@link #setHintScreenTimeout} to keep the screen on for a
7009 * short amount of time when this notification is displayed on the screen. This
7010 * is the default value.
7011 */
7012 public static final int SCREEN_TIMEOUT_SHORT = 0;
7013
7014 /**
7015 * Sentinel value for use with {@link #setHintScreenTimeout} to keep the screen on
7016 * for a longer amount of time when this notification is displayed on the screen.
7017 */
7018 public static final int SCREEN_TIMEOUT_LONG = -1;
7019
Griff Hazen61a9e862014-05-22 16:05:19 -07007020 /** Notification extra which contains wearable extensions */
7021 private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS";
7022
Pete Gastaf6781d2014-10-07 15:17:05 -04007023 // Keys within EXTRA_WEARABLE_EXTENSIONS for wearable options.
Griff Hazen61a9e862014-05-22 16:05:19 -07007024 private static final String KEY_ACTIONS = "actions";
7025 private static final String KEY_FLAGS = "flags";
7026 private static final String KEY_DISPLAY_INTENT = "displayIntent";
7027 private static final String KEY_PAGES = "pages";
7028 private static final String KEY_BACKGROUND = "background";
7029 private static final String KEY_CONTENT_ICON = "contentIcon";
7030 private static final String KEY_CONTENT_ICON_GRAVITY = "contentIconGravity";
7031 private static final String KEY_CONTENT_ACTION_INDEX = "contentActionIndex";
7032 private static final String KEY_CUSTOM_SIZE_PRESET = "customSizePreset";
7033 private static final String KEY_CUSTOM_CONTENT_HEIGHT = "customContentHeight";
7034 private static final String KEY_GRAVITY = "gravity";
Griff Hazen5f2edfc2014-09-29 16:28:44 -07007035 private static final String KEY_HINT_SCREEN_TIMEOUT = "hintScreenTimeout";
Nadia Benbernou948627e2016-04-14 14:41:08 -04007036 private static final String KEY_DISMISSAL_ID = "dismissalId";
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04007037 private static final String KEY_BRIDGE_TAG = "bridgeTag";
Griff Hazen61a9e862014-05-22 16:05:19 -07007038
7039 // Flags bitwise-ored to mFlags
7040 private static final int FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE = 0x1;
7041 private static final int FLAG_HINT_HIDE_ICON = 1 << 1;
7042 private static final int FLAG_HINT_SHOW_BACKGROUND_ONLY = 1 << 2;
7043 private static final int FLAG_START_SCROLL_BOTTOM = 1 << 3;
Griff Hazen5f2edfc2014-09-29 16:28:44 -07007044 private static final int FLAG_HINT_AVOID_BACKGROUND_CLIPPING = 1 << 4;
Alex Hills4bcb06b2016-04-05 14:26:25 -04007045 private static final int FLAG_BIG_PICTURE_AMBIENT = 1 << 5;
Alex Hills9ab3a232016-04-05 14:54:56 -04007046 private static final int FLAG_HINT_CONTENT_INTENT_LAUNCHES_ACTIVITY = 1 << 6;
Griff Hazen61a9e862014-05-22 16:05:19 -07007047
7048 // Default value for flags integer
7049 private static final int DEFAULT_FLAGS = FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE;
7050
7051 private static final int DEFAULT_CONTENT_ICON_GRAVITY = Gravity.END;
7052 private static final int DEFAULT_GRAVITY = Gravity.BOTTOM;
7053
7054 private ArrayList<Action> mActions = new ArrayList<Action>();
7055 private int mFlags = DEFAULT_FLAGS;
7056 private PendingIntent mDisplayIntent;
7057 private ArrayList<Notification> mPages = new ArrayList<Notification>();
7058 private Bitmap mBackground;
7059 private int mContentIcon;
7060 private int mContentIconGravity = DEFAULT_CONTENT_ICON_GRAVITY;
7061 private int mContentActionIndex = UNSET_ACTION_INDEX;
7062 private int mCustomSizePreset = SIZE_DEFAULT;
7063 private int mCustomContentHeight;
7064 private int mGravity = DEFAULT_GRAVITY;
Griff Hazen5f2edfc2014-09-29 16:28:44 -07007065 private int mHintScreenTimeout;
Nadia Benbernou948627e2016-04-14 14:41:08 -04007066 private String mDismissalId;
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04007067 private String mBridgeTag;
Griff Hazen61a9e862014-05-22 16:05:19 -07007068
7069 /**
7070 * Create a {@link android.app.Notification.WearableExtender} with default
7071 * options.
7072 */
7073 public WearableExtender() {
7074 }
7075
7076 public WearableExtender(Notification notif) {
7077 Bundle wearableBundle = notif.extras.getBundle(EXTRA_WEARABLE_EXTENSIONS);
7078 if (wearableBundle != null) {
7079 List<Action> actions = wearableBundle.getParcelableArrayList(KEY_ACTIONS);
7080 if (actions != null) {
7081 mActions.addAll(actions);
7082 }
7083
7084 mFlags = wearableBundle.getInt(KEY_FLAGS, DEFAULT_FLAGS);
7085 mDisplayIntent = wearableBundle.getParcelable(KEY_DISPLAY_INTENT);
7086
7087 Notification[] pages = getNotificationArrayFromBundle(
7088 wearableBundle, KEY_PAGES);
7089 if (pages != null) {
7090 Collections.addAll(mPages, pages);
7091 }
7092
7093 mBackground = wearableBundle.getParcelable(KEY_BACKGROUND);
7094 mContentIcon = wearableBundle.getInt(KEY_CONTENT_ICON);
7095 mContentIconGravity = wearableBundle.getInt(KEY_CONTENT_ICON_GRAVITY,
7096 DEFAULT_CONTENT_ICON_GRAVITY);
7097 mContentActionIndex = wearableBundle.getInt(KEY_CONTENT_ACTION_INDEX,
7098 UNSET_ACTION_INDEX);
7099 mCustomSizePreset = wearableBundle.getInt(KEY_CUSTOM_SIZE_PRESET,
7100 SIZE_DEFAULT);
7101 mCustomContentHeight = wearableBundle.getInt(KEY_CUSTOM_CONTENT_HEIGHT);
7102 mGravity = wearableBundle.getInt(KEY_GRAVITY, DEFAULT_GRAVITY);
Griff Hazen5f2edfc2014-09-29 16:28:44 -07007103 mHintScreenTimeout = wearableBundle.getInt(KEY_HINT_SCREEN_TIMEOUT);
Nadia Benbernou948627e2016-04-14 14:41:08 -04007104 mDismissalId = wearableBundle.getString(KEY_DISMISSAL_ID);
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04007105 mBridgeTag = wearableBundle.getString(KEY_BRIDGE_TAG);
Griff Hazen61a9e862014-05-22 16:05:19 -07007106 }
7107 }
7108
7109 /**
7110 * Apply wearable extensions to a notification that is being built. This is typically
7111 * called by the {@link android.app.Notification.Builder#extend} method of
7112 * {@link android.app.Notification.Builder}.
7113 */
7114 @Override
7115 public Notification.Builder extend(Notification.Builder builder) {
7116 Bundle wearableBundle = new Bundle();
7117
7118 if (!mActions.isEmpty()) {
7119 wearableBundle.putParcelableArrayList(KEY_ACTIONS, mActions);
7120 }
7121 if (mFlags != DEFAULT_FLAGS) {
7122 wearableBundle.putInt(KEY_FLAGS, mFlags);
7123 }
7124 if (mDisplayIntent != null) {
7125 wearableBundle.putParcelable(KEY_DISPLAY_INTENT, mDisplayIntent);
7126 }
7127 if (!mPages.isEmpty()) {
7128 wearableBundle.putParcelableArray(KEY_PAGES, mPages.toArray(
7129 new Notification[mPages.size()]));
7130 }
7131 if (mBackground != null) {
7132 wearableBundle.putParcelable(KEY_BACKGROUND, mBackground);
7133 }
7134 if (mContentIcon != 0) {
7135 wearableBundle.putInt(KEY_CONTENT_ICON, mContentIcon);
7136 }
7137 if (mContentIconGravity != DEFAULT_CONTENT_ICON_GRAVITY) {
7138 wearableBundle.putInt(KEY_CONTENT_ICON_GRAVITY, mContentIconGravity);
7139 }
7140 if (mContentActionIndex != UNSET_ACTION_INDEX) {
7141 wearableBundle.putInt(KEY_CONTENT_ACTION_INDEX,
7142 mContentActionIndex);
7143 }
7144 if (mCustomSizePreset != SIZE_DEFAULT) {
7145 wearableBundle.putInt(KEY_CUSTOM_SIZE_PRESET, mCustomSizePreset);
7146 }
7147 if (mCustomContentHeight != 0) {
7148 wearableBundle.putInt(KEY_CUSTOM_CONTENT_HEIGHT, mCustomContentHeight);
7149 }
7150 if (mGravity != DEFAULT_GRAVITY) {
7151 wearableBundle.putInt(KEY_GRAVITY, mGravity);
7152 }
Griff Hazen5f2edfc2014-09-29 16:28:44 -07007153 if (mHintScreenTimeout != 0) {
7154 wearableBundle.putInt(KEY_HINT_SCREEN_TIMEOUT, mHintScreenTimeout);
7155 }
Nadia Benbernou948627e2016-04-14 14:41:08 -04007156 if (mDismissalId != null) {
7157 wearableBundle.putString(KEY_DISMISSAL_ID, mDismissalId);
7158 }
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04007159 if (mBridgeTag != null) {
7160 wearableBundle.putString(KEY_BRIDGE_TAG, mBridgeTag);
7161 }
Griff Hazen61a9e862014-05-22 16:05:19 -07007162
7163 builder.getExtras().putBundle(EXTRA_WEARABLE_EXTENSIONS, wearableBundle);
7164 return builder;
7165 }
7166
7167 @Override
7168 public WearableExtender clone() {
7169 WearableExtender that = new WearableExtender();
7170 that.mActions = new ArrayList<Action>(this.mActions);
7171 that.mFlags = this.mFlags;
7172 that.mDisplayIntent = this.mDisplayIntent;
7173 that.mPages = new ArrayList<Notification>(this.mPages);
7174 that.mBackground = this.mBackground;
7175 that.mContentIcon = this.mContentIcon;
7176 that.mContentIconGravity = this.mContentIconGravity;
7177 that.mContentActionIndex = this.mContentActionIndex;
7178 that.mCustomSizePreset = this.mCustomSizePreset;
7179 that.mCustomContentHeight = this.mCustomContentHeight;
7180 that.mGravity = this.mGravity;
Griff Hazen5f2edfc2014-09-29 16:28:44 -07007181 that.mHintScreenTimeout = this.mHintScreenTimeout;
Nadia Benbernou948627e2016-04-14 14:41:08 -04007182 that.mDismissalId = this.mDismissalId;
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04007183 that.mBridgeTag = this.mBridgeTag;
Griff Hazen61a9e862014-05-22 16:05:19 -07007184 return that;
7185 }
7186
7187 /**
7188 * Add a wearable action to this notification.
7189 *
7190 * <p>When wearable actions are added using this method, the set of actions that
7191 * show on a wearable device splits from devices that only show actions added
7192 * using {@link android.app.Notification.Builder#addAction}. This allows for customization
7193 * of which actions display on different devices.
7194 *
7195 * @param action the action to add to this notification
7196 * @return this object for method chaining
7197 * @see android.app.Notification.Action
7198 */
7199 public WearableExtender addAction(Action action) {
7200 mActions.add(action);
7201 return this;
7202 }
7203
7204 /**
7205 * Adds wearable actions to this notification.
7206 *
7207 * <p>When wearable actions are added using this method, the set of actions that
7208 * show on a wearable device splits from devices that only show actions added
7209 * using {@link android.app.Notification.Builder#addAction}. This allows for customization
7210 * of which actions display on different devices.
7211 *
7212 * @param actions the actions to add to this notification
7213 * @return this object for method chaining
7214 * @see android.app.Notification.Action
7215 */
7216 public WearableExtender addActions(List<Action> actions) {
7217 mActions.addAll(actions);
7218 return this;
7219 }
7220
7221 /**
7222 * Clear all wearable actions present on this builder.
7223 * @return this object for method chaining.
7224 * @see #addAction
7225 */
7226 public WearableExtender clearActions() {
7227 mActions.clear();
7228 return this;
7229 }
7230
7231 /**
7232 * Get the wearable actions present on this notification.
7233 */
7234 public List<Action> getActions() {
7235 return mActions;
7236 }
7237
7238 /**
7239 * Set an intent to launch inside of an activity view when displaying
Griff Hazen14f57992014-05-26 09:07:14 -07007240 * this notification. The {@link PendingIntent} provided should be for an activity.
7241 *
7242 * <pre class="prettyprint">
7243 * Intent displayIntent = new Intent(context, MyDisplayActivity.class);
7244 * PendingIntent displayPendingIntent = PendingIntent.getActivity(context,
7245 * 0, displayIntent, PendingIntent.FLAG_UPDATE_CURRENT);
7246 * Notification notif = new Notification.Builder(context)
7247 * .extend(new Notification.WearableExtender()
7248 * .setDisplayIntent(displayPendingIntent)
7249 * .setCustomSizePreset(Notification.WearableExtender.SIZE_MEDIUM))
7250 * .build();</pre>
7251 *
7252 * <p>The activity to launch needs to allow embedding, must be exported, and
Griff Hazen831ca9d2014-06-17 00:38:38 -07007253 * should have an empty task affinity. It is also recommended to use the device
7254 * default light theme.
Griff Hazen14f57992014-05-26 09:07:14 -07007255 *
7256 * <p>Example AndroidManifest.xml entry:
7257 * <pre class="prettyprint">
7258 * &lt;activity android:name=&quot;com.example.MyDisplayActivity&quot;
7259 * android:exported=&quot;true&quot;
7260 * android:allowEmbedded=&quot;true&quot;
Griff Hazen831ca9d2014-06-17 00:38:38 -07007261 * android:taskAffinity=&quot;&quot;
7262 * android:theme=&quot;@android:style/Theme.DeviceDefault.Light&quot; /&gt;</pre>
Griff Hazen61a9e862014-05-22 16:05:19 -07007263 *
7264 * @param intent the {@link PendingIntent} for an activity
7265 * @return this object for method chaining
7266 * @see android.app.Notification.WearableExtender#getDisplayIntent
7267 */
7268 public WearableExtender setDisplayIntent(PendingIntent intent) {
7269 mDisplayIntent = intent;
7270 return this;
7271 }
7272
7273 /**
7274 * Get the intent to launch inside of an activity view when displaying this
7275 * notification. This {@code PendingIntent} should be for an activity.
7276 */
7277 public PendingIntent getDisplayIntent() {
7278 return mDisplayIntent;
7279 }
7280
7281 /**
7282 * Add an additional page of content to display with this notification. The current
7283 * notification forms the first page, and pages added using this function form
7284 * subsequent pages. This field can be used to separate a notification into multiple
7285 * sections.
7286 *
7287 * @param page the notification to add as another page
7288 * @return this object for method chaining
7289 * @see android.app.Notification.WearableExtender#getPages
7290 */
7291 public WearableExtender addPage(Notification page) {
7292 mPages.add(page);
7293 return this;
7294 }
7295
7296 /**
7297 * Add additional pages of content to display with this notification. The current
7298 * notification forms the first page, and pages added using this function form
7299 * subsequent pages. This field can be used to separate a notification into multiple
7300 * sections.
7301 *
7302 * @param pages a list of notifications
7303 * @return this object for method chaining
7304 * @see android.app.Notification.WearableExtender#getPages
7305 */
7306 public WearableExtender addPages(List<Notification> pages) {
7307 mPages.addAll(pages);
7308 return this;
7309 }
7310
7311 /**
7312 * Clear all additional pages present on this builder.
7313 * @return this object for method chaining.
7314 * @see #addPage
7315 */
7316 public WearableExtender clearPages() {
7317 mPages.clear();
7318 return this;
7319 }
7320
7321 /**
7322 * Get the array of additional pages of content for displaying this notification. The
7323 * current notification forms the first page, and elements within this array form
7324 * subsequent pages. This field can be used to separate a notification into multiple
7325 * sections.
7326 * @return the pages for this notification
7327 */
7328 public List<Notification> getPages() {
7329 return mPages;
7330 }
7331
7332 /**
7333 * Set a background image to be displayed behind the notification content.
7334 * Contrary to the {@link android.app.Notification.BigPictureStyle}, this background
7335 * will work with any notification style.
7336 *
7337 * @param background the background bitmap
7338 * @return this object for method chaining
7339 * @see android.app.Notification.WearableExtender#getBackground
7340 */
7341 public WearableExtender setBackground(Bitmap background) {
7342 mBackground = background;
7343 return this;
7344 }
7345
7346 /**
7347 * Get a background image to be displayed behind the notification content.
7348 * Contrary to the {@link android.app.Notification.BigPictureStyle}, this background
7349 * will work with any notification style.
7350 *
7351 * @return the background image
7352 * @see android.app.Notification.WearableExtender#setBackground
7353 */
7354 public Bitmap getBackground() {
7355 return mBackground;
7356 }
7357
7358 /**
7359 * Set an icon that goes with the content of this notification.
7360 */
7361 public WearableExtender setContentIcon(int icon) {
7362 mContentIcon = icon;
7363 return this;
7364 }
7365
7366 /**
7367 * Get an icon that goes with the content of this notification.
7368 */
7369 public int getContentIcon() {
7370 return mContentIcon;
7371 }
7372
7373 /**
7374 * Set the gravity that the content icon should have within the notification display.
7375 * Supported values include {@link android.view.Gravity#START} and
7376 * {@link android.view.Gravity#END}. The default value is {@link android.view.Gravity#END}.
7377 * @see #setContentIcon
7378 */
7379 public WearableExtender setContentIconGravity(int contentIconGravity) {
7380 mContentIconGravity = contentIconGravity;
7381 return this;
7382 }
7383
7384 /**
7385 * Get the gravity that the content icon should have within the notification display.
7386 * Supported values include {@link android.view.Gravity#START} and
7387 * {@link android.view.Gravity#END}. The default value is {@link android.view.Gravity#END}.
7388 * @see #getContentIcon
7389 */
7390 public int getContentIconGravity() {
7391 return mContentIconGravity;
7392 }
7393
7394 /**
7395 * Set an action from this notification's actions to be clickable with the content of
Griff Hazen14f57992014-05-26 09:07:14 -07007396 * this notification. This action will no longer display separately from the
7397 * notification's content.
7398 *
Griff Hazenca48d352014-05-28 22:37:13 -07007399 * <p>For notifications with multiple pages, child pages can also have content actions
Griff Hazen14f57992014-05-26 09:07:14 -07007400 * set, although the list of available actions comes from the main notification and not
7401 * from the child page's notification.
7402 *
7403 * @param actionIndex The index of the action to hoist onto the current notification page.
7404 * If wearable actions were added to the main notification, this index
7405 * will apply to that list, otherwise it will apply to the regular
7406 * actions list.
Griff Hazen61a9e862014-05-22 16:05:19 -07007407 */
7408 public WearableExtender setContentAction(int actionIndex) {
7409 mContentActionIndex = actionIndex;
7410 return this;
7411 }
7412
7413 /**
Griff Hazenca48d352014-05-28 22:37:13 -07007414 * Get the index of the notification action, if any, that was specified as being clickable
7415 * with the content of this notification. This action will no longer display separately
Griff Hazen14f57992014-05-26 09:07:14 -07007416 * from the notification's content.
Griff Hazen61a9e862014-05-22 16:05:19 -07007417 *
Griff Hazenca48d352014-05-28 22:37:13 -07007418 * <p>For notifications with multiple pages, child pages can also have content actions
Griff Hazen14f57992014-05-26 09:07:14 -07007419 * set, although the list of available actions comes from the main notification and not
7420 * from the child page's notification.
7421 *
7422 * <p>If wearable specific actions were added to the main notification, this index will
7423 * apply to that list, otherwise it will apply to the regular actions list.
Griff Hazenca48d352014-05-28 22:37:13 -07007424 *
7425 * @return the action index or {@link #UNSET_ACTION_INDEX} if no action was selected.
Griff Hazen61a9e862014-05-22 16:05:19 -07007426 */
7427 public int getContentAction() {
7428 return mContentActionIndex;
7429 }
7430
7431 /**
7432 * Set the gravity that this notification should have within the available viewport space.
7433 * Supported values include {@link android.view.Gravity#TOP},
7434 * {@link android.view.Gravity#CENTER_VERTICAL} and {@link android.view.Gravity#BOTTOM}.
7435 * The default value is {@link android.view.Gravity#BOTTOM}.
7436 */
7437 public WearableExtender setGravity(int gravity) {
7438 mGravity = gravity;
7439 return this;
7440 }
7441
7442 /**
7443 * Get the gravity that this notification should have within the available viewport space.
7444 * Supported values include {@link android.view.Gravity#TOP},
7445 * {@link android.view.Gravity#CENTER_VERTICAL} and {@link android.view.Gravity#BOTTOM}.
7446 * The default value is {@link android.view.Gravity#BOTTOM}.
7447 */
7448 public int getGravity() {
7449 return mGravity;
7450 }
7451
7452 /**
7453 * Set the custom size preset for the display of this notification out of the available
7454 * presets found in {@link android.app.Notification.WearableExtender}, e.g.
7455 * {@link #SIZE_LARGE}.
7456 * <p>Some custom size presets are only applicable for custom display notifications created
7457 * using {@link android.app.Notification.WearableExtender#setDisplayIntent}. Check the
7458 * documentation for the preset in question. See also
7459 * {@link #setCustomContentHeight} and {@link #getCustomSizePreset}.
7460 */
7461 public WearableExtender setCustomSizePreset(int sizePreset) {
7462 mCustomSizePreset = sizePreset;
7463 return this;
7464 }
7465
7466 /**
7467 * Get the custom size preset for the display of this notification out of the available
7468 * presets found in {@link android.app.Notification.WearableExtender}, e.g.
7469 * {@link #SIZE_LARGE}.
7470 * <p>Some custom size presets are only applicable for custom display notifications created
7471 * using {@link #setDisplayIntent}. Check the documentation for the preset in question.
7472 * See also {@link #setCustomContentHeight} and {@link #setCustomSizePreset}.
7473 */
7474 public int getCustomSizePreset() {
7475 return mCustomSizePreset;
7476 }
7477
7478 /**
7479 * Set the custom height in pixels for the display of this notification's content.
7480 * <p>This option is only available for custom display notifications created
7481 * using {@link android.app.Notification.WearableExtender#setDisplayIntent}. See also
7482 * {@link android.app.Notification.WearableExtender#setCustomSizePreset} and
7483 * {@link #getCustomContentHeight}.
7484 */
7485 public WearableExtender setCustomContentHeight(int height) {
7486 mCustomContentHeight = height;
7487 return this;
7488 }
7489
7490 /**
7491 * Get the custom height in pixels for the display of this notification's content.
7492 * <p>This option is only available for custom display notifications created
7493 * using {@link #setDisplayIntent}. See also {@link #setCustomSizePreset} and
7494 * {@link #setCustomContentHeight}.
7495 */
7496 public int getCustomContentHeight() {
7497 return mCustomContentHeight;
7498 }
7499
7500 /**
7501 * Set whether the scrolling position for the contents of this notification should start
7502 * at the bottom of the contents instead of the top when the contents are too long to
7503 * display within the screen. Default is false (start scroll at the top).
7504 */
7505 public WearableExtender setStartScrollBottom(boolean startScrollBottom) {
7506 setFlag(FLAG_START_SCROLL_BOTTOM, startScrollBottom);
7507 return this;
7508 }
7509
7510 /**
7511 * Get whether the scrolling position for the contents of this notification should start
7512 * at the bottom of the contents instead of the top when the contents are too long to
7513 * display within the screen. Default is false (start scroll at the top).
7514 */
7515 public boolean getStartScrollBottom() {
7516 return (mFlags & FLAG_START_SCROLL_BOTTOM) != 0;
7517 }
7518
7519 /**
7520 * Set whether the content intent is available when the wearable device is not connected
7521 * to a companion device. The user can still trigger this intent when the wearable device
7522 * is offline, but a visual hint will indicate that the content intent may not be available.
7523 * Defaults to true.
7524 */
7525 public WearableExtender setContentIntentAvailableOffline(
7526 boolean contentIntentAvailableOffline) {
7527 setFlag(FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE, contentIntentAvailableOffline);
7528 return this;
7529 }
7530
7531 /**
7532 * Get whether the content intent is available when the wearable device is not connected
7533 * to a companion device. The user can still trigger this intent when the wearable device
7534 * is offline, but a visual hint will indicate that the content intent may not be available.
7535 * Defaults to true.
7536 */
7537 public boolean getContentIntentAvailableOffline() {
7538 return (mFlags & FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE) != 0;
7539 }
7540
7541 /**
7542 * Set a hint that this notification's icon should not be displayed.
7543 * @param hintHideIcon {@code true} to hide the icon, {@code false} otherwise.
7544 * @return this object for method chaining
7545 */
7546 public WearableExtender setHintHideIcon(boolean hintHideIcon) {
7547 setFlag(FLAG_HINT_HIDE_ICON, hintHideIcon);
7548 return this;
7549 }
7550
7551 /**
7552 * Get a hint that this notification's icon should not be displayed.
7553 * @return {@code true} if this icon should not be displayed, false otherwise.
7554 * The default value is {@code false} if this was never set.
7555 */
7556 public boolean getHintHideIcon() {
7557 return (mFlags & FLAG_HINT_HIDE_ICON) != 0;
7558 }
7559
7560 /**
7561 * Set a visual hint that only the background image of this notification should be
7562 * displayed, and other semantic content should be hidden. This hint is only applicable
7563 * to sub-pages added using {@link #addPage}.
7564 */
7565 public WearableExtender setHintShowBackgroundOnly(boolean hintShowBackgroundOnly) {
7566 setFlag(FLAG_HINT_SHOW_BACKGROUND_ONLY, hintShowBackgroundOnly);
7567 return this;
7568 }
7569
7570 /**
7571 * Get a visual hint that only the background image of this notification should be
7572 * displayed, and other semantic content should be hidden. This hint is only applicable
7573 * to sub-pages added using {@link android.app.Notification.WearableExtender#addPage}.
7574 */
7575 public boolean getHintShowBackgroundOnly() {
7576 return (mFlags & FLAG_HINT_SHOW_BACKGROUND_ONLY) != 0;
7577 }
7578
Griff Hazen5f2edfc2014-09-29 16:28:44 -07007579 /**
Griff Hazen9c5be4e2014-11-17 17:47:50 -08007580 * Set a hint that this notification's background should not be clipped if possible,
7581 * and should instead be resized to fully display on the screen, retaining the aspect
7582 * ratio of the image. This can be useful for images like barcodes or qr codes.
Griff Hazen5f2edfc2014-09-29 16:28:44 -07007583 * @param hintAvoidBackgroundClipping {@code true} to avoid clipping if possible.
7584 * @return this object for method chaining
7585 */
7586 public WearableExtender setHintAvoidBackgroundClipping(
7587 boolean hintAvoidBackgroundClipping) {
7588 setFlag(FLAG_HINT_AVOID_BACKGROUND_CLIPPING, hintAvoidBackgroundClipping);
7589 return this;
7590 }
7591
7592 /**
Griff Hazen9c5be4e2014-11-17 17:47:50 -08007593 * Get a hint that this notification's background should not be clipped if possible,
7594 * and should instead be resized to fully display on the screen, retaining the aspect
7595 * ratio of the image. This can be useful for images like barcodes or qr codes.
Griff Hazen5f2edfc2014-09-29 16:28:44 -07007596 * @return {@code true} if it's ok if the background is clipped on the screen, false
7597 * otherwise. The default value is {@code false} if this was never set.
7598 */
7599 public boolean getHintAvoidBackgroundClipping() {
7600 return (mFlags & FLAG_HINT_AVOID_BACKGROUND_CLIPPING) != 0;
7601 }
7602
7603 /**
7604 * Set a hint that the screen should remain on for at least this duration when
7605 * this notification is displayed on the screen.
7606 * @param timeout The requested screen timeout in milliseconds. Can also be either
7607 * {@link #SCREEN_TIMEOUT_SHORT} or {@link #SCREEN_TIMEOUT_LONG}.
7608 * @return this object for method chaining
7609 */
7610 public WearableExtender setHintScreenTimeout(int timeout) {
7611 mHintScreenTimeout = timeout;
7612 return this;
7613 }
7614
7615 /**
7616 * Get the duration, in milliseconds, that the screen should remain on for
7617 * when this notification is displayed.
7618 * @return the duration in milliseconds if > 0, or either one of the sentinel values
7619 * {@link #SCREEN_TIMEOUT_SHORT} or {@link #SCREEN_TIMEOUT_LONG}.
7620 */
7621 public int getHintScreenTimeout() {
7622 return mHintScreenTimeout;
7623 }
7624
Alex Hills9ab3a232016-04-05 14:54:56 -04007625 /**
Alex Hills4bcb06b2016-04-05 14:26:25 -04007626 * Set a hint that this notification's {@link BigPictureStyle} (if present) should be
7627 * converted to low-bit and displayed in ambient mode, especially useful for barcodes and
7628 * qr codes, as well as other simple black-and-white tickets.
7629 * @param hintAmbientBigPicture {@code true} to enable converstion and ambient.
7630 * @return this object for method chaining
7631 */
7632 public WearableExtender setHintAmbientBigPicture(boolean hintAmbientBigPicture) {
7633 setFlag(FLAG_BIG_PICTURE_AMBIENT, hintAmbientBigPicture);
7634 return this;
7635 }
7636
7637 /**
7638 * Get a hint that this notification's {@link BigPictureStyle} (if present) should be
7639 * converted to low-bit and displayed in ambient mode, especially useful for barcodes and
7640 * qr codes, as well as other simple black-and-white tickets.
7641 * @return {@code true} if it should be displayed in ambient, false otherwise
7642 * otherwise. The default value is {@code false} if this was never set.
7643 */
7644 public boolean getHintAmbientBigPicture() {
7645 return (mFlags & FLAG_BIG_PICTURE_AMBIENT) != 0;
7646 }
7647
7648 /**
Alex Hills9ab3a232016-04-05 14:54:56 -04007649 * Set a hint that this notification's content intent will launch an {@link Activity}
7650 * directly, telling the platform that it can generate the appropriate transitions.
7651 * @param hintContentIntentLaunchesActivity {@code true} if the content intent will launch
7652 * an activity and transitions should be generated, false otherwise.
7653 * @return this object for method chaining
7654 */
7655 public WearableExtender setHintContentIntentLaunchesActivity(
7656 boolean hintContentIntentLaunchesActivity) {
7657 setFlag(FLAG_HINT_CONTENT_INTENT_LAUNCHES_ACTIVITY, hintContentIntentLaunchesActivity);
7658 return this;
7659 }
7660
7661 /**
7662 * Get a hint that this notification's content intent will launch an {@link Activity}
7663 * directly, telling the platform that it can generate the appropriate transitions
7664 * @return {@code true} if the content intent will launch an activity and transitions should
7665 * be generated, false otherwise. The default value is {@code false} if this was never set.
7666 */
7667 public boolean getHintContentIntentLaunchesActivity() {
7668 return (mFlags & FLAG_HINT_CONTENT_INTENT_LAUNCHES_ACTIVITY) != 0;
7669 }
7670
Nadia Benbernou948627e2016-04-14 14:41:08 -04007671 /**
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04007672 * Sets the dismissal id for this notification. If a notification is posted with a
7673 * dismissal id, then when that notification is canceled, notifications on other wearables
7674 * and the paired Android phone having that same dismissal id will also be canceled. See
Nadia Benbernou948627e2016-04-14 14:41:08 -04007675 * <a href="{@docRoot}wear/notifications/index.html">Adding Wearable Features to
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04007676 * Notifications</a> for more information.
Nadia Benbernou948627e2016-04-14 14:41:08 -04007677 * @param dismissalId the dismissal id of the notification.
7678 * @return this object for method chaining
7679 */
7680 public WearableExtender setDismissalId(String dismissalId) {
7681 mDismissalId = dismissalId;
7682 return this;
7683 }
7684
7685 /**
7686 * Returns the dismissal id of the notification.
7687 * @return the dismissal id of the notification or null if it has not been set.
7688 */
7689 public String getDismissalId() {
7690 return mDismissalId;
7691 }
7692
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04007693 /**
7694 * Sets a bridge tag for this notification. A bridge tag can be set for notifications
7695 * posted from a phone to provide finer-grained control on what notifications are bridged
7696 * to wearables. See <a href="{@docRoot}wear/notifications/index.html">Adding Wearable
7697 * Features to Notifications</a> for more information.
7698 * @param bridgeTag the bridge tag of the notification.
7699 * @return this object for method chaining
7700 */
7701 public WearableExtender setBridgeTag(String bridgeTag) {
7702 mBridgeTag = bridgeTag;
7703 return this;
7704 }
7705
7706 /**
7707 * Returns the bridge tag of the notification.
7708 * @return the bridge tag or null if not present.
7709 */
7710 public String getBridgeTag() {
7711 return mBridgeTag;
7712 }
7713
Griff Hazen61a9e862014-05-22 16:05:19 -07007714 private void setFlag(int mask, boolean value) {
7715 if (value) {
7716 mFlags |= mask;
7717 } else {
7718 mFlags &= ~mask;
7719 }
7720 }
7721 }
7722
7723 /**
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08007724 * <p>Helper class to add Android Auto extensions to notifications. To create a notification
7725 * with car extensions:
7726 *
7727 * <ol>
7728 * <li>Create an {@link Notification.Builder}, setting any desired
7729 * properties.
7730 * <li>Create a {@link CarExtender}.
7731 * <li>Set car-specific properties using the {@code add} and {@code set} methods of
7732 * {@link CarExtender}.
7733 * <li>Call {@link Notification.Builder#extend(Notification.Extender)}
7734 * to apply the extensions to a notification.
7735 * </ol>
7736 *
7737 * <pre class="prettyprint">
7738 * Notification notification = new Notification.Builder(context)
7739 * ...
7740 * .extend(new CarExtender()
7741 * .set*(...))
7742 * .build();
7743 * </pre>
7744 *
7745 * <p>Car extensions can be accessed on an existing notification by using the
7746 * {@code CarExtender(Notification)} constructor, and then using the {@code get} methods
7747 * to access values.
7748 */
7749 public static final class CarExtender implements Extender {
7750 private static final String TAG = "CarExtender";
7751
7752 private static final String EXTRA_CAR_EXTENDER = "android.car.EXTENSIONS";
7753 private static final String EXTRA_LARGE_ICON = "large_icon";
7754 private static final String EXTRA_CONVERSATION = "car_conversation";
7755 private static final String EXTRA_COLOR = "app_color";
7756
7757 private Bitmap mLargeIcon;
7758 private UnreadConversation mUnreadConversation;
7759 private int mColor = Notification.COLOR_DEFAULT;
7760
7761 /**
7762 * Create a {@link CarExtender} with default options.
7763 */
7764 public CarExtender() {
7765 }
7766
7767 /**
7768 * Create a {@link CarExtender} from the CarExtender options of an existing Notification.
7769 *
7770 * @param notif The notification from which to copy options.
7771 */
7772 public CarExtender(Notification notif) {
7773 Bundle carBundle = notif.extras == null ?
7774 null : notif.extras.getBundle(EXTRA_CAR_EXTENDER);
7775 if (carBundle != null) {
7776 mLargeIcon = carBundle.getParcelable(EXTRA_LARGE_ICON);
7777 mColor = carBundle.getInt(EXTRA_COLOR, Notification.COLOR_DEFAULT);
7778
7779 Bundle b = carBundle.getBundle(EXTRA_CONVERSATION);
7780 mUnreadConversation = UnreadConversation.getUnreadConversationFromBundle(b);
7781 }
7782 }
7783
7784 /**
7785 * Apply car extensions to a notification that is being built. This is typically called by
7786 * the {@link Notification.Builder#extend(Notification.Extender)}
7787 * method of {@link Notification.Builder}.
7788 */
7789 @Override
7790 public Notification.Builder extend(Notification.Builder builder) {
7791 Bundle carExtensions = new Bundle();
7792
7793 if (mLargeIcon != null) {
7794 carExtensions.putParcelable(EXTRA_LARGE_ICON, mLargeIcon);
7795 }
7796 if (mColor != Notification.COLOR_DEFAULT) {
7797 carExtensions.putInt(EXTRA_COLOR, mColor);
7798 }
7799
7800 if (mUnreadConversation != null) {
7801 Bundle b = mUnreadConversation.getBundleForUnreadConversation();
7802 carExtensions.putBundle(EXTRA_CONVERSATION, b);
7803 }
7804
7805 builder.getExtras().putBundle(EXTRA_CAR_EXTENDER, carExtensions);
7806 return builder;
7807 }
7808
7809 /**
7810 * Sets the accent color to use when Android Auto presents the notification.
7811 *
7812 * Android Auto uses the color set with {@link Notification.Builder#setColor(int)}
7813 * to accent the displayed notification. However, not all colors are acceptable in an
7814 * automotive setting. This method can be used to override the color provided in the
7815 * notification in such a situation.
7816 */
Tor Norbye80756e32015-03-02 09:39:27 -08007817 public CarExtender setColor(@ColorInt int color) {
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08007818 mColor = color;
7819 return this;
7820 }
7821
7822 /**
7823 * Gets the accent color.
7824 *
Julia Reynoldsd9228f12015-10-20 10:37:27 -04007825 * @see #setColor
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08007826 */
Tor Norbye80756e32015-03-02 09:39:27 -08007827 @ColorInt
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08007828 public int getColor() {
7829 return mColor;
7830 }
7831
7832 /**
7833 * Sets the large icon of the car notification.
7834 *
7835 * If no large icon is set in the extender, Android Auto will display the icon
7836 * specified by {@link Notification.Builder#setLargeIcon(android.graphics.Bitmap)}
7837 *
7838 * @param largeIcon The large icon to use in the car notification.
7839 * @return This object for method chaining.
7840 */
7841 public CarExtender setLargeIcon(Bitmap largeIcon) {
7842 mLargeIcon = largeIcon;
7843 return this;
7844 }
7845
7846 /**
7847 * Gets the large icon used in this car notification, or null if no icon has been set.
7848 *
7849 * @return The large icon for the car notification.
7850 * @see CarExtender#setLargeIcon
7851 */
7852 public Bitmap getLargeIcon() {
7853 return mLargeIcon;
7854 }
7855
7856 /**
7857 * Sets the unread conversation in a message notification.
7858 *
7859 * @param unreadConversation The unread part of the conversation this notification conveys.
7860 * @return This object for method chaining.
7861 */
7862 public CarExtender setUnreadConversation(UnreadConversation unreadConversation) {
7863 mUnreadConversation = unreadConversation;
7864 return this;
7865 }
7866
7867 /**
7868 * Returns the unread conversation conveyed by this notification.
7869 * @see #setUnreadConversation(UnreadConversation)
7870 */
7871 public UnreadConversation getUnreadConversation() {
7872 return mUnreadConversation;
7873 }
7874
7875 /**
7876 * A class which holds the unread messages from a conversation.
7877 */
7878 public static class UnreadConversation {
7879 private static final String KEY_AUTHOR = "author";
7880 private static final String KEY_TEXT = "text";
7881 private static final String KEY_MESSAGES = "messages";
7882 private static final String KEY_REMOTE_INPUT = "remote_input";
7883 private static final String KEY_ON_REPLY = "on_reply";
7884 private static final String KEY_ON_READ = "on_read";
7885 private static final String KEY_PARTICIPANTS = "participants";
7886 private static final String KEY_TIMESTAMP = "timestamp";
7887
7888 private final String[] mMessages;
7889 private final RemoteInput mRemoteInput;
7890 private final PendingIntent mReplyPendingIntent;
7891 private final PendingIntent mReadPendingIntent;
7892 private final String[] mParticipants;
7893 private final long mLatestTimestamp;
7894
7895 UnreadConversation(String[] messages, RemoteInput remoteInput,
7896 PendingIntent replyPendingIntent, PendingIntent readPendingIntent,
7897 String[] participants, long latestTimestamp) {
7898 mMessages = messages;
7899 mRemoteInput = remoteInput;
7900 mReadPendingIntent = readPendingIntent;
7901 mReplyPendingIntent = replyPendingIntent;
7902 mParticipants = participants;
7903 mLatestTimestamp = latestTimestamp;
7904 }
7905
7906 /**
7907 * Gets the list of messages conveyed by this notification.
7908 */
7909 public String[] getMessages() {
7910 return mMessages;
7911 }
7912
7913 /**
7914 * Gets the remote input that will be used to convey the response to a message list, or
7915 * null if no such remote input exists.
7916 */
7917 public RemoteInput getRemoteInput() {
7918 return mRemoteInput;
7919 }
7920
7921 /**
7922 * Gets the pending intent that will be triggered when the user replies to this
7923 * notification.
7924 */
7925 public PendingIntent getReplyPendingIntent() {
7926 return mReplyPendingIntent;
7927 }
7928
7929 /**
7930 * Gets the pending intent that Android Auto will send after it reads aloud all messages
7931 * in this object's message list.
7932 */
7933 public PendingIntent getReadPendingIntent() {
7934 return mReadPendingIntent;
7935 }
7936
7937 /**
7938 * Gets the participants in the conversation.
7939 */
7940 public String[] getParticipants() {
7941 return mParticipants;
7942 }
7943
7944 /**
7945 * Gets the firs participant in the conversation.
7946 */
7947 public String getParticipant() {
7948 return mParticipants.length > 0 ? mParticipants[0] : null;
7949 }
7950
7951 /**
7952 * Gets the timestamp of the conversation.
7953 */
7954 public long getLatestTimestamp() {
7955 return mLatestTimestamp;
7956 }
7957
7958 Bundle getBundleForUnreadConversation() {
7959 Bundle b = new Bundle();
7960 String author = null;
7961 if (mParticipants != null && mParticipants.length > 1) {
7962 author = mParticipants[0];
7963 }
7964 Parcelable[] messages = new Parcelable[mMessages.length];
7965 for (int i = 0; i < messages.length; i++) {
7966 Bundle m = new Bundle();
7967 m.putString(KEY_TEXT, mMessages[i]);
7968 m.putString(KEY_AUTHOR, author);
7969 messages[i] = m;
7970 }
7971 b.putParcelableArray(KEY_MESSAGES, messages);
7972 if (mRemoteInput != null) {
7973 b.putParcelable(KEY_REMOTE_INPUT, mRemoteInput);
7974 }
7975 b.putParcelable(KEY_ON_REPLY, mReplyPendingIntent);
7976 b.putParcelable(KEY_ON_READ, mReadPendingIntent);
7977 b.putStringArray(KEY_PARTICIPANTS, mParticipants);
7978 b.putLong(KEY_TIMESTAMP, mLatestTimestamp);
7979 return b;
7980 }
7981
7982 static UnreadConversation getUnreadConversationFromBundle(Bundle b) {
7983 if (b == null) {
7984 return null;
7985 }
7986 Parcelable[] parcelableMessages = b.getParcelableArray(KEY_MESSAGES);
7987 String[] messages = null;
7988 if (parcelableMessages != null) {
7989 String[] tmp = new String[parcelableMessages.length];
7990 boolean success = true;
7991 for (int i = 0; i < tmp.length; i++) {
7992 if (!(parcelableMessages[i] instanceof Bundle)) {
7993 success = false;
7994 break;
7995 }
7996 tmp[i] = ((Bundle) parcelableMessages[i]).getString(KEY_TEXT);
7997 if (tmp[i] == null) {
7998 success = false;
7999 break;
8000 }
8001 }
8002 if (success) {
8003 messages = tmp;
8004 } else {
8005 return null;
8006 }
8007 }
8008
8009 PendingIntent onRead = b.getParcelable(KEY_ON_READ);
8010 PendingIntent onReply = b.getParcelable(KEY_ON_REPLY);
8011
8012 RemoteInput remoteInput = b.getParcelable(KEY_REMOTE_INPUT);
8013
8014 String[] participants = b.getStringArray(KEY_PARTICIPANTS);
8015 if (participants == null || participants.length != 1) {
8016 return null;
8017 }
8018
8019 return new UnreadConversation(messages,
8020 remoteInput,
8021 onReply,
8022 onRead,
8023 participants, b.getLong(KEY_TIMESTAMP));
8024 }
8025 };
8026
8027 /**
8028 * Builder class for {@link CarExtender.UnreadConversation} objects.
8029 */
8030 public static class Builder {
8031 private final List<String> mMessages = new ArrayList<String>();
8032 private final String mParticipant;
8033 private RemoteInput mRemoteInput;
8034 private PendingIntent mReadPendingIntent;
8035 private PendingIntent mReplyPendingIntent;
8036 private long mLatestTimestamp;
8037
8038 /**
8039 * Constructs a new builder for {@link CarExtender.UnreadConversation}.
8040 *
8041 * @param name The name of the other participant in the conversation.
8042 */
8043 public Builder(String name) {
8044 mParticipant = name;
8045 }
8046
8047 /**
8048 * Appends a new unread message to the list of messages for this conversation.
8049 *
8050 * The messages should be added from oldest to newest.
8051 *
8052 * @param message The text of the new unread message.
8053 * @return This object for method chaining.
8054 */
8055 public Builder addMessage(String message) {
8056 mMessages.add(message);
8057 return this;
8058 }
8059
8060 /**
8061 * Sets the pending intent and remote input which will convey the reply to this
8062 * notification.
8063 *
8064 * @param pendingIntent The pending intent which will be triggered on a reply.
8065 * @param remoteInput The remote input parcelable which will carry the reply.
8066 * @return This object for method chaining.
8067 *
8068 * @see CarExtender.UnreadConversation#getRemoteInput
8069 * @see CarExtender.UnreadConversation#getReplyPendingIntent
8070 */
8071 public Builder setReplyAction(
8072 PendingIntent pendingIntent, RemoteInput remoteInput) {
8073 mRemoteInput = remoteInput;
8074 mReplyPendingIntent = pendingIntent;
8075
8076 return this;
8077 }
8078
8079 /**
8080 * Sets the pending intent that will be sent once the messages in this notification
8081 * are read.
8082 *
8083 * @param pendingIntent The pending intent to use.
8084 * @return This object for method chaining.
8085 */
8086 public Builder setReadPendingIntent(PendingIntent pendingIntent) {
8087 mReadPendingIntent = pendingIntent;
8088 return this;
8089 }
8090
8091 /**
8092 * Sets the timestamp of the most recent message in an unread conversation.
8093 *
8094 * If a messaging notification has been posted by your application and has not
8095 * yet been cancelled, posting a later notification with the same id and tag
8096 * but without a newer timestamp may result in Android Auto not displaying a
8097 * heads up notification for the later notification.
8098 *
8099 * @param timestamp The timestamp of the most recent message in the conversation.
8100 * @return This object for method chaining.
8101 */
8102 public Builder setLatestTimestamp(long timestamp) {
8103 mLatestTimestamp = timestamp;
8104 return this;
8105 }
8106
8107 /**
8108 * Builds a new unread conversation object.
8109 *
8110 * @return The new unread conversation object.
8111 */
8112 public UnreadConversation build() {
8113 String[] messages = mMessages.toArray(new String[mMessages.size()]);
8114 String[] participants = { mParticipant };
8115 return new UnreadConversation(messages, mRemoteInput, mReplyPendingIntent,
8116 mReadPendingIntent, participants, mLatestTimestamp);
8117 }
8118 }
8119 }
8120
8121 /**
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08008122 * <p>Helper class to add Android TV extensions to notifications. To create a notification
8123 * with a TV extension:
8124 *
8125 * <ol>
8126 * <li>Create an {@link Notification.Builder}, setting any desired properties.
8127 * <li>Create a {@link TvExtender}.
8128 * <li>Set TV-specific properties using the {@code set} methods of
8129 * {@link TvExtender}.
8130 * <li>Call {@link Notification.Builder#extend(Notification.Extender)}
8131 * to apply the extension to a notification.
8132 * </ol>
8133 *
8134 * <pre class="prettyprint">
8135 * Notification notification = new Notification.Builder(context)
8136 * ...
8137 * .extend(new TvExtender()
8138 * .set*(...))
8139 * .build();
8140 * </pre>
8141 *
8142 * <p>TV extensions can be accessed on an existing notification by using the
8143 * {@code TvExtender(Notification)} constructor, and then using the {@code get} methods
8144 * to access values.
8145 *
8146 * @hide
8147 */
8148 @SystemApi
8149 public static final class TvExtender implements Extender {
8150 private static final String TAG = "TvExtender";
8151
8152 private static final String EXTRA_TV_EXTENDER = "android.tv.EXTENSIONS";
8153 private static final String EXTRA_FLAGS = "flags";
8154 private static final String EXTRA_CONTENT_INTENT = "content_intent";
8155 private static final String EXTRA_DELETE_INTENT = "delete_intent";
Dmitri Plotnikovb8a04ee2017-01-20 10:39:22 -08008156 private static final String EXTRA_CHANNEL_ID = "channel_id";
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08008157
8158 // Flags bitwise-ored to mFlags
8159 private static final int FLAG_AVAILABLE_ON_TV = 0x1;
8160
8161 private int mFlags;
Dmitri Plotnikovb8a04ee2017-01-20 10:39:22 -08008162 private String mChannelId;
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08008163 private PendingIntent mContentIntent;
8164 private PendingIntent mDeleteIntent;
8165
8166 /**
8167 * Create a {@link TvExtender} with default options.
8168 */
8169 public TvExtender() {
8170 mFlags = FLAG_AVAILABLE_ON_TV;
8171 }
8172
8173 /**
8174 * Create a {@link TvExtender} from the TvExtender options of an existing Notification.
8175 *
8176 * @param notif The notification from which to copy options.
8177 */
8178 public TvExtender(Notification notif) {
8179 Bundle bundle = notif.extras == null ?
8180 null : notif.extras.getBundle(EXTRA_TV_EXTENDER);
8181 if (bundle != null) {
8182 mFlags = bundle.getInt(EXTRA_FLAGS);
Dmitri Plotnikovb8a04ee2017-01-20 10:39:22 -08008183 mChannelId = bundle.getString(EXTRA_CHANNEL_ID);
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08008184 mContentIntent = bundle.getParcelable(EXTRA_CONTENT_INTENT);
8185 mDeleteIntent = bundle.getParcelable(EXTRA_DELETE_INTENT);
8186 }
8187 }
8188
8189 /**
8190 * Apply a TV extension to a notification that is being built. This is typically called by
8191 * the {@link Notification.Builder#extend(Notification.Extender)}
8192 * method of {@link Notification.Builder}.
8193 */
8194 @Override
8195 public Notification.Builder extend(Notification.Builder builder) {
8196 Bundle bundle = new Bundle();
8197
8198 bundle.putInt(EXTRA_FLAGS, mFlags);
Dmitri Plotnikovb8a04ee2017-01-20 10:39:22 -08008199 bundle.putString(EXTRA_CHANNEL_ID, mChannelId);
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08008200 if (mContentIntent != null) {
8201 bundle.putParcelable(EXTRA_CONTENT_INTENT, mContentIntent);
8202 }
8203
8204 if (mDeleteIntent != null) {
8205 bundle.putParcelable(EXTRA_DELETE_INTENT, mDeleteIntent);
8206 }
8207
8208 builder.getExtras().putBundle(EXTRA_TV_EXTENDER, bundle);
8209 return builder;
8210 }
8211
8212 /**
8213 * Returns true if this notification should be shown on TV. This method return true
8214 * if the notification was extended with a TvExtender.
8215 */
8216 public boolean isAvailableOnTv() {
8217 return (mFlags & FLAG_AVAILABLE_ON_TV) != 0;
8218 }
8219
8220 /**
Dmitri Plotnikovb8a04ee2017-01-20 10:39:22 -08008221 * Specifies the channel the notification should be delivered on when shown on TV.
8222 * It can be different from the channel that the notification is delivered to when
8223 * posting on a non-TV device.
8224 */
8225 public TvExtender setChannel(String channelId) {
8226 mChannelId = channelId;
8227 return this;
8228 }
8229
8230 /**
Julia Reynoldsbad42972017-04-25 13:52:49 -04008231 * Specifies the channel the notification should be delivered on when shown on TV.
8232 * It can be different from the channel that the notification is delivered to when
8233 * posting on a non-TV device.
8234 */
8235 public TvExtender setChannelId(String channelId) {
8236 mChannelId = channelId;
8237 return this;
8238 }
8239
Jeff Sharkey000ce802017-04-29 13:13:27 -06008240 /** @removed */
8241 @Deprecated
Dmitri Plotnikovb8a04ee2017-01-20 10:39:22 -08008242 public String getChannel() {
8243 return mChannelId;
8244 }
8245
8246 /**
Julia Reynoldsbad42972017-04-25 13:52:49 -04008247 * Returns the id of the channel this notification posts to on TV.
8248 */
8249 public String getChannelId() {
8250 return mChannelId;
8251 }
8252
8253 /**
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08008254 * Supplies a {@link PendingIntent} to be sent when the notification is selected on TV.
8255 * If provided, it is used instead of the content intent specified
8256 * at the level of Notification.
8257 */
8258 public TvExtender setContentIntent(PendingIntent intent) {
8259 mContentIntent = intent;
8260 return this;
8261 }
8262
8263 /**
8264 * Returns the TV-specific content intent. If this method returns null, the
8265 * main content intent on the notification should be used.
8266 *
8267 * @see {@link Notification#contentIntent}
8268 */
8269 public PendingIntent getContentIntent() {
8270 return mContentIntent;
8271 }
8272
8273 /**
8274 * Supplies a {@link PendingIntent} to send when the notification is cleared explicitly
8275 * by the user on TV. If provided, it is used instead of the delete intent specified
8276 * at the level of Notification.
8277 */
8278 public TvExtender setDeleteIntent(PendingIntent intent) {
8279 mDeleteIntent = intent;
8280 return this;
8281 }
8282
8283 /**
8284 * Returns the TV-specific delete intent. If this method returns null, the
8285 * main delete intent on the notification should be used.
8286 *
8287 * @see {@link Notification#deleteIntent}
8288 */
8289 public PendingIntent getDeleteIntent() {
8290 return mDeleteIntent;
8291 }
8292 }
8293
8294 /**
Griff Hazen61a9e862014-05-22 16:05:19 -07008295 * Get an array of Notification objects from a parcelable array bundle field.
8296 * Update the bundle to have a typed array so fetches in the future don't need
8297 * to do an array copy.
8298 */
8299 private static Notification[] getNotificationArrayFromBundle(Bundle bundle, String key) {
8300 Parcelable[] array = bundle.getParcelableArray(key);
8301 if (array instanceof Notification[] || array == null) {
8302 return (Notification[]) array;
8303 }
8304 Notification[] typedArray = Arrays.copyOf(array, array.length,
8305 Notification[].class);
8306 bundle.putParcelableArray(key, typedArray);
8307 return typedArray;
8308 }
Christoph Studer4600f9b2014-07-22 22:44:43 +02008309
8310 private static class BuilderRemoteViews extends RemoteViews {
8311 public BuilderRemoteViews(Parcel parcel) {
8312 super(parcel);
8313 }
8314
Kenny Guy77320062014-08-27 21:37:15 +01008315 public BuilderRemoteViews(ApplicationInfo appInfo, int layoutId) {
8316 super(appInfo, layoutId);
Christoph Studer4600f9b2014-07-22 22:44:43 +02008317 }
8318
8319 @Override
8320 public BuilderRemoteViews clone() {
8321 Parcel p = Parcel.obtain();
8322 writeToParcel(p, 0);
8323 p.setDataPosition(0);
8324 BuilderRemoteViews brv = new BuilderRemoteViews(p);
8325 p.recycle();
8326 return brv;
8327 }
8328 }
Adrian Roos70d7aa32017-01-11 15:39:06 -08008329
8330 private static class StandardTemplateParams {
8331 boolean hasProgress = true;
8332 boolean ambient = false;
8333 CharSequence title;
8334 CharSequence text;
8335
8336 final StandardTemplateParams reset() {
8337 hasProgress = true;
8338 ambient = false;
8339 title = null;
8340 text = null;
8341 return this;
8342 }
8343
8344 final StandardTemplateParams hasProgress(boolean hasProgress) {
8345 this.hasProgress = hasProgress;
8346 return this;
8347 }
8348
8349 final StandardTemplateParams title(CharSequence title) {
8350 this.title = title;
8351 return this;
8352 }
8353
8354 final StandardTemplateParams text(CharSequence text) {
8355 this.text = text;
8356 return this;
8357 }
8358
8359 final StandardTemplateParams ambient(boolean ambient) {
Adrian Roos0bc3f6a2017-03-06 11:54:05 -08008360 Preconditions.checkState(title == null && text == null, "must set ambient before text");
Adrian Roos70d7aa32017-01-11 15:39:06 -08008361 this.ambient = ambient;
8362 return this;
8363 }
8364
8365 final StandardTemplateParams fillTextsFrom(Builder b) {
8366 Bundle extras = b.mN.extras;
Adrian Roos0bc3f6a2017-03-06 11:54:05 -08008367 title = b.processLegacyText(extras.getCharSequence(EXTRA_TITLE), ambient);
8368 text = b.processLegacyText(extras.getCharSequence(EXTRA_TEXT), ambient);
Adrian Roos70d7aa32017-01-11 15:39:06 -08008369 return this;
8370 }
8371 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08008372}