blob: 4a076945bee46c4182ee45e2d07c4eea6b56cc54 [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;
Daniel Sandler01df1c62014-06-09 10:54:01 -040025import android.annotation.SdkConstant;
26import android.annotation.SdkConstant.SdkConstantType;
Julia Reynoldse46bb372016-03-17 11:05:58 -040027import android.annotation.SystemApi;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080028import android.content.Context;
29import android.content.Intent;
Kenny Guy77320062014-08-27 21:37:15 +010030import android.content.pm.ApplicationInfo;
Dan Sandler732bd6c2016-04-12 14:20:32 -040031import android.content.pm.PackageManager;
Christoph Studer4600f9b2014-07-22 22:44:43 +020032import android.content.pm.PackageManager.NameNotFoundException;
Julia Reynolds13d898c2017-02-02 12:22:05 -050033import android.content.pm.ShortcutInfo;
Jorim Jaggief72a192014-08-26 21:57:46 +020034import android.content.res.ColorStateList;
Joe Onoratoef1e7762010-09-17 18:38:38 -040035import android.graphics.Bitmap;
Kenny Guy8a0101b2014-05-08 23:34:12 +010036import android.graphics.Canvas;
Adrian Roosc1a80b02016-04-05 14:54:55 -070037import android.graphics.Color;
Jorim Jaggi5c2d8462014-03-21 17:37:00 +010038import android.graphics.PorterDuff;
Kenny Guy8a0101b2014-05-08 23:34:12 +010039import android.graphics.drawable.Drawable;
Dan Sandlerd63f9322015-05-06 15:18:49 -040040import android.graphics.drawable.Icon;
John Spurlockc0650f022014-07-19 13:22:39 -040041import android.media.AudioAttributes;
Jeff Sharkey098d5802012-04-26 17:30:34 -070042import android.media.AudioManager;
Jean-Michel Trivi2f7511f2016-11-28 15:40:27 -080043import android.media.PlayerBase;
Jeff Browndba34ba2014-06-24 20:46:03 -070044import android.media.session.MediaSession;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080045import android.net.Uri;
Daniel Sandlerdcbaf662013-04-26 16:23:09 -040046import android.os.BadParcelableException;
Christoph Studer239f8352014-08-25 15:13:18 +020047import android.os.Build;
Daniel Sandler2561b0b2012-02-13 21:04:12 -050048import android.os.Bundle;
Dianne Hackborn98305522017-05-05 17:53:53 -070049import android.os.IBinder;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080050import android.os.Parcel;
51import android.os.Parcelable;
Daniel Sandlera2985ed2012-04-03 16:42:00 -040052import android.os.SystemClock;
Selim Cinek6743c0b2017-01-18 18:24:01 -080053import android.os.SystemProperties;
Jeff Sharkey6d515712012-09-20 16:06:08 -070054import android.os.UserHandle;
Adrian Roosc1a80b02016-04-05 14:54:55 -070055import android.text.BidiFormatter;
Selim Cinek60a54252016-02-26 17:03:25 -080056import android.text.SpannableStringBuilder;
57import android.text.Spanned;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080058import android.text.TextUtils;
Selim Cinek60a54252016-02-26 17:03:25 -080059import android.text.style.AbsoluteSizeSpan;
Selim Cinek981962e2016-07-20 20:41:58 -070060import android.text.style.BackgroundColorSpan;
Selim Cinek89991a22016-03-07 19:51:54 -080061import android.text.style.CharacterStyle;
Selim Cinek981962e2016-07-20 20:41:58 -070062import android.text.style.ForegroundColorSpan;
Selim Cinek60a54252016-02-26 17:03:25 -080063import android.text.style.RelativeSizeSpan;
64import android.text.style.TextAppearanceSpan;
Svet Ganovddb94882016-06-23 19:55:24 -070065import android.util.ArraySet;
Daniel Sandlerdcbaf662013-04-26 16:23:09 -040066import android.util.Log;
Dan Sandler50128532015-12-08 15:42:41 -050067import android.util.SparseArray;
Griff Hazen61a9e862014-05-22 16:05:19 -070068import android.view.Gravity;
Selim Cinekea4bef72015-12-02 15:51:10 -080069import android.view.NotificationHeaderView;
Joe Onorato8595a3d2010-11-19 18:12:07 -080070import android.view.View;
Selim Cinek954cc232016-05-20 13:29:23 -070071import android.view.ViewGroup;
Jeff Sharkey1c400132011-08-05 14:50:13 -070072import android.widget.ProgressBar;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080073import android.widget.RemoteViews;
74
Griff Hazen959591e2014-05-15 22:26:18 -070075import com.android.internal.R;
Selim Cinek389edcd2017-05-11 19:16:44 -070076import com.android.internal.annotations.VisibleForTesting;
Svet Ganovddb94882016-06-23 19:55:24 -070077import com.android.internal.util.ArrayUtils;
Griff Hazenc091ba82014-05-16 10:13:26 -070078import com.android.internal.util.NotificationColorUtil;
Adrian Roos0bc3f6a2017-03-06 11:54:05 -080079import com.android.internal.util.Preconditions;
Griff Hazen959591e2014-05-15 22:26:18 -070080
Tor Norbyed9273d62013-05-30 15:59:53 -070081import java.lang.annotation.Retention;
82import java.lang.annotation.RetentionPolicy;
Christoph Studer4600f9b2014-07-22 22:44:43 +020083import java.lang.reflect.Constructor;
Daniel Sandler2561b0b2012-02-13 21:04:12 -050084import java.util.ArrayList;
Griff Hazen61a9e862014-05-22 16:05:19 -070085import java.util.Arrays;
Griff Hazen5cadc3b2014-05-20 09:55:39 -070086import java.util.Collections;
Griff Hazen61a9e862014-05-22 16:05:19 -070087import java.util.List;
Dan Sandler50128532015-12-08 15:42:41 -050088import java.util.Set;
Joe Onorato561d3852010-11-20 18:09:34 -080089
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080090/**
91 * A class that represents how a persistent notification is to be presented to
92 * the user using the {@link android.app.NotificationManager}.
93 *
Joe Onoratocb109a02011-01-18 17:57:41 -080094 * <p>The {@link Notification.Builder Notification.Builder} has been added to make it
95 * easier to construct Notifications.</p>
96 *
Joe Fernandez558459f2011-10-13 16:47:36 -070097 * <div class="special reference">
98 * <h3>Developer Guides</h3>
99 * <p>For a guide to creating notifications, read the
100 * <a href="{@docRoot}guide/topics/ui/notifiers/notifications.html">Status Bar Notifications</a>
101 * developer guide.</p>
102 * </div>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800103 */
104public class Notification implements Parcelable
105{
Daniel Sandlerdcbaf662013-04-26 16:23:09 -0400106 private static final String TAG = "Notification";
107
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800108 /**
Daniel Sandler01df1c62014-06-09 10:54:01 -0400109 * An activity that provides a user interface for adjusting notification preferences for its
Julia Reynolds3aedded2017-03-31 14:42:09 -0400110 * containing application.
Daniel Sandler01df1c62014-06-09 10:54:01 -0400111 */
112 @SdkConstant(SdkConstantType.INTENT_CATEGORY)
113 public static final String INTENT_CATEGORY_NOTIFICATION_PREFERENCES
114 = "android.intent.category.NOTIFICATION_PREFERENCES";
115
116 /**
Julia Reynolds2619b5e2017-02-09 09:58:15 -0500117 * Optional extra for {@link #INTENT_CATEGORY_NOTIFICATION_PREFERENCES}. If provided, will
118 * contain a {@link NotificationChannel#getId() channel id} that can be used to narrow down
Julia Reynolds3aedded2017-03-31 14:42:09 -0400119 * what settings should be shown in the target app.
Julia Reynolds2619b5e2017-02-09 09:58:15 -0500120 */
121 public static final String EXTRA_CHANNEL_ID = "android.intent.extra.CHANNEL_ID";
122
123 /**
Julia Reynolds3aedded2017-03-31 14:42:09 -0400124 * Optional extra for {@link #INTENT_CATEGORY_NOTIFICATION_PREFERENCES}. If provided, will
125 * contain the tag provided to {@link NotificationManager#notify(String, int, Notification)}
126 * that can be used to narrow down what settings should be shown in the target app.
127 */
128 public static final String EXTRA_NOTIFICATION_TAG = "android.intent.extra.NOTIFICATION_TAG";
129
130 /**
131 * Optional extra for {@link #INTENT_CATEGORY_NOTIFICATION_PREFERENCES}. If provided, will
132 * contain the id provided to {@link NotificationManager#notify(String, int, Notification)}
133 * that can be used to narrow down what settings should be shown in the target app.
134 */
135 public static final String EXTRA_NOTIFICATION_ID = "android.intent.extra.NOTIFICATION_ID";
136
137 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800138 * Use all default values (where applicable).
139 */
140 public static final int DEFAULT_ALL = ~0;
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500141
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800142 /**
143 * Use the default notification sound. This will ignore any given
144 * {@link #sound}.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500145 *
Chris Wren47c20a12014-06-18 17:27:29 -0400146 * <p>
147 * A notification that is noisy is more likely to be presented as a heads-up notification.
148 * </p>
149 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800150 * @see #defaults
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500151 */
152
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800153 public static final int DEFAULT_SOUND = 1;
154
155 /**
156 * Use the default notification vibrate. This will ignore any given
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500157 * {@link #vibrate}. Using phone vibration requires the
Scott Mainb8b36452009-04-26 15:50:49 -0700158 * {@link android.Manifest.permission#VIBRATE VIBRATE} permission.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500159 *
Chris Wren47c20a12014-06-18 17:27:29 -0400160 * <p>
161 * A notification that vibrates is more likely to be presented as a heads-up notification.
162 * </p>
163 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800164 * @see #defaults
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500165 */
166
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800167 public static final int DEFAULT_VIBRATE = 2;
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500168
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800169 /**
170 * Use the default notification lights. This will ignore the
171 * {@link #FLAG_SHOW_LIGHTS} bit, and {@link #ledARGB}, {@link #ledOffMS}, or
172 * {@link #ledOnMS}.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500173 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800174 * @see #defaults
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500175 */
176
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800177 public static final int DEFAULT_LIGHTS = 4;
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500178
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800179 /**
Christoph Studer535ec612014-09-03 15:47:47 +0200180 * Maximum length of CharSequences accepted by Builder and friends.
181 *
182 * <p>
183 * Avoids spamming the system with overly large strings such as full e-mails.
184 */
185 private static final int MAX_CHARSEQUENCE_LENGTH = 5 * 1024;
186
187 /**
Adrian Roose458aa82015-12-08 16:17:19 -0800188 * Maximum entries of reply text that are accepted by Builder and friends.
189 */
190 private static final int MAX_REPLY_HISTORY = 5;
191
192 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500193 * A timestamp related to this notification, in milliseconds since the epoch.
Joe Malin8d40d042012-11-05 11:36:40 -0800194 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500195 * Default value: {@link System#currentTimeMillis() Now}.
196 *
197 * Choose a timestamp that will be most relevant to the user. For most finite events, this
198 * corresponds to the time the event happened (or will happen, in the case of events that have
199 * yet to occur but about which the user is being informed). Indefinite events should be
Joe Malin8d40d042012-11-05 11:36:40 -0800200 * timestamped according to when the activity began.
201 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500202 * Some examples:
Joe Malin8d40d042012-11-05 11:36:40 -0800203 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500204 * <ul>
205 * <li>Notification of a new chat message should be stamped when the message was received.</li>
206 * <li>Notification of an ongoing file download (with a progress bar, for example) should be stamped when the download started.</li>
207 * <li>Notification of a completed file download should be stamped when the download finished.</li>
208 * <li>Notification of an upcoming meeting should be stamped with the time the meeting will begin (that is, in the future).</li>
209 * <li>Notification of an ongoing stopwatch (increasing timer) should be stamped with the watch's start time.
210 * <li>Notification of an ongoing countdown timer should be stamped with the timer's end time.
Joe Malin8d40d042012-11-05 11:36:40 -0800211 * </ul>
212 *
Selim Cinek0ff1ce602016-04-05 18:27:16 -0700213 * For apps targeting {@link android.os.Build.VERSION_CODES#N} and above, this time is not shown
214 * anymore by default and must be opted into by using
215 * {@link android.app.Notification.Builder#setShowWhen(boolean)}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800216 */
217 public long when;
218
219 /**
Selim Cinekb85f36fd2016-04-20 18:46:36 -0700220 * The creation time of the notification
221 */
222 private long creationTime;
223
224 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800225 * The resource id of a drawable to use as the icon in the status bar.
Dan Sandler86647982015-05-13 23:41:13 -0400226 *
227 * @deprecated Use {@link Builder#setSmallIcon(Icon)} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800228 */
Dan Sandler86647982015-05-13 23:41:13 -0400229 @Deprecated
Tor Norbye7b9c9122013-05-30 16:48:33 -0700230 @DrawableRes
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800231 public int icon;
232
233 /**
Joe Onorato46439ce2010-11-19 13:56:21 -0800234 * If the icon in the status bar is to have more than one level, you can set this. Otherwise,
235 * leave it at its default value of 0.
236 *
237 * @see android.widget.ImageView#setImageLevel
Griff Hazen959591e2014-05-15 22:26:18 -0700238 * @see android.graphics.drawable.Drawable#setLevel
Joe Onorato46439ce2010-11-19 13:56:21 -0800239 */
240 public int iconLevel;
241
242 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500243 * The number of events that this notification represents. For example, in a new mail
244 * notification, this could be the number of unread messages.
Joe Malin8d40d042012-11-05 11:36:40 -0800245 *
Julia Reynolds30331982017-04-27 10:12:50 -0400246 * The system may or may not use this field to modify the appearance of the notification.
Julia Reynolds13d898c2017-02-02 12:22:05 -0500247 * Starting with {@link android.os.Build.VERSION_CODES#O}, the number may be displayed as a
248 * badge icon in Launchers that support badging.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800249 */
Julia Reynolds30331982017-04-27 10:12:50 -0400250 public int number = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800251
252 /**
253 * The intent to execute when the expanded status entry is clicked. If
254 * this is an activity, it must include the
255 * {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} flag, which requires
Scott Main7aee61f2011-02-08 11:25:01 -0800256 * that you take care of task management as described in the
257 * <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back
Dianne Hackborn6ceca582012-01-10 15:24:26 -0800258 * Stack</a> document. In particular, make sure to read the notification section
259 * <a href="{@docRoot}guide/topics/ui/notifiers/notifications.html#HandlingNotifications">Handling
260 * Notifications</a> for the correct ways to launch an application from a
261 * notification.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800262 */
263 public PendingIntent contentIntent;
264
265 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500266 * The intent to execute when the notification is explicitly dismissed by the user, either with
267 * the "Clear All" button or by swiping it away individually.
268 *
269 * This probably shouldn't be launching an activity since several of those will be sent
270 * at the same time.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800271 */
272 public PendingIntent deleteIntent;
273
274 /**
Dianne Hackborn170bae72010-09-03 15:14:28 -0700275 * An intent to launch instead of posting the notification to the status bar.
Joe Onoratocb109a02011-01-18 17:57:41 -0800276 *
Chris Wren47c20a12014-06-18 17:27:29 -0400277 * <p>
278 * The system UI may choose to display a heads-up notification, instead of
279 * launching this intent, while the user is using the device.
280 * </p>
281 *
Joe Onoratocb109a02011-01-18 17:57:41 -0800282 * @see Notification.Builder#setFullScreenIntent
Daniel Sandlere46cbd32010-06-17 10:35:26 -0400283 */
284 public PendingIntent fullScreenIntent;
285
286 /**
Dan Sandler5fcdf6e2014-07-18 11:31:15 -0400287 * Text that summarizes this notification for accessibility services.
288 *
289 * As of the L release, this text is no longer shown on screen, but it is still useful to
290 * accessibility services (where it serves as an audible announcement of the notification's
291 * appearance).
Joe Onoratoef1e7762010-09-17 18:38:38 -0400292 *
Joe Onorato46439ce2010-11-19 13:56:21 -0800293 * @see #tickerView
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800294 */
295 public CharSequence tickerText;
296
297 /**
Dan Sandler5fcdf6e2014-07-18 11:31:15 -0400298 * Formerly, a view showing the {@link #tickerText}.
299 *
300 * No longer displayed in the status bar as of API 21.
Joe Onoratoef1e7762010-09-17 18:38:38 -0400301 */
Dan Sandler5fcdf6e2014-07-18 11:31:15 -0400302 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -0800303 public RemoteViews tickerView;
Joe Onoratoef1e7762010-09-17 18:38:38 -0400304
305 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400306 * The view that will represent this notification in the notification list (which is pulled
307 * down from the status bar).
308 *
Julia Reynoldsd4ea7412016-02-17 14:00:56 -0500309 * As of N, this field may be null. The notification view is determined by the inputs
310 * to {@link Notification.Builder}; a custom RemoteViews can optionally be
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400311 * supplied with {@link Notification.Builder#setCustomContentView(RemoteViews)}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800312 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400313 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800314 public RemoteViews contentView;
315
Daniel Sandlera0a938c2012-03-15 08:42:37 -0400316 /**
Daniel Sandler4dfbe832012-04-11 14:51:46 -0400317 * A large-format version of {@link #contentView}, giving the Notification an
Daniel Sandlerf3b73432012-03-27 15:01:25 -0400318 * opportunity to show more detail. The system UI may choose to show this
319 * instead of the normal content view at its discretion.
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400320 *
Julia Reynoldsd4ea7412016-02-17 14:00:56 -0500321 * As of N, this field may be null. The expanded notification view is determined by the
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400322 * inputs to {@link Notification.Builder}; a custom RemoteViews can optionally be
323 * supplied with {@link Notification.Builder#setCustomBigContentView(RemoteViews)}.
Daniel Sandlerf3b73432012-03-27 15:01:25 -0400324 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400325 @Deprecated
Daniel Sandlerf3b73432012-03-27 15:01:25 -0400326 public RemoteViews bigContentView;
327
Chris Wren8fd39ec2014-02-27 17:43:26 -0500328
329 /**
Chris Wren47c20a12014-06-18 17:27:29 -0400330 * A medium-format version of {@link #contentView}, providing the Notification an
331 * opportunity to add action buttons to contentView. At its discretion, the system UI may
332 * choose to show this as a heads-up notification, which will pop up so the user can see
333 * it without leaving their current activity.
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400334 *
Julia Reynoldsd4ea7412016-02-17 14:00:56 -0500335 * As of N, this field may be null. The heads-up notification view is determined by the
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400336 * inputs to {@link Notification.Builder}; a custom RemoteViews can optionally be
337 * supplied with {@link Notification.Builder#setCustomHeadsUpContentView(RemoteViews)}.
Chris Wren8fd39ec2014-02-27 17:43:26 -0500338 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400339 @Deprecated
Chris Wren8fd39ec2014-02-27 17:43:26 -0500340 public RemoteViews headsUpContentView;
341
Daniel Sandlerf3b73432012-03-27 15:01:25 -0400342 /**
Dan Sandler86647982015-05-13 23:41:13 -0400343 * A large bitmap to be shown in the notification content area.
344 *
345 * @deprecated Use {@link Builder#setLargeIcon(Icon)} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800346 */
Dan Sandler86647982015-05-13 23:41:13 -0400347 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -0800348 public Bitmap largeIcon;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800349
350 /**
351 * The sound to play.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500352 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800353 * <p>
Chris Wren47c20a12014-06-18 17:27:29 -0400354 * A notification that is noisy is more likely to be presented as a heads-up notification.
355 * </p>
356 *
357 * <p>
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500358 * To play the default notification sound, see {@link #defaults}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800359 * </p>
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500360 * @deprecated use {@link NotificationChannel#getSound()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800361 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500362 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800363 public Uri sound;
364
365 /**
366 * Use this constant as the value for audioStreamType to request that
367 * the default stream type for notifications be used. Currently the
Jeff Sharkey098d5802012-04-26 17:30:34 -0700368 * default stream type is {@link AudioManager#STREAM_NOTIFICATION}.
John Spurlockc0650f022014-07-19 13:22:39 -0400369 *
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500370 * @deprecated Use {@link NotificationChannel#getAudioAttributes()} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800371 */
Jean-Michel Trivi81f871e2014-08-06 16:32:38 -0700372 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800373 public static final int STREAM_DEFAULT = -1;
374
375 /**
376 * The audio stream type to use when playing the sound.
377 * Should be one of the STREAM_ constants from
378 * {@link android.media.AudioManager}.
John Spurlockc0650f022014-07-19 13:22:39 -0400379 *
380 * @deprecated Use {@link #audioAttributes} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800381 */
Jean-Michel Trivi81f871e2014-08-06 16:32:38 -0700382 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800383 public int audioStreamType = STREAM_DEFAULT;
384
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800385 /**
John Spurlockc0650f022014-07-19 13:22:39 -0400386 * The default value of {@link #audioAttributes}.
387 */
388 public static final AudioAttributes AUDIO_ATTRIBUTES_DEFAULT = new AudioAttributes.Builder()
389 .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
390 .setUsage(AudioAttributes.USAGE_NOTIFICATION)
391 .build();
392
393 /**
394 * The {@link AudioAttributes audio attributes} to use when playing the sound.
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500395 *
396 * @deprecated use {@link NotificationChannel#getAudioAttributes()} instead.
John Spurlockc0650f022014-07-19 13:22:39 -0400397 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500398 @Deprecated
John Spurlockc0650f022014-07-19 13:22:39 -0400399 public AudioAttributes audioAttributes = AUDIO_ATTRIBUTES_DEFAULT;
400
401 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500402 * The pattern with which to vibrate.
403 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800404 * <p>
405 * To vibrate the default pattern, see {@link #defaults}.
406 * </p>
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500407 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800408 * @see android.os.Vibrator#vibrate(long[],int)
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500409 * @deprecated use {@link NotificationChannel#getVibrationPattern()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800410 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500411 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800412 public long[] vibrate;
413
414 /**
415 * The color of the led. The hardware will do its best approximation.
416 *
417 * @see #FLAG_SHOW_LIGHTS
418 * @see #flags
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500419 * @deprecated use {@link NotificationChannel#shouldShowLights()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800420 */
Tor Norbye80756e32015-03-02 09:39:27 -0800421 @ColorInt
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500422 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800423 public int ledARGB;
424
425 /**
426 * The number of milliseconds for the LED to be on while it's flashing.
427 * The hardware will do its best approximation.
428 *
429 * @see #FLAG_SHOW_LIGHTS
430 * @see #flags
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500431 * @deprecated use {@link NotificationChannel#shouldShowLights()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800432 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500433 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800434 public int ledOnMS;
435
436 /**
437 * The number of milliseconds for the LED to be off while it's flashing.
438 * The hardware will do its best approximation.
439 *
440 * @see #FLAG_SHOW_LIGHTS
441 * @see #flags
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500442 *
443 * @deprecated use {@link NotificationChannel#shouldShowLights()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800444 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500445 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800446 public int ledOffMS;
447
448 /**
449 * Specifies which values should be taken from the defaults.
450 * <p>
451 * To set, OR the desired from {@link #DEFAULT_SOUND},
452 * {@link #DEFAULT_VIBRATE}, {@link #DEFAULT_LIGHTS}. For all default
453 * values, use {@link #DEFAULT_ALL}.
454 * </p>
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500455 *
456 * @deprecated use {@link NotificationChannel#getSound()} and
457 * {@link NotificationChannel#shouldShowLights()} and
458 * {@link NotificationChannel#shouldVibrate()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800459 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500460 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800461 public int defaults;
462
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800463 /**
464 * Bit to be bitwise-ored into the {@link #flags} field that should be
465 * set if you want the LED on for this notification.
466 * <ul>
467 * <li>To turn the LED off, pass 0 in the alpha channel for colorARGB
468 * or 0 for both ledOnMS and ledOffMS.</li>
469 * <li>To turn the LED on, pass 1 for ledOnMS and 0 for ledOffMS.</li>
470 * <li>To flash the LED, pass the number of milliseconds that it should
471 * be on and off to ledOnMS and ledOffMS.</li>
472 * </ul>
473 * <p>
474 * Since hardware varies, you are not guaranteed that any of the values
475 * you pass are honored exactly. Use the system defaults (TODO) if possible
476 * because they will be set to values that work on any given hardware.
477 * <p>
478 * The alpha channel must be set for forward compatibility.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500479 *
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500480 * @deprecated use {@link NotificationChannel#shouldShowLights()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800481 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500482 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800483 public static final int FLAG_SHOW_LIGHTS = 0x00000001;
484
485 /**
486 * Bit to be bitwise-ored into the {@link #flags} field that should be
487 * set if this notification is in reference to something that is ongoing,
488 * like a phone call. It should not be set if this notification is in
489 * reference to something that happened at a particular point in time,
490 * like a missed phone call.
491 */
492 public static final int FLAG_ONGOING_EVENT = 0x00000002;
493
494 /**
495 * Bit to be bitwise-ored into the {@link #flags} field that if set,
Scott Mainb8b36452009-04-26 15:50:49 -0700496 * the audio will be repeated until the notification is
497 * cancelled or the notification window is opened.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800498 */
499 public static final int FLAG_INSISTENT = 0x00000004;
500
501 /**
502 * Bit to be bitwise-ored into the {@link #flags} field that should be
Griff Hazen293977b2014-04-28 08:37:20 -0700503 * set if you would only like the sound, vibrate and ticker to be played
504 * if the notification was not already showing.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800505 */
506 public static final int FLAG_ONLY_ALERT_ONCE = 0x00000008;
507
508 /**
509 * Bit to be bitwise-ored into the {@link #flags} field that should be
510 * set if the notification should be canceled when it is clicked by the
Daniel Sandler8aa9ae62012-12-04 23:31:47 -0500511 * user.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800512 */
513 public static final int FLAG_AUTO_CANCEL = 0x00000010;
514
515 /**
516 * Bit to be bitwise-ored into the {@link #flags} field that should be
517 * set if the notification should not be canceled when the user clicks
518 * the Clear all button.
519 */
520 public static final int FLAG_NO_CLEAR = 0x00000020;
521
Dianne Hackbornd8a43f62009-08-17 23:33:56 -0700522 /**
523 * Bit to be bitwise-ored into the {@link #flags} field that should be
524 * set if this notification represents a currently running service. This
525 * will normally be set for you by {@link Service#startForeground}.
526 */
527 public static final int FLAG_FOREGROUND_SERVICE = 0x00000040;
528
Daniel Sandlere46cbd32010-06-17 10:35:26 -0400529 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500530 * Obsolete flag indicating high-priority notifications; use the priority field instead.
Joe Malin8d40d042012-11-05 11:36:40 -0800531 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500532 * @deprecated Use {@link #priority} with a positive value.
Daniel Sandlere46cbd32010-06-17 10:35:26 -0400533 */
Aurimas Liutikas514c5ef2016-05-24 15:22:55 -0700534 @Deprecated
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500535 public static final int FLAG_HIGH_PRIORITY = 0x00000080;
Daniel Sandlere46cbd32010-06-17 10:35:26 -0400536
Griff Hazendfcb0802014-02-11 12:00:00 -0800537 /**
538 * Bit to be bitswise-ored into the {@link #flags} field that should be
539 * set if this notification is relevant to the current device only
540 * and it is not recommended that it bridge to other devices.
541 */
542 public static final int FLAG_LOCAL_ONLY = 0x00000100;
543
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700544 /**
545 * Bit to be bitswise-ored into the {@link #flags} field that should be
546 * set if this notification is the group summary for a group of notifications.
547 * Grouped notifications may display in a cluster or stack on devices which
548 * support such rendering. Requires a group key also be set using {@link Builder#setGroup}.
549 */
550 public static final int FLAG_GROUP_SUMMARY = 0x00000200;
551
Julia Reynoldse46bb372016-03-17 11:05:58 -0400552 /**
553 * Bit to be bitswise-ored into the {@link #flags} field that should be
554 * set if this notification is the group summary for an auto-group of notifications.
555 *
556 * @hide
557 */
558 @SystemApi
559 public static final int FLAG_AUTOGROUP_SUMMARY = 0x00000400;
560
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800561 public int flags;
562
Tor Norbyed9273d62013-05-30 15:59:53 -0700563 /** @hide */
564 @IntDef({PRIORITY_DEFAULT,PRIORITY_LOW,PRIORITY_MIN,PRIORITY_HIGH,PRIORITY_MAX})
565 @Retention(RetentionPolicy.SOURCE)
566 public @interface Priority {}
567
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800568 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500569 * Default notification {@link #priority}. If your application does not prioritize its own
570 * notifications, use this value for all notifications.
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500571 *
572 * @deprecated use {@link NotificationManager#IMPORTANCE_DEFAULT} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500573 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500574 @Deprecated
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500575 public static final int PRIORITY_DEFAULT = 0;
576
577 /**
578 * Lower {@link #priority}, for items that are less important. The UI may choose to show these
579 * items smaller, or at a different position in the list, compared with your app's
580 * {@link #PRIORITY_DEFAULT} items.
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500581 *
582 * @deprecated use {@link NotificationManager#IMPORTANCE_LOW} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500583 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500584 @Deprecated
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500585 public static final int PRIORITY_LOW = -1;
586
587 /**
588 * Lowest {@link #priority}; these items might not be shown to the user except under special
589 * circumstances, such as detailed notification logs.
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500590 *
591 * @deprecated use {@link NotificationManager#IMPORTANCE_MIN} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500592 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500593 @Deprecated
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500594 public static final int PRIORITY_MIN = -2;
595
596 /**
597 * Higher {@link #priority}, for more important notifications or alerts. The UI may choose to
598 * show these items larger, or at a different position in notification lists, compared with
599 * your app's {@link #PRIORITY_DEFAULT} items.
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500600 *
601 * @deprecated use {@link NotificationManager#IMPORTANCE_HIGH} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500602 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500603 @Deprecated
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500604 public static final int PRIORITY_HIGH = 1;
605
606 /**
607 * Highest {@link #priority}, for your application's most important items that require the
608 * user's prompt attention or input.
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500609 *
610 * @deprecated use {@link NotificationManager#IMPORTANCE_HIGH} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500611 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500612 @Deprecated
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500613 public static final int PRIORITY_MAX = 2;
614
615 /**
616 * Relative priority for this notification.
Joe Malin8d40d042012-11-05 11:36:40 -0800617 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500618 * Priority is an indication of how much of the user's valuable attention should be consumed by
619 * this notification. Low-priority notifications may be hidden from the user in certain
620 * situations, while the user might be interrupted for a higher-priority notification. The
Daniel Sandler6738eee2012-11-16 12:03:32 -0500621 * system will make a determination about how to interpret this priority when presenting
622 * the notification.
Chris Wren47c20a12014-06-18 17:27:29 -0400623 *
624 * <p>
625 * A notification that is at least {@link #PRIORITY_HIGH} is more likely to be presented
626 * as a heads-up notification.
627 * </p>
628 *
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500629 * @deprecated use {@link NotificationChannel#getImportance()} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500630 */
Tor Norbyed9273d62013-05-30 15:59:53 -0700631 @Priority
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500632 @Deprecated
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500633 public int priority;
Joe Malin8d40d042012-11-05 11:36:40 -0800634
Dan Sandler26e81cf2014-05-06 10:01:27 -0400635 /**
636 * Accent color (an ARGB integer like the constants in {@link android.graphics.Color})
637 * to be applied by the standard Style templates when presenting this notification.
638 *
639 * The current template design constructs a colorful header image by overlaying the
640 * {@link #icon} image (stenciled in white) atop a field of this color. Alpha components are
641 * ignored.
642 */
Tor Norbye80756e32015-03-02 09:39:27 -0800643 @ColorInt
Dan Sandler26e81cf2014-05-06 10:01:27 -0400644 public int color = COLOR_DEFAULT;
645
646 /**
647 * Special value of {@link #color} telling the system not to decorate this notification with
648 * any special color but instead use default colors when presenting this notification.
649 */
Tor Norbye80756e32015-03-02 09:39:27 -0800650 @ColorInt
Dan Sandler26e81cf2014-05-06 10:01:27 -0400651 public static final int COLOR_DEFAULT = 0; // AKA Color.TRANSPARENT
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600652
653 /**
Adrian Roos4ff3b122016-02-01 12:26:13 -0800654 * Special value of {@link #color} used as a place holder for an invalid color.
Selim Cinek99104832017-01-25 14:47:33 -0800655 * @hide
Adrian Roos4ff3b122016-02-01 12:26:13 -0800656 */
657 @ColorInt
Selim Cinek99104832017-01-25 14:47:33 -0800658 public static final int COLOR_INVALID = 1;
Adrian Roos4ff3b122016-02-01 12:26:13 -0800659
660 /**
Adrian Roosc1a80b02016-04-05 14:54:55 -0700661 * Sphere of visibility of this notification, which affects how and when the SystemUI reveals
662 * the notification's presence and contents in untrusted situations (namely, on the secure
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600663 * lockscreen).
664 *
665 * The default level, {@link #VISIBILITY_PRIVATE}, behaves exactly as notifications have always
666 * done on Android: The notification's {@link #icon} and {@link #tickerText} (if available) are
667 * shown in all situations, but the contents are only available if the device is unlocked for
668 * the appropriate user.
669 *
670 * A more permissive policy can be expressed by {@link #VISIBILITY_PUBLIC}; such a notification
671 * can be read even in an "insecure" context (that is, above a secure lockscreen).
672 * To modify the public version of this notification—for example, to redact some portions—see
673 * {@link Builder#setPublicVersion(Notification)}.
674 *
675 * Finally, a notification can be made {@link #VISIBILITY_SECRET}, which will suppress its icon
676 * and ticker until the user has bypassed the lockscreen.
677 */
Jeff Sharkey30e06bb2017-04-24 11:18:03 -0600678 public @Visibility int visibility;
679
680 /** @hide */
681 @IntDef(prefix = { "VISIBILITY_" }, value = {
682 VISIBILITY_PUBLIC,
683 VISIBILITY_PRIVATE,
684 VISIBILITY_SECRET,
685 })
686 @Retention(RetentionPolicy.SOURCE)
687 public @interface Visibility {}
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600688
Griff Hazenfc3922d2014-08-20 11:56:44 -0700689 /**
690 * Notification visibility: Show this notification in its entirety on all lockscreens.
691 *
692 * {@see #visibility}
693 */
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600694 public static final int VISIBILITY_PUBLIC = 1;
Griff Hazenfc3922d2014-08-20 11:56:44 -0700695
696 /**
697 * Notification visibility: Show this notification on all lockscreens, but conceal sensitive or
698 * private information on secure lockscreens.
699 *
700 * {@see #visibility}
701 */
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600702 public static final int VISIBILITY_PRIVATE = 0;
Griff Hazenfc3922d2014-08-20 11:56:44 -0700703
704 /**
705 * Notification visibility: Do not reveal any part of this notification on a secure lockscreen.
706 *
707 * {@see #visibility}
708 */
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600709 public static final int VISIBILITY_SECRET = -1;
710
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500711 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400712 * Notification category: incoming call (voice or video) or similar synchronous communication request.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500713 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400714 public static final String CATEGORY_CALL = "call";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500715
716 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400717 * Notification category: incoming direct message (SMS, instant message, etc.).
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500718 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400719 public static final String CATEGORY_MESSAGE = "msg";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500720
721 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400722 * Notification category: asynchronous bulk message (email).
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500723 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400724 public static final String CATEGORY_EMAIL = "email";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500725
726 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400727 * Notification category: calendar event.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500728 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400729 public static final String CATEGORY_EVENT = "event";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500730
731 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400732 * Notification category: promotion or advertisement.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500733 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400734 public static final String CATEGORY_PROMO = "promo";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500735
736 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400737 * Notification category: alarm or timer.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500738 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400739 public static final String CATEGORY_ALARM = "alarm";
740
741 /**
742 * Notification category: progress of a long-running background operation.
743 */
744 public static final String CATEGORY_PROGRESS = "progress";
745
746 /**
747 * Notification category: social network or sharing update.
748 */
749 public static final String CATEGORY_SOCIAL = "social";
750
751 /**
752 * Notification category: error in background operation or authentication status.
753 */
754 public static final String CATEGORY_ERROR = "err";
755
756 /**
757 * Notification category: media transport control for playback.
758 */
759 public static final String CATEGORY_TRANSPORT = "transport";
760
761 /**
762 * Notification category: system or device status update. Reserved for system use.
763 */
764 public static final String CATEGORY_SYSTEM = "sys";
765
766 /**
767 * Notification category: indication of running background service.
768 */
769 public static final String CATEGORY_SERVICE = "service";
770
771 /**
John Spurlock0a69c8c2014-03-21 13:30:57 -0400772 * Notification category: a specific, timely recommendation for a single thing.
773 * For example, a news app might want to recommend a news story it believes the user will
774 * want to read next.
775 */
776 public static final String CATEGORY_RECOMMENDATION = "recommendation";
777
778 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400779 * Notification category: ongoing information about device or contextual status.
780 */
781 public static final String CATEGORY_STATUS = "status";
782
783 /**
John Spurlock24d3dad2015-04-02 12:24:02 -0400784 * Notification category: user-scheduled reminder.
785 */
786 public static final String CATEGORY_REMINDER = "reminder";
787
788 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400789 * One of the predefined notification categories (see the <code>CATEGORY_*</code> constants)
790 * that best describes this Notification. May be used by the system for ranking and filtering.
791 */
792 public String category;
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500793
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700794 private String mGroupKey;
795
796 /**
797 * Get the key used to group this notification into a cluster or stack
798 * with other notifications on devices which support such rendering.
799 */
800 public String getGroup() {
801 return mGroupKey;
802 }
803
804 private String mSortKey;
805
806 /**
807 * Get a sort key that orders this notification among other notifications from the
808 * same package. This can be useful if an external sort was already applied and an app
809 * would like to preserve this. Notifications will be sorted lexicographically using this
810 * value, although providing different priorities in addition to providing sort key may
811 * cause this value to be ignored.
812 *
813 * <p>This sort key can also be used to order members of a notification group. See
814 * {@link Builder#setGroup}.
815 *
816 * @see String#compareTo(String)
817 */
818 public String getSortKey() {
819 return mSortKey;
820 }
821
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500822 /**
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400823 * Additional semantic data to be carried around with this Notification.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400824 * <p>
825 * The extras keys defined here are intended to capture the original inputs to {@link Builder}
826 * APIs, and are intended to be used by
827 * {@link android.service.notification.NotificationListenerService} implementations to extract
828 * detailed information from notification objects.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500829 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400830 public Bundle extras = new Bundle();
831
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400832 /**
Felipe Lemedd85da62016-06-28 11:29:54 -0700833 * All pending intents in the notification as the system needs to be able to access them but
834 * touching the extras bundle in the system process is not safe because the bundle may contain
Svet Ganovddb94882016-06-23 19:55:24 -0700835 * custom parcelable objects.
836 *
837 * @hide
838 */
Felipe Lemedd85da62016-06-28 11:29:54 -0700839 public ArraySet<PendingIntent> allPendingIntents;
Svet Ganovddb94882016-06-23 19:55:24 -0700840
841 /**
Dianne Hackborn98305522017-05-05 17:53:53 -0700842 * Token identifying the notification that is applying doze/bgcheck whitelisting to the
843 * pending intents inside of it, so only those will get the behavior.
844 *
845 * @hide
846 */
847 static public IBinder whitelistToken;
848
849 /**
850 * Must be set by a process to start associating tokens with Notification objects
851 * coming in to it. This is set by NotificationManagerService.
852 *
853 * @hide
854 */
855 static public IBinder processWhitelistToken;
856
857 /**
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400858 * {@link #extras} key: this is the title of the notification,
859 * as supplied to {@link Builder#setContentTitle(CharSequence)}.
860 */
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -0500861 public static final String EXTRA_TITLE = "android.title";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400862
863 /**
864 * {@link #extras} key: this is the title of the notification when shown in expanded form,
865 * e.g. as supplied to {@link BigTextStyle#setBigContentTitle(CharSequence)}.
866 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400867 public static final String EXTRA_TITLE_BIG = EXTRA_TITLE + ".big";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400868
869 /**
870 * {@link #extras} key: this is the main text payload, as supplied to
871 * {@link Builder#setContentText(CharSequence)}.
872 */
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -0500873 public static final String EXTRA_TEXT = "android.text";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400874
875 /**
876 * {@link #extras} key: this is a third line of text, as supplied to
877 * {@link Builder#setSubText(CharSequence)}.
878 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400879 public static final String EXTRA_SUB_TEXT = "android.subText";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400880
881 /**
Adrian Roose458aa82015-12-08 16:17:19 -0800882 * {@link #extras} key: this is the remote input history, as supplied to
883 * {@link Builder#setRemoteInputHistory(CharSequence[])}.
Adrian Roos005e7742016-04-13 15:02:20 -0700884 *
885 * Apps can fill this through {@link Builder#setRemoteInputHistory(CharSequence[])}
886 * with the most recent inputs that have been sent through a {@link RemoteInput} of this
887 * Notification and are expected to clear it once the it is no longer relevant (e.g. for chat
888 * notifications once the other party has responded).
889 *
890 * The extra with this key is of type CharSequence[] and contains the most recent entry at
891 * the 0 index, the second most recent at the 1 index, etc.
892 *
893 * @see Builder#setRemoteInputHistory(CharSequence[])
Adrian Roose458aa82015-12-08 16:17:19 -0800894 */
895 public static final String EXTRA_REMOTE_INPUT_HISTORY = "android.remoteInputHistory";
896
897 /**
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400898 * {@link #extras} key: this is a small piece of additional text as supplied to
899 * {@link Builder#setContentInfo(CharSequence)}.
900 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400901 public static final String EXTRA_INFO_TEXT = "android.infoText";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400902
903 /**
904 * {@link #extras} key: this is a line of summary information intended to be shown
905 * alongside expanded notifications, as supplied to (e.g.)
906 * {@link BigTextStyle#setSummaryText(CharSequence)}.
907 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400908 public static final String EXTRA_SUMMARY_TEXT = "android.summaryText";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400909
910 /**
Christoph Studer4600f9b2014-07-22 22:44:43 +0200911 * {@link #extras} key: this is the longer text shown in the big form of a
912 * {@link BigTextStyle} notification, as supplied to
913 * {@link BigTextStyle#bigText(CharSequence)}.
914 */
915 public static final String EXTRA_BIG_TEXT = "android.bigText";
916
917 /**
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400918 * {@link #extras} key: this is the resource ID of the notification's main small icon, as
919 * supplied to {@link Builder#setSmallIcon(int)}.
Julia Reynoldse071abd2017-03-22 10:52:11 -0400920 *
921 * @deprecated Use {@link #getSmallIcon()}, which supports a wider variety of icon sources.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400922 */
Julia Reynoldse071abd2017-03-22 10:52:11 -0400923 @Deprecated
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -0500924 public static final String EXTRA_SMALL_ICON = "android.icon";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400925
926 /**
927 * {@link #extras} key: this is a bitmap to be used instead of the small icon when showing the
928 * notification payload, as
929 * supplied to {@link Builder#setLargeIcon(android.graphics.Bitmap)}.
Julia Reynoldse071abd2017-03-22 10:52:11 -0400930 *
931 * @deprecated Use {@link #getLargeIcon()}, which supports a wider variety of icon sources.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400932 */
Julia Reynoldse071abd2017-03-22 10:52:11 -0400933 @Deprecated
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400934 public static final String EXTRA_LARGE_ICON = "android.largeIcon";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400935
936 /**
937 * {@link #extras} key: this is a bitmap to be used instead of the one from
938 * {@link Builder#setLargeIcon(android.graphics.Bitmap)} when the notification is
939 * shown in its expanded form, as supplied to
940 * {@link BigPictureStyle#bigLargeIcon(android.graphics.Bitmap)}.
941 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400942 public static final String EXTRA_LARGE_ICON_BIG = EXTRA_LARGE_ICON + ".big";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400943
944 /**
945 * {@link #extras} key: this is the progress value supplied to
946 * {@link Builder#setProgress(int, int, boolean)}.
947 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400948 public static final String EXTRA_PROGRESS = "android.progress";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400949
950 /**
951 * {@link #extras} key: this is the maximum value supplied to
952 * {@link Builder#setProgress(int, int, boolean)}.
953 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400954 public static final String EXTRA_PROGRESS_MAX = "android.progressMax";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400955
956 /**
957 * {@link #extras} key: whether the progress bar is indeterminate, supplied to
958 * {@link Builder#setProgress(int, int, boolean)}.
959 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400960 public static final String EXTRA_PROGRESS_INDETERMINATE = "android.progressIndeterminate";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400961
962 /**
963 * {@link #extras} key: whether {@link #when} should be shown as a count-up timer (specifically
964 * a {@link android.widget.Chronometer}) instead of a timestamp, as supplied to
965 * {@link Builder#setUsesChronometer(boolean)}.
966 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400967 public static final String EXTRA_SHOW_CHRONOMETER = "android.showChronometer";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400968
969 /**
Selim Cinek81c23aa2016-02-25 16:23:13 -0800970 * {@link #extras} key: whether the chronometer set on the notification should count down
971 * instead of counting up. Is only relevant if key {@link #EXTRA_SHOW_CHRONOMETER} is present.
Adrian Roos96b7e202016-05-17 13:50:38 -0700972 * This extra is a boolean. The default is false.
Selim Cinek81c23aa2016-02-25 16:23:13 -0800973 */
Adrian Roos96b7e202016-05-17 13:50:38 -0700974 public static final String EXTRA_CHRONOMETER_COUNT_DOWN = "android.chronometerCountDown";
Selim Cinek81c23aa2016-02-25 16:23:13 -0800975
976 /**
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400977 * {@link #extras} key: whether {@link #when} should be shown,
978 * as supplied to {@link Builder#setShowWhen(boolean)}.
979 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400980 public static final String EXTRA_SHOW_WHEN = "android.showWhen";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400981
982 /**
983 * {@link #extras} key: this is a bitmap to be shown in {@link BigPictureStyle} expanded
984 * notifications, supplied to {@link BigPictureStyle#bigPicture(android.graphics.Bitmap)}.
985 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400986 public static final String EXTRA_PICTURE = "android.picture";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400987
988 /**
989 * {@link #extras} key: An array of CharSequences to show in {@link InboxStyle} expanded
990 * notifications, each of which was supplied to {@link InboxStyle#addLine(CharSequence)}.
991 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400992 public static final String EXTRA_TEXT_LINES = "android.textLines";
Dan Sandler842dd772014-05-15 09:36:47 -0400993
994 /**
995 * {@link #extras} key: A string representing the name of the specific
996 * {@link android.app.Notification.Style} used to create this notification.
997 */
Chris Wren91ad5632013-06-05 15:05:57 -0400998 public static final String EXTRA_TEMPLATE = "android.template";
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400999
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001000 /**
Chris Wrene6c48932014-09-29 17:19:27 -04001001 * {@link #extras} key: A String array containing the people that this notification relates to,
1002 * each of which was supplied to {@link Builder#addPerson(String)}.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001003 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -04001004 public static final String EXTRA_PEOPLE = "android.people";
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001005
1006 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -04001007 * Allow certain system-generated notifications to appear before the device is provisioned.
1008 * Only available to notifications coming from the android package.
1009 * @hide
1010 */
Maurice Lam96c10032017-03-29 15:42:38 -07001011 @SystemApi
John Spurlockfd7f1e02014-03-18 16:41:57 -04001012 public static final String EXTRA_ALLOW_DURING_SETUP = "android.allowDuringSetup";
1013
1014 /**
Jose Limae9e3b3b2014-05-18 23:44:50 -07001015 * {@link #extras} key: A
1016 * {@link android.content.ContentUris content URI} pointing to an image that can be displayed
1017 * in the background when the notification is selected. The URI must point to an image stream
1018 * suitable for passing into
1019 * {@link android.graphics.BitmapFactory#decodeStream(java.io.InputStream)
1020 * BitmapFactory.decodeStream}; all other content types will be ignored. The content provider
1021 * URI used for this purpose must require no permissions to read the image data.
1022 */
1023 public static final String EXTRA_BACKGROUND_IMAGE_URI = "android.backgroundImageUri";
1024
1025 /**
Dan Sandler842dd772014-05-15 09:36:47 -04001026 * {@link #extras} key: A
Jeff Browndba34ba2014-06-24 20:46:03 -07001027 * {@link android.media.session.MediaSession.Token} associated with a
Dan Sandler842dd772014-05-15 09:36:47 -04001028 * {@link android.app.Notification.MediaStyle} notification.
1029 */
1030 public static final String EXTRA_MEDIA_SESSION = "android.mediaSession";
1031
1032 /**
Bryan Mawhinneye191f902014-07-22 12:50:09 +01001033 * {@link #extras} key: the indices of actions to be shown in the compact view,
1034 * as supplied to (e.g.) {@link MediaStyle#setShowActionsInCompactView(int...)}.
1035 */
1036 public static final String EXTRA_COMPACT_ACTIONS = "android.compactActions";
1037
Christoph Studer943aa672014-08-03 20:31:16 +02001038 /**
Alex Hillsfc737de2016-03-23 17:33:02 -04001039 * {@link #extras} key: the username to be displayed for all messages sent by the user including
1040 * direct replies
Adrian Roos96b7e202016-05-17 13:50:38 -07001041 * {@link android.app.Notification.MessagingStyle} notification. This extra is a
1042 * {@link CharSequence}
Alex Hillsfc737de2016-03-23 17:33:02 -04001043 */
1044 public static final String EXTRA_SELF_DISPLAY_NAME = "android.selfDisplayName";
1045
1046 /**
Adrian Roos96b7e202016-05-17 13:50:38 -07001047 * {@link #extras} key: a {@link CharSequence} to be displayed as the title to a conversation
Alex Hillsd9b04d92016-04-11 16:38:16 -04001048 * represented by a {@link android.app.Notification.MessagingStyle}
Alex Hillsfc737de2016-03-23 17:33:02 -04001049 */
Alex Hillsd9b04d92016-04-11 16:38:16 -04001050 public static final String EXTRA_CONVERSATION_TITLE = "android.conversationTitle";
Alex Hillsfc737de2016-03-23 17:33:02 -04001051
1052 /**
1053 * {@link #extras} key: an array of {@link android.app.Notification.MessagingStyle.Message}
1054 * bundles provided by a
Adrian Roos96b7e202016-05-17 13:50:38 -07001055 * {@link android.app.Notification.MessagingStyle} notification. This extra is a parcelable
1056 * array of bundles.
Alex Hillsfc737de2016-03-23 17:33:02 -04001057 */
1058 public static final String EXTRA_MESSAGES = "android.messages";
1059
1060 /**
Adrian Roos437cd562017-01-18 15:47:03 -08001061 * {@link #extras} key: an array of
1062 * {@link android.app.Notification.MessagingStyle#addHistoricMessage historic}
1063 * {@link android.app.Notification.MessagingStyle.Message} bundles provided by a
1064 * {@link android.app.Notification.MessagingStyle} notification. This extra is a parcelable
1065 * array of bundles.
1066 */
1067 public static final String EXTRA_HISTORIC_MESSAGES = "android.messages.historic";
1068
1069 /**
Selim Cinek7b9605b2017-01-19 17:36:00 -08001070 * {@link #extras} key: whether the notification should be colorized as
1071 * supplied to {@link Builder#setColorized(boolean)}}.
1072 */
1073 public static final String EXTRA_COLORIZED = "android.colorized";
1074
1075 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04001076 * @hide
1077 */
1078 public static final String EXTRA_BUILDER_APPLICATION_INFO = "android.appInfo";
1079
Selim Cinek247fa012016-02-18 09:50:48 -08001080 /**
1081 * @hide
1082 */
1083 public static final String EXTRA_CONTAINS_CUSTOM_VIEW = "android.contains.customView";
1084
Shane Brennan472a3b32016-12-12 15:28:10 -08001085 /**
1086 * {@link #extras} key: the audio contents of this notification.
1087 *
1088 * This is for use when rendering the notification on an audio-focused interface;
1089 * the audio contents are a complete sound sample that contains the contents/body of the
1090 * notification. This may be used in substitute of a Text-to-Speech reading of the
1091 * notification. For example if the notification represents a voice message this should point
1092 * to the audio of that message.
1093 *
1094 * The data stored under this key should be a String representation of a Uri that contains the
1095 * audio contents in one of the following formats: WAV, PCM 16-bit, AMR-WB.
1096 *
1097 * This extra is unnecessary if you are using {@code MessagingStyle} since each {@code Message}
1098 * has a field for holding data URI. That field can be used for audio.
1099 * See {@code Message#setData}.
1100 *
1101 * Example usage:
1102 * <pre>
1103 * {@code
1104 * Notification.Builder myBuilder = (build your Notification as normal);
1105 * myBuilder.getExtras().putString(EXTRA_AUDIO_CONTENTS_URI, myAudioUri.toString());
1106 * }
1107 * </pre>
1108 */
1109 public static final String EXTRA_AUDIO_CONTENTS_URI = "android.audioContents";
1110
Dan Sandler80eaa592016-04-14 23:34:54 -04001111 /** @hide */
1112 @SystemApi
Dan Sandler732bd6c2016-04-12 14:20:32 -04001113 public static final String EXTRA_SUBSTITUTE_APP_NAME = "android.substName";
1114
Dan Sandlerd63f9322015-05-06 15:18:49 -04001115 private Icon mSmallIcon;
1116 private Icon mLargeIcon;
1117
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04001118 private String mChannelId;
Julia Reynolds2a128742016-11-28 14:29:25 -05001119 private long mTimeout;
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04001120
Julia Reynolds13d898c2017-02-02 12:22:05 -05001121 private String mShortcutId;
Julia Reynolds3aedded2017-03-31 14:42:09 -04001122 private CharSequence mSettingsText;
Julia Reynolds13d898c2017-02-02 12:22:05 -05001123
Julia Reynoldsa79c3712017-04-21 10:29:57 -04001124 /** @hide */
1125 @IntDef(prefix = { "GROUP_ALERT_" }, value = {
1126 GROUP_ALERT_ALL, GROUP_ALERT_CHILDREN, GROUP_ALERT_SUMMARY
1127 })
1128 @Retention(RetentionPolicy.SOURCE)
1129 public @interface GroupAlertBehavior {}
1130
1131 /**
1132 * Constant for {@link Builder#setGroupAlertBehavior(int)}, meaning that all notifications in a
1133 * group with sound or vibration ought to make sound or vibrate (respectively), so this
1134 * notification will not be muted when it is in a group.
1135 */
1136 public static final int GROUP_ALERT_ALL = 0;
1137
1138 /**
1139 * Constant for {@link Builder#setGroupAlertBehavior(int)}, meaning that all children
1140 * notification in a group should be silenced (no sound or vibration) even if they are posted
1141 * to a {@link NotificationChannel} that has sound and/or vibration. Use this constant to
1142 * mute this notification if this notification is a group child.
1143 *
1144 * <p> For example, you might want to use this constant if you post a number of children
1145 * notifications at once (say, after a periodic sync), and only need to notify the user
1146 * audibly once.
1147 */
1148 public static final int GROUP_ALERT_SUMMARY = 1;
1149
1150 /**
1151 * Constant for {@link Builder#setGroupAlertBehavior(int)}, meaning that the summary
1152 * notification in a group should be silenced (no sound or vibration) even if they are
1153 * posted to a {@link NotificationChannel} that has sound and/or vibration. Use this constant
1154 * to mute this notification if this notification is a group summary.
1155 *
1156 * <p>For example, you might want to use this constant if only the children notifications
1157 * in your group have content and the summary is only used to visually group notifications.
1158 */
1159 public static final int GROUP_ALERT_CHILDREN = 2;
1160
1161 private int mGroupAlertBehavior = GROUP_ALERT_ALL;
1162
Julia Reynolds13d898c2017-02-02 12:22:05 -05001163 /**
1164 * If this notification is being shown as a badge, always show as a number.
1165 */
1166 public static final int BADGE_ICON_NONE = 0;
1167
1168 /**
1169 * If this notification is being shown as a badge, use the {@link #getSmallIcon()} to
1170 * represent this notification.
1171 */
1172 public static final int BADGE_ICON_SMALL = 1;
1173
1174 /**
1175 * If this notification is being shown as a badge, use the {@link #getLargeIcon()} to
1176 * represent this notification.
1177 */
1178 public static final int BADGE_ICON_LARGE = 2;
Julia Reynolds7d5b4da2017-03-20 10:18:49 -04001179 private int mBadgeIcon = BADGE_ICON_NONE;
Julia Reynolds13d898c2017-02-02 12:22:05 -05001180
Chris Wren51c75102013-07-16 20:49:17 -04001181 /**
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001182 * Structure to encapsulate a named action that can be shown as part of this notification.
1183 * It must include an icon, a label, and a {@link PendingIntent} to be fired when the action is
1184 * selected by the user.
1185 * <p>
Griff Hazen959591e2014-05-15 22:26:18 -07001186 * Apps should use {@link Notification.Builder#addAction(int, CharSequence, PendingIntent)}
1187 * or {@link Notification.Builder#addAction(Notification.Action)}
1188 * to attach actions.
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001189 */
Daniel Sandlerea2a3172013-02-20 22:24:20 -05001190 public static class Action implements Parcelable {
Shane Brennan472a3b32016-12-12 15:28:10 -08001191 /**
1192 * {@link #extras} key: Keys to a {@link Parcelable} {@link ArrayList} of
1193 * {@link RemoteInput}s.
1194 *
1195 * This is intended for {@link RemoteInput}s that only accept data, meaning
1196 * {@link RemoteInput#getAllowFreeFormInput} is false, {@link RemoteInput#getChoices}
1197 * is null or empty, and {@link RemoteInput#getAllowedDataTypes is non-null and not
1198 * empty. These {@link RemoteInput}s will be ignored by devices that do not
1199 * support non-text-based {@link RemoteInput}s. See {@link Builder#build}.
1200 *
1201 * You can test if a RemoteInput matches these constraints using
1202 * {@link RemoteInput#isDataOnly}.
1203 */
1204 private static final String EXTRA_DATA_ONLY_INPUTS = "android.extra.DATA_ONLY_INPUTS";
1205
Griff Hazen959591e2014-05-15 22:26:18 -07001206 private final Bundle mExtras;
Dan Sandler86647982015-05-13 23:41:13 -04001207 private Icon mIcon;
Griff Hazen61a9e862014-05-22 16:05:19 -07001208 private final RemoteInput[] mRemoteInputs;
Alex Hills1f27f882017-01-12 11:24:46 -05001209 private boolean mAllowGeneratedReplies = true;
Griff Hazen959591e2014-05-15 22:26:18 -07001210
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001211 /**
1212 * Small icon representing the action.
Dan Sandler86647982015-05-13 23:41:13 -04001213 *
1214 * @deprecated Use {@link Action#getIcon()} instead.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001215 */
Dan Sandler86647982015-05-13 23:41:13 -04001216 @Deprecated
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001217 public int icon;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001218
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001219 /**
1220 * Title of the action.
1221 */
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001222 public CharSequence title;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001223
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001224 /**
1225 * Intent to send when the user invokes this action. May be null, in which case the action
1226 * may be rendered in a disabled presentation by the system UI.
1227 */
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001228 public PendingIntent actionIntent;
Griff Hazen959591e2014-05-15 22:26:18 -07001229
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001230 private Action(Parcel in) {
Dan Sandler86647982015-05-13 23:41:13 -04001231 if (in.readInt() != 0) {
1232 mIcon = Icon.CREATOR.createFromParcel(in);
Dan Sandler68079d52015-07-22 10:45:30 -04001233 if (mIcon.getType() == Icon.TYPE_RESOURCE) {
1234 icon = mIcon.getResId();
1235 }
Dan Sandler86647982015-05-13 23:41:13 -04001236 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001237 title = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
1238 if (in.readInt() == 1) {
1239 actionIntent = PendingIntent.CREATOR.createFromParcel(in);
1240 }
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001241 mExtras = Bundle.setDefusable(in.readBundle(), true);
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001242 mRemoteInputs = in.createTypedArray(RemoteInput.CREATOR);
Alex Hills42b0c4d2016-04-26 13:35:36 -04001243 mAllowGeneratedReplies = in.readInt() == 1;
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001244 }
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001245
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001246 /**
Dan Sandler86647982015-05-13 23:41:13 -04001247 * @deprecated Use {@link android.app.Notification.Action.Builder}.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001248 */
Dan Sandler86647982015-05-13 23:41:13 -04001249 @Deprecated
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001250 public Action(int icon, CharSequence title, PendingIntent intent) {
Alex Hills1f27f882017-01-12 11:24:46 -05001251 this(Icon.createWithResource("", icon), title, intent, new Bundle(), null, true);
Griff Hazen959591e2014-05-15 22:26:18 -07001252 }
1253
Adrian Roos7af53622016-10-12 13:44:05 -07001254 /** Keep in sync with {@link Notification.Action.Builder#Builder(Action)}! */
Dan Sandler86647982015-05-13 23:41:13 -04001255 private Action(Icon icon, CharSequence title, PendingIntent intent, Bundle extras,
Alex Hills42b0c4d2016-04-26 13:35:36 -04001256 RemoteInput[] remoteInputs, boolean allowGeneratedReplies) {
Dan Sandler86647982015-05-13 23:41:13 -04001257 this.mIcon = icon;
Gus Prevasf5bff46f2015-08-24 10:34:28 -04001258 if (icon != null && icon.getType() == Icon.TYPE_RESOURCE) {
1259 this.icon = icon.getResId();
1260 }
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001261 this.title = title;
1262 this.actionIntent = intent;
Griff Hazen959591e2014-05-15 22:26:18 -07001263 this.mExtras = extras != null ? extras : new Bundle();
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001264 this.mRemoteInputs = remoteInputs;
Alex Hills42b0c4d2016-04-26 13:35:36 -04001265 this.mAllowGeneratedReplies = allowGeneratedReplies;
Griff Hazen959591e2014-05-15 22:26:18 -07001266 }
1267
1268 /**
Dan Sandler86647982015-05-13 23:41:13 -04001269 * Return an icon representing the action.
1270 */
1271 public Icon getIcon() {
1272 if (mIcon == null && icon != 0) {
1273 // you snuck an icon in here without using the builder; let's try to keep it
1274 mIcon = Icon.createWithResource("", icon);
1275 }
1276 return mIcon;
1277 }
1278
1279 /**
Griff Hazen959591e2014-05-15 22:26:18 -07001280 * Get additional metadata carried around with this Action.
1281 */
1282 public Bundle getExtras() {
1283 return mExtras;
1284 }
1285
1286 /**
Alex Hills42b0c4d2016-04-26 13:35:36 -04001287 * Return whether the platform should automatically generate possible replies for this
1288 * {@link Action}
1289 */
1290 public boolean getAllowGeneratedReplies() {
1291 return mAllowGeneratedReplies;
1292 }
1293
1294 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001295 * Get the list of inputs to be collected from the user when this action is sent.
Shane Brennan472a3b32016-12-12 15:28:10 -08001296 * May return null if no remote inputs were added. Only returns inputs which accept
1297 * a text input. For inputs which only accept data use {@link #getDataOnlyRemoteInputs}.
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001298 */
1299 public RemoteInput[] getRemoteInputs() {
1300 return mRemoteInputs;
1301 }
1302
1303 /**
Shane Brennan472a3b32016-12-12 15:28:10 -08001304 * Get the list of inputs to be collected from the user that ONLY accept data when this
1305 * action is sent. These remote inputs are guaranteed to return true on a call to
1306 * {@link RemoteInput#isDataOnly}.
1307 *
Shane Brennanf670d6c2017-04-25 13:05:45 -07001308 * Returns null if there are no data-only remote inputs.
Shane Brennan472a3b32016-12-12 15:28:10 -08001309 *
1310 * This method exists so that legacy RemoteInput collectors that pre-date the addition
1311 * of non-textual RemoteInputs do not access these remote inputs.
1312 */
1313 public RemoteInput[] getDataOnlyRemoteInputs() {
1314 return (RemoteInput[]) mExtras.getParcelableArray(EXTRA_DATA_ONLY_INPUTS);
1315 }
1316
1317 /**
Griff Hazen959591e2014-05-15 22:26:18 -07001318 * Builder class for {@link Action} objects.
1319 */
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001320 public static final class Builder {
Dan Sandler86647982015-05-13 23:41:13 -04001321 private final Icon mIcon;
Griff Hazen959591e2014-05-15 22:26:18 -07001322 private final CharSequence mTitle;
1323 private final PendingIntent mIntent;
Alex Hills1f27f882017-01-12 11:24:46 -05001324 private boolean mAllowGeneratedReplies = true;
Griff Hazen959591e2014-05-15 22:26:18 -07001325 private final Bundle mExtras;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001326 private ArrayList<RemoteInput> mRemoteInputs;
Griff Hazen959591e2014-05-15 22:26:18 -07001327
1328 /**
1329 * Construct a new builder for {@link Action} object.
1330 * @param icon icon to show for this action
1331 * @param title the title of the action
1332 * @param intent the {@link PendingIntent} to fire when users trigger this action
1333 */
Dan Sandler86647982015-05-13 23:41:13 -04001334 @Deprecated
Griff Hazen959591e2014-05-15 22:26:18 -07001335 public Builder(int icon, CharSequence title, PendingIntent intent) {
Adrian Roos7af53622016-10-12 13:44:05 -07001336 this(Icon.createWithResource("", icon), title, intent);
Dan Sandler86647982015-05-13 23:41:13 -04001337 }
1338
1339 /**
1340 * Construct a new builder for {@link Action} object.
1341 * @param icon icon to show for this action
1342 * @param title the title of the action
1343 * @param intent the {@link PendingIntent} to fire when users trigger this action
1344 */
1345 public Builder(Icon icon, CharSequence title, PendingIntent intent) {
Alex Hills1f27f882017-01-12 11:24:46 -05001346 this(icon, title, intent, new Bundle(), null, true);
Griff Hazen959591e2014-05-15 22:26:18 -07001347 }
1348
1349 /**
1350 * Construct a new builder for {@link Action} object using the fields from an
1351 * {@link Action}.
1352 * @param action the action to read fields from.
1353 */
1354 public Builder(Action action) {
Adrian Roos7af53622016-10-12 13:44:05 -07001355 this(action.getIcon(), action.title, action.actionIntent,
1356 new Bundle(action.mExtras), action.getRemoteInputs(),
1357 action.getAllowGeneratedReplies());
Griff Hazen959591e2014-05-15 22:26:18 -07001358 }
1359
Dan Sandler86647982015-05-13 23:41:13 -04001360 private Builder(Icon icon, CharSequence title, PendingIntent intent, Bundle extras,
Adrian Roos7af53622016-10-12 13:44:05 -07001361 RemoteInput[] remoteInputs, boolean allowGeneratedReplies) {
Griff Hazen959591e2014-05-15 22:26:18 -07001362 mIcon = icon;
1363 mTitle = title;
1364 mIntent = intent;
1365 mExtras = extras;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001366 if (remoteInputs != null) {
1367 mRemoteInputs = new ArrayList<RemoteInput>(remoteInputs.length);
1368 Collections.addAll(mRemoteInputs, remoteInputs);
1369 }
Adrian Roos7af53622016-10-12 13:44:05 -07001370 mAllowGeneratedReplies = allowGeneratedReplies;
Griff Hazen959591e2014-05-15 22:26:18 -07001371 }
1372
1373 /**
1374 * Merge additional metadata into this builder.
1375 *
1376 * <p>Values within the Bundle will replace existing extras values in this Builder.
1377 *
1378 * @see Notification.Action#extras
1379 */
1380 public Builder addExtras(Bundle extras) {
1381 if (extras != null) {
1382 mExtras.putAll(extras);
1383 }
1384 return this;
1385 }
1386
1387 /**
1388 * Get the metadata Bundle used by this Builder.
1389 *
1390 * <p>The returned Bundle is shared with this Builder.
1391 */
1392 public Bundle getExtras() {
1393 return mExtras;
1394 }
1395
1396 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001397 * Add an input to be collected from the user when this action is sent.
1398 * Response values can be retrieved from the fired intent by using the
1399 * {@link RemoteInput#getResultsFromIntent} function.
1400 * @param remoteInput a {@link RemoteInput} to add to the action
1401 * @return this object for method chaining
1402 */
1403 public Builder addRemoteInput(RemoteInput remoteInput) {
1404 if (mRemoteInputs == null) {
1405 mRemoteInputs = new ArrayList<RemoteInput>();
1406 }
1407 mRemoteInputs.add(remoteInput);
1408 return this;
1409 }
1410
1411 /**
Alex Hills42b0c4d2016-04-26 13:35:36 -04001412 * Set whether the platform should automatically generate possible replies to add to
1413 * {@link RemoteInput#getChoices()}. If the {@link Action} doesn't have a
1414 * {@link RemoteInput}, this has no effect.
1415 * @param allowGeneratedReplies {@code true} to allow generated replies, {@code false}
1416 * otherwise
1417 * @return this object for method chaining
Alex Hills1f27f882017-01-12 11:24:46 -05001418 * The default value is {@code true}
Alex Hills42b0c4d2016-04-26 13:35:36 -04001419 */
1420 public Builder setAllowGeneratedReplies(boolean allowGeneratedReplies) {
1421 mAllowGeneratedReplies = allowGeneratedReplies;
1422 return this;
1423 }
1424
1425 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001426 * Apply an extender to this action builder. Extenders may be used to add
1427 * metadata or change options on this builder.
1428 */
Griff Hazen61a9e862014-05-22 16:05:19 -07001429 public Builder extend(Extender extender) {
1430 extender.extend(this);
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001431 return this;
1432 }
1433
1434 /**
Griff Hazen959591e2014-05-15 22:26:18 -07001435 * Combine all of the options that have been set and return a new {@link Action}
1436 * object.
1437 * @return the built action
1438 */
1439 public Action build() {
Shane Brennan472a3b32016-12-12 15:28:10 -08001440 ArrayList<RemoteInput> dataOnlyInputs = new ArrayList<>();
1441 RemoteInput[] previousDataInputs =
1442 (RemoteInput[]) mExtras.getParcelableArray(EXTRA_DATA_ONLY_INPUTS);
Felipe Lemedfd11962017-01-18 18:38:46 -08001443 if (previousDataInputs != null) {
Shane Brennan472a3b32016-12-12 15:28:10 -08001444 for (RemoteInput input : previousDataInputs) {
1445 dataOnlyInputs.add(input);
1446 }
1447 }
1448 List<RemoteInput> textInputs = new ArrayList<>();
1449 if (mRemoteInputs != null) {
1450 for (RemoteInput input : mRemoteInputs) {
1451 if (input.isDataOnly()) {
1452 dataOnlyInputs.add(input);
1453 } else {
1454 textInputs.add(input);
1455 }
1456 }
1457 }
1458 if (!dataOnlyInputs.isEmpty()) {
1459 RemoteInput[] dataInputsArr =
1460 dataOnlyInputs.toArray(new RemoteInput[dataOnlyInputs.size()]);
1461 mExtras.putParcelableArray(EXTRA_DATA_ONLY_INPUTS, dataInputsArr);
1462 }
1463 RemoteInput[] textInputsArr = textInputs.isEmpty()
1464 ? null : textInputs.toArray(new RemoteInput[textInputs.size()]);
1465 return new Action(mIcon, mTitle, mIntent, mExtras, textInputsArr,
Alex Hills42b0c4d2016-04-26 13:35:36 -04001466 mAllowGeneratedReplies);
Griff Hazen959591e2014-05-15 22:26:18 -07001467 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001468 }
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001469
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001470 @Override
1471 public Action clone() {
1472 return new Action(
Dan Sandler86647982015-05-13 23:41:13 -04001473 getIcon(),
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001474 title,
1475 actionIntent, // safe to alias
Julia Reynolds53c934c2016-09-16 14:03:43 -04001476 mExtras == null ? new Bundle() : new Bundle(mExtras),
Alex Hills42b0c4d2016-04-26 13:35:36 -04001477 getRemoteInputs(),
1478 getAllowGeneratedReplies());
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001479 }
1480 @Override
1481 public int describeContents() {
1482 return 0;
1483 }
1484 @Override
1485 public void writeToParcel(Parcel out, int flags) {
Dan Sandler86647982015-05-13 23:41:13 -04001486 final Icon ic = getIcon();
1487 if (ic != null) {
1488 out.writeInt(1);
1489 ic.writeToParcel(out, 0);
1490 } else {
1491 out.writeInt(0);
1492 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001493 TextUtils.writeToParcel(title, out, flags);
1494 if (actionIntent != null) {
1495 out.writeInt(1);
1496 actionIntent.writeToParcel(out, flags);
1497 } else {
1498 out.writeInt(0);
1499 }
Griff Hazen959591e2014-05-15 22:26:18 -07001500 out.writeBundle(mExtras);
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001501 out.writeTypedArray(mRemoteInputs, flags);
Alex Hills42b0c4d2016-04-26 13:35:36 -04001502 out.writeInt(mAllowGeneratedReplies ? 1 : 0);
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001503 }
Griff Hazen959591e2014-05-15 22:26:18 -07001504 public static final Parcelable.Creator<Action> CREATOR =
1505 new Parcelable.Creator<Action>() {
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001506 public Action createFromParcel(Parcel in) {
1507 return new Action(in);
1508 }
1509 public Action[] newArray(int size) {
1510 return new Action[size];
1511 }
1512 };
Griff Hazen61a9e862014-05-22 16:05:19 -07001513
1514 /**
1515 * Extender interface for use with {@link Builder#extend}. Extenders may be used to add
1516 * metadata or change options on an action builder.
1517 */
1518 public interface Extender {
1519 /**
1520 * Apply this extender to a notification action builder.
1521 * @param builder the builder to be modified.
1522 * @return the build object for chaining.
1523 */
1524 public Builder extend(Builder builder);
1525 }
1526
1527 /**
1528 * Wearable extender for notification actions. To add extensions to an action,
1529 * create a new {@link android.app.Notification.Action.WearableExtender} object using
1530 * the {@code WearableExtender()} constructor and apply it to a
1531 * {@link android.app.Notification.Action.Builder} using
1532 * {@link android.app.Notification.Action.Builder#extend}.
1533 *
1534 * <pre class="prettyprint">
1535 * Notification.Action action = new Notification.Action.Builder(
1536 * R.drawable.archive_all, "Archive all", actionIntent)
Griff Hazen14f57992014-05-26 09:07:14 -07001537 * .extend(new Notification.Action.WearableExtender()
Griff Hazen61a9e862014-05-22 16:05:19 -07001538 * .setAvailableOffline(false))
Griff Hazen14f57992014-05-26 09:07:14 -07001539 * .build();</pre>
Griff Hazen61a9e862014-05-22 16:05:19 -07001540 */
1541 public static final class WearableExtender implements Extender {
1542 /** Notification action extra which contains wearable extensions */
1543 private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS";
1544
Pete Gastaf6781d2014-10-07 15:17:05 -04001545 // Keys within EXTRA_WEARABLE_EXTENSIONS for wearable options.
Griff Hazen61a9e862014-05-22 16:05:19 -07001546 private static final String KEY_FLAGS = "flags";
Pete Gastaf6781d2014-10-07 15:17:05 -04001547 private static final String KEY_IN_PROGRESS_LABEL = "inProgressLabel";
1548 private static final String KEY_CONFIRM_LABEL = "confirmLabel";
1549 private static final String KEY_CANCEL_LABEL = "cancelLabel";
Griff Hazen61a9e862014-05-22 16:05:19 -07001550
1551 // Flags bitwise-ored to mFlags
1552 private static final int FLAG_AVAILABLE_OFFLINE = 0x1;
Alex Hills9ab3a232016-04-05 14:54:56 -04001553 private static final int FLAG_HINT_LAUNCHES_ACTIVITY = 1 << 1;
Alex Hills9f087612016-06-07 09:08:59 -04001554 private static final int FLAG_HINT_DISPLAY_INLINE = 1 << 2;
Griff Hazen61a9e862014-05-22 16:05:19 -07001555
1556 // Default value for flags integer
1557 private static final int DEFAULT_FLAGS = FLAG_AVAILABLE_OFFLINE;
1558
1559 private int mFlags = DEFAULT_FLAGS;
1560
Pete Gastaf6781d2014-10-07 15:17:05 -04001561 private CharSequence mInProgressLabel;
1562 private CharSequence mConfirmLabel;
1563 private CharSequence mCancelLabel;
1564
Griff Hazen61a9e862014-05-22 16:05:19 -07001565 /**
1566 * Create a {@link android.app.Notification.Action.WearableExtender} with default
1567 * options.
1568 */
1569 public WearableExtender() {
1570 }
1571
1572 /**
1573 * Create a {@link android.app.Notification.Action.WearableExtender} by reading
1574 * wearable options present in an existing notification action.
1575 * @param action the notification action to inspect.
1576 */
1577 public WearableExtender(Action action) {
1578 Bundle wearableBundle = action.getExtras().getBundle(EXTRA_WEARABLE_EXTENSIONS);
1579 if (wearableBundle != null) {
1580 mFlags = wearableBundle.getInt(KEY_FLAGS, DEFAULT_FLAGS);
Pete Gastaf6781d2014-10-07 15:17:05 -04001581 mInProgressLabel = wearableBundle.getCharSequence(KEY_IN_PROGRESS_LABEL);
1582 mConfirmLabel = wearableBundle.getCharSequence(KEY_CONFIRM_LABEL);
1583 mCancelLabel = wearableBundle.getCharSequence(KEY_CANCEL_LABEL);
Griff Hazen61a9e862014-05-22 16:05:19 -07001584 }
1585 }
1586
1587 /**
1588 * Apply wearable extensions to a notification action that is being built. This is
1589 * typically called by the {@link android.app.Notification.Action.Builder#extend}
1590 * method of {@link android.app.Notification.Action.Builder}.
1591 */
1592 @Override
1593 public Action.Builder extend(Action.Builder builder) {
1594 Bundle wearableBundle = new Bundle();
1595
1596 if (mFlags != DEFAULT_FLAGS) {
1597 wearableBundle.putInt(KEY_FLAGS, mFlags);
1598 }
Pete Gastaf6781d2014-10-07 15:17:05 -04001599 if (mInProgressLabel != null) {
1600 wearableBundle.putCharSequence(KEY_IN_PROGRESS_LABEL, mInProgressLabel);
1601 }
1602 if (mConfirmLabel != null) {
1603 wearableBundle.putCharSequence(KEY_CONFIRM_LABEL, mConfirmLabel);
1604 }
1605 if (mCancelLabel != null) {
1606 wearableBundle.putCharSequence(KEY_CANCEL_LABEL, mCancelLabel);
1607 }
Griff Hazen61a9e862014-05-22 16:05:19 -07001608
1609 builder.getExtras().putBundle(EXTRA_WEARABLE_EXTENSIONS, wearableBundle);
1610 return builder;
1611 }
1612
1613 @Override
1614 public WearableExtender clone() {
1615 WearableExtender that = new WearableExtender();
1616 that.mFlags = this.mFlags;
Pete Gastaf6781d2014-10-07 15:17:05 -04001617 that.mInProgressLabel = this.mInProgressLabel;
1618 that.mConfirmLabel = this.mConfirmLabel;
1619 that.mCancelLabel = this.mCancelLabel;
Griff Hazen61a9e862014-05-22 16:05:19 -07001620 return that;
1621 }
1622
1623 /**
1624 * Set whether this action is available when the wearable device is not connected to
1625 * a companion device. The user can still trigger this action when the wearable device is
1626 * offline, but a visual hint will indicate that the action may not be available.
1627 * Defaults to true.
1628 */
1629 public WearableExtender setAvailableOffline(boolean availableOffline) {
1630 setFlag(FLAG_AVAILABLE_OFFLINE, availableOffline);
1631 return this;
1632 }
1633
1634 /**
1635 * Get whether this action is available when the wearable device is not connected to
1636 * a companion device. The user can still trigger this action when the wearable device is
1637 * offline, but a visual hint will indicate that the action may not be available.
1638 * Defaults to true.
1639 */
1640 public boolean isAvailableOffline() {
1641 return (mFlags & FLAG_AVAILABLE_OFFLINE) != 0;
1642 }
1643
1644 private void setFlag(int mask, boolean value) {
1645 if (value) {
1646 mFlags |= mask;
1647 } else {
1648 mFlags &= ~mask;
1649 }
1650 }
Pete Gastaf6781d2014-10-07 15:17:05 -04001651
1652 /**
1653 * Set a label to display while the wearable is preparing to automatically execute the
1654 * action. This is usually a 'ing' verb ending in ellipsis like "Sending..."
1655 *
1656 * @param label the label to display while the action is being prepared to execute
1657 * @return this object for method chaining
1658 */
1659 public WearableExtender setInProgressLabel(CharSequence label) {
1660 mInProgressLabel = label;
1661 return this;
1662 }
1663
1664 /**
1665 * Get the label to display while the wearable is preparing to automatically execute
1666 * the action. This is usually a 'ing' verb ending in ellipsis like "Sending..."
1667 *
1668 * @return the label to display while the action is being prepared to execute
1669 */
1670 public CharSequence getInProgressLabel() {
1671 return mInProgressLabel;
1672 }
1673
1674 /**
1675 * Set a label to display to confirm that the action should be executed.
1676 * This is usually an imperative verb like "Send".
1677 *
1678 * @param label the label to confirm the action should be executed
1679 * @return this object for method chaining
1680 */
1681 public WearableExtender setConfirmLabel(CharSequence label) {
1682 mConfirmLabel = label;
1683 return this;
1684 }
1685
1686 /**
1687 * Get the label to display to confirm that the action should be executed.
1688 * This is usually an imperative verb like "Send".
1689 *
1690 * @return the label to confirm the action should be executed
1691 */
1692 public CharSequence getConfirmLabel() {
1693 return mConfirmLabel;
1694 }
1695
1696 /**
1697 * Set a label to display to cancel the action.
1698 * This is usually an imperative verb, like "Cancel".
1699 *
1700 * @param label the label to display to cancel the action
1701 * @return this object for method chaining
1702 */
1703 public WearableExtender setCancelLabel(CharSequence label) {
1704 mCancelLabel = label;
1705 return this;
1706 }
1707
1708 /**
1709 * Get the label to display to cancel the action.
1710 * This is usually an imperative verb like "Cancel".
1711 *
1712 * @return the label to display to cancel the action
1713 */
1714 public CharSequence getCancelLabel() {
1715 return mCancelLabel;
1716 }
Alex Hills9ab3a232016-04-05 14:54:56 -04001717
1718 /**
1719 * Set a hint that this Action will launch an {@link Activity} directly, telling the
1720 * platform that it can generate the appropriate transitions.
1721 * @param hintLaunchesActivity {@code true} if the content intent will launch
1722 * an activity and transitions should be generated, false otherwise.
1723 * @return this object for method chaining
1724 */
Alex Hills4ec3ff42016-04-12 11:36:18 -04001725 public WearableExtender setHintLaunchesActivity(
Alex Hills9ab3a232016-04-05 14:54:56 -04001726 boolean hintLaunchesActivity) {
1727 setFlag(FLAG_HINT_LAUNCHES_ACTIVITY, hintLaunchesActivity);
1728 return this;
1729 }
1730
1731 /**
1732 * Get a hint that this Action will launch an {@link Activity} directly, telling the
1733 * platform that it can generate the appropriate transitions
1734 * @return {@code true} if the content intent will launch an activity and transitions
1735 * should be generated, false otherwise. The default value is {@code false} if this was
1736 * never set.
1737 */
Alex Hills4ec3ff42016-04-12 11:36:18 -04001738 public boolean getHintLaunchesActivity() {
Alex Hills9ab3a232016-04-05 14:54:56 -04001739 return (mFlags & FLAG_HINT_LAUNCHES_ACTIVITY) != 0;
1740 }
Alex Hills9f087612016-06-07 09:08:59 -04001741
1742 /**
1743 * Set a hint that this Action should be displayed inline.
1744 *
1745 * @param hintDisplayInline {@code true} if action should be displayed inline, false
1746 * otherwise
1747 * @return this object for method chaining
1748 */
1749 public WearableExtender setHintDisplayActionInline(
1750 boolean hintDisplayInline) {
1751 setFlag(FLAG_HINT_DISPLAY_INLINE, hintDisplayInline);
1752 return this;
1753 }
1754
1755 /**
1756 * Get a hint that this Action should be displayed inline.
1757 *
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08001758 * @return {@code true} if the Action should be displayed inline, {@code false}
Alex Hills9f087612016-06-07 09:08:59 -04001759 * otherwise. The default value is {@code false} if this was never set.
1760 */
1761 public boolean getHintDisplayActionInline() {
1762 return (mFlags & FLAG_HINT_DISPLAY_INLINE) != 0;
1763 }
Griff Hazen61a9e862014-05-22 16:05:19 -07001764 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001765 }
1766
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001767 /**
1768 * Array of all {@link Action} structures attached to this notification by
1769 * {@link Builder#addAction(int, CharSequence, PendingIntent)}. Mostly useful for instances of
1770 * {@link android.service.notification.NotificationListenerService} that provide an alternative
1771 * interface for invoking actions.
1772 */
Daniel Sandlerea2a3172013-02-20 22:24:20 -05001773 public Action[] actions;
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001774
1775 /**
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001776 * Replacement version of this notification whose content will be shown
1777 * in an insecure context such as atop a secure keyguard. See {@link #visibility}
1778 * and {@link #VISIBILITY_PUBLIC}.
1779 */
1780 public Notification publicVersion;
1781
1782 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001783 * Constructs a Notification object with default values.
Joe Onorato46439ce2010-11-19 13:56:21 -08001784 * You might want to consider using {@link Builder} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001785 */
1786 public Notification()
1787 {
1788 this.when = System.currentTimeMillis();
Selim Cinekb85f36fd2016-04-20 18:46:36 -07001789 this.creationTime = System.currentTimeMillis();
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001790 this.priority = PRIORITY_DEFAULT;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001791 }
1792
1793 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001794 * @hide
1795 */
1796 public Notification(Context context, int icon, CharSequence tickerText, long when,
1797 CharSequence contentTitle, CharSequence contentText, Intent contentIntent)
1798 {
Chris Wren1ce4b6d2015-06-11 10:19:43 -04001799 new Builder(context)
1800 .setWhen(when)
1801 .setSmallIcon(icon)
1802 .setTicker(tickerText)
1803 .setContentTitle(contentTitle)
1804 .setContentText(contentText)
1805 .setContentIntent(PendingIntent.getActivity(context, 0, contentIntent, 0))
1806 .buildInto(this);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001807 }
1808
1809 /**
1810 * Constructs a Notification object with the information needed to
1811 * have a status bar icon without the standard expanded view.
1812 *
1813 * @param icon The resource id of the icon to put in the status bar.
1814 * @param tickerText The text that flows by in the status bar when the notification first
1815 * activates.
1816 * @param when The time to show in the time field. In the System.currentTimeMillis
1817 * timebase.
Joe Onorato46439ce2010-11-19 13:56:21 -08001818 *
1819 * @deprecated Use {@link Builder} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001820 */
Joe Onorato46439ce2010-11-19 13:56:21 -08001821 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001822 public Notification(int icon, CharSequence tickerText, long when)
1823 {
1824 this.icon = icon;
1825 this.tickerText = tickerText;
1826 this.when = when;
Selim Cinekb85f36fd2016-04-20 18:46:36 -07001827 this.creationTime = System.currentTimeMillis();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001828 }
1829
1830 /**
1831 * Unflatten the notification from a parcel.
1832 */
Svet Ganovddb94882016-06-23 19:55:24 -07001833 @SuppressWarnings("unchecked")
1834 public Notification(Parcel parcel) {
1835 // IMPORTANT: Add unmarshaling code in readFromParcel as the pending
1836 // intents in extras are always written as the last entry.
1837 readFromParcelImpl(parcel);
1838 // Must be read last!
Felipe Lemedd85da62016-06-28 11:29:54 -07001839 allPendingIntents = (ArraySet<PendingIntent>) parcel.readArraySet(null);
Svet Ganovddb94882016-06-23 19:55:24 -07001840 }
1841
1842 private void readFromParcelImpl(Parcel parcel)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001843 {
1844 int version = parcel.readInt();
1845
Dianne Hackborn98305522017-05-05 17:53:53 -07001846 whitelistToken = parcel.readStrongBinder();
1847 if (whitelistToken == null) {
1848 whitelistToken = processWhitelistToken;
1849 }
1850 // Propagate this token to all pending intents that are unmarshalled from the parcel.
1851 parcel.setClassCookie(PendingIntent.class, whitelistToken);
1852
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001853 when = parcel.readLong();
Selim Cinekb85f36fd2016-04-20 18:46:36 -07001854 creationTime = parcel.readLong();
Dan Sandler3936e7a2015-05-19 20:59:12 -04001855 if (parcel.readInt() != 0) {
1856 mSmallIcon = Icon.CREATOR.createFromParcel(parcel);
Dan Sandler4e787062015-06-17 15:09:48 -04001857 if (mSmallIcon.getType() == Icon.TYPE_RESOURCE) {
1858 icon = mSmallIcon.getResId();
1859 }
Dan Sandler3936e7a2015-05-19 20:59:12 -04001860 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001861 number = parcel.readInt();
1862 if (parcel.readInt() != 0) {
1863 contentIntent = PendingIntent.CREATOR.createFromParcel(parcel);
1864 }
1865 if (parcel.readInt() != 0) {
1866 deleteIntent = PendingIntent.CREATOR.createFromParcel(parcel);
1867 }
1868 if (parcel.readInt() != 0) {
1869 tickerText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
1870 }
1871 if (parcel.readInt() != 0) {
Joe Onorato46439ce2010-11-19 13:56:21 -08001872 tickerView = RemoteViews.CREATOR.createFromParcel(parcel);
Joe Onoratoef1e7762010-09-17 18:38:38 -04001873 }
1874 if (parcel.readInt() != 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001875 contentView = RemoteViews.CREATOR.createFromParcel(parcel);
1876 }
Joe Onorato561d3852010-11-20 18:09:34 -08001877 if (parcel.readInt() != 0) {
Dan Sandlerd63f9322015-05-06 15:18:49 -04001878 mLargeIcon = Icon.CREATOR.createFromParcel(parcel);
Joe Onorato561d3852010-11-20 18:09:34 -08001879 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001880 defaults = parcel.readInt();
1881 flags = parcel.readInt();
1882 if (parcel.readInt() != 0) {
1883 sound = Uri.CREATOR.createFromParcel(parcel);
1884 }
1885
1886 audioStreamType = parcel.readInt();
John Spurlockc0650f022014-07-19 13:22:39 -04001887 if (parcel.readInt() != 0) {
1888 audioAttributes = AudioAttributes.CREATOR.createFromParcel(parcel);
1889 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001890 vibrate = parcel.createLongArray();
1891 ledARGB = parcel.readInt();
1892 ledOnMS = parcel.readInt();
1893 ledOffMS = parcel.readInt();
1894 iconLevel = parcel.readInt();
Daniel Sandlere46cbd32010-06-17 10:35:26 -04001895
1896 if (parcel.readInt() != 0) {
1897 fullScreenIntent = PendingIntent.CREATOR.createFromParcel(parcel);
1898 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001899
1900 priority = parcel.readInt();
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001901
John Spurlockfd7f1e02014-03-18 16:41:57 -04001902 category = parcel.readString();
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001903
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001904 mGroupKey = parcel.readString();
1905
1906 mSortKey = parcel.readString();
1907
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001908 extras = Bundle.setDefusable(parcel.readBundle(), true); // may be null
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001909
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001910 actions = parcel.createTypedArray(Action.CREATOR); // may be null
1911
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001912 if (parcel.readInt() != 0) {
Daniel Sandlerf3b73432012-03-27 15:01:25 -04001913 bigContentView = RemoteViews.CREATOR.createFromParcel(parcel);
1914 }
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001915
Chris Wren8fd39ec2014-02-27 17:43:26 -05001916 if (parcel.readInt() != 0) {
1917 headsUpContentView = RemoteViews.CREATOR.createFromParcel(parcel);
1918 }
1919
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001920 visibility = parcel.readInt();
1921
1922 if (parcel.readInt() != 0) {
1923 publicVersion = Notification.CREATOR.createFromParcel(parcel);
1924 }
Dan Sandler26e81cf2014-05-06 10:01:27 -04001925
1926 color = parcel.readInt();
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04001927
1928 if (parcel.readInt() != 0) {
1929 mChannelId = parcel.readString();
1930 }
Julia Reynolds2a128742016-11-28 14:29:25 -05001931 mTimeout = parcel.readLong();
Julia Reynolds13d898c2017-02-02 12:22:05 -05001932
1933 if (parcel.readInt() != 0) {
1934 mShortcutId = parcel.readString();
1935 }
1936
1937 mBadgeIcon = parcel.readInt();
Julia Reynolds3aedded2017-03-31 14:42:09 -04001938
1939 if (parcel.readInt() != 0) {
1940 mSettingsText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
1941 }
Julia Reynoldsa79c3712017-04-21 10:29:57 -04001942
1943 mGroupAlertBehavior = parcel.readInt();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001944 }
1945
Andy Stadler110988c2010-12-03 14:29:16 -08001946 @Override
Joe Onorato18e69df2010-05-17 22:26:12 -07001947 public Notification clone() {
1948 Notification that = new Notification();
Daniel Sandler1a497d32013-04-18 14:52:45 -04001949 cloneInto(that, true);
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001950 return that;
1951 }
Joe Onorato18e69df2010-05-17 22:26:12 -07001952
Daniel Sandler1a497d32013-04-18 14:52:45 -04001953 /**
1954 * Copy all (or if heavy is false, all except Bitmaps and RemoteViews) members
1955 * of this into that.
1956 * @hide
1957 */
1958 public void cloneInto(Notification that, boolean heavy) {
Dianne Hackborn98305522017-05-05 17:53:53 -07001959 that.whitelistToken = this.whitelistToken;
Joe Onorato18e69df2010-05-17 22:26:12 -07001960 that.when = this.when;
Selim Cinekb85f36fd2016-04-20 18:46:36 -07001961 that.creationTime = this.creationTime;
Dan Sandlerd63f9322015-05-06 15:18:49 -04001962 that.mSmallIcon = this.mSmallIcon;
Joe Onorato18e69df2010-05-17 22:26:12 -07001963 that.number = this.number;
1964
1965 // PendingIntents are global, so there's no reason (or way) to clone them.
1966 that.contentIntent = this.contentIntent;
1967 that.deleteIntent = this.deleteIntent;
Daniel Sandlere46cbd32010-06-17 10:35:26 -04001968 that.fullScreenIntent = this.fullScreenIntent;
Joe Onorato18e69df2010-05-17 22:26:12 -07001969
1970 if (this.tickerText != null) {
1971 that.tickerText = this.tickerText.toString();
1972 }
Daniel Sandler1a497d32013-04-18 14:52:45 -04001973 if (heavy && this.tickerView != null) {
Joe Onorato46439ce2010-11-19 13:56:21 -08001974 that.tickerView = this.tickerView.clone();
Joe Onoratoef1e7762010-09-17 18:38:38 -04001975 }
Daniel Sandler1a497d32013-04-18 14:52:45 -04001976 if (heavy && this.contentView != null) {
Joe Onorato18e69df2010-05-17 22:26:12 -07001977 that.contentView = this.contentView.clone();
1978 }
Dan Sandlerd63f9322015-05-06 15:18:49 -04001979 if (heavy && this.mLargeIcon != null) {
1980 that.mLargeIcon = this.mLargeIcon;
Joe Onorato561d3852010-11-20 18:09:34 -08001981 }
Jozef BABJAKa8b91832011-02-22 08:05:08 +01001982 that.iconLevel = this.iconLevel;
Joe Onorato18e69df2010-05-17 22:26:12 -07001983 that.sound = this.sound; // android.net.Uri is immutable
1984 that.audioStreamType = this.audioStreamType;
John Spurlockc0650f022014-07-19 13:22:39 -04001985 if (this.audioAttributes != null) {
1986 that.audioAttributes = new AudioAttributes.Builder(this.audioAttributes).build();
1987 }
Joe Onorato18e69df2010-05-17 22:26:12 -07001988
1989 final long[] vibrate = this.vibrate;
1990 if (vibrate != null) {
1991 final int N = vibrate.length;
1992 final long[] vib = that.vibrate = new long[N];
1993 System.arraycopy(vibrate, 0, vib, 0, N);
1994 }
1995
1996 that.ledARGB = this.ledARGB;
1997 that.ledOnMS = this.ledOnMS;
1998 that.ledOffMS = this.ledOffMS;
1999 that.defaults = this.defaults;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002000
Joe Onorato18e69df2010-05-17 22:26:12 -07002001 that.flags = this.flags;
2002
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002003 that.priority = this.priority;
Joe Malin8d40d042012-11-05 11:36:40 -08002004
John Spurlockfd7f1e02014-03-18 16:41:57 -04002005 that.category = this.category;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002006
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002007 that.mGroupKey = this.mGroupKey;
2008
2009 that.mSortKey = this.mSortKey;
2010
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002011 if (this.extras != null) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04002012 try {
2013 that.extras = new Bundle(this.extras);
2014 // will unparcel
2015 that.extras.size();
2016 } catch (BadParcelableException e) {
2017 Log.e(TAG, "could not unparcel extras from notification: " + this, e);
2018 that.extras = null;
2019 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002020 }
2021
Felipe Lemedd85da62016-06-28 11:29:54 -07002022 if (!ArrayUtils.isEmpty(allPendingIntents)) {
2023 that.allPendingIntents = new ArraySet<>(allPendingIntents);
Svet Ganovddb94882016-06-23 19:55:24 -07002024 }
2025
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002026 if (this.actions != null) {
2027 that.actions = new Action[this.actions.length];
2028 for(int i=0; i<this.actions.length; i++) {
liangweikang63b03b52017-03-16 19:22:15 +08002029 if ( this.actions[i] != null) {
2030 that.actions[i] = this.actions[i].clone();
2031 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002032 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002033 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002034
Daniel Sandler1a497d32013-04-18 14:52:45 -04002035 if (heavy && this.bigContentView != null) {
Daniel Sandlerf3b73432012-03-27 15:01:25 -04002036 that.bigContentView = this.bigContentView.clone();
2037 }
Daniel Sandler1a497d32013-04-18 14:52:45 -04002038
Chris Wren8fd39ec2014-02-27 17:43:26 -05002039 if (heavy && this.headsUpContentView != null) {
2040 that.headsUpContentView = this.headsUpContentView.clone();
2041 }
2042
Dan Sandler0bf2ed82013-12-21 23:33:41 -06002043 that.visibility = this.visibility;
2044
2045 if (this.publicVersion != null) {
2046 that.publicVersion = new Notification();
2047 this.publicVersion.cloneInto(that.publicVersion, heavy);
2048 }
2049
Dan Sandler26e81cf2014-05-06 10:01:27 -04002050 that.color = this.color;
2051
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04002052 that.mChannelId = this.mChannelId;
Julia Reynolds2a128742016-11-28 14:29:25 -05002053 that.mTimeout = this.mTimeout;
Julia Reynolds3aedded2017-03-31 14:42:09 -04002054 that.mShortcutId = this.mShortcutId;
2055 that.mBadgeIcon = this.mBadgeIcon;
2056 that.mSettingsText = this.mSettingsText;
Julia Reynoldsa79c3712017-04-21 10:29:57 -04002057 that.mGroupAlertBehavior = this.mGroupAlertBehavior;
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04002058
Daniel Sandler1a497d32013-04-18 14:52:45 -04002059 if (!heavy) {
2060 that.lightenPayload(); // will clean out extras
2061 }
2062 }
2063
2064 /**
2065 * Removes heavyweight parts of the Notification object for archival or for sending to
2066 * listeners when the full contents are not necessary.
2067 * @hide
2068 */
2069 public final void lightenPayload() {
2070 tickerView = null;
2071 contentView = null;
2072 bigContentView = null;
Chris Wren8fd39ec2014-02-27 17:43:26 -05002073 headsUpContentView = null;
Dan Sandlerd63f9322015-05-06 15:18:49 -04002074 mLargeIcon = null;
Dan Sandler50128532015-12-08 15:42:41 -05002075 if (extras != null && !extras.isEmpty()) {
2076 final Set<String> keyset = extras.keySet();
2077 final int N = keyset.size();
2078 final String[] keys = keyset.toArray(new String[N]);
2079 for (int i=0; i<N; i++) {
2080 final String key = keys[i];
Dmitri Plotnikov22281362017-01-30 11:16:21 -08002081 if (TvExtender.EXTRA_TV_EXTENDER.equals(key)) {
2082 continue;
2083 }
Dan Sandler50128532015-12-08 15:42:41 -05002084 final Object obj = extras.get(key);
2085 if (obj != null &&
2086 ( obj instanceof Parcelable
2087 || obj instanceof Parcelable[]
2088 || obj instanceof SparseArray
2089 || obj instanceof ArrayList)) {
2090 extras.remove(key);
2091 }
2092 }
Daniel Sandler1a497d32013-04-18 14:52:45 -04002093 }
Joe Onorato18e69df2010-05-17 22:26:12 -07002094 }
2095
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04002096 /**
2097 * Make sure this CharSequence is safe to put into a bundle, which basically
2098 * means it had better not be some custom Parcelable implementation.
2099 * @hide
2100 */
2101 public static CharSequence safeCharSequence(CharSequence cs) {
Christoph Studer535ec612014-09-03 15:47:47 +02002102 if (cs == null) return cs;
2103 if (cs.length() > MAX_CHARSEQUENCE_LENGTH) {
2104 cs = cs.subSequence(0, MAX_CHARSEQUENCE_LENGTH);
2105 }
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04002106 if (cs instanceof Parcelable) {
2107 Log.e(TAG, "warning: " + cs.getClass().getCanonicalName()
2108 + " instance is a custom Parcelable and not allowed in Notification");
2109 return cs.toString();
2110 }
Selim Cinek60a54252016-02-26 17:03:25 -08002111 return removeTextSizeSpans(cs);
2112 }
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04002113
Selim Cinek60a54252016-02-26 17:03:25 -08002114 private static CharSequence removeTextSizeSpans(CharSequence charSequence) {
2115 if (charSequence instanceof Spanned) {
2116 Spanned ss = (Spanned) charSequence;
2117 Object[] spans = ss.getSpans(0, ss.length(), Object.class);
2118 SpannableStringBuilder builder = new SpannableStringBuilder(ss.toString());
2119 for (Object span : spans) {
2120 Object resultSpan = span;
Selim Cinek89991a22016-03-07 19:51:54 -08002121 if (resultSpan instanceof CharacterStyle) {
2122 resultSpan = ((CharacterStyle) span).getUnderlying();
2123 }
2124 if (resultSpan instanceof TextAppearanceSpan) {
2125 TextAppearanceSpan originalSpan = (TextAppearanceSpan) resultSpan;
Selim Cinek60a54252016-02-26 17:03:25 -08002126 resultSpan = new TextAppearanceSpan(
2127 originalSpan.getFamily(),
2128 originalSpan.getTextStyle(),
2129 -1,
2130 originalSpan.getTextColor(),
2131 originalSpan.getLinkTextColor());
Selim Cinek89991a22016-03-07 19:51:54 -08002132 } else if (resultSpan instanceof RelativeSizeSpan
2133 || resultSpan instanceof AbsoluteSizeSpan) {
Selim Cinek60a54252016-02-26 17:03:25 -08002134 continue;
Selim Cinek89991a22016-03-07 19:51:54 -08002135 } else {
2136 resultSpan = span;
Selim Cinek60a54252016-02-26 17:03:25 -08002137 }
2138 builder.setSpan(resultSpan, ss.getSpanStart(span), ss.getSpanEnd(span),
2139 ss.getSpanFlags(span));
2140 }
2141 return builder;
2142 }
2143 return charSequence;
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04002144 }
2145
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002146 public int describeContents() {
2147 return 0;
2148 }
2149
2150 /**
Dan Sandler4e787062015-06-17 15:09:48 -04002151 * Flatten this notification into a parcel.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002152 */
Svet Ganovddb94882016-06-23 19:55:24 -07002153 public void writeToParcel(Parcel parcel, int flags) {
2154 // We need to mark all pending intents getting into the notification
2155 // system as being put there to later allow the notification ranker
2156 // to launch them and by doing so add the app to the battery saver white
2157 // list for a short period of time. The problem is that the system
2158 // cannot look into the extras as there may be parcelables there that
2159 // the platform does not know how to handle. To go around that we have
2160 // an explicit list of the pending intents in the extras bundle.
Felipe Lemedd85da62016-06-28 11:29:54 -07002161 final boolean collectPendingIntents = (allPendingIntents == null);
Svet Ganovddb94882016-06-23 19:55:24 -07002162 if (collectPendingIntents) {
2163 PendingIntent.setOnMarshaledListener(
2164 (PendingIntent intent, Parcel out, int outFlags) -> {
2165 if (parcel == out) {
Felipe Lemedd85da62016-06-28 11:29:54 -07002166 if (allPendingIntents == null) {
2167 allPendingIntents = new ArraySet<>();
Svet Ganovddb94882016-06-23 19:55:24 -07002168 }
Felipe Lemedd85da62016-06-28 11:29:54 -07002169 allPendingIntents.add(intent);
Svet Ganovddb94882016-06-23 19:55:24 -07002170 }
2171 });
2172 }
2173 try {
2174 // IMPORTANT: Add marshaling code in writeToParcelImpl as we
Julia Reynolds13d898c2017-02-02 12:22:05 -05002175 // want to intercept all pending events written to the parcel.
Svet Ganovddb94882016-06-23 19:55:24 -07002176 writeToParcelImpl(parcel, flags);
2177 // Must be written last!
Felipe Lemedd85da62016-06-28 11:29:54 -07002178 parcel.writeArraySet(allPendingIntents);
Svet Ganovddb94882016-06-23 19:55:24 -07002179 } finally {
2180 if (collectPendingIntents) {
2181 PendingIntent.setOnMarshaledListener(null);
2182 }
2183 }
2184 }
2185
2186 private void writeToParcelImpl(Parcel parcel, int flags) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002187 parcel.writeInt(1);
2188
Dianne Hackborn98305522017-05-05 17:53:53 -07002189 parcel.writeStrongBinder(whitelistToken);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002190 parcel.writeLong(when);
Selim Cinekb85f36fd2016-04-20 18:46:36 -07002191 parcel.writeLong(creationTime);
Dan Sandler4e787062015-06-17 15:09:48 -04002192 if (mSmallIcon == null && icon != 0) {
2193 // you snuck an icon in here without using the builder; let's try to keep it
2194 mSmallIcon = Icon.createWithResource("", icon);
2195 }
Dan Sandler3936e7a2015-05-19 20:59:12 -04002196 if (mSmallIcon != null) {
2197 parcel.writeInt(1);
2198 mSmallIcon.writeToParcel(parcel, 0);
2199 } else {
2200 parcel.writeInt(0);
2201 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002202 parcel.writeInt(number);
2203 if (contentIntent != null) {
2204 parcel.writeInt(1);
2205 contentIntent.writeToParcel(parcel, 0);
2206 } else {
2207 parcel.writeInt(0);
2208 }
2209 if (deleteIntent != null) {
2210 parcel.writeInt(1);
2211 deleteIntent.writeToParcel(parcel, 0);
2212 } else {
2213 parcel.writeInt(0);
2214 }
2215 if (tickerText != null) {
2216 parcel.writeInt(1);
2217 TextUtils.writeToParcel(tickerText, parcel, flags);
2218 } else {
2219 parcel.writeInt(0);
2220 }
Joe Onorato46439ce2010-11-19 13:56:21 -08002221 if (tickerView != null) {
Joe Onoratoef1e7762010-09-17 18:38:38 -04002222 parcel.writeInt(1);
Joe Onorato46439ce2010-11-19 13:56:21 -08002223 tickerView.writeToParcel(parcel, 0);
Joe Onoratoef1e7762010-09-17 18:38:38 -04002224 } else {
2225 parcel.writeInt(0);
2226 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002227 if (contentView != null) {
2228 parcel.writeInt(1);
2229 contentView.writeToParcel(parcel, 0);
2230 } else {
2231 parcel.writeInt(0);
2232 }
Selim Cinek279fa862016-06-14 10:57:25 -07002233 if (mLargeIcon == null && largeIcon != null) {
2234 // you snuck an icon in here without using the builder; let's try to keep it
2235 mLargeIcon = Icon.createWithBitmap(largeIcon);
2236 }
Dan Sandlerd63f9322015-05-06 15:18:49 -04002237 if (mLargeIcon != null) {
Joe Onorato561d3852010-11-20 18:09:34 -08002238 parcel.writeInt(1);
Dan Sandlerd63f9322015-05-06 15:18:49 -04002239 mLargeIcon.writeToParcel(parcel, 0);
Joe Onorato561d3852010-11-20 18:09:34 -08002240 } else {
2241 parcel.writeInt(0);
2242 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002243
2244 parcel.writeInt(defaults);
2245 parcel.writeInt(this.flags);
2246
2247 if (sound != null) {
2248 parcel.writeInt(1);
2249 sound.writeToParcel(parcel, 0);
2250 } else {
2251 parcel.writeInt(0);
2252 }
2253 parcel.writeInt(audioStreamType);
John Spurlockc0650f022014-07-19 13:22:39 -04002254
2255 if (audioAttributes != null) {
2256 parcel.writeInt(1);
2257 audioAttributes.writeToParcel(parcel, 0);
2258 } else {
2259 parcel.writeInt(0);
2260 }
2261
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002262 parcel.writeLongArray(vibrate);
2263 parcel.writeInt(ledARGB);
2264 parcel.writeInt(ledOnMS);
2265 parcel.writeInt(ledOffMS);
2266 parcel.writeInt(iconLevel);
Daniel Sandlere46cbd32010-06-17 10:35:26 -04002267
2268 if (fullScreenIntent != null) {
2269 parcel.writeInt(1);
2270 fullScreenIntent.writeToParcel(parcel, 0);
2271 } else {
2272 parcel.writeInt(0);
2273 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002274
2275 parcel.writeInt(priority);
Joe Malin8d40d042012-11-05 11:36:40 -08002276
John Spurlockfd7f1e02014-03-18 16:41:57 -04002277 parcel.writeString(category);
Joe Malin8d40d042012-11-05 11:36:40 -08002278
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002279 parcel.writeString(mGroupKey);
2280
2281 parcel.writeString(mSortKey);
2282
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002283 parcel.writeBundle(extras); // null ok
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002284
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002285 parcel.writeTypedArray(actions, 0); // null ok
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002286
Daniel Sandlerf3b73432012-03-27 15:01:25 -04002287 if (bigContentView != null) {
2288 parcel.writeInt(1);
2289 bigContentView.writeToParcel(parcel, 0);
2290 } else {
2291 parcel.writeInt(0);
2292 }
Dan Sandler0bf2ed82013-12-21 23:33:41 -06002293
Chris Wren8fd39ec2014-02-27 17:43:26 -05002294 if (headsUpContentView != null) {
2295 parcel.writeInt(1);
2296 headsUpContentView.writeToParcel(parcel, 0);
2297 } else {
2298 parcel.writeInt(0);
2299 }
2300
Dan Sandler0bf2ed82013-12-21 23:33:41 -06002301 parcel.writeInt(visibility);
2302
2303 if (publicVersion != null) {
2304 parcel.writeInt(1);
2305 publicVersion.writeToParcel(parcel, 0);
2306 } else {
2307 parcel.writeInt(0);
2308 }
Dan Sandler26e81cf2014-05-06 10:01:27 -04002309
2310 parcel.writeInt(color);
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04002311
2312 if (mChannelId != null) {
2313 parcel.writeInt(1);
2314 parcel.writeString(mChannelId);
2315 } else {
2316 parcel.writeInt(0);
2317 }
Julia Reynolds2a128742016-11-28 14:29:25 -05002318 parcel.writeLong(mTimeout);
Julia Reynolds13d898c2017-02-02 12:22:05 -05002319
2320 if (mShortcutId != null) {
2321 parcel.writeInt(1);
2322 parcel.writeString(mShortcutId);
2323 } else {
2324 parcel.writeInt(0);
2325 }
2326
2327 parcel.writeInt(mBadgeIcon);
Julia Reynolds3aedded2017-03-31 14:42:09 -04002328
2329 if (mSettingsText != null) {
2330 parcel.writeInt(1);
2331 TextUtils.writeToParcel(mSettingsText, parcel, flags);
2332 } else {
2333 parcel.writeInt(0);
2334 }
Julia Reynoldsa79c3712017-04-21 10:29:57 -04002335
2336 parcel.writeInt(mGroupAlertBehavior);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002337 }
2338
2339 /**
2340 * Parcelable.Creator that instantiates Notification objects
2341 */
2342 public static final Parcelable.Creator<Notification> CREATOR
2343 = new Parcelable.Creator<Notification>()
2344 {
2345 public Notification createFromParcel(Parcel parcel)
2346 {
2347 return new Notification(parcel);
2348 }
2349
2350 public Notification[] newArray(int size)
2351 {
2352 return new Notification[size];
2353 }
2354 };
2355
2356 /**
2357 * Sets the {@link #contentView} field to be a view with the standard "Latest Event"
2358 * layout.
2359 *
2360 * <p>Uses the {@link #icon} and {@link #when} fields to set the icon and time fields
2361 * in the view.</p>
2362 * @param context The context for your application / activity.
2363 * @param contentTitle The title that goes in the expanded entry.
2364 * @param contentText The text that goes in the expanded entry.
2365 * @param contentIntent The intent to launch when the user clicks the expanded notification.
2366 * If this is an activity, it must include the
2367 * {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} flag, which requires
Scott Main7aee61f2011-02-08 11:25:01 -08002368 * that you take care of task management as described in the
2369 * <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back
2370 * Stack</a> document.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002371 *
Joe Onorato46439ce2010-11-19 13:56:21 -08002372 * @deprecated Use {@link Builder} instead.
Chris Wrena05db382015-06-24 15:18:34 -04002373 * @removed
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002374 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002375 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002376 public void setLatestEventInfo(Context context,
2377 CharSequence contentTitle, CharSequence contentText, PendingIntent contentIntent) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002378 if (context.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1){
2379 Log.e(TAG, "setLatestEventInfo() is deprecated and you should feel deprecated.",
2380 new Throwable());
2381 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002382
Selim Cinek4ac6f602016-06-13 15:47:03 -07002383 if (context.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N) {
2384 extras.putBoolean(EXTRA_SHOW_WHEN, true);
2385 }
2386
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002387 // ensure that any information already set directly is preserved
2388 final Notification.Builder builder = new Notification.Builder(context, this);
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002389
2390 // now apply the latestEventInfo fields
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002391 if (contentTitle != null) {
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002392 builder.setContentTitle(contentTitle);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002393 }
2394 if (contentText != null) {
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002395 builder.setContentText(contentText);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002396 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002397 builder.setContentIntent(contentIntent);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002398
2399 builder.build(); // callers expect this notification to be ready to use
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002400 }
2401
Julia Reynoldsda303542015-11-23 14:00:20 -05002402 /**
2403 * @hide
2404 */
2405 public static void addFieldsFromContext(Context context, Notification notification) {
Julia Reynoldse071abd2017-03-22 10:52:11 -04002406 addFieldsFromContext(context.getApplicationInfo(), notification);
Jeff Sharkey012bc7b2016-04-11 16:30:27 -06002407 }
2408
2409 /**
2410 * @hide
2411 */
Julia Reynoldse071abd2017-03-22 10:52:11 -04002412 public static void addFieldsFromContext(ApplicationInfo ai, Notification notification) {
Jeff Sharkey012bc7b2016-04-11 16:30:27 -06002413 notification.extras.putParcelable(EXTRA_BUILDER_APPLICATION_INFO, ai);
Julia Reynoldsda303542015-11-23 14:00:20 -05002414 }
2415
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002416 @Override
2417 public String toString() {
2418 StringBuilder sb = new StringBuilder();
Julia Reynoldsb9e712e2017-04-17 10:31:03 -04002419 sb.append("Notification(channel=");
Julia Reynoldsbad42972017-04-25 13:52:49 -04002420 sb.append(getChannelId());
Julia Reynoldsb9e712e2017-04-17 10:31:03 -04002421 sb.append(" pri=");
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002422 sb.append(priority);
2423 sb.append(" contentView=");
Joe Onoratoc9596d62011-01-12 17:03:11 -08002424 if (contentView != null) {
2425 sb.append(contentView.getPackage());
2426 sb.append("/0x");
2427 sb.append(Integer.toHexString(contentView.getLayoutId()));
2428 } else {
2429 sb.append("null");
2430 }
2431 sb.append(" vibrate=");
Daniel Sandler6738eee2012-11-16 12:03:32 -05002432 if ((this.defaults & DEFAULT_VIBRATE) != 0) {
2433 sb.append("default");
2434 } else if (this.vibrate != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002435 int N = this.vibrate.length-1;
2436 sb.append("[");
2437 for (int i=0; i<N; i++) {
2438 sb.append(this.vibrate[i]);
2439 sb.append(',');
2440 }
Simon Schoar8cf97d92009-06-10 22:08:37 +02002441 if (N != -1) {
2442 sb.append(this.vibrate[N]);
2443 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002444 sb.append("]");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002445 } else {
2446 sb.append("null");
2447 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002448 sb.append(" sound=");
Daniel Sandler6738eee2012-11-16 12:03:32 -05002449 if ((this.defaults & DEFAULT_SOUND) != 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002450 sb.append("default");
Daniel Sandler6738eee2012-11-16 12:03:32 -05002451 } else if (this.sound != null) {
2452 sb.append(this.sound.toString());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002453 } else {
2454 sb.append("null");
2455 }
Chris Wren365b6d32015-07-16 10:39:26 -04002456 if (this.tickerText != null) {
2457 sb.append(" tick");
2458 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002459 sb.append(" defaults=0x");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002460 sb.append(Integer.toHexString(this.defaults));
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002461 sb.append(" flags=0x");
Daniel Sandlere46cbd32010-06-17 10:35:26 -04002462 sb.append(Integer.toHexString(this.flags));
Dan Sandler26e81cf2014-05-06 10:01:27 -04002463 sb.append(String.format(" color=0x%08x", this.color));
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002464 if (this.category != null) {
2465 sb.append(" category=");
2466 sb.append(this.category);
2467 }
2468 if (this.mGroupKey != null) {
2469 sb.append(" groupKey=");
2470 sb.append(this.mGroupKey);
2471 }
2472 if (this.mSortKey != null) {
2473 sb.append(" sortKey=");
2474 sb.append(this.mSortKey);
2475 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002476 if (actions != null) {
Dan Sandler1b718782014-07-18 12:43:45 -04002477 sb.append(" actions=");
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002478 sb.append(actions.length);
Dan Sandler1b718782014-07-18 12:43:45 -04002479 }
2480 sb.append(" vis=");
2481 sb.append(visibilityToString(this.visibility));
2482 if (this.publicVersion != null) {
2483 sb.append(" publicVersion=");
2484 sb.append(publicVersion.toString());
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002485 }
2486 sb.append(")");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002487 return sb.toString();
2488 }
Joe Onorato46439ce2010-11-19 13:56:21 -08002489
Dan Sandler1b718782014-07-18 12:43:45 -04002490 /**
2491 * {@hide}
2492 */
2493 public static String visibilityToString(int vis) {
2494 switch (vis) {
2495 case VISIBILITY_PRIVATE:
2496 return "PRIVATE";
2497 case VISIBILITY_PUBLIC:
2498 return "PUBLIC";
2499 case VISIBILITY_SECRET:
2500 return "SECRET";
2501 default:
2502 return "UNKNOWN(" + String.valueOf(vis) + ")";
2503 }
2504 }
2505
Joe Onoratocb109a02011-01-18 17:57:41 -08002506 /**
John Spurlock1d881a12015-03-18 19:21:54 -04002507 * {@hide}
2508 */
2509 public static String priorityToString(@Priority int pri) {
2510 switch (pri) {
2511 case PRIORITY_MIN:
2512 return "MIN";
2513 case PRIORITY_LOW:
2514 return "LOW";
2515 case PRIORITY_DEFAULT:
2516 return "DEFAULT";
2517 case PRIORITY_HIGH:
2518 return "HIGH";
2519 case PRIORITY_MAX:
2520 return "MAX";
2521 default:
2522 return "UNKNOWN(" + String.valueOf(pri) + ")";
2523 }
2524 }
2525
Jeff Sharkey000ce802017-04-29 13:13:27 -06002526 /** @removed */
2527 @Deprecated
Julia Reynolds37856052016-11-11 09:20:07 -05002528 public String getChannel() {
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04002529 return mChannelId;
2530 }
2531
2532 /**
Julia Reynoldsbad42972017-04-25 13:52:49 -04002533 * Returns the id of the channel this notification posts to.
2534 */
2535 public String getChannelId() {
2536 return mChannelId;
2537 }
2538
Jeff Sharkey000ce802017-04-29 13:13:27 -06002539 /** @removed */
2540 @Deprecated
Julia Reynolds2a128742016-11-28 14:29:25 -05002541 public long getTimeout() {
2542 return mTimeout;
2543 }
2544
2545 /**
Julia Reynoldsbad42972017-04-25 13:52:49 -04002546 * Returns the duration from posting after which this notification should be canceled by the
2547 * system, if it's not canceled already.
2548 */
2549 public long getTimeoutAfter() {
2550 return mTimeout;
2551 }
2552
2553 /**
Julia Reynoldse071abd2017-03-22 10:52:11 -04002554 * Returns what icon should be shown for this notification if it is being displayed in a
2555 * Launcher that supports badging. Will be one of {@link #BADGE_ICON_NONE},
2556 * {@link #BADGE_ICON_SMALL}, or {@link #BADGE_ICON_LARGE}.
2557 */
2558 public int getBadgeIconType() {
2559 return mBadgeIcon;
2560 }
2561
2562 /**
Julia Reynolds13d898c2017-02-02 12:22:05 -05002563 * Returns the {@link ShortcutInfo#getId() id} that this notification supersedes, if any.
Julia Reynoldsbad42972017-04-25 13:52:49 -04002564 *
2565 * <p>Used by some Launchers that display notification content to hide shortcuts that duplicate
2566 * notifications.
Julia Reynolds13d898c2017-02-02 12:22:05 -05002567 */
2568 public String getShortcutId() {
2569 return mShortcutId;
2570 }
2571
Julia Reynolds3aedded2017-03-31 14:42:09 -04002572
2573 /**
2574 * Returns the settings text provided to {@link Builder#setSettingsText(CharSequence)}.
2575 */
2576 public CharSequence getSettingsText() {
2577 return mSettingsText;
2578 }
2579
Julia Reynolds13d898c2017-02-02 12:22:05 -05002580 /**
Julia Reynoldsa79c3712017-04-21 10:29:57 -04002581 * Returns which type of notifications in a group are responsible for audibly alerting the
2582 * user. See {@link #GROUP_ALERT_ALL}, {@link #GROUP_ALERT_CHILDREN},
2583 * {@link #GROUP_ALERT_SUMMARY}.
2584 */
2585 public @GroupAlertBehavior int getGroupAlertBehavior() {
2586 return mGroupAlertBehavior;
2587 }
2588
2589 /**
Dan Sandlerd63f9322015-05-06 15:18:49 -04002590 * The small icon representing this notification in the status bar and content view.
2591 *
2592 * @return the small icon representing this notification.
2593 *
2594 * @see Builder#getSmallIcon()
2595 * @see Builder#setSmallIcon(Icon)
2596 */
2597 public Icon getSmallIcon() {
2598 return mSmallIcon;
2599 }
2600
2601 /**
2602 * Used when notifying to clean up legacy small icons.
2603 * @hide
2604 */
2605 public void setSmallIcon(Icon icon) {
2606 mSmallIcon = icon;
2607 }
2608
2609 /**
2610 * The large icon shown in this notification's content view.
2611 * @see Builder#getLargeIcon()
2612 * @see Builder#setLargeIcon(Icon)
2613 */
2614 public Icon getLargeIcon() {
2615 return mLargeIcon;
2616 }
2617
2618 /**
Christoph Studer4600f9b2014-07-22 22:44:43 +02002619 * @hide
2620 */
Christoph Studerc8db24b2014-07-25 17:50:30 +02002621 public boolean isGroupSummary() {
2622 return mGroupKey != null && (flags & FLAG_GROUP_SUMMARY) != 0;
2623 }
2624
2625 /**
2626 * @hide
2627 */
2628 public boolean isGroupChild() {
2629 return mGroupKey != null && (flags & FLAG_GROUP_SUMMARY) == 0;
2630 }
2631
2632 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002633 * Builder class for {@link Notification} objects.
Joe Malin8d40d042012-11-05 11:36:40 -08002634 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002635 * Provides a convenient way to set the various fields of a {@link Notification} and generate
Scott Main183bf112012-08-13 19:12:13 -07002636 * content views using the platform's notification layout template. If your app supports
2637 * versions of Android as old as API level 4, you can instead use
2638 * {@link android.support.v4.app.NotificationCompat.Builder NotificationCompat.Builder},
2639 * available in the <a href="{@docRoot}tools/extras/support-library.html">Android Support
2640 * library</a>.
Joe Malin8d40d042012-11-05 11:36:40 -08002641 *
Scott Main183bf112012-08-13 19:12:13 -07002642 * <p>Example:
Joe Malin8d40d042012-11-05 11:36:40 -08002643 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002644 * <pre class="prettyprint">
Scott Main183bf112012-08-13 19:12:13 -07002645 * Notification noti = new Notification.Builder(mContext)
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002646 * .setContentTitle(&quot;New mail from &quot; + sender.toString())
2647 * .setContentText(subject)
2648 * .setSmallIcon(R.drawable.new_mail)
2649 * .setLargeIcon(aBitmap)
Chris Wrenfbd96ba2012-05-01 12:03:58 -04002650 * .build();
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002651 * </pre>
Joe Onoratocb109a02011-01-18 17:57:41 -08002652 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002653 public static class Builder {
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05002654 /**
2655 * @hide
2656 */
2657 public static final String EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT =
2658 "android.rebuild.contentViewActionCount";
2659 /**
2660 * @hide
2661 */
2662 public static final String EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT
2663 = "android.rebuild.bigViewActionCount";
2664 /**
2665 * @hide
2666 */
2667 public static final String EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT
2668 = "android.rebuild.hudViewActionCount";
2669
Daniel Sandler602ad1c2012-06-12 16:06:27 -04002670 private static final int MAX_ACTION_BUTTONS = 3;
Daniel Sandler8680bf82012-05-15 16:52:52 -04002671
Selim Cinek6743c0b2017-01-18 18:24:01 -08002672 private static final boolean USE_ONLY_TITLE_IN_LOW_PRIORITY_SUMMARY =
2673 SystemProperties.getBoolean("notifications.only_title", true);
2674
Selim Cinek389edcd2017-05-11 19:16:44 -07002675 /**
2676 * The lightness difference that has to be added to the primary text color to obtain the
2677 * secondary text color when the background is light.
2678 */
2679 private static final int LIGHTNESS_TEXT_DIFFERENCE_LIGHT = 20;
2680
2681 /**
2682 * The lightness difference that has to be added to the primary text color to obtain the
2683 * secondary text color when the background is dark.
2684 * A bit less then the above value, since it looks better on dark backgrounds.
2685 */
2686 private static final int LIGHTNESS_TEXT_DIFFERENCE_DARK = -10;
2687
Joe Onorato46439ce2010-11-19 13:56:21 -08002688 private Context mContext;
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002689 private Notification mN;
2690 private Bundle mUserExtras = new Bundle();
Chris Wrenfbd96ba2012-05-01 12:03:58 -04002691 private Style mStyle;
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002692 private ArrayList<Action> mActions = new ArrayList<Action>(MAX_ACTION_BUTTONS);
2693 private ArrayList<String> mPersonList = new ArrayList<String>();
2694 private NotificationColorUtil mColorUtil;
Selim Cinek7b9605b2017-01-19 17:36:00 -08002695 private boolean mIsLegacy;
2696 private boolean mIsLegacyInitialized;
Christoph Studer7ac80e62014-08-04 16:01:57 +02002697
2698 /**
Adrian Roos4ff3b122016-02-01 12:26:13 -08002699 * Caches a contrast-enhanced version of {@link #mCachedContrastColorIsFor}.
2700 */
2701 private int mCachedContrastColor = COLOR_INVALID;
2702 private int mCachedContrastColorIsFor = COLOR_INVALID;
Adrian Roos487374f2017-01-11 15:48:14 -08002703 /**
2704 * Caches a ambient version of {@link #mCachedContrastColorIsFor}.
2705 */
2706 private int mCachedAmbientColor = COLOR_INVALID;
2707 private int mCachedAmbientColorIsFor = COLOR_INVALID;
Adrian Roos4ff3b122016-02-01 12:26:13 -08002708
2709 /**
Adrian Roos70d7aa32017-01-11 15:39:06 -08002710 * Caches an instance of StandardTemplateParams. Note that this may have been used before,
2711 * so make sure to call {@link StandardTemplateParams#reset()} before using it.
2712 */
2713 StandardTemplateParams mParams = new StandardTemplateParams();
Selim Cinek7b9605b2017-01-19 17:36:00 -08002714 private int mTextColorsAreForBackground = COLOR_INVALID;
2715 private int mPrimaryTextColor = COLOR_INVALID;
2716 private int mSecondaryTextColor = COLOR_INVALID;
2717 private int mActionBarColor = COLOR_INVALID;
Selim Cinek5fb73f82017-04-20 16:55:38 -07002718 private int mBackgroundColor = COLOR_INVALID;
2719 private int mForegroundColor = COLOR_INVALID;
Selim Cinekac5f0272017-05-02 16:05:41 -07002720 private int mBackgroundColorHint = COLOR_INVALID;
Adrian Roos70d7aa32017-01-11 15:39:06 -08002721
2722 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002723 * Constructs a new Builder with the defaults:
Joe Onoratocb109a02011-01-18 17:57:41 -08002724 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002725 * @param context
2726 * A {@link Context} that will be used by the Builder to construct the
2727 * RemoteViews. The Context will not be held past the lifetime of this Builder
2728 * object.
Geoffrey Pitsch5caa2762017-01-12 09:35:54 -05002729 * @param channelId
2730 * The constructed Notification will be posted on this
2731 * {@link NotificationChannel}. To use a NotificationChannel, it must first be
2732 * created using {@link NotificationManager#createNotificationChannel}.
Joe Onoratocb109a02011-01-18 17:57:41 -08002733 */
Geoffrey Pitsch5caa2762017-01-12 09:35:54 -05002734 public Builder(Context context, String channelId) {
2735 this(context, (Notification) null);
2736 mN.mChannelId = channelId;
2737 }
2738
2739 /**
2740 * @deprecated use {@link Notification.Builder#Notification.Builder(Context, String)}
2741 * instead. All posted Notifications must specify a NotificationChannel Id.
2742 */
2743 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08002744 public Builder(Context context) {
Geoffrey Pitsch5caa2762017-01-12 09:35:54 -05002745 this(context, (Notification) null);
Joe Onorato46439ce2010-11-19 13:56:21 -08002746 }
2747
Joe Onoratocb109a02011-01-18 17:57:41 -08002748 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002749 * @hide
Christoph Studer4600f9b2014-07-22 22:44:43 +02002750 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002751 public Builder(Context context, Notification toAdopt) {
2752 mContext = context;
Christoph Studer4600f9b2014-07-22 22:44:43 +02002753
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002754 if (toAdopt == null) {
2755 mN = new Notification();
Selim Cinek0ff1ce602016-04-05 18:27:16 -07002756 if (context.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N) {
2757 mN.extras.putBoolean(EXTRA_SHOW_WHEN, true);
2758 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002759 mN.priority = PRIORITY_DEFAULT;
2760 mN.visibility = VISIBILITY_PRIVATE;
2761 } else {
2762 mN = toAdopt;
2763 if (mN.actions != null) {
2764 Collections.addAll(mActions, mN.actions);
Christoph Studer4600f9b2014-07-22 22:44:43 +02002765 }
2766
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002767 if (mN.extras.containsKey(EXTRA_PEOPLE)) {
2768 Collections.addAll(mPersonList, mN.extras.getStringArray(EXTRA_PEOPLE));
2769 }
2770
Selim Cinek4ac6f602016-06-13 15:47:03 -07002771 if (mN.getSmallIcon() == null && mN.icon != 0) {
2772 setSmallIcon(mN.icon);
2773 }
2774
2775 if (mN.getLargeIcon() == null && mN.largeIcon != null) {
2776 setLargeIcon(mN.largeIcon);
2777 }
2778
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002779 String templateClass = mN.extras.getString(EXTRA_TEMPLATE);
2780 if (!TextUtils.isEmpty(templateClass)) {
2781 final Class<? extends Style> styleClass
2782 = getNotificationStyleClass(templateClass);
2783 if (styleClass == null) {
2784 Log.d(TAG, "Unknown style class: " + templateClass);
2785 } else {
2786 try {
Adrian Roosc1a80b02016-04-05 14:54:55 -07002787 final Constructor<? extends Style> ctor =
2788 styleClass.getDeclaredConstructor();
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002789 ctor.setAccessible(true);
2790 final Style style = ctor.newInstance();
2791 style.restoreFromExtras(mN.extras);
2792
2793 if (style != null) {
2794 setStyle(style);
2795 }
2796 } catch (Throwable t) {
2797 Log.e(TAG, "Could not create Style", t);
2798 }
2799 }
2800 }
2801
2802 }
2803 }
2804
2805 private NotificationColorUtil getColorUtil() {
Selim Cinek99104832017-01-25 14:47:33 -08002806 if (mColorUtil == null) {
2807 mColorUtil = NotificationColorUtil.getInstance(mContext);
Christoph Studer4600f9b2014-07-22 22:44:43 +02002808 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002809 return mColorUtil;
Christoph Studer4600f9b2014-07-22 22:44:43 +02002810 }
2811
2812 /**
Julia Reynolds13d898c2017-02-02 12:22:05 -05002813 * If this notification is duplicative of a Launcher shortcut, sets the
2814 * {@link ShortcutInfo#getId() id} of the shortcut, in case the Launcher wants to hide
2815 * the shortcut.
2816 *
Julia Reynoldsbad42972017-04-25 13:52:49 -04002817 * This field will be ignored by Launchers that don't support badging, don't show
2818 * notification content, or don't show {@link android.content.pm.ShortcutManager shortcuts}.
Julia Reynolds13d898c2017-02-02 12:22:05 -05002819 *
2820 * @param shortcutId the {@link ShortcutInfo#getId() id} of the shortcut this notification
2821 * supersedes
2822 */
2823 public Builder setShortcutId(String shortcutId) {
2824 mN.mShortcutId = shortcutId;
2825 return this;
2826 }
2827
2828 /**
Julia Reynoldse071abd2017-03-22 10:52:11 -04002829 * Sets which icon to display as a badge for this notification.
2830 *
2831 * Must be one of {@link #BADGE_ICON_NONE}, {@link #BADGE_ICON_SMALL},
2832 * {@link #BADGE_ICON_LARGE}.
2833 *
2834 * Note: This value might be ignored, for launchers that don't support badge icons.
2835 */
Julia Reynolds612beb22017-03-30 10:48:30 -04002836 public Builder setBadgeIconType(int icon) {
Julia Reynoldse071abd2017-03-22 10:52:11 -04002837 mN.mBadgeIcon = icon;
2838 return this;
2839 }
2840
2841 /**
Julia Reynoldsa79c3712017-04-21 10:29:57 -04002842 * Sets the group alert behavior for this notification. Use this method to mute this
2843 * notification if alerts for this notification's group should be handled by a different
2844 * notification. This is only applicable for notifications that belong to a
2845 * {@link #setGroup(String) group}.
2846 *
2847 * <p> The default value is {@link #GROUP_ALERT_ALL}.</p>
2848 */
2849 public Builder setGroupAlertBehavior(@GroupAlertBehavior int groupAlertBehavior) {
2850 mN.mGroupAlertBehavior = groupAlertBehavior;
2851 return this;
2852 }
2853
Jeff Sharkey000ce802017-04-29 13:13:27 -06002854 /** @removed */
2855 @Deprecated
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04002856 public Builder setChannel(String channelId) {
2857 mN.mChannelId = channelId;
2858 return this;
2859 }
2860
2861 /**
Julia Reynoldsbad42972017-04-25 13:52:49 -04002862 * Specifies the channel the notification should be delivered on.
2863 */
2864 public Builder setChannelId(String channelId) {
2865 mN.mChannelId = channelId;
2866 return this;
2867 }
2868
Jeff Sharkey000ce802017-04-29 13:13:27 -06002869 /** @removed */
2870 @Deprecated
Julia Reynolds50989772017-02-23 14:32:16 -05002871 public Builder setTimeout(long durationMs) {
2872 mN.mTimeout = durationMs;
Julia Reynolds2a128742016-11-28 14:29:25 -05002873 return this;
2874 }
2875
2876 /**
Julia Reynoldsbad42972017-04-25 13:52:49 -04002877 * Specifies a duration in milliseconds after which this notification should be canceled,
2878 * if it is not already canceled.
2879 */
2880 public Builder setTimeoutAfter(long durationMs) {
2881 mN.mTimeout = durationMs;
2882 return this;
2883 }
2884
2885 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002886 * Add a timestamp pertaining to the notification (usually the time the event occurred).
Selim Cinek0ff1ce602016-04-05 18:27:16 -07002887 *
2888 * For apps targeting {@link android.os.Build.VERSION_CODES#N} and above, this time is not
2889 * shown anymore by default and must be opted into by using
2890 * {@link android.app.Notification.Builder#setShowWhen(boolean)}
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002891 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002892 * @see Notification#when
Joe Onoratocb109a02011-01-18 17:57:41 -08002893 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002894 public Builder setWhen(long when) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002895 mN.when = when;
Joe Onorato46439ce2010-11-19 13:56:21 -08002896 return this;
2897 }
2898
Joe Onoratocb109a02011-01-18 17:57:41 -08002899 /**
Griff Hazen50c11652014-05-16 09:46:31 -07002900 * Control whether the timestamp set with {@link #setWhen(long) setWhen} is shown
Daniel Sandler0c890492012-09-12 17:23:10 -07002901 * in the content view.
Selim Cinek0ff1ce602016-04-05 18:27:16 -07002902 * For apps targeting {@link android.os.Build.VERSION_CODES#N} and above, this defaults to
2903 * {@code false}. For earlier apps, the default is {@code true}.
Daniel Sandler0c890492012-09-12 17:23:10 -07002904 */
2905 public Builder setShowWhen(boolean show) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002906 mN.extras.putBoolean(EXTRA_SHOW_WHEN, show);
Daniel Sandler0c890492012-09-12 17:23:10 -07002907 return this;
2908 }
2909
2910 /**
Daniel Sandlerd33b8032012-05-10 11:41:48 -04002911 * Show the {@link Notification#when} field as a stopwatch.
Joe Malin8d40d042012-11-05 11:36:40 -08002912 *
2913 * Instead of presenting <code>when</code> as a timestamp, the notification will show an
Daniel Sandlerd33b8032012-05-10 11:41:48 -04002914 * automatically updating display of the minutes and seconds since <code>when</code>.
Daniel Sandlera2985ed2012-04-03 16:42:00 -04002915 *
Daniel Sandlerd33b8032012-05-10 11:41:48 -04002916 * Useful when showing an elapsed time (like an ongoing phone call).
2917 *
Selim Cinek81c23aa2016-02-25 16:23:13 -08002918 * The counter can also be set to count down to <code>when</code> when using
Adrian Roos96b7e202016-05-17 13:50:38 -07002919 * {@link #setChronometerCountDown(boolean)}.
Selim Cinek81c23aa2016-02-25 16:23:13 -08002920 *
Daniel Sandlerd33b8032012-05-10 11:41:48 -04002921 * @see android.widget.Chronometer
Daniel Sandlera2985ed2012-04-03 16:42:00 -04002922 * @see Notification#when
Adrian Roos96b7e202016-05-17 13:50:38 -07002923 * @see #setChronometerCountDown(boolean)
Daniel Sandlera2985ed2012-04-03 16:42:00 -04002924 */
2925 public Builder setUsesChronometer(boolean b) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002926 mN.extras.putBoolean(EXTRA_SHOW_CHRONOMETER, b);
Daniel Sandlera2985ed2012-04-03 16:42:00 -04002927 return this;
2928 }
2929
2930 /**
Selim Cinek81c23aa2016-02-25 16:23:13 -08002931 * Sets the Chronometer to count down instead of counting up.
2932 *
2933 * <p>This is only relevant if {@link #setUsesChronometer(boolean)} has been set to true.
2934 * If it isn't set the chronometer will count up.
2935 *
2936 * @see #setUsesChronometer(boolean)
2937 */
Adrian Roos96b7e202016-05-17 13:50:38 -07002938 public Builder setChronometerCountDown(boolean countDown) {
2939 mN.extras.putBoolean(EXTRA_CHRONOMETER_COUNT_DOWN, countDown);
Selim Cinek81c23aa2016-02-25 16:23:13 -08002940 return this;
2941 }
2942
2943 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002944 * Set the small icon resource, which will be used to represent the notification in the
2945 * status bar.
Joe Onoratocb109a02011-01-18 17:57:41 -08002946 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002947
2948 * The platform template for the expanded view will draw this icon in the left, unless a
2949 * {@link #setLargeIcon(Bitmap) large icon} has also been specified, in which case the small
2950 * icon will be moved to the right-hand side.
2951 *
2952
2953 * @param icon
2954 * A resource ID in the application's package of the drawable to use.
2955 * @see Notification#icon
Joe Onoratocb109a02011-01-18 17:57:41 -08002956 */
Tor Norbye7b9c9122013-05-30 16:48:33 -07002957 public Builder setSmallIcon(@DrawableRes int icon) {
Dan Sandlerd63f9322015-05-06 15:18:49 -04002958 return setSmallIcon(icon != 0
2959 ? Icon.createWithResource(mContext, icon)
2960 : null);
Joe Onorato46439ce2010-11-19 13:56:21 -08002961 }
2962
Joe Onoratocb109a02011-01-18 17:57:41 -08002963 /**
2964 * A variant of {@link #setSmallIcon(int) setSmallIcon(int)} that takes an additional
2965 * level parameter for when the icon is a {@link android.graphics.drawable.LevelListDrawable
2966 * LevelListDrawable}.
2967 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002968 * @param icon A resource ID in the application's package of the drawable to use.
Joe Onoratocb109a02011-01-18 17:57:41 -08002969 * @param level The level to use for the icon.
2970 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002971 * @see Notification#icon
2972 * @see Notification#iconLevel
Joe Onoratocb109a02011-01-18 17:57:41 -08002973 */
Tor Norbye7b9c9122013-05-30 16:48:33 -07002974 public Builder setSmallIcon(@DrawableRes int icon, int level) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002975 mN.iconLevel = level;
Dan Sandlerd63f9322015-05-06 15:18:49 -04002976 return setSmallIcon(icon);
2977 }
2978
2979 /**
2980 * Set the small icon, which will be used to represent the notification in the
2981 * status bar and content view (unless overriden there by a
2982 * {@link #setLargeIcon(Bitmap) large icon}).
2983 *
2984 * @param icon An Icon object to use.
2985 * @see Notification#icon
2986 */
2987 public Builder setSmallIcon(Icon icon) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002988 mN.setSmallIcon(icon);
2989 if (icon != null && icon.getType() == Icon.TYPE_RESOURCE) {
2990 mN.icon = icon.getResId();
2991 }
Joe Onorato46439ce2010-11-19 13:56:21 -08002992 return this;
2993 }
2994
Joe Onoratocb109a02011-01-18 17:57:41 -08002995 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002996 * Set the first line of text in the platform notification template.
Joe Onoratocb109a02011-01-18 17:57:41 -08002997 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002998 public Builder setContentTitle(CharSequence title) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002999 mN.extras.putCharSequence(EXTRA_TITLE, safeCharSequence(title));
Joe Onorato46439ce2010-11-19 13:56:21 -08003000 return this;
3001 }
3002
Joe Onoratocb109a02011-01-18 17:57:41 -08003003 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003004 * Set the second line of text in the platform notification template.
Joe Onoratocb109a02011-01-18 17:57:41 -08003005 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003006 public Builder setContentText(CharSequence text) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003007 mN.extras.putCharSequence(EXTRA_TEXT, safeCharSequence(text));
Joe Onorato46439ce2010-11-19 13:56:21 -08003008 return this;
3009 }
3010
Joe Onoratocb109a02011-01-18 17:57:41 -08003011 /**
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07003012 * This provides some additional information that is displayed in the notification. No
3013 * guarantees are given where exactly it is displayed.
3014 *
3015 * <p>This information should only be provided if it provides an essential
3016 * benefit to the understanding of the notification. The more text you provide the
3017 * less readable it becomes. For example, an email client should only provide the account
3018 * name here if more than one email account has been added.</p>
3019 *
3020 * <p>As of {@link android.os.Build.VERSION_CODES#N} this information is displayed in the
3021 * notification header area.
3022 *
3023 * On Android versions before {@link android.os.Build.VERSION_CODES#N}
3024 * this will be shown in the third line of text in the platform notification template.
3025 * You should not be using {@link #setProgress(int, int, boolean)} at the
3026 * same time on those versions; they occupy the same place.
3027 * </p>
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003028 */
3029 public Builder setSubText(CharSequence text) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003030 mN.extras.putCharSequence(EXTRA_SUB_TEXT, safeCharSequence(text));
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003031 return this;
3032 }
3033
3034 /**
Julia Reynolds3aedded2017-03-31 14:42:09 -04003035 * Provides text that will appear as a link to your application's settings.
3036 *
3037 * <p>This text does not appear within notification {@link Style templates} but may
3038 * appear when the user uses an affordance to learn more about the notification.
3039 * Additionally, this text will not appear unless you provide a valid link target by
3040 * handling {@link #INTENT_CATEGORY_NOTIFICATION_PREFERENCES}.
3041 *
3042 * <p>This text is meant to be concise description about what the user can customize
3043 * when they click on this link. The recommended maximum length is 40 characters.
3044 * @param text
3045 * @return
3046 */
3047 public Builder setSettingsText(CharSequence text) {
3048 mN.mSettingsText = safeCharSequence(text);
3049 return this;
3050 }
3051
3052 /**
Adrian Roose458aa82015-12-08 16:17:19 -08003053 * Set the remote input history.
3054 *
3055 * This should be set to the most recent inputs that have been sent
3056 * through a {@link RemoteInput} of this Notification and cleared once the it is no
3057 * longer relevant (e.g. for chat notifications once the other party has responded).
3058 *
3059 * The most recent input must be stored at the 0 index, the second most recent at the
3060 * 1 index, etc. Note that the system will limit both how far back the inputs will be shown
3061 * and how much of each individual input is shown.
3062 *
3063 * <p>Note: The reply text will only be shown on notifications that have least one action
3064 * with a {@code RemoteInput}.</p>
3065 */
3066 public Builder setRemoteInputHistory(CharSequence[] text) {
3067 if (text == null) {
3068 mN.extras.putCharSequenceArray(EXTRA_REMOTE_INPUT_HISTORY, null);
3069 } else {
3070 final int N = Math.min(MAX_REPLY_HISTORY, text.length);
3071 CharSequence[] safe = new CharSequence[N];
3072 for (int i = 0; i < N; i++) {
3073 safe[i] = safeCharSequence(text[i]);
3074 }
3075 mN.extras.putCharSequenceArray(EXTRA_REMOTE_INPUT_HISTORY, safe);
3076 }
3077 return this;
3078 }
3079
3080 /**
Julia Reynolds13d898c2017-02-02 12:22:05 -05003081 * Sets the number of items this notification represents. May be displayed as a badge count
3082 * for Launchers that support badging.
Joe Onoratocb109a02011-01-18 17:57:41 -08003083 */
Joe Onorato8595a3d2010-11-19 18:12:07 -08003084 public Builder setNumber(int number) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003085 mN.number = number;
Joe Onorato8595a3d2010-11-19 18:12:07 -08003086 return this;
3087 }
3088
Joe Onoratocb109a02011-01-18 17:57:41 -08003089 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003090 * A small piece of additional information pertaining to this notification.
3091 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003092 * The platform template will draw this on the last line of the notification, at the far
3093 * right (to the right of a smallIcon if it has been placed there).
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07003094 *
3095 * @deprecated use {@link #setSubText(CharSequence)} instead to set a text in the header.
3096 * For legacy apps targeting a version below {@link android.os.Build.VERSION_CODES#N} this
3097 * field will still show up, but the subtext will take precedence.
Joe Onoratocb109a02011-01-18 17:57:41 -08003098 */
Aurimas Liutikas514c5ef2016-05-24 15:22:55 -07003099 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08003100 public Builder setContentInfo(CharSequence info) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003101 mN.extras.putCharSequence(EXTRA_INFO_TEXT, safeCharSequence(info));
Joe Onorato46439ce2010-11-19 13:56:21 -08003102 return this;
3103 }
3104
Joe Onoratocb109a02011-01-18 17:57:41 -08003105 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003106 * Set the progress this notification represents.
3107 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003108 * The platform template will represent this using a {@link ProgressBar}.
Jeff Sharkey1c400132011-08-05 14:50:13 -07003109 */
3110 public Builder setProgress(int max, int progress, boolean indeterminate) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003111 mN.extras.putInt(EXTRA_PROGRESS, progress);
3112 mN.extras.putInt(EXTRA_PROGRESS_MAX, max);
3113 mN.extras.putBoolean(EXTRA_PROGRESS_INDETERMINATE, indeterminate);
Jeff Sharkey1c400132011-08-05 14:50:13 -07003114 return this;
3115 }
3116
3117 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003118 * Supply a custom RemoteViews to use instead of the platform template.
3119 *
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003120 * Use {@link #setCustomContentView(RemoteViews)} instead.
Joe Onoratocb109a02011-01-18 17:57:41 -08003121 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003122 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08003123 public Builder setContent(RemoteViews views) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003124 return setCustomContentView(views);
3125 }
3126
3127 /**
3128 * Supply custom RemoteViews to use instead of the platform template.
3129 *
3130 * This will override the layout that would otherwise be constructed by this Builder
3131 * object.
3132 */
3133 public Builder setCustomContentView(RemoteViews contentView) {
3134 mN.contentView = contentView;
3135 return this;
3136 }
3137
3138 /**
3139 * Supply custom RemoteViews to use instead of the platform template in the expanded form.
3140 *
3141 * This will override the expanded layout that would otherwise be constructed by this
3142 * Builder object.
3143 */
3144 public Builder setCustomBigContentView(RemoteViews contentView) {
3145 mN.bigContentView = contentView;
3146 return this;
3147 }
3148
3149 /**
3150 * Supply custom RemoteViews to use instead of the platform template in the heads up dialog.
3151 *
3152 * This will override the heads-up layout that would otherwise be constructed by this
3153 * Builder object.
3154 */
3155 public Builder setCustomHeadsUpContentView(RemoteViews contentView) {
3156 mN.headsUpContentView = contentView;
Joe Onorato46439ce2010-11-19 13:56:21 -08003157 return this;
3158 }
3159
Joe Onoratocb109a02011-01-18 17:57:41 -08003160 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003161 * Supply a {@link PendingIntent} to be sent when the notification is clicked.
3162 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003163 * As of {@link android.os.Build.VERSION_CODES#HONEYCOMB}, if this field is unset and you
3164 * have specified a custom RemoteViews with {@link #setContent(RemoteViews)}, you can use
3165 * {@link RemoteViews#setOnClickPendingIntent RemoteViews.setOnClickPendingIntent(int,PendingIntent)}
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003166 * to assign PendingIntents to individual views in that custom layout (i.e., to create
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003167 * clickable buttons inside the notification view).
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003168 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003169 * @see Notification#contentIntent Notification.contentIntent
Joe Onoratocb109a02011-01-18 17:57:41 -08003170 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003171 public Builder setContentIntent(PendingIntent intent) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003172 mN.contentIntent = intent;
Joe Onorato46439ce2010-11-19 13:56:21 -08003173 return this;
3174 }
3175
Joe Onoratocb109a02011-01-18 17:57:41 -08003176 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003177 * Supply a {@link PendingIntent} to send when the notification is cleared explicitly by the user.
3178 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003179 * @see Notification#deleteIntent
Joe Onoratocb109a02011-01-18 17:57:41 -08003180 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003181 public Builder setDeleteIntent(PendingIntent intent) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003182 mN.deleteIntent = intent;
Joe Onorato46439ce2010-11-19 13:56:21 -08003183 return this;
3184 }
3185
Joe Onoratocb109a02011-01-18 17:57:41 -08003186 /**
3187 * An intent to launch instead of posting the notification to the status bar.
3188 * Only for use with extremely high-priority notifications demanding the user's
3189 * <strong>immediate</strong> attention, such as an incoming phone call or
3190 * alarm clock that the user has explicitly set to a particular time.
3191 * If this facility is used for something else, please give the user an option
3192 * to turn it off and use a normal notification, as this can be extremely
3193 * disruptive.
3194 *
Chris Wren47c20a12014-06-18 17:27:29 -04003195 * <p>
3196 * The system UI may choose to display a heads-up notification, instead of
3197 * launching this intent, while the user is using the device.
3198 * </p>
3199 *
Joe Onoratocb109a02011-01-18 17:57:41 -08003200 * @param intent The pending intent to launch.
3201 * @param highPriority Passing true will cause this notification to be sent
3202 * even if other notifications are suppressed.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003203 *
3204 * @see Notification#fullScreenIntent
Joe Onoratocb109a02011-01-18 17:57:41 -08003205 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003206 public Builder setFullScreenIntent(PendingIntent intent, boolean highPriority) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003207 mN.fullScreenIntent = intent;
Joe Onorato46439ce2010-11-19 13:56:21 -08003208 setFlag(FLAG_HIGH_PRIORITY, highPriority);
3209 return this;
3210 }
3211
Joe Onoratocb109a02011-01-18 17:57:41 -08003212 /**
Dan Sandler5fcdf6e2014-07-18 11:31:15 -04003213 * Set the "ticker" text which is sent to accessibility services.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003214 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003215 * @see Notification#tickerText
Joe Onoratocb109a02011-01-18 17:57:41 -08003216 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003217 public Builder setTicker(CharSequence tickerText) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003218 mN.tickerText = safeCharSequence(tickerText);
Joe Onorato46439ce2010-11-19 13:56:21 -08003219 return this;
3220 }
3221
Joe Onoratocb109a02011-01-18 17:57:41 -08003222 /**
Dan Sandler5fcdf6e2014-07-18 11:31:15 -04003223 * Obsolete version of {@link #setTicker(CharSequence)}.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003224 *
Joe Onoratocb109a02011-01-18 17:57:41 -08003225 */
Dan Sandler5fcdf6e2014-07-18 11:31:15 -04003226 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08003227 public Builder setTicker(CharSequence tickerText, RemoteViews views) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003228 setTicker(tickerText);
3229 // views is ignored
Joe Onorato46439ce2010-11-19 13:56:21 -08003230 return this;
3231 }
3232
Joe Onoratocb109a02011-01-18 17:57:41 -08003233 /**
Dan Sandlerd63f9322015-05-06 15:18:49 -04003234 * Add a large icon to the notification content view.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003235 *
3236 * In the platform template, this image will be shown on the left of the notification view
Dan Sandlerd63f9322015-05-06 15:18:49 -04003237 * in place of the {@link #setSmallIcon(Icon) small icon} (which will be placed in a small
3238 * badge atop the large icon).
Dan Sandler08a04c12015-05-06 15:18:49 -04003239 */
Dan Sandlerd63f9322015-05-06 15:18:49 -04003240 public Builder setLargeIcon(Bitmap b) {
3241 return setLargeIcon(b != null ? Icon.createWithBitmap(b) : null);
3242 }
3243
3244 /**
3245 * Add a large icon to the notification content view.
3246 *
3247 * In the platform template, this image will be shown on the left of the notification view
3248 * in place of the {@link #setSmallIcon(Icon) small icon} (which will be placed in a small
3249 * badge atop the large icon).
3250 */
3251 public Builder setLargeIcon(Icon icon) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003252 mN.mLargeIcon = icon;
3253 mN.extras.putParcelable(EXTRA_LARGE_ICON, icon);
Joe Onorato46439ce2010-11-19 13:56:21 -08003254 return this;
3255 }
3256
Joe Onoratocb109a02011-01-18 17:57:41 -08003257 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003258 * Set the sound to play.
3259 *
John Spurlockc0650f022014-07-19 13:22:39 -04003260 * It will be played using the {@link #AUDIO_ATTRIBUTES_DEFAULT default audio attributes}
3261 * for notifications.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003262 *
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003263 * @deprecated use {@link NotificationChannel#setSound(Uri, AudioAttributes)} instead.
Joe Onoratocb109a02011-01-18 17:57:41 -08003264 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003265 @Deprecated
Joe Onorato52f80cd2010-11-21 15:34:48 -08003266 public Builder setSound(Uri sound) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003267 mN.sound = sound;
3268 mN.audioAttributes = AUDIO_ATTRIBUTES_DEFAULT;
Joe Onorato52f80cd2010-11-21 15:34:48 -08003269 return this;
3270 }
3271
Joe Onoratocb109a02011-01-18 17:57:41 -08003272 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003273 * Set the sound to play, along with a specific stream on which to play it.
Joe Onoratocb109a02011-01-18 17:57:41 -08003274 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003275 * See {@link android.media.AudioManager} for the <code>STREAM_</code> constants.
3276 *
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003277 * @deprecated use {@link NotificationChannel#setSound(Uri, AudioAttributes)}.
Joe Onoratocb109a02011-01-18 17:57:41 -08003278 */
Jean-Michel Trivi81f871e2014-08-06 16:32:38 -07003279 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08003280 public Builder setSound(Uri sound, int streamType) {
Jean-Michel Trivi2f7511f2016-11-28 15:40:27 -08003281 PlayerBase.deprecateStreamTypeForPlayback(streamType, "Notification", "setSound()");
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003282 mN.sound = sound;
3283 mN.audioStreamType = streamType;
Joe Onorato46439ce2010-11-19 13:56:21 -08003284 return this;
3285 }
3286
Joe Onoratocb109a02011-01-18 17:57:41 -08003287 /**
John Spurlockc0650f022014-07-19 13:22:39 -04003288 * Set the sound to play, along with specific {@link AudioAttributes audio attributes} to
3289 * use during playback.
3290 *
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003291 * @deprecated use {@link NotificationChannel#setSound(Uri, AudioAttributes)} instead.
John Spurlockc0650f022014-07-19 13:22:39 -04003292 * @see Notification#sound
3293 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003294 @Deprecated
John Spurlockc0650f022014-07-19 13:22:39 -04003295 public Builder setSound(Uri sound, AudioAttributes audioAttributes) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003296 mN.sound = sound;
3297 mN.audioAttributes = audioAttributes;
John Spurlockc0650f022014-07-19 13:22:39 -04003298 return this;
3299 }
3300
3301 /**
Joe Onoratocb109a02011-01-18 17:57:41 -08003302 * Set the vibration pattern to use.
3303 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003304 * See {@link android.os.Vibrator#vibrate(long[], int)} for a discussion of the
3305 * <code>pattern</code> parameter.
3306 *
Chris Wren47c20a12014-06-18 17:27:29 -04003307 * <p>
3308 * A notification that vibrates is more likely to be presented as a heads-up notification.
3309 * </p>
3310 *
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003311 * @deprecated use {@link NotificationChannel#setVibrationPattern(long[])} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003312 * @see Notification#vibrate
Joe Onoratocb109a02011-01-18 17:57:41 -08003313 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003314 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08003315 public Builder setVibrate(long[] pattern) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003316 mN.vibrate = pattern;
Joe Onorato46439ce2010-11-19 13:56:21 -08003317 return this;
3318 }
3319
Joe Onoratocb109a02011-01-18 17:57:41 -08003320 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003321 * Set the desired color for the indicator LED on the device, as well as the
3322 * blink duty cycle (specified in milliseconds).
3323 *
3324
3325 * Not all devices will honor all (or even any) of these values.
3326 *
Julia Reynolds529e3322017-02-06 08:33:01 -05003327 * @deprecated use {@link NotificationChannel#enableLights(boolean)} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003328 * @see Notification#ledARGB
3329 * @see Notification#ledOnMS
3330 * @see Notification#ledOffMS
Joe Onoratocb109a02011-01-18 17:57:41 -08003331 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003332 @Deprecated
Tor Norbye80756e32015-03-02 09:39:27 -08003333 public Builder setLights(@ColorInt int argb, int onMs, int offMs) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003334 mN.ledARGB = argb;
3335 mN.ledOnMS = onMs;
3336 mN.ledOffMS = offMs;
Julia Reynolds10ee1fc2015-11-09 11:04:55 -05003337 if (onMs != 0 || offMs != 0) {
3338 mN.flags |= FLAG_SHOW_LIGHTS;
3339 }
Joe Onorato46439ce2010-11-19 13:56:21 -08003340 return this;
3341 }
3342
Joe Onoratocb109a02011-01-18 17:57:41 -08003343 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003344 * Set whether this is an "ongoing" notification.
Joe Onoratocb109a02011-01-18 17:57:41 -08003345 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003346
3347 * Ongoing notifications cannot be dismissed by the user, so your application or service
3348 * must take care of canceling them.
3349 *
3350
3351 * They are typically used to indicate a background task that the user is actively engaged
3352 * with (e.g., playing music) or is pending in some way and therefore occupying the device
3353 * (e.g., a file download, sync operation, active network connection).
3354 *
3355
3356 * @see Notification#FLAG_ONGOING_EVENT
3357 * @see Service#setForeground(boolean)
Joe Onoratocb109a02011-01-18 17:57:41 -08003358 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003359 public Builder setOngoing(boolean ongoing) {
3360 setFlag(FLAG_ONGOING_EVENT, ongoing);
3361 return this;
3362 }
3363
Joe Onoratocb109a02011-01-18 17:57:41 -08003364 /**
Selim Cinek7b9605b2017-01-19 17:36:00 -08003365 * Set whether this notification should be colorized. When set, the color set with
3366 * {@link #setColor(int)} will be used as the background color of this notification.
3367 * <p>
Selim Cinek7b9605b2017-01-19 17:36:00 -08003368 * This should only be used for high priority ongoing tasks like navigation, an ongoing
3369 * call, or other similarly high-priority events for the user.
Selim Cinek99104832017-01-25 14:47:33 -08003370 * <p>
Selim Cinek22714f12017-04-13 16:23:53 -07003371 * For most styles, the coloring will only be applied if the notification is for a
3372 * foreground service notification.
Selim Cinek99104832017-01-25 14:47:33 -08003373 * However, for {@link MediaStyle} and {@link DecoratedMediaCustomViewStyle} notifications
Selim Cinek22714f12017-04-13 16:23:53 -07003374 * that have a media session attached there is no such requirement.
Selim Cinek7b9605b2017-01-19 17:36:00 -08003375 *
Selim Cinek7b9605b2017-01-19 17:36:00 -08003376 * @see Builder#setColor(int)
Selim Cinek99104832017-01-25 14:47:33 -08003377 * @see MediaStyle#setMediaSession(MediaSession.Token)
Selim Cinek7b9605b2017-01-19 17:36:00 -08003378 */
3379 public Builder setColorized(boolean colorize) {
3380 mN.extras.putBoolean(EXTRA_COLORIZED, colorize);
3381 return this;
3382 }
3383
3384 /**
Joe Onoratocb109a02011-01-18 17:57:41 -08003385 * Set this flag if you would only like the sound, vibrate
3386 * and ticker to be played if the notification is not already showing.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003387 *
3388 * @see Notification#FLAG_ONLY_ALERT_ONCE
Joe Onoratocb109a02011-01-18 17:57:41 -08003389 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003390 public Builder setOnlyAlertOnce(boolean onlyAlertOnce) {
3391 setFlag(FLAG_ONLY_ALERT_ONCE, onlyAlertOnce);
3392 return this;
3393 }
3394
Joe Onoratocb109a02011-01-18 17:57:41 -08003395 /**
Julia Reynolds04499532016-09-13 14:04:53 -04003396 * Make this notification automatically dismissed when the user touches it.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003397 *
3398 * @see Notification#FLAG_AUTO_CANCEL
Joe Onoratocb109a02011-01-18 17:57:41 -08003399 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003400 public Builder setAutoCancel(boolean autoCancel) {
Joe Onorato281d83f2011-01-04 17:13:10 -08003401 setFlag(FLAG_AUTO_CANCEL, autoCancel);
Joe Onorato46439ce2010-11-19 13:56:21 -08003402 return this;
3403 }
3404
Joe Onoratocb109a02011-01-18 17:57:41 -08003405 /**
Griff Hazendfcb0802014-02-11 12:00:00 -08003406 * Set whether or not this notification should not bridge to other devices.
3407 *
3408 * <p>Some notifications can be bridged to other devices for remote display.
3409 * This hint can be set to recommend this notification not be bridged.
3410 */
3411 public Builder setLocalOnly(boolean localOnly) {
3412 setFlag(FLAG_LOCAL_ONLY, localOnly);
3413 return this;
3414 }
3415
3416 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003417 * Set which notification properties will be inherited from system defaults.
Joe Onoratocb109a02011-01-18 17:57:41 -08003418 * <p>
3419 * The value should be one or more of the following fields combined with
3420 * bitwise-or:
3421 * {@link #DEFAULT_SOUND}, {@link #DEFAULT_VIBRATE}, {@link #DEFAULT_LIGHTS}.
3422 * <p>
3423 * For all default values, use {@link #DEFAULT_ALL}.
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003424 *
3425 * @deprecated use {@link NotificationChannel#enableVibration(boolean)} and
Julia Reynolds529e3322017-02-06 08:33:01 -05003426 * {@link NotificationChannel#enableLights(boolean)} and
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003427 * {@link NotificationChannel#setSound(Uri, AudioAttributes)} instead.
Joe Onoratocb109a02011-01-18 17:57:41 -08003428 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003429 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08003430 public Builder setDefaults(int defaults) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003431 mN.defaults = defaults;
Joe Onorato46439ce2010-11-19 13:56:21 -08003432 return this;
3433 }
3434
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003435 /**
3436 * Set the priority of this notification.
3437 *
3438 * @see Notification#priority
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003439 * @deprecated use {@link NotificationChannel#setImportance(int)} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003440 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003441 @Deprecated
Tor Norbyed9273d62013-05-30 15:59:53 -07003442 public Builder setPriority(@Priority int pri) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003443 mN.priority = pri;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003444 return this;
3445 }
Joe Malin8d40d042012-11-05 11:36:40 -08003446
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003447 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -04003448 * Set the notification category.
Joe Malin8d40d042012-11-05 11:36:40 -08003449 *
John Spurlockfd7f1e02014-03-18 16:41:57 -04003450 * @see Notification#category
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003451 */
John Spurlockfd7f1e02014-03-18 16:41:57 -04003452 public Builder setCategory(String category) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003453 mN.category = category;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003454 return this;
3455 }
3456
3457 /**
Chris Wrendde75302014-03-26 17:24:15 -04003458 * Add a person that is relevant to this notification.
3459 *
Chris Wrene6c48932014-09-29 17:19:27 -04003460 * <P>
3461 * Depending on user preferences, this annotation may allow the notification to pass
Julia Reynoldse071abd2017-03-22 10:52:11 -04003462 * through interruption filters, if this notification is of category {@link #CATEGORY_CALL}
3463 * or {@link #CATEGORY_MESSAGE}. The addition of people may also cause this notification to
3464 * appear more prominently in the user interface.
Chris Wrene6c48932014-09-29 17:19:27 -04003465 * </P>
3466 *
3467 * <P>
3468 * The person should be specified by the {@code String} representation of a
3469 * {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI}.
3470 * </P>
3471 *
3472 * <P>The system will also attempt to resolve {@code mailto:} and {@code tel:} schema
3473 * URIs. The path part of these URIs must exist in the contacts database, in the
3474 * appropriate column, or the reference will be discarded as invalid. Telephone schema
3475 * URIs will be resolved by {@link android.provider.ContactsContract.PhoneLookup}.
3476 * </P>
3477 *
3478 * @param uri A URI for the person.
Chris Wrendde75302014-03-26 17:24:15 -04003479 * @see Notification#EXTRA_PEOPLE
3480 */
Chris Wrene6c48932014-09-29 17:19:27 -04003481 public Builder addPerson(String uri) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003482 mPersonList.add(uri);
Chris Wrendde75302014-03-26 17:24:15 -04003483 return this;
3484 }
3485
3486 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003487 * Set this notification to be part of a group of notifications sharing the same key.
3488 * Grouped notifications may display in a cluster or stack on devices which
3489 * support such rendering.
3490 *
3491 * <p>To make this notification the summary for its group, also call
3492 * {@link #setGroupSummary}. A sort order can be specified for group members by using
3493 * {@link #setSortKey}.
3494 * @param groupKey The group key of the group.
3495 * @return this object for method chaining
3496 */
3497 public Builder setGroup(String groupKey) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003498 mN.mGroupKey = groupKey;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003499 return this;
3500 }
3501
3502 /**
3503 * Set this notification to be the group summary for a group of notifications.
3504 * Grouped notifications may display in a cluster or stack on devices which
Julia Reynolds04499532016-09-13 14:04:53 -04003505 * support such rendering. If thereRequires a group key also be set using {@link #setGroup}.
3506 * The group summary may be suppressed if too few notifications are included in the group.
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003507 * @param isGroupSummary Whether this notification should be a group summary.
3508 * @return this object for method chaining
3509 */
3510 public Builder setGroupSummary(boolean isGroupSummary) {
3511 setFlag(FLAG_GROUP_SUMMARY, isGroupSummary);
3512 return this;
3513 }
3514
3515 /**
3516 * Set a sort key that orders this notification among other notifications from the
3517 * same package. This can be useful if an external sort was already applied and an app
3518 * would like to preserve this. Notifications will be sorted lexicographically using this
3519 * value, although providing different priorities in addition to providing sort key may
3520 * cause this value to be ignored.
3521 *
3522 * <p>This sort key can also be used to order members of a notification group. See
Griff Hazen9e1379f2014-05-20 12:50:51 -07003523 * {@link #setGroup}.
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003524 *
3525 * @see String#compareTo(String)
3526 */
3527 public Builder setSortKey(String sortKey) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003528 mN.mSortKey = sortKey;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003529 return this;
3530 }
3531
3532 /**
Griff Hazen720042b2014-02-24 15:46:56 -08003533 * Merge additional metadata into this notification.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003534 *
Griff Hazen720042b2014-02-24 15:46:56 -08003535 * <p>Values within the Bundle will replace existing extras values in this Builder.
3536 *
3537 * @see Notification#extras
3538 */
Griff Hazen959591e2014-05-15 22:26:18 -07003539 public Builder addExtras(Bundle extras) {
3540 if (extras != null) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003541 mUserExtras.putAll(extras);
Griff Hazen720042b2014-02-24 15:46:56 -08003542 }
3543 return this;
3544 }
3545
3546 /**
3547 * Set metadata for this notification.
3548 *
3549 * <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 -04003550 * current contents are copied into the Notification each time {@link #build()} is
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003551 * called.
3552 *
Griff Hazen720042b2014-02-24 15:46:56 -08003553 * <p>Replaces any existing extras values with those from the provided Bundle.
3554 * Use {@link #addExtras} to merge in metadata instead.
3555 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003556 * @see Notification#extras
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003557 */
Griff Hazen959591e2014-05-15 22:26:18 -07003558 public Builder setExtras(Bundle extras) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003559 if (extras != null) {
3560 mUserExtras = extras;
3561 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003562 return this;
3563 }
3564
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003565 /**
Griff Hazen720042b2014-02-24 15:46:56 -08003566 * Get the current metadata Bundle used by this notification Builder.
3567 *
3568 * <p>The returned Bundle is shared with this Builder.
3569 *
3570 * <p>The current contents of this Bundle are copied into the Notification each time
3571 * {@link #build()} is called.
3572 *
3573 * @see Notification#extras
3574 */
3575 public Bundle getExtras() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003576 return mUserExtras;
3577 }
3578
3579 private Bundle getAllExtras() {
3580 final Bundle saveExtras = (Bundle) mUserExtras.clone();
3581 saveExtras.putAll(mN.extras);
3582 return saveExtras;
Griff Hazen720042b2014-02-24 15:46:56 -08003583 }
3584
3585 /**
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003586 * Add an action to this notification. Actions are typically displayed by
3587 * the system as a button adjacent to the notification content.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04003588 * <p>
3589 * Every action must have an icon (32dp square and matching the
3590 * <a href="{@docRoot}design/style/iconography.html#action-bar">Holo
3591 * Dark action bar</a> visual style), a textual label, and a {@link PendingIntent}.
3592 * <p>
3593 * A notification in its expanded form can display up to 3 actions, from left to right in
3594 * the order they were added. Actions will not be displayed when the notification is
3595 * collapsed, however, so be sure that any essential functions may be accessed by the user
3596 * in some other way (for example, in the Activity pointed to by {@link #contentIntent}).
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003597 *
3598 * @param icon Resource ID of a drawable that represents the action.
3599 * @param title Text describing the action.
3600 * @param intent PendingIntent to be fired when the action is invoked.
Dan Sandler86647982015-05-13 23:41:13 -04003601 *
3602 * @deprecated Use {@link #addAction(Action)} instead.
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003603 */
Dan Sandler86647982015-05-13 23:41:13 -04003604 @Deprecated
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003605 public Builder addAction(int icon, CharSequence title, PendingIntent intent) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04003606 mActions.add(new Action(icon, safeCharSequence(title), intent));
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003607 return this;
3608 }
3609
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003610 /**
Griff Hazen959591e2014-05-15 22:26:18 -07003611 * Add an action to this notification. Actions are typically displayed by
3612 * the system as a button adjacent to the notification content.
3613 * <p>
3614 * Every action must have an icon (32dp square and matching the
3615 * <a href="{@docRoot}design/style/iconography.html#action-bar">Holo
3616 * Dark action bar</a> visual style), a textual label, and a {@link PendingIntent}.
3617 * <p>
3618 * A notification in its expanded form can display up to 3 actions, from left to right in
3619 * the order they were added. Actions will not be displayed when the notification is
3620 * collapsed, however, so be sure that any essential functions may be accessed by the user
3621 * in some other way (for example, in the Activity pointed to by {@link #contentIntent}).
3622 *
3623 * @param action The action to add.
3624 */
3625 public Builder addAction(Action action) {
liangweikang63b03b52017-03-16 19:22:15 +08003626 if (action != null) {
3627 mActions.add(action);
3628 }
Griff Hazen959591e2014-05-15 22:26:18 -07003629 return this;
3630 }
3631
3632 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003633 * Alter the complete list of actions attached to this notification.
3634 * @see #addAction(Action).
3635 *
3636 * @param actions
3637 * @return
3638 */
3639 public Builder setActions(Action... actions) {
3640 mActions.clear();
3641 for (int i = 0; i < actions.length; i++) {
liangweikang63b03b52017-03-16 19:22:15 +08003642 if (actions[i] != null) {
3643 mActions.add(actions[i]);
3644 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003645 }
3646 return this;
3647 }
3648
3649 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003650 * Add a rich notification style to be applied at build time.
3651 *
3652 * @param style Object responsible for modifying the notification style.
3653 */
3654 public Builder setStyle(Style style) {
3655 if (mStyle != style) {
3656 mStyle = style;
Daniel Sandlerc08dea22012-06-28 08:35:24 -07003657 if (mStyle != null) {
3658 mStyle.setBuilder(this);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003659 mN.extras.putString(EXTRA_TEMPLATE, style.getClass().getName());
3660 } else {
3661 mN.extras.remove(EXTRA_TEMPLATE);
Daniel Sandlerc08dea22012-06-28 08:35:24 -07003662 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003663 }
3664 return this;
3665 }
3666
Dan Sandler0bf2ed82013-12-21 23:33:41 -06003667 /**
3668 * Specify the value of {@link #visibility}.
Griff Hazenb720abe2014-05-20 13:15:30 -07003669 *
Dan Sandler0bf2ed82013-12-21 23:33:41 -06003670 * @return The same Builder.
3671 */
Jeff Sharkey30e06bb2017-04-24 11:18:03 -06003672 public Builder setVisibility(@Visibility int visibility) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003673 mN.visibility = visibility;
Dan Sandler0bf2ed82013-12-21 23:33:41 -06003674 return this;
3675 }
3676
3677 /**
3678 * Supply a replacement Notification whose contents should be shown in insecure contexts
3679 * (i.e. atop the secure lockscreen). See {@link #visibility} and {@link #VISIBILITY_PUBLIC}.
3680 * @param n A replacement notification, presumably with some or all info redacted.
3681 * @return The same Builder.
3682 */
3683 public Builder setPublicVersion(Notification n) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003684 if (n != null) {
3685 mN.publicVersion = new Notification();
3686 n.cloneInto(mN.publicVersion, /*heavy=*/ true);
3687 } else {
3688 mN.publicVersion = null;
3689 }
Dan Sandler0bf2ed82013-12-21 23:33:41 -06003690 return this;
3691 }
3692
Griff Hazenb720abe2014-05-20 13:15:30 -07003693 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003694 * Apply an extender to this notification builder. Extenders may be used to add
3695 * metadata or change options on this builder.
3696 */
Griff Hazen61a9e862014-05-22 16:05:19 -07003697 public Builder extend(Extender extender) {
3698 extender.extend(this);
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003699 return this;
3700 }
3701
Dan Sandler4e787062015-06-17 15:09:48 -04003702 /**
3703 * @hide
3704 */
Julia Reynoldse46bb372016-03-17 11:05:58 -04003705 public Builder setFlag(int mask, boolean value) {
Joe Onorato46439ce2010-11-19 13:56:21 -08003706 if (value) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003707 mN.flags |= mask;
Joe Onorato46439ce2010-11-19 13:56:21 -08003708 } else {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003709 mN.flags &= ~mask;
Joe Onorato46439ce2010-11-19 13:56:21 -08003710 }
Julia Reynoldse46bb372016-03-17 11:05:58 -04003711 return this;
Joe Onorato46439ce2010-11-19 13:56:21 -08003712 }
3713
Dan Sandler26e81cf2014-05-06 10:01:27 -04003714 /**
3715 * Sets {@link Notification#color}.
3716 *
3717 * @param argb The accent color to use
3718 *
3719 * @return The same Builder.
3720 */
Tor Norbye80756e32015-03-02 09:39:27 -08003721 public Builder setColor(@ColorInt int argb) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003722 mN.color = argb;
Julia Reynolds10ee1fc2015-11-09 11:04:55 -05003723 sanitizeColor();
Dan Sandler26e81cf2014-05-06 10:01:27 -04003724 return this;
3725 }
3726
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003727 private Drawable getProfileBadgeDrawable() {
Chris Wren66619a22016-05-12 16:42:37 -04003728 if (mContext.getUserId() == UserHandle.USER_SYSTEM) {
3729 // This user can never be a badged profile,
3730 // and also includes USER_ALL system notifications.
3731 return null;
3732 }
Christoph Studer7ac80e62014-08-04 16:01:57 +02003733 // Note: This assumes that the current user can read the profile badge of the
3734 // originating user.
Selim Cineke6ff9462016-01-15 15:07:06 -08003735 return mContext.getPackageManager().getUserBadgeForDensityNoBackground(
Julia Reynoldsda303542015-11-23 14:00:20 -05003736 new UserHandle(mContext.getUserId()), 0);
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003737 }
3738
3739 private Bitmap getProfileBadge() {
3740 Drawable badge = getProfileBadgeDrawable();
Kenny Guy8a0101b2014-05-08 23:34:12 +01003741 if (badge == null) {
3742 return null;
3743 }
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003744 final int size = mContext.getResources().getDimensionPixelSize(
3745 R.dimen.notification_badge_size);
3746 Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
Kenny Guy8a0101b2014-05-08 23:34:12 +01003747 Canvas canvas = new Canvas(bitmap);
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003748 badge.setBounds(0, 0, size, size);
Kenny Guy8a0101b2014-05-08 23:34:12 +01003749 badge.draw(canvas);
3750 return bitmap;
3751 }
3752
Selim Cinekc848c3a2016-01-13 15:27:30 -08003753 private void bindProfileBadge(RemoteViews contentView) {
Kenny Guy98193ea2014-07-24 19:54:37 +01003754 Bitmap profileBadge = getProfileBadge();
3755
Kenny Guy98193ea2014-07-24 19:54:37 +01003756 if (profileBadge != null) {
Selim Cinekc848c3a2016-01-13 15:27:30 -08003757 contentView.setImageViewBitmap(R.id.profile_badge, profileBadge);
3758 contentView.setViewVisibility(R.id.profile_badge, View.VISIBLE);
Selim Cinek7b9605b2017-01-19 17:36:00 -08003759 if (isColorized()) {
3760 contentView.setDrawableParameters(R.id.profile_badge, false, -1,
3761 getPrimaryTextColor(), PorterDuff.Mode.SRC_ATOP, -1);
3762 }
Kenny Guy98193ea2014-07-24 19:54:37 +01003763 }
Kenny Guy98193ea2014-07-24 19:54:37 +01003764 }
3765
Christoph Studerfe718432014-09-01 18:21:18 +02003766 private void resetStandardTemplate(RemoteViews contentView) {
Selim Cinekeaa29ca2015-11-23 13:51:13 -08003767 resetNotificationHeader(contentView);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003768 resetContentMargins(contentView);
Christoph Studerfe718432014-09-01 18:21:18 +02003769 contentView.setViewVisibility(R.id.right_icon, View.GONE);
Selim Cinek860b6da2015-12-16 19:02:19 -08003770 contentView.setViewVisibility(R.id.title, View.GONE);
Christoph Studerfe718432014-09-01 18:21:18 +02003771 contentView.setTextViewText(R.id.title, null);
Selim Cinek41598732016-01-11 16:58:37 -08003772 contentView.setViewVisibility(R.id.text, View.GONE);
Christoph Studerfe718432014-09-01 18:21:18 +02003773 contentView.setTextViewText(R.id.text, null);
Selim Cinek29603462015-11-17 19:04:39 -08003774 contentView.setViewVisibility(R.id.text_line_1, View.GONE);
Selim Cinek41598732016-01-11 16:58:37 -08003775 contentView.setTextViewText(R.id.text_line_1, null);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003776 }
3777
Selim Cinekeaa29ca2015-11-23 13:51:13 -08003778 /**
3779 * Resets the notification header to its original state
3780 */
3781 private void resetNotificationHeader(RemoteViews contentView) {
Adrian Roosc4337a32016-08-02 18:30:34 -07003782 // Small icon doesn't need to be reset, as it's always set. Resetting would prevent
3783 // re-using the drawable when the notification is updated.
Selim Cinek7b836392015-12-04 20:02:59 -08003784 contentView.setBoolean(R.id.notification_header, "setExpanded", false);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003785 contentView.setTextViewText(R.id.app_name_text, null);
Christoph Studerca1db712014-09-10 17:31:33 +02003786 contentView.setViewVisibility(R.id.chronometer, View.GONE);
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07003787 contentView.setViewVisibility(R.id.header_text, View.GONE);
Adrian Roos9dfb78f2016-06-30 15:43:44 -07003788 contentView.setTextViewText(R.id.header_text, null);
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07003789 contentView.setViewVisibility(R.id.header_text_divider, View.GONE);
Selim Cinek29603462015-11-17 19:04:39 -08003790 contentView.setViewVisibility(R.id.time_divider, View.GONE);
Selim Cinekb85f36fd2016-04-20 18:46:36 -07003791 contentView.setViewVisibility(R.id.time, View.GONE);
Selim Cinekc848c3a2016-01-13 15:27:30 -08003792 contentView.setImageViewIcon(R.id.profile_badge, null);
3793 contentView.setViewVisibility(R.id.profile_badge, View.GONE);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003794 }
3795
3796 private void resetContentMargins(RemoteViews contentView) {
Adrian Roos2d5dbba2016-06-08 17:11:53 -07003797 contentView.setViewLayoutMarginEndDimen(R.id.line1, 0);
3798 contentView.setViewLayoutMarginEndDimen(R.id.text, 0);
Christoph Studerfe718432014-09-01 18:21:18 +02003799 }
3800
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003801 private RemoteViews applyStandardTemplate(int resId) {
Adrian Roos70d7aa32017-01-11 15:39:06 -08003802 return applyStandardTemplate(resId, mParams.reset().fillTextsFrom(this));
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02003803 }
3804
3805 /**
3806 * @param hasProgress whether the progress bar should be shown and set
3807 */
3808 private RemoteViews applyStandardTemplate(int resId, boolean hasProgress) {
Adrian Roos70d7aa32017-01-11 15:39:06 -08003809 return applyStandardTemplate(resId, mParams.reset().hasProgress(hasProgress)
3810 .fillTextsFrom(this));
Adrian Roosc1a80b02016-04-05 14:54:55 -07003811 }
3812
Adrian Roos70d7aa32017-01-11 15:39:06 -08003813 private RemoteViews applyStandardTemplate(int resId, StandardTemplateParams p) {
Kenny Guy77320062014-08-27 21:37:15 +01003814 RemoteViews contentView = new BuilderRemoteViews(mContext.getApplicationInfo(), resId);
Dan Sandler539aad42014-08-04 00:43:39 -04003815
Christoph Studerfe718432014-09-01 18:21:18 +02003816 resetStandardTemplate(contentView);
3817
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003818 final Bundle ex = mN.extras;
Selim Cinek7b9605b2017-01-19 17:36:00 -08003819 updateBackgroundColor(contentView);
Adrian Roos487374f2017-01-11 15:48:14 -08003820 bindNotificationHeader(contentView, p.ambient);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003821 bindLargeIcon(contentView);
Adrian Roos70d7aa32017-01-11 15:39:06 -08003822 boolean showProgress = handleProgressBar(p.hasProgress, contentView, ex);
3823 if (p.title != null) {
Selim Cinek860b6da2015-12-16 19:02:19 -08003824 contentView.setViewVisibility(R.id.title, View.VISIBLE);
Adrian Roos70d7aa32017-01-11 15:39:06 -08003825 contentView.setTextViewText(R.id.title, p.title);
Adrian Roos72171622017-01-27 10:32:06 -08003826 if (!p.ambient) {
3827 setTextViewColorPrimary(contentView, R.id.title);
3828 }
Selim Cinek954cc232016-05-20 13:29:23 -07003829 contentView.setViewLayoutWidth(R.id.title, showProgress
3830 ? ViewGroup.LayoutParams.WRAP_CONTENT
3831 : ViewGroup.LayoutParams.MATCH_PARENT);
Joe Onorato561d3852010-11-20 18:09:34 -08003832 }
Adrian Roos70d7aa32017-01-11 15:39:06 -08003833 if (p.text != null) {
Selim Cinek41598732016-01-11 16:58:37 -08003834 int textId = showProgress ? com.android.internal.R.id.text_line_1
3835 : com.android.internal.R.id.text;
Adrian Roos70d7aa32017-01-11 15:39:06 -08003836 contentView.setTextViewText(textId, p.text);
Adrian Roos72171622017-01-27 10:32:06 -08003837 if (!p.ambient) {
3838 setTextViewColorSecondary(contentView, textId);
3839 }
Selim Cinek41598732016-01-11 16:58:37 -08003840 contentView.setViewVisibility(textId, View.VISIBLE);
Joe Onorato561d3852010-11-20 18:09:34 -08003841 }
Selim Cinekc848c3a2016-01-13 15:27:30 -08003842
Selim Cinek279fa862016-06-14 10:57:25 -07003843 setContentMinHeight(contentView, showProgress || mN.hasLargeIcon());
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003844
Selim Cinek29603462015-11-17 19:04:39 -08003845 return contentView;
3846 }
3847
Selim Cinek7b9605b2017-01-19 17:36:00 -08003848 private void setTextViewColorPrimary(RemoteViews contentView, int id) {
3849 ensureColors();
3850 contentView.setTextColor(id, mPrimaryTextColor);
3851 }
3852
Selim Cinek389edcd2017-05-11 19:16:44 -07003853 /**
3854 * @return the primary text color
3855 * @hide
3856 */
3857 @VisibleForTesting
3858 public int getPrimaryTextColor() {
Selim Cinek7b9605b2017-01-19 17:36:00 -08003859 ensureColors();
3860 return mPrimaryTextColor;
3861 }
3862
Selim Cinek389edcd2017-05-11 19:16:44 -07003863 /**
3864 * @return the secondary text color
3865 * @hide
3866 */
3867 @VisibleForTesting
3868 public int getSecondaryTextColor() {
3869 ensureColors();
3870 return mSecondaryTextColor;
3871 }
3872
Selim Cinek7b9605b2017-01-19 17:36:00 -08003873 private int getActionBarColor() {
3874 ensureColors();
3875 return mActionBarColor;
3876 }
3877
Selim Cinek622c64a2017-04-17 17:10:05 -07003878 private int getActionBarColorDeEmphasized() {
3879 int backgroundColor = getBackgroundColor();
3880 return NotificationColorUtil.getShiftedColor(backgroundColor, 12);
3881 }
3882
Selim Cinek7b9605b2017-01-19 17:36:00 -08003883 private void setTextViewColorSecondary(RemoteViews contentView, int id) {
3884 ensureColors();
3885 contentView.setTextColor(id, mSecondaryTextColor);
3886 }
3887
3888 private void ensureColors() {
3889 int backgroundColor = getBackgroundColor();
3890 if (mPrimaryTextColor == COLOR_INVALID
3891 || mSecondaryTextColor == COLOR_INVALID
3892 || mActionBarColor == COLOR_INVALID
3893 || mTextColorsAreForBackground != backgroundColor) {
3894 mTextColorsAreForBackground = backgroundColor;
Selim Cinek5fb73f82017-04-20 16:55:38 -07003895 if (mForegroundColor == COLOR_INVALID || !isColorized()) {
3896 mPrimaryTextColor = NotificationColorUtil.resolvePrimaryColor(mContext,
3897 backgroundColor);
3898 mSecondaryTextColor = NotificationColorUtil.resolveSecondaryColor(mContext,
3899 backgroundColor);
Selim Cinekac5f0272017-05-02 16:05:41 -07003900 if (backgroundColor != COLOR_DEFAULT
3901 && (mBackgroundColorHint != COLOR_INVALID || isColorized())) {
3902 mPrimaryTextColor = NotificationColorUtil.findAlphaToMeetContrast(
3903 mPrimaryTextColor, backgroundColor, 4.5);
3904 mSecondaryTextColor = NotificationColorUtil.findAlphaToMeetContrast(
3905 mSecondaryTextColor, backgroundColor, 4.5);
3906 }
Selim Cinek5fb73f82017-04-20 16:55:38 -07003907 } else {
3908 double backLum = NotificationColorUtil.calculateLuminance(backgroundColor);
3909 double textLum = NotificationColorUtil.calculateLuminance(mForegroundColor);
3910 double contrast = NotificationColorUtil.calculateContrast(mForegroundColor,
3911 backgroundColor);
Selim Cinek389edcd2017-05-11 19:16:44 -07003912 // We only respect the given colors if worst case Black or White still has
3913 // contrast
3914 boolean backgroundLight = backLum > textLum
3915 && satisfiesTextContrast(backgroundColor, Color.BLACK)
3916 || backLum <= textLum
3917 && !satisfiesTextContrast(backgroundColor, Color.WHITE);
Selim Cinek5fb73f82017-04-20 16:55:38 -07003918 if (contrast < 4.5f) {
Selim Cinek389edcd2017-05-11 19:16:44 -07003919 if (backgroundLight) {
Selim Cinek5fb73f82017-04-20 16:55:38 -07003920 mSecondaryTextColor = NotificationColorUtil.findContrastColor(
3921 mForegroundColor,
3922 backgroundColor,
3923 true /* findFG */,
3924 4.5f);
3925 mPrimaryTextColor = NotificationColorUtil.changeColorLightness(
Selim Cinek389edcd2017-05-11 19:16:44 -07003926 mSecondaryTextColor, -LIGHTNESS_TEXT_DIFFERENCE_LIGHT);
Selim Cinek5fb73f82017-04-20 16:55:38 -07003927 } else {
3928 mSecondaryTextColor =
3929 NotificationColorUtil.findContrastColorAgainstDark(
3930 mForegroundColor,
3931 backgroundColor,
3932 true /* findFG */,
3933 4.5f);
3934 mPrimaryTextColor = NotificationColorUtil.changeColorLightness(
Selim Cinek389edcd2017-05-11 19:16:44 -07003935 mSecondaryTextColor, -LIGHTNESS_TEXT_DIFFERENCE_DARK);
Selim Cinek5fb73f82017-04-20 16:55:38 -07003936 }
3937 } else {
3938 mPrimaryTextColor = mForegroundColor;
3939 mSecondaryTextColor = NotificationColorUtil.changeColorLightness(
Selim Cinek389edcd2017-05-11 19:16:44 -07003940 mPrimaryTextColor, backgroundLight ? LIGHTNESS_TEXT_DIFFERENCE_LIGHT
3941 : LIGHTNESS_TEXT_DIFFERENCE_DARK);
Selim Cinek5fb73f82017-04-20 16:55:38 -07003942 if (NotificationColorUtil.calculateContrast(mSecondaryTextColor,
3943 backgroundColor) < 4.5f) {
3944 // oh well the secondary is not good enough
Selim Cinek389edcd2017-05-11 19:16:44 -07003945 if (backgroundLight) {
Selim Cinek5fb73f82017-04-20 16:55:38 -07003946 mSecondaryTextColor = NotificationColorUtil.findContrastColor(
3947 mSecondaryTextColor,
3948 backgroundColor,
3949 true /* findFG */,
3950 4.5f);
3951 } else {
3952 mSecondaryTextColor
3953 = NotificationColorUtil.findContrastColorAgainstDark(
3954 mSecondaryTextColor,
3955 backgroundColor,
3956 true /* findFG */,
3957 4.5f);
3958 }
3959 mPrimaryTextColor = NotificationColorUtil.changeColorLightness(
Selim Cinek389edcd2017-05-11 19:16:44 -07003960 mSecondaryTextColor, backgroundLight
3961 ? -LIGHTNESS_TEXT_DIFFERENCE_LIGHT
3962 : -LIGHTNESS_TEXT_DIFFERENCE_DARK);
Selim Cinek5fb73f82017-04-20 16:55:38 -07003963 }
3964 }
3965 }
Selim Cinek875ba9b2017-02-13 16:20:17 -08003966 mActionBarColor = NotificationColorUtil.resolveActionBarColor(mContext,
3967 backgroundColor);
Selim Cinek7b9605b2017-01-19 17:36:00 -08003968 }
3969 }
3970
3971 private void updateBackgroundColor(RemoteViews contentView) {
3972 if (isColorized()) {
3973 contentView.setInt(R.id.status_bar_latest_event_content, "setBackgroundColor",
3974 getBackgroundColor());
3975 } else {
3976 // Clear it!
3977 contentView.setInt(R.id.status_bar_latest_event_content, "setBackgroundResource",
3978 0);
3979 }
3980 }
3981
Selim Cinek860b6da2015-12-16 19:02:19 -08003982 /**
3983 * @param remoteView the remote view to update the minheight in
3984 * @param hasMinHeight does it have a mimHeight
3985 * @hide
3986 */
3987 void setContentMinHeight(RemoteViews remoteView, boolean hasMinHeight) {
3988 int minHeight = 0;
3989 if (hasMinHeight) {
3990 // we need to set the minHeight of the notification
3991 minHeight = mContext.getResources().getDimensionPixelSize(
3992 com.android.internal.R.dimen.notification_min_content_height);
3993 }
3994 remoteView.setInt(R.id.notification_main_column, "setMinimumHeight", minHeight);
3995 }
3996
Selim Cinek29603462015-11-17 19:04:39 -08003997 private boolean handleProgressBar(boolean hasProgress, RemoteViews contentView, Bundle ex) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003998 final int max = ex.getInt(EXTRA_PROGRESS_MAX, 0);
3999 final int progress = ex.getInt(EXTRA_PROGRESS, 0);
4000 final boolean ind = ex.getBoolean(EXTRA_PROGRESS_INDETERMINATE);
4001 if (hasProgress && (max != 0 || ind)) {
Selim Cinek29603462015-11-17 19:04:39 -08004002 contentView.setViewVisibility(com.android.internal.R.id.progress, View.VISIBLE);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004003 contentView.setProgressBar(
Selim Cinek29603462015-11-17 19:04:39 -08004004 R.id.progress, max, progress, ind);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004005 contentView.setProgressBackgroundTintList(
4006 R.id.progress, ColorStateList.valueOf(mContext.getColor(
4007 R.color.notification_progress_background_color)));
Selim Cinek29603462015-11-17 19:04:39 -08004008 if (mN.color != COLOR_DEFAULT) {
Adrian Roos4ff3b122016-02-01 12:26:13 -08004009 ColorStateList colorStateList = ColorStateList.valueOf(resolveContrastColor());
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004010 contentView.setProgressTintList(R.id.progress, colorStateList);
4011 contentView.setProgressIndeterminateTintList(R.id.progress, colorStateList);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004012 }
Selim Cinek29603462015-11-17 19:04:39 -08004013 return true;
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004014 } else {
4015 contentView.setViewVisibility(R.id.progress, View.GONE);
Selim Cinek29603462015-11-17 19:04:39 -08004016 return false;
Jeff Sharkey1c400132011-08-05 14:50:13 -07004017 }
Joe Onorato561d3852010-11-20 18:09:34 -08004018 }
4019
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004020 private void bindLargeIcon(RemoteViews contentView) {
Selim Cinek279fa862016-06-14 10:57:25 -07004021 if (mN.mLargeIcon == null && mN.largeIcon != null) {
4022 mN.mLargeIcon = Icon.createWithBitmap(mN.largeIcon);
4023 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004024 if (mN.mLargeIcon != null) {
4025 contentView.setViewVisibility(R.id.right_icon, View.VISIBLE);
4026 contentView.setImageViewIcon(R.id.right_icon, mN.mLargeIcon);
4027 processLargeLegacyIcon(mN.mLargeIcon, contentView);
Adrian Roos2d5dbba2016-06-08 17:11:53 -07004028 int endMargin = R.dimen.notification_content_picture_margin;
4029 contentView.setViewLayoutMarginEndDimen(R.id.line1, endMargin);
4030 contentView.setViewLayoutMarginEndDimen(R.id.text, endMargin);
4031 contentView.setViewLayoutMarginEndDimen(R.id.progress, endMargin);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004032 }
4033 }
4034
Adrian Roos487374f2017-01-11 15:48:14 -08004035 private void bindNotificationHeader(RemoteViews contentView, boolean ambient) {
4036 bindSmallIcon(contentView, ambient);
4037 bindHeaderAppName(contentView, ambient);
4038 if (!ambient) {
4039 // Ambient view does not have these
4040 bindHeaderText(contentView);
4041 bindHeaderChronometerAndTime(contentView);
Adrian Roos487374f2017-01-11 15:48:14 -08004042 bindProfileBadge(contentView);
4043 }
Adrian Roosd83e9992017-03-16 15:17:57 -07004044 bindExpandButton(contentView);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004045 }
4046
4047 private void bindExpandButton(RemoteViews contentView) {
Selim Cinek99104832017-01-25 14:47:33 -08004048 int color = getPrimaryHighlightColor();
Selim Cinek7b9605b2017-01-19 17:36:00 -08004049 contentView.setDrawableParameters(R.id.expand_button, false, -1, color,
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004050 PorterDuff.Mode.SRC_ATOP, -1);
Selim Cinekea4bef72015-12-02 15:51:10 -08004051 contentView.setInt(R.id.notification_header, "setOriginalNotificationColor",
Selim Cinek7b9605b2017-01-19 17:36:00 -08004052 color);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004053 }
4054
Selim Cinek99104832017-01-25 14:47:33 -08004055 /**
4056 * @return the color that is used as the first primary highlight color. This is applied
4057 * in several places like the action buttons or the app name in the header.
4058 */
4059 private int getPrimaryHighlightColor() {
4060 return isColorized() ? getPrimaryTextColor() : resolveContrastColor();
4061 }
4062
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004063 private void bindHeaderChronometerAndTime(RemoteViews contentView) {
4064 if (showsTimeOrChronometer()) {
Selim Cinek29603462015-11-17 19:04:39 -08004065 contentView.setViewVisibility(R.id.time_divider, View.VISIBLE);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004066 setTextViewColorSecondary(contentView, R.id.time_divider);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004067 if (mN.extras.getBoolean(EXTRA_SHOW_CHRONOMETER)) {
4068 contentView.setViewVisibility(R.id.chronometer, View.VISIBLE);
4069 contentView.setLong(R.id.chronometer, "setBase",
4070 mN.when + (SystemClock.elapsedRealtime() - System.currentTimeMillis()));
4071 contentView.setBoolean(R.id.chronometer, "setStarted", true);
Adrian Roos96b7e202016-05-17 13:50:38 -07004072 boolean countsDown = mN.extras.getBoolean(EXTRA_CHRONOMETER_COUNT_DOWN);
Selim Cinekc3b752e2016-04-20 16:13:59 -07004073 contentView.setChronometerCountDown(R.id.chronometer, countsDown);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004074 setTextViewColorSecondary(contentView, R.id.chronometer);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004075 } else {
4076 contentView.setViewVisibility(R.id.time, View.VISIBLE);
4077 contentView.setLong(R.id.time, "setTime", mN.when);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004078 setTextViewColorSecondary(contentView, R.id.time);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004079 }
Selim Cinekb85f36fd2016-04-20 18:46:36 -07004080 } else {
4081 // We still want a time to be set but gone, such that we can show and hide it
4082 // on demand in case it's a child notification without anything in the header
4083 contentView.setLong(R.id.time, "setTime", mN.when != 0 ? mN.when : mN.creationTime);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004084 }
4085 }
4086
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07004087 private void bindHeaderText(RemoteViews contentView) {
4088 CharSequence headerText = mN.extras.getCharSequence(EXTRA_SUB_TEXT);
4089 if (headerText == null && mStyle != null && mStyle.mSummaryTextSet
Selim Cinek03d0d652015-11-13 13:18:09 -05004090 && mStyle.hasSummaryInHeader()) {
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07004091 headerText = mStyle.mSummaryText;
Selim Cinek03d0d652015-11-13 13:18:09 -05004092 }
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07004093 if (headerText == null
4094 && mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N
4095 && mN.extras.getCharSequence(EXTRA_INFO_TEXT) != null) {
4096 headerText = mN.extras.getCharSequence(EXTRA_INFO_TEXT);
4097 }
4098 if (headerText != null) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004099 // TODO: Remove the span entirely to only have the string with propper formating.
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07004100 contentView.setTextViewText(R.id.header_text, processLegacyText(headerText));
Selim Cinek7b9605b2017-01-19 17:36:00 -08004101 setTextViewColorSecondary(contentView, R.id.header_text);
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07004102 contentView.setViewVisibility(R.id.header_text, View.VISIBLE);
4103 contentView.setViewVisibility(R.id.header_text_divider, View.VISIBLE);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004104 setTextViewColorSecondary(contentView, R.id.header_text_divider);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004105 }
4106 }
4107
Adrian Rooseba05822016-04-22 17:09:27 -07004108 /**
4109 * @hide
4110 */
4111 public String loadHeaderAppName() {
Dan Sandler732bd6c2016-04-12 14:20:32 -04004112 CharSequence name = null;
4113 final PackageManager pm = mContext.getPackageManager();
4114 if (mN.extras.containsKey(EXTRA_SUBSTITUTE_APP_NAME)) {
4115 // only system packages which lump together a bunch of unrelated stuff
4116 // may substitute a different name to make the purpose of the
4117 // notification more clear. the correct package label should always
4118 // be accessible via SystemUI.
4119 final String pkg = mContext.getPackageName();
4120 final String subName = mN.extras.getString(EXTRA_SUBSTITUTE_APP_NAME);
4121 if (PackageManager.PERMISSION_GRANTED == pm.checkPermission(
4122 android.Manifest.permission.SUBSTITUTE_NOTIFICATION_APP_NAME, pkg)) {
4123 name = subName;
4124 } else {
4125 Log.w(TAG, "warning: pkg "
4126 + pkg + " attempting to substitute app name '" + subName
4127 + "' without holding perm "
4128 + android.Manifest.permission.SUBSTITUTE_NOTIFICATION_APP_NAME);
4129 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004130 }
Dan Sandler732bd6c2016-04-12 14:20:32 -04004131 if (TextUtils.isEmpty(name)) {
4132 name = pm.getApplicationLabel(mContext.getApplicationInfo());
4133 }
4134 if (TextUtils.isEmpty(name)) {
4135 // still nothing?
4136 return null;
4137 }
4138
4139 return String.valueOf(name);
4140 }
Adrian Roos487374f2017-01-11 15:48:14 -08004141 private void bindHeaderAppName(RemoteViews contentView, boolean ambient) {
Dan Sandler732bd6c2016-04-12 14:20:32 -04004142 contentView.setTextViewText(R.id.app_name_text, loadHeaderAppName());
Adrian Roos72171622017-01-27 10:32:06 -08004143 if (isColorized() && !ambient) {
Selim Cinek7b9605b2017-01-19 17:36:00 -08004144 setTextViewColorPrimary(contentView, R.id.app_name_text);
4145 } else {
4146 contentView.setTextColor(R.id.app_name_text,
4147 ambient ? resolveAmbientColor() : resolveContrastColor());
4148 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004149 }
4150
Adrian Roos487374f2017-01-11 15:48:14 -08004151 private void bindSmallIcon(RemoteViews contentView, boolean ambient) {
Selim Cinek279fa862016-06-14 10:57:25 -07004152 if (mN.mSmallIcon == null && mN.icon != 0) {
4153 mN.mSmallIcon = Icon.createWithResource(mContext, mN.icon);
4154 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004155 contentView.setImageViewIcon(R.id.icon, mN.mSmallIcon);
Adrian Roos9b45a152016-06-28 13:32:29 -07004156 contentView.setDrawableParameters(R.id.icon, false /* targetBackground */,
4157 -1 /* alpha */, -1 /* colorFilter */, null /* mode */, mN.iconLevel);
Adrian Roos487374f2017-01-11 15:48:14 -08004158 processSmallIconColor(mN.mSmallIcon, contentView, ambient);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004159 }
4160
Jorim Jaggi445d3c02014-08-19 22:33:42 +02004161 /**
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004162 * @return true if the built notification will show the time or the chronometer; false
4163 * otherwise
4164 */
4165 private boolean showsTimeOrChronometer() {
Selim Cinekc2c0b042016-05-18 17:13:46 -07004166 return mN.showsTime() || mN.showsChronometer();
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004167 }
4168
Christoph Studerfe718432014-09-01 18:21:18 +02004169 private void resetStandardTemplateWithActions(RemoteViews big) {
Adrian Roos4c1fcc82016-03-31 14:39:39 -07004170 // actions_container is only reset when there are no actions to avoid focus issues with
4171 // remote inputs.
Christoph Studerfe718432014-09-01 18:21:18 +02004172 big.setViewVisibility(R.id.actions, View.GONE);
Christoph Studerfe718432014-09-01 18:21:18 +02004173 big.removeAllViews(R.id.actions);
Adrian Roose458aa82015-12-08 16:17:19 -08004174
4175 big.setViewVisibility(R.id.notification_material_reply_container, View.GONE);
4176 big.setTextViewText(R.id.notification_material_reply_text_1, null);
4177
4178 big.setViewVisibility(R.id.notification_material_reply_text_2, View.GONE);
4179 big.setTextViewText(R.id.notification_material_reply_text_2, null);
4180 big.setViewVisibility(R.id.notification_material_reply_text_3, View.GONE);
4181 big.setTextViewText(R.id.notification_material_reply_text_3, null);
Adrian Roosf852a422016-06-03 13:33:43 -07004182
4183 big.setViewLayoutMarginBottomDimen(R.id.notification_action_list_margin_target, 0);
Christoph Studerfe718432014-09-01 18:21:18 +02004184 }
4185
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004186 private RemoteViews applyStandardTemplateWithActions(int layoutId) {
Adrian Roos70d7aa32017-01-11 15:39:06 -08004187 return applyStandardTemplateWithActions(layoutId, mParams.reset().fillTextsFrom(this));
Adrian Roos48d746a2016-04-12 14:57:28 -07004188 }
4189
Adrian Roos70d7aa32017-01-11 15:39:06 -08004190 private RemoteViews applyStandardTemplateWithActions(int layoutId,
4191 StandardTemplateParams p) {
4192 RemoteViews big = applyStandardTemplate(layoutId, p);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004193
Christoph Studerfe718432014-09-01 18:21:18 +02004194 resetStandardTemplateWithActions(big);
4195
Adrian Roose458aa82015-12-08 16:17:19 -08004196 boolean validRemoteInput = false;
4197
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004198 int N = mActions.size();
Adrian Roos487374f2017-01-11 15:48:14 -08004199 boolean emphazisedMode = mN.fullScreenIntent != null && !p.ambient;
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004200 big.setBoolean(R.id.actions, "setEmphasizedMode", emphazisedMode);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004201 if (N > 0) {
Adrian Roos7052de52016-03-03 15:53:34 -08004202 big.setViewVisibility(R.id.actions_container, View.VISIBLE);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004203 big.setViewVisibility(R.id.actions, View.VISIBLE);
Adrian Roos487374f2017-01-11 15:48:14 -08004204 if (p.ambient) {
4205 big.setInt(R.id.actions, "setBackgroundColor", Color.TRANSPARENT);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004206 } else if (isColorized()) {
4207 big.setInt(R.id.actions, "setBackgroundColor", getActionBarColor());
4208 } else {
4209 big.setInt(R.id.actions, "setBackgroundColor", mContext.getColor(
4210 R.color.notification_action_list));
Adrian Roos487374f2017-01-11 15:48:14 -08004211 }
Adrian Roosf852a422016-06-03 13:33:43 -07004212 big.setViewLayoutMarginBottomDimen(R.id.notification_action_list_margin_target,
4213 R.dimen.notification_action_list_height);
Daniel Sandler8680bf82012-05-15 16:52:52 -04004214 if (N>MAX_ACTION_BUTTONS) N=MAX_ACTION_BUTTONS;
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004215 for (int i=0; i<N; i++) {
Adrian Roose458aa82015-12-08 16:17:19 -08004216 Action action = mActions.get(i);
4217 validRemoteInput |= hasValidRemoteInput(action);
4218
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004219 final RemoteViews button = generateActionButton(action, emphazisedMode,
Adrian Roos487374f2017-01-11 15:48:14 -08004220 i % 2 != 0, p.ambient);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004221 big.addView(R.id.actions, button);
4222 }
Adrian Roos4c1fcc82016-03-31 14:39:39 -07004223 } else {
4224 big.setViewVisibility(R.id.actions_container, View.GONE);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004225 }
Adrian Roose458aa82015-12-08 16:17:19 -08004226
4227 CharSequence[] replyText = mN.extras.getCharSequenceArray(EXTRA_REMOTE_INPUT_HISTORY);
Adrian Roos487374f2017-01-11 15:48:14 -08004228 if (!p.ambient && validRemoteInput && replyText != null
Adrian Roose458aa82015-12-08 16:17:19 -08004229 && replyText.length > 0 && !TextUtils.isEmpty(replyText[0])) {
4230 big.setViewVisibility(R.id.notification_material_reply_container, View.VISIBLE);
4231 big.setTextViewText(R.id.notification_material_reply_text_1, replyText[0]);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004232 setTextViewColorSecondary(big, R.id.notification_material_reply_text_1);
Adrian Roose458aa82015-12-08 16:17:19 -08004233
4234 if (replyText.length > 1 && !TextUtils.isEmpty(replyText[1])) {
4235 big.setViewVisibility(R.id.notification_material_reply_text_2, View.VISIBLE);
4236 big.setTextViewText(R.id.notification_material_reply_text_2, replyText[1]);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004237 setTextViewColorSecondary(big, R.id.notification_material_reply_text_2);
Adrian Roose458aa82015-12-08 16:17:19 -08004238
4239 if (replyText.length > 2 && !TextUtils.isEmpty(replyText[2])) {
4240 big.setViewVisibility(
4241 R.id.notification_material_reply_text_3, View.VISIBLE);
4242 big.setTextViewText(R.id.notification_material_reply_text_3, replyText[2]);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004243 setTextViewColorSecondary(big, R.id.notification_material_reply_text_3);
Adrian Roose458aa82015-12-08 16:17:19 -08004244 }
4245 }
4246 }
4247
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004248 return big;
4249 }
4250
Adrian Roose458aa82015-12-08 16:17:19 -08004251 private boolean hasValidRemoteInput(Action action) {
4252 if (TextUtils.isEmpty(action.title) || action.actionIntent == null) {
4253 // Weird actions
4254 return false;
4255 }
4256
4257 RemoteInput[] remoteInputs = action.getRemoteInputs();
4258 if (remoteInputs == null) {
4259 return false;
4260 }
4261
4262 for (RemoteInput r : remoteInputs) {
4263 CharSequence[] choices = r.getChoices();
4264 if (r.getAllowFreeFormInput() || (choices != null && choices.length != 0)) {
4265 return true;
4266 }
4267 }
4268 return false;
4269 }
4270
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004271 /**
4272 * Construct a RemoteViews for the final 1U notification layout. In order:
4273 * 1. Custom contentView from the caller
4274 * 2. Style's proposed content view
4275 * 3. Standard template view
4276 */
Julia Reynolds3b848122016-02-26 10:45:32 -05004277 public RemoteViews createContentView() {
Selim Cinek7d1009b2017-01-25 15:28:28 -08004278 return createContentView(false /* increasedheight */ );
4279 }
4280
4281 /**
4282 * Construct a RemoteViews for the smaller content view.
4283 *
4284 * @param increasedHeight true if this layout be created with an increased height. Some
4285 * styles may support showing more then just that basic 1U size
4286 * and the system may decide to render important notifications
4287 * slightly bigger even when collapsed.
4288 *
4289 * @hide
4290 */
4291 public RemoteViews createContentView(boolean increasedHeight) {
Selim Cinek593610c2016-02-16 18:42:57 -08004292 if (mN.contentView != null && (mStyle == null || !mStyle.displayCustomViewInline())) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004293 return mN.contentView;
4294 } else if (mStyle != null) {
Selim Cinek7d1009b2017-01-25 15:28:28 -08004295 final RemoteViews styleView = mStyle.makeContentView(increasedHeight);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004296 if (styleView != null) {
4297 return styleView;
4298 }
Joe Onorato46439ce2010-11-19 13:56:21 -08004299 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004300 return applyStandardTemplate(getBaseLayoutResource());
Joe Onorato46439ce2010-11-19 13:56:21 -08004301 }
4302
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004303 /**
4304 * Construct a RemoteViews for the final big notification layout.
4305 */
Julia Reynolds3b848122016-02-26 10:45:32 -05004306 public RemoteViews createBigContentView() {
Selim Cinek850a8542015-11-11 11:48:36 -05004307 RemoteViews result = null;
Selim Cinek593610c2016-02-16 18:42:57 -08004308 if (mN.bigContentView != null
4309 && (mStyle == null || !mStyle.displayCustomViewInline())) {
Julia Reynolds089e3e42015-11-18 09:59:57 -05004310 return mN.bigContentView;
4311 } else if (mStyle != null) {
Selim Cinek850a8542015-11-11 11:48:36 -05004312 result = mStyle.makeBigContentView();
Selim Cinek90dcf6d2015-11-18 20:24:13 -08004313 hideLine1Text(result);
Selim Cinekcc10bfb2016-02-10 16:24:21 -08004314 } else if (mActions.size() != 0) {
4315 result = applyStandardTemplateWithActions(getBigBaseLayoutResource());
Selim Cinek850a8542015-11-11 11:48:36 -05004316 }
Selim Cinek6743c0b2017-01-18 18:24:01 -08004317 makeHeaderExpanded(result);
Selim Cinek850a8542015-11-11 11:48:36 -05004318 return result;
4319 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004320
Selim Cinekeaa29ca2015-11-23 13:51:13 -08004321 /**
Selim Cinek414ad332017-02-24 19:06:12 -08004322 * Construct a RemoteViews for the final notification header only. This will not be
4323 * colorized.
Selim Cinekeaa29ca2015-11-23 13:51:13 -08004324 *
Adrian Roos6f6e1592017-05-02 16:22:53 -07004325 * @param ambient if true, generate the header for the ambient display layout.
Selim Cinekeaa29ca2015-11-23 13:51:13 -08004326 * @hide
4327 */
Adrian Roos6f6e1592017-05-02 16:22:53 -07004328 public RemoteViews makeNotificationHeader(boolean ambient) {
Selim Cinek414ad332017-02-24 19:06:12 -08004329 Boolean colorized = (Boolean) mN.extras.get(EXTRA_COLORIZED);
4330 mN.extras.putBoolean(EXTRA_COLORIZED, false);
Selim Cinekeaa29ca2015-11-23 13:51:13 -08004331 RemoteViews header = new BuilderRemoteViews(mContext.getApplicationInfo(),
Adrian Roos6f6e1592017-05-02 16:22:53 -07004332 ambient ? R.layout.notification_template_ambient_header
4333 : R.layout.notification_template_header);
Selim Cinekeaa29ca2015-11-23 13:51:13 -08004334 resetNotificationHeader(header);
Adrian Roos6f6e1592017-05-02 16:22:53 -07004335 bindNotificationHeader(header, ambient);
Selim Cinek414ad332017-02-24 19:06:12 -08004336 if (colorized != null) {
4337 mN.extras.putBoolean(EXTRA_COLORIZED, colorized);
4338 } else {
4339 mN.extras.remove(EXTRA_COLORIZED);
4340 }
Selim Cinekeaa29ca2015-11-23 13:51:13 -08004341 return header;
4342 }
4343
Adrian Roos487374f2017-01-11 15:48:14 -08004344 /**
4345 * Construct a RemoteViews for the ambient version of the notification.
4346 *
4347 * @hide
4348 */
4349 public RemoteViews makeAmbientNotification() {
4350 RemoteViews ambient = applyStandardTemplateWithActions(
4351 R.layout.notification_template_material_ambient,
Adrian Roos0bc3f6a2017-03-06 11:54:05 -08004352 mParams.reset().ambient(true).fillTextsFrom(this).hasProgress(false));
Adrian Roos487374f2017-01-11 15:48:14 -08004353 return ambient;
4354 }
4355
Selim Cinek29603462015-11-17 19:04:39 -08004356 private void hideLine1Text(RemoteViews result) {
Selim Cinekcc10bfb2016-02-10 16:24:21 -08004357 if (result != null) {
4358 result.setViewVisibility(R.id.text_line_1, View.GONE);
4359 }
Selim Cinek29603462015-11-17 19:04:39 -08004360 }
4361
Selim Cinek6743c0b2017-01-18 18:24:01 -08004362 /**
4363 * Adapt the Notification header if this view is used as an expanded view.
4364 *
4365 * @hide
4366 */
4367 public static void makeHeaderExpanded(RemoteViews result) {
Selim Cinekcc10bfb2016-02-10 16:24:21 -08004368 if (result != null) {
4369 result.setBoolean(R.id.notification_header, "setExpanded", true);
4370 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004371 }
4372
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004373 /**
4374 * Construct a RemoteViews for the final heads-up notification layout.
Selim Cinek87ed69b2017-02-09 15:59:43 -08004375 *
4376 * @param increasedHeight true if this layout be created with an increased height. Some
4377 * styles may support showing more then just that basic 1U size
4378 * and the system may decide to render important notifications
4379 * slightly bigger even when collapsed.
4380 *
4381 * @hide
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004382 */
Selim Cinek87ed69b2017-02-09 15:59:43 -08004383 public RemoteViews createHeadsUpContentView(boolean increasedHeight) {
Selim Cinek593610c2016-02-16 18:42:57 -08004384 if (mN.headsUpContentView != null
4385 && (mStyle == null || !mStyle.displayCustomViewInline())) {
Julia Reynolds089e3e42015-11-18 09:59:57 -05004386 return mN.headsUpContentView;
4387 } else if (mStyle != null) {
Selim Cinek87ed69b2017-02-09 15:59:43 -08004388 final RemoteViews styleView = mStyle.makeHeadsUpContentView(increasedHeight);
4389 if (styleView != null) {
4390 return styleView;
4391 }
Julia Reynolds089e3e42015-11-18 09:59:57 -05004392 } else if (mActions.size() == 0) {
4393 return null;
4394 }
4395
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004396 return applyStandardTemplateWithActions(getBigBaseLayoutResource());
Chris Wren8fd39ec2014-02-27 17:43:26 -05004397 }
4398
Selim Cinek624c02db2015-12-14 21:00:02 -08004399 /**
Selim Cinek87ed69b2017-02-09 15:59:43 -08004400 * Construct a RemoteViews for the final heads-up notification layout.
4401 */
4402 public RemoteViews createHeadsUpContentView() {
4403 return createHeadsUpContentView(false /* useIncreasedHeight */);
4404 }
4405
4406 /**
Selim Cinek624c02db2015-12-14 21:00:02 -08004407 * Construct a RemoteViews for the display in public contexts like on the lockscreen.
4408 *
4409 * @hide
4410 */
4411 public RemoteViews makePublicContentView() {
Adrian Roos1a1ecfc2017-04-17 11:17:59 -07004412 return makePublicView(false /* ambient */);
4413 }
4414
4415 /**
4416 * Construct a RemoteViews for the display in public contexts like on the lockscreen.
4417 *
4418 * @hide
4419 */
4420 public RemoteViews makePublicAmbientNotification() {
4421 return makePublicView(true /* ambient */);
4422 }
4423
4424 private RemoteViews makePublicView(boolean ambient) {
Selim Cinek624c02db2015-12-14 21:00:02 -08004425 if (mN.publicVersion != null) {
4426 final Builder builder = recoverBuilder(mContext, mN.publicVersion);
Adrian Roos1a1ecfc2017-04-17 11:17:59 -07004427 return ambient ? builder.makeAmbientNotification() : builder.createContentView();
Selim Cinek624c02db2015-12-14 21:00:02 -08004428 }
4429 Bundle savedBundle = mN.extras;
4430 Style style = mStyle;
4431 mStyle = null;
4432 Icon largeIcon = mN.mLargeIcon;
4433 mN.mLargeIcon = null;
Selim Cinek279fa862016-06-14 10:57:25 -07004434 Bitmap largeIconLegacy = mN.largeIcon;
4435 mN.largeIcon = null;
Adrian Roosb19b06b2017-05-02 18:54:40 -07004436 ArrayList<Action> actions = mActions;
4437 mActions = new ArrayList<>();
Selim Cinek624c02db2015-12-14 21:00:02 -08004438 Bundle publicExtras = new Bundle();
4439 publicExtras.putBoolean(EXTRA_SHOW_WHEN,
4440 savedBundle.getBoolean(EXTRA_SHOW_WHEN));
4441 publicExtras.putBoolean(EXTRA_SHOW_CHRONOMETER,
4442 savedBundle.getBoolean(EXTRA_SHOW_CHRONOMETER));
Adrian Roos96b7e202016-05-17 13:50:38 -07004443 publicExtras.putBoolean(EXTRA_CHRONOMETER_COUNT_DOWN,
4444 savedBundle.getBoolean(EXTRA_CHRONOMETER_COUNT_DOWN));
Selim Cinek624c02db2015-12-14 21:00:02 -08004445 publicExtras.putCharSequence(EXTRA_TITLE,
Adrian Roos1a1ecfc2017-04-17 11:17:59 -07004446 mContext.getString(com.android.internal.R.string.notification_hidden_text));
Selim Cinek624c02db2015-12-14 21:00:02 -08004447 mN.extras = publicExtras;
Adrian Roos1a1ecfc2017-04-17 11:17:59 -07004448 final RemoteViews view = ambient ? makeAmbientNotification()
4449 : applyStandardTemplate(getBaseLayoutResource());
Selim Cinek624c02db2015-12-14 21:00:02 -08004450 mN.extras = savedBundle;
4451 mN.mLargeIcon = largeIcon;
Selim Cinek279fa862016-06-14 10:57:25 -07004452 mN.largeIcon = largeIconLegacy;
Adrian Roosb19b06b2017-05-02 18:54:40 -07004453 mActions = actions;
Selim Cinek624c02db2015-12-14 21:00:02 -08004454 mStyle = style;
Adrian Roos1a1ecfc2017-04-17 11:17:59 -07004455 return view;
Selim Cinek624c02db2015-12-14 21:00:02 -08004456 }
4457
Selim Cinek6743c0b2017-01-18 18:24:01 -08004458 /**
4459 * Construct a content view for the display when low - priority
4460 *
4461 * @param useRegularSubtext uses the normal subtext set if there is one available. Otherwise
4462 * a new subtext is created consisting of the content of the
4463 * notification.
4464 * @hide
4465 */
4466 public RemoteViews makeLowPriorityContentView(boolean useRegularSubtext) {
4467 int color = mN.color;
4468 mN.color = COLOR_DEFAULT;
4469 CharSequence summary = mN.extras.getCharSequence(EXTRA_SUB_TEXT);
4470 if (!useRegularSubtext || TextUtils.isEmpty(summary)) {
4471 CharSequence newSummary = createSummaryText();
4472 if (!TextUtils.isEmpty(newSummary)) {
4473 mN.extras.putCharSequence(EXTRA_SUB_TEXT, newSummary);
4474 }
4475 }
Selim Cinek875ba9b2017-02-13 16:20:17 -08004476
Adrian Roos6f6e1592017-05-02 16:22:53 -07004477 RemoteViews header = makeNotificationHeader(false /* ambient */);
Selim Cinek1b554392017-02-28 17:22:49 -08004478 header.setBoolean(R.id.notification_header, "setAcceptAllTouches", true);
Selim Cinek6743c0b2017-01-18 18:24:01 -08004479 if (summary != null) {
4480 mN.extras.putCharSequence(EXTRA_SUB_TEXT, summary);
4481 } else {
4482 mN.extras.remove(EXTRA_SUB_TEXT);
4483 }
4484 mN.color = color;
4485 return header;
4486 }
Selim Cinek624c02db2015-12-14 21:00:02 -08004487
Selim Cinek6743c0b2017-01-18 18:24:01 -08004488 private CharSequence createSummaryText() {
4489 CharSequence titleText = mN.extras.getCharSequence(Notification.EXTRA_TITLE);
4490 if (USE_ONLY_TITLE_IN_LOW_PRIORITY_SUMMARY) {
4491 return titleText;
4492 }
4493 SpannableStringBuilder summary = new SpannableStringBuilder();
4494 if (titleText == null) {
4495 titleText = mN.extras.getCharSequence(Notification.EXTRA_TITLE_BIG);
4496 }
4497 BidiFormatter bidi = BidiFormatter.getInstance();
4498 if (titleText != null) {
4499 summary.append(bidi.unicodeWrap(titleText));
4500 }
4501 CharSequence contentText = mN.extras.getCharSequence(Notification.EXTRA_TEXT);
4502 if (titleText != null && contentText != null) {
4503 summary.append(bidi.unicodeWrap(mContext.getText(
4504 R.string.notification_header_divider_symbol_with_spaces)));
4505 }
4506 if (contentText != null) {
4507 summary.append(bidi.unicodeWrap(contentText));
4508 }
4509 return summary;
4510 }
Chris Wren8fd39ec2014-02-27 17:43:26 -05004511
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004512 private RemoteViews generateActionButton(Action action, boolean emphazisedMode,
Adrian Roos487374f2017-01-11 15:48:14 -08004513 boolean oddAction, boolean ambient) {
Daniel Sandler8680bf82012-05-15 16:52:52 -04004514 final boolean tombstone = (action.actionIntent == null);
Selim Cinekf33b1112015-07-15 17:45:11 -07004515 RemoteViews button = new BuilderRemoteViews(mContext.getApplicationInfo(),
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004516 emphazisedMode ? getEmphasizedActionLayoutResource()
4517 : tombstone ? getActionTombstoneLayoutResource()
4518 : getActionLayoutResource());
Daniel Sandler8680bf82012-05-15 16:52:52 -04004519 if (!tombstone) {
Daniel Sandlere5518842012-05-10 16:20:40 -04004520 button.setOnClickPendingIntent(R.id.action0, action.actionIntent);
Daniel Sandlere5518842012-05-10 16:20:40 -04004521 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004522 button.setContentDescription(R.id.action0, action.title);
Adrian Roosfe84e1f2015-11-04 15:55:39 -08004523 if (action.mRemoteInputs != null) {
4524 button.setRemoteInputs(R.id.action0, action.mRemoteInputs);
4525 }
Selim Cinek7b9605b2017-01-19 17:36:00 -08004526 // TODO: handle emphasized mode / actions right
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004527 if (emphazisedMode) {
Selim Cinek981962e2016-07-20 20:41:58 -07004528 // change the background bgColor
Selim Cinek622c64a2017-04-17 17:10:05 -07004529 int bgColor;
4530 if (isColorized()) {
4531 bgColor = oddAction ? getActionBarColor() : getActionBarColorDeEmphasized();
4532 } else {
4533 bgColor = mContext.getColor(oddAction ? R.color.notification_action_list
4534 : R.color.notification_action_list_dark);
4535 }
Selim Cinek981962e2016-07-20 20:41:58 -07004536 button.setDrawableParameters(R.id.button_holder, true, -1, bgColor,
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004537 PorterDuff.Mode.SRC_ATOP, -1);
Selim Cinek981962e2016-07-20 20:41:58 -07004538 CharSequence title = action.title;
4539 ColorStateList[] outResultColor = null;
4540 if (isLegacy()) {
4541 title = clearColorSpans(title);
4542 } else {
4543 outResultColor = new ColorStateList[1];
4544 title = ensureColorSpanContrast(title, bgColor, outResultColor);
4545 }
4546 button.setTextViewText(R.id.action0, title);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004547 setTextViewColorPrimary(button, R.id.action0);
Selim Cinek981962e2016-07-20 20:41:58 -07004548 if (outResultColor != null && outResultColor[0] != null) {
4549 // We need to set the text color as well since changing a text to uppercase
4550 // clears its spans.
4551 button.setTextColor(R.id.action0, outResultColor[0]);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004552 } else if (mN.color != COLOR_DEFAULT && !isColorized()) {
Selim Cinek981962e2016-07-20 20:41:58 -07004553 button.setTextColor(R.id.action0,resolveContrastColor());
4554 }
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004555 } else {
Selim Cinek981962e2016-07-20 20:41:58 -07004556 button.setTextViewText(R.id.action0, processLegacyText(action.title));
Adrian Roos72171622017-01-27 10:32:06 -08004557 if (isColorized() && !ambient) {
Selim Cinek7b9605b2017-01-19 17:36:00 -08004558 setTextViewColorPrimary(button, R.id.action0);
4559 } else if (mN.color != COLOR_DEFAULT) {
Adrian Roos487374f2017-01-11 15:48:14 -08004560 button.setTextColor(R.id.action0,
4561 ambient ? resolveAmbientColor() : resolveContrastColor());
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004562 }
Adrian Roosfe84e1f2015-11-04 15:55:39 -08004563 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004564 return button;
4565 }
4566
Joe Onoratocb109a02011-01-18 17:57:41 -08004567 /**
Selim Cinek981962e2016-07-20 20:41:58 -07004568 * Clears all color spans of a text
4569 * @param charSequence the input text
4570 * @return the same text but without color spans
4571 */
4572 private CharSequence clearColorSpans(CharSequence charSequence) {
4573 if (charSequence instanceof Spanned) {
4574 Spanned ss = (Spanned) charSequence;
4575 Object[] spans = ss.getSpans(0, ss.length(), Object.class);
4576 SpannableStringBuilder builder = new SpannableStringBuilder(ss.toString());
4577 for (Object span : spans) {
4578 Object resultSpan = span;
4579 if (resultSpan instanceof CharacterStyle) {
4580 resultSpan = ((CharacterStyle) span).getUnderlying();
4581 }
4582 if (resultSpan instanceof TextAppearanceSpan) {
4583 TextAppearanceSpan originalSpan = (TextAppearanceSpan) resultSpan;
4584 if (originalSpan.getTextColor() != null) {
4585 resultSpan = new TextAppearanceSpan(
4586 originalSpan.getFamily(),
4587 originalSpan.getTextStyle(),
4588 originalSpan.getTextSize(),
4589 null,
4590 originalSpan.getLinkTextColor());
4591 }
4592 } else if (resultSpan instanceof ForegroundColorSpan
4593 || (resultSpan instanceof BackgroundColorSpan)) {
4594 continue;
4595 } else {
4596 resultSpan = span;
4597 }
4598 builder.setSpan(resultSpan, ss.getSpanStart(span), ss.getSpanEnd(span),
4599 ss.getSpanFlags(span));
4600 }
4601 return builder;
4602 }
4603 return charSequence;
4604 }
4605
4606 /**
4607 * Ensures contrast on color spans against a background color. also returns the color of the
4608 * text if a span was found that spans over the whole text.
4609 *
4610 * @param charSequence the charSequence on which the spans are
4611 * @param background the background color to ensure the contrast against
4612 * @param outResultColor an array in which a color will be returned as the first element if
4613 * there exists a full length color span.
4614 * @return the contrasted charSequence
4615 */
4616 private CharSequence ensureColorSpanContrast(CharSequence charSequence, int background,
4617 ColorStateList[] outResultColor) {
4618 if (charSequence instanceof Spanned) {
4619 Spanned ss = (Spanned) charSequence;
4620 Object[] spans = ss.getSpans(0, ss.length(), Object.class);
4621 SpannableStringBuilder builder = new SpannableStringBuilder(ss.toString());
4622 for (Object span : spans) {
4623 Object resultSpan = span;
4624 int spanStart = ss.getSpanStart(span);
4625 int spanEnd = ss.getSpanEnd(span);
4626 boolean fullLength = (spanEnd - spanStart) == charSequence.length();
4627 if (resultSpan instanceof CharacterStyle) {
4628 resultSpan = ((CharacterStyle) span).getUnderlying();
4629 }
4630 if (resultSpan instanceof TextAppearanceSpan) {
4631 TextAppearanceSpan originalSpan = (TextAppearanceSpan) resultSpan;
4632 ColorStateList textColor = originalSpan.getTextColor();
4633 if (textColor != null) {
4634 int[] colors = textColor.getColors();
4635 int[] newColors = new int[colors.length];
4636 for (int i = 0; i < newColors.length; i++) {
4637 newColors[i] = NotificationColorUtil.ensureLargeTextContrast(
4638 colors[i], background);
4639 }
4640 textColor = new ColorStateList(textColor.getStates().clone(),
4641 newColors);
4642 resultSpan = new TextAppearanceSpan(
4643 originalSpan.getFamily(),
4644 originalSpan.getTextStyle(),
4645 originalSpan.getTextSize(),
4646 textColor,
4647 originalSpan.getLinkTextColor());
4648 if (fullLength) {
4649 outResultColor[0] = new ColorStateList(
4650 textColor.getStates().clone(), newColors);
4651 }
4652 }
4653 } else if (resultSpan instanceof ForegroundColorSpan) {
4654 ForegroundColorSpan originalSpan = (ForegroundColorSpan) resultSpan;
4655 int foregroundColor = originalSpan.getForegroundColor();
4656 foregroundColor = NotificationColorUtil.ensureLargeTextContrast(
4657 foregroundColor, background);
4658 resultSpan = new ForegroundColorSpan(foregroundColor);
4659 if (fullLength) {
4660 outResultColor[0] = ColorStateList.valueOf(foregroundColor);
4661 }
4662 } else {
4663 resultSpan = span;
4664 }
4665
4666 builder.setSpan(resultSpan, spanStart, spanEnd, ss.getSpanFlags(span));
4667 }
4668 return builder;
4669 }
4670 return charSequence;
4671 }
4672
4673 /**
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004674 * @return Whether we are currently building a notification from a legacy (an app that
Alan Viverette3cb07a462014-06-06 14:19:53 -07004675 * doesn't create material notifications by itself) app.
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004676 */
4677 private boolean isLegacy() {
Selim Cinek7b9605b2017-01-19 17:36:00 -08004678 if (!mIsLegacyInitialized) {
4679 mIsLegacy = mContext.getApplicationInfo().targetSdkVersion
4680 < Build.VERSION_CODES.LOLLIPOP;
4681 mIsLegacyInitialized = true;
4682 }
4683 return mIsLegacy;
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004684 }
4685
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004686 private CharSequence processLegacyText(CharSequence charSequence) {
Adrian Roos0bc3f6a2017-03-06 11:54:05 -08004687 return processLegacyText(charSequence, false /* ambient */);
4688 }
4689
4690 private CharSequence processLegacyText(CharSequence charSequence, boolean ambient) {
4691 boolean isAlreadyLightText = isLegacy() || textColorsNeedInversion();
4692 boolean wantLightText = ambient;
4693 if (isAlreadyLightText != wantLightText) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004694 return getColorUtil().invertCharSequenceColors(charSequence);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004695 } else {
4696 return charSequence;
4697 }
4698 }
4699
Dan Sandler26e81cf2014-05-06 10:01:27 -04004700 /**
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004701 * Apply any necessariy colors to the small icon
Dan Sandler26e81cf2014-05-06 10:01:27 -04004702 */
Adrian Roos487374f2017-01-11 15:48:14 -08004703 private void processSmallIconColor(Icon smallIcon, RemoteViews contentView,
4704 boolean ambient) {
Selim Cinekea4bef72015-12-02 15:51:10 -08004705 boolean colorable = !isLegacy() || getColorUtil().isGrayscaleIcon(mContext, smallIcon);
Selim Cinek99104832017-01-25 14:47:33 -08004706 int color = ambient ? resolveAmbientColor() : getPrimaryHighlightColor();
Selim Cinekea4bef72015-12-02 15:51:10 -08004707 if (colorable) {
Adrian Roos487374f2017-01-11 15:48:14 -08004708 contentView.setDrawableParameters(R.id.icon, false, -1, color,
Jorim Jaggi92df1f22014-12-16 19:44:41 +01004709 PorterDuff.Mode.SRC_ATOP, -1);
Selim Cinekea4bef72015-12-02 15:51:10 -08004710
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004711 }
Selim Cinekea4bef72015-12-02 15:51:10 -08004712 contentView.setInt(R.id.notification_header, "setOriginalIconColor",
Adrian Roos487374f2017-01-11 15:48:14 -08004713 colorable ? color : NotificationHeaderView.NO_COLOR);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004714 }
4715
Dan Sandler26e81cf2014-05-06 10:01:27 -04004716 /**
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004717 * Make the largeIcon dark if it's a fake smallIcon (that is,
Dan Sandler26e81cf2014-05-06 10:01:27 -04004718 * if it's grayscale).
4719 */
4720 // TODO: also check bounds, transparency, that sort of thing.
Dan Sandlerd63f9322015-05-06 15:18:49 -04004721 private void processLargeLegacyIcon(Icon largeIcon, RemoteViews contentView) {
4722 if (largeIcon != null && isLegacy()
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004723 && getColorUtil().isGrayscaleIcon(mContext, largeIcon)) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004724 // resolve color will fall back to the default when legacy
Adrian Roos4ff3b122016-02-01 12:26:13 -08004725 contentView.setDrawableParameters(R.id.icon, false, -1, resolveContrastColor(),
Dan Sandler190d58d2014-05-15 09:33:39 -04004726 PorterDuff.Mode.SRC_ATOP, -1);
Jorim Jaggi92df1f22014-12-16 19:44:41 +01004727 }
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004728 }
4729
Julia Reynolds10ee1fc2015-11-09 11:04:55 -05004730 private void sanitizeColor() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004731 if (mN.color != COLOR_DEFAULT) {
4732 mN.color |= 0xFF000000; // no alpha for custom colors
Jorim Jaggi74419312014-06-10 20:57:21 +02004733 }
Jorim Jaggi74419312014-06-10 20:57:21 +02004734 }
4735
Adrian Roos4ff3b122016-02-01 12:26:13 -08004736 int resolveContrastColor() {
4737 if (mCachedContrastColorIsFor == mN.color && mCachedContrastColor != COLOR_INVALID) {
4738 return mCachedContrastColor;
Dan Sandler26e81cf2014-05-06 10:01:27 -04004739 }
Adrian Roos4ff3b122016-02-01 12:26:13 -08004740
Selim Cinekac5f0272017-05-02 16:05:41 -07004741 int color;
4742 int background = mBackgroundColorHint;
4743 if (mBackgroundColorHint == COLOR_INVALID) {
4744 background = mContext.getColor(
4745 com.android.internal.R.color.notification_material_background_color);
4746 }
4747 if (mN.color == COLOR_DEFAULT) {
4748 ensureColors();
4749 color = mSecondaryTextColor;
4750 } else {
4751 color = NotificationColorUtil.resolveContrastColor(mContext, mN.color,
4752 background);
4753 }
4754 if (Color.alpha(color) < 255) {
4755 // alpha doesn't go well for color filters, so let's blend it manually
4756 color = NotificationColorUtil.compositeColors(color, background);
4757 }
Adrian Roos4ff3b122016-02-01 12:26:13 -08004758 mCachedContrastColorIsFor = mN.color;
Selim Cinekac5f0272017-05-02 16:05:41 -07004759 return mCachedContrastColor = color;
Dan Sandler26e81cf2014-05-06 10:01:27 -04004760 }
4761
Adrian Roos487374f2017-01-11 15:48:14 -08004762 int resolveAmbientColor() {
4763 if (mCachedAmbientColorIsFor == mN.color && mCachedAmbientColorIsFor != COLOR_INVALID) {
4764 return mCachedAmbientColor;
4765 }
4766 final int contrasted = NotificationColorUtil.resolveAmbientColor(mContext, mN.color);
4767
4768 mCachedAmbientColorIsFor = mN.color;
4769 return mCachedAmbientColor = contrasted;
4770 }
4771
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004772 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004773 * Apply the unstyled operations and return a new {@link Notification} object.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04004774 * @hide
Joe Onoratocb109a02011-01-18 17:57:41 -08004775 */
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04004776 public Notification buildUnstyled() {
Daniel Sandlera0a938c2012-03-15 08:42:37 -04004777 if (mActions.size() > 0) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004778 mN.actions = new Action[mActions.size()];
4779 mActions.toArray(mN.actions);
Daniel Sandlera0a938c2012-03-15 08:42:37 -04004780 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004781 if (!mPersonList.isEmpty()) {
4782 mN.extras.putStringArray(EXTRA_PEOPLE,
4783 mPersonList.toArray(new String[mPersonList.size()]));
Dan Sandler0bf2ed82013-12-21 23:33:41 -06004784 }
Selim Cinek247fa012016-02-18 09:50:48 -08004785 if (mN.bigContentView != null || mN.contentView != null
4786 || mN.headsUpContentView != null) {
4787 mN.extras.putBoolean(EXTRA_CONTAINS_CUSTOM_VIEW, true);
4788 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004789 return mN;
Joe Onorato46439ce2010-11-19 13:56:21 -08004790 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004791
Julia Reynolds3b848122016-02-26 10:45:32 -05004792 /**
4793 * Creates a Builder from an existing notification so further changes can be made.
4794 * @param context The context for your application / activity.
4795 * @param n The notification to create a Builder from.
4796 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004797 public static Notification.Builder recoverBuilder(Context context, Notification n) {
Christoph Studer4600f9b2014-07-22 22:44:43 +02004798 // Re-create notification context so we can access app resources.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004799 ApplicationInfo applicationInfo = n.extras.getParcelable(
4800 EXTRA_BUILDER_APPLICATION_INFO);
Christoph Studer4600f9b2014-07-22 22:44:43 +02004801 Context builderContext;
Julia Reynoldsda303542015-11-23 14:00:20 -05004802 if (applicationInfo != null) {
4803 try {
4804 builderContext = context.createApplicationContext(applicationInfo,
4805 Context.CONTEXT_RESTRICTED);
4806 } catch (NameNotFoundException e) {
4807 Log.e(TAG, "ApplicationInfo " + applicationInfo + " not found");
4808 builderContext = context; // try with our context
4809 }
4810 } else {
4811 builderContext = context; // try with given context
Christoph Studer4600f9b2014-07-22 22:44:43 +02004812 }
4813
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004814 return new Builder(builderContext, n);
Christoph Studer4600f9b2014-07-22 22:44:43 +02004815 }
4816
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004817 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004818 * @deprecated Use {@link #build()} instead.
4819 */
4820 @Deprecated
4821 public Notification getNotification() {
4822 return build();
4823 }
4824
4825 /**
4826 * Combine all of the options that have been set and return a new {@link Notification}
4827 * object.
4828 */
4829 public Notification build() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004830 // first, add any extras from the calling code
4831 if (mUserExtras != null) {
4832 mN.extras = getAllExtras();
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07004833 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004834
Selim Cinekb85f36fd2016-04-20 18:46:36 -07004835 mN.creationTime = System.currentTimeMillis();
4836
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004837 // lazy stuff from mContext; see comment in Builder(Context, Notification)
Julia Reynoldsda303542015-11-23 14:00:20 -05004838 Notification.addFieldsFromContext(mContext, mN);
Christoph Studer943aa672014-08-03 20:31:16 +02004839
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004840 buildUnstyled();
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004841
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004842 if (mStyle != null) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004843 mStyle.buildStyled(mN);
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004844 }
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004845
Adrian Roos5081c0d2016-02-26 16:04:19 -08004846 if (mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N
4847 && (mStyle == null || !mStyle.displayCustomViewInline())) {
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004848 if (mN.contentView == null) {
Julia Reynolds3b848122016-02-26 10:45:32 -05004849 mN.contentView = createContentView();
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004850 mN.extras.putInt(EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT,
4851 mN.contentView.getSequenceNumber());
4852 }
4853 if (mN.bigContentView == null) {
Julia Reynolds3b848122016-02-26 10:45:32 -05004854 mN.bigContentView = createBigContentView();
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004855 if (mN.bigContentView != null) {
4856 mN.extras.putInt(EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT,
4857 mN.bigContentView.getSequenceNumber());
4858 }
4859 }
4860 if (mN.headsUpContentView == null) {
Julia Reynolds3b848122016-02-26 10:45:32 -05004861 mN.headsUpContentView = createHeadsUpContentView();
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004862 if (mN.headsUpContentView != null) {
4863 mN.extras.putInt(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT,
4864 mN.headsUpContentView.getSequenceNumber());
4865 }
4866 }
4867 }
4868
Julia Reynolds4c0c2022016-02-02 15:11:59 -05004869 if ((mN.defaults & DEFAULT_LIGHTS) != 0) {
4870 mN.flags |= FLAG_SHOW_LIGHTS;
4871 }
4872
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004873 return mN;
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004874 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05004875
4876 /**
4877 * Apply this Builder to an existing {@link Notification} object.
4878 *
4879 * @hide
4880 */
4881 public Notification buildInto(Notification n) {
Daniel Sandler1a497d32013-04-18 14:52:45 -04004882 build().cloneInto(n, true);
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05004883 return n;
4884 }
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004885
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004886 /**
Adrian Roos184bfe022016-03-03 13:41:44 -08004887 * Removes RemoteViews that were created for compatibility from {@param n}, if they did not
4888 * change.
4889 *
4890 * @return {@param n}, if no stripping is needed, otherwise a stripped clone of {@param n}.
4891 *
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004892 * @hide
4893 */
Adrian Roos184bfe022016-03-03 13:41:44 -08004894 public static Notification maybeCloneStrippedForDelivery(Notification n) {
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004895 String templateClass = n.extras.getString(EXTRA_TEMPLATE);
Adrian Roos184bfe022016-03-03 13:41:44 -08004896
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004897 // Only strip views for known Styles because we won't know how to
4898 // re-create them otherwise.
Adrian Roos184bfe022016-03-03 13:41:44 -08004899 if (!TextUtils.isEmpty(templateClass)
4900 && getNotificationStyleClass(templateClass) == null) {
4901 return n;
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004902 }
Adrian Roos184bfe022016-03-03 13:41:44 -08004903
4904 // Only strip unmodified BuilderRemoteViews.
4905 boolean stripContentView = n.contentView instanceof BuilderRemoteViews &&
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004906 n.extras.getInt(EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT, -1) ==
Adrian Roos184bfe022016-03-03 13:41:44 -08004907 n.contentView.getSequenceNumber();
4908 boolean stripBigContentView = n.bigContentView instanceof BuilderRemoteViews &&
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004909 n.extras.getInt(EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT, -1) ==
Adrian Roos184bfe022016-03-03 13:41:44 -08004910 n.bigContentView.getSequenceNumber();
4911 boolean stripHeadsUpContentView = n.headsUpContentView instanceof BuilderRemoteViews &&
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004912 n.extras.getInt(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT, -1) ==
Adrian Roos184bfe022016-03-03 13:41:44 -08004913 n.headsUpContentView.getSequenceNumber();
4914
4915 // Nothing to do here, no need to clone.
4916 if (!stripContentView && !stripBigContentView && !stripHeadsUpContentView) {
4917 return n;
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004918 }
Adrian Roos184bfe022016-03-03 13:41:44 -08004919
4920 Notification clone = n.clone();
4921 if (stripContentView) {
4922 clone.contentView = null;
4923 clone.extras.remove(EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT);
4924 }
4925 if (stripBigContentView) {
4926 clone.bigContentView = null;
4927 clone.extras.remove(EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT);
4928 }
4929 if (stripHeadsUpContentView) {
4930 clone.headsUpContentView = null;
4931 clone.extras.remove(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT);
4932 }
4933 return clone;
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004934 }
4935
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004936 private int getBaseLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07004937 return R.layout.notification_template_material_base;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004938 }
4939
4940 private int getBigBaseLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07004941 return R.layout.notification_template_material_big_base;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004942 }
4943
4944 private int getBigPictureLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07004945 return R.layout.notification_template_material_big_picture;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004946 }
4947
4948 private int getBigTextLayoutResource() {
Jorim Jaggi445d3c02014-08-19 22:33:42 +02004949 return R.layout.notification_template_material_big_text;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004950 }
4951
4952 private int getInboxLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07004953 return R.layout.notification_template_material_inbox;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004954 }
4955
Adrian Roosc1a80b02016-04-05 14:54:55 -07004956 private int getMessagingLayoutResource() {
4957 return R.layout.notification_template_material_messaging;
4958 }
4959
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004960 private int getActionLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07004961 return R.layout.notification_material_action;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004962 }
4963
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004964 private int getEmphasizedActionLayoutResource() {
4965 return R.layout.notification_material_action_emphasized;
4966 }
4967
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004968 private int getActionTombstoneLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07004969 return R.layout.notification_material_action_tombstone;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004970 }
Selim Cinek7b9605b2017-01-19 17:36:00 -08004971
4972 private int getBackgroundColor() {
4973 if (isColorized()) {
Selim Cinek5fb73f82017-04-20 16:55:38 -07004974 return mBackgroundColor != COLOR_INVALID ? mBackgroundColor : mN.color;
Selim Cinek7b9605b2017-01-19 17:36:00 -08004975 } else {
Selim Cinekac5f0272017-05-02 16:05:41 -07004976 return mBackgroundColorHint != COLOR_INVALID ? mBackgroundColorHint
4977 : COLOR_DEFAULT;
Selim Cinek7b9605b2017-01-19 17:36:00 -08004978 }
4979 }
4980
4981 private boolean isColorized() {
4982 return mN.isColorized();
4983 }
Selim Cinek99104832017-01-25 14:47:33 -08004984
4985 private boolean textColorsNeedInversion() {
4986 if (mStyle == null || !MediaStyle.class.equals(mStyle.getClass())) {
4987 return false;
4988 }
4989 int targetSdkVersion = mContext.getApplicationInfo().targetSdkVersion;
4990 return targetSdkVersion > Build.VERSION_CODES.M
4991 && targetSdkVersion < Build.VERSION_CODES.O;
4992 }
Selim Cinek5fb73f82017-04-20 16:55:38 -07004993
4994 /**
4995 * Set a color palette to be used as the background and textColors
4996 *
4997 * @param backgroundColor the color to be used as the background
4998 * @param foregroundColor the color to be used as the foreground
4999 *
5000 * @hide
5001 */
5002 public void setColorPalette(int backgroundColor, int foregroundColor) {
5003 mBackgroundColor = backgroundColor;
5004 mForegroundColor = foregroundColor;
5005 mTextColorsAreForBackground = COLOR_INVALID;
5006 ensureColors();
5007 }
Selim Cinekac5f0272017-05-02 16:05:41 -07005008
5009 /**
5010 * Sets the background color for this notification to be a different one then the default.
5011 * This is mainly used to calculate contrast and won't necessarily be applied to the
5012 * background.
5013 *
5014 * @hide
5015 */
5016 public void setBackgroundColorHint(int backgroundColor) {
5017 mBackgroundColorHint = backgroundColor;
5018 }
Selim Cinek7b9605b2017-01-19 17:36:00 -08005019 }
5020
5021 /**
Selim Cinek22714f12017-04-13 16:23:53 -07005022 * @return whether this notification is a foreground service notification
Selim Cinek7b9605b2017-01-19 17:36:00 -08005023 */
Selim Cinek22714f12017-04-13 16:23:53 -07005024 private boolean isForegroundService() {
5025 return (flags & Notification.FLAG_FOREGROUND_SERVICE) != 0;
Selim Cinek7b9605b2017-01-19 17:36:00 -08005026 }
5027
5028 /**
Selim Cinek99104832017-01-25 14:47:33 -08005029 * @return whether this notification has a media session attached
5030 * @hide
5031 */
5032 public boolean hasMediaSession() {
5033 return extras.getParcelable(Notification.EXTRA_MEDIA_SESSION) != null;
5034 }
5035
5036 /**
Selim Cinek7d1009b2017-01-25 15:28:28 -08005037 * @return the style class of this notification
5038 * @hide
5039 */
5040 public Class<? extends Notification.Style> getNotificationStyle() {
5041 String templateClass = extras.getString(Notification.EXTRA_TEMPLATE);
5042
5043 if (!TextUtils.isEmpty(templateClass)) {
5044 return Notification.getNotificationStyleClass(templateClass);
5045 }
5046 return null;
5047 }
5048
5049 /**
Selim Cinek22714f12017-04-13 16:23:53 -07005050 * @return true if this notification is colorized.
Selim Cinek7b9605b2017-01-19 17:36:00 -08005051 *
5052 * @hide
5053 */
5054 public boolean isColorized() {
Selim Cinek5fb73f82017-04-20 16:55:38 -07005055 if (isColorizedMedia()) {
5056 return true;
5057 }
5058 return extras.getBoolean(EXTRA_COLORIZED) && isForegroundService();
5059 }
5060
5061 /**
5062 * @return true if this notification is colorized and it is a media notification
5063 *
5064 * @hide
5065 */
5066 public boolean isColorizedMedia() {
Selim Cinek99104832017-01-25 14:47:33 -08005067 Class<? extends Style> style = getNotificationStyle();
5068 if (MediaStyle.class.equals(style)) {
5069 Boolean colorized = (Boolean) extras.get(EXTRA_COLORIZED);
5070 if ((colorized == null || colorized) && hasMediaSession()) {
5071 return true;
5072 }
5073 } else if (DecoratedMediaCustomViewStyle.class.equals(style)) {
5074 if (extras.getBoolean(EXTRA_COLORIZED) && hasMediaSession()) {
5075 return true;
5076 }
5077 }
Selim Cinek5fb73f82017-04-20 16:55:38 -07005078 return false;
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005079 }
5080
Selim Cinek0847acd2017-04-24 19:48:29 -07005081
5082 /**
5083 * @return true if this is a media notification
5084 *
5085 * @hide
5086 */
5087 public boolean isMediaNotification() {
5088 Class<? extends Style> style = getNotificationStyle();
5089 if (MediaStyle.class.equals(style)) {
5090 return true;
5091 } else if (DecoratedMediaCustomViewStyle.class.equals(style)) {
5092 return true;
5093 }
5094 return false;
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005095 }
5096
Selim Cinek279fa862016-06-14 10:57:25 -07005097 private boolean hasLargeIcon() {
5098 return mLargeIcon != null || largeIcon != null;
5099 }
5100
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005101 /**
Selim Cinekc2c0b042016-05-18 17:13:46 -07005102 * @return true if the notification will show the time; false otherwise
Selim Cinekb85f36fd2016-04-20 18:46:36 -07005103 * @hide
5104 */
Selim Cinekc2c0b042016-05-18 17:13:46 -07005105 public boolean showsTime() {
Selim Cinekb85f36fd2016-04-20 18:46:36 -07005106 return when != 0 && extras.getBoolean(EXTRA_SHOW_WHEN);
5107 }
5108
5109 /**
Selim Cinekc2c0b042016-05-18 17:13:46 -07005110 * @return true if the notification will show a chronometer; false otherwise
5111 * @hide
5112 */
5113 public boolean showsChronometer() {
5114 return when != 0 && extras.getBoolean(EXTRA_SHOW_CHRONOMETER);
5115 }
5116
5117 /**
Julia Reynolds4a02afb2016-12-13 13:39:52 -05005118 * @hide
5119 */
5120 @SystemApi
5121 public static Class<? extends Style> getNotificationStyleClass(String templateClass) {
5122 Class<? extends Style>[] classes = new Class[] {
5123 BigTextStyle.class, BigPictureStyle.class, InboxStyle.class, MediaStyle.class,
5124 DecoratedCustomViewStyle.class, DecoratedMediaCustomViewStyle.class,
5125 MessagingStyle.class };
5126 for (Class<? extends Style> innerClass : classes) {
5127 if (templateClass.equals(innerClass.getName())) {
5128 return innerClass;
5129 }
5130 }
5131 return null;
5132 }
5133
5134 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005135 * An object that can apply a rich notification style to a {@link Notification.Builder}
5136 * object.
5137 */
Griff Hazendfcb0802014-02-11 12:00:00 -08005138 public static abstract class Style {
Chris Wrend6297db2012-05-03 16:20:13 -04005139 private CharSequence mBigContentTitle;
Jorim Jaggi457a10d2014-09-08 16:18:23 +02005140
5141 /**
5142 * @hide
5143 */
5144 protected CharSequence mSummaryText = null;
5145
5146 /**
5147 * @hide
5148 */
5149 protected boolean mSummaryTextSet = false;
Chris Wrend6297db2012-05-03 16:20:13 -04005150
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005151 protected Builder mBuilder;
5152
Chris Wrend6297db2012-05-03 16:20:13 -04005153 /**
5154 * Overrides ContentTitle in the big form of the template.
5155 * This defaults to the value passed to setContentTitle().
5156 */
5157 protected void internalSetBigContentTitle(CharSequence title) {
5158 mBigContentTitle = title;
5159 }
5160
5161 /**
5162 * Set the first line of text after the detail section in the big form of the template.
5163 */
5164 protected void internalSetSummaryText(CharSequence cs) {
5165 mSummaryText = cs;
Daniel Sandler619738c2012-06-07 16:33:08 -04005166 mSummaryTextSet = true;
Chris Wrend6297db2012-05-03 16:20:13 -04005167 }
5168
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005169 public void setBuilder(Builder builder) {
5170 if (mBuilder != builder) {
5171 mBuilder = builder;
Daniel Sandlerc08dea22012-06-28 08:35:24 -07005172 if (mBuilder != null) {
5173 mBuilder.setStyle(this);
5174 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005175 }
5176 }
5177
Chris Wrend6297db2012-05-03 16:20:13 -04005178 protected void checkBuilder() {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005179 if (mBuilder == null) {
5180 throw new IllegalArgumentException("Style requires a valid Builder object");
5181 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005182 }
Chris Wrend6297db2012-05-03 16:20:13 -04005183
5184 protected RemoteViews getStandardView(int layoutId) {
5185 checkBuilder();
5186
Christoph Studer4600f9b2014-07-22 22:44:43 +02005187 // Nasty.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005188 CharSequence oldBuilderContentTitle =
5189 mBuilder.getAllExtras().getCharSequence(EXTRA_TITLE);
Chris Wrend6297db2012-05-03 16:20:13 -04005190 if (mBigContentTitle != null) {
5191 mBuilder.setContentTitle(mBigContentTitle);
5192 }
5193
Chris Wrend6297db2012-05-03 16:20:13 -04005194 RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(layoutId);
5195
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005196 mBuilder.getAllExtras().putCharSequence(EXTRA_TITLE, oldBuilderContentTitle);
Christoph Studer4600f9b2014-07-22 22:44:43 +02005197
Chris Wrend6297db2012-05-03 16:20:13 -04005198 if (mBigContentTitle != null && mBigContentTitle.equals("")) {
5199 contentView.setViewVisibility(R.id.line1, View.GONE);
Chris Wren67dc9a02012-05-16 01:03:20 -04005200 } else {
5201 contentView.setViewVisibility(R.id.line1, View.VISIBLE);
Chris Wrend6297db2012-05-03 16:20:13 -04005202 }
5203
Chris Wrend6297db2012-05-03 16:20:13 -04005204 return contentView;
5205 }
5206
Daniel Sandlerf45564e2013-04-15 15:05:08 -04005207 /**
Selim Cinek7d1009b2017-01-25 15:28:28 -08005208 * Construct a Style-specific RemoteViews for the collapsed notification layout.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005209 * The default implementation has nothing additional to add.
Selim Cinek7d1009b2017-01-25 15:28:28 -08005210 *
5211 * @param increasedHeight true if this layout be created with an increased height.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005212 * @hide
5213 */
Selim Cinek7d1009b2017-01-25 15:28:28 -08005214 public RemoteViews makeContentView(boolean increasedHeight) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005215 return null;
5216 }
5217
5218 /**
5219 * Construct a Style-specific RemoteViews for the final big notification layout.
5220 * @hide
5221 */
5222 public RemoteViews makeBigContentView() {
5223 return null;
5224 }
5225
5226 /**
5227 * Construct a Style-specific RemoteViews for the final HUN layout.
Selim Cinek87ed69b2017-02-09 15:59:43 -08005228 *
5229 * @param increasedHeight true if this layout be created with an increased height.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005230 * @hide
5231 */
Selim Cinek87ed69b2017-02-09 15:59:43 -08005232 public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005233 return null;
5234 }
5235
5236 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005237 * Apply any style-specific extras to this notification before shipping it out.
Daniel Sandlerf45564e2013-04-15 15:05:08 -04005238 * @hide
5239 */
5240 public void addExtras(Bundle extras) {
5241 if (mSummaryTextSet) {
5242 extras.putCharSequence(EXTRA_SUMMARY_TEXT, mSummaryText);
5243 }
5244 if (mBigContentTitle != null) {
5245 extras.putCharSequence(EXTRA_TITLE_BIG, mBigContentTitle);
5246 }
Chris Wren91ad5632013-06-05 15:05:57 -04005247 extras.putString(EXTRA_TEMPLATE, this.getClass().getName());
Daniel Sandlerf45564e2013-04-15 15:05:08 -04005248 }
5249
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04005250 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005251 * Reconstruct the internal state of this Style object from extras.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04005252 * @hide
5253 */
Christoph Studer4600f9b2014-07-22 22:44:43 +02005254 protected void restoreFromExtras(Bundle extras) {
5255 if (extras.containsKey(EXTRA_SUMMARY_TEXT)) {
5256 mSummaryText = extras.getCharSequence(EXTRA_SUMMARY_TEXT);
5257 mSummaryTextSet = true;
5258 }
5259 if (extras.containsKey(EXTRA_TITLE_BIG)) {
5260 mBigContentTitle = extras.getCharSequence(EXTRA_TITLE_BIG);
5261 }
5262 }
5263
5264
5265 /**
5266 * @hide
5267 */
5268 public Notification buildStyled(Notification wip) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005269 addExtras(wip.extras);
Christoph Studer4600f9b2014-07-22 22:44:43 +02005270 return wip;
5271 }
5272
Daniel Sandler0ec46202015-06-24 01:27:05 -04005273 /**
5274 * @hide
5275 */
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07005276 public void purgeResources() {}
5277
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04005278 /**
5279 * Calls {@link android.app.Notification.Builder#build()} on the Builder this Style is
5280 * attached to.
5281 *
5282 * @return the fully constructed Notification.
5283 */
5284 public Notification build() {
5285 checkBuilder();
5286 return mBuilder.build();
5287 }
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02005288
5289 /**
5290 * @hide
5291 * @return true if the style positions the progress bar on the second line; false if the
5292 * style hides the progress bar
5293 */
5294 protected boolean hasProgress() {
5295 return true;
5296 }
Selim Cinek03d0d652015-11-13 13:18:09 -05005297
5298 /**
5299 * @hide
5300 * @return Whether we should put the summary be put into the notification header
5301 */
5302 public boolean hasSummaryInHeader() {
5303 return true;
5304 }
Selim Cinek593610c2016-02-16 18:42:57 -08005305
5306 /**
5307 * @hide
5308 * @return Whether custom content views are displayed inline in the style
5309 */
5310 public boolean displayCustomViewInline() {
5311 return false;
5312 }
Joe Onorato46439ce2010-11-19 13:56:21 -08005313 }
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005314
5315 /**
Daniel Sandler4dfbe832012-04-11 14:51:46 -04005316 * Helper class for generating large-format notifications that include a large image attachment.
Joe Malin8d40d042012-11-05 11:36:40 -08005317 *
Robert Ly91c5ce32014-06-08 15:37:00 -07005318 * Here's how you'd set the <code>BigPictureStyle</code> on a notification:
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005319 * <pre class="prettyprint">
Robert Ly91c5ce32014-06-08 15:37:00 -07005320 * Notification notif = new Notification.Builder(mContext)
5321 * .setContentTitle(&quot;New photo from &quot; + sender.toString())
5322 * .setContentText(subject)
5323 * .setSmallIcon(R.drawable.new_post)
5324 * .setLargeIcon(aBitmap)
5325 * .setStyle(new Notification.BigPictureStyle()
5326 * .bigPicture(aBigBitmap))
5327 * .build();
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005328 * </pre>
Joe Malin8d40d042012-11-05 11:36:40 -08005329 *
Daniel Sandler4dfbe832012-04-11 14:51:46 -04005330 * @see Notification#bigContentView
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005331 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005332 public static class BigPictureStyle extends Style {
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005333 private Bitmap mPicture;
Dan Sandlerd63f9322015-05-06 15:18:49 -04005334 private Icon mBigLargeIcon;
Chris Wren3745a3d2012-05-22 15:11:52 -04005335 private boolean mBigLargeIconSet = false;
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005336
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005337 public BigPictureStyle() {
5338 }
5339
Adrian Roosf5faf9d2016-05-23 13:56:15 -07005340 /**
5341 * @deprecated use {@code BigPictureStyle()}.
5342 */
5343 @Deprecated
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005344 public BigPictureStyle(Builder builder) {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005345 setBuilder(builder);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005346 }
5347
Chris Wrend6297db2012-05-03 16:20:13 -04005348 /**
5349 * Overrides ContentTitle in the big form of the template.
5350 * This defaults to the value passed to setContentTitle().
5351 */
5352 public BigPictureStyle setBigContentTitle(CharSequence title) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04005353 internalSetBigContentTitle(safeCharSequence(title));
Chris Wrend6297db2012-05-03 16:20:13 -04005354 return this;
5355 }
5356
5357 /**
5358 * Set the first line of text after the detail section in the big form of the template.
5359 */
5360 public BigPictureStyle setSummaryText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04005361 internalSetSummaryText(safeCharSequence(cs));
Chris Wrend6297db2012-05-03 16:20:13 -04005362 return this;
5363 }
5364
Chris Wren0bd664d2012-08-01 13:56:56 -04005365 /**
5366 * Provide the bitmap to be used as the payload for the BigPicture notification.
5367 */
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005368 public BigPictureStyle bigPicture(Bitmap b) {
5369 mPicture = b;
5370 return this;
5371 }
5372
Chris Wren3745a3d2012-05-22 15:11:52 -04005373 /**
Chris Wren3745a3d2012-05-22 15:11:52 -04005374 * Override the large icon when the big notification is shown.
5375 */
5376 public BigPictureStyle bigLargeIcon(Bitmap b) {
Dan Sandlerd63f9322015-05-06 15:18:49 -04005377 return bigLargeIcon(b != null ? Icon.createWithBitmap(b) : null);
5378 }
5379
5380 /**
5381 * Override the large icon when the big notification is shown.
5382 */
5383 public BigPictureStyle bigLargeIcon(Icon icon) {
Chris Wren3745a3d2012-05-22 15:11:52 -04005384 mBigLargeIconSet = true;
Dan Sandlerd63f9322015-05-06 15:18:49 -04005385 mBigLargeIcon = icon;
Chris Wren3745a3d2012-05-22 15:11:52 -04005386 return this;
5387 }
5388
Riley Andrews0394a0c2015-11-03 23:36:52 -08005389 /** @hide */
5390 public static final int MIN_ASHMEM_BITMAP_SIZE = 128 * (1 << 10);
5391
Daniel Sandler0ec46202015-06-24 01:27:05 -04005392 /**
5393 * @hide
5394 */
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07005395 @Override
5396 public void purgeResources() {
5397 super.purgeResources();
Riley Andrews8cee7c12015-11-01 23:36:04 -08005398 if (mPicture != null &&
5399 mPicture.isMutable() &&
Riley Andrews0394a0c2015-11-03 23:36:52 -08005400 mPicture.getAllocationByteCount() >= MIN_ASHMEM_BITMAP_SIZE) {
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07005401 mPicture = mPicture.createAshmemBitmap();
5402 }
5403 if (mBigLargeIcon != null) {
5404 mBigLargeIcon.convertToAshmem();
5405 }
5406 }
Christoph Studer5c510ee2014-12-15 16:32:27 +01005407
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005408 /**
5409 * @hide
5410 */
5411 public RemoteViews makeBigContentView() {
5412 // Replace mN.mLargeIcon with mBigLargeIcon if mBigLargeIconSet
Christoph Studer5c510ee2014-12-15 16:32:27 +01005413 // This covers the following cases:
5414 // 1. mBigLargeIconSet -> mBigLargeIcon (null or non-null) applies, overrides
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005415 // mN.mLargeIcon
5416 // 2. !mBigLargeIconSet -> mN.mLargeIcon applies
Dan Sandlerd63f9322015-05-06 15:18:49 -04005417 Icon oldLargeIcon = null;
Selim Cineke99acb22016-08-04 12:55:48 -07005418 Bitmap largeIconLegacy = null;
Christoph Studer5c510ee2014-12-15 16:32:27 +01005419 if (mBigLargeIconSet) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005420 oldLargeIcon = mBuilder.mN.mLargeIcon;
5421 mBuilder.mN.mLargeIcon = mBigLargeIcon;
Selim Cineke99acb22016-08-04 12:55:48 -07005422 // The legacy largeIcon might not allow us to clear the image, as it's taken in
5423 // replacement if the other one is null. Because we're restoring these legacy icons
5424 // for old listeners, this is in general non-null.
5425 largeIconLegacy = mBuilder.mN.largeIcon;
5426 mBuilder.mN.largeIcon = null;
Christoph Studer5c510ee2014-12-15 16:32:27 +01005427 }
5428
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01005429 RemoteViews contentView = getStandardView(mBuilder.getBigPictureLayoutResource());
Selim Cinek03d0d652015-11-13 13:18:09 -05005430 if (mSummaryTextSet) {
5431 contentView.setTextViewText(R.id.text, mBuilder.processLegacyText(mSummaryText));
Selim Cinek7b9605b2017-01-19 17:36:00 -08005432 mBuilder.setTextViewColorSecondary(contentView, R.id.text);
Selim Cinekc848c3a2016-01-13 15:27:30 -08005433 contentView.setViewVisibility(R.id.text, View.VISIBLE);
Selim Cinek03d0d652015-11-13 13:18:09 -05005434 }
Selim Cinek279fa862016-06-14 10:57:25 -07005435 mBuilder.setContentMinHeight(contentView, mBuilder.mN.hasLargeIcon());
Selim Cinek53e64a42015-11-16 10:40:56 -08005436
Christoph Studer5c510ee2014-12-15 16:32:27 +01005437 if (mBigLargeIconSet) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005438 mBuilder.mN.mLargeIcon = oldLargeIcon;
Selim Cineke99acb22016-08-04 12:55:48 -07005439 mBuilder.mN.largeIcon = largeIconLegacy;
Christoph Studer5c510ee2014-12-15 16:32:27 +01005440 }
5441
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005442 contentView.setImageViewBitmap(R.id.big_picture, mPicture);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005443 return contentView;
5444 }
5445
Daniel Sandlerf45564e2013-04-15 15:05:08 -04005446 /**
5447 * @hide
5448 */
5449 public void addExtras(Bundle extras) {
5450 super.addExtras(extras);
5451
5452 if (mBigLargeIconSet) {
5453 extras.putParcelable(EXTRA_LARGE_ICON_BIG, mBigLargeIcon);
5454 }
5455 extras.putParcelable(EXTRA_PICTURE, mPicture);
5456 }
5457
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04005458 /**
5459 * @hide
5460 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005461 @Override
Christoph Studer4600f9b2014-07-22 22:44:43 +02005462 protected void restoreFromExtras(Bundle extras) {
5463 super.restoreFromExtras(extras);
5464
5465 if (extras.containsKey(EXTRA_LARGE_ICON_BIG)) {
Christoph Studer5c510ee2014-12-15 16:32:27 +01005466 mBigLargeIconSet = true;
Christoph Studer4600f9b2014-07-22 22:44:43 +02005467 mBigLargeIcon = extras.getParcelable(EXTRA_LARGE_ICON_BIG);
Chris Wren3745a3d2012-05-22 15:11:52 -04005468 }
Christoph Studer4600f9b2014-07-22 22:44:43 +02005469 mPicture = extras.getParcelable(EXTRA_PICTURE);
5470 }
Selim Cinek03d0d652015-11-13 13:18:09 -05005471
5472 /**
5473 * @hide
5474 */
5475 @Override
5476 public boolean hasSummaryInHeader() {
5477 return false;
5478 }
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005479 }
5480
5481 /**
Daniel Sandler4dfbe832012-04-11 14:51:46 -04005482 * Helper class for generating large-format notifications that include a lot of text.
Joe Malin8d40d042012-11-05 11:36:40 -08005483 *
Robert Ly91c5ce32014-06-08 15:37:00 -07005484 * Here's how you'd set the <code>BigTextStyle</code> on a notification:
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005485 * <pre class="prettyprint">
Robert Ly91c5ce32014-06-08 15:37:00 -07005486 * Notification notif = new Notification.Builder(mContext)
5487 * .setContentTitle(&quot;New mail from &quot; + sender.toString())
5488 * .setContentText(subject)
5489 * .setSmallIcon(R.drawable.new_mail)
5490 * .setLargeIcon(aBitmap)
5491 * .setStyle(new Notification.BigTextStyle()
5492 * .bigText(aVeryLongString))
5493 * .build();
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005494 * </pre>
Joe Malin8d40d042012-11-05 11:36:40 -08005495 *
Daniel Sandler4dfbe832012-04-11 14:51:46 -04005496 * @see Notification#bigContentView
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005497 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005498 public static class BigTextStyle extends Style {
Jorim Jaggi457a10d2014-09-08 16:18:23 +02005499
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005500 private CharSequence mBigText;
5501
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005502 public BigTextStyle() {
5503 }
5504
Adrian Roosf5faf9d2016-05-23 13:56:15 -07005505 /**
5506 * @deprecated use {@code BigTextStyle()}.
5507 */
5508 @Deprecated
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005509 public BigTextStyle(Builder builder) {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005510 setBuilder(builder);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005511 }
5512
Chris Wrend6297db2012-05-03 16:20:13 -04005513 /**
5514 * Overrides ContentTitle in the big form of the template.
5515 * This defaults to the value passed to setContentTitle().
5516 */
5517 public BigTextStyle setBigContentTitle(CharSequence title) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04005518 internalSetBigContentTitle(safeCharSequence(title));
Chris Wrend6297db2012-05-03 16:20:13 -04005519 return this;
5520 }
5521
5522 /**
5523 * Set the first line of text after the detail section in the big form of the template.
5524 */
5525 public BigTextStyle setSummaryText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04005526 internalSetSummaryText(safeCharSequence(cs));
Chris Wrend6297db2012-05-03 16:20:13 -04005527 return this;
5528 }
5529
Chris Wren0bd664d2012-08-01 13:56:56 -04005530 /**
5531 * Provide the longer text to be displayed in the big form of the
5532 * template in place of the content text.
5533 */
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005534 public BigTextStyle bigText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04005535 mBigText = safeCharSequence(cs);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005536 return this;
5537 }
5538
Daniel Sandlerf45564e2013-04-15 15:05:08 -04005539 /**
5540 * @hide
5541 */
5542 public void addExtras(Bundle extras) {
5543 super.addExtras(extras);
5544
Christoph Studer4600f9b2014-07-22 22:44:43 +02005545 extras.putCharSequence(EXTRA_BIG_TEXT, mBigText);
5546 }
5547
5548 /**
5549 * @hide
5550 */
5551 @Override
5552 protected void restoreFromExtras(Bundle extras) {
5553 super.restoreFromExtras(extras);
5554
5555 mBigText = extras.getCharSequence(EXTRA_BIG_TEXT);
Daniel Sandlerf45564e2013-04-15 15:05:08 -04005556 }
5557
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005558 /**
Selim Cinek7d1009b2017-01-25 15:28:28 -08005559 * @param increasedHeight true if this layout be created with an increased height.
5560 *
5561 * @hide
5562 */
5563 @Override
5564 public RemoteViews makeContentView(boolean increasedHeight) {
5565 if (increasedHeight) {
5566 ArrayList<Action> actions = mBuilder.mActions;
5567 mBuilder.mActions = new ArrayList<>();
5568 RemoteViews remoteViews = makeBigContentView();
5569 mBuilder.mActions = actions;
5570 return remoteViews;
5571 }
5572 return super.makeContentView(increasedHeight);
5573 }
5574
5575 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005576 * @hide
5577 */
Selim Cinek87ed69b2017-02-09 15:59:43 -08005578 @Override
5579 public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
5580 if (increasedHeight && mBuilder.mActions.size() > 0) {
5581 return makeBigContentView();
5582 }
5583 return super.makeHeadsUpContentView(increasedHeight);
5584 }
5585
5586 /**
5587 * @hide
5588 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005589 public RemoteViews makeBigContentView() {
Christoph Studer4600f9b2014-07-22 22:44:43 +02005590
5591 // Nasty
Selim Cinek75998782016-04-26 10:39:17 -07005592 CharSequence text = mBuilder.getAllExtras().getCharSequence(EXTRA_TEXT);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005593 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, null);
Daniel Sandler916ad912012-06-13 12:17:07 -04005594
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01005595 RemoteViews contentView = getStandardView(mBuilder.getBigTextLayoutResource());
Joe Malin8d40d042012-11-05 11:36:40 -08005596
Selim Cinek75998782016-04-26 10:39:17 -07005597 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, text);
Christoph Studer4600f9b2014-07-22 22:44:43 +02005598
Selim Cinek3a2c4b92015-12-17 17:01:17 -08005599 CharSequence bigTextText = mBuilder.processLegacyText(mBigText);
Selim Cinek75998782016-04-26 10:39:17 -07005600 if (TextUtils.isEmpty(bigTextText)) {
5601 // In case the bigtext is null / empty fall back to the normal text to avoid a weird
5602 // experience
5603 bigTextText = mBuilder.processLegacyText(text);
5604 }
Adrian Roosb1f427c2016-05-26 12:27:15 -07005605 applyBigTextContentView(mBuilder, contentView, bigTextText);
Selim Cinek4fb12d32015-11-19 18:10:48 -08005606
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005607 return contentView;
5608 }
5609
Adrian Roosb1f427c2016-05-26 12:27:15 -07005610 static void applyBigTextContentView(Builder builder,
5611 RemoteViews contentView, CharSequence bigTextText) {
5612 contentView.setTextViewText(R.id.big_text, bigTextText);
Selim Cinek7b9605b2017-01-19 17:36:00 -08005613 builder.setTextViewColorSecondary(contentView, R.id.big_text);
Adrian Roosb1f427c2016-05-26 12:27:15 -07005614 contentView.setViewVisibility(R.id.big_text,
5615 TextUtils.isEmpty(bigTextText) ? View.GONE : View.VISIBLE);
Selim Cinek279fa862016-06-14 10:57:25 -07005616 contentView.setBoolean(R.id.big_text, "setHasImage", builder.mN.hasLargeIcon());
Adrian Roosb1f427c2016-05-26 12:27:15 -07005617 }
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005618 }
Daniel Sandler879c5e02012-04-17 16:46:51 -04005619
5620 /**
Alex Hillsfc737de2016-03-23 17:33:02 -04005621 * Helper class for generating large-format notifications that include multiple back-and-forth
5622 * messages of varying types between any number of people.
5623 *
5624 * <br>
5625 * If the platform does not provide large-format notifications, this method has no effect. The
5626 * user will always see the normal notification view.
5627 * <br>
5628 * This class is a "rebuilder": It attaches to a Builder object and modifies its behavior, like
5629 * so:
5630 * <pre class="prettyprint">
5631 *
5632 * Notification noti = new Notification.Builder()
5633 * .setContentTitle(&quot;2 new messages wtih &quot; + sender.toString())
5634 * .setContentText(subject)
5635 * .setSmallIcon(R.drawable.new_message)
5636 * .setLargeIcon(aBitmap)
5637 * .setStyle(new Notification.MessagingStyle(resources.getString(R.string.reply_name))
5638 * .addMessage(messages[0].getText(), messages[0].getTime(), messages[0].getSender())
5639 * .addMessage(messages[1].getText(), messages[1].getTime(), messages[1].getSender()))
5640 * .build();
5641 * </pre>
5642 */
5643 public static class MessagingStyle extends Style {
5644
5645 /**
5646 * The maximum number of messages that will be retained in the Notification itself (the
5647 * number displayed is up to the platform).
5648 */
5649 public static final int MAXIMUM_RETAINED_MESSAGES = 25;
5650
5651 CharSequence mUserDisplayName;
5652 CharSequence mConversationTitle;
Alex Hillsd9b04d92016-04-11 16:38:16 -04005653 List<Message> mMessages = new ArrayList<>();
Adrian Roos437cd562017-01-18 15:47:03 -08005654 List<Message> mHistoricMessages = new ArrayList<>();
Alex Hillsfc737de2016-03-23 17:33:02 -04005655
5656 MessagingStyle() {
5657 }
5658
5659 /**
Alex Hillsfd590442016-10-07 09:52:44 -04005660 * @param userDisplayName Required - the name to be displayed for any replies sent by the
5661 * user before the posting app reposts the notification with those messages after they've
5662 * been actually sent and in previous messages sent by the user added in
Alex Hillsfc737de2016-03-23 17:33:02 -04005663 * {@link #addMessage(Notification.MessagingStyle.Message)}
5664 */
Alex Hillsfd590442016-10-07 09:52:44 -04005665 public MessagingStyle(@NonNull CharSequence userDisplayName) {
Alex Hillsfc737de2016-03-23 17:33:02 -04005666 mUserDisplayName = userDisplayName;
5667 }
5668
5669 /**
5670 * Returns the name to be displayed for any replies sent by the user
5671 */
5672 public CharSequence getUserDisplayName() {
5673 return mUserDisplayName;
5674 }
5675
5676 /**
Alex Hillsfc737de2016-03-23 17:33:02 -04005677 * Sets the title to be displayed on this conversation. This should only be used for
5678 * group messaging and left unset for one-on-one conversations.
5679 * @param conversationTitle
5680 * @return this object for method chaining.
5681 */
5682 public MessagingStyle setConversationTitle(CharSequence conversationTitle) {
5683 mConversationTitle = conversationTitle;
5684 return this;
5685 }
5686
5687 /**
5688 * Return the title to be displayed on this conversation. Can be <code>null</code> and
5689 * should be for one-on-one conversations
5690 */
5691 public CharSequence getConversationTitle() {
5692 return mConversationTitle;
5693 }
5694
5695 /**
5696 * Adds a message for display by this notification. Convenience call for a simple
5697 * {@link Message} in {@link #addMessage(Notification.MessagingStyle.Message)}.
5698 * @param text A {@link CharSequence} to be displayed as the message content
5699 * @param timestamp Time at which the message arrived
5700 * @param sender A {@link CharSequence} to be used for displaying the name of the
5701 * sender. Should be <code>null</code> for messages by the current user, in which case
5702 * the platform will insert {@link #getUserDisplayName()}.
5703 * Should be unique amongst all individuals in the conversation, and should be
5704 * consistent during re-posts of the notification.
5705 *
5706 * @see Message#Message(CharSequence, long, CharSequence)
5707 *
5708 * @return this object for method chaining
5709 */
5710 public MessagingStyle addMessage(CharSequence text, long timestamp, CharSequence sender) {
Adrian Roos437cd562017-01-18 15:47:03 -08005711 return addMessage(new Message(text, timestamp, sender));
Alex Hillsfc737de2016-03-23 17:33:02 -04005712 }
5713
5714 /**
5715 * Adds a {@link Message} for display in this notification.
Adrian Roos437cd562017-01-18 15:47:03 -08005716 *
5717 * <p>The messages should be added in chronologic order, i.e. the oldest first,
5718 * the newest last.
5719 *
Alex Hillsfc737de2016-03-23 17:33:02 -04005720 * @param message The {@link Message} to be displayed
5721 * @return this object for method chaining
5722 */
5723 public MessagingStyle addMessage(Message message) {
5724 mMessages.add(message);
5725 if (mMessages.size() > MAXIMUM_RETAINED_MESSAGES) {
5726 mMessages.remove(0);
5727 }
5728 return this;
5729 }
5730
5731 /**
Adrian Roos437cd562017-01-18 15:47:03 -08005732 * Adds a {@link Message} for historic context in this notification.
5733 *
5734 * <p>Messages should be added as historic if they are not the main subject of the
5735 * notification but may give context to a conversation. The system may choose to present
5736 * them only when relevant, e.g. when replying to a message through a {@link RemoteInput}.
5737 *
5738 * <p>The messages should be added in chronologic order, i.e. the oldest first,
5739 * the newest last.
5740 *
5741 * @param message The historic {@link Message} to be added
5742 * @return this object for method chaining
5743 */
5744 public MessagingStyle addHistoricMessage(Message message) {
5745 mHistoricMessages.add(message);
5746 if (mHistoricMessages.size() > MAXIMUM_RETAINED_MESSAGES) {
5747 mHistoricMessages.remove(0);
5748 }
5749 return this;
5750 }
5751
5752 /**
Alex Hillsfc737de2016-03-23 17:33:02 -04005753 * Gets the list of {@code Message} objects that represent the notification
5754 */
5755 public List<Message> getMessages() {
5756 return mMessages;
5757 }
5758
5759 /**
Adrian Roos437cd562017-01-18 15:47:03 -08005760 * Gets the list of historic {@code Message}s in the notification.
5761 */
5762 public List<Message> getHistoricMessages() {
5763 return mHistoricMessages;
5764 }
5765
5766 /**
Alex Hillsfc737de2016-03-23 17:33:02 -04005767 * @hide
5768 */
5769 @Override
5770 public void addExtras(Bundle extras) {
5771 super.addExtras(extras);
5772 if (mUserDisplayName != null) {
5773 extras.putCharSequence(EXTRA_SELF_DISPLAY_NAME, mUserDisplayName);
5774 }
5775 if (mConversationTitle != null) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04005776 extras.putCharSequence(EXTRA_CONVERSATION_TITLE, mConversationTitle);
Alex Hillsfc737de2016-03-23 17:33:02 -04005777 }
Alex Hillsd9b04d92016-04-11 16:38:16 -04005778 if (!mMessages.isEmpty()) { extras.putParcelableArray(EXTRA_MESSAGES,
5779 Message.getBundleArrayForMessages(mMessages));
Alex Hillsfc737de2016-03-23 17:33:02 -04005780 }
Adrian Roos437cd562017-01-18 15:47:03 -08005781 if (!mHistoricMessages.isEmpty()) { extras.putParcelableArray(EXTRA_HISTORIC_MESSAGES,
5782 Message.getBundleArrayForMessages(mHistoricMessages));
5783 }
Adrian Roos33fbd2c2016-05-27 15:35:28 -07005784
5785 fixTitleAndTextExtras(extras);
5786 }
5787
5788 private void fixTitleAndTextExtras(Bundle extras) {
5789 Message m = findLatestIncomingMessage();
5790 CharSequence text = (m == null) ? null : m.mText;
5791 CharSequence sender = m == null ? null
5792 : TextUtils.isEmpty(m.mSender) ? mUserDisplayName : m.mSender;
5793 CharSequence title;
5794 if (!TextUtils.isEmpty(mConversationTitle)) {
5795 if (!TextUtils.isEmpty(sender)) {
5796 BidiFormatter bidi = BidiFormatter.getInstance();
5797 title = mBuilder.mContext.getString(
5798 com.android.internal.R.string.notification_messaging_title_template,
5799 bidi.unicodeWrap(mConversationTitle), bidi.unicodeWrap(m.mSender));
5800 } else {
5801 title = mConversationTitle;
5802 }
5803 } else {
5804 title = sender;
5805 }
5806
5807 if (title != null) {
5808 extras.putCharSequence(EXTRA_TITLE, title);
5809 }
5810 if (text != null) {
5811 extras.putCharSequence(EXTRA_TEXT, text);
5812 }
Alex Hillsfc737de2016-03-23 17:33:02 -04005813 }
5814
5815 /**
5816 * @hide
5817 */
5818 @Override
5819 protected void restoreFromExtras(Bundle extras) {
5820 super.restoreFromExtras(extras);
5821
5822 mMessages.clear();
Adrian Roos437cd562017-01-18 15:47:03 -08005823 mHistoricMessages.clear();
Adrian Roos96b7e202016-05-17 13:50:38 -07005824 mUserDisplayName = extras.getCharSequence(EXTRA_SELF_DISPLAY_NAME);
5825 mConversationTitle = extras.getCharSequence(EXTRA_CONVERSATION_TITLE);
Adrian Roos437cd562017-01-18 15:47:03 -08005826 Parcelable[] messages = extras.getParcelableArray(EXTRA_MESSAGES);
5827 if (messages != null && messages instanceof Parcelable[]) {
5828 mMessages = Message.getMessagesFromBundleArray(messages);
5829 }
5830 Parcelable[] histMessages = extras.getParcelableArray(EXTRA_HISTORIC_MESSAGES);
5831 if (histMessages != null && histMessages instanceof Parcelable[]) {
5832 mHistoricMessages = Message.getMessagesFromBundleArray(histMessages);
Alex Hillsfc737de2016-03-23 17:33:02 -04005833 }
5834 }
5835
5836 /**
5837 * @hide
5838 */
Adrian Roosc1a80b02016-04-05 14:54:55 -07005839 @Override
Selim Cinek7d1009b2017-01-25 15:28:28 -08005840 public RemoteViews makeContentView(boolean increasedHeight) {
5841 if (!increasedHeight) {
5842 Message m = findLatestIncomingMessage();
5843 CharSequence title = mConversationTitle != null
5844 ? mConversationTitle
5845 : (m == null) ? null : m.mSender;
5846 CharSequence text = (m == null)
5847 ? null
5848 : mConversationTitle != null ? makeMessageLine(m, mBuilder) : m.mText;
Alex Hillsfc737de2016-03-23 17:33:02 -04005849
Selim Cinek7d1009b2017-01-25 15:28:28 -08005850 return mBuilder.applyStandardTemplate(mBuilder.getBaseLayoutResource(),
5851 mBuilder.mParams.reset().hasProgress(false).title(title).text(text));
5852 } else {
5853 ArrayList<Action> actions = mBuilder.mActions;
5854 mBuilder.mActions = new ArrayList<>();
5855 RemoteViews remoteViews = makeBigContentView();
5856 mBuilder.mActions = actions;
5857 return remoteViews;
5858 }
Adrian Roosc1a80b02016-04-05 14:54:55 -07005859 }
5860
5861 private Message findLatestIncomingMessage() {
5862 for (int i = mMessages.size() - 1; i >= 0; i--) {
5863 Message m = mMessages.get(i);
5864 // Incoming messages have a non-empty sender.
5865 if (!TextUtils.isEmpty(m.mSender)) {
5866 return m;
5867 }
5868 }
Adrian Roos33fbd2c2016-05-27 15:35:28 -07005869 if (!mMessages.isEmpty()) {
5870 // No incoming messages, fall back to outgoing message
5871 return mMessages.get(mMessages.size() - 1);
5872 }
Adrian Roosc1a80b02016-04-05 14:54:55 -07005873 return null;
5874 }
5875
5876 /**
5877 * @hide
5878 */
5879 @Override
5880 public RemoteViews makeBigContentView() {
5881 CharSequence title = !TextUtils.isEmpty(super.mBigContentTitle)
5882 ? super.mBigContentTitle
5883 : mConversationTitle;
5884 boolean hasTitle = !TextUtils.isEmpty(title);
5885
Adrian Roosfeafa052016-06-01 17:09:45 -07005886 if (mMessages.size() == 1) {
5887 // Special case for a single message: Use the big text style
5888 // so the collapsed and expanded versions match nicely.
5889 CharSequence bigTitle;
5890 CharSequence text;
5891 if (hasTitle) {
5892 bigTitle = title;
Selim Cinek7b9605b2017-01-19 17:36:00 -08005893 text = makeMessageLine(mMessages.get(0), mBuilder);
Adrian Roosfeafa052016-06-01 17:09:45 -07005894 } else {
5895 bigTitle = mMessages.get(0).mSender;
5896 text = mMessages.get(0).mText;
5897 }
Adrian Roosb1f427c2016-05-26 12:27:15 -07005898 RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(
5899 mBuilder.getBigTextLayoutResource(),
Adrian Roos70d7aa32017-01-11 15:39:06 -08005900 mBuilder.mParams.reset().hasProgress(false).title(bigTitle).text(null));
Adrian Roosb1f427c2016-05-26 12:27:15 -07005901 BigTextStyle.applyBigTextContentView(mBuilder, contentView, text);
5902 return contentView;
5903 }
5904
Adrian Roos48d746a2016-04-12 14:57:28 -07005905 RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(
Adrian Roosc1a80b02016-04-05 14:54:55 -07005906 mBuilder.getMessagingLayoutResource(),
Adrian Roos70d7aa32017-01-11 15:39:06 -08005907 mBuilder.mParams.reset().hasProgress(false).title(title).text(null));
Adrian Roosc1a80b02016-04-05 14:54:55 -07005908
5909 int[] rowIds = {R.id.inbox_text0, R.id.inbox_text1, R.id.inbox_text2, R.id.inbox_text3,
5910 R.id.inbox_text4, R.id.inbox_text5, R.id.inbox_text6};
5911
5912 // Make sure all rows are gone in case we reuse a view.
5913 for (int rowId : rowIds) {
5914 contentView.setViewVisibility(rowId, View.GONE);
5915 }
5916
5917 int i=0;
Adrian Roos2d5dbba2016-06-08 17:11:53 -07005918 contentView.setViewLayoutMarginBottomDimen(R.id.line1,
5919 hasTitle ? R.dimen.notification_messaging_spacing : 0);
Adrian Roosc1a80b02016-04-05 14:54:55 -07005920 contentView.setInt(R.id.notification_messaging, "setNumIndentLines",
Selim Cinek279fa862016-06-14 10:57:25 -07005921 !mBuilder.mN.hasLargeIcon() ? 0 : (hasTitle ? 1 : 2));
Adrian Roosc1a80b02016-04-05 14:54:55 -07005922
Adrian Roosfeafa052016-06-01 17:09:45 -07005923 int contractedChildId = View.NO_ID;
5924 Message contractedMessage = findLatestIncomingMessage();
Adrian Roos437cd562017-01-18 15:47:03 -08005925 int firstHistoricMessage = Math.max(0, mHistoricMessages.size()
5926 - (rowIds.length - mMessages.size()));
5927 while (firstHistoricMessage + i < mHistoricMessages.size() && i < rowIds.length) {
5928 Message m = mHistoricMessages.get(firstHistoricMessage + i);
5929 int rowId = rowIds[i];
5930
Selim Cinek7b9605b2017-01-19 17:36:00 -08005931 contentView.setTextViewText(rowId, makeMessageLine(m, mBuilder));
Adrian Roos437cd562017-01-18 15:47:03 -08005932
5933 if (contractedMessage == m) {
5934 contractedChildId = rowId;
5935 }
5936
5937 i++;
5938 }
5939
Adrian Roosc1a80b02016-04-05 14:54:55 -07005940 int firstMessage = Math.max(0, mMessages.size() - rowIds.length);
5941 while (firstMessage + i < mMessages.size() && i < rowIds.length) {
5942 Message m = mMessages.get(firstMessage + i);
5943 int rowId = rowIds[i];
5944
5945 contentView.setViewVisibility(rowId, View.VISIBLE);
Selim Cinek7b9605b2017-01-19 17:36:00 -08005946 contentView.setTextViewText(rowId, makeMessageLine(m, mBuilder));
5947 mBuilder.setTextViewColorSecondary(contentView, rowId);
Adrian Roosc1a80b02016-04-05 14:54:55 -07005948
Adrian Roosfeafa052016-06-01 17:09:45 -07005949 if (contractedMessage == m) {
5950 contractedChildId = rowId;
5951 }
5952
Adrian Roosc1a80b02016-04-05 14:54:55 -07005953 i++;
5954 }
Adrian Roos437cd562017-01-18 15:47:03 -08005955 // Clear the remaining views for reapply. Ensures that historic message views can
5956 // reliably be identified as being GONE and having non-null text.
5957 while (i < rowIds.length) {
5958 int rowId = rowIds[i];
5959 contentView.setTextViewText(rowId, null);
5960 i++;
5961 }
5962
Adrian Roosfeafa052016-06-01 17:09:45 -07005963 // Record this here to allow transformation between the contracted and expanded views.
5964 contentView.setInt(R.id.notification_messaging, "setContractedChildId",
5965 contractedChildId);
Alex Hillsfc737de2016-03-23 17:33:02 -04005966 return contentView;
5967 }
5968
Selim Cinek7b9605b2017-01-19 17:36:00 -08005969 private CharSequence makeMessageLine(Message m, Builder builder) {
Adrian Roosc1a80b02016-04-05 14:54:55 -07005970 BidiFormatter bidi = BidiFormatter.getInstance();
5971 SpannableStringBuilder sb = new SpannableStringBuilder();
Selim Cinek7b9605b2017-01-19 17:36:00 -08005972 boolean colorize = builder.isColorized();
Adrian Roosc1a80b02016-04-05 14:54:55 -07005973 if (TextUtils.isEmpty(m.mSender)) {
5974 CharSequence replyName = mUserDisplayName == null ? "" : mUserDisplayName;
5975 sb.append(bidi.unicodeWrap(replyName),
Selim Cinek7b9605b2017-01-19 17:36:00 -08005976 makeFontColorSpan(colorize
5977 ? builder.getPrimaryTextColor()
5978 : mBuilder.resolveContrastColor()),
Adrian Roosc1a80b02016-04-05 14:54:55 -07005979 0 /* flags */);
5980 } else {
5981 sb.append(bidi.unicodeWrap(m.mSender),
Selim Cinek7b9605b2017-01-19 17:36:00 -08005982 makeFontColorSpan(colorize
5983 ? builder.getPrimaryTextColor()
5984 : Color.BLACK),
Adrian Roosc1a80b02016-04-05 14:54:55 -07005985 0 /* flags */);
5986 }
5987 CharSequence text = m.mText == null ? "" : m.mText;
5988 sb.append(" ").append(bidi.unicodeWrap(text));
5989 return sb;
5990 }
5991
Adrian Roosdedd1df2016-04-26 16:38:47 -07005992 /**
5993 * @hide
5994 */
5995 @Override
Selim Cinek87ed69b2017-02-09 15:59:43 -08005996 public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
5997 if (increasedHeight) {
5998 return makeBigContentView();
5999 }
Adrian Roosdedd1df2016-04-26 16:38:47 -07006000 Message m = findLatestIncomingMessage();
6001 CharSequence title = mConversationTitle != null
6002 ? mConversationTitle
6003 : (m == null) ? null : m.mSender;
6004 CharSequence text = (m == null)
6005 ? null
Selim Cinek7b9605b2017-01-19 17:36:00 -08006006 : mConversationTitle != null ? makeMessageLine(m, mBuilder) : m.mText;
Adrian Roosdedd1df2016-04-26 16:38:47 -07006007
6008 return mBuilder.applyStandardTemplateWithActions(mBuilder.getBigBaseLayoutResource(),
Adrian Roos70d7aa32017-01-11 15:39:06 -08006009 mBuilder.mParams.reset().hasProgress(false).title(title).text(text));
Adrian Roosdedd1df2016-04-26 16:38:47 -07006010 }
6011
Adrian Roosc1a80b02016-04-05 14:54:55 -07006012 private static TextAppearanceSpan makeFontColorSpan(int color) {
6013 return new TextAppearanceSpan(null, 0, 0,
6014 ColorStateList.valueOf(color), null);
6015 }
6016
Alex Hillsd9b04d92016-04-11 16:38:16 -04006017 public static final class Message {
6018
6019 static final String KEY_TEXT = "text";
6020 static final String KEY_TIMESTAMP = "time";
6021 static final String KEY_SENDER = "sender";
6022 static final String KEY_DATA_MIME_TYPE = "type";
6023 static final String KEY_DATA_URI= "uri";
Shane Brennan5a871862017-03-11 13:14:17 -08006024 static final String KEY_EXTRAS_BUNDLE = "extras";
Alex Hillsfc737de2016-03-23 17:33:02 -04006025
6026 private final CharSequence mText;
6027 private final long mTimestamp;
6028 private final CharSequence mSender;
6029
Shane Brennan5a871862017-03-11 13:14:17 -08006030 private Bundle mExtras = new Bundle();
Alex Hillsfc737de2016-03-23 17:33:02 -04006031 private String mDataMimeType;
6032 private Uri mDataUri;
6033
6034 /**
6035 * Constructor
6036 * @param text A {@link CharSequence} to be displayed as the message content
6037 * @param timestamp Time at which the message arrived
6038 * @param sender A {@link CharSequence} to be used for displaying the name of the
6039 * sender. Should be <code>null</code> for messages by the current user, in which case
6040 * the platform will insert {@link MessagingStyle#getUserDisplayName()}.
6041 * Should be unique amongst all individuals in the conversation, and should be
6042 * consistent during re-posts of the notification.
6043 */
6044 public Message(CharSequence text, long timestamp, CharSequence sender){
6045 mText = text;
6046 mTimestamp = timestamp;
6047 mSender = sender;
6048 }
6049
6050 /**
6051 * Sets a binary blob of data and an associated MIME type for a message. In the case
6052 * where the platform doesn't support the MIME type, the original text provided in the
6053 * constructor will be used.
6054 * @param dataMimeType The MIME type of the content. See
6055 * <a href="{@docRoot}notifications/messaging.html"> for the list of supported MIME
6056 * types on Android and Android Wear.
6057 * @param dataUri The uri containing the content whose type is given by the MIME type.
6058 * <p class="note">
6059 * <ol>
6060 * <li>Notification Listeners including the System UI need permission to access the
6061 * data the Uri points to. The recommended ways to do this are:</li>
6062 * <li>Store the data in your own ContentProvider, making sure that other apps have
6063 * the correct permission to access your provider. The preferred mechanism for
6064 * providing access is to use per-URI permissions which are temporary and only
6065 * grant access to the receiving application. An easy way to create a
6066 * ContentProvider like this is to use the FileProvider helper class.</li>
6067 * <li>Use the system MediaStore. The MediaStore is primarily aimed at video, audio
6068 * and image MIME types, however beginning with Android 3.0 (API level 11) it can
6069 * also store non-media types (see MediaStore.Files for more info). Files can be
6070 * inserted into the MediaStore using scanFile() after which a content:// style
6071 * Uri suitable for sharing is passed to the provided onScanCompleted() callback.
6072 * Note that once added to the system MediaStore the content is accessible to any
6073 * app on the device.</li>
6074 * </ol>
6075 * @return this object for method chaining
6076 */
6077 public Message setData(String dataMimeType, Uri dataUri) {
6078 mDataMimeType = dataMimeType;
6079 mDataUri = dataUri;
6080 return this;
6081 }
6082
Alex Hillsfc737de2016-03-23 17:33:02 -04006083 /**
6084 * Get the text to be used for this message, or the fallback text if a type and content
6085 * Uri have been set
6086 */
6087 public CharSequence getText() {
6088 return mText;
6089 }
6090
6091 /**
6092 * Get the time at which this message arrived
6093 */
6094 public long getTimestamp() {
6095 return mTimestamp;
6096 }
6097
6098 /**
Shane Brennan5a871862017-03-11 13:14:17 -08006099 * Get the extras Bundle for this message.
6100 */
6101 public Bundle getExtras() {
6102 return mExtras;
6103 }
6104
6105 /**
Alex Hillsfc737de2016-03-23 17:33:02 -04006106 * Get the text used to display the contact's name in the messaging experience
6107 */
6108 public CharSequence getSender() {
6109 return mSender;
6110 }
6111
6112 /**
6113 * Get the MIME type of the data pointed to by the Uri
6114 */
6115 public String getDataMimeType() {
6116 return mDataMimeType;
6117 }
6118
6119 /**
6120 * Get the the Uri pointing to the content of the message. Can be null, in which case
6121 * {@see #getText()} is used.
6122 */
6123 public Uri getDataUri() {
6124 return mDataUri;
6125 }
6126
Alex Hillsd9b04d92016-04-11 16:38:16 -04006127 private Bundle toBundle() {
6128 Bundle bundle = new Bundle();
Alex Hillsfc737de2016-03-23 17:33:02 -04006129 if (mText != null) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04006130 bundle.putCharSequence(KEY_TEXT, mText);
Alex Hillsfc737de2016-03-23 17:33:02 -04006131 }
Alex Hillsd9b04d92016-04-11 16:38:16 -04006132 bundle.putLong(KEY_TIMESTAMP, mTimestamp);
Alex Hillsfc737de2016-03-23 17:33:02 -04006133 if (mSender != null) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04006134 bundle.putCharSequence(KEY_SENDER, mSender);
Alex Hillsfc737de2016-03-23 17:33:02 -04006135 }
6136 if (mDataMimeType != null) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04006137 bundle.putString(KEY_DATA_MIME_TYPE, mDataMimeType);
Alex Hillsfc737de2016-03-23 17:33:02 -04006138 }
6139 if (mDataUri != null) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04006140 bundle.putParcelable(KEY_DATA_URI, mDataUri);
Alex Hillsfc737de2016-03-23 17:33:02 -04006141 }
Shane Brennan5a871862017-03-11 13:14:17 -08006142 if (mExtras != null) {
6143 bundle.putBundle(KEY_EXTRAS_BUNDLE, mExtras);
6144 }
Alex Hillsd9b04d92016-04-11 16:38:16 -04006145 return bundle;
Alex Hillsfc737de2016-03-23 17:33:02 -04006146 }
6147
Alex Hillsd9b04d92016-04-11 16:38:16 -04006148 static Bundle[] getBundleArrayForMessages(List<Message> messages) {
6149 Bundle[] bundles = new Bundle[messages.size()];
6150 final int N = messages.size();
6151 for (int i = 0; i < N; i++) {
6152 bundles[i] = messages.get(i).toBundle();
6153 }
6154 return bundles;
6155 }
6156
Adrian Roosdedd1df2016-04-26 16:38:47 -07006157 static List<Message> getMessagesFromBundleArray(Parcelable[] bundles) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04006158 List<Message> messages = new ArrayList<>(bundles.length);
6159 for (int i = 0; i < bundles.length; i++) {
Adrian Roosdedd1df2016-04-26 16:38:47 -07006160 if (bundles[i] instanceof Bundle) {
6161 Message message = getMessageFromBundle((Bundle)bundles[i]);
6162 if (message != null) {
6163 messages.add(message);
6164 }
Alex Hillsd9b04d92016-04-11 16:38:16 -04006165 }
6166 }
6167 return messages;
6168 }
6169
6170 static Message getMessageFromBundle(Bundle bundle) {
6171 try {
Adrian Roosfbddd2c2016-05-13 12:57:20 -07006172 if (!bundle.containsKey(KEY_TEXT) || !bundle.containsKey(KEY_TIMESTAMP)) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04006173 return null;
6174 } else {
6175 Message message = new Message(bundle.getCharSequence(KEY_TEXT),
6176 bundle.getLong(KEY_TIMESTAMP), bundle.getCharSequence(KEY_SENDER));
6177 if (bundle.containsKey(KEY_DATA_MIME_TYPE) &&
6178 bundle.containsKey(KEY_DATA_URI)) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04006179 message.setData(bundle.getString(KEY_DATA_MIME_TYPE),
6180 (Uri) bundle.getParcelable(KEY_DATA_URI));
Alex Hillsfc737de2016-03-23 17:33:02 -04006181 }
Shane Brennan5a871862017-03-11 13:14:17 -08006182 if (bundle.containsKey(KEY_EXTRAS_BUNDLE)) {
6183 message.getExtras().putAll(bundle.getBundle(KEY_EXTRAS_BUNDLE));
6184 }
Alex Hillsd9b04d92016-04-11 16:38:16 -04006185 return message;
6186 }
6187 } catch (ClassCastException e) {
6188 return null;
6189 }
6190 }
Alex Hillsfc737de2016-03-23 17:33:02 -04006191 }
6192 }
6193
6194 /**
Daniel Sandler879c5e02012-04-17 16:46:51 -04006195 * Helper class for generating large-format notifications that include a list of (up to 5) strings.
Joe Malin8d40d042012-11-05 11:36:40 -08006196 *
Robert Ly91c5ce32014-06-08 15:37:00 -07006197 * Here's how you'd set the <code>InboxStyle</code> on a notification:
Daniel Sandler879c5e02012-04-17 16:46:51 -04006198 * <pre class="prettyprint">
Robert Ly91c5ce32014-06-08 15:37:00 -07006199 * Notification notif = new Notification.Builder(mContext)
6200 * .setContentTitle(&quot;5 New mails from &quot; + sender.toString())
6201 * .setContentText(subject)
6202 * .setSmallIcon(R.drawable.new_mail)
6203 * .setLargeIcon(aBitmap)
6204 * .setStyle(new Notification.InboxStyle()
6205 * .addLine(str1)
6206 * .addLine(str2)
6207 * .setContentTitle(&quot;&quot;)
6208 * .setSummaryText(&quot;+3 more&quot;))
6209 * .build();
Daniel Sandler879c5e02012-04-17 16:46:51 -04006210 * </pre>
Joe Malin8d40d042012-11-05 11:36:40 -08006211 *
Daniel Sandler879c5e02012-04-17 16:46:51 -04006212 * @see Notification#bigContentView
6213 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04006214 public static class InboxStyle extends Style {
Daniel Sandler879c5e02012-04-17 16:46:51 -04006215 private ArrayList<CharSequence> mTexts = new ArrayList<CharSequence>(5);
6216
Chris Wrenfbd96ba2012-05-01 12:03:58 -04006217 public InboxStyle() {
6218 }
6219
Adrian Roosf5faf9d2016-05-23 13:56:15 -07006220 /**
6221 * @deprecated use {@code InboxStyle()}.
6222 */
6223 @Deprecated
Daniel Sandler879c5e02012-04-17 16:46:51 -04006224 public InboxStyle(Builder builder) {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04006225 setBuilder(builder);
Daniel Sandler879c5e02012-04-17 16:46:51 -04006226 }
6227
Chris Wrend6297db2012-05-03 16:20:13 -04006228 /**
6229 * Overrides ContentTitle in the big form of the template.
6230 * This defaults to the value passed to setContentTitle().
6231 */
6232 public InboxStyle setBigContentTitle(CharSequence title) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04006233 internalSetBigContentTitle(safeCharSequence(title));
Chris Wrend6297db2012-05-03 16:20:13 -04006234 return this;
6235 }
6236
6237 /**
6238 * Set the first line of text after the detail section in the big form of the template.
6239 */
6240 public InboxStyle setSummaryText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04006241 internalSetSummaryText(safeCharSequence(cs));
Chris Wrend6297db2012-05-03 16:20:13 -04006242 return this;
6243 }
6244
Chris Wren0bd664d2012-08-01 13:56:56 -04006245 /**
6246 * Append a line to the digest section of the Inbox notification.
6247 */
Daniel Sandler879c5e02012-04-17 16:46:51 -04006248 public InboxStyle addLine(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04006249 mTexts.add(safeCharSequence(cs));
Daniel Sandler879c5e02012-04-17 16:46:51 -04006250 return this;
6251 }
6252
Daniel Sandlerf45564e2013-04-15 15:05:08 -04006253 /**
6254 * @hide
6255 */
6256 public void addExtras(Bundle extras) {
6257 super.addExtras(extras);
Christoph Studer4600f9b2014-07-22 22:44:43 +02006258
Daniel Sandlerf45564e2013-04-15 15:05:08 -04006259 CharSequence[] a = new CharSequence[mTexts.size()];
6260 extras.putCharSequenceArray(EXTRA_TEXT_LINES, mTexts.toArray(a));
6261 }
6262
Christoph Studer4600f9b2014-07-22 22:44:43 +02006263 /**
6264 * @hide
6265 */
6266 @Override
6267 protected void restoreFromExtras(Bundle extras) {
6268 super.restoreFromExtras(extras);
6269
6270 mTexts.clear();
6271 if (extras.containsKey(EXTRA_TEXT_LINES)) {
6272 Collections.addAll(mTexts, extras.getCharSequenceArray(EXTRA_TEXT_LINES));
6273 }
6274 }
6275
Julia Reynoldsd9228f12015-10-20 10:37:27 -04006276 /**
6277 * @hide
6278 */
6279 public RemoteViews makeBigContentView() {
Selim Cinekc848c3a2016-01-13 15:27:30 -08006280 // Remove the content text so it disappears unless you have a summary
Christoph Studer4600f9b2014-07-22 22:44:43 +02006281 // Nasty
Julia Reynoldsd9228f12015-10-20 10:37:27 -04006282 CharSequence oldBuilderContentText = mBuilder.mN.extras.getCharSequence(EXTRA_TEXT);
6283 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, null);
Christoph Studer4600f9b2014-07-22 22:44:43 +02006284
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01006285 RemoteViews contentView = getStandardView(mBuilder.getInboxLayoutResource());
Daniel Sandler619738c2012-06-07 16:33:08 -04006286
Julia Reynoldsd9228f12015-10-20 10:37:27 -04006287 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, oldBuilderContentText);
Christoph Studer4600f9b2014-07-22 22:44:43 +02006288
Chris Wrend6297db2012-05-03 16:20:13 -04006289 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 -04006290 R.id.inbox_text4, R.id.inbox_text5, R.id.inbox_text6};
Chris Wrend6297db2012-05-03 16:20:13 -04006291
Chris Wren4ed80d52012-05-17 09:30:03 -04006292 // Make sure all rows are gone in case we reuse a view.
6293 for (int rowId : rowIds) {
6294 contentView.setViewVisibility(rowId, View.GONE);
6295 }
6296
Daniel Sandler879c5e02012-04-17 16:46:51 -04006297 int i=0;
Selim Cinek07c80172016-04-21 16:40:47 -07006298 int topPadding = mBuilder.mContext.getResources().getDimensionPixelSize(
6299 R.dimen.notification_inbox_item_top_padding);
Selim Cinek247fa012016-02-18 09:50:48 -08006300 boolean first = true;
Selim Cinek07c80172016-04-21 16:40:47 -07006301 int onlyViewId = 0;
6302 int maxRows = rowIds.length;
6303 if (mBuilder.mActions.size() > 0) {
6304 maxRows--;
6305 }
6306 while (i < mTexts.size() && i < maxRows) {
Daniel Sandler879c5e02012-04-17 16:46:51 -04006307 CharSequence str = mTexts.get(i);
Selim Cinek07c80172016-04-21 16:40:47 -07006308 if (!TextUtils.isEmpty(str)) {
Daniel Sandler879c5e02012-04-17 16:46:51 -04006309 contentView.setViewVisibility(rowIds[i], View.VISIBLE);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01006310 contentView.setTextViewText(rowIds[i], mBuilder.processLegacyText(str));
Selim Cinek7b9605b2017-01-19 17:36:00 -08006311 mBuilder.setTextViewColorSecondary(contentView, rowIds[i]);
Selim Cinek07c80172016-04-21 16:40:47 -07006312 contentView.setViewPadding(rowIds[i], 0, topPadding, 0, 0);
Selim Cinek247fa012016-02-18 09:50:48 -08006313 handleInboxImageMargin(contentView, rowIds[i], first);
Selim Cinek07c80172016-04-21 16:40:47 -07006314 if (first) {
6315 onlyViewId = rowIds[i];
6316 } else {
6317 onlyViewId = 0;
6318 }
Selim Cinek247fa012016-02-18 09:50:48 -08006319 first = false;
Daniel Sandler879c5e02012-04-17 16:46:51 -04006320 }
6321 i++;
6322 }
Selim Cinek07c80172016-04-21 16:40:47 -07006323 if (onlyViewId != 0) {
6324 // We only have 1 entry, lets make it look like the normal Text of a Bigtext
6325 topPadding = mBuilder.mContext.getResources().getDimensionPixelSize(
6326 R.dimen.notification_text_margin_top);
6327 contentView.setViewPadding(onlyViewId, 0, topPadding, 0, 0);
6328 }
Selim Cinek1e0bf612015-11-20 15:57:26 -08006329
Daniel Sandler879c5e02012-04-17 16:46:51 -04006330 return contentView;
6331 }
Selim Cinek1e0bf612015-11-20 15:57:26 -08006332
Selim Cinek247fa012016-02-18 09:50:48 -08006333 private void handleInboxImageMargin(RemoteViews contentView, int id, boolean first) {
Selim Cinek1e0bf612015-11-20 15:57:26 -08006334 int endMargin = 0;
Selim Cinek247fa012016-02-18 09:50:48 -08006335 if (first) {
6336 final int max = mBuilder.mN.extras.getInt(EXTRA_PROGRESS_MAX, 0);
6337 final boolean ind = mBuilder.mN.extras.getBoolean(EXTRA_PROGRESS_INDETERMINATE);
6338 boolean hasProgress = max != 0 || ind;
Selim Cinek279fa862016-06-14 10:57:25 -07006339 if (mBuilder.mN.hasLargeIcon() && !hasProgress) {
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006340 endMargin = R.dimen.notification_content_picture_margin;
Selim Cinek247fa012016-02-18 09:50:48 -08006341 }
Selim Cinek1e0bf612015-11-20 15:57:26 -08006342 }
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006343 contentView.setViewLayoutMarginEndDimen(id, endMargin);
Selim Cinek1e0bf612015-11-20 15:57:26 -08006344 }
Daniel Sandler879c5e02012-04-17 16:46:51 -04006345 }
Dan Sandler842dd772014-05-15 09:36:47 -04006346
6347 /**
6348 * Notification style for media playback notifications.
6349 *
6350 * In the expanded form, {@link Notification#bigContentView}, up to 5
6351 * {@link Notification.Action}s specified with
Dan Sandler86647982015-05-13 23:41:13 -04006352 * {@link Notification.Builder#addAction(Action) addAction} will be
Dan Sandler842dd772014-05-15 09:36:47 -04006353 * shown as icon-only pushbuttons, suitable for transport controls. The Bitmap given to
6354 * {@link Notification.Builder#setLargeIcon(android.graphics.Bitmap) setLargeIcon()} will be
6355 * treated as album artwork.
Selim Cinek99104832017-01-25 14:47:33 -08006356 * <p>
Dan Sandler842dd772014-05-15 09:36:47 -04006357 * Unlike the other styles provided here, MediaStyle can also modify the standard-size
6358 * {@link Notification#contentView}; by providing action indices to
Christoph Studerfde6f4d2014-12-12 13:23:26 +01006359 * {@link #setShowActionsInCompactView(int...)} you can promote up to 3 actions to be displayed
Dan Sandler842dd772014-05-15 09:36:47 -04006360 * in the standard view alongside the usual content.
Selim Cinek99104832017-01-25 14:47:33 -08006361 * <p>
Bryan Mawhinney6be8de32014-07-18 10:35:12 +01006362 * Notifications created with MediaStyle will have their category set to
6363 * {@link Notification#CATEGORY_TRANSPORT CATEGORY_TRANSPORT} unless you set a different
6364 * category using {@link Notification.Builder#setCategory(String) setCategory()}.
Selim Cinek99104832017-01-25 14:47:33 -08006365 * <p>
Jeff Browndba34ba2014-06-24 20:46:03 -07006366 * Finally, if you attach a {@link android.media.session.MediaSession.Token} using
6367 * {@link android.app.Notification.MediaStyle#setMediaSession(MediaSession.Token)},
Dan Sandler842dd772014-05-15 09:36:47 -04006368 * the System UI can identify this as a notification representing an active media session
6369 * and respond accordingly (by showing album artwork in the lockscreen, for example).
6370 *
Selim Cinek99104832017-01-25 14:47:33 -08006371 * <p>
6372 * Starting at {@link android.os.Build.VERSION_CODES#O Android O} any notification that has a
6373 * media session attached with {@link #setMediaSession(MediaSession.Token)} will be colorized.
6374 * You can opt-out of this behavior by using {@link Notification.Builder#setColorized(boolean)}.
6375 * <p>
6376 *
Dan Sandler842dd772014-05-15 09:36:47 -04006377 * To use this style with your Notification, feed it to
6378 * {@link Notification.Builder#setStyle(android.app.Notification.Style)} like so:
6379 * <pre class="prettyprint">
6380 * Notification noti = new Notification.Builder()
6381 * .setSmallIcon(R.drawable.ic_stat_player)
Christoph Studere935fe92014-11-24 14:18:06 +01006382 * .setContentTitle(&quot;Track title&quot;)
6383 * .setContentText(&quot;Artist - Album&quot;)
6384 * .setLargeIcon(albumArtBitmap))
Bryan Mawhinney6be8de32014-07-18 10:35:12 +01006385 * .setStyle(<b>new Notification.MediaStyle()</b>
6386 * .setMediaSession(mySession))
Dan Sandler842dd772014-05-15 09:36:47 -04006387 * .build();
6388 * </pre>
6389 *
6390 * @see Notification#bigContentView
Selim Cinek99104832017-01-25 14:47:33 -08006391 * @see Notification.Builder#setColorized(boolean)
Dan Sandler842dd772014-05-15 09:36:47 -04006392 */
6393 public static class MediaStyle extends Style {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006394 static final int MAX_MEDIA_BUTTONS_IN_COMPACT = 3;
Dan Sandler842dd772014-05-15 09:36:47 -04006395 static final int MAX_MEDIA_BUTTONS = 5;
6396
6397 private int[] mActionsToShowInCompact = null;
Jeff Browndba34ba2014-06-24 20:46:03 -07006398 private MediaSession.Token mToken;
Dan Sandler842dd772014-05-15 09:36:47 -04006399
6400 public MediaStyle() {
6401 }
6402
Adrian Roosf5faf9d2016-05-23 13:56:15 -07006403 /**
6404 * @deprecated use {@code MediaStyle()}.
6405 */
6406 @Deprecated
Dan Sandler842dd772014-05-15 09:36:47 -04006407 public MediaStyle(Builder builder) {
6408 setBuilder(builder);
6409 }
6410
6411 /**
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006412 * 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 -04006413 * notification view.
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006414 *
6415 * @param actions the indices of the actions to show in the compact notification view
Dan Sandler842dd772014-05-15 09:36:47 -04006416 */
6417 public MediaStyle setShowActionsInCompactView(int...actions) {
6418 mActionsToShowInCompact = actions;
6419 return this;
6420 }
6421
6422 /**
Jeff Browndba34ba2014-06-24 20:46:03 -07006423 * Attach a {@link android.media.session.MediaSession.Token} to this Notification
6424 * to provide additional playback information and control to the SystemUI.
Dan Sandler842dd772014-05-15 09:36:47 -04006425 */
Jeff Browndba34ba2014-06-24 20:46:03 -07006426 public MediaStyle setMediaSession(MediaSession.Token token) {
Dan Sandler842dd772014-05-15 09:36:47 -04006427 mToken = token;
6428 return this;
6429 }
6430
Christoph Studer4600f9b2014-07-22 22:44:43 +02006431 /**
6432 * @hide
6433 */
Dan Sandler842dd772014-05-15 09:36:47 -04006434 @Override
6435 public Notification buildStyled(Notification wip) {
Christoph Studer4600f9b2014-07-22 22:44:43 +02006436 super.buildStyled(wip);
Bryan Mawhinney6be8de32014-07-18 10:35:12 +01006437 if (wip.category == null) {
6438 wip.category = Notification.CATEGORY_TRANSPORT;
6439 }
Dan Sandler842dd772014-05-15 09:36:47 -04006440 return wip;
6441 }
6442
Christoph Studer4600f9b2014-07-22 22:44:43 +02006443 /**
6444 * @hide
6445 */
6446 @Override
Selim Cinek7d1009b2017-01-25 15:28:28 -08006447 public RemoteViews makeContentView(boolean increasedHeight) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04006448 return makeMediaContentView();
Christoph Studer4600f9b2014-07-22 22:44:43 +02006449 }
6450
6451 /**
6452 * @hide
6453 */
6454 @Override
Julia Reynoldsd9228f12015-10-20 10:37:27 -04006455 public RemoteViews makeBigContentView() {
6456 return makeMediaBigContentView();
Christoph Studer4600f9b2014-07-22 22:44:43 +02006457 }
6458
Selim Cinekcc10bfb2016-02-10 16:24:21 -08006459 /**
6460 * @hide
6461 */
6462 @Override
Selim Cinek87ed69b2017-02-09 15:59:43 -08006463 public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
Selim Cinekcc10bfb2016-02-10 16:24:21 -08006464 RemoteViews expanded = makeMediaBigContentView();
6465 return expanded != null ? expanded : makeMediaContentView();
6466 }
6467
Dan Sandler842dd772014-05-15 09:36:47 -04006468 /** @hide */
6469 @Override
6470 public void addExtras(Bundle extras) {
6471 super.addExtras(extras);
6472
6473 if (mToken != null) {
6474 extras.putParcelable(EXTRA_MEDIA_SESSION, mToken);
6475 }
Bryan Mawhinneye191f902014-07-22 12:50:09 +01006476 if (mActionsToShowInCompact != null) {
6477 extras.putIntArray(EXTRA_COMPACT_ACTIONS, mActionsToShowInCompact);
6478 }
Dan Sandler842dd772014-05-15 09:36:47 -04006479 }
6480
Christoph Studer4600f9b2014-07-22 22:44:43 +02006481 /**
6482 * @hide
6483 */
6484 @Override
6485 protected void restoreFromExtras(Bundle extras) {
6486 super.restoreFromExtras(extras);
6487
6488 if (extras.containsKey(EXTRA_MEDIA_SESSION)) {
6489 mToken = extras.getParcelable(EXTRA_MEDIA_SESSION);
6490 }
6491 if (extras.containsKey(EXTRA_COMPACT_ACTIONS)) {
6492 mActionsToShowInCompact = extras.getIntArray(EXTRA_COMPACT_ACTIONS);
6493 }
6494 }
6495
Selim Cinek5bf069a2015-11-10 19:14:27 -05006496 private RemoteViews generateMediaActionButton(Action action, int color) {
Dan Sandler842dd772014-05-15 09:36:47 -04006497 final boolean tombstone = (action.actionIntent == null);
Selim Cinekf33b1112015-07-15 17:45:11 -07006498 RemoteViews button = new BuilderRemoteViews(mBuilder.mContext.getApplicationInfo(),
Alan Viverette3cb07a462014-06-06 14:19:53 -07006499 R.layout.notification_material_media_action);
Dan Sandler68079d52015-07-22 10:45:30 -04006500 button.setImageViewIcon(R.id.action0, action.getIcon());
Selim Cinek5bf069a2015-11-10 19:14:27 -05006501 button.setDrawableParameters(R.id.action0, false, -1, color, PorterDuff.Mode.SRC_ATOP,
6502 -1);
Dan Sandler842dd772014-05-15 09:36:47 -04006503 if (!tombstone) {
6504 button.setOnClickPendingIntent(R.id.action0, action.actionIntent);
6505 }
6506 button.setContentDescription(R.id.action0, action.title);
6507 return button;
6508 }
6509
6510 private RemoteViews makeMediaContentView() {
6511 RemoteViews view = mBuilder.applyStandardTemplate(
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006512 R.layout.notification_template_material_media, false /* hasProgress */);
Dan Sandler842dd772014-05-15 09:36:47 -04006513
6514 final int numActions = mBuilder.mActions.size();
6515 final int N = mActionsToShowInCompact == null
6516 ? 0
6517 : Math.min(mActionsToShowInCompact.length, MAX_MEDIA_BUTTONS_IN_COMPACT);
6518 if (N > 0) {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006519 view.removeAllViews(com.android.internal.R.id.media_actions);
Dan Sandler842dd772014-05-15 09:36:47 -04006520 for (int i = 0; i < N; i++) {
6521 if (i >= numActions) {
6522 throw new IllegalArgumentException(String.format(
6523 "setShowActionsInCompactView: action %d out of bounds (max %d)",
6524 i, numActions - 1));
6525 }
6526
6527 final Action action = mBuilder.mActions.get(mActionsToShowInCompact[i]);
Selim Cinek5bf069a2015-11-10 19:14:27 -05006528 final RemoteViews button = generateMediaActionButton(action,
Selim Cinek99104832017-01-25 14:47:33 -08006529 getPrimaryHighlightColor());
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006530 view.addView(com.android.internal.R.id.media_actions, button);
Dan Sandler842dd772014-05-15 09:36:47 -04006531 }
6532 }
Selim Cinekfdc738f2016-01-27 20:04:27 -08006533 handleImage(view);
6534 // handle the content margin
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006535 int endMargin = R.dimen.notification_content_margin_end;
Selim Cinek279fa862016-06-14 10:57:25 -07006536 if (mBuilder.mN.hasLargeIcon()) {
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006537 endMargin = R.dimen.notification_content_plus_picture_margin_end;
Selim Cinekfdc738f2016-01-27 20:04:27 -08006538 }
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006539 view.setViewLayoutMarginEndDimen(R.id.notification_main_column, endMargin);
Dan Sandler842dd772014-05-15 09:36:47 -04006540 return view;
6541 }
6542
Selim Cinek99104832017-01-25 14:47:33 -08006543 private int getPrimaryHighlightColor() {
6544 return mBuilder.getPrimaryHighlightColor();
6545 }
6546
Dan Sandler842dd772014-05-15 09:36:47 -04006547 private RemoteViews makeMediaBigContentView() {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006548 final int actionCount = Math.min(mBuilder.mActions.size(), MAX_MEDIA_BUTTONS);
Selim Cinekcc10bfb2016-02-10 16:24:21 -08006549 // Dont add an expanded view if there is no more content to be revealed
6550 int actionsInCompact = mActionsToShowInCompact == null
6551 ? 0
6552 : Math.min(mActionsToShowInCompact.length, MAX_MEDIA_BUTTONS_IN_COMPACT);
Selim Cinek279fa862016-06-14 10:57:25 -07006553 if (!mBuilder.mN.hasLargeIcon() && actionCount <= actionsInCompact) {
Selim Cinekcc10bfb2016-02-10 16:24:21 -08006554 return null;
6555 }
Selim Cinek5bf069a2015-11-10 19:14:27 -05006556 RemoteViews big = mBuilder.applyStandardTemplate(
6557 R.layout.notification_template_material_big_media,
6558 false);
Dan Sandler842dd772014-05-15 09:36:47 -04006559
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006560 if (actionCount > 0) {
6561 big.removeAllViews(com.android.internal.R.id.media_actions);
6562 for (int i = 0; i < actionCount; i++) {
Selim Cinek5bf069a2015-11-10 19:14:27 -05006563 final RemoteViews button = generateMediaActionButton(mBuilder.mActions.get(i),
Selim Cinek99104832017-01-25 14:47:33 -08006564 getPrimaryHighlightColor());
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006565 big.addView(com.android.internal.R.id.media_actions, button);
Dan Sandler842dd772014-05-15 09:36:47 -04006566 }
6567 }
Selim Cinek5bf069a2015-11-10 19:14:27 -05006568 handleImage(big);
Dan Sandler842dd772014-05-15 09:36:47 -04006569 return big;
6570 }
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006571
Selim Cinek5bf069a2015-11-10 19:14:27 -05006572 private void handleImage(RemoteViews contentView) {
Selim Cinek279fa862016-06-14 10:57:25 -07006573 if (mBuilder.mN.hasLargeIcon()) {
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006574 contentView.setViewLayoutMarginEndDimen(R.id.line1, 0);
6575 contentView.setViewLayoutMarginEndDimen(R.id.text, 0);
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006576 }
6577 }
6578
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006579 /**
6580 * @hide
6581 */
6582 @Override
6583 protected boolean hasProgress() {
6584 return false;
6585 }
Dan Sandler842dd772014-05-15 09:36:47 -04006586 }
Griff Hazen61a9e862014-05-22 16:05:19 -07006587
Selim Cinek593610c2016-02-16 18:42:57 -08006588 /**
6589 * Notification style for custom views that are decorated by the system
6590 *
6591 * <p>Instead of providing a notification that is completely custom, a developer can set this
6592 * style and still obtain system decorations like the notification header with the expand
6593 * affordance and actions.
6594 *
6595 * <p>Use {@link android.app.Notification.Builder#setCustomContentView(RemoteViews)},
6596 * {@link android.app.Notification.Builder#setCustomBigContentView(RemoteViews)} and
6597 * {@link android.app.Notification.Builder#setCustomHeadsUpContentView(RemoteViews)} to set the
6598 * corresponding custom views to display.
6599 *
6600 * To use this style with your Notification, feed it to
6601 * {@link Notification.Builder#setStyle(android.app.Notification.Style)} like so:
6602 * <pre class="prettyprint">
6603 * Notification noti = new Notification.Builder()
6604 * .setSmallIcon(R.drawable.ic_stat_player)
6605 * .setLargeIcon(albumArtBitmap))
6606 * .setCustomContentView(contentView);
6607 * .setStyle(<b>new Notification.DecoratedCustomViewStyle()</b>)
6608 * .build();
6609 * </pre>
6610 */
6611 public static class DecoratedCustomViewStyle extends Style {
6612
6613 public DecoratedCustomViewStyle() {
6614 }
6615
Selim Cinek593610c2016-02-16 18:42:57 -08006616 /**
6617 * @hide
6618 */
6619 public boolean displayCustomViewInline() {
6620 return true;
6621 }
6622
6623 /**
6624 * @hide
6625 */
6626 @Override
Selim Cinek7d1009b2017-01-25 15:28:28 -08006627 public RemoteViews makeContentView(boolean increasedHeight) {
Selim Cinek593610c2016-02-16 18:42:57 -08006628 return makeStandardTemplateWithCustomContent(mBuilder.mN.contentView);
6629 }
6630
6631 /**
6632 * @hide
6633 */
6634 @Override
6635 public RemoteViews makeBigContentView() {
6636 return makeDecoratedBigContentView();
6637 }
6638
6639 /**
6640 * @hide
6641 */
6642 @Override
Selim Cinek87ed69b2017-02-09 15:59:43 -08006643 public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
Selim Cinek593610c2016-02-16 18:42:57 -08006644 return makeDecoratedHeadsUpContentView();
6645 }
6646
Selim Cinek593610c2016-02-16 18:42:57 -08006647 private RemoteViews makeDecoratedHeadsUpContentView() {
6648 RemoteViews headsUpContentView = mBuilder.mN.headsUpContentView == null
6649 ? mBuilder.mN.contentView
6650 : mBuilder.mN.headsUpContentView;
6651 if (mBuilder.mActions.size() == 0) {
6652 return makeStandardTemplateWithCustomContent(headsUpContentView);
6653 }
6654 RemoteViews remoteViews = mBuilder.applyStandardTemplateWithActions(
6655 mBuilder.getBigBaseLayoutResource());
Selim Cinek247fa012016-02-18 09:50:48 -08006656 buildIntoRemoteViewContent(remoteViews, headsUpContentView);
Selim Cinek593610c2016-02-16 18:42:57 -08006657 return remoteViews;
6658 }
6659
Selim Cinek593610c2016-02-16 18:42:57 -08006660 private RemoteViews makeStandardTemplateWithCustomContent(RemoteViews customContent) {
6661 RemoteViews remoteViews = mBuilder.applyStandardTemplate(
6662 mBuilder.getBaseLayoutResource());
Selim Cinek247fa012016-02-18 09:50:48 -08006663 buildIntoRemoteViewContent(remoteViews, customContent);
Selim Cinek593610c2016-02-16 18:42:57 -08006664 return remoteViews;
6665 }
6666
Selim Cinek593610c2016-02-16 18:42:57 -08006667 private RemoteViews makeDecoratedBigContentView() {
6668 RemoteViews bigContentView = mBuilder.mN.bigContentView == null
6669 ? mBuilder.mN.contentView
6670 : mBuilder.mN.bigContentView;
6671 if (mBuilder.mActions.size() == 0) {
6672 return makeStandardTemplateWithCustomContent(bigContentView);
6673 }
6674 RemoteViews remoteViews = mBuilder.applyStandardTemplateWithActions(
6675 mBuilder.getBigBaseLayoutResource());
Selim Cinek247fa012016-02-18 09:50:48 -08006676 buildIntoRemoteViewContent(remoteViews, bigContentView);
Selim Cinek593610c2016-02-16 18:42:57 -08006677 return remoteViews;
6678 }
Selim Cinek247fa012016-02-18 09:50:48 -08006679
6680 private void buildIntoRemoteViewContent(RemoteViews remoteViews,
6681 RemoteViews customContent) {
Adrian Roos5081c0d2016-02-26 16:04:19 -08006682 if (customContent != null) {
Selim Cinekf91017e2016-03-14 12:25:09 -07006683 // Need to clone customContent before adding, because otherwise it can no longer be
6684 // parceled independently of remoteViews.
Adrian Roos5081c0d2016-02-26 16:04:19 -08006685 customContent = customContent.clone();
Selim Cinekf91017e2016-03-14 12:25:09 -07006686 remoteViews.removeAllViews(R.id.notification_main_column);
6687 remoteViews.addView(R.id.notification_main_column, customContent);
Adrian Roos5081c0d2016-02-26 16:04:19 -08006688 }
Selim Cinek247fa012016-02-18 09:50:48 -08006689 // also update the end margin if there is an image
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006690 int endMargin = R.dimen.notification_content_margin_end;
Selim Cinek279fa862016-06-14 10:57:25 -07006691 if (mBuilder.mN.hasLargeIcon()) {
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006692 endMargin = R.dimen.notification_content_plus_picture_margin_end;
Selim Cinek247fa012016-02-18 09:50:48 -08006693 }
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006694 remoteViews.setViewLayoutMarginEndDimen(R.id.notification_main_column, endMargin);
Selim Cinek247fa012016-02-18 09:50:48 -08006695 }
Selim Cinek593610c2016-02-16 18:42:57 -08006696 }
6697
Selim Cinek03eb3b72016-02-18 10:39:45 -08006698 /**
6699 * Notification style for media custom views that are decorated by the system
6700 *
6701 * <p>Instead of providing a media notification that is completely custom, a developer can set
6702 * this style and still obtain system decorations like the notification header with the expand
6703 * affordance and actions.
6704 *
6705 * <p>Use {@link android.app.Notification.Builder#setCustomContentView(RemoteViews)},
6706 * {@link android.app.Notification.Builder#setCustomBigContentView(RemoteViews)} and
6707 * {@link android.app.Notification.Builder#setCustomHeadsUpContentView(RemoteViews)} to set the
6708 * corresponding custom views to display.
Selim Cinek99104832017-01-25 14:47:33 -08006709 * <p>
6710 * Contrary to {@link MediaStyle} a developer has to opt-in to the colorizing of the
6711 * notification by using {@link Notification.Builder#setColorized(boolean)}.
6712 * <p>
Selim Cinek03eb3b72016-02-18 10:39:45 -08006713 * To use this style with your Notification, feed it to
6714 * {@link Notification.Builder#setStyle(android.app.Notification.Style)} like so:
6715 * <pre class="prettyprint">
6716 * Notification noti = new Notification.Builder()
6717 * .setSmallIcon(R.drawable.ic_stat_player)
6718 * .setLargeIcon(albumArtBitmap))
6719 * .setCustomContentView(contentView);
6720 * .setStyle(<b>new Notification.DecoratedMediaCustomViewStyle()</b>
6721 * .setMediaSession(mySession))
6722 * .build();
6723 * </pre>
6724 *
6725 * @see android.app.Notification.DecoratedCustomViewStyle
6726 * @see android.app.Notification.MediaStyle
6727 */
6728 public static class DecoratedMediaCustomViewStyle extends MediaStyle {
6729
6730 public DecoratedMediaCustomViewStyle() {
6731 }
6732
Selim Cinek03eb3b72016-02-18 10:39:45 -08006733 /**
6734 * @hide
6735 */
6736 public boolean displayCustomViewInline() {
6737 return true;
6738 }
6739
6740 /**
6741 * @hide
6742 */
6743 @Override
Selim Cinek7d1009b2017-01-25 15:28:28 -08006744 public RemoteViews makeContentView(boolean increasedHeight) {
6745 RemoteViews remoteViews = super.makeContentView(false /* increasedHeight */);
Selim Cinek03eb3b72016-02-18 10:39:45 -08006746 return buildIntoRemoteView(remoteViews, R.id.notification_content_container,
6747 mBuilder.mN.contentView);
6748 }
6749
6750 /**
6751 * @hide
6752 */
6753 @Override
6754 public RemoteViews makeBigContentView() {
6755 RemoteViews customRemoteView = mBuilder.mN.bigContentView != null
6756 ? mBuilder.mN.bigContentView
6757 : mBuilder.mN.contentView;
6758 return makeBigContentViewWithCustomContent(customRemoteView);
6759 }
6760
6761 private RemoteViews makeBigContentViewWithCustomContent(RemoteViews customRemoteView) {
6762 RemoteViews remoteViews = super.makeBigContentView();
6763 if (remoteViews != null) {
6764 return buildIntoRemoteView(remoteViews, R.id.notification_main_column,
6765 customRemoteView);
6766 } else if (customRemoteView != mBuilder.mN.contentView){
Selim Cinek7d1009b2017-01-25 15:28:28 -08006767 remoteViews = super.makeContentView(false /* increasedHeight */);
Selim Cinek03eb3b72016-02-18 10:39:45 -08006768 return buildIntoRemoteView(remoteViews, R.id.notification_content_container,
6769 customRemoteView);
6770 } else {
6771 return null;
6772 }
6773 }
6774
6775 /**
6776 * @hide
6777 */
6778 @Override
Selim Cinek87ed69b2017-02-09 15:59:43 -08006779 public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
Selim Cinek03eb3b72016-02-18 10:39:45 -08006780 RemoteViews customRemoteView = mBuilder.mN.headsUpContentView != null
6781 ? mBuilder.mN.headsUpContentView
6782 : mBuilder.mN.contentView;
6783 return makeBigContentViewWithCustomContent(customRemoteView);
6784 }
6785
6786 private RemoteViews buildIntoRemoteView(RemoteViews remoteViews, int id,
6787 RemoteViews customContent) {
Adrian Roos5081c0d2016-02-26 16:04:19 -08006788 if (customContent != null) {
Selim Cinekf91017e2016-03-14 12:25:09 -07006789 // Need to clone customContent before adding, because otherwise it can no longer be
6790 // parceled independently of remoteViews.
Adrian Roos5081c0d2016-02-26 16:04:19 -08006791 customContent = customContent.clone();
Selim Cinekf91017e2016-03-14 12:25:09 -07006792 remoteViews.removeAllViews(id);
6793 remoteViews.addView(id, customContent);
Adrian Roos5081c0d2016-02-26 16:04:19 -08006794 }
Selim Cinek03eb3b72016-02-18 10:39:45 -08006795 return remoteViews;
6796 }
6797 }
6798
Christoph Studer4600f9b2014-07-22 22:44:43 +02006799 // When adding a new Style subclass here, don't forget to update
6800 // Builder.getNotificationStyleClass.
6801
Griff Hazen61a9e862014-05-22 16:05:19 -07006802 /**
6803 * Extender interface for use with {@link Builder#extend}. Extenders may be used to add
6804 * metadata or change options on a notification builder.
6805 */
6806 public interface Extender {
6807 /**
6808 * Apply this extender to a notification builder.
6809 * @param builder the builder to be modified.
6810 * @return the build object for chaining.
6811 */
6812 public Builder extend(Builder builder);
6813 }
6814
6815 /**
6816 * Helper class to add wearable extensions to notifications.
6817 * <p class="note"> See
6818 * <a href="{@docRoot}wear/notifications/creating.html">Creating Notifications
6819 * for Android Wear</a> for more information on how to use this class.
6820 * <p>
6821 * To create a notification with wearable extensions:
6822 * <ol>
6823 * <li>Create a {@link android.app.Notification.Builder}, setting any desired
6824 * properties.
6825 * <li>Create a {@link android.app.Notification.WearableExtender}.
6826 * <li>Set wearable-specific properties using the
6827 * {@code add} and {@code set} methods of {@link android.app.Notification.WearableExtender}.
6828 * <li>Call {@link android.app.Notification.Builder#extend} to apply the extensions to a
6829 * notification.
6830 * <li>Post the notification to the notification system with the
6831 * {@code NotificationManager.notify(...)} methods.
6832 * </ol>
6833 *
6834 * <pre class="prettyprint">
6835 * Notification notif = new Notification.Builder(mContext)
6836 * .setContentTitle(&quot;New mail from &quot; + sender.toString())
6837 * .setContentText(subject)
6838 * .setSmallIcon(R.drawable.new_mail)
6839 * .extend(new Notification.WearableExtender()
6840 * .setContentIcon(R.drawable.new_mail))
6841 * .build();
6842 * NotificationManager notificationManger =
6843 * (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
6844 * notificationManger.notify(0, notif);</pre>
6845 *
6846 * <p>Wearable extensions can be accessed on an existing notification by using the
6847 * {@code WearableExtender(Notification)} constructor,
6848 * and then using the {@code get} methods to access values.
6849 *
6850 * <pre class="prettyprint">
6851 * Notification.WearableExtender wearableExtender = new Notification.WearableExtender(
6852 * notification);
Griff Hazen14f57992014-05-26 09:07:14 -07006853 * List&lt;Notification&gt; pages = wearableExtender.getPages();</pre>
Griff Hazen61a9e862014-05-22 16:05:19 -07006854 */
6855 public static final class WearableExtender implements Extender {
6856 /**
6857 * Sentinel value for an action index that is unset.
6858 */
6859 public static final int UNSET_ACTION_INDEX = -1;
6860
6861 /**
6862 * Size value for use with {@link #setCustomSizePreset} to show this notification with
6863 * default sizing.
6864 * <p>For custom display notifications created using {@link #setDisplayIntent},
Paul Soulosaa4f4bf2015-08-04 11:59:45 -07006865 * the default is {@link #SIZE_MEDIUM}. All other notifications size automatically based
Griff Hazen61a9e862014-05-22 16:05:19 -07006866 * on their content.
6867 */
6868 public static final int SIZE_DEFAULT = 0;
6869
6870 /**
6871 * Size value for use with {@link #setCustomSizePreset} to show this notification
6872 * with an extra small size.
6873 * <p>This value is only applicable for custom display notifications created using
6874 * {@link #setDisplayIntent}.
6875 */
6876 public static final int SIZE_XSMALL = 1;
6877
6878 /**
6879 * Size value for use with {@link #setCustomSizePreset} to show this notification
6880 * with a small size.
6881 * <p>This value is only applicable for custom display notifications created using
6882 * {@link #setDisplayIntent}.
6883 */
6884 public static final int SIZE_SMALL = 2;
6885
6886 /**
6887 * Size value for use with {@link #setCustomSizePreset} to show this notification
6888 * with a medium size.
6889 * <p>This value is only applicable for custom display notifications created using
6890 * {@link #setDisplayIntent}.
6891 */
6892 public static final int SIZE_MEDIUM = 3;
6893
6894 /**
6895 * Size value for use with {@link #setCustomSizePreset} to show this notification
6896 * with a large size.
6897 * <p>This value is only applicable for custom display notifications created using
6898 * {@link #setDisplayIntent}.
6899 */
6900 public static final int SIZE_LARGE = 4;
6901
Griff Hazend5f11f92014-05-27 15:40:09 -07006902 /**
6903 * Size value for use with {@link #setCustomSizePreset} to show this notification
6904 * full screen.
6905 * <p>This value is only applicable for custom display notifications created using
6906 * {@link #setDisplayIntent}.
6907 */
6908 public static final int SIZE_FULL_SCREEN = 5;
6909
Griff Hazen5f2edfc2014-09-29 16:28:44 -07006910 /**
6911 * Sentinel value for use with {@link #setHintScreenTimeout} to keep the screen on for a
6912 * short amount of time when this notification is displayed on the screen. This
6913 * is the default value.
6914 */
6915 public static final int SCREEN_TIMEOUT_SHORT = 0;
6916
6917 /**
6918 * Sentinel value for use with {@link #setHintScreenTimeout} to keep the screen on
6919 * for a longer amount of time when this notification is displayed on the screen.
6920 */
6921 public static final int SCREEN_TIMEOUT_LONG = -1;
6922
Griff Hazen61a9e862014-05-22 16:05:19 -07006923 /** Notification extra which contains wearable extensions */
6924 private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS";
6925
Pete Gastaf6781d2014-10-07 15:17:05 -04006926 // Keys within EXTRA_WEARABLE_EXTENSIONS for wearable options.
Griff Hazen61a9e862014-05-22 16:05:19 -07006927 private static final String KEY_ACTIONS = "actions";
6928 private static final String KEY_FLAGS = "flags";
6929 private static final String KEY_DISPLAY_INTENT = "displayIntent";
6930 private static final String KEY_PAGES = "pages";
6931 private static final String KEY_BACKGROUND = "background";
6932 private static final String KEY_CONTENT_ICON = "contentIcon";
6933 private static final String KEY_CONTENT_ICON_GRAVITY = "contentIconGravity";
6934 private static final String KEY_CONTENT_ACTION_INDEX = "contentActionIndex";
6935 private static final String KEY_CUSTOM_SIZE_PRESET = "customSizePreset";
6936 private static final String KEY_CUSTOM_CONTENT_HEIGHT = "customContentHeight";
6937 private static final String KEY_GRAVITY = "gravity";
Griff Hazen5f2edfc2014-09-29 16:28:44 -07006938 private static final String KEY_HINT_SCREEN_TIMEOUT = "hintScreenTimeout";
Nadia Benbernou948627e2016-04-14 14:41:08 -04006939 private static final String KEY_DISMISSAL_ID = "dismissalId";
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04006940 private static final String KEY_BRIDGE_TAG = "bridgeTag";
Griff Hazen61a9e862014-05-22 16:05:19 -07006941
6942 // Flags bitwise-ored to mFlags
6943 private static final int FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE = 0x1;
6944 private static final int FLAG_HINT_HIDE_ICON = 1 << 1;
6945 private static final int FLAG_HINT_SHOW_BACKGROUND_ONLY = 1 << 2;
6946 private static final int FLAG_START_SCROLL_BOTTOM = 1 << 3;
Griff Hazen5f2edfc2014-09-29 16:28:44 -07006947 private static final int FLAG_HINT_AVOID_BACKGROUND_CLIPPING = 1 << 4;
Alex Hills4bcb06b2016-04-05 14:26:25 -04006948 private static final int FLAG_BIG_PICTURE_AMBIENT = 1 << 5;
Alex Hills9ab3a232016-04-05 14:54:56 -04006949 private static final int FLAG_HINT_CONTENT_INTENT_LAUNCHES_ACTIVITY = 1 << 6;
Griff Hazen61a9e862014-05-22 16:05:19 -07006950
6951 // Default value for flags integer
6952 private static final int DEFAULT_FLAGS = FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE;
6953
6954 private static final int DEFAULT_CONTENT_ICON_GRAVITY = Gravity.END;
6955 private static final int DEFAULT_GRAVITY = Gravity.BOTTOM;
6956
6957 private ArrayList<Action> mActions = new ArrayList<Action>();
6958 private int mFlags = DEFAULT_FLAGS;
6959 private PendingIntent mDisplayIntent;
6960 private ArrayList<Notification> mPages = new ArrayList<Notification>();
6961 private Bitmap mBackground;
6962 private int mContentIcon;
6963 private int mContentIconGravity = DEFAULT_CONTENT_ICON_GRAVITY;
6964 private int mContentActionIndex = UNSET_ACTION_INDEX;
6965 private int mCustomSizePreset = SIZE_DEFAULT;
6966 private int mCustomContentHeight;
6967 private int mGravity = DEFAULT_GRAVITY;
Griff Hazen5f2edfc2014-09-29 16:28:44 -07006968 private int mHintScreenTimeout;
Nadia Benbernou948627e2016-04-14 14:41:08 -04006969 private String mDismissalId;
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04006970 private String mBridgeTag;
Griff Hazen61a9e862014-05-22 16:05:19 -07006971
6972 /**
6973 * Create a {@link android.app.Notification.WearableExtender} with default
6974 * options.
6975 */
6976 public WearableExtender() {
6977 }
6978
6979 public WearableExtender(Notification notif) {
6980 Bundle wearableBundle = notif.extras.getBundle(EXTRA_WEARABLE_EXTENSIONS);
6981 if (wearableBundle != null) {
6982 List<Action> actions = wearableBundle.getParcelableArrayList(KEY_ACTIONS);
6983 if (actions != null) {
6984 mActions.addAll(actions);
6985 }
6986
6987 mFlags = wearableBundle.getInt(KEY_FLAGS, DEFAULT_FLAGS);
6988 mDisplayIntent = wearableBundle.getParcelable(KEY_DISPLAY_INTENT);
6989
6990 Notification[] pages = getNotificationArrayFromBundle(
6991 wearableBundle, KEY_PAGES);
6992 if (pages != null) {
6993 Collections.addAll(mPages, pages);
6994 }
6995
6996 mBackground = wearableBundle.getParcelable(KEY_BACKGROUND);
6997 mContentIcon = wearableBundle.getInt(KEY_CONTENT_ICON);
6998 mContentIconGravity = wearableBundle.getInt(KEY_CONTENT_ICON_GRAVITY,
6999 DEFAULT_CONTENT_ICON_GRAVITY);
7000 mContentActionIndex = wearableBundle.getInt(KEY_CONTENT_ACTION_INDEX,
7001 UNSET_ACTION_INDEX);
7002 mCustomSizePreset = wearableBundle.getInt(KEY_CUSTOM_SIZE_PRESET,
7003 SIZE_DEFAULT);
7004 mCustomContentHeight = wearableBundle.getInt(KEY_CUSTOM_CONTENT_HEIGHT);
7005 mGravity = wearableBundle.getInt(KEY_GRAVITY, DEFAULT_GRAVITY);
Griff Hazen5f2edfc2014-09-29 16:28:44 -07007006 mHintScreenTimeout = wearableBundle.getInt(KEY_HINT_SCREEN_TIMEOUT);
Nadia Benbernou948627e2016-04-14 14:41:08 -04007007 mDismissalId = wearableBundle.getString(KEY_DISMISSAL_ID);
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04007008 mBridgeTag = wearableBundle.getString(KEY_BRIDGE_TAG);
Griff Hazen61a9e862014-05-22 16:05:19 -07007009 }
7010 }
7011
7012 /**
7013 * Apply wearable extensions to a notification that is being built. This is typically
7014 * called by the {@link android.app.Notification.Builder#extend} method of
7015 * {@link android.app.Notification.Builder}.
7016 */
7017 @Override
7018 public Notification.Builder extend(Notification.Builder builder) {
7019 Bundle wearableBundle = new Bundle();
7020
7021 if (!mActions.isEmpty()) {
7022 wearableBundle.putParcelableArrayList(KEY_ACTIONS, mActions);
7023 }
7024 if (mFlags != DEFAULT_FLAGS) {
7025 wearableBundle.putInt(KEY_FLAGS, mFlags);
7026 }
7027 if (mDisplayIntent != null) {
7028 wearableBundle.putParcelable(KEY_DISPLAY_INTENT, mDisplayIntent);
7029 }
7030 if (!mPages.isEmpty()) {
7031 wearableBundle.putParcelableArray(KEY_PAGES, mPages.toArray(
7032 new Notification[mPages.size()]));
7033 }
7034 if (mBackground != null) {
7035 wearableBundle.putParcelable(KEY_BACKGROUND, mBackground);
7036 }
7037 if (mContentIcon != 0) {
7038 wearableBundle.putInt(KEY_CONTENT_ICON, mContentIcon);
7039 }
7040 if (mContentIconGravity != DEFAULT_CONTENT_ICON_GRAVITY) {
7041 wearableBundle.putInt(KEY_CONTENT_ICON_GRAVITY, mContentIconGravity);
7042 }
7043 if (mContentActionIndex != UNSET_ACTION_INDEX) {
7044 wearableBundle.putInt(KEY_CONTENT_ACTION_INDEX,
7045 mContentActionIndex);
7046 }
7047 if (mCustomSizePreset != SIZE_DEFAULT) {
7048 wearableBundle.putInt(KEY_CUSTOM_SIZE_PRESET, mCustomSizePreset);
7049 }
7050 if (mCustomContentHeight != 0) {
7051 wearableBundle.putInt(KEY_CUSTOM_CONTENT_HEIGHT, mCustomContentHeight);
7052 }
7053 if (mGravity != DEFAULT_GRAVITY) {
7054 wearableBundle.putInt(KEY_GRAVITY, mGravity);
7055 }
Griff Hazen5f2edfc2014-09-29 16:28:44 -07007056 if (mHintScreenTimeout != 0) {
7057 wearableBundle.putInt(KEY_HINT_SCREEN_TIMEOUT, mHintScreenTimeout);
7058 }
Nadia Benbernou948627e2016-04-14 14:41:08 -04007059 if (mDismissalId != null) {
7060 wearableBundle.putString(KEY_DISMISSAL_ID, mDismissalId);
7061 }
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04007062 if (mBridgeTag != null) {
7063 wearableBundle.putString(KEY_BRIDGE_TAG, mBridgeTag);
7064 }
Griff Hazen61a9e862014-05-22 16:05:19 -07007065
7066 builder.getExtras().putBundle(EXTRA_WEARABLE_EXTENSIONS, wearableBundle);
7067 return builder;
7068 }
7069
7070 @Override
7071 public WearableExtender clone() {
7072 WearableExtender that = new WearableExtender();
7073 that.mActions = new ArrayList<Action>(this.mActions);
7074 that.mFlags = this.mFlags;
7075 that.mDisplayIntent = this.mDisplayIntent;
7076 that.mPages = new ArrayList<Notification>(this.mPages);
7077 that.mBackground = this.mBackground;
7078 that.mContentIcon = this.mContentIcon;
7079 that.mContentIconGravity = this.mContentIconGravity;
7080 that.mContentActionIndex = this.mContentActionIndex;
7081 that.mCustomSizePreset = this.mCustomSizePreset;
7082 that.mCustomContentHeight = this.mCustomContentHeight;
7083 that.mGravity = this.mGravity;
Griff Hazen5f2edfc2014-09-29 16:28:44 -07007084 that.mHintScreenTimeout = this.mHintScreenTimeout;
Nadia Benbernou948627e2016-04-14 14:41:08 -04007085 that.mDismissalId = this.mDismissalId;
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04007086 that.mBridgeTag = this.mBridgeTag;
Griff Hazen61a9e862014-05-22 16:05:19 -07007087 return that;
7088 }
7089
7090 /**
7091 * Add a wearable action to this notification.
7092 *
7093 * <p>When wearable actions are added using this method, the set of actions that
7094 * show on a wearable device splits from devices that only show actions added
7095 * using {@link android.app.Notification.Builder#addAction}. This allows for customization
7096 * of which actions display on different devices.
7097 *
7098 * @param action the action to add to this notification
7099 * @return this object for method chaining
7100 * @see android.app.Notification.Action
7101 */
7102 public WearableExtender addAction(Action action) {
7103 mActions.add(action);
7104 return this;
7105 }
7106
7107 /**
7108 * Adds wearable actions to this notification.
7109 *
7110 * <p>When wearable actions are added using this method, the set of actions that
7111 * show on a wearable device splits from devices that only show actions added
7112 * using {@link android.app.Notification.Builder#addAction}. This allows for customization
7113 * of which actions display on different devices.
7114 *
7115 * @param actions the actions to add to this notification
7116 * @return this object for method chaining
7117 * @see android.app.Notification.Action
7118 */
7119 public WearableExtender addActions(List<Action> actions) {
7120 mActions.addAll(actions);
7121 return this;
7122 }
7123
7124 /**
7125 * Clear all wearable actions present on this builder.
7126 * @return this object for method chaining.
7127 * @see #addAction
7128 */
7129 public WearableExtender clearActions() {
7130 mActions.clear();
7131 return this;
7132 }
7133
7134 /**
7135 * Get the wearable actions present on this notification.
7136 */
7137 public List<Action> getActions() {
7138 return mActions;
7139 }
7140
7141 /**
7142 * Set an intent to launch inside of an activity view when displaying
Griff Hazen14f57992014-05-26 09:07:14 -07007143 * this notification. The {@link PendingIntent} provided should be for an activity.
7144 *
7145 * <pre class="prettyprint">
7146 * Intent displayIntent = new Intent(context, MyDisplayActivity.class);
7147 * PendingIntent displayPendingIntent = PendingIntent.getActivity(context,
7148 * 0, displayIntent, PendingIntent.FLAG_UPDATE_CURRENT);
7149 * Notification notif = new Notification.Builder(context)
7150 * .extend(new Notification.WearableExtender()
7151 * .setDisplayIntent(displayPendingIntent)
7152 * .setCustomSizePreset(Notification.WearableExtender.SIZE_MEDIUM))
7153 * .build();</pre>
7154 *
7155 * <p>The activity to launch needs to allow embedding, must be exported, and
Griff Hazen831ca9d2014-06-17 00:38:38 -07007156 * should have an empty task affinity. It is also recommended to use the device
7157 * default light theme.
Griff Hazen14f57992014-05-26 09:07:14 -07007158 *
7159 * <p>Example AndroidManifest.xml entry:
7160 * <pre class="prettyprint">
7161 * &lt;activity android:name=&quot;com.example.MyDisplayActivity&quot;
7162 * android:exported=&quot;true&quot;
7163 * android:allowEmbedded=&quot;true&quot;
Griff Hazen831ca9d2014-06-17 00:38:38 -07007164 * android:taskAffinity=&quot;&quot;
7165 * android:theme=&quot;@android:style/Theme.DeviceDefault.Light&quot; /&gt;</pre>
Griff Hazen61a9e862014-05-22 16:05:19 -07007166 *
7167 * @param intent the {@link PendingIntent} for an activity
7168 * @return this object for method chaining
7169 * @see android.app.Notification.WearableExtender#getDisplayIntent
7170 */
7171 public WearableExtender setDisplayIntent(PendingIntent intent) {
7172 mDisplayIntent = intent;
7173 return this;
7174 }
7175
7176 /**
7177 * Get the intent to launch inside of an activity view when displaying this
7178 * notification. This {@code PendingIntent} should be for an activity.
7179 */
7180 public PendingIntent getDisplayIntent() {
7181 return mDisplayIntent;
7182 }
7183
7184 /**
7185 * Add an additional page of content to display with this notification. The current
7186 * notification forms the first page, and pages added using this function form
7187 * subsequent pages. This field can be used to separate a notification into multiple
7188 * sections.
7189 *
7190 * @param page the notification to add as another page
7191 * @return this object for method chaining
7192 * @see android.app.Notification.WearableExtender#getPages
7193 */
7194 public WearableExtender addPage(Notification page) {
7195 mPages.add(page);
7196 return this;
7197 }
7198
7199 /**
7200 * Add additional pages of content to display with this notification. The current
7201 * notification forms the first page, and pages added using this function form
7202 * subsequent pages. This field can be used to separate a notification into multiple
7203 * sections.
7204 *
7205 * @param pages a list of notifications
7206 * @return this object for method chaining
7207 * @see android.app.Notification.WearableExtender#getPages
7208 */
7209 public WearableExtender addPages(List<Notification> pages) {
7210 mPages.addAll(pages);
7211 return this;
7212 }
7213
7214 /**
7215 * Clear all additional pages present on this builder.
7216 * @return this object for method chaining.
7217 * @see #addPage
7218 */
7219 public WearableExtender clearPages() {
7220 mPages.clear();
7221 return this;
7222 }
7223
7224 /**
7225 * Get the array of additional pages of content for displaying this notification. The
7226 * current notification forms the first page, and elements within this array form
7227 * subsequent pages. This field can be used to separate a notification into multiple
7228 * sections.
7229 * @return the pages for this notification
7230 */
7231 public List<Notification> getPages() {
7232 return mPages;
7233 }
7234
7235 /**
7236 * Set a background image to be displayed behind the notification content.
7237 * Contrary to the {@link android.app.Notification.BigPictureStyle}, this background
7238 * will work with any notification style.
7239 *
7240 * @param background the background bitmap
7241 * @return this object for method chaining
7242 * @see android.app.Notification.WearableExtender#getBackground
7243 */
7244 public WearableExtender setBackground(Bitmap background) {
7245 mBackground = background;
7246 return this;
7247 }
7248
7249 /**
7250 * Get a background image to be displayed behind the notification content.
7251 * Contrary to the {@link android.app.Notification.BigPictureStyle}, this background
7252 * will work with any notification style.
7253 *
7254 * @return the background image
7255 * @see android.app.Notification.WearableExtender#setBackground
7256 */
7257 public Bitmap getBackground() {
7258 return mBackground;
7259 }
7260
7261 /**
7262 * Set an icon that goes with the content of this notification.
7263 */
7264 public WearableExtender setContentIcon(int icon) {
7265 mContentIcon = icon;
7266 return this;
7267 }
7268
7269 /**
7270 * Get an icon that goes with the content of this notification.
7271 */
7272 public int getContentIcon() {
7273 return mContentIcon;
7274 }
7275
7276 /**
7277 * Set the gravity that the content icon should have within the notification display.
7278 * Supported values include {@link android.view.Gravity#START} and
7279 * {@link android.view.Gravity#END}. The default value is {@link android.view.Gravity#END}.
7280 * @see #setContentIcon
7281 */
7282 public WearableExtender setContentIconGravity(int contentIconGravity) {
7283 mContentIconGravity = contentIconGravity;
7284 return this;
7285 }
7286
7287 /**
7288 * Get the gravity that the content icon should have within the notification display.
7289 * Supported values include {@link android.view.Gravity#START} and
7290 * {@link android.view.Gravity#END}. The default value is {@link android.view.Gravity#END}.
7291 * @see #getContentIcon
7292 */
7293 public int getContentIconGravity() {
7294 return mContentIconGravity;
7295 }
7296
7297 /**
7298 * Set an action from this notification's actions to be clickable with the content of
Griff Hazen14f57992014-05-26 09:07:14 -07007299 * this notification. This action will no longer display separately from the
7300 * notification's content.
7301 *
Griff Hazenca48d352014-05-28 22:37:13 -07007302 * <p>For notifications with multiple pages, child pages can also have content actions
Griff Hazen14f57992014-05-26 09:07:14 -07007303 * set, although the list of available actions comes from the main notification and not
7304 * from the child page's notification.
7305 *
7306 * @param actionIndex The index of the action to hoist onto the current notification page.
7307 * If wearable actions were added to the main notification, this index
7308 * will apply to that list, otherwise it will apply to the regular
7309 * actions list.
Griff Hazen61a9e862014-05-22 16:05:19 -07007310 */
7311 public WearableExtender setContentAction(int actionIndex) {
7312 mContentActionIndex = actionIndex;
7313 return this;
7314 }
7315
7316 /**
Griff Hazenca48d352014-05-28 22:37:13 -07007317 * Get the index of the notification action, if any, that was specified as being clickable
7318 * with the content of this notification. This action will no longer display separately
Griff Hazen14f57992014-05-26 09:07:14 -07007319 * from the notification's content.
Griff Hazen61a9e862014-05-22 16:05:19 -07007320 *
Griff Hazenca48d352014-05-28 22:37:13 -07007321 * <p>For notifications with multiple pages, child pages can also have content actions
Griff Hazen14f57992014-05-26 09:07:14 -07007322 * set, although the list of available actions comes from the main notification and not
7323 * from the child page's notification.
7324 *
7325 * <p>If wearable specific actions were added to the main notification, this index will
7326 * apply to that list, otherwise it will apply to the regular actions list.
Griff Hazenca48d352014-05-28 22:37:13 -07007327 *
7328 * @return the action index or {@link #UNSET_ACTION_INDEX} if no action was selected.
Griff Hazen61a9e862014-05-22 16:05:19 -07007329 */
7330 public int getContentAction() {
7331 return mContentActionIndex;
7332 }
7333
7334 /**
7335 * Set the gravity that this notification should have within the available viewport space.
7336 * Supported values include {@link android.view.Gravity#TOP},
7337 * {@link android.view.Gravity#CENTER_VERTICAL} and {@link android.view.Gravity#BOTTOM}.
7338 * The default value is {@link android.view.Gravity#BOTTOM}.
7339 */
7340 public WearableExtender setGravity(int gravity) {
7341 mGravity = gravity;
7342 return this;
7343 }
7344
7345 /**
7346 * Get the gravity that this notification should have within the available viewport space.
7347 * Supported values include {@link android.view.Gravity#TOP},
7348 * {@link android.view.Gravity#CENTER_VERTICAL} and {@link android.view.Gravity#BOTTOM}.
7349 * The default value is {@link android.view.Gravity#BOTTOM}.
7350 */
7351 public int getGravity() {
7352 return mGravity;
7353 }
7354
7355 /**
7356 * Set the custom size preset for the display of this notification out of the available
7357 * presets found in {@link android.app.Notification.WearableExtender}, e.g.
7358 * {@link #SIZE_LARGE}.
7359 * <p>Some custom size presets are only applicable for custom display notifications created
7360 * using {@link android.app.Notification.WearableExtender#setDisplayIntent}. Check the
7361 * documentation for the preset in question. See also
7362 * {@link #setCustomContentHeight} and {@link #getCustomSizePreset}.
7363 */
7364 public WearableExtender setCustomSizePreset(int sizePreset) {
7365 mCustomSizePreset = sizePreset;
7366 return this;
7367 }
7368
7369 /**
7370 * Get the custom size preset for the display of this notification out of the available
7371 * presets found in {@link android.app.Notification.WearableExtender}, e.g.
7372 * {@link #SIZE_LARGE}.
7373 * <p>Some custom size presets are only applicable for custom display notifications created
7374 * using {@link #setDisplayIntent}. Check the documentation for the preset in question.
7375 * See also {@link #setCustomContentHeight} and {@link #setCustomSizePreset}.
7376 */
7377 public int getCustomSizePreset() {
7378 return mCustomSizePreset;
7379 }
7380
7381 /**
7382 * Set the custom height in pixels for the display of this notification's content.
7383 * <p>This option is only available for custom display notifications created
7384 * using {@link android.app.Notification.WearableExtender#setDisplayIntent}. See also
7385 * {@link android.app.Notification.WearableExtender#setCustomSizePreset} and
7386 * {@link #getCustomContentHeight}.
7387 */
7388 public WearableExtender setCustomContentHeight(int height) {
7389 mCustomContentHeight = height;
7390 return this;
7391 }
7392
7393 /**
7394 * Get the custom height in pixels for the display of this notification's content.
7395 * <p>This option is only available for custom display notifications created
7396 * using {@link #setDisplayIntent}. See also {@link #setCustomSizePreset} and
7397 * {@link #setCustomContentHeight}.
7398 */
7399 public int getCustomContentHeight() {
7400 return mCustomContentHeight;
7401 }
7402
7403 /**
7404 * Set whether the scrolling position for the contents of this notification should start
7405 * at the bottom of the contents instead of the top when the contents are too long to
7406 * display within the screen. Default is false (start scroll at the top).
7407 */
7408 public WearableExtender setStartScrollBottom(boolean startScrollBottom) {
7409 setFlag(FLAG_START_SCROLL_BOTTOM, startScrollBottom);
7410 return this;
7411 }
7412
7413 /**
7414 * Get whether the scrolling position for the contents of this notification should start
7415 * at the bottom of the contents instead of the top when the contents are too long to
7416 * display within the screen. Default is false (start scroll at the top).
7417 */
7418 public boolean getStartScrollBottom() {
7419 return (mFlags & FLAG_START_SCROLL_BOTTOM) != 0;
7420 }
7421
7422 /**
7423 * Set whether the content intent is available when the wearable device is not connected
7424 * to a companion device. The user can still trigger this intent when the wearable device
7425 * is offline, but a visual hint will indicate that the content intent may not be available.
7426 * Defaults to true.
7427 */
7428 public WearableExtender setContentIntentAvailableOffline(
7429 boolean contentIntentAvailableOffline) {
7430 setFlag(FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE, contentIntentAvailableOffline);
7431 return this;
7432 }
7433
7434 /**
7435 * Get whether the content intent is available when the wearable device is not connected
7436 * to a companion device. The user can still trigger this intent when the wearable device
7437 * is offline, but a visual hint will indicate that the content intent may not be available.
7438 * Defaults to true.
7439 */
7440 public boolean getContentIntentAvailableOffline() {
7441 return (mFlags & FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE) != 0;
7442 }
7443
7444 /**
7445 * Set a hint that this notification's icon should not be displayed.
7446 * @param hintHideIcon {@code true} to hide the icon, {@code false} otherwise.
7447 * @return this object for method chaining
7448 */
7449 public WearableExtender setHintHideIcon(boolean hintHideIcon) {
7450 setFlag(FLAG_HINT_HIDE_ICON, hintHideIcon);
7451 return this;
7452 }
7453
7454 /**
7455 * Get a hint that this notification's icon should not be displayed.
7456 * @return {@code true} if this icon should not be displayed, false otherwise.
7457 * The default value is {@code false} if this was never set.
7458 */
7459 public boolean getHintHideIcon() {
7460 return (mFlags & FLAG_HINT_HIDE_ICON) != 0;
7461 }
7462
7463 /**
7464 * Set a visual hint that only the background image of this notification should be
7465 * displayed, and other semantic content should be hidden. This hint is only applicable
7466 * to sub-pages added using {@link #addPage}.
7467 */
7468 public WearableExtender setHintShowBackgroundOnly(boolean hintShowBackgroundOnly) {
7469 setFlag(FLAG_HINT_SHOW_BACKGROUND_ONLY, hintShowBackgroundOnly);
7470 return this;
7471 }
7472
7473 /**
7474 * Get a visual hint that only the background image of this notification should be
7475 * displayed, and other semantic content should be hidden. This hint is only applicable
7476 * to sub-pages added using {@link android.app.Notification.WearableExtender#addPage}.
7477 */
7478 public boolean getHintShowBackgroundOnly() {
7479 return (mFlags & FLAG_HINT_SHOW_BACKGROUND_ONLY) != 0;
7480 }
7481
Griff Hazen5f2edfc2014-09-29 16:28:44 -07007482 /**
Griff Hazen9c5be4e2014-11-17 17:47:50 -08007483 * Set a hint that this notification's background should not be clipped if possible,
7484 * and should instead be resized to fully display on the screen, retaining the aspect
7485 * ratio of the image. This can be useful for images like barcodes or qr codes.
Griff Hazen5f2edfc2014-09-29 16:28:44 -07007486 * @param hintAvoidBackgroundClipping {@code true} to avoid clipping if possible.
7487 * @return this object for method chaining
7488 */
7489 public WearableExtender setHintAvoidBackgroundClipping(
7490 boolean hintAvoidBackgroundClipping) {
7491 setFlag(FLAG_HINT_AVOID_BACKGROUND_CLIPPING, hintAvoidBackgroundClipping);
7492 return this;
7493 }
7494
7495 /**
Griff Hazen9c5be4e2014-11-17 17:47:50 -08007496 * Get a hint that this notification's background should not be clipped if possible,
7497 * and should instead be resized to fully display on the screen, retaining the aspect
7498 * ratio of the image. This can be useful for images like barcodes or qr codes.
Griff Hazen5f2edfc2014-09-29 16:28:44 -07007499 * @return {@code true} if it's ok if the background is clipped on the screen, false
7500 * otherwise. The default value is {@code false} if this was never set.
7501 */
7502 public boolean getHintAvoidBackgroundClipping() {
7503 return (mFlags & FLAG_HINT_AVOID_BACKGROUND_CLIPPING) != 0;
7504 }
7505
7506 /**
7507 * Set a hint that the screen should remain on for at least this duration when
7508 * this notification is displayed on the screen.
7509 * @param timeout The requested screen timeout in milliseconds. Can also be either
7510 * {@link #SCREEN_TIMEOUT_SHORT} or {@link #SCREEN_TIMEOUT_LONG}.
7511 * @return this object for method chaining
7512 */
7513 public WearableExtender setHintScreenTimeout(int timeout) {
7514 mHintScreenTimeout = timeout;
7515 return this;
7516 }
7517
7518 /**
7519 * Get the duration, in milliseconds, that the screen should remain on for
7520 * when this notification is displayed.
7521 * @return the duration in milliseconds if > 0, or either one of the sentinel values
7522 * {@link #SCREEN_TIMEOUT_SHORT} or {@link #SCREEN_TIMEOUT_LONG}.
7523 */
7524 public int getHintScreenTimeout() {
7525 return mHintScreenTimeout;
7526 }
7527
Alex Hills9ab3a232016-04-05 14:54:56 -04007528 /**
Alex Hills4bcb06b2016-04-05 14:26:25 -04007529 * Set a hint that this notification's {@link BigPictureStyle} (if present) should be
7530 * converted to low-bit and displayed in ambient mode, especially useful for barcodes and
7531 * qr codes, as well as other simple black-and-white tickets.
7532 * @param hintAmbientBigPicture {@code true} to enable converstion and ambient.
7533 * @return this object for method chaining
7534 */
7535 public WearableExtender setHintAmbientBigPicture(boolean hintAmbientBigPicture) {
7536 setFlag(FLAG_BIG_PICTURE_AMBIENT, hintAmbientBigPicture);
7537 return this;
7538 }
7539
7540 /**
7541 * Get a hint that this notification's {@link BigPictureStyle} (if present) should be
7542 * converted to low-bit and displayed in ambient mode, especially useful for barcodes and
7543 * qr codes, as well as other simple black-and-white tickets.
7544 * @return {@code true} if it should be displayed in ambient, false otherwise
7545 * otherwise. The default value is {@code false} if this was never set.
7546 */
7547 public boolean getHintAmbientBigPicture() {
7548 return (mFlags & FLAG_BIG_PICTURE_AMBIENT) != 0;
7549 }
7550
7551 /**
Alex Hills9ab3a232016-04-05 14:54:56 -04007552 * Set a hint that this notification's content intent will launch an {@link Activity}
7553 * directly, telling the platform that it can generate the appropriate transitions.
7554 * @param hintContentIntentLaunchesActivity {@code true} if the content intent will launch
7555 * an activity and transitions should be generated, false otherwise.
7556 * @return this object for method chaining
7557 */
7558 public WearableExtender setHintContentIntentLaunchesActivity(
7559 boolean hintContentIntentLaunchesActivity) {
7560 setFlag(FLAG_HINT_CONTENT_INTENT_LAUNCHES_ACTIVITY, hintContentIntentLaunchesActivity);
7561 return this;
7562 }
7563
7564 /**
7565 * Get a hint that this notification's content intent will launch an {@link Activity}
7566 * directly, telling the platform that it can generate the appropriate transitions
7567 * @return {@code true} if the content intent will launch an activity and transitions should
7568 * be generated, false otherwise. The default value is {@code false} if this was never set.
7569 */
7570 public boolean getHintContentIntentLaunchesActivity() {
7571 return (mFlags & FLAG_HINT_CONTENT_INTENT_LAUNCHES_ACTIVITY) != 0;
7572 }
7573
Nadia Benbernou948627e2016-04-14 14:41:08 -04007574 /**
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04007575 * Sets the dismissal id for this notification. If a notification is posted with a
7576 * dismissal id, then when that notification is canceled, notifications on other wearables
7577 * and the paired Android phone having that same dismissal id will also be canceled. See
Nadia Benbernou948627e2016-04-14 14:41:08 -04007578 * <a href="{@docRoot}wear/notifications/index.html">Adding Wearable Features to
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04007579 * Notifications</a> for more information.
Nadia Benbernou948627e2016-04-14 14:41:08 -04007580 * @param dismissalId the dismissal id of the notification.
7581 * @return this object for method chaining
7582 */
7583 public WearableExtender setDismissalId(String dismissalId) {
7584 mDismissalId = dismissalId;
7585 return this;
7586 }
7587
7588 /**
7589 * Returns the dismissal id of the notification.
7590 * @return the dismissal id of the notification or null if it has not been set.
7591 */
7592 public String getDismissalId() {
7593 return mDismissalId;
7594 }
7595
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04007596 /**
7597 * Sets a bridge tag for this notification. A bridge tag can be set for notifications
7598 * posted from a phone to provide finer-grained control on what notifications are bridged
7599 * to wearables. See <a href="{@docRoot}wear/notifications/index.html">Adding Wearable
7600 * Features to Notifications</a> for more information.
7601 * @param bridgeTag the bridge tag of the notification.
7602 * @return this object for method chaining
7603 */
7604 public WearableExtender setBridgeTag(String bridgeTag) {
7605 mBridgeTag = bridgeTag;
7606 return this;
7607 }
7608
7609 /**
7610 * Returns the bridge tag of the notification.
7611 * @return the bridge tag or null if not present.
7612 */
7613 public String getBridgeTag() {
7614 return mBridgeTag;
7615 }
7616
Griff Hazen61a9e862014-05-22 16:05:19 -07007617 private void setFlag(int mask, boolean value) {
7618 if (value) {
7619 mFlags |= mask;
7620 } else {
7621 mFlags &= ~mask;
7622 }
7623 }
7624 }
7625
7626 /**
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08007627 * <p>Helper class to add Android Auto extensions to notifications. To create a notification
7628 * with car extensions:
7629 *
7630 * <ol>
7631 * <li>Create an {@link Notification.Builder}, setting any desired
7632 * properties.
7633 * <li>Create a {@link CarExtender}.
7634 * <li>Set car-specific properties using the {@code add} and {@code set} methods of
7635 * {@link CarExtender}.
7636 * <li>Call {@link Notification.Builder#extend(Notification.Extender)}
7637 * to apply the extensions to a notification.
7638 * </ol>
7639 *
7640 * <pre class="prettyprint">
7641 * Notification notification = new Notification.Builder(context)
7642 * ...
7643 * .extend(new CarExtender()
7644 * .set*(...))
7645 * .build();
7646 * </pre>
7647 *
7648 * <p>Car extensions can be accessed on an existing notification by using the
7649 * {@code CarExtender(Notification)} constructor, and then using the {@code get} methods
7650 * to access values.
7651 */
7652 public static final class CarExtender implements Extender {
7653 private static final String TAG = "CarExtender";
7654
7655 private static final String EXTRA_CAR_EXTENDER = "android.car.EXTENSIONS";
7656 private static final String EXTRA_LARGE_ICON = "large_icon";
7657 private static final String EXTRA_CONVERSATION = "car_conversation";
7658 private static final String EXTRA_COLOR = "app_color";
7659
7660 private Bitmap mLargeIcon;
7661 private UnreadConversation mUnreadConversation;
7662 private int mColor = Notification.COLOR_DEFAULT;
7663
7664 /**
7665 * Create a {@link CarExtender} with default options.
7666 */
7667 public CarExtender() {
7668 }
7669
7670 /**
7671 * Create a {@link CarExtender} from the CarExtender options of an existing Notification.
7672 *
7673 * @param notif The notification from which to copy options.
7674 */
7675 public CarExtender(Notification notif) {
7676 Bundle carBundle = notif.extras == null ?
7677 null : notif.extras.getBundle(EXTRA_CAR_EXTENDER);
7678 if (carBundle != null) {
7679 mLargeIcon = carBundle.getParcelable(EXTRA_LARGE_ICON);
7680 mColor = carBundle.getInt(EXTRA_COLOR, Notification.COLOR_DEFAULT);
7681
7682 Bundle b = carBundle.getBundle(EXTRA_CONVERSATION);
7683 mUnreadConversation = UnreadConversation.getUnreadConversationFromBundle(b);
7684 }
7685 }
7686
7687 /**
7688 * Apply car extensions to a notification that is being built. This is typically called by
7689 * the {@link Notification.Builder#extend(Notification.Extender)}
7690 * method of {@link Notification.Builder}.
7691 */
7692 @Override
7693 public Notification.Builder extend(Notification.Builder builder) {
7694 Bundle carExtensions = new Bundle();
7695
7696 if (mLargeIcon != null) {
7697 carExtensions.putParcelable(EXTRA_LARGE_ICON, mLargeIcon);
7698 }
7699 if (mColor != Notification.COLOR_DEFAULT) {
7700 carExtensions.putInt(EXTRA_COLOR, mColor);
7701 }
7702
7703 if (mUnreadConversation != null) {
7704 Bundle b = mUnreadConversation.getBundleForUnreadConversation();
7705 carExtensions.putBundle(EXTRA_CONVERSATION, b);
7706 }
7707
7708 builder.getExtras().putBundle(EXTRA_CAR_EXTENDER, carExtensions);
7709 return builder;
7710 }
7711
7712 /**
7713 * Sets the accent color to use when Android Auto presents the notification.
7714 *
7715 * Android Auto uses the color set with {@link Notification.Builder#setColor(int)}
7716 * to accent the displayed notification. However, not all colors are acceptable in an
7717 * automotive setting. This method can be used to override the color provided in the
7718 * notification in such a situation.
7719 */
Tor Norbye80756e32015-03-02 09:39:27 -08007720 public CarExtender setColor(@ColorInt int color) {
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08007721 mColor = color;
7722 return this;
7723 }
7724
7725 /**
7726 * Gets the accent color.
7727 *
Julia Reynoldsd9228f12015-10-20 10:37:27 -04007728 * @see #setColor
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08007729 */
Tor Norbye80756e32015-03-02 09:39:27 -08007730 @ColorInt
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08007731 public int getColor() {
7732 return mColor;
7733 }
7734
7735 /**
7736 * Sets the large icon of the car notification.
7737 *
7738 * If no large icon is set in the extender, Android Auto will display the icon
7739 * specified by {@link Notification.Builder#setLargeIcon(android.graphics.Bitmap)}
7740 *
7741 * @param largeIcon The large icon to use in the car notification.
7742 * @return This object for method chaining.
7743 */
7744 public CarExtender setLargeIcon(Bitmap largeIcon) {
7745 mLargeIcon = largeIcon;
7746 return this;
7747 }
7748
7749 /**
7750 * Gets the large icon used in this car notification, or null if no icon has been set.
7751 *
7752 * @return The large icon for the car notification.
7753 * @see CarExtender#setLargeIcon
7754 */
7755 public Bitmap getLargeIcon() {
7756 return mLargeIcon;
7757 }
7758
7759 /**
7760 * Sets the unread conversation in a message notification.
7761 *
7762 * @param unreadConversation The unread part of the conversation this notification conveys.
7763 * @return This object for method chaining.
7764 */
7765 public CarExtender setUnreadConversation(UnreadConversation unreadConversation) {
7766 mUnreadConversation = unreadConversation;
7767 return this;
7768 }
7769
7770 /**
7771 * Returns the unread conversation conveyed by this notification.
7772 * @see #setUnreadConversation(UnreadConversation)
7773 */
7774 public UnreadConversation getUnreadConversation() {
7775 return mUnreadConversation;
7776 }
7777
7778 /**
7779 * A class which holds the unread messages from a conversation.
7780 */
7781 public static class UnreadConversation {
7782 private static final String KEY_AUTHOR = "author";
7783 private static final String KEY_TEXT = "text";
7784 private static final String KEY_MESSAGES = "messages";
7785 private static final String KEY_REMOTE_INPUT = "remote_input";
7786 private static final String KEY_ON_REPLY = "on_reply";
7787 private static final String KEY_ON_READ = "on_read";
7788 private static final String KEY_PARTICIPANTS = "participants";
7789 private static final String KEY_TIMESTAMP = "timestamp";
7790
7791 private final String[] mMessages;
7792 private final RemoteInput mRemoteInput;
7793 private final PendingIntent mReplyPendingIntent;
7794 private final PendingIntent mReadPendingIntent;
7795 private final String[] mParticipants;
7796 private final long mLatestTimestamp;
7797
7798 UnreadConversation(String[] messages, RemoteInput remoteInput,
7799 PendingIntent replyPendingIntent, PendingIntent readPendingIntent,
7800 String[] participants, long latestTimestamp) {
7801 mMessages = messages;
7802 mRemoteInput = remoteInput;
7803 mReadPendingIntent = readPendingIntent;
7804 mReplyPendingIntent = replyPendingIntent;
7805 mParticipants = participants;
7806 mLatestTimestamp = latestTimestamp;
7807 }
7808
7809 /**
7810 * Gets the list of messages conveyed by this notification.
7811 */
7812 public String[] getMessages() {
7813 return mMessages;
7814 }
7815
7816 /**
7817 * Gets the remote input that will be used to convey the response to a message list, or
7818 * null if no such remote input exists.
7819 */
7820 public RemoteInput getRemoteInput() {
7821 return mRemoteInput;
7822 }
7823
7824 /**
7825 * Gets the pending intent that will be triggered when the user replies to this
7826 * notification.
7827 */
7828 public PendingIntent getReplyPendingIntent() {
7829 return mReplyPendingIntent;
7830 }
7831
7832 /**
7833 * Gets the pending intent that Android Auto will send after it reads aloud all messages
7834 * in this object's message list.
7835 */
7836 public PendingIntent getReadPendingIntent() {
7837 return mReadPendingIntent;
7838 }
7839
7840 /**
7841 * Gets the participants in the conversation.
7842 */
7843 public String[] getParticipants() {
7844 return mParticipants;
7845 }
7846
7847 /**
7848 * Gets the firs participant in the conversation.
7849 */
7850 public String getParticipant() {
7851 return mParticipants.length > 0 ? mParticipants[0] : null;
7852 }
7853
7854 /**
7855 * Gets the timestamp of the conversation.
7856 */
7857 public long getLatestTimestamp() {
7858 return mLatestTimestamp;
7859 }
7860
7861 Bundle getBundleForUnreadConversation() {
7862 Bundle b = new Bundle();
7863 String author = null;
7864 if (mParticipants != null && mParticipants.length > 1) {
7865 author = mParticipants[0];
7866 }
7867 Parcelable[] messages = new Parcelable[mMessages.length];
7868 for (int i = 0; i < messages.length; i++) {
7869 Bundle m = new Bundle();
7870 m.putString(KEY_TEXT, mMessages[i]);
7871 m.putString(KEY_AUTHOR, author);
7872 messages[i] = m;
7873 }
7874 b.putParcelableArray(KEY_MESSAGES, messages);
7875 if (mRemoteInput != null) {
7876 b.putParcelable(KEY_REMOTE_INPUT, mRemoteInput);
7877 }
7878 b.putParcelable(KEY_ON_REPLY, mReplyPendingIntent);
7879 b.putParcelable(KEY_ON_READ, mReadPendingIntent);
7880 b.putStringArray(KEY_PARTICIPANTS, mParticipants);
7881 b.putLong(KEY_TIMESTAMP, mLatestTimestamp);
7882 return b;
7883 }
7884
7885 static UnreadConversation getUnreadConversationFromBundle(Bundle b) {
7886 if (b == null) {
7887 return null;
7888 }
7889 Parcelable[] parcelableMessages = b.getParcelableArray(KEY_MESSAGES);
7890 String[] messages = null;
7891 if (parcelableMessages != null) {
7892 String[] tmp = new String[parcelableMessages.length];
7893 boolean success = true;
7894 for (int i = 0; i < tmp.length; i++) {
7895 if (!(parcelableMessages[i] instanceof Bundle)) {
7896 success = false;
7897 break;
7898 }
7899 tmp[i] = ((Bundle) parcelableMessages[i]).getString(KEY_TEXT);
7900 if (tmp[i] == null) {
7901 success = false;
7902 break;
7903 }
7904 }
7905 if (success) {
7906 messages = tmp;
7907 } else {
7908 return null;
7909 }
7910 }
7911
7912 PendingIntent onRead = b.getParcelable(KEY_ON_READ);
7913 PendingIntent onReply = b.getParcelable(KEY_ON_REPLY);
7914
7915 RemoteInput remoteInput = b.getParcelable(KEY_REMOTE_INPUT);
7916
7917 String[] participants = b.getStringArray(KEY_PARTICIPANTS);
7918 if (participants == null || participants.length != 1) {
7919 return null;
7920 }
7921
7922 return new UnreadConversation(messages,
7923 remoteInput,
7924 onReply,
7925 onRead,
7926 participants, b.getLong(KEY_TIMESTAMP));
7927 }
7928 };
7929
7930 /**
7931 * Builder class for {@link CarExtender.UnreadConversation} objects.
7932 */
7933 public static class Builder {
7934 private final List<String> mMessages = new ArrayList<String>();
7935 private final String mParticipant;
7936 private RemoteInput mRemoteInput;
7937 private PendingIntent mReadPendingIntent;
7938 private PendingIntent mReplyPendingIntent;
7939 private long mLatestTimestamp;
7940
7941 /**
7942 * Constructs a new builder for {@link CarExtender.UnreadConversation}.
7943 *
7944 * @param name The name of the other participant in the conversation.
7945 */
7946 public Builder(String name) {
7947 mParticipant = name;
7948 }
7949
7950 /**
7951 * Appends a new unread message to the list of messages for this conversation.
7952 *
7953 * The messages should be added from oldest to newest.
7954 *
7955 * @param message The text of the new unread message.
7956 * @return This object for method chaining.
7957 */
7958 public Builder addMessage(String message) {
7959 mMessages.add(message);
7960 return this;
7961 }
7962
7963 /**
7964 * Sets the pending intent and remote input which will convey the reply to this
7965 * notification.
7966 *
7967 * @param pendingIntent The pending intent which will be triggered on a reply.
7968 * @param remoteInput The remote input parcelable which will carry the reply.
7969 * @return This object for method chaining.
7970 *
7971 * @see CarExtender.UnreadConversation#getRemoteInput
7972 * @see CarExtender.UnreadConversation#getReplyPendingIntent
7973 */
7974 public Builder setReplyAction(
7975 PendingIntent pendingIntent, RemoteInput remoteInput) {
7976 mRemoteInput = remoteInput;
7977 mReplyPendingIntent = pendingIntent;
7978
7979 return this;
7980 }
7981
7982 /**
7983 * Sets the pending intent that will be sent once the messages in this notification
7984 * are read.
7985 *
7986 * @param pendingIntent The pending intent to use.
7987 * @return This object for method chaining.
7988 */
7989 public Builder setReadPendingIntent(PendingIntent pendingIntent) {
7990 mReadPendingIntent = pendingIntent;
7991 return this;
7992 }
7993
7994 /**
7995 * Sets the timestamp of the most recent message in an unread conversation.
7996 *
7997 * If a messaging notification has been posted by your application and has not
7998 * yet been cancelled, posting a later notification with the same id and tag
7999 * but without a newer timestamp may result in Android Auto not displaying a
8000 * heads up notification for the later notification.
8001 *
8002 * @param timestamp The timestamp of the most recent message in the conversation.
8003 * @return This object for method chaining.
8004 */
8005 public Builder setLatestTimestamp(long timestamp) {
8006 mLatestTimestamp = timestamp;
8007 return this;
8008 }
8009
8010 /**
8011 * Builds a new unread conversation object.
8012 *
8013 * @return The new unread conversation object.
8014 */
8015 public UnreadConversation build() {
8016 String[] messages = mMessages.toArray(new String[mMessages.size()]);
8017 String[] participants = { mParticipant };
8018 return new UnreadConversation(messages, mRemoteInput, mReplyPendingIntent,
8019 mReadPendingIntent, participants, mLatestTimestamp);
8020 }
8021 }
8022 }
8023
8024 /**
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08008025 * <p>Helper class to add Android TV extensions to notifications. To create a notification
8026 * with a TV extension:
8027 *
8028 * <ol>
8029 * <li>Create an {@link Notification.Builder}, setting any desired properties.
8030 * <li>Create a {@link TvExtender}.
8031 * <li>Set TV-specific properties using the {@code set} methods of
8032 * {@link TvExtender}.
8033 * <li>Call {@link Notification.Builder#extend(Notification.Extender)}
8034 * to apply the extension to a notification.
8035 * </ol>
8036 *
8037 * <pre class="prettyprint">
8038 * Notification notification = new Notification.Builder(context)
8039 * ...
8040 * .extend(new TvExtender()
8041 * .set*(...))
8042 * .build();
8043 * </pre>
8044 *
8045 * <p>TV extensions can be accessed on an existing notification by using the
8046 * {@code TvExtender(Notification)} constructor, and then using the {@code get} methods
8047 * to access values.
8048 *
8049 * @hide
8050 */
8051 @SystemApi
8052 public static final class TvExtender implements Extender {
8053 private static final String TAG = "TvExtender";
8054
8055 private static final String EXTRA_TV_EXTENDER = "android.tv.EXTENSIONS";
8056 private static final String EXTRA_FLAGS = "flags";
8057 private static final String EXTRA_CONTENT_INTENT = "content_intent";
8058 private static final String EXTRA_DELETE_INTENT = "delete_intent";
Dmitri Plotnikovb8a04ee2017-01-20 10:39:22 -08008059 private static final String EXTRA_CHANNEL_ID = "channel_id";
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08008060
8061 // Flags bitwise-ored to mFlags
8062 private static final int FLAG_AVAILABLE_ON_TV = 0x1;
8063
8064 private int mFlags;
Dmitri Plotnikovb8a04ee2017-01-20 10:39:22 -08008065 private String mChannelId;
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08008066 private PendingIntent mContentIntent;
8067 private PendingIntent mDeleteIntent;
8068
8069 /**
8070 * Create a {@link TvExtender} with default options.
8071 */
8072 public TvExtender() {
8073 mFlags = FLAG_AVAILABLE_ON_TV;
8074 }
8075
8076 /**
8077 * Create a {@link TvExtender} from the TvExtender options of an existing Notification.
8078 *
8079 * @param notif The notification from which to copy options.
8080 */
8081 public TvExtender(Notification notif) {
8082 Bundle bundle = notif.extras == null ?
8083 null : notif.extras.getBundle(EXTRA_TV_EXTENDER);
8084 if (bundle != null) {
8085 mFlags = bundle.getInt(EXTRA_FLAGS);
Dmitri Plotnikovb8a04ee2017-01-20 10:39:22 -08008086 mChannelId = bundle.getString(EXTRA_CHANNEL_ID);
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08008087 mContentIntent = bundle.getParcelable(EXTRA_CONTENT_INTENT);
8088 mDeleteIntent = bundle.getParcelable(EXTRA_DELETE_INTENT);
8089 }
8090 }
8091
8092 /**
8093 * Apply a TV extension to a notification that is being built. This is typically called by
8094 * the {@link Notification.Builder#extend(Notification.Extender)}
8095 * method of {@link Notification.Builder}.
8096 */
8097 @Override
8098 public Notification.Builder extend(Notification.Builder builder) {
8099 Bundle bundle = new Bundle();
8100
8101 bundle.putInt(EXTRA_FLAGS, mFlags);
Dmitri Plotnikovb8a04ee2017-01-20 10:39:22 -08008102 bundle.putString(EXTRA_CHANNEL_ID, mChannelId);
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08008103 if (mContentIntent != null) {
8104 bundle.putParcelable(EXTRA_CONTENT_INTENT, mContentIntent);
8105 }
8106
8107 if (mDeleteIntent != null) {
8108 bundle.putParcelable(EXTRA_DELETE_INTENT, mDeleteIntent);
8109 }
8110
8111 builder.getExtras().putBundle(EXTRA_TV_EXTENDER, bundle);
8112 return builder;
8113 }
8114
8115 /**
8116 * Returns true if this notification should be shown on TV. This method return true
8117 * if the notification was extended with a TvExtender.
8118 */
8119 public boolean isAvailableOnTv() {
8120 return (mFlags & FLAG_AVAILABLE_ON_TV) != 0;
8121 }
8122
8123 /**
Dmitri Plotnikovb8a04ee2017-01-20 10:39:22 -08008124 * Specifies the channel the notification should be delivered on when shown on TV.
8125 * It can be different from the channel that the notification is delivered to when
8126 * posting on a non-TV device.
8127 */
8128 public TvExtender setChannel(String channelId) {
8129 mChannelId = channelId;
8130 return this;
8131 }
8132
8133 /**
Julia Reynoldsbad42972017-04-25 13:52:49 -04008134 * Specifies the channel the notification should be delivered on when shown on TV.
8135 * It can be different from the channel that the notification is delivered to when
8136 * posting on a non-TV device.
8137 */
8138 public TvExtender setChannelId(String channelId) {
8139 mChannelId = channelId;
8140 return this;
8141 }
8142
Jeff Sharkey000ce802017-04-29 13:13:27 -06008143 /** @removed */
8144 @Deprecated
Dmitri Plotnikovb8a04ee2017-01-20 10:39:22 -08008145 public String getChannel() {
8146 return mChannelId;
8147 }
8148
8149 /**
Julia Reynoldsbad42972017-04-25 13:52:49 -04008150 * Returns the id of the channel this notification posts to on TV.
8151 */
8152 public String getChannelId() {
8153 return mChannelId;
8154 }
8155
8156 /**
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08008157 * Supplies a {@link PendingIntent} to be sent when the notification is selected on TV.
8158 * If provided, it is used instead of the content intent specified
8159 * at the level of Notification.
8160 */
8161 public TvExtender setContentIntent(PendingIntent intent) {
8162 mContentIntent = intent;
8163 return this;
8164 }
8165
8166 /**
8167 * Returns the TV-specific content intent. If this method returns null, the
8168 * main content intent on the notification should be used.
8169 *
8170 * @see {@link Notification#contentIntent}
8171 */
8172 public PendingIntent getContentIntent() {
8173 return mContentIntent;
8174 }
8175
8176 /**
8177 * Supplies a {@link PendingIntent} to send when the notification is cleared explicitly
8178 * by the user on TV. If provided, it is used instead of the delete intent specified
8179 * at the level of Notification.
8180 */
8181 public TvExtender setDeleteIntent(PendingIntent intent) {
8182 mDeleteIntent = intent;
8183 return this;
8184 }
8185
8186 /**
8187 * Returns the TV-specific delete intent. If this method returns null, the
8188 * main delete intent on the notification should be used.
8189 *
8190 * @see {@link Notification#deleteIntent}
8191 */
8192 public PendingIntent getDeleteIntent() {
8193 return mDeleteIntent;
8194 }
8195 }
8196
8197 /**
Griff Hazen61a9e862014-05-22 16:05:19 -07008198 * Get an array of Notification objects from a parcelable array bundle field.
8199 * Update the bundle to have a typed array so fetches in the future don't need
8200 * to do an array copy.
8201 */
8202 private static Notification[] getNotificationArrayFromBundle(Bundle bundle, String key) {
8203 Parcelable[] array = bundle.getParcelableArray(key);
8204 if (array instanceof Notification[] || array == null) {
8205 return (Notification[]) array;
8206 }
8207 Notification[] typedArray = Arrays.copyOf(array, array.length,
8208 Notification[].class);
8209 bundle.putParcelableArray(key, typedArray);
8210 return typedArray;
8211 }
Christoph Studer4600f9b2014-07-22 22:44:43 +02008212
8213 private static class BuilderRemoteViews extends RemoteViews {
8214 public BuilderRemoteViews(Parcel parcel) {
8215 super(parcel);
8216 }
8217
Kenny Guy77320062014-08-27 21:37:15 +01008218 public BuilderRemoteViews(ApplicationInfo appInfo, int layoutId) {
8219 super(appInfo, layoutId);
Christoph Studer4600f9b2014-07-22 22:44:43 +02008220 }
8221
8222 @Override
8223 public BuilderRemoteViews clone() {
8224 Parcel p = Parcel.obtain();
8225 writeToParcel(p, 0);
8226 p.setDataPosition(0);
8227 BuilderRemoteViews brv = new BuilderRemoteViews(p);
8228 p.recycle();
8229 return brv;
8230 }
8231 }
Adrian Roos70d7aa32017-01-11 15:39:06 -08008232
8233 private static class StandardTemplateParams {
8234 boolean hasProgress = true;
8235 boolean ambient = false;
8236 CharSequence title;
8237 CharSequence text;
8238
8239 final StandardTemplateParams reset() {
8240 hasProgress = true;
8241 ambient = false;
8242 title = null;
8243 text = null;
8244 return this;
8245 }
8246
8247 final StandardTemplateParams hasProgress(boolean hasProgress) {
8248 this.hasProgress = hasProgress;
8249 return this;
8250 }
8251
8252 final StandardTemplateParams title(CharSequence title) {
8253 this.title = title;
8254 return this;
8255 }
8256
8257 final StandardTemplateParams text(CharSequence text) {
8258 this.text = text;
8259 return this;
8260 }
8261
8262 final StandardTemplateParams ambient(boolean ambient) {
Adrian Roos0bc3f6a2017-03-06 11:54:05 -08008263 Preconditions.checkState(title == null && text == null, "must set ambient before text");
Adrian Roos70d7aa32017-01-11 15:39:06 -08008264 this.ambient = ambient;
8265 return this;
8266 }
8267
8268 final StandardTemplateParams fillTextsFrom(Builder b) {
8269 Bundle extras = b.mN.extras;
Adrian Roos0bc3f6a2017-03-06 11:54:05 -08008270 title = b.processLegacyText(extras.getCharSequence(EXTRA_TITLE), ambient);
8271 text = b.processLegacyText(extras.getCharSequence(EXTRA_TEXT), ambient);
Adrian Roos70d7aa32017-01-11 15:39:06 -08008272 return this;
8273 }
8274 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08008275}