blob: c626ae9630b8f8404647371761c0fbafbce8465f [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
Dianne Hackbornfb5d4b52017-05-16 17:03:44 -07001115 /**
1116 * This is set on the notification shown by the activity manager about all apps
1117 * running in the background. It indicates that the notification should be shown
1118 * only if any of the given apps do not already have a {@link #FLAG_FOREGROUND_SERVICE}
1119 * notification currently visible to the user. This is a string array of all
1120 * package names of the apps.
1121 * @hide
1122 */
1123 public static final String EXTRA_FOREGROUND_APPS = "android.foregroundApps";
1124
Dan Sandlerd63f9322015-05-06 15:18:49 -04001125 private Icon mSmallIcon;
1126 private Icon mLargeIcon;
1127
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04001128 private String mChannelId;
Julia Reynolds2a128742016-11-28 14:29:25 -05001129 private long mTimeout;
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04001130
Julia Reynolds13d898c2017-02-02 12:22:05 -05001131 private String mShortcutId;
Julia Reynolds3aedded2017-03-31 14:42:09 -04001132 private CharSequence mSettingsText;
Julia Reynolds13d898c2017-02-02 12:22:05 -05001133
Julia Reynoldsa79c3712017-04-21 10:29:57 -04001134 /** @hide */
1135 @IntDef(prefix = { "GROUP_ALERT_" }, value = {
1136 GROUP_ALERT_ALL, GROUP_ALERT_CHILDREN, GROUP_ALERT_SUMMARY
1137 })
1138 @Retention(RetentionPolicy.SOURCE)
1139 public @interface GroupAlertBehavior {}
1140
1141 /**
1142 * Constant for {@link Builder#setGroupAlertBehavior(int)}, meaning that all notifications in a
1143 * group with sound or vibration ought to make sound or vibrate (respectively), so this
1144 * notification will not be muted when it is in a group.
1145 */
1146 public static final int GROUP_ALERT_ALL = 0;
1147
1148 /**
1149 * Constant for {@link Builder#setGroupAlertBehavior(int)}, meaning that all children
1150 * notification in a group should be silenced (no sound or vibration) even if they are posted
1151 * to a {@link NotificationChannel} that has sound and/or vibration. Use this constant to
1152 * mute this notification if this notification is a group child.
1153 *
1154 * <p> For example, you might want to use this constant if you post a number of children
1155 * notifications at once (say, after a periodic sync), and only need to notify the user
1156 * audibly once.
1157 */
1158 public static final int GROUP_ALERT_SUMMARY = 1;
1159
1160 /**
1161 * Constant for {@link Builder#setGroupAlertBehavior(int)}, meaning that the summary
1162 * notification in a group should be silenced (no sound or vibration) even if they are
1163 * posted to a {@link NotificationChannel} that has sound and/or vibration. Use this constant
1164 * to mute this notification if this notification is a group summary.
1165 *
1166 * <p>For example, you might want to use this constant if only the children notifications
1167 * in your group have content and the summary is only used to visually group notifications.
1168 */
1169 public static final int GROUP_ALERT_CHILDREN = 2;
1170
1171 private int mGroupAlertBehavior = GROUP_ALERT_ALL;
1172
Julia Reynolds13d898c2017-02-02 12:22:05 -05001173 /**
1174 * If this notification is being shown as a badge, always show as a number.
1175 */
1176 public static final int BADGE_ICON_NONE = 0;
1177
1178 /**
1179 * If this notification is being shown as a badge, use the {@link #getSmallIcon()} to
1180 * represent this notification.
1181 */
1182 public static final int BADGE_ICON_SMALL = 1;
1183
1184 /**
1185 * If this notification is being shown as a badge, use the {@link #getLargeIcon()} to
1186 * represent this notification.
1187 */
1188 public static final int BADGE_ICON_LARGE = 2;
Julia Reynolds7d5b4da2017-03-20 10:18:49 -04001189 private int mBadgeIcon = BADGE_ICON_NONE;
Julia Reynolds13d898c2017-02-02 12:22:05 -05001190
Chris Wren51c75102013-07-16 20:49:17 -04001191 /**
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001192 * Structure to encapsulate a named action that can be shown as part of this notification.
1193 * It must include an icon, a label, and a {@link PendingIntent} to be fired when the action is
1194 * selected by the user.
1195 * <p>
Griff Hazen959591e2014-05-15 22:26:18 -07001196 * Apps should use {@link Notification.Builder#addAction(int, CharSequence, PendingIntent)}
1197 * or {@link Notification.Builder#addAction(Notification.Action)}
1198 * to attach actions.
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001199 */
Daniel Sandlerea2a3172013-02-20 22:24:20 -05001200 public static class Action implements Parcelable {
Shane Brennan472a3b32016-12-12 15:28:10 -08001201 /**
1202 * {@link #extras} key: Keys to a {@link Parcelable} {@link ArrayList} of
1203 * {@link RemoteInput}s.
1204 *
1205 * This is intended for {@link RemoteInput}s that only accept data, meaning
1206 * {@link RemoteInput#getAllowFreeFormInput} is false, {@link RemoteInput#getChoices}
1207 * is null or empty, and {@link RemoteInput#getAllowedDataTypes is non-null and not
1208 * empty. These {@link RemoteInput}s will be ignored by devices that do not
1209 * support non-text-based {@link RemoteInput}s. See {@link Builder#build}.
1210 *
1211 * You can test if a RemoteInput matches these constraints using
1212 * {@link RemoteInput#isDataOnly}.
1213 */
1214 private static final String EXTRA_DATA_ONLY_INPUTS = "android.extra.DATA_ONLY_INPUTS";
1215
Griff Hazen959591e2014-05-15 22:26:18 -07001216 private final Bundle mExtras;
Dan Sandler86647982015-05-13 23:41:13 -04001217 private Icon mIcon;
Griff Hazen61a9e862014-05-22 16:05:19 -07001218 private final RemoteInput[] mRemoteInputs;
Alex Hills1f27f882017-01-12 11:24:46 -05001219 private boolean mAllowGeneratedReplies = true;
Griff Hazen959591e2014-05-15 22:26:18 -07001220
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001221 /**
1222 * Small icon representing the action.
Dan Sandler86647982015-05-13 23:41:13 -04001223 *
1224 * @deprecated Use {@link Action#getIcon()} instead.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001225 */
Dan Sandler86647982015-05-13 23:41:13 -04001226 @Deprecated
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001227 public int icon;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001228
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001229 /**
1230 * Title of the action.
1231 */
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001232 public CharSequence title;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001233
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001234 /**
1235 * Intent to send when the user invokes this action. May be null, in which case the action
1236 * may be rendered in a disabled presentation by the system UI.
1237 */
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001238 public PendingIntent actionIntent;
Griff Hazen959591e2014-05-15 22:26:18 -07001239
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001240 private Action(Parcel in) {
Dan Sandler86647982015-05-13 23:41:13 -04001241 if (in.readInt() != 0) {
1242 mIcon = Icon.CREATOR.createFromParcel(in);
Dan Sandler68079d52015-07-22 10:45:30 -04001243 if (mIcon.getType() == Icon.TYPE_RESOURCE) {
1244 icon = mIcon.getResId();
1245 }
Dan Sandler86647982015-05-13 23:41:13 -04001246 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001247 title = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
1248 if (in.readInt() == 1) {
1249 actionIntent = PendingIntent.CREATOR.createFromParcel(in);
1250 }
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001251 mExtras = Bundle.setDefusable(in.readBundle(), true);
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001252 mRemoteInputs = in.createTypedArray(RemoteInput.CREATOR);
Alex Hills42b0c4d2016-04-26 13:35:36 -04001253 mAllowGeneratedReplies = in.readInt() == 1;
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001254 }
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001255
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001256 /**
Dan Sandler86647982015-05-13 23:41:13 -04001257 * @deprecated Use {@link android.app.Notification.Action.Builder}.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001258 */
Dan Sandler86647982015-05-13 23:41:13 -04001259 @Deprecated
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001260 public Action(int icon, CharSequence title, PendingIntent intent) {
Alex Hills1f27f882017-01-12 11:24:46 -05001261 this(Icon.createWithResource("", icon), title, intent, new Bundle(), null, true);
Griff Hazen959591e2014-05-15 22:26:18 -07001262 }
1263
Adrian Roos7af53622016-10-12 13:44:05 -07001264 /** Keep in sync with {@link Notification.Action.Builder#Builder(Action)}! */
Dan Sandler86647982015-05-13 23:41:13 -04001265 private Action(Icon icon, CharSequence title, PendingIntent intent, Bundle extras,
Alex Hills42b0c4d2016-04-26 13:35:36 -04001266 RemoteInput[] remoteInputs, boolean allowGeneratedReplies) {
Dan Sandler86647982015-05-13 23:41:13 -04001267 this.mIcon = icon;
Gus Prevasf5bff46f2015-08-24 10:34:28 -04001268 if (icon != null && icon.getType() == Icon.TYPE_RESOURCE) {
1269 this.icon = icon.getResId();
1270 }
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001271 this.title = title;
1272 this.actionIntent = intent;
Griff Hazen959591e2014-05-15 22:26:18 -07001273 this.mExtras = extras != null ? extras : new Bundle();
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001274 this.mRemoteInputs = remoteInputs;
Alex Hills42b0c4d2016-04-26 13:35:36 -04001275 this.mAllowGeneratedReplies = allowGeneratedReplies;
Griff Hazen959591e2014-05-15 22:26:18 -07001276 }
1277
1278 /**
Dan Sandler86647982015-05-13 23:41:13 -04001279 * Return an icon representing the action.
1280 */
1281 public Icon getIcon() {
1282 if (mIcon == null && icon != 0) {
1283 // you snuck an icon in here without using the builder; let's try to keep it
1284 mIcon = Icon.createWithResource("", icon);
1285 }
1286 return mIcon;
1287 }
1288
1289 /**
Griff Hazen959591e2014-05-15 22:26:18 -07001290 * Get additional metadata carried around with this Action.
1291 */
1292 public Bundle getExtras() {
1293 return mExtras;
1294 }
1295
1296 /**
Alex Hills42b0c4d2016-04-26 13:35:36 -04001297 * Return whether the platform should automatically generate possible replies for this
1298 * {@link Action}
1299 */
1300 public boolean getAllowGeneratedReplies() {
1301 return mAllowGeneratedReplies;
1302 }
1303
1304 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001305 * Get the list of inputs to be collected from the user when this action is sent.
Shane Brennan472a3b32016-12-12 15:28:10 -08001306 * May return null if no remote inputs were added. Only returns inputs which accept
1307 * a text input. For inputs which only accept data use {@link #getDataOnlyRemoteInputs}.
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001308 */
1309 public RemoteInput[] getRemoteInputs() {
1310 return mRemoteInputs;
1311 }
1312
1313 /**
Shane Brennan472a3b32016-12-12 15:28:10 -08001314 * Get the list of inputs to be collected from the user that ONLY accept data when this
1315 * action is sent. These remote inputs are guaranteed to return true on a call to
1316 * {@link RemoteInput#isDataOnly}.
1317 *
Shane Brennanf670d6c2017-04-25 13:05:45 -07001318 * Returns null if there are no data-only remote inputs.
Shane Brennan472a3b32016-12-12 15:28:10 -08001319 *
1320 * This method exists so that legacy RemoteInput collectors that pre-date the addition
1321 * of non-textual RemoteInputs do not access these remote inputs.
1322 */
1323 public RemoteInput[] getDataOnlyRemoteInputs() {
1324 return (RemoteInput[]) mExtras.getParcelableArray(EXTRA_DATA_ONLY_INPUTS);
1325 }
1326
1327 /**
Griff Hazen959591e2014-05-15 22:26:18 -07001328 * Builder class for {@link Action} objects.
1329 */
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001330 public static final class Builder {
Dan Sandler86647982015-05-13 23:41:13 -04001331 private final Icon mIcon;
Griff Hazen959591e2014-05-15 22:26:18 -07001332 private final CharSequence mTitle;
1333 private final PendingIntent mIntent;
Alex Hills1f27f882017-01-12 11:24:46 -05001334 private boolean mAllowGeneratedReplies = true;
Griff Hazen959591e2014-05-15 22:26:18 -07001335 private final Bundle mExtras;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001336 private ArrayList<RemoteInput> mRemoteInputs;
Griff Hazen959591e2014-05-15 22:26:18 -07001337
1338 /**
1339 * Construct a new builder for {@link Action} object.
1340 * @param icon icon to show for this action
1341 * @param title the title of the action
1342 * @param intent the {@link PendingIntent} to fire when users trigger this action
1343 */
Dan Sandler86647982015-05-13 23:41:13 -04001344 @Deprecated
Griff Hazen959591e2014-05-15 22:26:18 -07001345 public Builder(int icon, CharSequence title, PendingIntent intent) {
Adrian Roos7af53622016-10-12 13:44:05 -07001346 this(Icon.createWithResource("", icon), title, intent);
Dan Sandler86647982015-05-13 23:41:13 -04001347 }
1348
1349 /**
1350 * Construct a new builder for {@link Action} object.
1351 * @param icon icon to show for this action
1352 * @param title the title of the action
1353 * @param intent the {@link PendingIntent} to fire when users trigger this action
1354 */
1355 public Builder(Icon icon, CharSequence title, PendingIntent intent) {
Alex Hills1f27f882017-01-12 11:24:46 -05001356 this(icon, title, intent, new Bundle(), null, true);
Griff Hazen959591e2014-05-15 22:26:18 -07001357 }
1358
1359 /**
1360 * Construct a new builder for {@link Action} object using the fields from an
1361 * {@link Action}.
1362 * @param action the action to read fields from.
1363 */
1364 public Builder(Action action) {
Adrian Roos7af53622016-10-12 13:44:05 -07001365 this(action.getIcon(), action.title, action.actionIntent,
1366 new Bundle(action.mExtras), action.getRemoteInputs(),
1367 action.getAllowGeneratedReplies());
Griff Hazen959591e2014-05-15 22:26:18 -07001368 }
1369
Dan Sandler86647982015-05-13 23:41:13 -04001370 private Builder(Icon icon, CharSequence title, PendingIntent intent, Bundle extras,
Adrian Roos7af53622016-10-12 13:44:05 -07001371 RemoteInput[] remoteInputs, boolean allowGeneratedReplies) {
Griff Hazen959591e2014-05-15 22:26:18 -07001372 mIcon = icon;
1373 mTitle = title;
1374 mIntent = intent;
1375 mExtras = extras;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001376 if (remoteInputs != null) {
1377 mRemoteInputs = new ArrayList<RemoteInput>(remoteInputs.length);
1378 Collections.addAll(mRemoteInputs, remoteInputs);
1379 }
Adrian Roos7af53622016-10-12 13:44:05 -07001380 mAllowGeneratedReplies = allowGeneratedReplies;
Griff Hazen959591e2014-05-15 22:26:18 -07001381 }
1382
1383 /**
1384 * Merge additional metadata into this builder.
1385 *
1386 * <p>Values within the Bundle will replace existing extras values in this Builder.
1387 *
1388 * @see Notification.Action#extras
1389 */
1390 public Builder addExtras(Bundle extras) {
1391 if (extras != null) {
1392 mExtras.putAll(extras);
1393 }
1394 return this;
1395 }
1396
1397 /**
1398 * Get the metadata Bundle used by this Builder.
1399 *
1400 * <p>The returned Bundle is shared with this Builder.
1401 */
1402 public Bundle getExtras() {
1403 return mExtras;
1404 }
1405
1406 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001407 * Add an input to be collected from the user when this action is sent.
1408 * Response values can be retrieved from the fired intent by using the
1409 * {@link RemoteInput#getResultsFromIntent} function.
1410 * @param remoteInput a {@link RemoteInput} to add to the action
1411 * @return this object for method chaining
1412 */
1413 public Builder addRemoteInput(RemoteInput remoteInput) {
1414 if (mRemoteInputs == null) {
1415 mRemoteInputs = new ArrayList<RemoteInput>();
1416 }
1417 mRemoteInputs.add(remoteInput);
1418 return this;
1419 }
1420
1421 /**
Alex Hills42b0c4d2016-04-26 13:35:36 -04001422 * Set whether the platform should automatically generate possible replies to add to
1423 * {@link RemoteInput#getChoices()}. If the {@link Action} doesn't have a
1424 * {@link RemoteInput}, this has no effect.
1425 * @param allowGeneratedReplies {@code true} to allow generated replies, {@code false}
1426 * otherwise
1427 * @return this object for method chaining
Alex Hills1f27f882017-01-12 11:24:46 -05001428 * The default value is {@code true}
Alex Hills42b0c4d2016-04-26 13:35:36 -04001429 */
1430 public Builder setAllowGeneratedReplies(boolean allowGeneratedReplies) {
1431 mAllowGeneratedReplies = allowGeneratedReplies;
1432 return this;
1433 }
1434
1435 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001436 * Apply an extender to this action builder. Extenders may be used to add
1437 * metadata or change options on this builder.
1438 */
Griff Hazen61a9e862014-05-22 16:05:19 -07001439 public Builder extend(Extender extender) {
1440 extender.extend(this);
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001441 return this;
1442 }
1443
1444 /**
Griff Hazen959591e2014-05-15 22:26:18 -07001445 * Combine all of the options that have been set and return a new {@link Action}
1446 * object.
1447 * @return the built action
1448 */
1449 public Action build() {
Shane Brennan472a3b32016-12-12 15:28:10 -08001450 ArrayList<RemoteInput> dataOnlyInputs = new ArrayList<>();
1451 RemoteInput[] previousDataInputs =
1452 (RemoteInput[]) mExtras.getParcelableArray(EXTRA_DATA_ONLY_INPUTS);
Felipe Lemedfd11962017-01-18 18:38:46 -08001453 if (previousDataInputs != null) {
Shane Brennan472a3b32016-12-12 15:28:10 -08001454 for (RemoteInput input : previousDataInputs) {
1455 dataOnlyInputs.add(input);
1456 }
1457 }
1458 List<RemoteInput> textInputs = new ArrayList<>();
1459 if (mRemoteInputs != null) {
1460 for (RemoteInput input : mRemoteInputs) {
1461 if (input.isDataOnly()) {
1462 dataOnlyInputs.add(input);
1463 } else {
1464 textInputs.add(input);
1465 }
1466 }
1467 }
1468 if (!dataOnlyInputs.isEmpty()) {
1469 RemoteInput[] dataInputsArr =
1470 dataOnlyInputs.toArray(new RemoteInput[dataOnlyInputs.size()]);
1471 mExtras.putParcelableArray(EXTRA_DATA_ONLY_INPUTS, dataInputsArr);
1472 }
1473 RemoteInput[] textInputsArr = textInputs.isEmpty()
1474 ? null : textInputs.toArray(new RemoteInput[textInputs.size()]);
1475 return new Action(mIcon, mTitle, mIntent, mExtras, textInputsArr,
Alex Hills42b0c4d2016-04-26 13:35:36 -04001476 mAllowGeneratedReplies);
Griff Hazen959591e2014-05-15 22:26:18 -07001477 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001478 }
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001479
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001480 @Override
1481 public Action clone() {
1482 return new Action(
Dan Sandler86647982015-05-13 23:41:13 -04001483 getIcon(),
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001484 title,
1485 actionIntent, // safe to alias
Julia Reynolds53c934c2016-09-16 14:03:43 -04001486 mExtras == null ? new Bundle() : new Bundle(mExtras),
Alex Hills42b0c4d2016-04-26 13:35:36 -04001487 getRemoteInputs(),
1488 getAllowGeneratedReplies());
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001489 }
1490 @Override
1491 public int describeContents() {
1492 return 0;
1493 }
1494 @Override
1495 public void writeToParcel(Parcel out, int flags) {
Dan Sandler86647982015-05-13 23:41:13 -04001496 final Icon ic = getIcon();
1497 if (ic != null) {
1498 out.writeInt(1);
1499 ic.writeToParcel(out, 0);
1500 } else {
1501 out.writeInt(0);
1502 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001503 TextUtils.writeToParcel(title, out, flags);
1504 if (actionIntent != null) {
1505 out.writeInt(1);
1506 actionIntent.writeToParcel(out, flags);
1507 } else {
1508 out.writeInt(0);
1509 }
Griff Hazen959591e2014-05-15 22:26:18 -07001510 out.writeBundle(mExtras);
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001511 out.writeTypedArray(mRemoteInputs, flags);
Alex Hills42b0c4d2016-04-26 13:35:36 -04001512 out.writeInt(mAllowGeneratedReplies ? 1 : 0);
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001513 }
Griff Hazen959591e2014-05-15 22:26:18 -07001514 public static final Parcelable.Creator<Action> CREATOR =
1515 new Parcelable.Creator<Action>() {
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001516 public Action createFromParcel(Parcel in) {
1517 return new Action(in);
1518 }
1519 public Action[] newArray(int size) {
1520 return new Action[size];
1521 }
1522 };
Griff Hazen61a9e862014-05-22 16:05:19 -07001523
1524 /**
1525 * Extender interface for use with {@link Builder#extend}. Extenders may be used to add
1526 * metadata or change options on an action builder.
1527 */
1528 public interface Extender {
1529 /**
1530 * Apply this extender to a notification action builder.
1531 * @param builder the builder to be modified.
1532 * @return the build object for chaining.
1533 */
1534 public Builder extend(Builder builder);
1535 }
1536
1537 /**
1538 * Wearable extender for notification actions. To add extensions to an action,
1539 * create a new {@link android.app.Notification.Action.WearableExtender} object using
1540 * the {@code WearableExtender()} constructor and apply it to a
1541 * {@link android.app.Notification.Action.Builder} using
1542 * {@link android.app.Notification.Action.Builder#extend}.
1543 *
1544 * <pre class="prettyprint">
1545 * Notification.Action action = new Notification.Action.Builder(
1546 * R.drawable.archive_all, "Archive all", actionIntent)
Griff Hazen14f57992014-05-26 09:07:14 -07001547 * .extend(new Notification.Action.WearableExtender()
Griff Hazen61a9e862014-05-22 16:05:19 -07001548 * .setAvailableOffline(false))
Griff Hazen14f57992014-05-26 09:07:14 -07001549 * .build();</pre>
Griff Hazen61a9e862014-05-22 16:05:19 -07001550 */
1551 public static final class WearableExtender implements Extender {
1552 /** Notification action extra which contains wearable extensions */
1553 private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS";
1554
Pete Gastaf6781d2014-10-07 15:17:05 -04001555 // Keys within EXTRA_WEARABLE_EXTENSIONS for wearable options.
Griff Hazen61a9e862014-05-22 16:05:19 -07001556 private static final String KEY_FLAGS = "flags";
Pete Gastaf6781d2014-10-07 15:17:05 -04001557 private static final String KEY_IN_PROGRESS_LABEL = "inProgressLabel";
1558 private static final String KEY_CONFIRM_LABEL = "confirmLabel";
1559 private static final String KEY_CANCEL_LABEL = "cancelLabel";
Griff Hazen61a9e862014-05-22 16:05:19 -07001560
1561 // Flags bitwise-ored to mFlags
1562 private static final int FLAG_AVAILABLE_OFFLINE = 0x1;
Alex Hills9ab3a232016-04-05 14:54:56 -04001563 private static final int FLAG_HINT_LAUNCHES_ACTIVITY = 1 << 1;
Alex Hills9f087612016-06-07 09:08:59 -04001564 private static final int FLAG_HINT_DISPLAY_INLINE = 1 << 2;
Griff Hazen61a9e862014-05-22 16:05:19 -07001565
1566 // Default value for flags integer
1567 private static final int DEFAULT_FLAGS = FLAG_AVAILABLE_OFFLINE;
1568
1569 private int mFlags = DEFAULT_FLAGS;
1570
Pete Gastaf6781d2014-10-07 15:17:05 -04001571 private CharSequence mInProgressLabel;
1572 private CharSequence mConfirmLabel;
1573 private CharSequence mCancelLabel;
1574
Griff Hazen61a9e862014-05-22 16:05:19 -07001575 /**
1576 * Create a {@link android.app.Notification.Action.WearableExtender} with default
1577 * options.
1578 */
1579 public WearableExtender() {
1580 }
1581
1582 /**
1583 * Create a {@link android.app.Notification.Action.WearableExtender} by reading
1584 * wearable options present in an existing notification action.
1585 * @param action the notification action to inspect.
1586 */
1587 public WearableExtender(Action action) {
1588 Bundle wearableBundle = action.getExtras().getBundle(EXTRA_WEARABLE_EXTENSIONS);
1589 if (wearableBundle != null) {
1590 mFlags = wearableBundle.getInt(KEY_FLAGS, DEFAULT_FLAGS);
Pete Gastaf6781d2014-10-07 15:17:05 -04001591 mInProgressLabel = wearableBundle.getCharSequence(KEY_IN_PROGRESS_LABEL);
1592 mConfirmLabel = wearableBundle.getCharSequence(KEY_CONFIRM_LABEL);
1593 mCancelLabel = wearableBundle.getCharSequence(KEY_CANCEL_LABEL);
Griff Hazen61a9e862014-05-22 16:05:19 -07001594 }
1595 }
1596
1597 /**
1598 * Apply wearable extensions to a notification action that is being built. This is
1599 * typically called by the {@link android.app.Notification.Action.Builder#extend}
1600 * method of {@link android.app.Notification.Action.Builder}.
1601 */
1602 @Override
1603 public Action.Builder extend(Action.Builder builder) {
1604 Bundle wearableBundle = new Bundle();
1605
1606 if (mFlags != DEFAULT_FLAGS) {
1607 wearableBundle.putInt(KEY_FLAGS, mFlags);
1608 }
Pete Gastaf6781d2014-10-07 15:17:05 -04001609 if (mInProgressLabel != null) {
1610 wearableBundle.putCharSequence(KEY_IN_PROGRESS_LABEL, mInProgressLabel);
1611 }
1612 if (mConfirmLabel != null) {
1613 wearableBundle.putCharSequence(KEY_CONFIRM_LABEL, mConfirmLabel);
1614 }
1615 if (mCancelLabel != null) {
1616 wearableBundle.putCharSequence(KEY_CANCEL_LABEL, mCancelLabel);
1617 }
Griff Hazen61a9e862014-05-22 16:05:19 -07001618
1619 builder.getExtras().putBundle(EXTRA_WEARABLE_EXTENSIONS, wearableBundle);
1620 return builder;
1621 }
1622
1623 @Override
1624 public WearableExtender clone() {
1625 WearableExtender that = new WearableExtender();
1626 that.mFlags = this.mFlags;
Pete Gastaf6781d2014-10-07 15:17:05 -04001627 that.mInProgressLabel = this.mInProgressLabel;
1628 that.mConfirmLabel = this.mConfirmLabel;
1629 that.mCancelLabel = this.mCancelLabel;
Griff Hazen61a9e862014-05-22 16:05:19 -07001630 return that;
1631 }
1632
1633 /**
1634 * Set whether this action is available when the wearable device is not connected to
1635 * a companion device. The user can still trigger this action when the wearable device is
1636 * offline, but a visual hint will indicate that the action may not be available.
1637 * Defaults to true.
1638 */
1639 public WearableExtender setAvailableOffline(boolean availableOffline) {
1640 setFlag(FLAG_AVAILABLE_OFFLINE, availableOffline);
1641 return this;
1642 }
1643
1644 /**
1645 * Get whether this action is available when the wearable device is not connected to
1646 * a companion device. The user can still trigger this action when the wearable device is
1647 * offline, but a visual hint will indicate that the action may not be available.
1648 * Defaults to true.
1649 */
1650 public boolean isAvailableOffline() {
1651 return (mFlags & FLAG_AVAILABLE_OFFLINE) != 0;
1652 }
1653
1654 private void setFlag(int mask, boolean value) {
1655 if (value) {
1656 mFlags |= mask;
1657 } else {
1658 mFlags &= ~mask;
1659 }
1660 }
Pete Gastaf6781d2014-10-07 15:17:05 -04001661
1662 /**
1663 * Set a label to display while the wearable is preparing to automatically execute the
1664 * action. This is usually a 'ing' verb ending in ellipsis like "Sending..."
1665 *
1666 * @param label the label to display while the action is being prepared to execute
1667 * @return this object for method chaining
1668 */
1669 public WearableExtender setInProgressLabel(CharSequence label) {
1670 mInProgressLabel = label;
1671 return this;
1672 }
1673
1674 /**
1675 * Get the label to display while the wearable is preparing to automatically execute
1676 * the action. This is usually a 'ing' verb ending in ellipsis like "Sending..."
1677 *
1678 * @return the label to display while the action is being prepared to execute
1679 */
1680 public CharSequence getInProgressLabel() {
1681 return mInProgressLabel;
1682 }
1683
1684 /**
1685 * Set a label to display to confirm that the action should be executed.
1686 * This is usually an imperative verb like "Send".
1687 *
1688 * @param label the label to confirm the action should be executed
1689 * @return this object for method chaining
1690 */
1691 public WearableExtender setConfirmLabel(CharSequence label) {
1692 mConfirmLabel = label;
1693 return this;
1694 }
1695
1696 /**
1697 * Get the label to display to confirm that the action should be executed.
1698 * This is usually an imperative verb like "Send".
1699 *
1700 * @return the label to confirm the action should be executed
1701 */
1702 public CharSequence getConfirmLabel() {
1703 return mConfirmLabel;
1704 }
1705
1706 /**
1707 * Set a label to display to cancel the action.
1708 * This is usually an imperative verb, like "Cancel".
1709 *
1710 * @param label the label to display to cancel the action
1711 * @return this object for method chaining
1712 */
1713 public WearableExtender setCancelLabel(CharSequence label) {
1714 mCancelLabel = label;
1715 return this;
1716 }
1717
1718 /**
1719 * Get the label to display to cancel the action.
1720 * This is usually an imperative verb like "Cancel".
1721 *
1722 * @return the label to display to cancel the action
1723 */
1724 public CharSequence getCancelLabel() {
1725 return mCancelLabel;
1726 }
Alex Hills9ab3a232016-04-05 14:54:56 -04001727
1728 /**
1729 * Set a hint that this Action will launch an {@link Activity} directly, telling the
1730 * platform that it can generate the appropriate transitions.
1731 * @param hintLaunchesActivity {@code true} if the content intent will launch
1732 * an activity and transitions should be generated, false otherwise.
1733 * @return this object for method chaining
1734 */
Alex Hills4ec3ff42016-04-12 11:36:18 -04001735 public WearableExtender setHintLaunchesActivity(
Alex Hills9ab3a232016-04-05 14:54:56 -04001736 boolean hintLaunchesActivity) {
1737 setFlag(FLAG_HINT_LAUNCHES_ACTIVITY, hintLaunchesActivity);
1738 return this;
1739 }
1740
1741 /**
1742 * Get a hint that this Action will launch an {@link Activity} directly, telling the
1743 * platform that it can generate the appropriate transitions
1744 * @return {@code true} if the content intent will launch an activity and transitions
1745 * should be generated, false otherwise. The default value is {@code false} if this was
1746 * never set.
1747 */
Alex Hills4ec3ff42016-04-12 11:36:18 -04001748 public boolean getHintLaunchesActivity() {
Alex Hills9ab3a232016-04-05 14:54:56 -04001749 return (mFlags & FLAG_HINT_LAUNCHES_ACTIVITY) != 0;
1750 }
Alex Hills9f087612016-06-07 09:08:59 -04001751
1752 /**
1753 * Set a hint that this Action should be displayed inline.
1754 *
1755 * @param hintDisplayInline {@code true} if action should be displayed inline, false
1756 * otherwise
1757 * @return this object for method chaining
1758 */
1759 public WearableExtender setHintDisplayActionInline(
1760 boolean hintDisplayInline) {
1761 setFlag(FLAG_HINT_DISPLAY_INLINE, hintDisplayInline);
1762 return this;
1763 }
1764
1765 /**
1766 * Get a hint that this Action should be displayed inline.
1767 *
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08001768 * @return {@code true} if the Action should be displayed inline, {@code false}
Alex Hills9f087612016-06-07 09:08:59 -04001769 * otherwise. The default value is {@code false} if this was never set.
1770 */
1771 public boolean getHintDisplayActionInline() {
1772 return (mFlags & FLAG_HINT_DISPLAY_INLINE) != 0;
1773 }
Griff Hazen61a9e862014-05-22 16:05:19 -07001774 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001775 }
1776
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001777 /**
1778 * Array of all {@link Action} structures attached to this notification by
1779 * {@link Builder#addAction(int, CharSequence, PendingIntent)}. Mostly useful for instances of
1780 * {@link android.service.notification.NotificationListenerService} that provide an alternative
1781 * interface for invoking actions.
1782 */
Daniel Sandlerea2a3172013-02-20 22:24:20 -05001783 public Action[] actions;
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001784
1785 /**
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001786 * Replacement version of this notification whose content will be shown
1787 * in an insecure context such as atop a secure keyguard. See {@link #visibility}
1788 * and {@link #VISIBILITY_PUBLIC}.
1789 */
1790 public Notification publicVersion;
1791
1792 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001793 * Constructs a Notification object with default values.
Joe Onorato46439ce2010-11-19 13:56:21 -08001794 * You might want to consider using {@link Builder} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001795 */
1796 public Notification()
1797 {
1798 this.when = System.currentTimeMillis();
Selim Cinekb85f36fd2016-04-20 18:46:36 -07001799 this.creationTime = System.currentTimeMillis();
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001800 this.priority = PRIORITY_DEFAULT;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001801 }
1802
1803 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001804 * @hide
1805 */
1806 public Notification(Context context, int icon, CharSequence tickerText, long when,
1807 CharSequence contentTitle, CharSequence contentText, Intent contentIntent)
1808 {
Chris Wren1ce4b6d2015-06-11 10:19:43 -04001809 new Builder(context)
1810 .setWhen(when)
1811 .setSmallIcon(icon)
1812 .setTicker(tickerText)
1813 .setContentTitle(contentTitle)
1814 .setContentText(contentText)
1815 .setContentIntent(PendingIntent.getActivity(context, 0, contentIntent, 0))
1816 .buildInto(this);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001817 }
1818
1819 /**
1820 * Constructs a Notification object with the information needed to
1821 * have a status bar icon without the standard expanded view.
1822 *
1823 * @param icon The resource id of the icon to put in the status bar.
1824 * @param tickerText The text that flows by in the status bar when the notification first
1825 * activates.
1826 * @param when The time to show in the time field. In the System.currentTimeMillis
1827 * timebase.
Joe Onorato46439ce2010-11-19 13:56:21 -08001828 *
1829 * @deprecated Use {@link Builder} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001830 */
Joe Onorato46439ce2010-11-19 13:56:21 -08001831 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001832 public Notification(int icon, CharSequence tickerText, long when)
1833 {
1834 this.icon = icon;
1835 this.tickerText = tickerText;
1836 this.when = when;
Selim Cinekb85f36fd2016-04-20 18:46:36 -07001837 this.creationTime = System.currentTimeMillis();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001838 }
1839
1840 /**
1841 * Unflatten the notification from a parcel.
1842 */
Svet Ganovddb94882016-06-23 19:55:24 -07001843 @SuppressWarnings("unchecked")
1844 public Notification(Parcel parcel) {
1845 // IMPORTANT: Add unmarshaling code in readFromParcel as the pending
1846 // intents in extras are always written as the last entry.
1847 readFromParcelImpl(parcel);
1848 // Must be read last!
Felipe Lemedd85da62016-06-28 11:29:54 -07001849 allPendingIntents = (ArraySet<PendingIntent>) parcel.readArraySet(null);
Svet Ganovddb94882016-06-23 19:55:24 -07001850 }
1851
1852 private void readFromParcelImpl(Parcel parcel)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001853 {
1854 int version = parcel.readInt();
1855
Dianne Hackborn98305522017-05-05 17:53:53 -07001856 whitelistToken = parcel.readStrongBinder();
1857 if (whitelistToken == null) {
1858 whitelistToken = processWhitelistToken;
1859 }
1860 // Propagate this token to all pending intents that are unmarshalled from the parcel.
1861 parcel.setClassCookie(PendingIntent.class, whitelistToken);
1862
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001863 when = parcel.readLong();
Selim Cinekb85f36fd2016-04-20 18:46:36 -07001864 creationTime = parcel.readLong();
Dan Sandler3936e7a2015-05-19 20:59:12 -04001865 if (parcel.readInt() != 0) {
1866 mSmallIcon = Icon.CREATOR.createFromParcel(parcel);
Dan Sandler4e787062015-06-17 15:09:48 -04001867 if (mSmallIcon.getType() == Icon.TYPE_RESOURCE) {
1868 icon = mSmallIcon.getResId();
1869 }
Dan Sandler3936e7a2015-05-19 20:59:12 -04001870 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001871 number = parcel.readInt();
1872 if (parcel.readInt() != 0) {
1873 contentIntent = PendingIntent.CREATOR.createFromParcel(parcel);
1874 }
1875 if (parcel.readInt() != 0) {
1876 deleteIntent = PendingIntent.CREATOR.createFromParcel(parcel);
1877 }
1878 if (parcel.readInt() != 0) {
1879 tickerText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
1880 }
1881 if (parcel.readInt() != 0) {
Joe Onorato46439ce2010-11-19 13:56:21 -08001882 tickerView = RemoteViews.CREATOR.createFromParcel(parcel);
Joe Onoratoef1e7762010-09-17 18:38:38 -04001883 }
1884 if (parcel.readInt() != 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001885 contentView = RemoteViews.CREATOR.createFromParcel(parcel);
1886 }
Joe Onorato561d3852010-11-20 18:09:34 -08001887 if (parcel.readInt() != 0) {
Dan Sandlerd63f9322015-05-06 15:18:49 -04001888 mLargeIcon = Icon.CREATOR.createFromParcel(parcel);
Joe Onorato561d3852010-11-20 18:09:34 -08001889 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001890 defaults = parcel.readInt();
1891 flags = parcel.readInt();
1892 if (parcel.readInt() != 0) {
1893 sound = Uri.CREATOR.createFromParcel(parcel);
1894 }
1895
1896 audioStreamType = parcel.readInt();
John Spurlockc0650f022014-07-19 13:22:39 -04001897 if (parcel.readInt() != 0) {
1898 audioAttributes = AudioAttributes.CREATOR.createFromParcel(parcel);
1899 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001900 vibrate = parcel.createLongArray();
1901 ledARGB = parcel.readInt();
1902 ledOnMS = parcel.readInt();
1903 ledOffMS = parcel.readInt();
1904 iconLevel = parcel.readInt();
Daniel Sandlere46cbd32010-06-17 10:35:26 -04001905
1906 if (parcel.readInt() != 0) {
1907 fullScreenIntent = PendingIntent.CREATOR.createFromParcel(parcel);
1908 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001909
1910 priority = parcel.readInt();
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001911
John Spurlockfd7f1e02014-03-18 16:41:57 -04001912 category = parcel.readString();
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001913
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001914 mGroupKey = parcel.readString();
1915
1916 mSortKey = parcel.readString();
1917
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001918 extras = Bundle.setDefusable(parcel.readBundle(), true); // may be null
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001919
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001920 actions = parcel.createTypedArray(Action.CREATOR); // may be null
1921
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001922 if (parcel.readInt() != 0) {
Daniel Sandlerf3b73432012-03-27 15:01:25 -04001923 bigContentView = RemoteViews.CREATOR.createFromParcel(parcel);
1924 }
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001925
Chris Wren8fd39ec2014-02-27 17:43:26 -05001926 if (parcel.readInt() != 0) {
1927 headsUpContentView = RemoteViews.CREATOR.createFromParcel(parcel);
1928 }
1929
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001930 visibility = parcel.readInt();
1931
1932 if (parcel.readInt() != 0) {
1933 publicVersion = Notification.CREATOR.createFromParcel(parcel);
1934 }
Dan Sandler26e81cf2014-05-06 10:01:27 -04001935
1936 color = parcel.readInt();
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04001937
1938 if (parcel.readInt() != 0) {
1939 mChannelId = parcel.readString();
1940 }
Julia Reynolds2a128742016-11-28 14:29:25 -05001941 mTimeout = parcel.readLong();
Julia Reynolds13d898c2017-02-02 12:22:05 -05001942
1943 if (parcel.readInt() != 0) {
1944 mShortcutId = parcel.readString();
1945 }
1946
1947 mBadgeIcon = parcel.readInt();
Julia Reynolds3aedded2017-03-31 14:42:09 -04001948
1949 if (parcel.readInt() != 0) {
1950 mSettingsText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
1951 }
Julia Reynoldsa79c3712017-04-21 10:29:57 -04001952
1953 mGroupAlertBehavior = parcel.readInt();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001954 }
1955
Andy Stadler110988c2010-12-03 14:29:16 -08001956 @Override
Joe Onorato18e69df2010-05-17 22:26:12 -07001957 public Notification clone() {
1958 Notification that = new Notification();
Daniel Sandler1a497d32013-04-18 14:52:45 -04001959 cloneInto(that, true);
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001960 return that;
1961 }
Joe Onorato18e69df2010-05-17 22:26:12 -07001962
Daniel Sandler1a497d32013-04-18 14:52:45 -04001963 /**
1964 * Copy all (or if heavy is false, all except Bitmaps and RemoteViews) members
1965 * of this into that.
1966 * @hide
1967 */
1968 public void cloneInto(Notification that, boolean heavy) {
Dianne Hackborn98305522017-05-05 17:53:53 -07001969 that.whitelistToken = this.whitelistToken;
Joe Onorato18e69df2010-05-17 22:26:12 -07001970 that.when = this.when;
Selim Cinekb85f36fd2016-04-20 18:46:36 -07001971 that.creationTime = this.creationTime;
Dan Sandlerd63f9322015-05-06 15:18:49 -04001972 that.mSmallIcon = this.mSmallIcon;
Joe Onorato18e69df2010-05-17 22:26:12 -07001973 that.number = this.number;
1974
1975 // PendingIntents are global, so there's no reason (or way) to clone them.
1976 that.contentIntent = this.contentIntent;
1977 that.deleteIntent = this.deleteIntent;
Daniel Sandlere46cbd32010-06-17 10:35:26 -04001978 that.fullScreenIntent = this.fullScreenIntent;
Joe Onorato18e69df2010-05-17 22:26:12 -07001979
1980 if (this.tickerText != null) {
1981 that.tickerText = this.tickerText.toString();
1982 }
Daniel Sandler1a497d32013-04-18 14:52:45 -04001983 if (heavy && this.tickerView != null) {
Joe Onorato46439ce2010-11-19 13:56:21 -08001984 that.tickerView = this.tickerView.clone();
Joe Onoratoef1e7762010-09-17 18:38:38 -04001985 }
Daniel Sandler1a497d32013-04-18 14:52:45 -04001986 if (heavy && this.contentView != null) {
Joe Onorato18e69df2010-05-17 22:26:12 -07001987 that.contentView = this.contentView.clone();
1988 }
Dan Sandlerd63f9322015-05-06 15:18:49 -04001989 if (heavy && this.mLargeIcon != null) {
1990 that.mLargeIcon = this.mLargeIcon;
Joe Onorato561d3852010-11-20 18:09:34 -08001991 }
Jozef BABJAKa8b91832011-02-22 08:05:08 +01001992 that.iconLevel = this.iconLevel;
Joe Onorato18e69df2010-05-17 22:26:12 -07001993 that.sound = this.sound; // android.net.Uri is immutable
1994 that.audioStreamType = this.audioStreamType;
John Spurlockc0650f022014-07-19 13:22:39 -04001995 if (this.audioAttributes != null) {
1996 that.audioAttributes = new AudioAttributes.Builder(this.audioAttributes).build();
1997 }
Joe Onorato18e69df2010-05-17 22:26:12 -07001998
1999 final long[] vibrate = this.vibrate;
2000 if (vibrate != null) {
2001 final int N = vibrate.length;
2002 final long[] vib = that.vibrate = new long[N];
2003 System.arraycopy(vibrate, 0, vib, 0, N);
2004 }
2005
2006 that.ledARGB = this.ledARGB;
2007 that.ledOnMS = this.ledOnMS;
2008 that.ledOffMS = this.ledOffMS;
2009 that.defaults = this.defaults;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002010
Joe Onorato18e69df2010-05-17 22:26:12 -07002011 that.flags = this.flags;
2012
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002013 that.priority = this.priority;
Joe Malin8d40d042012-11-05 11:36:40 -08002014
John Spurlockfd7f1e02014-03-18 16:41:57 -04002015 that.category = this.category;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002016
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002017 that.mGroupKey = this.mGroupKey;
2018
2019 that.mSortKey = this.mSortKey;
2020
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002021 if (this.extras != null) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04002022 try {
2023 that.extras = new Bundle(this.extras);
2024 // will unparcel
2025 that.extras.size();
2026 } catch (BadParcelableException e) {
2027 Log.e(TAG, "could not unparcel extras from notification: " + this, e);
2028 that.extras = null;
2029 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002030 }
2031
Felipe Lemedd85da62016-06-28 11:29:54 -07002032 if (!ArrayUtils.isEmpty(allPendingIntents)) {
2033 that.allPendingIntents = new ArraySet<>(allPendingIntents);
Svet Ganovddb94882016-06-23 19:55:24 -07002034 }
2035
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002036 if (this.actions != null) {
2037 that.actions = new Action[this.actions.length];
2038 for(int i=0; i<this.actions.length; i++) {
liangweikang63b03b52017-03-16 19:22:15 +08002039 if ( this.actions[i] != null) {
2040 that.actions[i] = this.actions[i].clone();
2041 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002042 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002043 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002044
Daniel Sandler1a497d32013-04-18 14:52:45 -04002045 if (heavy && this.bigContentView != null) {
Daniel Sandlerf3b73432012-03-27 15:01:25 -04002046 that.bigContentView = this.bigContentView.clone();
2047 }
Daniel Sandler1a497d32013-04-18 14:52:45 -04002048
Chris Wren8fd39ec2014-02-27 17:43:26 -05002049 if (heavy && this.headsUpContentView != null) {
2050 that.headsUpContentView = this.headsUpContentView.clone();
2051 }
2052
Dan Sandler0bf2ed82013-12-21 23:33:41 -06002053 that.visibility = this.visibility;
2054
2055 if (this.publicVersion != null) {
2056 that.publicVersion = new Notification();
2057 this.publicVersion.cloneInto(that.publicVersion, heavy);
2058 }
2059
Dan Sandler26e81cf2014-05-06 10:01:27 -04002060 that.color = this.color;
2061
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04002062 that.mChannelId = this.mChannelId;
Julia Reynolds2a128742016-11-28 14:29:25 -05002063 that.mTimeout = this.mTimeout;
Julia Reynolds3aedded2017-03-31 14:42:09 -04002064 that.mShortcutId = this.mShortcutId;
2065 that.mBadgeIcon = this.mBadgeIcon;
2066 that.mSettingsText = this.mSettingsText;
Julia Reynoldsa79c3712017-04-21 10:29:57 -04002067 that.mGroupAlertBehavior = this.mGroupAlertBehavior;
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04002068
Daniel Sandler1a497d32013-04-18 14:52:45 -04002069 if (!heavy) {
2070 that.lightenPayload(); // will clean out extras
2071 }
2072 }
2073
2074 /**
2075 * Removes heavyweight parts of the Notification object for archival or for sending to
2076 * listeners when the full contents are not necessary.
2077 * @hide
2078 */
2079 public final void lightenPayload() {
2080 tickerView = null;
2081 contentView = null;
2082 bigContentView = null;
Chris Wren8fd39ec2014-02-27 17:43:26 -05002083 headsUpContentView = null;
Dan Sandlerd63f9322015-05-06 15:18:49 -04002084 mLargeIcon = null;
Dan Sandler50128532015-12-08 15:42:41 -05002085 if (extras != null && !extras.isEmpty()) {
2086 final Set<String> keyset = extras.keySet();
2087 final int N = keyset.size();
2088 final String[] keys = keyset.toArray(new String[N]);
2089 for (int i=0; i<N; i++) {
2090 final String key = keys[i];
Dmitri Plotnikov22281362017-01-30 11:16:21 -08002091 if (TvExtender.EXTRA_TV_EXTENDER.equals(key)) {
2092 continue;
2093 }
Dan Sandler50128532015-12-08 15:42:41 -05002094 final Object obj = extras.get(key);
2095 if (obj != null &&
2096 ( obj instanceof Parcelable
2097 || obj instanceof Parcelable[]
2098 || obj instanceof SparseArray
2099 || obj instanceof ArrayList)) {
2100 extras.remove(key);
2101 }
2102 }
Daniel Sandler1a497d32013-04-18 14:52:45 -04002103 }
Joe Onorato18e69df2010-05-17 22:26:12 -07002104 }
2105
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04002106 /**
2107 * Make sure this CharSequence is safe to put into a bundle, which basically
2108 * means it had better not be some custom Parcelable implementation.
2109 * @hide
2110 */
2111 public static CharSequence safeCharSequence(CharSequence cs) {
Christoph Studer535ec612014-09-03 15:47:47 +02002112 if (cs == null) return cs;
2113 if (cs.length() > MAX_CHARSEQUENCE_LENGTH) {
2114 cs = cs.subSequence(0, MAX_CHARSEQUENCE_LENGTH);
2115 }
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04002116 if (cs instanceof Parcelable) {
2117 Log.e(TAG, "warning: " + cs.getClass().getCanonicalName()
2118 + " instance is a custom Parcelable and not allowed in Notification");
2119 return cs.toString();
2120 }
Selim Cinek60a54252016-02-26 17:03:25 -08002121 return removeTextSizeSpans(cs);
2122 }
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04002123
Selim Cinek60a54252016-02-26 17:03:25 -08002124 private static CharSequence removeTextSizeSpans(CharSequence charSequence) {
2125 if (charSequence instanceof Spanned) {
2126 Spanned ss = (Spanned) charSequence;
2127 Object[] spans = ss.getSpans(0, ss.length(), Object.class);
2128 SpannableStringBuilder builder = new SpannableStringBuilder(ss.toString());
2129 for (Object span : spans) {
2130 Object resultSpan = span;
Selim Cinek89991a22016-03-07 19:51:54 -08002131 if (resultSpan instanceof CharacterStyle) {
2132 resultSpan = ((CharacterStyle) span).getUnderlying();
2133 }
2134 if (resultSpan instanceof TextAppearanceSpan) {
2135 TextAppearanceSpan originalSpan = (TextAppearanceSpan) resultSpan;
Selim Cinek60a54252016-02-26 17:03:25 -08002136 resultSpan = new TextAppearanceSpan(
2137 originalSpan.getFamily(),
2138 originalSpan.getTextStyle(),
2139 -1,
2140 originalSpan.getTextColor(),
2141 originalSpan.getLinkTextColor());
Selim Cinek89991a22016-03-07 19:51:54 -08002142 } else if (resultSpan instanceof RelativeSizeSpan
2143 || resultSpan instanceof AbsoluteSizeSpan) {
Selim Cinek60a54252016-02-26 17:03:25 -08002144 continue;
Selim Cinek89991a22016-03-07 19:51:54 -08002145 } else {
2146 resultSpan = span;
Selim Cinek60a54252016-02-26 17:03:25 -08002147 }
2148 builder.setSpan(resultSpan, ss.getSpanStart(span), ss.getSpanEnd(span),
2149 ss.getSpanFlags(span));
2150 }
2151 return builder;
2152 }
2153 return charSequence;
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04002154 }
2155
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002156 public int describeContents() {
2157 return 0;
2158 }
2159
2160 /**
Dan Sandler4e787062015-06-17 15:09:48 -04002161 * Flatten this notification into a parcel.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002162 */
Svet Ganovddb94882016-06-23 19:55:24 -07002163 public void writeToParcel(Parcel parcel, int flags) {
2164 // We need to mark all pending intents getting into the notification
2165 // system as being put there to later allow the notification ranker
2166 // to launch them and by doing so add the app to the battery saver white
2167 // list for a short period of time. The problem is that the system
2168 // cannot look into the extras as there may be parcelables there that
2169 // the platform does not know how to handle. To go around that we have
2170 // an explicit list of the pending intents in the extras bundle.
Felipe Lemedd85da62016-06-28 11:29:54 -07002171 final boolean collectPendingIntents = (allPendingIntents == null);
Svet Ganovddb94882016-06-23 19:55:24 -07002172 if (collectPendingIntents) {
2173 PendingIntent.setOnMarshaledListener(
2174 (PendingIntent intent, Parcel out, int outFlags) -> {
2175 if (parcel == out) {
Felipe Lemedd85da62016-06-28 11:29:54 -07002176 if (allPendingIntents == null) {
2177 allPendingIntents = new ArraySet<>();
Svet Ganovddb94882016-06-23 19:55:24 -07002178 }
Felipe Lemedd85da62016-06-28 11:29:54 -07002179 allPendingIntents.add(intent);
Svet Ganovddb94882016-06-23 19:55:24 -07002180 }
2181 });
2182 }
2183 try {
2184 // IMPORTANT: Add marshaling code in writeToParcelImpl as we
Julia Reynolds13d898c2017-02-02 12:22:05 -05002185 // want to intercept all pending events written to the parcel.
Svet Ganovddb94882016-06-23 19:55:24 -07002186 writeToParcelImpl(parcel, flags);
2187 // Must be written last!
Felipe Lemedd85da62016-06-28 11:29:54 -07002188 parcel.writeArraySet(allPendingIntents);
Svet Ganovddb94882016-06-23 19:55:24 -07002189 } finally {
2190 if (collectPendingIntents) {
2191 PendingIntent.setOnMarshaledListener(null);
2192 }
2193 }
2194 }
2195
2196 private void writeToParcelImpl(Parcel parcel, int flags) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002197 parcel.writeInt(1);
2198
Dianne Hackborn98305522017-05-05 17:53:53 -07002199 parcel.writeStrongBinder(whitelistToken);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002200 parcel.writeLong(when);
Selim Cinekb85f36fd2016-04-20 18:46:36 -07002201 parcel.writeLong(creationTime);
Dan Sandler4e787062015-06-17 15:09:48 -04002202 if (mSmallIcon == null && icon != 0) {
2203 // you snuck an icon in here without using the builder; let's try to keep it
2204 mSmallIcon = Icon.createWithResource("", icon);
2205 }
Dan Sandler3936e7a2015-05-19 20:59:12 -04002206 if (mSmallIcon != null) {
2207 parcel.writeInt(1);
2208 mSmallIcon.writeToParcel(parcel, 0);
2209 } else {
2210 parcel.writeInt(0);
2211 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002212 parcel.writeInt(number);
2213 if (contentIntent != null) {
2214 parcel.writeInt(1);
2215 contentIntent.writeToParcel(parcel, 0);
2216 } else {
2217 parcel.writeInt(0);
2218 }
2219 if (deleteIntent != null) {
2220 parcel.writeInt(1);
2221 deleteIntent.writeToParcel(parcel, 0);
2222 } else {
2223 parcel.writeInt(0);
2224 }
2225 if (tickerText != null) {
2226 parcel.writeInt(1);
2227 TextUtils.writeToParcel(tickerText, parcel, flags);
2228 } else {
2229 parcel.writeInt(0);
2230 }
Joe Onorato46439ce2010-11-19 13:56:21 -08002231 if (tickerView != null) {
Joe Onoratoef1e7762010-09-17 18:38:38 -04002232 parcel.writeInt(1);
Joe Onorato46439ce2010-11-19 13:56:21 -08002233 tickerView.writeToParcel(parcel, 0);
Joe Onoratoef1e7762010-09-17 18:38:38 -04002234 } else {
2235 parcel.writeInt(0);
2236 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002237 if (contentView != null) {
2238 parcel.writeInt(1);
2239 contentView.writeToParcel(parcel, 0);
2240 } else {
2241 parcel.writeInt(0);
2242 }
Selim Cinek279fa862016-06-14 10:57:25 -07002243 if (mLargeIcon == null && largeIcon != null) {
2244 // you snuck an icon in here without using the builder; let's try to keep it
2245 mLargeIcon = Icon.createWithBitmap(largeIcon);
2246 }
Dan Sandlerd63f9322015-05-06 15:18:49 -04002247 if (mLargeIcon != null) {
Joe Onorato561d3852010-11-20 18:09:34 -08002248 parcel.writeInt(1);
Dan Sandlerd63f9322015-05-06 15:18:49 -04002249 mLargeIcon.writeToParcel(parcel, 0);
Joe Onorato561d3852010-11-20 18:09:34 -08002250 } else {
2251 parcel.writeInt(0);
2252 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002253
2254 parcel.writeInt(defaults);
2255 parcel.writeInt(this.flags);
2256
2257 if (sound != null) {
2258 parcel.writeInt(1);
2259 sound.writeToParcel(parcel, 0);
2260 } else {
2261 parcel.writeInt(0);
2262 }
2263 parcel.writeInt(audioStreamType);
John Spurlockc0650f022014-07-19 13:22:39 -04002264
2265 if (audioAttributes != null) {
2266 parcel.writeInt(1);
2267 audioAttributes.writeToParcel(parcel, 0);
2268 } else {
2269 parcel.writeInt(0);
2270 }
2271
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002272 parcel.writeLongArray(vibrate);
2273 parcel.writeInt(ledARGB);
2274 parcel.writeInt(ledOnMS);
2275 parcel.writeInt(ledOffMS);
2276 parcel.writeInt(iconLevel);
Daniel Sandlere46cbd32010-06-17 10:35:26 -04002277
2278 if (fullScreenIntent != null) {
2279 parcel.writeInt(1);
2280 fullScreenIntent.writeToParcel(parcel, 0);
2281 } else {
2282 parcel.writeInt(0);
2283 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002284
2285 parcel.writeInt(priority);
Joe Malin8d40d042012-11-05 11:36:40 -08002286
John Spurlockfd7f1e02014-03-18 16:41:57 -04002287 parcel.writeString(category);
Joe Malin8d40d042012-11-05 11:36:40 -08002288
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002289 parcel.writeString(mGroupKey);
2290
2291 parcel.writeString(mSortKey);
2292
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002293 parcel.writeBundle(extras); // null ok
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002294
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002295 parcel.writeTypedArray(actions, 0); // null ok
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002296
Daniel Sandlerf3b73432012-03-27 15:01:25 -04002297 if (bigContentView != null) {
2298 parcel.writeInt(1);
2299 bigContentView.writeToParcel(parcel, 0);
2300 } else {
2301 parcel.writeInt(0);
2302 }
Dan Sandler0bf2ed82013-12-21 23:33:41 -06002303
Chris Wren8fd39ec2014-02-27 17:43:26 -05002304 if (headsUpContentView != null) {
2305 parcel.writeInt(1);
2306 headsUpContentView.writeToParcel(parcel, 0);
2307 } else {
2308 parcel.writeInt(0);
2309 }
2310
Dan Sandler0bf2ed82013-12-21 23:33:41 -06002311 parcel.writeInt(visibility);
2312
2313 if (publicVersion != null) {
2314 parcel.writeInt(1);
2315 publicVersion.writeToParcel(parcel, 0);
2316 } else {
2317 parcel.writeInt(0);
2318 }
Dan Sandler26e81cf2014-05-06 10:01:27 -04002319
2320 parcel.writeInt(color);
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04002321
2322 if (mChannelId != null) {
2323 parcel.writeInt(1);
2324 parcel.writeString(mChannelId);
2325 } else {
2326 parcel.writeInt(0);
2327 }
Julia Reynolds2a128742016-11-28 14:29:25 -05002328 parcel.writeLong(mTimeout);
Julia Reynolds13d898c2017-02-02 12:22:05 -05002329
2330 if (mShortcutId != null) {
2331 parcel.writeInt(1);
2332 parcel.writeString(mShortcutId);
2333 } else {
2334 parcel.writeInt(0);
2335 }
2336
2337 parcel.writeInt(mBadgeIcon);
Julia Reynolds3aedded2017-03-31 14:42:09 -04002338
2339 if (mSettingsText != null) {
2340 parcel.writeInt(1);
2341 TextUtils.writeToParcel(mSettingsText, parcel, flags);
2342 } else {
2343 parcel.writeInt(0);
2344 }
Julia Reynoldsa79c3712017-04-21 10:29:57 -04002345
2346 parcel.writeInt(mGroupAlertBehavior);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002347 }
2348
2349 /**
2350 * Parcelable.Creator that instantiates Notification objects
2351 */
2352 public static final Parcelable.Creator<Notification> CREATOR
2353 = new Parcelable.Creator<Notification>()
2354 {
2355 public Notification createFromParcel(Parcel parcel)
2356 {
2357 return new Notification(parcel);
2358 }
2359
2360 public Notification[] newArray(int size)
2361 {
2362 return new Notification[size];
2363 }
2364 };
2365
2366 /**
2367 * Sets the {@link #contentView} field to be a view with the standard "Latest Event"
2368 * layout.
2369 *
2370 * <p>Uses the {@link #icon} and {@link #when} fields to set the icon and time fields
2371 * in the view.</p>
2372 * @param context The context for your application / activity.
2373 * @param contentTitle The title that goes in the expanded entry.
2374 * @param contentText The text that goes in the expanded entry.
2375 * @param contentIntent The intent to launch when the user clicks the expanded notification.
2376 * If this is an activity, it must include the
2377 * {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} flag, which requires
Scott Main7aee61f2011-02-08 11:25:01 -08002378 * that you take care of task management as described in the
2379 * <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back
2380 * Stack</a> document.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002381 *
Joe Onorato46439ce2010-11-19 13:56:21 -08002382 * @deprecated Use {@link Builder} instead.
Chris Wrena05db382015-06-24 15:18:34 -04002383 * @removed
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002384 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002385 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002386 public void setLatestEventInfo(Context context,
2387 CharSequence contentTitle, CharSequence contentText, PendingIntent contentIntent) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002388 if (context.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1){
2389 Log.e(TAG, "setLatestEventInfo() is deprecated and you should feel deprecated.",
2390 new Throwable());
2391 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002392
Selim Cinek4ac6f602016-06-13 15:47:03 -07002393 if (context.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N) {
2394 extras.putBoolean(EXTRA_SHOW_WHEN, true);
2395 }
2396
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002397 // ensure that any information already set directly is preserved
2398 final Notification.Builder builder = new Notification.Builder(context, this);
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002399
2400 // now apply the latestEventInfo fields
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002401 if (contentTitle != null) {
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002402 builder.setContentTitle(contentTitle);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002403 }
2404 if (contentText != null) {
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002405 builder.setContentText(contentText);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002406 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002407 builder.setContentIntent(contentIntent);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002408
2409 builder.build(); // callers expect this notification to be ready to use
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002410 }
2411
Julia Reynoldsda303542015-11-23 14:00:20 -05002412 /**
2413 * @hide
2414 */
2415 public static void addFieldsFromContext(Context context, Notification notification) {
Julia Reynoldse071abd2017-03-22 10:52:11 -04002416 addFieldsFromContext(context.getApplicationInfo(), notification);
Jeff Sharkey012bc7b2016-04-11 16:30:27 -06002417 }
2418
2419 /**
2420 * @hide
2421 */
Julia Reynoldse071abd2017-03-22 10:52:11 -04002422 public static void addFieldsFromContext(ApplicationInfo ai, Notification notification) {
Jeff Sharkey012bc7b2016-04-11 16:30:27 -06002423 notification.extras.putParcelable(EXTRA_BUILDER_APPLICATION_INFO, ai);
Julia Reynoldsda303542015-11-23 14:00:20 -05002424 }
2425
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002426 @Override
2427 public String toString() {
2428 StringBuilder sb = new StringBuilder();
Julia Reynoldsb9e712e2017-04-17 10:31:03 -04002429 sb.append("Notification(channel=");
Julia Reynoldsbad42972017-04-25 13:52:49 -04002430 sb.append(getChannelId());
Julia Reynoldsb9e712e2017-04-17 10:31:03 -04002431 sb.append(" pri=");
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002432 sb.append(priority);
2433 sb.append(" contentView=");
Joe Onoratoc9596d62011-01-12 17:03:11 -08002434 if (contentView != null) {
2435 sb.append(contentView.getPackage());
2436 sb.append("/0x");
2437 sb.append(Integer.toHexString(contentView.getLayoutId()));
2438 } else {
2439 sb.append("null");
2440 }
2441 sb.append(" vibrate=");
Daniel Sandler6738eee2012-11-16 12:03:32 -05002442 if ((this.defaults & DEFAULT_VIBRATE) != 0) {
2443 sb.append("default");
2444 } else if (this.vibrate != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002445 int N = this.vibrate.length-1;
2446 sb.append("[");
2447 for (int i=0; i<N; i++) {
2448 sb.append(this.vibrate[i]);
2449 sb.append(',');
2450 }
Simon Schoar8cf97d92009-06-10 22:08:37 +02002451 if (N != -1) {
2452 sb.append(this.vibrate[N]);
2453 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002454 sb.append("]");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002455 } else {
2456 sb.append("null");
2457 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002458 sb.append(" sound=");
Daniel Sandler6738eee2012-11-16 12:03:32 -05002459 if ((this.defaults & DEFAULT_SOUND) != 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002460 sb.append("default");
Daniel Sandler6738eee2012-11-16 12:03:32 -05002461 } else if (this.sound != null) {
2462 sb.append(this.sound.toString());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002463 } else {
2464 sb.append("null");
2465 }
Chris Wren365b6d32015-07-16 10:39:26 -04002466 if (this.tickerText != null) {
2467 sb.append(" tick");
2468 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002469 sb.append(" defaults=0x");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002470 sb.append(Integer.toHexString(this.defaults));
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002471 sb.append(" flags=0x");
Daniel Sandlere46cbd32010-06-17 10:35:26 -04002472 sb.append(Integer.toHexString(this.flags));
Dan Sandler26e81cf2014-05-06 10:01:27 -04002473 sb.append(String.format(" color=0x%08x", this.color));
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002474 if (this.category != null) {
2475 sb.append(" category=");
2476 sb.append(this.category);
2477 }
2478 if (this.mGroupKey != null) {
2479 sb.append(" groupKey=");
2480 sb.append(this.mGroupKey);
2481 }
2482 if (this.mSortKey != null) {
2483 sb.append(" sortKey=");
2484 sb.append(this.mSortKey);
2485 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002486 if (actions != null) {
Dan Sandler1b718782014-07-18 12:43:45 -04002487 sb.append(" actions=");
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002488 sb.append(actions.length);
Dan Sandler1b718782014-07-18 12:43:45 -04002489 }
2490 sb.append(" vis=");
2491 sb.append(visibilityToString(this.visibility));
2492 if (this.publicVersion != null) {
2493 sb.append(" publicVersion=");
2494 sb.append(publicVersion.toString());
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002495 }
2496 sb.append(")");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002497 return sb.toString();
2498 }
Joe Onorato46439ce2010-11-19 13:56:21 -08002499
Dan Sandler1b718782014-07-18 12:43:45 -04002500 /**
2501 * {@hide}
2502 */
2503 public static String visibilityToString(int vis) {
2504 switch (vis) {
2505 case VISIBILITY_PRIVATE:
2506 return "PRIVATE";
2507 case VISIBILITY_PUBLIC:
2508 return "PUBLIC";
2509 case VISIBILITY_SECRET:
2510 return "SECRET";
2511 default:
2512 return "UNKNOWN(" + String.valueOf(vis) + ")";
2513 }
2514 }
2515
Joe Onoratocb109a02011-01-18 17:57:41 -08002516 /**
John Spurlock1d881a12015-03-18 19:21:54 -04002517 * {@hide}
2518 */
2519 public static String priorityToString(@Priority int pri) {
2520 switch (pri) {
2521 case PRIORITY_MIN:
2522 return "MIN";
2523 case PRIORITY_LOW:
2524 return "LOW";
2525 case PRIORITY_DEFAULT:
2526 return "DEFAULT";
2527 case PRIORITY_HIGH:
2528 return "HIGH";
2529 case PRIORITY_MAX:
2530 return "MAX";
2531 default:
2532 return "UNKNOWN(" + String.valueOf(pri) + ")";
2533 }
2534 }
2535
Jeff Sharkey000ce802017-04-29 13:13:27 -06002536 /** @removed */
2537 @Deprecated
Julia Reynolds37856052016-11-11 09:20:07 -05002538 public String getChannel() {
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04002539 return mChannelId;
2540 }
2541
2542 /**
Julia Reynoldsbad42972017-04-25 13:52:49 -04002543 * Returns the id of the channel this notification posts to.
2544 */
2545 public String getChannelId() {
2546 return mChannelId;
2547 }
2548
Jeff Sharkey000ce802017-04-29 13:13:27 -06002549 /** @removed */
2550 @Deprecated
Julia Reynolds2a128742016-11-28 14:29:25 -05002551 public long getTimeout() {
2552 return mTimeout;
2553 }
2554
2555 /**
Julia Reynoldsbad42972017-04-25 13:52:49 -04002556 * Returns the duration from posting after which this notification should be canceled by the
2557 * system, if it's not canceled already.
2558 */
2559 public long getTimeoutAfter() {
2560 return mTimeout;
2561 }
2562
2563 /**
Julia Reynoldse071abd2017-03-22 10:52:11 -04002564 * Returns what icon should be shown for this notification if it is being displayed in a
2565 * Launcher that supports badging. Will be one of {@link #BADGE_ICON_NONE},
2566 * {@link #BADGE_ICON_SMALL}, or {@link #BADGE_ICON_LARGE}.
2567 */
2568 public int getBadgeIconType() {
2569 return mBadgeIcon;
2570 }
2571
2572 /**
Julia Reynolds13d898c2017-02-02 12:22:05 -05002573 * Returns the {@link ShortcutInfo#getId() id} that this notification supersedes, if any.
Julia Reynoldsbad42972017-04-25 13:52:49 -04002574 *
2575 * <p>Used by some Launchers that display notification content to hide shortcuts that duplicate
2576 * notifications.
Julia Reynolds13d898c2017-02-02 12:22:05 -05002577 */
2578 public String getShortcutId() {
2579 return mShortcutId;
2580 }
2581
Julia Reynolds3aedded2017-03-31 14:42:09 -04002582
2583 /**
2584 * Returns the settings text provided to {@link Builder#setSettingsText(CharSequence)}.
2585 */
2586 public CharSequence getSettingsText() {
2587 return mSettingsText;
2588 }
2589
Julia Reynolds13d898c2017-02-02 12:22:05 -05002590 /**
Julia Reynoldsa79c3712017-04-21 10:29:57 -04002591 * Returns which type of notifications in a group are responsible for audibly alerting the
2592 * user. See {@link #GROUP_ALERT_ALL}, {@link #GROUP_ALERT_CHILDREN},
2593 * {@link #GROUP_ALERT_SUMMARY}.
2594 */
2595 public @GroupAlertBehavior int getGroupAlertBehavior() {
2596 return mGroupAlertBehavior;
2597 }
2598
2599 /**
Dan Sandlerd63f9322015-05-06 15:18:49 -04002600 * The small icon representing this notification in the status bar and content view.
2601 *
2602 * @return the small icon representing this notification.
2603 *
2604 * @see Builder#getSmallIcon()
2605 * @see Builder#setSmallIcon(Icon)
2606 */
2607 public Icon getSmallIcon() {
2608 return mSmallIcon;
2609 }
2610
2611 /**
2612 * Used when notifying to clean up legacy small icons.
2613 * @hide
2614 */
2615 public void setSmallIcon(Icon icon) {
2616 mSmallIcon = icon;
2617 }
2618
2619 /**
2620 * The large icon shown in this notification's content view.
2621 * @see Builder#getLargeIcon()
2622 * @see Builder#setLargeIcon(Icon)
2623 */
2624 public Icon getLargeIcon() {
2625 return mLargeIcon;
2626 }
2627
2628 /**
Christoph Studer4600f9b2014-07-22 22:44:43 +02002629 * @hide
2630 */
Christoph Studerc8db24b2014-07-25 17:50:30 +02002631 public boolean isGroupSummary() {
2632 return mGroupKey != null && (flags & FLAG_GROUP_SUMMARY) != 0;
2633 }
2634
2635 /**
2636 * @hide
2637 */
2638 public boolean isGroupChild() {
2639 return mGroupKey != null && (flags & FLAG_GROUP_SUMMARY) == 0;
2640 }
2641
2642 /**
Julia Reynolds30203152017-05-26 13:36:31 -04002643 * @hide
2644 */
2645 public boolean suppressAlertingDueToGrouping() {
2646 if (isGroupSummary()
2647 && getGroupAlertBehavior() == Notification.GROUP_ALERT_CHILDREN) {
2648 return true;
2649 } else if (isGroupChild()
2650 && getGroupAlertBehavior() == Notification.GROUP_ALERT_SUMMARY) {
2651 return true;
2652 }
2653 return false;
2654 }
2655
2656 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002657 * Builder class for {@link Notification} objects.
Joe Malin8d40d042012-11-05 11:36:40 -08002658 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002659 * Provides a convenient way to set the various fields of a {@link Notification} and generate
Scott Main183bf112012-08-13 19:12:13 -07002660 * content views using the platform's notification layout template. If your app supports
2661 * versions of Android as old as API level 4, you can instead use
2662 * {@link android.support.v4.app.NotificationCompat.Builder NotificationCompat.Builder},
2663 * available in the <a href="{@docRoot}tools/extras/support-library.html">Android Support
2664 * library</a>.
Joe Malin8d40d042012-11-05 11:36:40 -08002665 *
Scott Main183bf112012-08-13 19:12:13 -07002666 * <p>Example:
Joe Malin8d40d042012-11-05 11:36:40 -08002667 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002668 * <pre class="prettyprint">
Scott Main183bf112012-08-13 19:12:13 -07002669 * Notification noti = new Notification.Builder(mContext)
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002670 * .setContentTitle(&quot;New mail from &quot; + sender.toString())
2671 * .setContentText(subject)
2672 * .setSmallIcon(R.drawable.new_mail)
2673 * .setLargeIcon(aBitmap)
Chris Wrenfbd96ba2012-05-01 12:03:58 -04002674 * .build();
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002675 * </pre>
Joe Onoratocb109a02011-01-18 17:57:41 -08002676 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002677 public static class Builder {
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05002678 /**
2679 * @hide
2680 */
2681 public static final String EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT =
2682 "android.rebuild.contentViewActionCount";
2683 /**
2684 * @hide
2685 */
2686 public static final String EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT
2687 = "android.rebuild.bigViewActionCount";
2688 /**
2689 * @hide
2690 */
2691 public static final String EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT
2692 = "android.rebuild.hudViewActionCount";
2693
Daniel Sandler602ad1c2012-06-12 16:06:27 -04002694 private static final int MAX_ACTION_BUTTONS = 3;
Daniel Sandler8680bf82012-05-15 16:52:52 -04002695
Selim Cinek6743c0b2017-01-18 18:24:01 -08002696 private static final boolean USE_ONLY_TITLE_IN_LOW_PRIORITY_SUMMARY =
2697 SystemProperties.getBoolean("notifications.only_title", true);
2698
Selim Cinek389edcd2017-05-11 19:16:44 -07002699 /**
2700 * The lightness difference that has to be added to the primary text color to obtain the
2701 * secondary text color when the background is light.
2702 */
2703 private static final int LIGHTNESS_TEXT_DIFFERENCE_LIGHT = 20;
2704
2705 /**
2706 * The lightness difference that has to be added to the primary text color to obtain the
2707 * secondary text color when the background is dark.
2708 * A bit less then the above value, since it looks better on dark backgrounds.
2709 */
2710 private static final int LIGHTNESS_TEXT_DIFFERENCE_DARK = -10;
2711
Joe Onorato46439ce2010-11-19 13:56:21 -08002712 private Context mContext;
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002713 private Notification mN;
2714 private Bundle mUserExtras = new Bundle();
Chris Wrenfbd96ba2012-05-01 12:03:58 -04002715 private Style mStyle;
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002716 private ArrayList<Action> mActions = new ArrayList<Action>(MAX_ACTION_BUTTONS);
2717 private ArrayList<String> mPersonList = new ArrayList<String>();
2718 private NotificationColorUtil mColorUtil;
Selim Cinek7b9605b2017-01-19 17:36:00 -08002719 private boolean mIsLegacy;
2720 private boolean mIsLegacyInitialized;
Christoph Studer7ac80e62014-08-04 16:01:57 +02002721
2722 /**
Adrian Roos4ff3b122016-02-01 12:26:13 -08002723 * Caches a contrast-enhanced version of {@link #mCachedContrastColorIsFor}.
2724 */
2725 private int mCachedContrastColor = COLOR_INVALID;
2726 private int mCachedContrastColorIsFor = COLOR_INVALID;
Adrian Roos487374f2017-01-11 15:48:14 -08002727 /**
2728 * Caches a ambient version of {@link #mCachedContrastColorIsFor}.
2729 */
2730 private int mCachedAmbientColor = COLOR_INVALID;
2731 private int mCachedAmbientColorIsFor = COLOR_INVALID;
Adrian Roos4ff3b122016-02-01 12:26:13 -08002732
2733 /**
Adrian Roos70d7aa32017-01-11 15:39:06 -08002734 * Caches an instance of StandardTemplateParams. Note that this may have been used before,
2735 * so make sure to call {@link StandardTemplateParams#reset()} before using it.
2736 */
2737 StandardTemplateParams mParams = new StandardTemplateParams();
Selim Cinek7b9605b2017-01-19 17:36:00 -08002738 private int mTextColorsAreForBackground = COLOR_INVALID;
2739 private int mPrimaryTextColor = COLOR_INVALID;
2740 private int mSecondaryTextColor = COLOR_INVALID;
2741 private int mActionBarColor = COLOR_INVALID;
Selim Cinek5fb73f82017-04-20 16:55:38 -07002742 private int mBackgroundColor = COLOR_INVALID;
2743 private int mForegroundColor = COLOR_INVALID;
Selim Cinekac5f0272017-05-02 16:05:41 -07002744 private int mBackgroundColorHint = COLOR_INVALID;
Selim Cineka7679b62017-05-10 16:33:25 -07002745 private boolean mRebuildStyledRemoteViews;
Adrian Roos70d7aa32017-01-11 15:39:06 -08002746
2747 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002748 * Constructs a new Builder with the defaults:
Joe Onoratocb109a02011-01-18 17:57:41 -08002749 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002750 * @param context
2751 * A {@link Context} that will be used by the Builder to construct the
2752 * RemoteViews. The Context will not be held past the lifetime of this Builder
2753 * object.
Geoffrey Pitsch5caa2762017-01-12 09:35:54 -05002754 * @param channelId
2755 * The constructed Notification will be posted on this
2756 * {@link NotificationChannel}. To use a NotificationChannel, it must first be
2757 * created using {@link NotificationManager#createNotificationChannel}.
Joe Onoratocb109a02011-01-18 17:57:41 -08002758 */
Geoffrey Pitsch5caa2762017-01-12 09:35:54 -05002759 public Builder(Context context, String channelId) {
2760 this(context, (Notification) null);
2761 mN.mChannelId = channelId;
2762 }
2763
2764 /**
2765 * @deprecated use {@link Notification.Builder#Notification.Builder(Context, String)}
2766 * instead. All posted Notifications must specify a NotificationChannel Id.
2767 */
2768 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08002769 public Builder(Context context) {
Geoffrey Pitsch5caa2762017-01-12 09:35:54 -05002770 this(context, (Notification) null);
Joe Onorato46439ce2010-11-19 13:56:21 -08002771 }
2772
Joe Onoratocb109a02011-01-18 17:57:41 -08002773 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002774 * @hide
Christoph Studer4600f9b2014-07-22 22:44:43 +02002775 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002776 public Builder(Context context, Notification toAdopt) {
2777 mContext = context;
Christoph Studer4600f9b2014-07-22 22:44:43 +02002778
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002779 if (toAdopt == null) {
2780 mN = new Notification();
Selim Cinek0ff1ce602016-04-05 18:27:16 -07002781 if (context.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N) {
2782 mN.extras.putBoolean(EXTRA_SHOW_WHEN, true);
2783 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002784 mN.priority = PRIORITY_DEFAULT;
2785 mN.visibility = VISIBILITY_PRIVATE;
2786 } else {
2787 mN = toAdopt;
2788 if (mN.actions != null) {
2789 Collections.addAll(mActions, mN.actions);
Christoph Studer4600f9b2014-07-22 22:44:43 +02002790 }
2791
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002792 if (mN.extras.containsKey(EXTRA_PEOPLE)) {
2793 Collections.addAll(mPersonList, mN.extras.getStringArray(EXTRA_PEOPLE));
2794 }
2795
Selim Cinek4ac6f602016-06-13 15:47:03 -07002796 if (mN.getSmallIcon() == null && mN.icon != 0) {
2797 setSmallIcon(mN.icon);
2798 }
2799
2800 if (mN.getLargeIcon() == null && mN.largeIcon != null) {
2801 setLargeIcon(mN.largeIcon);
2802 }
2803
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002804 String templateClass = mN.extras.getString(EXTRA_TEMPLATE);
2805 if (!TextUtils.isEmpty(templateClass)) {
2806 final Class<? extends Style> styleClass
2807 = getNotificationStyleClass(templateClass);
2808 if (styleClass == null) {
2809 Log.d(TAG, "Unknown style class: " + templateClass);
2810 } else {
2811 try {
Adrian Roosc1a80b02016-04-05 14:54:55 -07002812 final Constructor<? extends Style> ctor =
2813 styleClass.getDeclaredConstructor();
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002814 ctor.setAccessible(true);
2815 final Style style = ctor.newInstance();
2816 style.restoreFromExtras(mN.extras);
2817
2818 if (style != null) {
2819 setStyle(style);
2820 }
2821 } catch (Throwable t) {
2822 Log.e(TAG, "Could not create Style", t);
2823 }
2824 }
2825 }
2826
2827 }
2828 }
2829
2830 private NotificationColorUtil getColorUtil() {
Selim Cinek99104832017-01-25 14:47:33 -08002831 if (mColorUtil == null) {
2832 mColorUtil = NotificationColorUtil.getInstance(mContext);
Christoph Studer4600f9b2014-07-22 22:44:43 +02002833 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002834 return mColorUtil;
Christoph Studer4600f9b2014-07-22 22:44:43 +02002835 }
2836
2837 /**
Julia Reynolds13d898c2017-02-02 12:22:05 -05002838 * If this notification is duplicative of a Launcher shortcut, sets the
2839 * {@link ShortcutInfo#getId() id} of the shortcut, in case the Launcher wants to hide
2840 * the shortcut.
2841 *
Julia Reynoldsbad42972017-04-25 13:52:49 -04002842 * This field will be ignored by Launchers that don't support badging, don't show
2843 * notification content, or don't show {@link android.content.pm.ShortcutManager shortcuts}.
Julia Reynolds13d898c2017-02-02 12:22:05 -05002844 *
2845 * @param shortcutId the {@link ShortcutInfo#getId() id} of the shortcut this notification
2846 * supersedes
2847 */
2848 public Builder setShortcutId(String shortcutId) {
2849 mN.mShortcutId = shortcutId;
2850 return this;
2851 }
2852
2853 /**
Julia Reynoldse071abd2017-03-22 10:52:11 -04002854 * Sets which icon to display as a badge for this notification.
2855 *
2856 * Must be one of {@link #BADGE_ICON_NONE}, {@link #BADGE_ICON_SMALL},
2857 * {@link #BADGE_ICON_LARGE}.
2858 *
2859 * Note: This value might be ignored, for launchers that don't support badge icons.
2860 */
Julia Reynolds612beb22017-03-30 10:48:30 -04002861 public Builder setBadgeIconType(int icon) {
Julia Reynoldse071abd2017-03-22 10:52:11 -04002862 mN.mBadgeIcon = icon;
2863 return this;
2864 }
2865
2866 /**
Julia Reynoldsa79c3712017-04-21 10:29:57 -04002867 * Sets the group alert behavior for this notification. Use this method to mute this
2868 * notification if alerts for this notification's group should be handled by a different
2869 * notification. This is only applicable for notifications that belong to a
2870 * {@link #setGroup(String) group}.
2871 *
2872 * <p> The default value is {@link #GROUP_ALERT_ALL}.</p>
2873 */
2874 public Builder setGroupAlertBehavior(@GroupAlertBehavior int groupAlertBehavior) {
2875 mN.mGroupAlertBehavior = groupAlertBehavior;
2876 return this;
2877 }
2878
Jeff Sharkey000ce802017-04-29 13:13:27 -06002879 /** @removed */
2880 @Deprecated
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04002881 public Builder setChannel(String channelId) {
2882 mN.mChannelId = channelId;
2883 return this;
2884 }
2885
2886 /**
Julia Reynoldsbad42972017-04-25 13:52:49 -04002887 * Specifies the channel the notification should be delivered on.
2888 */
2889 public Builder setChannelId(String channelId) {
2890 mN.mChannelId = channelId;
2891 return this;
2892 }
2893
Jeff Sharkey000ce802017-04-29 13:13:27 -06002894 /** @removed */
2895 @Deprecated
Julia Reynolds50989772017-02-23 14:32:16 -05002896 public Builder setTimeout(long durationMs) {
2897 mN.mTimeout = durationMs;
Julia Reynolds2a128742016-11-28 14:29:25 -05002898 return this;
2899 }
2900
2901 /**
Julia Reynoldsbad42972017-04-25 13:52:49 -04002902 * Specifies a duration in milliseconds after which this notification should be canceled,
2903 * if it is not already canceled.
2904 */
2905 public Builder setTimeoutAfter(long durationMs) {
2906 mN.mTimeout = durationMs;
2907 return this;
2908 }
2909
2910 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002911 * Add a timestamp pertaining to the notification (usually the time the event occurred).
Selim Cinek0ff1ce602016-04-05 18:27:16 -07002912 *
2913 * For apps targeting {@link android.os.Build.VERSION_CODES#N} and above, this time is not
2914 * shown anymore by default and must be opted into by using
2915 * {@link android.app.Notification.Builder#setShowWhen(boolean)}
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002916 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002917 * @see Notification#when
Joe Onoratocb109a02011-01-18 17:57:41 -08002918 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002919 public Builder setWhen(long when) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002920 mN.when = when;
Joe Onorato46439ce2010-11-19 13:56:21 -08002921 return this;
2922 }
2923
Joe Onoratocb109a02011-01-18 17:57:41 -08002924 /**
Griff Hazen50c11652014-05-16 09:46:31 -07002925 * Control whether the timestamp set with {@link #setWhen(long) setWhen} is shown
Daniel Sandler0c890492012-09-12 17:23:10 -07002926 * in the content view.
Selim Cinek0ff1ce602016-04-05 18:27:16 -07002927 * For apps targeting {@link android.os.Build.VERSION_CODES#N} and above, this defaults to
2928 * {@code false}. For earlier apps, the default is {@code true}.
Daniel Sandler0c890492012-09-12 17:23:10 -07002929 */
2930 public Builder setShowWhen(boolean show) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002931 mN.extras.putBoolean(EXTRA_SHOW_WHEN, show);
Daniel Sandler0c890492012-09-12 17:23:10 -07002932 return this;
2933 }
2934
2935 /**
Daniel Sandlerd33b8032012-05-10 11:41:48 -04002936 * Show the {@link Notification#when} field as a stopwatch.
Joe Malin8d40d042012-11-05 11:36:40 -08002937 *
2938 * Instead of presenting <code>when</code> as a timestamp, the notification will show an
Daniel Sandlerd33b8032012-05-10 11:41:48 -04002939 * automatically updating display of the minutes and seconds since <code>when</code>.
Daniel Sandlera2985ed2012-04-03 16:42:00 -04002940 *
Daniel Sandlerd33b8032012-05-10 11:41:48 -04002941 * Useful when showing an elapsed time (like an ongoing phone call).
2942 *
Selim Cinek81c23aa2016-02-25 16:23:13 -08002943 * The counter can also be set to count down to <code>when</code> when using
Adrian Roos96b7e202016-05-17 13:50:38 -07002944 * {@link #setChronometerCountDown(boolean)}.
Selim Cinek81c23aa2016-02-25 16:23:13 -08002945 *
Daniel Sandlerd33b8032012-05-10 11:41:48 -04002946 * @see android.widget.Chronometer
Daniel Sandlera2985ed2012-04-03 16:42:00 -04002947 * @see Notification#when
Adrian Roos96b7e202016-05-17 13:50:38 -07002948 * @see #setChronometerCountDown(boolean)
Daniel Sandlera2985ed2012-04-03 16:42:00 -04002949 */
2950 public Builder setUsesChronometer(boolean b) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002951 mN.extras.putBoolean(EXTRA_SHOW_CHRONOMETER, b);
Daniel Sandlera2985ed2012-04-03 16:42:00 -04002952 return this;
2953 }
2954
2955 /**
Selim Cinek81c23aa2016-02-25 16:23:13 -08002956 * Sets the Chronometer to count down instead of counting up.
2957 *
2958 * <p>This is only relevant if {@link #setUsesChronometer(boolean)} has been set to true.
2959 * If it isn't set the chronometer will count up.
2960 *
2961 * @see #setUsesChronometer(boolean)
2962 */
Adrian Roos96b7e202016-05-17 13:50:38 -07002963 public Builder setChronometerCountDown(boolean countDown) {
2964 mN.extras.putBoolean(EXTRA_CHRONOMETER_COUNT_DOWN, countDown);
Selim Cinek81c23aa2016-02-25 16:23:13 -08002965 return this;
2966 }
2967
2968 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002969 * Set the small icon resource, which will be used to represent the notification in the
2970 * status bar.
Joe Onoratocb109a02011-01-18 17:57:41 -08002971 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002972
2973 * The platform template for the expanded view will draw this icon in the left, unless a
2974 * {@link #setLargeIcon(Bitmap) large icon} has also been specified, in which case the small
2975 * icon will be moved to the right-hand side.
2976 *
2977
2978 * @param icon
2979 * A resource ID in the application's package of the drawable to use.
2980 * @see Notification#icon
Joe Onoratocb109a02011-01-18 17:57:41 -08002981 */
Tor Norbye7b9c9122013-05-30 16:48:33 -07002982 public Builder setSmallIcon(@DrawableRes int icon) {
Dan Sandlerd63f9322015-05-06 15:18:49 -04002983 return setSmallIcon(icon != 0
2984 ? Icon.createWithResource(mContext, icon)
2985 : null);
Joe Onorato46439ce2010-11-19 13:56:21 -08002986 }
2987
Joe Onoratocb109a02011-01-18 17:57:41 -08002988 /**
2989 * A variant of {@link #setSmallIcon(int) setSmallIcon(int)} that takes an additional
2990 * level parameter for when the icon is a {@link android.graphics.drawable.LevelListDrawable
2991 * LevelListDrawable}.
2992 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002993 * @param icon A resource ID in the application's package of the drawable to use.
Joe Onoratocb109a02011-01-18 17:57:41 -08002994 * @param level The level to use for the icon.
2995 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002996 * @see Notification#icon
2997 * @see Notification#iconLevel
Joe Onoratocb109a02011-01-18 17:57:41 -08002998 */
Tor Norbye7b9c9122013-05-30 16:48:33 -07002999 public Builder setSmallIcon(@DrawableRes int icon, int level) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003000 mN.iconLevel = level;
Dan Sandlerd63f9322015-05-06 15:18:49 -04003001 return setSmallIcon(icon);
3002 }
3003
3004 /**
3005 * Set the small icon, which will be used to represent the notification in the
3006 * status bar and content view (unless overriden there by a
3007 * {@link #setLargeIcon(Bitmap) large icon}).
3008 *
3009 * @param icon An Icon object to use.
3010 * @see Notification#icon
3011 */
3012 public Builder setSmallIcon(Icon icon) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003013 mN.setSmallIcon(icon);
3014 if (icon != null && icon.getType() == Icon.TYPE_RESOURCE) {
3015 mN.icon = icon.getResId();
3016 }
Joe Onorato46439ce2010-11-19 13:56:21 -08003017 return this;
3018 }
3019
Joe Onoratocb109a02011-01-18 17:57:41 -08003020 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003021 * Set the first line of text in the platform notification template.
Joe Onoratocb109a02011-01-18 17:57:41 -08003022 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003023 public Builder setContentTitle(CharSequence title) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003024 mN.extras.putCharSequence(EXTRA_TITLE, safeCharSequence(title));
Joe Onorato46439ce2010-11-19 13:56:21 -08003025 return this;
3026 }
3027
Joe Onoratocb109a02011-01-18 17:57:41 -08003028 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003029 * Set the second line of text in the platform notification template.
Joe Onoratocb109a02011-01-18 17:57:41 -08003030 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003031 public Builder setContentText(CharSequence text) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003032 mN.extras.putCharSequence(EXTRA_TEXT, safeCharSequence(text));
Joe Onorato46439ce2010-11-19 13:56:21 -08003033 return this;
3034 }
3035
Joe Onoratocb109a02011-01-18 17:57:41 -08003036 /**
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07003037 * This provides some additional information that is displayed in the notification. No
3038 * guarantees are given where exactly it is displayed.
3039 *
3040 * <p>This information should only be provided if it provides an essential
3041 * benefit to the understanding of the notification. The more text you provide the
3042 * less readable it becomes. For example, an email client should only provide the account
3043 * name here if more than one email account has been added.</p>
3044 *
3045 * <p>As of {@link android.os.Build.VERSION_CODES#N} this information is displayed in the
3046 * notification header area.
3047 *
3048 * On Android versions before {@link android.os.Build.VERSION_CODES#N}
3049 * this will be shown in the third line of text in the platform notification template.
3050 * You should not be using {@link #setProgress(int, int, boolean)} at the
3051 * same time on those versions; they occupy the same place.
3052 * </p>
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003053 */
3054 public Builder setSubText(CharSequence text) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003055 mN.extras.putCharSequence(EXTRA_SUB_TEXT, safeCharSequence(text));
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003056 return this;
3057 }
3058
3059 /**
Julia Reynolds3aedded2017-03-31 14:42:09 -04003060 * Provides text that will appear as a link to your application's settings.
3061 *
3062 * <p>This text does not appear within notification {@link Style templates} but may
3063 * appear when the user uses an affordance to learn more about the notification.
3064 * Additionally, this text will not appear unless you provide a valid link target by
3065 * handling {@link #INTENT_CATEGORY_NOTIFICATION_PREFERENCES}.
3066 *
3067 * <p>This text is meant to be concise description about what the user can customize
3068 * when they click on this link. The recommended maximum length is 40 characters.
3069 * @param text
3070 * @return
3071 */
3072 public Builder setSettingsText(CharSequence text) {
3073 mN.mSettingsText = safeCharSequence(text);
3074 return this;
3075 }
3076
3077 /**
Adrian Roose458aa82015-12-08 16:17:19 -08003078 * Set the remote input history.
3079 *
3080 * This should be set to the most recent inputs that have been sent
3081 * through a {@link RemoteInput} of this Notification and cleared once the it is no
3082 * longer relevant (e.g. for chat notifications once the other party has responded).
3083 *
3084 * The most recent input must be stored at the 0 index, the second most recent at the
3085 * 1 index, etc. Note that the system will limit both how far back the inputs will be shown
3086 * and how much of each individual input is shown.
3087 *
3088 * <p>Note: The reply text will only be shown on notifications that have least one action
3089 * with a {@code RemoteInput}.</p>
3090 */
3091 public Builder setRemoteInputHistory(CharSequence[] text) {
3092 if (text == null) {
3093 mN.extras.putCharSequenceArray(EXTRA_REMOTE_INPUT_HISTORY, null);
3094 } else {
3095 final int N = Math.min(MAX_REPLY_HISTORY, text.length);
3096 CharSequence[] safe = new CharSequence[N];
3097 for (int i = 0; i < N; i++) {
3098 safe[i] = safeCharSequence(text[i]);
3099 }
3100 mN.extras.putCharSequenceArray(EXTRA_REMOTE_INPUT_HISTORY, safe);
3101 }
3102 return this;
3103 }
3104
3105 /**
Julia Reynolds13d898c2017-02-02 12:22:05 -05003106 * Sets the number of items this notification represents. May be displayed as a badge count
3107 * for Launchers that support badging.
Joe Onoratocb109a02011-01-18 17:57:41 -08003108 */
Joe Onorato8595a3d2010-11-19 18:12:07 -08003109 public Builder setNumber(int number) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003110 mN.number = number;
Joe Onorato8595a3d2010-11-19 18:12:07 -08003111 return this;
3112 }
3113
Joe Onoratocb109a02011-01-18 17:57:41 -08003114 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003115 * A small piece of additional information pertaining to this notification.
3116 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003117 * The platform template will draw this on the last line of the notification, at the far
3118 * right (to the right of a smallIcon if it has been placed there).
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07003119 *
3120 * @deprecated use {@link #setSubText(CharSequence)} instead to set a text in the header.
3121 * For legacy apps targeting a version below {@link android.os.Build.VERSION_CODES#N} this
3122 * field will still show up, but the subtext will take precedence.
Joe Onoratocb109a02011-01-18 17:57:41 -08003123 */
Aurimas Liutikas514c5ef2016-05-24 15:22:55 -07003124 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08003125 public Builder setContentInfo(CharSequence info) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003126 mN.extras.putCharSequence(EXTRA_INFO_TEXT, safeCharSequence(info));
Joe Onorato46439ce2010-11-19 13:56:21 -08003127 return this;
3128 }
3129
Joe Onoratocb109a02011-01-18 17:57:41 -08003130 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003131 * Set the progress this notification represents.
3132 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003133 * The platform template will represent this using a {@link ProgressBar}.
Jeff Sharkey1c400132011-08-05 14:50:13 -07003134 */
3135 public Builder setProgress(int max, int progress, boolean indeterminate) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003136 mN.extras.putInt(EXTRA_PROGRESS, progress);
3137 mN.extras.putInt(EXTRA_PROGRESS_MAX, max);
3138 mN.extras.putBoolean(EXTRA_PROGRESS_INDETERMINATE, indeterminate);
Jeff Sharkey1c400132011-08-05 14:50:13 -07003139 return this;
3140 }
3141
3142 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003143 * Supply a custom RemoteViews to use instead of the platform template.
3144 *
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003145 * Use {@link #setCustomContentView(RemoteViews)} instead.
Joe Onoratocb109a02011-01-18 17:57:41 -08003146 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003147 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08003148 public Builder setContent(RemoteViews views) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003149 return setCustomContentView(views);
3150 }
3151
3152 /**
3153 * Supply custom RemoteViews to use instead of the platform template.
3154 *
3155 * This will override the layout that would otherwise be constructed by this Builder
3156 * object.
3157 */
3158 public Builder setCustomContentView(RemoteViews contentView) {
3159 mN.contentView = contentView;
3160 return this;
3161 }
3162
3163 /**
3164 * Supply custom RemoteViews to use instead of the platform template in the expanded form.
3165 *
3166 * This will override the expanded layout that would otherwise be constructed by this
3167 * Builder object.
3168 */
3169 public Builder setCustomBigContentView(RemoteViews contentView) {
3170 mN.bigContentView = contentView;
3171 return this;
3172 }
3173
3174 /**
3175 * Supply custom RemoteViews to use instead of the platform template in the heads up dialog.
3176 *
3177 * This will override the heads-up layout that would otherwise be constructed by this
3178 * Builder object.
3179 */
3180 public Builder setCustomHeadsUpContentView(RemoteViews contentView) {
3181 mN.headsUpContentView = contentView;
Joe Onorato46439ce2010-11-19 13:56:21 -08003182 return this;
3183 }
3184
Joe Onoratocb109a02011-01-18 17:57:41 -08003185 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003186 * Supply a {@link PendingIntent} to be sent when the notification is clicked.
3187 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003188 * As of {@link android.os.Build.VERSION_CODES#HONEYCOMB}, if this field is unset and you
3189 * have specified a custom RemoteViews with {@link #setContent(RemoteViews)}, you can use
3190 * {@link RemoteViews#setOnClickPendingIntent RemoteViews.setOnClickPendingIntent(int,PendingIntent)}
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003191 * to assign PendingIntents to individual views in that custom layout (i.e., to create
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003192 * clickable buttons inside the notification view).
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003193 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003194 * @see Notification#contentIntent Notification.contentIntent
Joe Onoratocb109a02011-01-18 17:57:41 -08003195 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003196 public Builder setContentIntent(PendingIntent intent) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003197 mN.contentIntent = intent;
Joe Onorato46439ce2010-11-19 13:56:21 -08003198 return this;
3199 }
3200
Joe Onoratocb109a02011-01-18 17:57:41 -08003201 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003202 * Supply a {@link PendingIntent} to send when the notification is cleared explicitly by the user.
3203 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003204 * @see Notification#deleteIntent
Joe Onoratocb109a02011-01-18 17:57:41 -08003205 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003206 public Builder setDeleteIntent(PendingIntent intent) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003207 mN.deleteIntent = intent;
Joe Onorato46439ce2010-11-19 13:56:21 -08003208 return this;
3209 }
3210
Joe Onoratocb109a02011-01-18 17:57:41 -08003211 /**
3212 * An intent to launch instead of posting the notification to the status bar.
3213 * Only for use with extremely high-priority notifications demanding the user's
3214 * <strong>immediate</strong> attention, such as an incoming phone call or
3215 * alarm clock that the user has explicitly set to a particular time.
3216 * If this facility is used for something else, please give the user an option
3217 * to turn it off and use a normal notification, as this can be extremely
3218 * disruptive.
3219 *
Chris Wren47c20a12014-06-18 17:27:29 -04003220 * <p>
3221 * The system UI may choose to display a heads-up notification, instead of
3222 * launching this intent, while the user is using the device.
3223 * </p>
3224 *
Joe Onoratocb109a02011-01-18 17:57:41 -08003225 * @param intent The pending intent to launch.
3226 * @param highPriority Passing true will cause this notification to be sent
3227 * even if other notifications are suppressed.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003228 *
3229 * @see Notification#fullScreenIntent
Joe Onoratocb109a02011-01-18 17:57:41 -08003230 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003231 public Builder setFullScreenIntent(PendingIntent intent, boolean highPriority) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003232 mN.fullScreenIntent = intent;
Joe Onorato46439ce2010-11-19 13:56:21 -08003233 setFlag(FLAG_HIGH_PRIORITY, highPriority);
3234 return this;
3235 }
3236
Joe Onoratocb109a02011-01-18 17:57:41 -08003237 /**
Dan Sandler5fcdf6e2014-07-18 11:31:15 -04003238 * Set the "ticker" text which is sent to accessibility services.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003239 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003240 * @see Notification#tickerText
Joe Onoratocb109a02011-01-18 17:57:41 -08003241 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003242 public Builder setTicker(CharSequence tickerText) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003243 mN.tickerText = safeCharSequence(tickerText);
Joe Onorato46439ce2010-11-19 13:56:21 -08003244 return this;
3245 }
3246
Joe Onoratocb109a02011-01-18 17:57:41 -08003247 /**
Dan Sandler5fcdf6e2014-07-18 11:31:15 -04003248 * Obsolete version of {@link #setTicker(CharSequence)}.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003249 *
Joe Onoratocb109a02011-01-18 17:57:41 -08003250 */
Dan Sandler5fcdf6e2014-07-18 11:31:15 -04003251 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08003252 public Builder setTicker(CharSequence tickerText, RemoteViews views) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003253 setTicker(tickerText);
3254 // views is ignored
Joe Onorato46439ce2010-11-19 13:56:21 -08003255 return this;
3256 }
3257
Joe Onoratocb109a02011-01-18 17:57:41 -08003258 /**
Dan Sandlerd63f9322015-05-06 15:18:49 -04003259 * Add a large icon to the notification content view.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003260 *
3261 * In the platform template, this image will be shown on the left of the notification view
Dan Sandlerd63f9322015-05-06 15:18:49 -04003262 * in place of the {@link #setSmallIcon(Icon) small icon} (which will be placed in a small
3263 * badge atop the large icon).
Dan Sandler08a04c12015-05-06 15:18:49 -04003264 */
Dan Sandlerd63f9322015-05-06 15:18:49 -04003265 public Builder setLargeIcon(Bitmap b) {
3266 return setLargeIcon(b != null ? Icon.createWithBitmap(b) : null);
3267 }
3268
3269 /**
3270 * Add a large icon to the notification content view.
3271 *
3272 * In the platform template, this image will be shown on the left of the notification view
3273 * in place of the {@link #setSmallIcon(Icon) small icon} (which will be placed in a small
3274 * badge atop the large icon).
3275 */
3276 public Builder setLargeIcon(Icon icon) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003277 mN.mLargeIcon = icon;
3278 mN.extras.putParcelable(EXTRA_LARGE_ICON, icon);
Joe Onorato46439ce2010-11-19 13:56:21 -08003279 return this;
3280 }
3281
Joe Onoratocb109a02011-01-18 17:57:41 -08003282 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003283 * Set the sound to play.
3284 *
John Spurlockc0650f022014-07-19 13:22:39 -04003285 * It will be played using the {@link #AUDIO_ATTRIBUTES_DEFAULT default audio attributes}
3286 * for notifications.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003287 *
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003288 * @deprecated use {@link NotificationChannel#setSound(Uri, AudioAttributes)} instead.
Joe Onoratocb109a02011-01-18 17:57:41 -08003289 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003290 @Deprecated
Joe Onorato52f80cd2010-11-21 15:34:48 -08003291 public Builder setSound(Uri sound) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003292 mN.sound = sound;
3293 mN.audioAttributes = AUDIO_ATTRIBUTES_DEFAULT;
Joe Onorato52f80cd2010-11-21 15:34:48 -08003294 return this;
3295 }
3296
Joe Onoratocb109a02011-01-18 17:57:41 -08003297 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003298 * Set the sound to play, along with a specific stream on which to play it.
Joe Onoratocb109a02011-01-18 17:57:41 -08003299 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003300 * See {@link android.media.AudioManager} for the <code>STREAM_</code> constants.
3301 *
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003302 * @deprecated use {@link NotificationChannel#setSound(Uri, AudioAttributes)}.
Joe Onoratocb109a02011-01-18 17:57:41 -08003303 */
Jean-Michel Trivi81f871e2014-08-06 16:32:38 -07003304 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08003305 public Builder setSound(Uri sound, int streamType) {
Jean-Michel Trivi2f7511f2016-11-28 15:40:27 -08003306 PlayerBase.deprecateStreamTypeForPlayback(streamType, "Notification", "setSound()");
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003307 mN.sound = sound;
3308 mN.audioStreamType = streamType;
Joe Onorato46439ce2010-11-19 13:56:21 -08003309 return this;
3310 }
3311
Joe Onoratocb109a02011-01-18 17:57:41 -08003312 /**
John Spurlockc0650f022014-07-19 13:22:39 -04003313 * Set the sound to play, along with specific {@link AudioAttributes audio attributes} to
3314 * use during playback.
3315 *
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003316 * @deprecated use {@link NotificationChannel#setSound(Uri, AudioAttributes)} instead.
John Spurlockc0650f022014-07-19 13:22:39 -04003317 * @see Notification#sound
3318 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003319 @Deprecated
John Spurlockc0650f022014-07-19 13:22:39 -04003320 public Builder setSound(Uri sound, AudioAttributes audioAttributes) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003321 mN.sound = sound;
3322 mN.audioAttributes = audioAttributes;
John Spurlockc0650f022014-07-19 13:22:39 -04003323 return this;
3324 }
3325
3326 /**
Joe Onoratocb109a02011-01-18 17:57:41 -08003327 * Set the vibration pattern to use.
3328 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003329 * See {@link android.os.Vibrator#vibrate(long[], int)} for a discussion of the
3330 * <code>pattern</code> parameter.
3331 *
Chris Wren47c20a12014-06-18 17:27:29 -04003332 * <p>
3333 * A notification that vibrates is more likely to be presented as a heads-up notification.
3334 * </p>
3335 *
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003336 * @deprecated use {@link NotificationChannel#setVibrationPattern(long[])} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003337 * @see Notification#vibrate
Joe Onoratocb109a02011-01-18 17:57:41 -08003338 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003339 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08003340 public Builder setVibrate(long[] pattern) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003341 mN.vibrate = pattern;
Joe Onorato46439ce2010-11-19 13:56:21 -08003342 return this;
3343 }
3344
Joe Onoratocb109a02011-01-18 17:57:41 -08003345 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003346 * Set the desired color for the indicator LED on the device, as well as the
3347 * blink duty cycle (specified in milliseconds).
3348 *
3349
3350 * Not all devices will honor all (or even any) of these values.
3351 *
Julia Reynolds529e3322017-02-06 08:33:01 -05003352 * @deprecated use {@link NotificationChannel#enableLights(boolean)} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003353 * @see Notification#ledARGB
3354 * @see Notification#ledOnMS
3355 * @see Notification#ledOffMS
Joe Onoratocb109a02011-01-18 17:57:41 -08003356 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003357 @Deprecated
Tor Norbye80756e32015-03-02 09:39:27 -08003358 public Builder setLights(@ColorInt int argb, int onMs, int offMs) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003359 mN.ledARGB = argb;
3360 mN.ledOnMS = onMs;
3361 mN.ledOffMS = offMs;
Julia Reynolds10ee1fc2015-11-09 11:04:55 -05003362 if (onMs != 0 || offMs != 0) {
3363 mN.flags |= FLAG_SHOW_LIGHTS;
3364 }
Joe Onorato46439ce2010-11-19 13:56:21 -08003365 return this;
3366 }
3367
Joe Onoratocb109a02011-01-18 17:57:41 -08003368 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003369 * Set whether this is an "ongoing" notification.
Joe Onoratocb109a02011-01-18 17:57:41 -08003370 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003371
3372 * Ongoing notifications cannot be dismissed by the user, so your application or service
3373 * must take care of canceling them.
3374 *
3375
3376 * They are typically used to indicate a background task that the user is actively engaged
3377 * with (e.g., playing music) or is pending in some way and therefore occupying the device
3378 * (e.g., a file download, sync operation, active network connection).
3379 *
3380
3381 * @see Notification#FLAG_ONGOING_EVENT
3382 * @see Service#setForeground(boolean)
Joe Onoratocb109a02011-01-18 17:57:41 -08003383 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003384 public Builder setOngoing(boolean ongoing) {
3385 setFlag(FLAG_ONGOING_EVENT, ongoing);
3386 return this;
3387 }
3388
Joe Onoratocb109a02011-01-18 17:57:41 -08003389 /**
Selim Cinek7b9605b2017-01-19 17:36:00 -08003390 * Set whether this notification should be colorized. When set, the color set with
3391 * {@link #setColor(int)} will be used as the background color of this notification.
3392 * <p>
Selim Cinek7b9605b2017-01-19 17:36:00 -08003393 * This should only be used for high priority ongoing tasks like navigation, an ongoing
3394 * call, or other similarly high-priority events for the user.
Selim Cinek99104832017-01-25 14:47:33 -08003395 * <p>
Selim Cinek22714f12017-04-13 16:23:53 -07003396 * For most styles, the coloring will only be applied if the notification is for a
3397 * foreground service notification.
Selim Cinek99104832017-01-25 14:47:33 -08003398 * However, for {@link MediaStyle} and {@link DecoratedMediaCustomViewStyle} notifications
Selim Cinek22714f12017-04-13 16:23:53 -07003399 * that have a media session attached there is no such requirement.
Selim Cinek7b9605b2017-01-19 17:36:00 -08003400 *
Selim Cinek7b9605b2017-01-19 17:36:00 -08003401 * @see Builder#setColor(int)
Selim Cinek99104832017-01-25 14:47:33 -08003402 * @see MediaStyle#setMediaSession(MediaSession.Token)
Selim Cinek7b9605b2017-01-19 17:36:00 -08003403 */
3404 public Builder setColorized(boolean colorize) {
3405 mN.extras.putBoolean(EXTRA_COLORIZED, colorize);
3406 return this;
3407 }
3408
3409 /**
Joe Onoratocb109a02011-01-18 17:57:41 -08003410 * Set this flag if you would only like the sound, vibrate
3411 * and ticker to be played if the notification is not already showing.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003412 *
3413 * @see Notification#FLAG_ONLY_ALERT_ONCE
Joe Onoratocb109a02011-01-18 17:57:41 -08003414 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003415 public Builder setOnlyAlertOnce(boolean onlyAlertOnce) {
3416 setFlag(FLAG_ONLY_ALERT_ONCE, onlyAlertOnce);
3417 return this;
3418 }
3419
Joe Onoratocb109a02011-01-18 17:57:41 -08003420 /**
Julia Reynolds04499532016-09-13 14:04:53 -04003421 * Make this notification automatically dismissed when the user touches it.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003422 *
3423 * @see Notification#FLAG_AUTO_CANCEL
Joe Onoratocb109a02011-01-18 17:57:41 -08003424 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003425 public Builder setAutoCancel(boolean autoCancel) {
Joe Onorato281d83f2011-01-04 17:13:10 -08003426 setFlag(FLAG_AUTO_CANCEL, autoCancel);
Joe Onorato46439ce2010-11-19 13:56:21 -08003427 return this;
3428 }
3429
Joe Onoratocb109a02011-01-18 17:57:41 -08003430 /**
Griff Hazendfcb0802014-02-11 12:00:00 -08003431 * Set whether or not this notification should not bridge to other devices.
3432 *
3433 * <p>Some notifications can be bridged to other devices for remote display.
3434 * This hint can be set to recommend this notification not be bridged.
3435 */
3436 public Builder setLocalOnly(boolean localOnly) {
3437 setFlag(FLAG_LOCAL_ONLY, localOnly);
3438 return this;
3439 }
3440
3441 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003442 * Set which notification properties will be inherited from system defaults.
Joe Onoratocb109a02011-01-18 17:57:41 -08003443 * <p>
3444 * The value should be one or more of the following fields combined with
3445 * bitwise-or:
3446 * {@link #DEFAULT_SOUND}, {@link #DEFAULT_VIBRATE}, {@link #DEFAULT_LIGHTS}.
3447 * <p>
3448 * For all default values, use {@link #DEFAULT_ALL}.
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003449 *
3450 * @deprecated use {@link NotificationChannel#enableVibration(boolean)} and
Julia Reynolds529e3322017-02-06 08:33:01 -05003451 * {@link NotificationChannel#enableLights(boolean)} and
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003452 * {@link NotificationChannel#setSound(Uri, AudioAttributes)} instead.
Joe Onoratocb109a02011-01-18 17:57:41 -08003453 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003454 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08003455 public Builder setDefaults(int defaults) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003456 mN.defaults = defaults;
Joe Onorato46439ce2010-11-19 13:56:21 -08003457 return this;
3458 }
3459
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003460 /**
3461 * Set the priority of this notification.
3462 *
3463 * @see Notification#priority
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003464 * @deprecated use {@link NotificationChannel#setImportance(int)} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003465 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003466 @Deprecated
Tor Norbyed9273d62013-05-30 15:59:53 -07003467 public Builder setPriority(@Priority int pri) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003468 mN.priority = pri;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003469 return this;
3470 }
Joe Malin8d40d042012-11-05 11:36:40 -08003471
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003472 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -04003473 * Set the notification category.
Joe Malin8d40d042012-11-05 11:36:40 -08003474 *
John Spurlockfd7f1e02014-03-18 16:41:57 -04003475 * @see Notification#category
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003476 */
John Spurlockfd7f1e02014-03-18 16:41:57 -04003477 public Builder setCategory(String category) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003478 mN.category = category;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003479 return this;
3480 }
3481
3482 /**
Chris Wrendde75302014-03-26 17:24:15 -04003483 * Add a person that is relevant to this notification.
3484 *
Chris Wrene6c48932014-09-29 17:19:27 -04003485 * <P>
3486 * Depending on user preferences, this annotation may allow the notification to pass
Julia Reynoldse071abd2017-03-22 10:52:11 -04003487 * through interruption filters, if this notification is of category {@link #CATEGORY_CALL}
3488 * or {@link #CATEGORY_MESSAGE}. The addition of people may also cause this notification to
3489 * appear more prominently in the user interface.
Chris Wrene6c48932014-09-29 17:19:27 -04003490 * </P>
3491 *
3492 * <P>
3493 * The person should be specified by the {@code String} representation of a
3494 * {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI}.
3495 * </P>
3496 *
3497 * <P>The system will also attempt to resolve {@code mailto:} and {@code tel:} schema
3498 * URIs. The path part of these URIs must exist in the contacts database, in the
3499 * appropriate column, or the reference will be discarded as invalid. Telephone schema
3500 * URIs will be resolved by {@link android.provider.ContactsContract.PhoneLookup}.
3501 * </P>
3502 *
3503 * @param uri A URI for the person.
Chris Wrendde75302014-03-26 17:24:15 -04003504 * @see Notification#EXTRA_PEOPLE
3505 */
Chris Wrene6c48932014-09-29 17:19:27 -04003506 public Builder addPerson(String uri) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003507 mPersonList.add(uri);
Chris Wrendde75302014-03-26 17:24:15 -04003508 return this;
3509 }
3510
3511 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003512 * Set this notification to be part of a group of notifications sharing the same key.
3513 * Grouped notifications may display in a cluster or stack on devices which
3514 * support such rendering.
3515 *
3516 * <p>To make this notification the summary for its group, also call
3517 * {@link #setGroupSummary}. A sort order can be specified for group members by using
3518 * {@link #setSortKey}.
3519 * @param groupKey The group key of the group.
3520 * @return this object for method chaining
3521 */
3522 public Builder setGroup(String groupKey) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003523 mN.mGroupKey = groupKey;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003524 return this;
3525 }
3526
3527 /**
3528 * Set this notification to be the group summary for a group of notifications.
3529 * Grouped notifications may display in a cluster or stack on devices which
Julia Reynolds04499532016-09-13 14:04:53 -04003530 * support such rendering. If thereRequires a group key also be set using {@link #setGroup}.
3531 * The group summary may be suppressed if too few notifications are included in the group.
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003532 * @param isGroupSummary Whether this notification should be a group summary.
3533 * @return this object for method chaining
3534 */
3535 public Builder setGroupSummary(boolean isGroupSummary) {
3536 setFlag(FLAG_GROUP_SUMMARY, isGroupSummary);
3537 return this;
3538 }
3539
3540 /**
3541 * Set a sort key that orders this notification among other notifications from the
3542 * same package. This can be useful if an external sort was already applied and an app
3543 * would like to preserve this. Notifications will be sorted lexicographically using this
3544 * value, although providing different priorities in addition to providing sort key may
3545 * cause this value to be ignored.
3546 *
3547 * <p>This sort key can also be used to order members of a notification group. See
Griff Hazen9e1379f2014-05-20 12:50:51 -07003548 * {@link #setGroup}.
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003549 *
3550 * @see String#compareTo(String)
3551 */
3552 public Builder setSortKey(String sortKey) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003553 mN.mSortKey = sortKey;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003554 return this;
3555 }
3556
3557 /**
Griff Hazen720042b2014-02-24 15:46:56 -08003558 * Merge additional metadata into this notification.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003559 *
Griff Hazen720042b2014-02-24 15:46:56 -08003560 * <p>Values within the Bundle will replace existing extras values in this Builder.
3561 *
3562 * @see Notification#extras
3563 */
Griff Hazen959591e2014-05-15 22:26:18 -07003564 public Builder addExtras(Bundle extras) {
3565 if (extras != null) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003566 mUserExtras.putAll(extras);
Griff Hazen720042b2014-02-24 15:46:56 -08003567 }
3568 return this;
3569 }
3570
3571 /**
3572 * Set metadata for this notification.
3573 *
3574 * <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 -04003575 * current contents are copied into the Notification each time {@link #build()} is
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003576 * called.
3577 *
Griff Hazen720042b2014-02-24 15:46:56 -08003578 * <p>Replaces any existing extras values with those from the provided Bundle.
3579 * Use {@link #addExtras} to merge in metadata instead.
3580 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003581 * @see Notification#extras
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003582 */
Griff Hazen959591e2014-05-15 22:26:18 -07003583 public Builder setExtras(Bundle extras) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003584 if (extras != null) {
3585 mUserExtras = extras;
3586 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003587 return this;
3588 }
3589
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003590 /**
Griff Hazen720042b2014-02-24 15:46:56 -08003591 * Get the current metadata Bundle used by this notification Builder.
3592 *
3593 * <p>The returned Bundle is shared with this Builder.
3594 *
3595 * <p>The current contents of this Bundle are copied into the Notification each time
3596 * {@link #build()} is called.
3597 *
3598 * @see Notification#extras
3599 */
3600 public Bundle getExtras() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003601 return mUserExtras;
3602 }
3603
3604 private Bundle getAllExtras() {
3605 final Bundle saveExtras = (Bundle) mUserExtras.clone();
3606 saveExtras.putAll(mN.extras);
3607 return saveExtras;
Griff Hazen720042b2014-02-24 15:46:56 -08003608 }
3609
3610 /**
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003611 * Add an action to this notification. Actions are typically displayed by
3612 * the system as a button adjacent to the notification content.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04003613 * <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}).
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003622 *
3623 * @param icon Resource ID of a drawable that represents the action.
3624 * @param title Text describing the action.
3625 * @param intent PendingIntent to be fired when the action is invoked.
Dan Sandler86647982015-05-13 23:41:13 -04003626 *
3627 * @deprecated Use {@link #addAction(Action)} instead.
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003628 */
Dan Sandler86647982015-05-13 23:41:13 -04003629 @Deprecated
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003630 public Builder addAction(int icon, CharSequence title, PendingIntent intent) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04003631 mActions.add(new Action(icon, safeCharSequence(title), intent));
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003632 return this;
3633 }
3634
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003635 /**
Griff Hazen959591e2014-05-15 22:26:18 -07003636 * Add an action to this notification. Actions are typically displayed by
3637 * the system as a button adjacent to the notification content.
3638 * <p>
3639 * Every action must have an icon (32dp square and matching the
3640 * <a href="{@docRoot}design/style/iconography.html#action-bar">Holo
3641 * Dark action bar</a> visual style), a textual label, and a {@link PendingIntent}.
3642 * <p>
3643 * A notification in its expanded form can display up to 3 actions, from left to right in
3644 * the order they were added. Actions will not be displayed when the notification is
3645 * collapsed, however, so be sure that any essential functions may be accessed by the user
3646 * in some other way (for example, in the Activity pointed to by {@link #contentIntent}).
3647 *
3648 * @param action The action to add.
3649 */
3650 public Builder addAction(Action action) {
liangweikang63b03b52017-03-16 19:22:15 +08003651 if (action != null) {
3652 mActions.add(action);
3653 }
Griff Hazen959591e2014-05-15 22:26:18 -07003654 return this;
3655 }
3656
3657 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003658 * Alter the complete list of actions attached to this notification.
3659 * @see #addAction(Action).
3660 *
3661 * @param actions
3662 * @return
3663 */
3664 public Builder setActions(Action... actions) {
3665 mActions.clear();
3666 for (int i = 0; i < actions.length; i++) {
liangweikang63b03b52017-03-16 19:22:15 +08003667 if (actions[i] != null) {
3668 mActions.add(actions[i]);
3669 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003670 }
3671 return this;
3672 }
3673
3674 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003675 * Add a rich notification style to be applied at build time.
3676 *
3677 * @param style Object responsible for modifying the notification style.
3678 */
3679 public Builder setStyle(Style style) {
3680 if (mStyle != style) {
3681 mStyle = style;
Daniel Sandlerc08dea22012-06-28 08:35:24 -07003682 if (mStyle != null) {
3683 mStyle.setBuilder(this);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003684 mN.extras.putString(EXTRA_TEMPLATE, style.getClass().getName());
3685 } else {
3686 mN.extras.remove(EXTRA_TEMPLATE);
Daniel Sandlerc08dea22012-06-28 08:35:24 -07003687 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003688 }
3689 return this;
3690 }
3691
Dan Sandler0bf2ed82013-12-21 23:33:41 -06003692 /**
3693 * Specify the value of {@link #visibility}.
Griff Hazenb720abe2014-05-20 13:15:30 -07003694 *
Dan Sandler0bf2ed82013-12-21 23:33:41 -06003695 * @return The same Builder.
3696 */
Jeff Sharkey30e06bb2017-04-24 11:18:03 -06003697 public Builder setVisibility(@Visibility int visibility) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003698 mN.visibility = visibility;
Dan Sandler0bf2ed82013-12-21 23:33:41 -06003699 return this;
3700 }
3701
3702 /**
3703 * Supply a replacement Notification whose contents should be shown in insecure contexts
3704 * (i.e. atop the secure lockscreen). See {@link #visibility} and {@link #VISIBILITY_PUBLIC}.
3705 * @param n A replacement notification, presumably with some or all info redacted.
3706 * @return The same Builder.
3707 */
3708 public Builder setPublicVersion(Notification n) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003709 if (n != null) {
3710 mN.publicVersion = new Notification();
3711 n.cloneInto(mN.publicVersion, /*heavy=*/ true);
3712 } else {
3713 mN.publicVersion = null;
3714 }
Dan Sandler0bf2ed82013-12-21 23:33:41 -06003715 return this;
3716 }
3717
Griff Hazenb720abe2014-05-20 13:15:30 -07003718 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003719 * Apply an extender to this notification builder. Extenders may be used to add
3720 * metadata or change options on this builder.
3721 */
Griff Hazen61a9e862014-05-22 16:05:19 -07003722 public Builder extend(Extender extender) {
3723 extender.extend(this);
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003724 return this;
3725 }
3726
Dan Sandler4e787062015-06-17 15:09:48 -04003727 /**
3728 * @hide
3729 */
Julia Reynoldse46bb372016-03-17 11:05:58 -04003730 public Builder setFlag(int mask, boolean value) {
Joe Onorato46439ce2010-11-19 13:56:21 -08003731 if (value) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003732 mN.flags |= mask;
Joe Onorato46439ce2010-11-19 13:56:21 -08003733 } else {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003734 mN.flags &= ~mask;
Joe Onorato46439ce2010-11-19 13:56:21 -08003735 }
Julia Reynoldse46bb372016-03-17 11:05:58 -04003736 return this;
Joe Onorato46439ce2010-11-19 13:56:21 -08003737 }
3738
Dan Sandler26e81cf2014-05-06 10:01:27 -04003739 /**
3740 * Sets {@link Notification#color}.
3741 *
3742 * @param argb The accent color to use
3743 *
3744 * @return The same Builder.
3745 */
Tor Norbye80756e32015-03-02 09:39:27 -08003746 public Builder setColor(@ColorInt int argb) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003747 mN.color = argb;
Julia Reynolds10ee1fc2015-11-09 11:04:55 -05003748 sanitizeColor();
Dan Sandler26e81cf2014-05-06 10:01:27 -04003749 return this;
3750 }
3751
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003752 private Drawable getProfileBadgeDrawable() {
Chris Wren66619a22016-05-12 16:42:37 -04003753 if (mContext.getUserId() == UserHandle.USER_SYSTEM) {
3754 // This user can never be a badged profile,
3755 // and also includes USER_ALL system notifications.
3756 return null;
3757 }
Christoph Studer7ac80e62014-08-04 16:01:57 +02003758 // Note: This assumes that the current user can read the profile badge of the
3759 // originating user.
Selim Cineke6ff9462016-01-15 15:07:06 -08003760 return mContext.getPackageManager().getUserBadgeForDensityNoBackground(
Julia Reynoldsda303542015-11-23 14:00:20 -05003761 new UserHandle(mContext.getUserId()), 0);
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003762 }
3763
3764 private Bitmap getProfileBadge() {
3765 Drawable badge = getProfileBadgeDrawable();
Kenny Guy8a0101b2014-05-08 23:34:12 +01003766 if (badge == null) {
3767 return null;
3768 }
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003769 final int size = mContext.getResources().getDimensionPixelSize(
3770 R.dimen.notification_badge_size);
3771 Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
Kenny Guy8a0101b2014-05-08 23:34:12 +01003772 Canvas canvas = new Canvas(bitmap);
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003773 badge.setBounds(0, 0, size, size);
Kenny Guy8a0101b2014-05-08 23:34:12 +01003774 badge.draw(canvas);
3775 return bitmap;
3776 }
3777
Selim Cinekc848c3a2016-01-13 15:27:30 -08003778 private void bindProfileBadge(RemoteViews contentView) {
Kenny Guy98193ea2014-07-24 19:54:37 +01003779 Bitmap profileBadge = getProfileBadge();
3780
Kenny Guy98193ea2014-07-24 19:54:37 +01003781 if (profileBadge != null) {
Selim Cinekc848c3a2016-01-13 15:27:30 -08003782 contentView.setImageViewBitmap(R.id.profile_badge, profileBadge);
3783 contentView.setViewVisibility(R.id.profile_badge, View.VISIBLE);
Selim Cinek7b9605b2017-01-19 17:36:00 -08003784 if (isColorized()) {
3785 contentView.setDrawableParameters(R.id.profile_badge, false, -1,
3786 getPrimaryTextColor(), PorterDuff.Mode.SRC_ATOP, -1);
3787 }
Kenny Guy98193ea2014-07-24 19:54:37 +01003788 }
Kenny Guy98193ea2014-07-24 19:54:37 +01003789 }
3790
Christoph Studerfe718432014-09-01 18:21:18 +02003791 private void resetStandardTemplate(RemoteViews contentView) {
Selim Cinekeaa29ca2015-11-23 13:51:13 -08003792 resetNotificationHeader(contentView);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003793 resetContentMargins(contentView);
Christoph Studerfe718432014-09-01 18:21:18 +02003794 contentView.setViewVisibility(R.id.right_icon, View.GONE);
Selim Cinek860b6da2015-12-16 19:02:19 -08003795 contentView.setViewVisibility(R.id.title, View.GONE);
Christoph Studerfe718432014-09-01 18:21:18 +02003796 contentView.setTextViewText(R.id.title, null);
Selim Cinek41598732016-01-11 16:58:37 -08003797 contentView.setViewVisibility(R.id.text, View.GONE);
Christoph Studerfe718432014-09-01 18:21:18 +02003798 contentView.setTextViewText(R.id.text, null);
Selim Cinek29603462015-11-17 19:04:39 -08003799 contentView.setViewVisibility(R.id.text_line_1, View.GONE);
Selim Cinek41598732016-01-11 16:58:37 -08003800 contentView.setTextViewText(R.id.text_line_1, null);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003801 }
3802
Selim Cinekeaa29ca2015-11-23 13:51:13 -08003803 /**
3804 * Resets the notification header to its original state
3805 */
3806 private void resetNotificationHeader(RemoteViews contentView) {
Adrian Roosc4337a32016-08-02 18:30:34 -07003807 // Small icon doesn't need to be reset, as it's always set. Resetting would prevent
3808 // re-using the drawable when the notification is updated.
Selim Cinek7b836392015-12-04 20:02:59 -08003809 contentView.setBoolean(R.id.notification_header, "setExpanded", false);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003810 contentView.setTextViewText(R.id.app_name_text, null);
Christoph Studerca1db712014-09-10 17:31:33 +02003811 contentView.setViewVisibility(R.id.chronometer, View.GONE);
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07003812 contentView.setViewVisibility(R.id.header_text, View.GONE);
Adrian Roos9dfb78f2016-06-30 15:43:44 -07003813 contentView.setTextViewText(R.id.header_text, null);
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07003814 contentView.setViewVisibility(R.id.header_text_divider, View.GONE);
Selim Cinek29603462015-11-17 19:04:39 -08003815 contentView.setViewVisibility(R.id.time_divider, View.GONE);
Selim Cinekb85f36fd2016-04-20 18:46:36 -07003816 contentView.setViewVisibility(R.id.time, View.GONE);
Selim Cinekc848c3a2016-01-13 15:27:30 -08003817 contentView.setImageViewIcon(R.id.profile_badge, null);
3818 contentView.setViewVisibility(R.id.profile_badge, View.GONE);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003819 }
3820
3821 private void resetContentMargins(RemoteViews contentView) {
Adrian Roos2d5dbba2016-06-08 17:11:53 -07003822 contentView.setViewLayoutMarginEndDimen(R.id.line1, 0);
3823 contentView.setViewLayoutMarginEndDimen(R.id.text, 0);
Christoph Studerfe718432014-09-01 18:21:18 +02003824 }
3825
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003826 private RemoteViews applyStandardTemplate(int resId) {
Adrian Roos70d7aa32017-01-11 15:39:06 -08003827 return applyStandardTemplate(resId, mParams.reset().fillTextsFrom(this));
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02003828 }
3829
3830 /**
3831 * @param hasProgress whether the progress bar should be shown and set
3832 */
3833 private RemoteViews applyStandardTemplate(int resId, boolean hasProgress) {
Adrian Roos70d7aa32017-01-11 15:39:06 -08003834 return applyStandardTemplate(resId, mParams.reset().hasProgress(hasProgress)
3835 .fillTextsFrom(this));
Adrian Roosc1a80b02016-04-05 14:54:55 -07003836 }
3837
Adrian Roos70d7aa32017-01-11 15:39:06 -08003838 private RemoteViews applyStandardTemplate(int resId, StandardTemplateParams p) {
Kenny Guy77320062014-08-27 21:37:15 +01003839 RemoteViews contentView = new BuilderRemoteViews(mContext.getApplicationInfo(), resId);
Dan Sandler539aad42014-08-04 00:43:39 -04003840
Christoph Studerfe718432014-09-01 18:21:18 +02003841 resetStandardTemplate(contentView);
3842
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003843 final Bundle ex = mN.extras;
Selim Cinek7b9605b2017-01-19 17:36:00 -08003844 updateBackgroundColor(contentView);
Adrian Roos487374f2017-01-11 15:48:14 -08003845 bindNotificationHeader(contentView, p.ambient);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003846 bindLargeIcon(contentView);
Adrian Roos70d7aa32017-01-11 15:39:06 -08003847 boolean showProgress = handleProgressBar(p.hasProgress, contentView, ex);
3848 if (p.title != null) {
Selim Cinek860b6da2015-12-16 19:02:19 -08003849 contentView.setViewVisibility(R.id.title, View.VISIBLE);
Adrian Roos70d7aa32017-01-11 15:39:06 -08003850 contentView.setTextViewText(R.id.title, p.title);
Adrian Roos72171622017-01-27 10:32:06 -08003851 if (!p.ambient) {
3852 setTextViewColorPrimary(contentView, R.id.title);
3853 }
Selim Cinek954cc232016-05-20 13:29:23 -07003854 contentView.setViewLayoutWidth(R.id.title, showProgress
3855 ? ViewGroup.LayoutParams.WRAP_CONTENT
3856 : ViewGroup.LayoutParams.MATCH_PARENT);
Joe Onorato561d3852010-11-20 18:09:34 -08003857 }
Adrian Roos70d7aa32017-01-11 15:39:06 -08003858 if (p.text != null) {
Selim Cinek41598732016-01-11 16:58:37 -08003859 int textId = showProgress ? com.android.internal.R.id.text_line_1
3860 : com.android.internal.R.id.text;
Adrian Roos70d7aa32017-01-11 15:39:06 -08003861 contentView.setTextViewText(textId, p.text);
Adrian Roos72171622017-01-27 10:32:06 -08003862 if (!p.ambient) {
3863 setTextViewColorSecondary(contentView, textId);
3864 }
Selim Cinek41598732016-01-11 16:58:37 -08003865 contentView.setViewVisibility(textId, View.VISIBLE);
Joe Onorato561d3852010-11-20 18:09:34 -08003866 }
Selim Cinekc848c3a2016-01-13 15:27:30 -08003867
Selim Cinek279fa862016-06-14 10:57:25 -07003868 setContentMinHeight(contentView, showProgress || mN.hasLargeIcon());
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003869
Selim Cinek29603462015-11-17 19:04:39 -08003870 return contentView;
3871 }
3872
Selim Cinek7b9605b2017-01-19 17:36:00 -08003873 private void setTextViewColorPrimary(RemoteViews contentView, int id) {
3874 ensureColors();
3875 contentView.setTextColor(id, mPrimaryTextColor);
3876 }
3877
Selim Cinek389edcd2017-05-11 19:16:44 -07003878 /**
3879 * @return the primary text color
3880 * @hide
3881 */
3882 @VisibleForTesting
3883 public int getPrimaryTextColor() {
Selim Cinek7b9605b2017-01-19 17:36:00 -08003884 ensureColors();
3885 return mPrimaryTextColor;
3886 }
3887
Selim Cinek389edcd2017-05-11 19:16:44 -07003888 /**
3889 * @return the secondary text color
3890 * @hide
3891 */
3892 @VisibleForTesting
3893 public int getSecondaryTextColor() {
3894 ensureColors();
3895 return mSecondaryTextColor;
3896 }
3897
Selim Cinek7b9605b2017-01-19 17:36:00 -08003898 private int getActionBarColor() {
3899 ensureColors();
3900 return mActionBarColor;
3901 }
3902
Selim Cinek622c64a2017-04-17 17:10:05 -07003903 private int getActionBarColorDeEmphasized() {
3904 int backgroundColor = getBackgroundColor();
3905 return NotificationColorUtil.getShiftedColor(backgroundColor, 12);
3906 }
3907
Selim Cinek7b9605b2017-01-19 17:36:00 -08003908 private void setTextViewColorSecondary(RemoteViews contentView, int id) {
3909 ensureColors();
3910 contentView.setTextColor(id, mSecondaryTextColor);
3911 }
3912
3913 private void ensureColors() {
3914 int backgroundColor = getBackgroundColor();
3915 if (mPrimaryTextColor == COLOR_INVALID
3916 || mSecondaryTextColor == COLOR_INVALID
3917 || mActionBarColor == COLOR_INVALID
3918 || mTextColorsAreForBackground != backgroundColor) {
3919 mTextColorsAreForBackground = backgroundColor;
Selim Cinek5fb73f82017-04-20 16:55:38 -07003920 if (mForegroundColor == COLOR_INVALID || !isColorized()) {
3921 mPrimaryTextColor = NotificationColorUtil.resolvePrimaryColor(mContext,
3922 backgroundColor);
3923 mSecondaryTextColor = NotificationColorUtil.resolveSecondaryColor(mContext,
3924 backgroundColor);
Selim Cinekac5f0272017-05-02 16:05:41 -07003925 if (backgroundColor != COLOR_DEFAULT
3926 && (mBackgroundColorHint != COLOR_INVALID || isColorized())) {
3927 mPrimaryTextColor = NotificationColorUtil.findAlphaToMeetContrast(
3928 mPrimaryTextColor, backgroundColor, 4.5);
3929 mSecondaryTextColor = NotificationColorUtil.findAlphaToMeetContrast(
3930 mSecondaryTextColor, backgroundColor, 4.5);
3931 }
Selim Cinek5fb73f82017-04-20 16:55:38 -07003932 } else {
3933 double backLum = NotificationColorUtil.calculateLuminance(backgroundColor);
3934 double textLum = NotificationColorUtil.calculateLuminance(mForegroundColor);
3935 double contrast = NotificationColorUtil.calculateContrast(mForegroundColor,
3936 backgroundColor);
Selim Cinek389edcd2017-05-11 19:16:44 -07003937 // We only respect the given colors if worst case Black or White still has
3938 // contrast
3939 boolean backgroundLight = backLum > textLum
3940 && satisfiesTextContrast(backgroundColor, Color.BLACK)
3941 || backLum <= textLum
3942 && !satisfiesTextContrast(backgroundColor, Color.WHITE);
Selim Cinek5fb73f82017-04-20 16:55:38 -07003943 if (contrast < 4.5f) {
Selim Cinek389edcd2017-05-11 19:16:44 -07003944 if (backgroundLight) {
Selim Cinek5fb73f82017-04-20 16:55:38 -07003945 mSecondaryTextColor = NotificationColorUtil.findContrastColor(
3946 mForegroundColor,
3947 backgroundColor,
3948 true /* findFG */,
3949 4.5f);
3950 mPrimaryTextColor = NotificationColorUtil.changeColorLightness(
Selim Cinek389edcd2017-05-11 19:16:44 -07003951 mSecondaryTextColor, -LIGHTNESS_TEXT_DIFFERENCE_LIGHT);
Selim Cinek5fb73f82017-04-20 16:55:38 -07003952 } else {
3953 mSecondaryTextColor =
3954 NotificationColorUtil.findContrastColorAgainstDark(
3955 mForegroundColor,
3956 backgroundColor,
3957 true /* findFG */,
3958 4.5f);
3959 mPrimaryTextColor = NotificationColorUtil.changeColorLightness(
Selim Cinek389edcd2017-05-11 19:16:44 -07003960 mSecondaryTextColor, -LIGHTNESS_TEXT_DIFFERENCE_DARK);
Selim Cinek5fb73f82017-04-20 16:55:38 -07003961 }
3962 } else {
3963 mPrimaryTextColor = mForegroundColor;
3964 mSecondaryTextColor = NotificationColorUtil.changeColorLightness(
Selim Cinek389edcd2017-05-11 19:16:44 -07003965 mPrimaryTextColor, backgroundLight ? LIGHTNESS_TEXT_DIFFERENCE_LIGHT
3966 : LIGHTNESS_TEXT_DIFFERENCE_DARK);
Selim Cinek5fb73f82017-04-20 16:55:38 -07003967 if (NotificationColorUtil.calculateContrast(mSecondaryTextColor,
3968 backgroundColor) < 4.5f) {
3969 // oh well the secondary is not good enough
Selim Cinek389edcd2017-05-11 19:16:44 -07003970 if (backgroundLight) {
Selim Cinek5fb73f82017-04-20 16:55:38 -07003971 mSecondaryTextColor = NotificationColorUtil.findContrastColor(
3972 mSecondaryTextColor,
3973 backgroundColor,
3974 true /* findFG */,
3975 4.5f);
3976 } else {
3977 mSecondaryTextColor
3978 = NotificationColorUtil.findContrastColorAgainstDark(
3979 mSecondaryTextColor,
3980 backgroundColor,
3981 true /* findFG */,
3982 4.5f);
3983 }
3984 mPrimaryTextColor = NotificationColorUtil.changeColorLightness(
Selim Cinek389edcd2017-05-11 19:16:44 -07003985 mSecondaryTextColor, backgroundLight
3986 ? -LIGHTNESS_TEXT_DIFFERENCE_LIGHT
3987 : -LIGHTNESS_TEXT_DIFFERENCE_DARK);
Selim Cinek5fb73f82017-04-20 16:55:38 -07003988 }
3989 }
3990 }
Selim Cinek875ba9b2017-02-13 16:20:17 -08003991 mActionBarColor = NotificationColorUtil.resolveActionBarColor(mContext,
3992 backgroundColor);
Selim Cinek7b9605b2017-01-19 17:36:00 -08003993 }
3994 }
3995
3996 private void updateBackgroundColor(RemoteViews contentView) {
3997 if (isColorized()) {
3998 contentView.setInt(R.id.status_bar_latest_event_content, "setBackgroundColor",
3999 getBackgroundColor());
4000 } else {
4001 // Clear it!
4002 contentView.setInt(R.id.status_bar_latest_event_content, "setBackgroundResource",
4003 0);
4004 }
4005 }
4006
Selim Cinek860b6da2015-12-16 19:02:19 -08004007 /**
4008 * @param remoteView the remote view to update the minheight in
4009 * @param hasMinHeight does it have a mimHeight
4010 * @hide
4011 */
4012 void setContentMinHeight(RemoteViews remoteView, boolean hasMinHeight) {
4013 int minHeight = 0;
4014 if (hasMinHeight) {
4015 // we need to set the minHeight of the notification
4016 minHeight = mContext.getResources().getDimensionPixelSize(
4017 com.android.internal.R.dimen.notification_min_content_height);
4018 }
4019 remoteView.setInt(R.id.notification_main_column, "setMinimumHeight", minHeight);
4020 }
4021
Selim Cinek29603462015-11-17 19:04:39 -08004022 private boolean handleProgressBar(boolean hasProgress, RemoteViews contentView, Bundle ex) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004023 final int max = ex.getInt(EXTRA_PROGRESS_MAX, 0);
4024 final int progress = ex.getInt(EXTRA_PROGRESS, 0);
4025 final boolean ind = ex.getBoolean(EXTRA_PROGRESS_INDETERMINATE);
4026 if (hasProgress && (max != 0 || ind)) {
Selim Cinek29603462015-11-17 19:04:39 -08004027 contentView.setViewVisibility(com.android.internal.R.id.progress, View.VISIBLE);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004028 contentView.setProgressBar(
Selim Cinek29603462015-11-17 19:04:39 -08004029 R.id.progress, max, progress, ind);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004030 contentView.setProgressBackgroundTintList(
4031 R.id.progress, ColorStateList.valueOf(mContext.getColor(
4032 R.color.notification_progress_background_color)));
Selim Cinek29603462015-11-17 19:04:39 -08004033 if (mN.color != COLOR_DEFAULT) {
Adrian Roos4ff3b122016-02-01 12:26:13 -08004034 ColorStateList colorStateList = ColorStateList.valueOf(resolveContrastColor());
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004035 contentView.setProgressTintList(R.id.progress, colorStateList);
4036 contentView.setProgressIndeterminateTintList(R.id.progress, colorStateList);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004037 }
Selim Cinek29603462015-11-17 19:04:39 -08004038 return true;
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004039 } else {
4040 contentView.setViewVisibility(R.id.progress, View.GONE);
Selim Cinek29603462015-11-17 19:04:39 -08004041 return false;
Jeff Sharkey1c400132011-08-05 14:50:13 -07004042 }
Joe Onorato561d3852010-11-20 18:09:34 -08004043 }
4044
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004045 private void bindLargeIcon(RemoteViews contentView) {
Selim Cinek279fa862016-06-14 10:57:25 -07004046 if (mN.mLargeIcon == null && mN.largeIcon != null) {
4047 mN.mLargeIcon = Icon.createWithBitmap(mN.largeIcon);
4048 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004049 if (mN.mLargeIcon != null) {
4050 contentView.setViewVisibility(R.id.right_icon, View.VISIBLE);
4051 contentView.setImageViewIcon(R.id.right_icon, mN.mLargeIcon);
4052 processLargeLegacyIcon(mN.mLargeIcon, contentView);
Adrian Roos2d5dbba2016-06-08 17:11:53 -07004053 int endMargin = R.dimen.notification_content_picture_margin;
4054 contentView.setViewLayoutMarginEndDimen(R.id.line1, endMargin);
4055 contentView.setViewLayoutMarginEndDimen(R.id.text, endMargin);
4056 contentView.setViewLayoutMarginEndDimen(R.id.progress, endMargin);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004057 }
4058 }
4059
Adrian Roos487374f2017-01-11 15:48:14 -08004060 private void bindNotificationHeader(RemoteViews contentView, boolean ambient) {
4061 bindSmallIcon(contentView, ambient);
4062 bindHeaderAppName(contentView, ambient);
4063 if (!ambient) {
4064 // Ambient view does not have these
4065 bindHeaderText(contentView);
4066 bindHeaderChronometerAndTime(contentView);
Adrian Roos487374f2017-01-11 15:48:14 -08004067 bindProfileBadge(contentView);
4068 }
Adrian Roosd83e9992017-03-16 15:17:57 -07004069 bindExpandButton(contentView);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004070 }
4071
4072 private void bindExpandButton(RemoteViews contentView) {
Selim Cinek99104832017-01-25 14:47:33 -08004073 int color = getPrimaryHighlightColor();
Selim Cinek7b9605b2017-01-19 17:36:00 -08004074 contentView.setDrawableParameters(R.id.expand_button, false, -1, color,
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004075 PorterDuff.Mode.SRC_ATOP, -1);
Selim Cinekea4bef72015-12-02 15:51:10 -08004076 contentView.setInt(R.id.notification_header, "setOriginalNotificationColor",
Selim Cinek7b9605b2017-01-19 17:36:00 -08004077 color);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004078 }
4079
Selim Cinek99104832017-01-25 14:47:33 -08004080 /**
4081 * @return the color that is used as the first primary highlight color. This is applied
4082 * in several places like the action buttons or the app name in the header.
4083 */
4084 private int getPrimaryHighlightColor() {
4085 return isColorized() ? getPrimaryTextColor() : resolveContrastColor();
4086 }
4087
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004088 private void bindHeaderChronometerAndTime(RemoteViews contentView) {
4089 if (showsTimeOrChronometer()) {
Selim Cinek29603462015-11-17 19:04:39 -08004090 contentView.setViewVisibility(R.id.time_divider, View.VISIBLE);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004091 setTextViewColorSecondary(contentView, R.id.time_divider);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004092 if (mN.extras.getBoolean(EXTRA_SHOW_CHRONOMETER)) {
4093 contentView.setViewVisibility(R.id.chronometer, View.VISIBLE);
4094 contentView.setLong(R.id.chronometer, "setBase",
4095 mN.when + (SystemClock.elapsedRealtime() - System.currentTimeMillis()));
4096 contentView.setBoolean(R.id.chronometer, "setStarted", true);
Adrian Roos96b7e202016-05-17 13:50:38 -07004097 boolean countsDown = mN.extras.getBoolean(EXTRA_CHRONOMETER_COUNT_DOWN);
Selim Cinekc3b752e2016-04-20 16:13:59 -07004098 contentView.setChronometerCountDown(R.id.chronometer, countsDown);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004099 setTextViewColorSecondary(contentView, R.id.chronometer);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004100 } else {
4101 contentView.setViewVisibility(R.id.time, View.VISIBLE);
4102 contentView.setLong(R.id.time, "setTime", mN.when);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004103 setTextViewColorSecondary(contentView, R.id.time);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004104 }
Selim Cinekb85f36fd2016-04-20 18:46:36 -07004105 } else {
4106 // We still want a time to be set but gone, such that we can show and hide it
4107 // on demand in case it's a child notification without anything in the header
4108 contentView.setLong(R.id.time, "setTime", mN.when != 0 ? mN.when : mN.creationTime);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004109 }
4110 }
4111
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07004112 private void bindHeaderText(RemoteViews contentView) {
4113 CharSequence headerText = mN.extras.getCharSequence(EXTRA_SUB_TEXT);
4114 if (headerText == null && mStyle != null && mStyle.mSummaryTextSet
Selim Cinek03d0d652015-11-13 13:18:09 -05004115 && mStyle.hasSummaryInHeader()) {
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07004116 headerText = mStyle.mSummaryText;
Selim Cinek03d0d652015-11-13 13:18:09 -05004117 }
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07004118 if (headerText == null
4119 && mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N
4120 && mN.extras.getCharSequence(EXTRA_INFO_TEXT) != null) {
4121 headerText = mN.extras.getCharSequence(EXTRA_INFO_TEXT);
4122 }
4123 if (headerText != null) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004124 // TODO: Remove the span entirely to only have the string with propper formating.
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07004125 contentView.setTextViewText(R.id.header_text, processLegacyText(headerText));
Selim Cinek7b9605b2017-01-19 17:36:00 -08004126 setTextViewColorSecondary(contentView, R.id.header_text);
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07004127 contentView.setViewVisibility(R.id.header_text, View.VISIBLE);
4128 contentView.setViewVisibility(R.id.header_text_divider, View.VISIBLE);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004129 setTextViewColorSecondary(contentView, R.id.header_text_divider);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004130 }
4131 }
4132
Adrian Rooseba05822016-04-22 17:09:27 -07004133 /**
4134 * @hide
4135 */
4136 public String loadHeaderAppName() {
Dan Sandler732bd6c2016-04-12 14:20:32 -04004137 CharSequence name = null;
4138 final PackageManager pm = mContext.getPackageManager();
4139 if (mN.extras.containsKey(EXTRA_SUBSTITUTE_APP_NAME)) {
4140 // only system packages which lump together a bunch of unrelated stuff
4141 // may substitute a different name to make the purpose of the
4142 // notification more clear. the correct package label should always
4143 // be accessible via SystemUI.
4144 final String pkg = mContext.getPackageName();
4145 final String subName = mN.extras.getString(EXTRA_SUBSTITUTE_APP_NAME);
4146 if (PackageManager.PERMISSION_GRANTED == pm.checkPermission(
4147 android.Manifest.permission.SUBSTITUTE_NOTIFICATION_APP_NAME, pkg)) {
4148 name = subName;
4149 } else {
4150 Log.w(TAG, "warning: pkg "
4151 + pkg + " attempting to substitute app name '" + subName
4152 + "' without holding perm "
4153 + android.Manifest.permission.SUBSTITUTE_NOTIFICATION_APP_NAME);
4154 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004155 }
Dan Sandler732bd6c2016-04-12 14:20:32 -04004156 if (TextUtils.isEmpty(name)) {
4157 name = pm.getApplicationLabel(mContext.getApplicationInfo());
4158 }
4159 if (TextUtils.isEmpty(name)) {
4160 // still nothing?
4161 return null;
4162 }
4163
4164 return String.valueOf(name);
4165 }
Adrian Roos487374f2017-01-11 15:48:14 -08004166 private void bindHeaderAppName(RemoteViews contentView, boolean ambient) {
Dan Sandler732bd6c2016-04-12 14:20:32 -04004167 contentView.setTextViewText(R.id.app_name_text, loadHeaderAppName());
Adrian Roos72171622017-01-27 10:32:06 -08004168 if (isColorized() && !ambient) {
Selim Cinek7b9605b2017-01-19 17:36:00 -08004169 setTextViewColorPrimary(contentView, R.id.app_name_text);
4170 } else {
4171 contentView.setTextColor(R.id.app_name_text,
4172 ambient ? resolveAmbientColor() : resolveContrastColor());
4173 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004174 }
4175
Adrian Roos487374f2017-01-11 15:48:14 -08004176 private void bindSmallIcon(RemoteViews contentView, boolean ambient) {
Selim Cinek279fa862016-06-14 10:57:25 -07004177 if (mN.mSmallIcon == null && mN.icon != 0) {
4178 mN.mSmallIcon = Icon.createWithResource(mContext, mN.icon);
4179 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004180 contentView.setImageViewIcon(R.id.icon, mN.mSmallIcon);
Adrian Roos9b45a152016-06-28 13:32:29 -07004181 contentView.setDrawableParameters(R.id.icon, false /* targetBackground */,
4182 -1 /* alpha */, -1 /* colorFilter */, null /* mode */, mN.iconLevel);
Adrian Roos487374f2017-01-11 15:48:14 -08004183 processSmallIconColor(mN.mSmallIcon, contentView, ambient);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004184 }
4185
Jorim Jaggi445d3c02014-08-19 22:33:42 +02004186 /**
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004187 * @return true if the built notification will show the time or the chronometer; false
4188 * otherwise
4189 */
4190 private boolean showsTimeOrChronometer() {
Selim Cinekc2c0b042016-05-18 17:13:46 -07004191 return mN.showsTime() || mN.showsChronometer();
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004192 }
4193
Christoph Studerfe718432014-09-01 18:21:18 +02004194 private void resetStandardTemplateWithActions(RemoteViews big) {
Adrian Roos4c1fcc82016-03-31 14:39:39 -07004195 // actions_container is only reset when there are no actions to avoid focus issues with
4196 // remote inputs.
Christoph Studerfe718432014-09-01 18:21:18 +02004197 big.setViewVisibility(R.id.actions, View.GONE);
Christoph Studerfe718432014-09-01 18:21:18 +02004198 big.removeAllViews(R.id.actions);
Adrian Roose458aa82015-12-08 16:17:19 -08004199
4200 big.setViewVisibility(R.id.notification_material_reply_container, View.GONE);
4201 big.setTextViewText(R.id.notification_material_reply_text_1, null);
4202
4203 big.setViewVisibility(R.id.notification_material_reply_text_2, View.GONE);
4204 big.setTextViewText(R.id.notification_material_reply_text_2, null);
4205 big.setViewVisibility(R.id.notification_material_reply_text_3, View.GONE);
4206 big.setTextViewText(R.id.notification_material_reply_text_3, null);
Adrian Roosf852a422016-06-03 13:33:43 -07004207
4208 big.setViewLayoutMarginBottomDimen(R.id.notification_action_list_margin_target, 0);
Christoph Studerfe718432014-09-01 18:21:18 +02004209 }
4210
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004211 private RemoteViews applyStandardTemplateWithActions(int layoutId) {
Adrian Roos70d7aa32017-01-11 15:39:06 -08004212 return applyStandardTemplateWithActions(layoutId, mParams.reset().fillTextsFrom(this));
Adrian Roos48d746a2016-04-12 14:57:28 -07004213 }
4214
Adrian Roos70d7aa32017-01-11 15:39:06 -08004215 private RemoteViews applyStandardTemplateWithActions(int layoutId,
4216 StandardTemplateParams p) {
4217 RemoteViews big = applyStandardTemplate(layoutId, p);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004218
Christoph Studerfe718432014-09-01 18:21:18 +02004219 resetStandardTemplateWithActions(big);
4220
Adrian Roose458aa82015-12-08 16:17:19 -08004221 boolean validRemoteInput = false;
4222
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004223 int N = mActions.size();
Adrian Roos487374f2017-01-11 15:48:14 -08004224 boolean emphazisedMode = mN.fullScreenIntent != null && !p.ambient;
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004225 big.setBoolean(R.id.actions, "setEmphasizedMode", emphazisedMode);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004226 if (N > 0) {
Adrian Roos7052de52016-03-03 15:53:34 -08004227 big.setViewVisibility(R.id.actions_container, View.VISIBLE);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004228 big.setViewVisibility(R.id.actions, View.VISIBLE);
Adrian Roos487374f2017-01-11 15:48:14 -08004229 if (p.ambient) {
4230 big.setInt(R.id.actions, "setBackgroundColor", Color.TRANSPARENT);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004231 } else if (isColorized()) {
4232 big.setInt(R.id.actions, "setBackgroundColor", getActionBarColor());
4233 } else {
4234 big.setInt(R.id.actions, "setBackgroundColor", mContext.getColor(
4235 R.color.notification_action_list));
Adrian Roos487374f2017-01-11 15:48:14 -08004236 }
Adrian Roosf852a422016-06-03 13:33:43 -07004237 big.setViewLayoutMarginBottomDimen(R.id.notification_action_list_margin_target,
4238 R.dimen.notification_action_list_height);
Daniel Sandler8680bf82012-05-15 16:52:52 -04004239 if (N>MAX_ACTION_BUTTONS) N=MAX_ACTION_BUTTONS;
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004240 for (int i=0; i<N; i++) {
Adrian Roose458aa82015-12-08 16:17:19 -08004241 Action action = mActions.get(i);
4242 validRemoteInput |= hasValidRemoteInput(action);
4243
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004244 final RemoteViews button = generateActionButton(action, emphazisedMode,
Adrian Roos487374f2017-01-11 15:48:14 -08004245 i % 2 != 0, p.ambient);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004246 big.addView(R.id.actions, button);
4247 }
Adrian Roos4c1fcc82016-03-31 14:39:39 -07004248 } else {
4249 big.setViewVisibility(R.id.actions_container, View.GONE);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004250 }
Adrian Roose458aa82015-12-08 16:17:19 -08004251
4252 CharSequence[] replyText = mN.extras.getCharSequenceArray(EXTRA_REMOTE_INPUT_HISTORY);
Adrian Roos487374f2017-01-11 15:48:14 -08004253 if (!p.ambient && validRemoteInput && replyText != null
Adrian Roose458aa82015-12-08 16:17:19 -08004254 && replyText.length > 0 && !TextUtils.isEmpty(replyText[0])) {
4255 big.setViewVisibility(R.id.notification_material_reply_container, View.VISIBLE);
4256 big.setTextViewText(R.id.notification_material_reply_text_1, replyText[0]);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004257 setTextViewColorSecondary(big, R.id.notification_material_reply_text_1);
Adrian Roose458aa82015-12-08 16:17:19 -08004258
4259 if (replyText.length > 1 && !TextUtils.isEmpty(replyText[1])) {
4260 big.setViewVisibility(R.id.notification_material_reply_text_2, View.VISIBLE);
4261 big.setTextViewText(R.id.notification_material_reply_text_2, replyText[1]);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004262 setTextViewColorSecondary(big, R.id.notification_material_reply_text_2);
Adrian Roose458aa82015-12-08 16:17:19 -08004263
4264 if (replyText.length > 2 && !TextUtils.isEmpty(replyText[2])) {
4265 big.setViewVisibility(
4266 R.id.notification_material_reply_text_3, View.VISIBLE);
4267 big.setTextViewText(R.id.notification_material_reply_text_3, replyText[2]);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004268 setTextViewColorSecondary(big, R.id.notification_material_reply_text_3);
Adrian Roose458aa82015-12-08 16:17:19 -08004269 }
4270 }
4271 }
4272
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004273 return big;
4274 }
4275
Adrian Roose458aa82015-12-08 16:17:19 -08004276 private boolean hasValidRemoteInput(Action action) {
4277 if (TextUtils.isEmpty(action.title) || action.actionIntent == null) {
4278 // Weird actions
4279 return false;
4280 }
4281
4282 RemoteInput[] remoteInputs = action.getRemoteInputs();
4283 if (remoteInputs == null) {
4284 return false;
4285 }
4286
4287 for (RemoteInput r : remoteInputs) {
4288 CharSequence[] choices = r.getChoices();
4289 if (r.getAllowFreeFormInput() || (choices != null && choices.length != 0)) {
4290 return true;
4291 }
4292 }
4293 return false;
4294 }
4295
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004296 /**
4297 * Construct a RemoteViews for the final 1U notification layout. In order:
4298 * 1. Custom contentView from the caller
4299 * 2. Style's proposed content view
4300 * 3. Standard template view
4301 */
Julia Reynolds3b848122016-02-26 10:45:32 -05004302 public RemoteViews createContentView() {
Selim Cinek7d1009b2017-01-25 15:28:28 -08004303 return createContentView(false /* increasedheight */ );
4304 }
4305
4306 /**
4307 * Construct a RemoteViews for the smaller content view.
4308 *
4309 * @param increasedHeight true if this layout be created with an increased height. Some
4310 * styles may support showing more then just that basic 1U size
4311 * and the system may decide to render important notifications
4312 * slightly bigger even when collapsed.
4313 *
4314 * @hide
4315 */
4316 public RemoteViews createContentView(boolean increasedHeight) {
Selim Cineka7679b62017-05-10 16:33:25 -07004317 if (mN.contentView != null && useExistingRemoteView()) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004318 return mN.contentView;
4319 } else if (mStyle != null) {
Selim Cinek7d1009b2017-01-25 15:28:28 -08004320 final RemoteViews styleView = mStyle.makeContentView(increasedHeight);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004321 if (styleView != null) {
4322 return styleView;
4323 }
Joe Onorato46439ce2010-11-19 13:56:21 -08004324 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004325 return applyStandardTemplate(getBaseLayoutResource());
Joe Onorato46439ce2010-11-19 13:56:21 -08004326 }
4327
Selim Cineka7679b62017-05-10 16:33:25 -07004328 private boolean useExistingRemoteView() {
4329 return mStyle == null || (!mStyle.displayCustomViewInline()
4330 && !mRebuildStyledRemoteViews);
4331 }
4332
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004333 /**
4334 * Construct a RemoteViews for the final big notification layout.
4335 */
Julia Reynolds3b848122016-02-26 10:45:32 -05004336 public RemoteViews createBigContentView() {
Selim Cinek850a8542015-11-11 11:48:36 -05004337 RemoteViews result = null;
Selim Cineka7679b62017-05-10 16:33:25 -07004338 if (mN.bigContentView != null && useExistingRemoteView()) {
Julia Reynolds089e3e42015-11-18 09:59:57 -05004339 return mN.bigContentView;
4340 } else if (mStyle != null) {
Selim Cinek850a8542015-11-11 11:48:36 -05004341 result = mStyle.makeBigContentView();
Selim Cinek90dcf6d2015-11-18 20:24:13 -08004342 hideLine1Text(result);
Selim Cinekcc10bfb2016-02-10 16:24:21 -08004343 } else if (mActions.size() != 0) {
4344 result = applyStandardTemplateWithActions(getBigBaseLayoutResource());
Selim Cinek850a8542015-11-11 11:48:36 -05004345 }
Selim Cinek6743c0b2017-01-18 18:24:01 -08004346 makeHeaderExpanded(result);
Selim Cinek850a8542015-11-11 11:48:36 -05004347 return result;
4348 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004349
Selim Cinekeaa29ca2015-11-23 13:51:13 -08004350 /**
Selim Cinek414ad332017-02-24 19:06:12 -08004351 * Construct a RemoteViews for the final notification header only. This will not be
4352 * colorized.
Selim Cinekeaa29ca2015-11-23 13:51:13 -08004353 *
Adrian Roos6f6e1592017-05-02 16:22:53 -07004354 * @param ambient if true, generate the header for the ambient display layout.
Selim Cinekeaa29ca2015-11-23 13:51:13 -08004355 * @hide
4356 */
Adrian Roos6f6e1592017-05-02 16:22:53 -07004357 public RemoteViews makeNotificationHeader(boolean ambient) {
Selim Cinek414ad332017-02-24 19:06:12 -08004358 Boolean colorized = (Boolean) mN.extras.get(EXTRA_COLORIZED);
4359 mN.extras.putBoolean(EXTRA_COLORIZED, false);
Selim Cinekeaa29ca2015-11-23 13:51:13 -08004360 RemoteViews header = new BuilderRemoteViews(mContext.getApplicationInfo(),
Adrian Roos6f6e1592017-05-02 16:22:53 -07004361 ambient ? R.layout.notification_template_ambient_header
4362 : R.layout.notification_template_header);
Selim Cinekeaa29ca2015-11-23 13:51:13 -08004363 resetNotificationHeader(header);
Adrian Roos6f6e1592017-05-02 16:22:53 -07004364 bindNotificationHeader(header, ambient);
Selim Cinek414ad332017-02-24 19:06:12 -08004365 if (colorized != null) {
4366 mN.extras.putBoolean(EXTRA_COLORIZED, colorized);
4367 } else {
4368 mN.extras.remove(EXTRA_COLORIZED);
4369 }
Selim Cinekeaa29ca2015-11-23 13:51:13 -08004370 return header;
4371 }
4372
Adrian Roos487374f2017-01-11 15:48:14 -08004373 /**
4374 * Construct a RemoteViews for the ambient version of the notification.
4375 *
4376 * @hide
4377 */
4378 public RemoteViews makeAmbientNotification() {
4379 RemoteViews ambient = applyStandardTemplateWithActions(
4380 R.layout.notification_template_material_ambient,
Adrian Roos0bc3f6a2017-03-06 11:54:05 -08004381 mParams.reset().ambient(true).fillTextsFrom(this).hasProgress(false));
Adrian Roos487374f2017-01-11 15:48:14 -08004382 return ambient;
4383 }
4384
Selim Cinek29603462015-11-17 19:04:39 -08004385 private void hideLine1Text(RemoteViews result) {
Selim Cinekcc10bfb2016-02-10 16:24:21 -08004386 if (result != null) {
4387 result.setViewVisibility(R.id.text_line_1, View.GONE);
4388 }
Selim Cinek29603462015-11-17 19:04:39 -08004389 }
4390
Selim Cinek6743c0b2017-01-18 18:24:01 -08004391 /**
4392 * Adapt the Notification header if this view is used as an expanded view.
4393 *
4394 * @hide
4395 */
4396 public static void makeHeaderExpanded(RemoteViews result) {
Selim Cinekcc10bfb2016-02-10 16:24:21 -08004397 if (result != null) {
4398 result.setBoolean(R.id.notification_header, "setExpanded", true);
4399 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004400 }
4401
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004402 /**
4403 * Construct a RemoteViews for the final heads-up notification layout.
Selim Cinek87ed69b2017-02-09 15:59:43 -08004404 *
4405 * @param increasedHeight true if this layout be created with an increased height. Some
4406 * styles may support showing more then just that basic 1U size
4407 * and the system may decide to render important notifications
4408 * slightly bigger even when collapsed.
4409 *
4410 * @hide
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004411 */
Selim Cinek87ed69b2017-02-09 15:59:43 -08004412 public RemoteViews createHeadsUpContentView(boolean increasedHeight) {
Selim Cineka7679b62017-05-10 16:33:25 -07004413 if (mN.headsUpContentView != null && useExistingRemoteView()) {
Julia Reynolds089e3e42015-11-18 09:59:57 -05004414 return mN.headsUpContentView;
4415 } else if (mStyle != null) {
Selim Cinek87ed69b2017-02-09 15:59:43 -08004416 final RemoteViews styleView = mStyle.makeHeadsUpContentView(increasedHeight);
4417 if (styleView != null) {
4418 return styleView;
4419 }
Julia Reynolds089e3e42015-11-18 09:59:57 -05004420 } else if (mActions.size() == 0) {
4421 return null;
4422 }
4423
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004424 return applyStandardTemplateWithActions(getBigBaseLayoutResource());
Chris Wren8fd39ec2014-02-27 17:43:26 -05004425 }
4426
Selim Cinek624c02db2015-12-14 21:00:02 -08004427 /**
Selim Cinek87ed69b2017-02-09 15:59:43 -08004428 * Construct a RemoteViews for the final heads-up notification layout.
4429 */
4430 public RemoteViews createHeadsUpContentView() {
4431 return createHeadsUpContentView(false /* useIncreasedHeight */);
4432 }
4433
4434 /**
Selim Cinek624c02db2015-12-14 21:00:02 -08004435 * Construct a RemoteViews for the display in public contexts like on the lockscreen.
4436 *
4437 * @hide
4438 */
4439 public RemoteViews makePublicContentView() {
Adrian Roos1a1ecfc2017-04-17 11:17:59 -07004440 return makePublicView(false /* ambient */);
4441 }
4442
4443 /**
4444 * Construct a RemoteViews for the display in public contexts like on the lockscreen.
4445 *
4446 * @hide
4447 */
4448 public RemoteViews makePublicAmbientNotification() {
4449 return makePublicView(true /* ambient */);
4450 }
4451
4452 private RemoteViews makePublicView(boolean ambient) {
Selim Cinek624c02db2015-12-14 21:00:02 -08004453 if (mN.publicVersion != null) {
4454 final Builder builder = recoverBuilder(mContext, mN.publicVersion);
Adrian Roos1a1ecfc2017-04-17 11:17:59 -07004455 return ambient ? builder.makeAmbientNotification() : builder.createContentView();
Selim Cinek624c02db2015-12-14 21:00:02 -08004456 }
4457 Bundle savedBundle = mN.extras;
4458 Style style = mStyle;
4459 mStyle = null;
4460 Icon largeIcon = mN.mLargeIcon;
4461 mN.mLargeIcon = null;
Selim Cinek279fa862016-06-14 10:57:25 -07004462 Bitmap largeIconLegacy = mN.largeIcon;
4463 mN.largeIcon = null;
Adrian Roosb19b06b2017-05-02 18:54:40 -07004464 ArrayList<Action> actions = mActions;
4465 mActions = new ArrayList<>();
Selim Cinek624c02db2015-12-14 21:00:02 -08004466 Bundle publicExtras = new Bundle();
4467 publicExtras.putBoolean(EXTRA_SHOW_WHEN,
4468 savedBundle.getBoolean(EXTRA_SHOW_WHEN));
4469 publicExtras.putBoolean(EXTRA_SHOW_CHRONOMETER,
4470 savedBundle.getBoolean(EXTRA_SHOW_CHRONOMETER));
Adrian Roos96b7e202016-05-17 13:50:38 -07004471 publicExtras.putBoolean(EXTRA_CHRONOMETER_COUNT_DOWN,
4472 savedBundle.getBoolean(EXTRA_CHRONOMETER_COUNT_DOWN));
Selim Cinek624c02db2015-12-14 21:00:02 -08004473 publicExtras.putCharSequence(EXTRA_TITLE,
Adrian Roos1a1ecfc2017-04-17 11:17:59 -07004474 mContext.getString(com.android.internal.R.string.notification_hidden_text));
Selim Cinek624c02db2015-12-14 21:00:02 -08004475 mN.extras = publicExtras;
Adrian Roos1a1ecfc2017-04-17 11:17:59 -07004476 final RemoteViews view = ambient ? makeAmbientNotification()
4477 : applyStandardTemplate(getBaseLayoutResource());
Selim Cinek624c02db2015-12-14 21:00:02 -08004478 mN.extras = savedBundle;
4479 mN.mLargeIcon = largeIcon;
Selim Cinek279fa862016-06-14 10:57:25 -07004480 mN.largeIcon = largeIconLegacy;
Adrian Roosb19b06b2017-05-02 18:54:40 -07004481 mActions = actions;
Selim Cinek624c02db2015-12-14 21:00:02 -08004482 mStyle = style;
Adrian Roos1a1ecfc2017-04-17 11:17:59 -07004483 return view;
Selim Cinek624c02db2015-12-14 21:00:02 -08004484 }
4485
Selim Cinek6743c0b2017-01-18 18:24:01 -08004486 /**
4487 * Construct a content view for the display when low - priority
4488 *
4489 * @param useRegularSubtext uses the normal subtext set if there is one available. Otherwise
4490 * a new subtext is created consisting of the content of the
4491 * notification.
4492 * @hide
4493 */
4494 public RemoteViews makeLowPriorityContentView(boolean useRegularSubtext) {
4495 int color = mN.color;
4496 mN.color = COLOR_DEFAULT;
4497 CharSequence summary = mN.extras.getCharSequence(EXTRA_SUB_TEXT);
4498 if (!useRegularSubtext || TextUtils.isEmpty(summary)) {
4499 CharSequence newSummary = createSummaryText();
4500 if (!TextUtils.isEmpty(newSummary)) {
4501 mN.extras.putCharSequence(EXTRA_SUB_TEXT, newSummary);
4502 }
4503 }
Selim Cinek875ba9b2017-02-13 16:20:17 -08004504
Adrian Roos6f6e1592017-05-02 16:22:53 -07004505 RemoteViews header = makeNotificationHeader(false /* ambient */);
Selim Cinek1b554392017-02-28 17:22:49 -08004506 header.setBoolean(R.id.notification_header, "setAcceptAllTouches", true);
Selim Cinek6743c0b2017-01-18 18:24:01 -08004507 if (summary != null) {
4508 mN.extras.putCharSequence(EXTRA_SUB_TEXT, summary);
4509 } else {
4510 mN.extras.remove(EXTRA_SUB_TEXT);
4511 }
4512 mN.color = color;
4513 return header;
4514 }
Selim Cinek624c02db2015-12-14 21:00:02 -08004515
Selim Cinek6743c0b2017-01-18 18:24:01 -08004516 private CharSequence createSummaryText() {
4517 CharSequence titleText = mN.extras.getCharSequence(Notification.EXTRA_TITLE);
4518 if (USE_ONLY_TITLE_IN_LOW_PRIORITY_SUMMARY) {
4519 return titleText;
4520 }
4521 SpannableStringBuilder summary = new SpannableStringBuilder();
4522 if (titleText == null) {
4523 titleText = mN.extras.getCharSequence(Notification.EXTRA_TITLE_BIG);
4524 }
4525 BidiFormatter bidi = BidiFormatter.getInstance();
4526 if (titleText != null) {
4527 summary.append(bidi.unicodeWrap(titleText));
4528 }
4529 CharSequence contentText = mN.extras.getCharSequence(Notification.EXTRA_TEXT);
4530 if (titleText != null && contentText != null) {
4531 summary.append(bidi.unicodeWrap(mContext.getText(
4532 R.string.notification_header_divider_symbol_with_spaces)));
4533 }
4534 if (contentText != null) {
4535 summary.append(bidi.unicodeWrap(contentText));
4536 }
4537 return summary;
4538 }
Chris Wren8fd39ec2014-02-27 17:43:26 -05004539
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004540 private RemoteViews generateActionButton(Action action, boolean emphazisedMode,
Adrian Roos487374f2017-01-11 15:48:14 -08004541 boolean oddAction, boolean ambient) {
Daniel Sandler8680bf82012-05-15 16:52:52 -04004542 final boolean tombstone = (action.actionIntent == null);
Selim Cinekf33b1112015-07-15 17:45:11 -07004543 RemoteViews button = new BuilderRemoteViews(mContext.getApplicationInfo(),
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004544 emphazisedMode ? getEmphasizedActionLayoutResource()
4545 : tombstone ? getActionTombstoneLayoutResource()
4546 : getActionLayoutResource());
Daniel Sandler8680bf82012-05-15 16:52:52 -04004547 if (!tombstone) {
Daniel Sandlere5518842012-05-10 16:20:40 -04004548 button.setOnClickPendingIntent(R.id.action0, action.actionIntent);
Daniel Sandlere5518842012-05-10 16:20:40 -04004549 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004550 button.setContentDescription(R.id.action0, action.title);
Adrian Roosfe84e1f2015-11-04 15:55:39 -08004551 if (action.mRemoteInputs != null) {
4552 button.setRemoteInputs(R.id.action0, action.mRemoteInputs);
4553 }
Selim Cinek7b9605b2017-01-19 17:36:00 -08004554 // TODO: handle emphasized mode / actions right
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004555 if (emphazisedMode) {
Selim Cinek981962e2016-07-20 20:41:58 -07004556 // change the background bgColor
Selim Cinek622c64a2017-04-17 17:10:05 -07004557 int bgColor;
4558 if (isColorized()) {
4559 bgColor = oddAction ? getActionBarColor() : getActionBarColorDeEmphasized();
4560 } else {
4561 bgColor = mContext.getColor(oddAction ? R.color.notification_action_list
4562 : R.color.notification_action_list_dark);
4563 }
Selim Cinek981962e2016-07-20 20:41:58 -07004564 button.setDrawableParameters(R.id.button_holder, true, -1, bgColor,
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004565 PorterDuff.Mode.SRC_ATOP, -1);
Selim Cinek981962e2016-07-20 20:41:58 -07004566 CharSequence title = action.title;
4567 ColorStateList[] outResultColor = null;
4568 if (isLegacy()) {
4569 title = clearColorSpans(title);
4570 } else {
4571 outResultColor = new ColorStateList[1];
4572 title = ensureColorSpanContrast(title, bgColor, outResultColor);
4573 }
4574 button.setTextViewText(R.id.action0, title);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004575 setTextViewColorPrimary(button, R.id.action0);
Selim Cinek981962e2016-07-20 20:41:58 -07004576 if (outResultColor != null && outResultColor[0] != null) {
4577 // We need to set the text color as well since changing a text to uppercase
4578 // clears its spans.
4579 button.setTextColor(R.id.action0, outResultColor[0]);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004580 } else if (mN.color != COLOR_DEFAULT && !isColorized()) {
Selim Cinek981962e2016-07-20 20:41:58 -07004581 button.setTextColor(R.id.action0,resolveContrastColor());
4582 }
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004583 } else {
Selim Cinek981962e2016-07-20 20:41:58 -07004584 button.setTextViewText(R.id.action0, processLegacyText(action.title));
Adrian Roos72171622017-01-27 10:32:06 -08004585 if (isColorized() && !ambient) {
Selim Cinek7b9605b2017-01-19 17:36:00 -08004586 setTextViewColorPrimary(button, R.id.action0);
4587 } else if (mN.color != COLOR_DEFAULT) {
Adrian Roos487374f2017-01-11 15:48:14 -08004588 button.setTextColor(R.id.action0,
4589 ambient ? resolveAmbientColor() : resolveContrastColor());
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004590 }
Adrian Roosfe84e1f2015-11-04 15:55:39 -08004591 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004592 return button;
4593 }
4594
Joe Onoratocb109a02011-01-18 17:57:41 -08004595 /**
Selim Cinek981962e2016-07-20 20:41:58 -07004596 * Clears all color spans of a text
4597 * @param charSequence the input text
4598 * @return the same text but without color spans
4599 */
4600 private CharSequence clearColorSpans(CharSequence charSequence) {
4601 if (charSequence instanceof Spanned) {
4602 Spanned ss = (Spanned) charSequence;
4603 Object[] spans = ss.getSpans(0, ss.length(), Object.class);
4604 SpannableStringBuilder builder = new SpannableStringBuilder(ss.toString());
4605 for (Object span : spans) {
4606 Object resultSpan = span;
4607 if (resultSpan instanceof CharacterStyle) {
4608 resultSpan = ((CharacterStyle) span).getUnderlying();
4609 }
4610 if (resultSpan instanceof TextAppearanceSpan) {
4611 TextAppearanceSpan originalSpan = (TextAppearanceSpan) resultSpan;
4612 if (originalSpan.getTextColor() != null) {
4613 resultSpan = new TextAppearanceSpan(
4614 originalSpan.getFamily(),
4615 originalSpan.getTextStyle(),
4616 originalSpan.getTextSize(),
4617 null,
4618 originalSpan.getLinkTextColor());
4619 }
4620 } else if (resultSpan instanceof ForegroundColorSpan
4621 || (resultSpan instanceof BackgroundColorSpan)) {
4622 continue;
4623 } else {
4624 resultSpan = span;
4625 }
4626 builder.setSpan(resultSpan, ss.getSpanStart(span), ss.getSpanEnd(span),
4627 ss.getSpanFlags(span));
4628 }
4629 return builder;
4630 }
4631 return charSequence;
4632 }
4633
4634 /**
4635 * Ensures contrast on color spans against a background color. also returns the color of the
4636 * text if a span was found that spans over the whole text.
4637 *
4638 * @param charSequence the charSequence on which the spans are
4639 * @param background the background color to ensure the contrast against
4640 * @param outResultColor an array in which a color will be returned as the first element if
4641 * there exists a full length color span.
4642 * @return the contrasted charSequence
4643 */
4644 private CharSequence ensureColorSpanContrast(CharSequence charSequence, int background,
4645 ColorStateList[] outResultColor) {
4646 if (charSequence instanceof Spanned) {
4647 Spanned ss = (Spanned) charSequence;
4648 Object[] spans = ss.getSpans(0, ss.length(), Object.class);
4649 SpannableStringBuilder builder = new SpannableStringBuilder(ss.toString());
4650 for (Object span : spans) {
4651 Object resultSpan = span;
4652 int spanStart = ss.getSpanStart(span);
4653 int spanEnd = ss.getSpanEnd(span);
4654 boolean fullLength = (spanEnd - spanStart) == charSequence.length();
4655 if (resultSpan instanceof CharacterStyle) {
4656 resultSpan = ((CharacterStyle) span).getUnderlying();
4657 }
4658 if (resultSpan instanceof TextAppearanceSpan) {
4659 TextAppearanceSpan originalSpan = (TextAppearanceSpan) resultSpan;
4660 ColorStateList textColor = originalSpan.getTextColor();
4661 if (textColor != null) {
4662 int[] colors = textColor.getColors();
4663 int[] newColors = new int[colors.length];
4664 for (int i = 0; i < newColors.length; i++) {
4665 newColors[i] = NotificationColorUtil.ensureLargeTextContrast(
4666 colors[i], background);
4667 }
4668 textColor = new ColorStateList(textColor.getStates().clone(),
4669 newColors);
4670 resultSpan = new TextAppearanceSpan(
4671 originalSpan.getFamily(),
4672 originalSpan.getTextStyle(),
4673 originalSpan.getTextSize(),
4674 textColor,
4675 originalSpan.getLinkTextColor());
4676 if (fullLength) {
4677 outResultColor[0] = new ColorStateList(
4678 textColor.getStates().clone(), newColors);
4679 }
4680 }
4681 } else if (resultSpan instanceof ForegroundColorSpan) {
4682 ForegroundColorSpan originalSpan = (ForegroundColorSpan) resultSpan;
4683 int foregroundColor = originalSpan.getForegroundColor();
4684 foregroundColor = NotificationColorUtil.ensureLargeTextContrast(
4685 foregroundColor, background);
4686 resultSpan = new ForegroundColorSpan(foregroundColor);
4687 if (fullLength) {
4688 outResultColor[0] = ColorStateList.valueOf(foregroundColor);
4689 }
4690 } else {
4691 resultSpan = span;
4692 }
4693
4694 builder.setSpan(resultSpan, spanStart, spanEnd, ss.getSpanFlags(span));
4695 }
4696 return builder;
4697 }
4698 return charSequence;
4699 }
4700
4701 /**
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004702 * @return Whether we are currently building a notification from a legacy (an app that
Alan Viverette3cb07a462014-06-06 14:19:53 -07004703 * doesn't create material notifications by itself) app.
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004704 */
4705 private boolean isLegacy() {
Selim Cinek7b9605b2017-01-19 17:36:00 -08004706 if (!mIsLegacyInitialized) {
4707 mIsLegacy = mContext.getApplicationInfo().targetSdkVersion
4708 < Build.VERSION_CODES.LOLLIPOP;
4709 mIsLegacyInitialized = true;
4710 }
4711 return mIsLegacy;
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004712 }
4713
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004714 private CharSequence processLegacyText(CharSequence charSequence) {
Adrian Roos0bc3f6a2017-03-06 11:54:05 -08004715 return processLegacyText(charSequence, false /* ambient */);
4716 }
4717
4718 private CharSequence processLegacyText(CharSequence charSequence, boolean ambient) {
4719 boolean isAlreadyLightText = isLegacy() || textColorsNeedInversion();
4720 boolean wantLightText = ambient;
4721 if (isAlreadyLightText != wantLightText) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004722 return getColorUtil().invertCharSequenceColors(charSequence);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004723 } else {
4724 return charSequence;
4725 }
4726 }
4727
Dan Sandler26e81cf2014-05-06 10:01:27 -04004728 /**
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004729 * Apply any necessariy colors to the small icon
Dan Sandler26e81cf2014-05-06 10:01:27 -04004730 */
Adrian Roos487374f2017-01-11 15:48:14 -08004731 private void processSmallIconColor(Icon smallIcon, RemoteViews contentView,
4732 boolean ambient) {
Selim Cinekea4bef72015-12-02 15:51:10 -08004733 boolean colorable = !isLegacy() || getColorUtil().isGrayscaleIcon(mContext, smallIcon);
Selim Cinek99104832017-01-25 14:47:33 -08004734 int color = ambient ? resolveAmbientColor() : getPrimaryHighlightColor();
Selim Cinekea4bef72015-12-02 15:51:10 -08004735 if (colorable) {
Adrian Roos487374f2017-01-11 15:48:14 -08004736 contentView.setDrawableParameters(R.id.icon, false, -1, color,
Jorim Jaggi92df1f22014-12-16 19:44:41 +01004737 PorterDuff.Mode.SRC_ATOP, -1);
Selim Cinekea4bef72015-12-02 15:51:10 -08004738
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004739 }
Selim Cinekea4bef72015-12-02 15:51:10 -08004740 contentView.setInt(R.id.notification_header, "setOriginalIconColor",
Adrian Roos487374f2017-01-11 15:48:14 -08004741 colorable ? color : NotificationHeaderView.NO_COLOR);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004742 }
4743
Dan Sandler26e81cf2014-05-06 10:01:27 -04004744 /**
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004745 * Make the largeIcon dark if it's a fake smallIcon (that is,
Dan Sandler26e81cf2014-05-06 10:01:27 -04004746 * if it's grayscale).
4747 */
4748 // TODO: also check bounds, transparency, that sort of thing.
Dan Sandlerd63f9322015-05-06 15:18:49 -04004749 private void processLargeLegacyIcon(Icon largeIcon, RemoteViews contentView) {
4750 if (largeIcon != null && isLegacy()
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004751 && getColorUtil().isGrayscaleIcon(mContext, largeIcon)) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004752 // resolve color will fall back to the default when legacy
Adrian Roos4ff3b122016-02-01 12:26:13 -08004753 contentView.setDrawableParameters(R.id.icon, false, -1, resolveContrastColor(),
Dan Sandler190d58d2014-05-15 09:33:39 -04004754 PorterDuff.Mode.SRC_ATOP, -1);
Jorim Jaggi92df1f22014-12-16 19:44:41 +01004755 }
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004756 }
4757
Julia Reynolds10ee1fc2015-11-09 11:04:55 -05004758 private void sanitizeColor() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004759 if (mN.color != COLOR_DEFAULT) {
4760 mN.color |= 0xFF000000; // no alpha for custom colors
Jorim Jaggi74419312014-06-10 20:57:21 +02004761 }
Jorim Jaggi74419312014-06-10 20:57:21 +02004762 }
4763
Adrian Roos4ff3b122016-02-01 12:26:13 -08004764 int resolveContrastColor() {
4765 if (mCachedContrastColorIsFor == mN.color && mCachedContrastColor != COLOR_INVALID) {
4766 return mCachedContrastColor;
Dan Sandler26e81cf2014-05-06 10:01:27 -04004767 }
Adrian Roos4ff3b122016-02-01 12:26:13 -08004768
Selim Cinekac5f0272017-05-02 16:05:41 -07004769 int color;
4770 int background = mBackgroundColorHint;
4771 if (mBackgroundColorHint == COLOR_INVALID) {
4772 background = mContext.getColor(
4773 com.android.internal.R.color.notification_material_background_color);
4774 }
4775 if (mN.color == COLOR_DEFAULT) {
4776 ensureColors();
4777 color = mSecondaryTextColor;
4778 } else {
4779 color = NotificationColorUtil.resolveContrastColor(mContext, mN.color,
4780 background);
4781 }
4782 if (Color.alpha(color) < 255) {
4783 // alpha doesn't go well for color filters, so let's blend it manually
4784 color = NotificationColorUtil.compositeColors(color, background);
4785 }
Adrian Roos4ff3b122016-02-01 12:26:13 -08004786 mCachedContrastColorIsFor = mN.color;
Selim Cinekac5f0272017-05-02 16:05:41 -07004787 return mCachedContrastColor = color;
Dan Sandler26e81cf2014-05-06 10:01:27 -04004788 }
4789
Adrian Roos487374f2017-01-11 15:48:14 -08004790 int resolveAmbientColor() {
4791 if (mCachedAmbientColorIsFor == mN.color && mCachedAmbientColorIsFor != COLOR_INVALID) {
4792 return mCachedAmbientColor;
4793 }
4794 final int contrasted = NotificationColorUtil.resolveAmbientColor(mContext, mN.color);
4795
4796 mCachedAmbientColorIsFor = mN.color;
4797 return mCachedAmbientColor = contrasted;
4798 }
4799
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004800 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004801 * Apply the unstyled operations and return a new {@link Notification} object.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04004802 * @hide
Joe Onoratocb109a02011-01-18 17:57:41 -08004803 */
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04004804 public Notification buildUnstyled() {
Daniel Sandlera0a938c2012-03-15 08:42:37 -04004805 if (mActions.size() > 0) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004806 mN.actions = new Action[mActions.size()];
4807 mActions.toArray(mN.actions);
Daniel Sandlera0a938c2012-03-15 08:42:37 -04004808 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004809 if (!mPersonList.isEmpty()) {
4810 mN.extras.putStringArray(EXTRA_PEOPLE,
4811 mPersonList.toArray(new String[mPersonList.size()]));
Dan Sandler0bf2ed82013-12-21 23:33:41 -06004812 }
Selim Cinek247fa012016-02-18 09:50:48 -08004813 if (mN.bigContentView != null || mN.contentView != null
4814 || mN.headsUpContentView != null) {
4815 mN.extras.putBoolean(EXTRA_CONTAINS_CUSTOM_VIEW, true);
4816 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004817 return mN;
Joe Onorato46439ce2010-11-19 13:56:21 -08004818 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004819
Julia Reynolds3b848122016-02-26 10:45:32 -05004820 /**
4821 * Creates a Builder from an existing notification so further changes can be made.
4822 * @param context The context for your application / activity.
4823 * @param n The notification to create a Builder from.
4824 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004825 public static Notification.Builder recoverBuilder(Context context, Notification n) {
Christoph Studer4600f9b2014-07-22 22:44:43 +02004826 // Re-create notification context so we can access app resources.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004827 ApplicationInfo applicationInfo = n.extras.getParcelable(
4828 EXTRA_BUILDER_APPLICATION_INFO);
Christoph Studer4600f9b2014-07-22 22:44:43 +02004829 Context builderContext;
Julia Reynoldsda303542015-11-23 14:00:20 -05004830 if (applicationInfo != null) {
4831 try {
4832 builderContext = context.createApplicationContext(applicationInfo,
4833 Context.CONTEXT_RESTRICTED);
4834 } catch (NameNotFoundException e) {
4835 Log.e(TAG, "ApplicationInfo " + applicationInfo + " not found");
4836 builderContext = context; // try with our context
4837 }
4838 } else {
4839 builderContext = context; // try with given context
Christoph Studer4600f9b2014-07-22 22:44:43 +02004840 }
4841
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004842 return new Builder(builderContext, n);
Christoph Studer4600f9b2014-07-22 22:44:43 +02004843 }
4844
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004845 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004846 * @deprecated Use {@link #build()} instead.
4847 */
4848 @Deprecated
4849 public Notification getNotification() {
4850 return build();
4851 }
4852
4853 /**
4854 * Combine all of the options that have been set and return a new {@link Notification}
4855 * object.
4856 */
4857 public Notification build() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004858 // first, add any extras from the calling code
4859 if (mUserExtras != null) {
4860 mN.extras = getAllExtras();
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07004861 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004862
Selim Cinekb85f36fd2016-04-20 18:46:36 -07004863 mN.creationTime = System.currentTimeMillis();
4864
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004865 // lazy stuff from mContext; see comment in Builder(Context, Notification)
Julia Reynoldsda303542015-11-23 14:00:20 -05004866 Notification.addFieldsFromContext(mContext, mN);
Christoph Studer943aa672014-08-03 20:31:16 +02004867
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004868 buildUnstyled();
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004869
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004870 if (mStyle != null) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004871 mStyle.buildStyled(mN);
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004872 }
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004873
Adrian Roos5081c0d2016-02-26 16:04:19 -08004874 if (mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N
Selim Cineka7679b62017-05-10 16:33:25 -07004875 && (useExistingRemoteView())) {
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004876 if (mN.contentView == null) {
Julia Reynolds3b848122016-02-26 10:45:32 -05004877 mN.contentView = createContentView();
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004878 mN.extras.putInt(EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT,
4879 mN.contentView.getSequenceNumber());
4880 }
4881 if (mN.bigContentView == null) {
Julia Reynolds3b848122016-02-26 10:45:32 -05004882 mN.bigContentView = createBigContentView();
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004883 if (mN.bigContentView != null) {
4884 mN.extras.putInt(EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT,
4885 mN.bigContentView.getSequenceNumber());
4886 }
4887 }
4888 if (mN.headsUpContentView == null) {
Julia Reynolds3b848122016-02-26 10:45:32 -05004889 mN.headsUpContentView = createHeadsUpContentView();
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004890 if (mN.headsUpContentView != null) {
4891 mN.extras.putInt(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT,
4892 mN.headsUpContentView.getSequenceNumber());
4893 }
4894 }
4895 }
4896
Julia Reynolds4c0c2022016-02-02 15:11:59 -05004897 if ((mN.defaults & DEFAULT_LIGHTS) != 0) {
4898 mN.flags |= FLAG_SHOW_LIGHTS;
4899 }
4900
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004901 return mN;
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004902 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05004903
4904 /**
4905 * Apply this Builder to an existing {@link Notification} object.
4906 *
4907 * @hide
4908 */
4909 public Notification buildInto(Notification n) {
Daniel Sandler1a497d32013-04-18 14:52:45 -04004910 build().cloneInto(n, true);
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05004911 return n;
4912 }
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004913
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004914 /**
Adrian Roos184bfe022016-03-03 13:41:44 -08004915 * Removes RemoteViews that were created for compatibility from {@param n}, if they did not
4916 * change.
4917 *
4918 * @return {@param n}, if no stripping is needed, otherwise a stripped clone of {@param n}.
4919 *
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004920 * @hide
4921 */
Adrian Roos184bfe022016-03-03 13:41:44 -08004922 public static Notification maybeCloneStrippedForDelivery(Notification n) {
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004923 String templateClass = n.extras.getString(EXTRA_TEMPLATE);
Adrian Roos184bfe022016-03-03 13:41:44 -08004924
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004925 // Only strip views for known Styles because we won't know how to
4926 // re-create them otherwise.
Adrian Roos184bfe022016-03-03 13:41:44 -08004927 if (!TextUtils.isEmpty(templateClass)
4928 && getNotificationStyleClass(templateClass) == null) {
4929 return n;
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004930 }
Adrian Roos184bfe022016-03-03 13:41:44 -08004931
4932 // Only strip unmodified BuilderRemoteViews.
4933 boolean stripContentView = n.contentView instanceof BuilderRemoteViews &&
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004934 n.extras.getInt(EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT, -1) ==
Adrian Roos184bfe022016-03-03 13:41:44 -08004935 n.contentView.getSequenceNumber();
4936 boolean stripBigContentView = n.bigContentView instanceof BuilderRemoteViews &&
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004937 n.extras.getInt(EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT, -1) ==
Adrian Roos184bfe022016-03-03 13:41:44 -08004938 n.bigContentView.getSequenceNumber();
4939 boolean stripHeadsUpContentView = n.headsUpContentView instanceof BuilderRemoteViews &&
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004940 n.extras.getInt(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT, -1) ==
Adrian Roos184bfe022016-03-03 13:41:44 -08004941 n.headsUpContentView.getSequenceNumber();
4942
4943 // Nothing to do here, no need to clone.
4944 if (!stripContentView && !stripBigContentView && !stripHeadsUpContentView) {
4945 return n;
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004946 }
Adrian Roos184bfe022016-03-03 13:41:44 -08004947
4948 Notification clone = n.clone();
4949 if (stripContentView) {
4950 clone.contentView = null;
4951 clone.extras.remove(EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT);
4952 }
4953 if (stripBigContentView) {
4954 clone.bigContentView = null;
4955 clone.extras.remove(EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT);
4956 }
4957 if (stripHeadsUpContentView) {
4958 clone.headsUpContentView = null;
4959 clone.extras.remove(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT);
4960 }
4961 return clone;
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004962 }
4963
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004964 private int getBaseLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07004965 return R.layout.notification_template_material_base;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004966 }
4967
4968 private int getBigBaseLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07004969 return R.layout.notification_template_material_big_base;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004970 }
4971
4972 private int getBigPictureLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07004973 return R.layout.notification_template_material_big_picture;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004974 }
4975
4976 private int getBigTextLayoutResource() {
Jorim Jaggi445d3c02014-08-19 22:33:42 +02004977 return R.layout.notification_template_material_big_text;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004978 }
4979
4980 private int getInboxLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07004981 return R.layout.notification_template_material_inbox;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004982 }
4983
Adrian Roosc1a80b02016-04-05 14:54:55 -07004984 private int getMessagingLayoutResource() {
4985 return R.layout.notification_template_material_messaging;
4986 }
4987
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004988 private int getActionLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07004989 return R.layout.notification_material_action;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004990 }
4991
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004992 private int getEmphasizedActionLayoutResource() {
4993 return R.layout.notification_material_action_emphasized;
4994 }
4995
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004996 private int getActionTombstoneLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07004997 return R.layout.notification_material_action_tombstone;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004998 }
Selim Cinek7b9605b2017-01-19 17:36:00 -08004999
5000 private int getBackgroundColor() {
5001 if (isColorized()) {
Selim Cinek5fb73f82017-04-20 16:55:38 -07005002 return mBackgroundColor != COLOR_INVALID ? mBackgroundColor : mN.color;
Selim Cinek7b9605b2017-01-19 17:36:00 -08005003 } else {
Selim Cinekac5f0272017-05-02 16:05:41 -07005004 return mBackgroundColorHint != COLOR_INVALID ? mBackgroundColorHint
5005 : COLOR_DEFAULT;
Selim Cinek7b9605b2017-01-19 17:36:00 -08005006 }
5007 }
5008
5009 private boolean isColorized() {
5010 return mN.isColorized();
5011 }
Selim Cinek99104832017-01-25 14:47:33 -08005012
5013 private boolean textColorsNeedInversion() {
5014 if (mStyle == null || !MediaStyle.class.equals(mStyle.getClass())) {
5015 return false;
5016 }
5017 int targetSdkVersion = mContext.getApplicationInfo().targetSdkVersion;
5018 return targetSdkVersion > Build.VERSION_CODES.M
5019 && targetSdkVersion < Build.VERSION_CODES.O;
5020 }
Selim Cinek5fb73f82017-04-20 16:55:38 -07005021
5022 /**
5023 * Set a color palette to be used as the background and textColors
5024 *
5025 * @param backgroundColor the color to be used as the background
5026 * @param foregroundColor the color to be used as the foreground
5027 *
5028 * @hide
5029 */
5030 public void setColorPalette(int backgroundColor, int foregroundColor) {
5031 mBackgroundColor = backgroundColor;
5032 mForegroundColor = foregroundColor;
5033 mTextColorsAreForBackground = COLOR_INVALID;
5034 ensureColors();
5035 }
Selim Cinekac5f0272017-05-02 16:05:41 -07005036
5037 /**
5038 * Sets the background color for this notification to be a different one then the default.
5039 * This is mainly used to calculate contrast and won't necessarily be applied to the
5040 * background.
5041 *
5042 * @hide
5043 */
5044 public void setBackgroundColorHint(int backgroundColor) {
5045 mBackgroundColorHint = backgroundColor;
5046 }
Selim Cineka7679b62017-05-10 16:33:25 -07005047
5048
5049 /**
5050 * Forces all styled remoteViews to be built from scratch and not use any cached
5051 * RemoteViews.
5052 * This is needed for legacy apps that are baking in their remoteviews into the
5053 * notification.
5054 *
5055 * @hide
5056 */
5057 public void setRebuildStyledRemoteViews(boolean rebuild) {
5058 mRebuildStyledRemoteViews = rebuild;
5059 }
Selim Cinek7b9605b2017-01-19 17:36:00 -08005060 }
5061
5062 /**
Selim Cinek22714f12017-04-13 16:23:53 -07005063 * @return whether this notification is a foreground service notification
Selim Cinek7b9605b2017-01-19 17:36:00 -08005064 */
Selim Cinek22714f12017-04-13 16:23:53 -07005065 private boolean isForegroundService() {
5066 return (flags & Notification.FLAG_FOREGROUND_SERVICE) != 0;
Selim Cinek7b9605b2017-01-19 17:36:00 -08005067 }
5068
5069 /**
Selim Cinek99104832017-01-25 14:47:33 -08005070 * @return whether this notification has a media session attached
5071 * @hide
5072 */
5073 public boolean hasMediaSession() {
5074 return extras.getParcelable(Notification.EXTRA_MEDIA_SESSION) != null;
5075 }
5076
5077 /**
Selim Cinek7d1009b2017-01-25 15:28:28 -08005078 * @return the style class of this notification
5079 * @hide
5080 */
5081 public Class<? extends Notification.Style> getNotificationStyle() {
5082 String templateClass = extras.getString(Notification.EXTRA_TEMPLATE);
5083
5084 if (!TextUtils.isEmpty(templateClass)) {
5085 return Notification.getNotificationStyleClass(templateClass);
5086 }
5087 return null;
5088 }
5089
5090 /**
Selim Cinek22714f12017-04-13 16:23:53 -07005091 * @return true if this notification is colorized.
Selim Cinek7b9605b2017-01-19 17:36:00 -08005092 *
5093 * @hide
5094 */
5095 public boolean isColorized() {
Selim Cinek5fb73f82017-04-20 16:55:38 -07005096 if (isColorizedMedia()) {
5097 return true;
5098 }
5099 return extras.getBoolean(EXTRA_COLORIZED) && isForegroundService();
5100 }
5101
5102 /**
5103 * @return true if this notification is colorized and it is a media notification
5104 *
5105 * @hide
5106 */
5107 public boolean isColorizedMedia() {
Selim Cinek99104832017-01-25 14:47:33 -08005108 Class<? extends Style> style = getNotificationStyle();
5109 if (MediaStyle.class.equals(style)) {
5110 Boolean colorized = (Boolean) extras.get(EXTRA_COLORIZED);
5111 if ((colorized == null || colorized) && hasMediaSession()) {
5112 return true;
5113 }
5114 } else if (DecoratedMediaCustomViewStyle.class.equals(style)) {
5115 if (extras.getBoolean(EXTRA_COLORIZED) && hasMediaSession()) {
5116 return true;
5117 }
5118 }
Selim Cinek5fb73f82017-04-20 16:55:38 -07005119 return false;
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005120 }
5121
Selim Cinek0847acd2017-04-24 19:48:29 -07005122
5123 /**
5124 * @return true if this is a media notification
5125 *
5126 * @hide
5127 */
5128 public boolean isMediaNotification() {
5129 Class<? extends Style> style = getNotificationStyle();
5130 if (MediaStyle.class.equals(style)) {
5131 return true;
5132 } else if (DecoratedMediaCustomViewStyle.class.equals(style)) {
5133 return true;
5134 }
5135 return false;
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005136 }
5137
Selim Cinek279fa862016-06-14 10:57:25 -07005138 private boolean hasLargeIcon() {
5139 return mLargeIcon != null || largeIcon != null;
5140 }
5141
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005142 /**
Selim Cinekc2c0b042016-05-18 17:13:46 -07005143 * @return true if the notification will show the time; false otherwise
Selim Cinekb85f36fd2016-04-20 18:46:36 -07005144 * @hide
5145 */
Selim Cinekc2c0b042016-05-18 17:13:46 -07005146 public boolean showsTime() {
Selim Cinekb85f36fd2016-04-20 18:46:36 -07005147 return when != 0 && extras.getBoolean(EXTRA_SHOW_WHEN);
5148 }
5149
5150 /**
Selim Cinekc2c0b042016-05-18 17:13:46 -07005151 * @return true if the notification will show a chronometer; false otherwise
5152 * @hide
5153 */
5154 public boolean showsChronometer() {
5155 return when != 0 && extras.getBoolean(EXTRA_SHOW_CHRONOMETER);
5156 }
5157
5158 /**
Julia Reynolds4a02afb2016-12-13 13:39:52 -05005159 * @hide
5160 */
5161 @SystemApi
5162 public static Class<? extends Style> getNotificationStyleClass(String templateClass) {
5163 Class<? extends Style>[] classes = new Class[] {
5164 BigTextStyle.class, BigPictureStyle.class, InboxStyle.class, MediaStyle.class,
5165 DecoratedCustomViewStyle.class, DecoratedMediaCustomViewStyle.class,
5166 MessagingStyle.class };
5167 for (Class<? extends Style> innerClass : classes) {
5168 if (templateClass.equals(innerClass.getName())) {
5169 return innerClass;
5170 }
5171 }
5172 return null;
5173 }
5174
5175 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005176 * An object that can apply a rich notification style to a {@link Notification.Builder}
5177 * object.
5178 */
Griff Hazendfcb0802014-02-11 12:00:00 -08005179 public static abstract class Style {
Chris Wrend6297db2012-05-03 16:20:13 -04005180 private CharSequence mBigContentTitle;
Jorim Jaggi457a10d2014-09-08 16:18:23 +02005181
5182 /**
5183 * @hide
5184 */
5185 protected CharSequence mSummaryText = null;
5186
5187 /**
5188 * @hide
5189 */
5190 protected boolean mSummaryTextSet = false;
Chris Wrend6297db2012-05-03 16:20:13 -04005191
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005192 protected Builder mBuilder;
5193
Chris Wrend6297db2012-05-03 16:20:13 -04005194 /**
5195 * Overrides ContentTitle in the big form of the template.
5196 * This defaults to the value passed to setContentTitle().
5197 */
5198 protected void internalSetBigContentTitle(CharSequence title) {
5199 mBigContentTitle = title;
5200 }
5201
5202 /**
5203 * Set the first line of text after the detail section in the big form of the template.
5204 */
5205 protected void internalSetSummaryText(CharSequence cs) {
5206 mSummaryText = cs;
Daniel Sandler619738c2012-06-07 16:33:08 -04005207 mSummaryTextSet = true;
Chris Wrend6297db2012-05-03 16:20:13 -04005208 }
5209
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005210 public void setBuilder(Builder builder) {
5211 if (mBuilder != builder) {
5212 mBuilder = builder;
Daniel Sandlerc08dea22012-06-28 08:35:24 -07005213 if (mBuilder != null) {
5214 mBuilder.setStyle(this);
5215 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005216 }
5217 }
5218
Chris Wrend6297db2012-05-03 16:20:13 -04005219 protected void checkBuilder() {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005220 if (mBuilder == null) {
5221 throw new IllegalArgumentException("Style requires a valid Builder object");
5222 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005223 }
Chris Wrend6297db2012-05-03 16:20:13 -04005224
5225 protected RemoteViews getStandardView(int layoutId) {
5226 checkBuilder();
5227
Christoph Studer4600f9b2014-07-22 22:44:43 +02005228 // Nasty.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005229 CharSequence oldBuilderContentTitle =
5230 mBuilder.getAllExtras().getCharSequence(EXTRA_TITLE);
Chris Wrend6297db2012-05-03 16:20:13 -04005231 if (mBigContentTitle != null) {
5232 mBuilder.setContentTitle(mBigContentTitle);
5233 }
5234
Chris Wrend6297db2012-05-03 16:20:13 -04005235 RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(layoutId);
5236
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005237 mBuilder.getAllExtras().putCharSequence(EXTRA_TITLE, oldBuilderContentTitle);
Christoph Studer4600f9b2014-07-22 22:44:43 +02005238
Chris Wrend6297db2012-05-03 16:20:13 -04005239 if (mBigContentTitle != null && mBigContentTitle.equals("")) {
5240 contentView.setViewVisibility(R.id.line1, View.GONE);
Chris Wren67dc9a02012-05-16 01:03:20 -04005241 } else {
5242 contentView.setViewVisibility(R.id.line1, View.VISIBLE);
Chris Wrend6297db2012-05-03 16:20:13 -04005243 }
5244
Chris Wrend6297db2012-05-03 16:20:13 -04005245 return contentView;
5246 }
5247
Daniel Sandlerf45564e2013-04-15 15:05:08 -04005248 /**
Selim Cinek7d1009b2017-01-25 15:28:28 -08005249 * Construct a Style-specific RemoteViews for the collapsed notification layout.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005250 * The default implementation has nothing additional to add.
Selim Cinek7d1009b2017-01-25 15:28:28 -08005251 *
5252 * @param increasedHeight true if this layout be created with an increased height.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005253 * @hide
5254 */
Selim Cinek7d1009b2017-01-25 15:28:28 -08005255 public RemoteViews makeContentView(boolean increasedHeight) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005256 return null;
5257 }
5258
5259 /**
5260 * Construct a Style-specific RemoteViews for the final big notification layout.
5261 * @hide
5262 */
5263 public RemoteViews makeBigContentView() {
5264 return null;
5265 }
5266
5267 /**
5268 * Construct a Style-specific RemoteViews for the final HUN layout.
Selim Cinek87ed69b2017-02-09 15:59:43 -08005269 *
5270 * @param increasedHeight true if this layout be created with an increased height.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005271 * @hide
5272 */
Selim Cinek87ed69b2017-02-09 15:59:43 -08005273 public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005274 return null;
5275 }
5276
5277 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005278 * Apply any style-specific extras to this notification before shipping it out.
Daniel Sandlerf45564e2013-04-15 15:05:08 -04005279 * @hide
5280 */
5281 public void addExtras(Bundle extras) {
5282 if (mSummaryTextSet) {
5283 extras.putCharSequence(EXTRA_SUMMARY_TEXT, mSummaryText);
5284 }
5285 if (mBigContentTitle != null) {
5286 extras.putCharSequence(EXTRA_TITLE_BIG, mBigContentTitle);
5287 }
Chris Wren91ad5632013-06-05 15:05:57 -04005288 extras.putString(EXTRA_TEMPLATE, this.getClass().getName());
Daniel Sandlerf45564e2013-04-15 15:05:08 -04005289 }
5290
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04005291 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005292 * Reconstruct the internal state of this Style object from extras.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04005293 * @hide
5294 */
Christoph Studer4600f9b2014-07-22 22:44:43 +02005295 protected void restoreFromExtras(Bundle extras) {
5296 if (extras.containsKey(EXTRA_SUMMARY_TEXT)) {
5297 mSummaryText = extras.getCharSequence(EXTRA_SUMMARY_TEXT);
5298 mSummaryTextSet = true;
5299 }
5300 if (extras.containsKey(EXTRA_TITLE_BIG)) {
5301 mBigContentTitle = extras.getCharSequence(EXTRA_TITLE_BIG);
5302 }
5303 }
5304
5305
5306 /**
5307 * @hide
5308 */
5309 public Notification buildStyled(Notification wip) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005310 addExtras(wip.extras);
Christoph Studer4600f9b2014-07-22 22:44:43 +02005311 return wip;
5312 }
5313
Daniel Sandler0ec46202015-06-24 01:27:05 -04005314 /**
5315 * @hide
5316 */
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07005317 public void purgeResources() {}
5318
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04005319 /**
5320 * Calls {@link android.app.Notification.Builder#build()} on the Builder this Style is
5321 * attached to.
5322 *
5323 * @return the fully constructed Notification.
5324 */
5325 public Notification build() {
5326 checkBuilder();
5327 return mBuilder.build();
5328 }
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02005329
5330 /**
5331 * @hide
5332 * @return true if the style positions the progress bar on the second line; false if the
5333 * style hides the progress bar
5334 */
5335 protected boolean hasProgress() {
5336 return true;
5337 }
Selim Cinek03d0d652015-11-13 13:18:09 -05005338
5339 /**
5340 * @hide
5341 * @return Whether we should put the summary be put into the notification header
5342 */
5343 public boolean hasSummaryInHeader() {
5344 return true;
5345 }
Selim Cinek593610c2016-02-16 18:42:57 -08005346
5347 /**
5348 * @hide
5349 * @return Whether custom content views are displayed inline in the style
5350 */
5351 public boolean displayCustomViewInline() {
5352 return false;
5353 }
Joe Onorato46439ce2010-11-19 13:56:21 -08005354 }
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005355
5356 /**
Daniel Sandler4dfbe832012-04-11 14:51:46 -04005357 * Helper class for generating large-format notifications that include a large image attachment.
Joe Malin8d40d042012-11-05 11:36:40 -08005358 *
Robert Ly91c5ce32014-06-08 15:37:00 -07005359 * Here's how you'd set the <code>BigPictureStyle</code> on a notification:
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005360 * <pre class="prettyprint">
Robert Ly91c5ce32014-06-08 15:37:00 -07005361 * Notification notif = new Notification.Builder(mContext)
5362 * .setContentTitle(&quot;New photo from &quot; + sender.toString())
5363 * .setContentText(subject)
5364 * .setSmallIcon(R.drawable.new_post)
5365 * .setLargeIcon(aBitmap)
5366 * .setStyle(new Notification.BigPictureStyle()
5367 * .bigPicture(aBigBitmap))
5368 * .build();
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005369 * </pre>
Joe Malin8d40d042012-11-05 11:36:40 -08005370 *
Daniel Sandler4dfbe832012-04-11 14:51:46 -04005371 * @see Notification#bigContentView
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005372 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005373 public static class BigPictureStyle extends Style {
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005374 private Bitmap mPicture;
Dan Sandlerd63f9322015-05-06 15:18:49 -04005375 private Icon mBigLargeIcon;
Chris Wren3745a3d2012-05-22 15:11:52 -04005376 private boolean mBigLargeIconSet = false;
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005377
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005378 public BigPictureStyle() {
5379 }
5380
Adrian Roosf5faf9d2016-05-23 13:56:15 -07005381 /**
5382 * @deprecated use {@code BigPictureStyle()}.
5383 */
5384 @Deprecated
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005385 public BigPictureStyle(Builder builder) {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005386 setBuilder(builder);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005387 }
5388
Chris Wrend6297db2012-05-03 16:20:13 -04005389 /**
5390 * Overrides ContentTitle in the big form of the template.
5391 * This defaults to the value passed to setContentTitle().
5392 */
5393 public BigPictureStyle setBigContentTitle(CharSequence title) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04005394 internalSetBigContentTitle(safeCharSequence(title));
Chris Wrend6297db2012-05-03 16:20:13 -04005395 return this;
5396 }
5397
5398 /**
5399 * Set the first line of text after the detail section in the big form of the template.
5400 */
5401 public BigPictureStyle setSummaryText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04005402 internalSetSummaryText(safeCharSequence(cs));
Chris Wrend6297db2012-05-03 16:20:13 -04005403 return this;
5404 }
5405
Chris Wren0bd664d2012-08-01 13:56:56 -04005406 /**
5407 * Provide the bitmap to be used as the payload for the BigPicture notification.
5408 */
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005409 public BigPictureStyle bigPicture(Bitmap b) {
5410 mPicture = b;
5411 return this;
5412 }
5413
Chris Wren3745a3d2012-05-22 15:11:52 -04005414 /**
Chris Wren3745a3d2012-05-22 15:11:52 -04005415 * Override the large icon when the big notification is shown.
5416 */
5417 public BigPictureStyle bigLargeIcon(Bitmap b) {
Dan Sandlerd63f9322015-05-06 15:18:49 -04005418 return bigLargeIcon(b != null ? Icon.createWithBitmap(b) : null);
5419 }
5420
5421 /**
5422 * Override the large icon when the big notification is shown.
5423 */
5424 public BigPictureStyle bigLargeIcon(Icon icon) {
Chris Wren3745a3d2012-05-22 15:11:52 -04005425 mBigLargeIconSet = true;
Dan Sandlerd63f9322015-05-06 15:18:49 -04005426 mBigLargeIcon = icon;
Chris Wren3745a3d2012-05-22 15:11:52 -04005427 return this;
5428 }
5429
Riley Andrews0394a0c2015-11-03 23:36:52 -08005430 /** @hide */
5431 public static final int MIN_ASHMEM_BITMAP_SIZE = 128 * (1 << 10);
5432
Daniel Sandler0ec46202015-06-24 01:27:05 -04005433 /**
5434 * @hide
5435 */
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07005436 @Override
5437 public void purgeResources() {
5438 super.purgeResources();
Riley Andrews8cee7c12015-11-01 23:36:04 -08005439 if (mPicture != null &&
5440 mPicture.isMutable() &&
Riley Andrews0394a0c2015-11-03 23:36:52 -08005441 mPicture.getAllocationByteCount() >= MIN_ASHMEM_BITMAP_SIZE) {
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07005442 mPicture = mPicture.createAshmemBitmap();
5443 }
5444 if (mBigLargeIcon != null) {
5445 mBigLargeIcon.convertToAshmem();
5446 }
5447 }
Christoph Studer5c510ee2014-12-15 16:32:27 +01005448
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005449 /**
5450 * @hide
5451 */
5452 public RemoteViews makeBigContentView() {
5453 // Replace mN.mLargeIcon with mBigLargeIcon if mBigLargeIconSet
Christoph Studer5c510ee2014-12-15 16:32:27 +01005454 // This covers the following cases:
5455 // 1. mBigLargeIconSet -> mBigLargeIcon (null or non-null) applies, overrides
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005456 // mN.mLargeIcon
5457 // 2. !mBigLargeIconSet -> mN.mLargeIcon applies
Dan Sandlerd63f9322015-05-06 15:18:49 -04005458 Icon oldLargeIcon = null;
Selim Cineke99acb22016-08-04 12:55:48 -07005459 Bitmap largeIconLegacy = null;
Christoph Studer5c510ee2014-12-15 16:32:27 +01005460 if (mBigLargeIconSet) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005461 oldLargeIcon = mBuilder.mN.mLargeIcon;
5462 mBuilder.mN.mLargeIcon = mBigLargeIcon;
Selim Cineke99acb22016-08-04 12:55:48 -07005463 // The legacy largeIcon might not allow us to clear the image, as it's taken in
5464 // replacement if the other one is null. Because we're restoring these legacy icons
5465 // for old listeners, this is in general non-null.
5466 largeIconLegacy = mBuilder.mN.largeIcon;
5467 mBuilder.mN.largeIcon = null;
Christoph Studer5c510ee2014-12-15 16:32:27 +01005468 }
5469
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01005470 RemoteViews contentView = getStandardView(mBuilder.getBigPictureLayoutResource());
Selim Cinek03d0d652015-11-13 13:18:09 -05005471 if (mSummaryTextSet) {
5472 contentView.setTextViewText(R.id.text, mBuilder.processLegacyText(mSummaryText));
Selim Cinek7b9605b2017-01-19 17:36:00 -08005473 mBuilder.setTextViewColorSecondary(contentView, R.id.text);
Selim Cinekc848c3a2016-01-13 15:27:30 -08005474 contentView.setViewVisibility(R.id.text, View.VISIBLE);
Selim Cinek03d0d652015-11-13 13:18:09 -05005475 }
Selim Cinek279fa862016-06-14 10:57:25 -07005476 mBuilder.setContentMinHeight(contentView, mBuilder.mN.hasLargeIcon());
Selim Cinek53e64a42015-11-16 10:40:56 -08005477
Christoph Studer5c510ee2014-12-15 16:32:27 +01005478 if (mBigLargeIconSet) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005479 mBuilder.mN.mLargeIcon = oldLargeIcon;
Selim Cineke99acb22016-08-04 12:55:48 -07005480 mBuilder.mN.largeIcon = largeIconLegacy;
Christoph Studer5c510ee2014-12-15 16:32:27 +01005481 }
5482
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005483 contentView.setImageViewBitmap(R.id.big_picture, mPicture);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005484 return contentView;
5485 }
5486
Daniel Sandlerf45564e2013-04-15 15:05:08 -04005487 /**
5488 * @hide
5489 */
5490 public void addExtras(Bundle extras) {
5491 super.addExtras(extras);
5492
5493 if (mBigLargeIconSet) {
5494 extras.putParcelable(EXTRA_LARGE_ICON_BIG, mBigLargeIcon);
5495 }
5496 extras.putParcelable(EXTRA_PICTURE, mPicture);
5497 }
5498
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04005499 /**
5500 * @hide
5501 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005502 @Override
Christoph Studer4600f9b2014-07-22 22:44:43 +02005503 protected void restoreFromExtras(Bundle extras) {
5504 super.restoreFromExtras(extras);
5505
5506 if (extras.containsKey(EXTRA_LARGE_ICON_BIG)) {
Christoph Studer5c510ee2014-12-15 16:32:27 +01005507 mBigLargeIconSet = true;
Christoph Studer4600f9b2014-07-22 22:44:43 +02005508 mBigLargeIcon = extras.getParcelable(EXTRA_LARGE_ICON_BIG);
Chris Wren3745a3d2012-05-22 15:11:52 -04005509 }
Christoph Studer4600f9b2014-07-22 22:44:43 +02005510 mPicture = extras.getParcelable(EXTRA_PICTURE);
5511 }
Selim Cinek03d0d652015-11-13 13:18:09 -05005512
5513 /**
5514 * @hide
5515 */
5516 @Override
5517 public boolean hasSummaryInHeader() {
5518 return false;
5519 }
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005520 }
5521
5522 /**
Daniel Sandler4dfbe832012-04-11 14:51:46 -04005523 * Helper class for generating large-format notifications that include a lot of text.
Joe Malin8d40d042012-11-05 11:36:40 -08005524 *
Robert Ly91c5ce32014-06-08 15:37:00 -07005525 * Here's how you'd set the <code>BigTextStyle</code> on a notification:
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005526 * <pre class="prettyprint">
Robert Ly91c5ce32014-06-08 15:37:00 -07005527 * Notification notif = new Notification.Builder(mContext)
5528 * .setContentTitle(&quot;New mail from &quot; + sender.toString())
5529 * .setContentText(subject)
5530 * .setSmallIcon(R.drawable.new_mail)
5531 * .setLargeIcon(aBitmap)
5532 * .setStyle(new Notification.BigTextStyle()
5533 * .bigText(aVeryLongString))
5534 * .build();
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005535 * </pre>
Joe Malin8d40d042012-11-05 11:36:40 -08005536 *
Daniel Sandler4dfbe832012-04-11 14:51:46 -04005537 * @see Notification#bigContentView
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005538 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005539 public static class BigTextStyle extends Style {
Jorim Jaggi457a10d2014-09-08 16:18:23 +02005540
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005541 private CharSequence mBigText;
5542
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005543 public BigTextStyle() {
5544 }
5545
Adrian Roosf5faf9d2016-05-23 13:56:15 -07005546 /**
5547 * @deprecated use {@code BigTextStyle()}.
5548 */
5549 @Deprecated
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005550 public BigTextStyle(Builder builder) {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005551 setBuilder(builder);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005552 }
5553
Chris Wrend6297db2012-05-03 16:20:13 -04005554 /**
5555 * Overrides ContentTitle in the big form of the template.
5556 * This defaults to the value passed to setContentTitle().
5557 */
5558 public BigTextStyle setBigContentTitle(CharSequence title) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04005559 internalSetBigContentTitle(safeCharSequence(title));
Chris Wrend6297db2012-05-03 16:20:13 -04005560 return this;
5561 }
5562
5563 /**
5564 * Set the first line of text after the detail section in the big form of the template.
5565 */
5566 public BigTextStyle setSummaryText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04005567 internalSetSummaryText(safeCharSequence(cs));
Chris Wrend6297db2012-05-03 16:20:13 -04005568 return this;
5569 }
5570
Chris Wren0bd664d2012-08-01 13:56:56 -04005571 /**
5572 * Provide the longer text to be displayed in the big form of the
5573 * template in place of the content text.
5574 */
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005575 public BigTextStyle bigText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04005576 mBigText = safeCharSequence(cs);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005577 return this;
5578 }
5579
Daniel Sandlerf45564e2013-04-15 15:05:08 -04005580 /**
5581 * @hide
5582 */
5583 public void addExtras(Bundle extras) {
5584 super.addExtras(extras);
5585
Christoph Studer4600f9b2014-07-22 22:44:43 +02005586 extras.putCharSequence(EXTRA_BIG_TEXT, mBigText);
5587 }
5588
5589 /**
5590 * @hide
5591 */
5592 @Override
5593 protected void restoreFromExtras(Bundle extras) {
5594 super.restoreFromExtras(extras);
5595
5596 mBigText = extras.getCharSequence(EXTRA_BIG_TEXT);
Daniel Sandlerf45564e2013-04-15 15:05:08 -04005597 }
5598
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005599 /**
Selim Cinek7d1009b2017-01-25 15:28:28 -08005600 * @param increasedHeight true if this layout be created with an increased height.
5601 *
5602 * @hide
5603 */
5604 @Override
5605 public RemoteViews makeContentView(boolean increasedHeight) {
5606 if (increasedHeight) {
5607 ArrayList<Action> actions = mBuilder.mActions;
5608 mBuilder.mActions = new ArrayList<>();
5609 RemoteViews remoteViews = makeBigContentView();
5610 mBuilder.mActions = actions;
5611 return remoteViews;
5612 }
5613 return super.makeContentView(increasedHeight);
5614 }
5615
5616 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005617 * @hide
5618 */
Selim Cinek87ed69b2017-02-09 15:59:43 -08005619 @Override
5620 public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
5621 if (increasedHeight && mBuilder.mActions.size() > 0) {
5622 return makeBigContentView();
5623 }
5624 return super.makeHeadsUpContentView(increasedHeight);
5625 }
5626
5627 /**
5628 * @hide
5629 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005630 public RemoteViews makeBigContentView() {
Christoph Studer4600f9b2014-07-22 22:44:43 +02005631
5632 // Nasty
Selim Cinek75998782016-04-26 10:39:17 -07005633 CharSequence text = mBuilder.getAllExtras().getCharSequence(EXTRA_TEXT);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005634 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, null);
Daniel Sandler916ad912012-06-13 12:17:07 -04005635
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01005636 RemoteViews contentView = getStandardView(mBuilder.getBigTextLayoutResource());
Joe Malin8d40d042012-11-05 11:36:40 -08005637
Selim Cinek75998782016-04-26 10:39:17 -07005638 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, text);
Christoph Studer4600f9b2014-07-22 22:44:43 +02005639
Selim Cinek3a2c4b92015-12-17 17:01:17 -08005640 CharSequence bigTextText = mBuilder.processLegacyText(mBigText);
Selim Cinek75998782016-04-26 10:39:17 -07005641 if (TextUtils.isEmpty(bigTextText)) {
5642 // In case the bigtext is null / empty fall back to the normal text to avoid a weird
5643 // experience
5644 bigTextText = mBuilder.processLegacyText(text);
5645 }
Adrian Roosb1f427c2016-05-26 12:27:15 -07005646 applyBigTextContentView(mBuilder, contentView, bigTextText);
Selim Cinek4fb12d32015-11-19 18:10:48 -08005647
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005648 return contentView;
5649 }
5650
Adrian Roosb1f427c2016-05-26 12:27:15 -07005651 static void applyBigTextContentView(Builder builder,
5652 RemoteViews contentView, CharSequence bigTextText) {
5653 contentView.setTextViewText(R.id.big_text, bigTextText);
Selim Cinek7b9605b2017-01-19 17:36:00 -08005654 builder.setTextViewColorSecondary(contentView, R.id.big_text);
Adrian Roosb1f427c2016-05-26 12:27:15 -07005655 contentView.setViewVisibility(R.id.big_text,
5656 TextUtils.isEmpty(bigTextText) ? View.GONE : View.VISIBLE);
Selim Cinek279fa862016-06-14 10:57:25 -07005657 contentView.setBoolean(R.id.big_text, "setHasImage", builder.mN.hasLargeIcon());
Adrian Roosb1f427c2016-05-26 12:27:15 -07005658 }
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005659 }
Daniel Sandler879c5e02012-04-17 16:46:51 -04005660
5661 /**
Alex Hillsfc737de2016-03-23 17:33:02 -04005662 * Helper class for generating large-format notifications that include multiple back-and-forth
5663 * messages of varying types between any number of people.
5664 *
5665 * <br>
5666 * If the platform does not provide large-format notifications, this method has no effect. The
5667 * user will always see the normal notification view.
5668 * <br>
5669 * This class is a "rebuilder": It attaches to a Builder object and modifies its behavior, like
5670 * so:
5671 * <pre class="prettyprint">
5672 *
5673 * Notification noti = new Notification.Builder()
5674 * .setContentTitle(&quot;2 new messages wtih &quot; + sender.toString())
5675 * .setContentText(subject)
5676 * .setSmallIcon(R.drawable.new_message)
5677 * .setLargeIcon(aBitmap)
5678 * .setStyle(new Notification.MessagingStyle(resources.getString(R.string.reply_name))
5679 * .addMessage(messages[0].getText(), messages[0].getTime(), messages[0].getSender())
5680 * .addMessage(messages[1].getText(), messages[1].getTime(), messages[1].getSender()))
5681 * .build();
5682 * </pre>
5683 */
5684 public static class MessagingStyle extends Style {
5685
5686 /**
5687 * The maximum number of messages that will be retained in the Notification itself (the
5688 * number displayed is up to the platform).
5689 */
5690 public static final int MAXIMUM_RETAINED_MESSAGES = 25;
5691
5692 CharSequence mUserDisplayName;
5693 CharSequence mConversationTitle;
Alex Hillsd9b04d92016-04-11 16:38:16 -04005694 List<Message> mMessages = new ArrayList<>();
Adrian Roos437cd562017-01-18 15:47:03 -08005695 List<Message> mHistoricMessages = new ArrayList<>();
Alex Hillsfc737de2016-03-23 17:33:02 -04005696
5697 MessagingStyle() {
5698 }
5699
5700 /**
Alex Hillsfd590442016-10-07 09:52:44 -04005701 * @param userDisplayName Required - the name to be displayed for any replies sent by the
5702 * user before the posting app reposts the notification with those messages after they've
5703 * been actually sent and in previous messages sent by the user added in
Alex Hillsfc737de2016-03-23 17:33:02 -04005704 * {@link #addMessage(Notification.MessagingStyle.Message)}
5705 */
Alex Hillsfd590442016-10-07 09:52:44 -04005706 public MessagingStyle(@NonNull CharSequence userDisplayName) {
Alex Hillsfc737de2016-03-23 17:33:02 -04005707 mUserDisplayName = userDisplayName;
5708 }
5709
5710 /**
5711 * Returns the name to be displayed for any replies sent by the user
5712 */
5713 public CharSequence getUserDisplayName() {
5714 return mUserDisplayName;
5715 }
5716
5717 /**
Alex Hillsfc737de2016-03-23 17:33:02 -04005718 * Sets the title to be displayed on this conversation. This should only be used for
5719 * group messaging and left unset for one-on-one conversations.
5720 * @param conversationTitle
5721 * @return this object for method chaining.
5722 */
5723 public MessagingStyle setConversationTitle(CharSequence conversationTitle) {
5724 mConversationTitle = conversationTitle;
5725 return this;
5726 }
5727
5728 /**
5729 * Return the title to be displayed on this conversation. Can be <code>null</code> and
5730 * should be for one-on-one conversations
5731 */
5732 public CharSequence getConversationTitle() {
5733 return mConversationTitle;
5734 }
5735
5736 /**
5737 * Adds a message for display by this notification. Convenience call for a simple
5738 * {@link Message} in {@link #addMessage(Notification.MessagingStyle.Message)}.
5739 * @param text A {@link CharSequence} to be displayed as the message content
5740 * @param timestamp Time at which the message arrived
5741 * @param sender A {@link CharSequence} to be used for displaying the name of the
5742 * sender. Should be <code>null</code> for messages by the current user, in which case
5743 * the platform will insert {@link #getUserDisplayName()}.
5744 * Should be unique amongst all individuals in the conversation, and should be
5745 * consistent during re-posts of the notification.
5746 *
5747 * @see Message#Message(CharSequence, long, CharSequence)
5748 *
5749 * @return this object for method chaining
5750 */
5751 public MessagingStyle addMessage(CharSequence text, long timestamp, CharSequence sender) {
Adrian Roos437cd562017-01-18 15:47:03 -08005752 return addMessage(new Message(text, timestamp, sender));
Alex Hillsfc737de2016-03-23 17:33:02 -04005753 }
5754
5755 /**
5756 * Adds a {@link Message} for display in this notification.
Adrian Roos437cd562017-01-18 15:47:03 -08005757 *
5758 * <p>The messages should be added in chronologic order, i.e. the oldest first,
5759 * the newest last.
5760 *
Alex Hillsfc737de2016-03-23 17:33:02 -04005761 * @param message The {@link Message} to be displayed
5762 * @return this object for method chaining
5763 */
5764 public MessagingStyle addMessage(Message message) {
5765 mMessages.add(message);
5766 if (mMessages.size() > MAXIMUM_RETAINED_MESSAGES) {
5767 mMessages.remove(0);
5768 }
5769 return this;
5770 }
5771
5772 /**
Adrian Roos437cd562017-01-18 15:47:03 -08005773 * Adds a {@link Message} for historic context in this notification.
5774 *
5775 * <p>Messages should be added as historic if they are not the main subject of the
5776 * notification but may give context to a conversation. The system may choose to present
5777 * them only when relevant, e.g. when replying to a message through a {@link RemoteInput}.
5778 *
5779 * <p>The messages should be added in chronologic order, i.e. the oldest first,
5780 * the newest last.
5781 *
5782 * @param message The historic {@link Message} to be added
5783 * @return this object for method chaining
5784 */
5785 public MessagingStyle addHistoricMessage(Message message) {
5786 mHistoricMessages.add(message);
5787 if (mHistoricMessages.size() > MAXIMUM_RETAINED_MESSAGES) {
5788 mHistoricMessages.remove(0);
5789 }
5790 return this;
5791 }
5792
5793 /**
Alex Hillsfc737de2016-03-23 17:33:02 -04005794 * Gets the list of {@code Message} objects that represent the notification
5795 */
5796 public List<Message> getMessages() {
5797 return mMessages;
5798 }
5799
5800 /**
Adrian Roos437cd562017-01-18 15:47:03 -08005801 * Gets the list of historic {@code Message}s in the notification.
5802 */
5803 public List<Message> getHistoricMessages() {
5804 return mHistoricMessages;
5805 }
5806
5807 /**
Alex Hillsfc737de2016-03-23 17:33:02 -04005808 * @hide
5809 */
5810 @Override
5811 public void addExtras(Bundle extras) {
5812 super.addExtras(extras);
5813 if (mUserDisplayName != null) {
5814 extras.putCharSequence(EXTRA_SELF_DISPLAY_NAME, mUserDisplayName);
5815 }
5816 if (mConversationTitle != null) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04005817 extras.putCharSequence(EXTRA_CONVERSATION_TITLE, mConversationTitle);
Alex Hillsfc737de2016-03-23 17:33:02 -04005818 }
Alex Hillsd9b04d92016-04-11 16:38:16 -04005819 if (!mMessages.isEmpty()) { extras.putParcelableArray(EXTRA_MESSAGES,
5820 Message.getBundleArrayForMessages(mMessages));
Alex Hillsfc737de2016-03-23 17:33:02 -04005821 }
Adrian Roos437cd562017-01-18 15:47:03 -08005822 if (!mHistoricMessages.isEmpty()) { extras.putParcelableArray(EXTRA_HISTORIC_MESSAGES,
5823 Message.getBundleArrayForMessages(mHistoricMessages));
5824 }
Adrian Roos33fbd2c2016-05-27 15:35:28 -07005825
5826 fixTitleAndTextExtras(extras);
5827 }
5828
5829 private void fixTitleAndTextExtras(Bundle extras) {
5830 Message m = findLatestIncomingMessage();
5831 CharSequence text = (m == null) ? null : m.mText;
5832 CharSequence sender = m == null ? null
5833 : TextUtils.isEmpty(m.mSender) ? mUserDisplayName : m.mSender;
5834 CharSequence title;
5835 if (!TextUtils.isEmpty(mConversationTitle)) {
5836 if (!TextUtils.isEmpty(sender)) {
5837 BidiFormatter bidi = BidiFormatter.getInstance();
5838 title = mBuilder.mContext.getString(
5839 com.android.internal.R.string.notification_messaging_title_template,
5840 bidi.unicodeWrap(mConversationTitle), bidi.unicodeWrap(m.mSender));
5841 } else {
5842 title = mConversationTitle;
5843 }
5844 } else {
5845 title = sender;
5846 }
5847
5848 if (title != null) {
5849 extras.putCharSequence(EXTRA_TITLE, title);
5850 }
5851 if (text != null) {
5852 extras.putCharSequence(EXTRA_TEXT, text);
5853 }
Alex Hillsfc737de2016-03-23 17:33:02 -04005854 }
5855
5856 /**
5857 * @hide
5858 */
5859 @Override
5860 protected void restoreFromExtras(Bundle extras) {
5861 super.restoreFromExtras(extras);
5862
5863 mMessages.clear();
Adrian Roos437cd562017-01-18 15:47:03 -08005864 mHistoricMessages.clear();
Adrian Roos96b7e202016-05-17 13:50:38 -07005865 mUserDisplayName = extras.getCharSequence(EXTRA_SELF_DISPLAY_NAME);
5866 mConversationTitle = extras.getCharSequence(EXTRA_CONVERSATION_TITLE);
Adrian Roos437cd562017-01-18 15:47:03 -08005867 Parcelable[] messages = extras.getParcelableArray(EXTRA_MESSAGES);
5868 if (messages != null && messages instanceof Parcelable[]) {
5869 mMessages = Message.getMessagesFromBundleArray(messages);
5870 }
5871 Parcelable[] histMessages = extras.getParcelableArray(EXTRA_HISTORIC_MESSAGES);
5872 if (histMessages != null && histMessages instanceof Parcelable[]) {
5873 mHistoricMessages = Message.getMessagesFromBundleArray(histMessages);
Alex Hillsfc737de2016-03-23 17:33:02 -04005874 }
5875 }
5876
5877 /**
5878 * @hide
5879 */
Adrian Roosc1a80b02016-04-05 14:54:55 -07005880 @Override
Selim Cinek7d1009b2017-01-25 15:28:28 -08005881 public RemoteViews makeContentView(boolean increasedHeight) {
5882 if (!increasedHeight) {
5883 Message m = findLatestIncomingMessage();
5884 CharSequence title = mConversationTitle != null
5885 ? mConversationTitle
5886 : (m == null) ? null : m.mSender;
5887 CharSequence text = (m == null)
5888 ? null
5889 : mConversationTitle != null ? makeMessageLine(m, mBuilder) : m.mText;
Alex Hillsfc737de2016-03-23 17:33:02 -04005890
Selim Cinek7d1009b2017-01-25 15:28:28 -08005891 return mBuilder.applyStandardTemplate(mBuilder.getBaseLayoutResource(),
5892 mBuilder.mParams.reset().hasProgress(false).title(title).text(text));
5893 } else {
5894 ArrayList<Action> actions = mBuilder.mActions;
5895 mBuilder.mActions = new ArrayList<>();
5896 RemoteViews remoteViews = makeBigContentView();
5897 mBuilder.mActions = actions;
5898 return remoteViews;
5899 }
Adrian Roosc1a80b02016-04-05 14:54:55 -07005900 }
5901
5902 private Message findLatestIncomingMessage() {
5903 for (int i = mMessages.size() - 1; i >= 0; i--) {
5904 Message m = mMessages.get(i);
5905 // Incoming messages have a non-empty sender.
5906 if (!TextUtils.isEmpty(m.mSender)) {
5907 return m;
5908 }
5909 }
Adrian Roos33fbd2c2016-05-27 15:35:28 -07005910 if (!mMessages.isEmpty()) {
5911 // No incoming messages, fall back to outgoing message
5912 return mMessages.get(mMessages.size() - 1);
5913 }
Adrian Roosc1a80b02016-04-05 14:54:55 -07005914 return null;
5915 }
5916
5917 /**
5918 * @hide
5919 */
5920 @Override
5921 public RemoteViews makeBigContentView() {
5922 CharSequence title = !TextUtils.isEmpty(super.mBigContentTitle)
5923 ? super.mBigContentTitle
5924 : mConversationTitle;
5925 boolean hasTitle = !TextUtils.isEmpty(title);
5926
Adrian Roosfeafa052016-06-01 17:09:45 -07005927 if (mMessages.size() == 1) {
5928 // Special case for a single message: Use the big text style
5929 // so the collapsed and expanded versions match nicely.
5930 CharSequence bigTitle;
5931 CharSequence text;
5932 if (hasTitle) {
5933 bigTitle = title;
Selim Cinek7b9605b2017-01-19 17:36:00 -08005934 text = makeMessageLine(mMessages.get(0), mBuilder);
Adrian Roosfeafa052016-06-01 17:09:45 -07005935 } else {
5936 bigTitle = mMessages.get(0).mSender;
5937 text = mMessages.get(0).mText;
5938 }
Adrian Roosb1f427c2016-05-26 12:27:15 -07005939 RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(
5940 mBuilder.getBigTextLayoutResource(),
Adrian Roos70d7aa32017-01-11 15:39:06 -08005941 mBuilder.mParams.reset().hasProgress(false).title(bigTitle).text(null));
Adrian Roosb1f427c2016-05-26 12:27:15 -07005942 BigTextStyle.applyBigTextContentView(mBuilder, contentView, text);
5943 return contentView;
5944 }
5945
Adrian Roos48d746a2016-04-12 14:57:28 -07005946 RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(
Adrian Roosc1a80b02016-04-05 14:54:55 -07005947 mBuilder.getMessagingLayoutResource(),
Adrian Roos70d7aa32017-01-11 15:39:06 -08005948 mBuilder.mParams.reset().hasProgress(false).title(title).text(null));
Adrian Roosc1a80b02016-04-05 14:54:55 -07005949
5950 int[] rowIds = {R.id.inbox_text0, R.id.inbox_text1, R.id.inbox_text2, R.id.inbox_text3,
5951 R.id.inbox_text4, R.id.inbox_text5, R.id.inbox_text6};
5952
5953 // Make sure all rows are gone in case we reuse a view.
5954 for (int rowId : rowIds) {
5955 contentView.setViewVisibility(rowId, View.GONE);
5956 }
5957
5958 int i=0;
Adrian Roos2d5dbba2016-06-08 17:11:53 -07005959 contentView.setViewLayoutMarginBottomDimen(R.id.line1,
5960 hasTitle ? R.dimen.notification_messaging_spacing : 0);
Adrian Roosc1a80b02016-04-05 14:54:55 -07005961 contentView.setInt(R.id.notification_messaging, "setNumIndentLines",
Selim Cinek279fa862016-06-14 10:57:25 -07005962 !mBuilder.mN.hasLargeIcon() ? 0 : (hasTitle ? 1 : 2));
Adrian Roosc1a80b02016-04-05 14:54:55 -07005963
Adrian Roosfeafa052016-06-01 17:09:45 -07005964 int contractedChildId = View.NO_ID;
5965 Message contractedMessage = findLatestIncomingMessage();
Adrian Roos437cd562017-01-18 15:47:03 -08005966 int firstHistoricMessage = Math.max(0, mHistoricMessages.size()
5967 - (rowIds.length - mMessages.size()));
5968 while (firstHistoricMessage + i < mHistoricMessages.size() && i < rowIds.length) {
5969 Message m = mHistoricMessages.get(firstHistoricMessage + i);
5970 int rowId = rowIds[i];
5971
Selim Cinek7b9605b2017-01-19 17:36:00 -08005972 contentView.setTextViewText(rowId, makeMessageLine(m, mBuilder));
Adrian Roos437cd562017-01-18 15:47:03 -08005973
5974 if (contractedMessage == m) {
5975 contractedChildId = rowId;
5976 }
5977
5978 i++;
5979 }
5980
Adrian Roosc1a80b02016-04-05 14:54:55 -07005981 int firstMessage = Math.max(0, mMessages.size() - rowIds.length);
5982 while (firstMessage + i < mMessages.size() && i < rowIds.length) {
5983 Message m = mMessages.get(firstMessage + i);
5984 int rowId = rowIds[i];
5985
5986 contentView.setViewVisibility(rowId, View.VISIBLE);
Selim Cinek7b9605b2017-01-19 17:36:00 -08005987 contentView.setTextViewText(rowId, makeMessageLine(m, mBuilder));
5988 mBuilder.setTextViewColorSecondary(contentView, rowId);
Adrian Roosc1a80b02016-04-05 14:54:55 -07005989
Adrian Roosfeafa052016-06-01 17:09:45 -07005990 if (contractedMessage == m) {
5991 contractedChildId = rowId;
5992 }
5993
Adrian Roosc1a80b02016-04-05 14:54:55 -07005994 i++;
5995 }
Adrian Roos437cd562017-01-18 15:47:03 -08005996 // Clear the remaining views for reapply. Ensures that historic message views can
5997 // reliably be identified as being GONE and having non-null text.
5998 while (i < rowIds.length) {
5999 int rowId = rowIds[i];
6000 contentView.setTextViewText(rowId, null);
6001 i++;
6002 }
6003
Adrian Roosfeafa052016-06-01 17:09:45 -07006004 // Record this here to allow transformation between the contracted and expanded views.
6005 contentView.setInt(R.id.notification_messaging, "setContractedChildId",
6006 contractedChildId);
Alex Hillsfc737de2016-03-23 17:33:02 -04006007 return contentView;
6008 }
6009
Selim Cinek7b9605b2017-01-19 17:36:00 -08006010 private CharSequence makeMessageLine(Message m, Builder builder) {
Adrian Roosc1a80b02016-04-05 14:54:55 -07006011 BidiFormatter bidi = BidiFormatter.getInstance();
6012 SpannableStringBuilder sb = new SpannableStringBuilder();
Selim Cinek7b9605b2017-01-19 17:36:00 -08006013 boolean colorize = builder.isColorized();
Adrian Roosc1a80b02016-04-05 14:54:55 -07006014 if (TextUtils.isEmpty(m.mSender)) {
6015 CharSequence replyName = mUserDisplayName == null ? "" : mUserDisplayName;
6016 sb.append(bidi.unicodeWrap(replyName),
Selim Cinek7b9605b2017-01-19 17:36:00 -08006017 makeFontColorSpan(colorize
6018 ? builder.getPrimaryTextColor()
6019 : mBuilder.resolveContrastColor()),
Adrian Roosc1a80b02016-04-05 14:54:55 -07006020 0 /* flags */);
6021 } else {
6022 sb.append(bidi.unicodeWrap(m.mSender),
Selim Cinek7b9605b2017-01-19 17:36:00 -08006023 makeFontColorSpan(colorize
6024 ? builder.getPrimaryTextColor()
6025 : Color.BLACK),
Adrian Roosc1a80b02016-04-05 14:54:55 -07006026 0 /* flags */);
6027 }
6028 CharSequence text = m.mText == null ? "" : m.mText;
6029 sb.append(" ").append(bidi.unicodeWrap(text));
6030 return sb;
6031 }
6032
Adrian Roosdedd1df2016-04-26 16:38:47 -07006033 /**
6034 * @hide
6035 */
6036 @Override
Selim Cinek87ed69b2017-02-09 15:59:43 -08006037 public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
6038 if (increasedHeight) {
6039 return makeBigContentView();
6040 }
Adrian Roosdedd1df2016-04-26 16:38:47 -07006041 Message m = findLatestIncomingMessage();
6042 CharSequence title = mConversationTitle != null
6043 ? mConversationTitle
6044 : (m == null) ? null : m.mSender;
6045 CharSequence text = (m == null)
6046 ? null
Selim Cinek7b9605b2017-01-19 17:36:00 -08006047 : mConversationTitle != null ? makeMessageLine(m, mBuilder) : m.mText;
Adrian Roosdedd1df2016-04-26 16:38:47 -07006048
6049 return mBuilder.applyStandardTemplateWithActions(mBuilder.getBigBaseLayoutResource(),
Adrian Roos70d7aa32017-01-11 15:39:06 -08006050 mBuilder.mParams.reset().hasProgress(false).title(title).text(text));
Adrian Roosdedd1df2016-04-26 16:38:47 -07006051 }
6052
Adrian Roosc1a80b02016-04-05 14:54:55 -07006053 private static TextAppearanceSpan makeFontColorSpan(int color) {
6054 return new TextAppearanceSpan(null, 0, 0,
6055 ColorStateList.valueOf(color), null);
6056 }
6057
Alex Hillsd9b04d92016-04-11 16:38:16 -04006058 public static final class Message {
6059
6060 static final String KEY_TEXT = "text";
6061 static final String KEY_TIMESTAMP = "time";
6062 static final String KEY_SENDER = "sender";
6063 static final String KEY_DATA_MIME_TYPE = "type";
6064 static final String KEY_DATA_URI= "uri";
Shane Brennan5a871862017-03-11 13:14:17 -08006065 static final String KEY_EXTRAS_BUNDLE = "extras";
Alex Hillsfc737de2016-03-23 17:33:02 -04006066
6067 private final CharSequence mText;
6068 private final long mTimestamp;
6069 private final CharSequence mSender;
6070
Shane Brennan5a871862017-03-11 13:14:17 -08006071 private Bundle mExtras = new Bundle();
Alex Hillsfc737de2016-03-23 17:33:02 -04006072 private String mDataMimeType;
6073 private Uri mDataUri;
6074
6075 /**
6076 * Constructor
6077 * @param text A {@link CharSequence} to be displayed as the message content
6078 * @param timestamp Time at which the message arrived
6079 * @param sender A {@link CharSequence} to be used for displaying the name of the
6080 * sender. Should be <code>null</code> for messages by the current user, in which case
6081 * the platform will insert {@link MessagingStyle#getUserDisplayName()}.
6082 * Should be unique amongst all individuals in the conversation, and should be
6083 * consistent during re-posts of the notification.
6084 */
6085 public Message(CharSequence text, long timestamp, CharSequence sender){
6086 mText = text;
6087 mTimestamp = timestamp;
6088 mSender = sender;
6089 }
6090
6091 /**
6092 * Sets a binary blob of data and an associated MIME type for a message. In the case
6093 * where the platform doesn't support the MIME type, the original text provided in the
6094 * constructor will be used.
6095 * @param dataMimeType The MIME type of the content. See
6096 * <a href="{@docRoot}notifications/messaging.html"> for the list of supported MIME
6097 * types on Android and Android Wear.
6098 * @param dataUri The uri containing the content whose type is given by the MIME type.
6099 * <p class="note">
6100 * <ol>
6101 * <li>Notification Listeners including the System UI need permission to access the
6102 * data the Uri points to. The recommended ways to do this are:</li>
6103 * <li>Store the data in your own ContentProvider, making sure that other apps have
6104 * the correct permission to access your provider. The preferred mechanism for
6105 * providing access is to use per-URI permissions which are temporary and only
6106 * grant access to the receiving application. An easy way to create a
6107 * ContentProvider like this is to use the FileProvider helper class.</li>
6108 * <li>Use the system MediaStore. The MediaStore is primarily aimed at video, audio
6109 * and image MIME types, however beginning with Android 3.0 (API level 11) it can
6110 * also store non-media types (see MediaStore.Files for more info). Files can be
6111 * inserted into the MediaStore using scanFile() after which a content:// style
6112 * Uri suitable for sharing is passed to the provided onScanCompleted() callback.
6113 * Note that once added to the system MediaStore the content is accessible to any
6114 * app on the device.</li>
6115 * </ol>
6116 * @return this object for method chaining
6117 */
6118 public Message setData(String dataMimeType, Uri dataUri) {
6119 mDataMimeType = dataMimeType;
6120 mDataUri = dataUri;
6121 return this;
6122 }
6123
Alex Hillsfc737de2016-03-23 17:33:02 -04006124 /**
6125 * Get the text to be used for this message, or the fallback text if a type and content
6126 * Uri have been set
6127 */
6128 public CharSequence getText() {
6129 return mText;
6130 }
6131
6132 /**
6133 * Get the time at which this message arrived
6134 */
6135 public long getTimestamp() {
6136 return mTimestamp;
6137 }
6138
6139 /**
Shane Brennan5a871862017-03-11 13:14:17 -08006140 * Get the extras Bundle for this message.
6141 */
6142 public Bundle getExtras() {
6143 return mExtras;
6144 }
6145
6146 /**
Alex Hillsfc737de2016-03-23 17:33:02 -04006147 * Get the text used to display the contact's name in the messaging experience
6148 */
6149 public CharSequence getSender() {
6150 return mSender;
6151 }
6152
6153 /**
6154 * Get the MIME type of the data pointed to by the Uri
6155 */
6156 public String getDataMimeType() {
6157 return mDataMimeType;
6158 }
6159
6160 /**
6161 * Get the the Uri pointing to the content of the message. Can be null, in which case
6162 * {@see #getText()} is used.
6163 */
6164 public Uri getDataUri() {
6165 return mDataUri;
6166 }
6167
Alex Hillsd9b04d92016-04-11 16:38:16 -04006168 private Bundle toBundle() {
6169 Bundle bundle = new Bundle();
Alex Hillsfc737de2016-03-23 17:33:02 -04006170 if (mText != null) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04006171 bundle.putCharSequence(KEY_TEXT, mText);
Alex Hillsfc737de2016-03-23 17:33:02 -04006172 }
Alex Hillsd9b04d92016-04-11 16:38:16 -04006173 bundle.putLong(KEY_TIMESTAMP, mTimestamp);
Alex Hillsfc737de2016-03-23 17:33:02 -04006174 if (mSender != null) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04006175 bundle.putCharSequence(KEY_SENDER, mSender);
Alex Hillsfc737de2016-03-23 17:33:02 -04006176 }
6177 if (mDataMimeType != null) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04006178 bundle.putString(KEY_DATA_MIME_TYPE, mDataMimeType);
Alex Hillsfc737de2016-03-23 17:33:02 -04006179 }
6180 if (mDataUri != null) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04006181 bundle.putParcelable(KEY_DATA_URI, mDataUri);
Alex Hillsfc737de2016-03-23 17:33:02 -04006182 }
Shane Brennan5a871862017-03-11 13:14:17 -08006183 if (mExtras != null) {
6184 bundle.putBundle(KEY_EXTRAS_BUNDLE, mExtras);
6185 }
Alex Hillsd9b04d92016-04-11 16:38:16 -04006186 return bundle;
Alex Hillsfc737de2016-03-23 17:33:02 -04006187 }
6188
Alex Hillsd9b04d92016-04-11 16:38:16 -04006189 static Bundle[] getBundleArrayForMessages(List<Message> messages) {
6190 Bundle[] bundles = new Bundle[messages.size()];
6191 final int N = messages.size();
6192 for (int i = 0; i < N; i++) {
6193 bundles[i] = messages.get(i).toBundle();
6194 }
6195 return bundles;
6196 }
6197
Adrian Roosdedd1df2016-04-26 16:38:47 -07006198 static List<Message> getMessagesFromBundleArray(Parcelable[] bundles) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04006199 List<Message> messages = new ArrayList<>(bundles.length);
6200 for (int i = 0; i < bundles.length; i++) {
Adrian Roosdedd1df2016-04-26 16:38:47 -07006201 if (bundles[i] instanceof Bundle) {
6202 Message message = getMessageFromBundle((Bundle)bundles[i]);
6203 if (message != null) {
6204 messages.add(message);
6205 }
Alex Hillsd9b04d92016-04-11 16:38:16 -04006206 }
6207 }
6208 return messages;
6209 }
6210
6211 static Message getMessageFromBundle(Bundle bundle) {
6212 try {
Adrian Roosfbddd2c2016-05-13 12:57:20 -07006213 if (!bundle.containsKey(KEY_TEXT) || !bundle.containsKey(KEY_TIMESTAMP)) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04006214 return null;
6215 } else {
6216 Message message = new Message(bundle.getCharSequence(KEY_TEXT),
6217 bundle.getLong(KEY_TIMESTAMP), bundle.getCharSequence(KEY_SENDER));
6218 if (bundle.containsKey(KEY_DATA_MIME_TYPE) &&
6219 bundle.containsKey(KEY_DATA_URI)) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04006220 message.setData(bundle.getString(KEY_DATA_MIME_TYPE),
6221 (Uri) bundle.getParcelable(KEY_DATA_URI));
Alex Hillsfc737de2016-03-23 17:33:02 -04006222 }
Shane Brennan5a871862017-03-11 13:14:17 -08006223 if (bundle.containsKey(KEY_EXTRAS_BUNDLE)) {
6224 message.getExtras().putAll(bundle.getBundle(KEY_EXTRAS_BUNDLE));
6225 }
Alex Hillsd9b04d92016-04-11 16:38:16 -04006226 return message;
6227 }
6228 } catch (ClassCastException e) {
6229 return null;
6230 }
6231 }
Alex Hillsfc737de2016-03-23 17:33:02 -04006232 }
6233 }
6234
6235 /**
Daniel Sandler879c5e02012-04-17 16:46:51 -04006236 * Helper class for generating large-format notifications that include a list of (up to 5) strings.
Joe Malin8d40d042012-11-05 11:36:40 -08006237 *
Robert Ly91c5ce32014-06-08 15:37:00 -07006238 * Here's how you'd set the <code>InboxStyle</code> on a notification:
Daniel Sandler879c5e02012-04-17 16:46:51 -04006239 * <pre class="prettyprint">
Robert Ly91c5ce32014-06-08 15:37:00 -07006240 * Notification notif = new Notification.Builder(mContext)
6241 * .setContentTitle(&quot;5 New mails from &quot; + sender.toString())
6242 * .setContentText(subject)
6243 * .setSmallIcon(R.drawable.new_mail)
6244 * .setLargeIcon(aBitmap)
6245 * .setStyle(new Notification.InboxStyle()
6246 * .addLine(str1)
6247 * .addLine(str2)
6248 * .setContentTitle(&quot;&quot;)
6249 * .setSummaryText(&quot;+3 more&quot;))
6250 * .build();
Daniel Sandler879c5e02012-04-17 16:46:51 -04006251 * </pre>
Joe Malin8d40d042012-11-05 11:36:40 -08006252 *
Daniel Sandler879c5e02012-04-17 16:46:51 -04006253 * @see Notification#bigContentView
6254 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04006255 public static class InboxStyle extends Style {
Daniel Sandler879c5e02012-04-17 16:46:51 -04006256 private ArrayList<CharSequence> mTexts = new ArrayList<CharSequence>(5);
6257
Chris Wrenfbd96ba2012-05-01 12:03:58 -04006258 public InboxStyle() {
6259 }
6260
Adrian Roosf5faf9d2016-05-23 13:56:15 -07006261 /**
6262 * @deprecated use {@code InboxStyle()}.
6263 */
6264 @Deprecated
Daniel Sandler879c5e02012-04-17 16:46:51 -04006265 public InboxStyle(Builder builder) {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04006266 setBuilder(builder);
Daniel Sandler879c5e02012-04-17 16:46:51 -04006267 }
6268
Chris Wrend6297db2012-05-03 16:20:13 -04006269 /**
6270 * Overrides ContentTitle in the big form of the template.
6271 * This defaults to the value passed to setContentTitle().
6272 */
6273 public InboxStyle setBigContentTitle(CharSequence title) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04006274 internalSetBigContentTitle(safeCharSequence(title));
Chris Wrend6297db2012-05-03 16:20:13 -04006275 return this;
6276 }
6277
6278 /**
6279 * Set the first line of text after the detail section in the big form of the template.
6280 */
6281 public InboxStyle setSummaryText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04006282 internalSetSummaryText(safeCharSequence(cs));
Chris Wrend6297db2012-05-03 16:20:13 -04006283 return this;
6284 }
6285
Chris Wren0bd664d2012-08-01 13:56:56 -04006286 /**
6287 * Append a line to the digest section of the Inbox notification.
6288 */
Daniel Sandler879c5e02012-04-17 16:46:51 -04006289 public InboxStyle addLine(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04006290 mTexts.add(safeCharSequence(cs));
Daniel Sandler879c5e02012-04-17 16:46:51 -04006291 return this;
6292 }
6293
Daniel Sandlerf45564e2013-04-15 15:05:08 -04006294 /**
6295 * @hide
6296 */
6297 public void addExtras(Bundle extras) {
6298 super.addExtras(extras);
Christoph Studer4600f9b2014-07-22 22:44:43 +02006299
Daniel Sandlerf45564e2013-04-15 15:05:08 -04006300 CharSequence[] a = new CharSequence[mTexts.size()];
6301 extras.putCharSequenceArray(EXTRA_TEXT_LINES, mTexts.toArray(a));
6302 }
6303
Christoph Studer4600f9b2014-07-22 22:44:43 +02006304 /**
6305 * @hide
6306 */
6307 @Override
6308 protected void restoreFromExtras(Bundle extras) {
6309 super.restoreFromExtras(extras);
6310
6311 mTexts.clear();
6312 if (extras.containsKey(EXTRA_TEXT_LINES)) {
6313 Collections.addAll(mTexts, extras.getCharSequenceArray(EXTRA_TEXT_LINES));
6314 }
6315 }
6316
Julia Reynoldsd9228f12015-10-20 10:37:27 -04006317 /**
6318 * @hide
6319 */
6320 public RemoteViews makeBigContentView() {
Selim Cinekc848c3a2016-01-13 15:27:30 -08006321 // Remove the content text so it disappears unless you have a summary
Christoph Studer4600f9b2014-07-22 22:44:43 +02006322 // Nasty
Julia Reynoldsd9228f12015-10-20 10:37:27 -04006323 CharSequence oldBuilderContentText = mBuilder.mN.extras.getCharSequence(EXTRA_TEXT);
6324 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, null);
Christoph Studer4600f9b2014-07-22 22:44:43 +02006325
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01006326 RemoteViews contentView = getStandardView(mBuilder.getInboxLayoutResource());
Daniel Sandler619738c2012-06-07 16:33:08 -04006327
Julia Reynoldsd9228f12015-10-20 10:37:27 -04006328 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, oldBuilderContentText);
Christoph Studer4600f9b2014-07-22 22:44:43 +02006329
Chris Wrend6297db2012-05-03 16:20:13 -04006330 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 -04006331 R.id.inbox_text4, R.id.inbox_text5, R.id.inbox_text6};
Chris Wrend6297db2012-05-03 16:20:13 -04006332
Chris Wren4ed80d52012-05-17 09:30:03 -04006333 // Make sure all rows are gone in case we reuse a view.
6334 for (int rowId : rowIds) {
6335 contentView.setViewVisibility(rowId, View.GONE);
6336 }
6337
Daniel Sandler879c5e02012-04-17 16:46:51 -04006338 int i=0;
Selim Cinek07c80172016-04-21 16:40:47 -07006339 int topPadding = mBuilder.mContext.getResources().getDimensionPixelSize(
6340 R.dimen.notification_inbox_item_top_padding);
Selim Cinek247fa012016-02-18 09:50:48 -08006341 boolean first = true;
Selim Cinek07c80172016-04-21 16:40:47 -07006342 int onlyViewId = 0;
6343 int maxRows = rowIds.length;
6344 if (mBuilder.mActions.size() > 0) {
6345 maxRows--;
6346 }
6347 while (i < mTexts.size() && i < maxRows) {
Daniel Sandler879c5e02012-04-17 16:46:51 -04006348 CharSequence str = mTexts.get(i);
Selim Cinek07c80172016-04-21 16:40:47 -07006349 if (!TextUtils.isEmpty(str)) {
Daniel Sandler879c5e02012-04-17 16:46:51 -04006350 contentView.setViewVisibility(rowIds[i], View.VISIBLE);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01006351 contentView.setTextViewText(rowIds[i], mBuilder.processLegacyText(str));
Selim Cinek7b9605b2017-01-19 17:36:00 -08006352 mBuilder.setTextViewColorSecondary(contentView, rowIds[i]);
Selim Cinek07c80172016-04-21 16:40:47 -07006353 contentView.setViewPadding(rowIds[i], 0, topPadding, 0, 0);
Selim Cinek247fa012016-02-18 09:50:48 -08006354 handleInboxImageMargin(contentView, rowIds[i], first);
Selim Cinek07c80172016-04-21 16:40:47 -07006355 if (first) {
6356 onlyViewId = rowIds[i];
6357 } else {
6358 onlyViewId = 0;
6359 }
Selim Cinek247fa012016-02-18 09:50:48 -08006360 first = false;
Daniel Sandler879c5e02012-04-17 16:46:51 -04006361 }
6362 i++;
6363 }
Selim Cinek07c80172016-04-21 16:40:47 -07006364 if (onlyViewId != 0) {
6365 // We only have 1 entry, lets make it look like the normal Text of a Bigtext
6366 topPadding = mBuilder.mContext.getResources().getDimensionPixelSize(
6367 R.dimen.notification_text_margin_top);
6368 contentView.setViewPadding(onlyViewId, 0, topPadding, 0, 0);
6369 }
Selim Cinek1e0bf612015-11-20 15:57:26 -08006370
Daniel Sandler879c5e02012-04-17 16:46:51 -04006371 return contentView;
6372 }
Selim Cinek1e0bf612015-11-20 15:57:26 -08006373
Selim Cinek247fa012016-02-18 09:50:48 -08006374 private void handleInboxImageMargin(RemoteViews contentView, int id, boolean first) {
Selim Cinek1e0bf612015-11-20 15:57:26 -08006375 int endMargin = 0;
Selim Cinek247fa012016-02-18 09:50:48 -08006376 if (first) {
6377 final int max = mBuilder.mN.extras.getInt(EXTRA_PROGRESS_MAX, 0);
6378 final boolean ind = mBuilder.mN.extras.getBoolean(EXTRA_PROGRESS_INDETERMINATE);
6379 boolean hasProgress = max != 0 || ind;
Selim Cinek279fa862016-06-14 10:57:25 -07006380 if (mBuilder.mN.hasLargeIcon() && !hasProgress) {
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006381 endMargin = R.dimen.notification_content_picture_margin;
Selim Cinek247fa012016-02-18 09:50:48 -08006382 }
Selim Cinek1e0bf612015-11-20 15:57:26 -08006383 }
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006384 contentView.setViewLayoutMarginEndDimen(id, endMargin);
Selim Cinek1e0bf612015-11-20 15:57:26 -08006385 }
Daniel Sandler879c5e02012-04-17 16:46:51 -04006386 }
Dan Sandler842dd772014-05-15 09:36:47 -04006387
6388 /**
6389 * Notification style for media playback notifications.
6390 *
6391 * In the expanded form, {@link Notification#bigContentView}, up to 5
6392 * {@link Notification.Action}s specified with
Dan Sandler86647982015-05-13 23:41:13 -04006393 * {@link Notification.Builder#addAction(Action) addAction} will be
Dan Sandler842dd772014-05-15 09:36:47 -04006394 * shown as icon-only pushbuttons, suitable for transport controls. The Bitmap given to
6395 * {@link Notification.Builder#setLargeIcon(android.graphics.Bitmap) setLargeIcon()} will be
6396 * treated as album artwork.
Selim Cinek99104832017-01-25 14:47:33 -08006397 * <p>
Dan Sandler842dd772014-05-15 09:36:47 -04006398 * Unlike the other styles provided here, MediaStyle can also modify the standard-size
6399 * {@link Notification#contentView}; by providing action indices to
Christoph Studerfde6f4d2014-12-12 13:23:26 +01006400 * {@link #setShowActionsInCompactView(int...)} you can promote up to 3 actions to be displayed
Dan Sandler842dd772014-05-15 09:36:47 -04006401 * in the standard view alongside the usual content.
Selim Cinek99104832017-01-25 14:47:33 -08006402 * <p>
Bryan Mawhinney6be8de32014-07-18 10:35:12 +01006403 * Notifications created with MediaStyle will have their category set to
6404 * {@link Notification#CATEGORY_TRANSPORT CATEGORY_TRANSPORT} unless you set a different
6405 * category using {@link Notification.Builder#setCategory(String) setCategory()}.
Selim Cinek99104832017-01-25 14:47:33 -08006406 * <p>
Jeff Browndba34ba2014-06-24 20:46:03 -07006407 * Finally, if you attach a {@link android.media.session.MediaSession.Token} using
6408 * {@link android.app.Notification.MediaStyle#setMediaSession(MediaSession.Token)},
Dan Sandler842dd772014-05-15 09:36:47 -04006409 * the System UI can identify this as a notification representing an active media session
6410 * and respond accordingly (by showing album artwork in the lockscreen, for example).
6411 *
Selim Cinek99104832017-01-25 14:47:33 -08006412 * <p>
6413 * Starting at {@link android.os.Build.VERSION_CODES#O Android O} any notification that has a
6414 * media session attached with {@link #setMediaSession(MediaSession.Token)} will be colorized.
6415 * You can opt-out of this behavior by using {@link Notification.Builder#setColorized(boolean)}.
6416 * <p>
6417 *
Dan Sandler842dd772014-05-15 09:36:47 -04006418 * To use this style with your Notification, feed it to
6419 * {@link Notification.Builder#setStyle(android.app.Notification.Style)} like so:
6420 * <pre class="prettyprint">
6421 * Notification noti = new Notification.Builder()
6422 * .setSmallIcon(R.drawable.ic_stat_player)
Christoph Studere935fe92014-11-24 14:18:06 +01006423 * .setContentTitle(&quot;Track title&quot;)
6424 * .setContentText(&quot;Artist - Album&quot;)
6425 * .setLargeIcon(albumArtBitmap))
Bryan Mawhinney6be8de32014-07-18 10:35:12 +01006426 * .setStyle(<b>new Notification.MediaStyle()</b>
6427 * .setMediaSession(mySession))
Dan Sandler842dd772014-05-15 09:36:47 -04006428 * .build();
6429 * </pre>
6430 *
6431 * @see Notification#bigContentView
Selim Cinek99104832017-01-25 14:47:33 -08006432 * @see Notification.Builder#setColorized(boolean)
Dan Sandler842dd772014-05-15 09:36:47 -04006433 */
6434 public static class MediaStyle extends Style {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006435 static final int MAX_MEDIA_BUTTONS_IN_COMPACT = 3;
Dan Sandler842dd772014-05-15 09:36:47 -04006436 static final int MAX_MEDIA_BUTTONS = 5;
6437
6438 private int[] mActionsToShowInCompact = null;
Jeff Browndba34ba2014-06-24 20:46:03 -07006439 private MediaSession.Token mToken;
Dan Sandler842dd772014-05-15 09:36:47 -04006440
6441 public MediaStyle() {
6442 }
6443
Adrian Roosf5faf9d2016-05-23 13:56:15 -07006444 /**
6445 * @deprecated use {@code MediaStyle()}.
6446 */
6447 @Deprecated
Dan Sandler842dd772014-05-15 09:36:47 -04006448 public MediaStyle(Builder builder) {
6449 setBuilder(builder);
6450 }
6451
6452 /**
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006453 * 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 -04006454 * notification view.
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006455 *
6456 * @param actions the indices of the actions to show in the compact notification view
Dan Sandler842dd772014-05-15 09:36:47 -04006457 */
6458 public MediaStyle setShowActionsInCompactView(int...actions) {
6459 mActionsToShowInCompact = actions;
6460 return this;
6461 }
6462
6463 /**
Jeff Browndba34ba2014-06-24 20:46:03 -07006464 * Attach a {@link android.media.session.MediaSession.Token} to this Notification
6465 * to provide additional playback information and control to the SystemUI.
Dan Sandler842dd772014-05-15 09:36:47 -04006466 */
Jeff Browndba34ba2014-06-24 20:46:03 -07006467 public MediaStyle setMediaSession(MediaSession.Token token) {
Dan Sandler842dd772014-05-15 09:36:47 -04006468 mToken = token;
6469 return this;
6470 }
6471
Christoph Studer4600f9b2014-07-22 22:44:43 +02006472 /**
6473 * @hide
6474 */
Dan Sandler842dd772014-05-15 09:36:47 -04006475 @Override
6476 public Notification buildStyled(Notification wip) {
Christoph Studer4600f9b2014-07-22 22:44:43 +02006477 super.buildStyled(wip);
Bryan Mawhinney6be8de32014-07-18 10:35:12 +01006478 if (wip.category == null) {
6479 wip.category = Notification.CATEGORY_TRANSPORT;
6480 }
Dan Sandler842dd772014-05-15 09:36:47 -04006481 return wip;
6482 }
6483
Christoph Studer4600f9b2014-07-22 22:44:43 +02006484 /**
6485 * @hide
6486 */
6487 @Override
Selim Cinek7d1009b2017-01-25 15:28:28 -08006488 public RemoteViews makeContentView(boolean increasedHeight) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04006489 return makeMediaContentView();
Christoph Studer4600f9b2014-07-22 22:44:43 +02006490 }
6491
6492 /**
6493 * @hide
6494 */
6495 @Override
Julia Reynoldsd9228f12015-10-20 10:37:27 -04006496 public RemoteViews makeBigContentView() {
6497 return makeMediaBigContentView();
Christoph Studer4600f9b2014-07-22 22:44:43 +02006498 }
6499
Selim Cinekcc10bfb2016-02-10 16:24:21 -08006500 /**
6501 * @hide
6502 */
6503 @Override
Selim Cinek87ed69b2017-02-09 15:59:43 -08006504 public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
Selim Cinekcc10bfb2016-02-10 16:24:21 -08006505 RemoteViews expanded = makeMediaBigContentView();
6506 return expanded != null ? expanded : makeMediaContentView();
6507 }
6508
Dan Sandler842dd772014-05-15 09:36:47 -04006509 /** @hide */
6510 @Override
6511 public void addExtras(Bundle extras) {
6512 super.addExtras(extras);
6513
6514 if (mToken != null) {
6515 extras.putParcelable(EXTRA_MEDIA_SESSION, mToken);
6516 }
Bryan Mawhinneye191f902014-07-22 12:50:09 +01006517 if (mActionsToShowInCompact != null) {
6518 extras.putIntArray(EXTRA_COMPACT_ACTIONS, mActionsToShowInCompact);
6519 }
Dan Sandler842dd772014-05-15 09:36:47 -04006520 }
6521
Christoph Studer4600f9b2014-07-22 22:44:43 +02006522 /**
6523 * @hide
6524 */
6525 @Override
6526 protected void restoreFromExtras(Bundle extras) {
6527 super.restoreFromExtras(extras);
6528
6529 if (extras.containsKey(EXTRA_MEDIA_SESSION)) {
6530 mToken = extras.getParcelable(EXTRA_MEDIA_SESSION);
6531 }
6532 if (extras.containsKey(EXTRA_COMPACT_ACTIONS)) {
6533 mActionsToShowInCompact = extras.getIntArray(EXTRA_COMPACT_ACTIONS);
6534 }
6535 }
6536
Selim Cinek5bf069a2015-11-10 19:14:27 -05006537 private RemoteViews generateMediaActionButton(Action action, int color) {
Dan Sandler842dd772014-05-15 09:36:47 -04006538 final boolean tombstone = (action.actionIntent == null);
Selim Cinekf33b1112015-07-15 17:45:11 -07006539 RemoteViews button = new BuilderRemoteViews(mBuilder.mContext.getApplicationInfo(),
Alan Viverette3cb07a462014-06-06 14:19:53 -07006540 R.layout.notification_material_media_action);
Dan Sandler68079d52015-07-22 10:45:30 -04006541 button.setImageViewIcon(R.id.action0, action.getIcon());
Selim Cinek5bf069a2015-11-10 19:14:27 -05006542 button.setDrawableParameters(R.id.action0, false, -1, color, PorterDuff.Mode.SRC_ATOP,
6543 -1);
Dan Sandler842dd772014-05-15 09:36:47 -04006544 if (!tombstone) {
6545 button.setOnClickPendingIntent(R.id.action0, action.actionIntent);
6546 }
6547 button.setContentDescription(R.id.action0, action.title);
6548 return button;
6549 }
6550
6551 private RemoteViews makeMediaContentView() {
6552 RemoteViews view = mBuilder.applyStandardTemplate(
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006553 R.layout.notification_template_material_media, false /* hasProgress */);
Dan Sandler842dd772014-05-15 09:36:47 -04006554
6555 final int numActions = mBuilder.mActions.size();
6556 final int N = mActionsToShowInCompact == null
6557 ? 0
6558 : Math.min(mActionsToShowInCompact.length, MAX_MEDIA_BUTTONS_IN_COMPACT);
6559 if (N > 0) {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006560 view.removeAllViews(com.android.internal.R.id.media_actions);
Dan Sandler842dd772014-05-15 09:36:47 -04006561 for (int i = 0; i < N; i++) {
6562 if (i >= numActions) {
6563 throw new IllegalArgumentException(String.format(
6564 "setShowActionsInCompactView: action %d out of bounds (max %d)",
6565 i, numActions - 1));
6566 }
6567
6568 final Action action = mBuilder.mActions.get(mActionsToShowInCompact[i]);
Selim Cinek5bf069a2015-11-10 19:14:27 -05006569 final RemoteViews button = generateMediaActionButton(action,
Selim Cinek99104832017-01-25 14:47:33 -08006570 getPrimaryHighlightColor());
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006571 view.addView(com.android.internal.R.id.media_actions, button);
Dan Sandler842dd772014-05-15 09:36:47 -04006572 }
6573 }
Selim Cinekfdc738f2016-01-27 20:04:27 -08006574 handleImage(view);
6575 // handle the content margin
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006576 int endMargin = R.dimen.notification_content_margin_end;
Selim Cinek279fa862016-06-14 10:57:25 -07006577 if (mBuilder.mN.hasLargeIcon()) {
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006578 endMargin = R.dimen.notification_content_plus_picture_margin_end;
Selim Cinekfdc738f2016-01-27 20:04:27 -08006579 }
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006580 view.setViewLayoutMarginEndDimen(R.id.notification_main_column, endMargin);
Dan Sandler842dd772014-05-15 09:36:47 -04006581 return view;
6582 }
6583
Selim Cinek99104832017-01-25 14:47:33 -08006584 private int getPrimaryHighlightColor() {
6585 return mBuilder.getPrimaryHighlightColor();
6586 }
6587
Dan Sandler842dd772014-05-15 09:36:47 -04006588 private RemoteViews makeMediaBigContentView() {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006589 final int actionCount = Math.min(mBuilder.mActions.size(), MAX_MEDIA_BUTTONS);
Selim Cinekcc10bfb2016-02-10 16:24:21 -08006590 // Dont add an expanded view if there is no more content to be revealed
6591 int actionsInCompact = mActionsToShowInCompact == null
6592 ? 0
6593 : Math.min(mActionsToShowInCompact.length, MAX_MEDIA_BUTTONS_IN_COMPACT);
Selim Cinek279fa862016-06-14 10:57:25 -07006594 if (!mBuilder.mN.hasLargeIcon() && actionCount <= actionsInCompact) {
Selim Cinekcc10bfb2016-02-10 16:24:21 -08006595 return null;
6596 }
Selim Cinek5bf069a2015-11-10 19:14:27 -05006597 RemoteViews big = mBuilder.applyStandardTemplate(
6598 R.layout.notification_template_material_big_media,
6599 false);
Dan Sandler842dd772014-05-15 09:36:47 -04006600
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006601 if (actionCount > 0) {
6602 big.removeAllViews(com.android.internal.R.id.media_actions);
6603 for (int i = 0; i < actionCount; i++) {
Selim Cinek5bf069a2015-11-10 19:14:27 -05006604 final RemoteViews button = generateMediaActionButton(mBuilder.mActions.get(i),
Selim Cinek99104832017-01-25 14:47:33 -08006605 getPrimaryHighlightColor());
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006606 big.addView(com.android.internal.R.id.media_actions, button);
Dan Sandler842dd772014-05-15 09:36:47 -04006607 }
6608 }
Selim Cinek5bf069a2015-11-10 19:14:27 -05006609 handleImage(big);
Dan Sandler842dd772014-05-15 09:36:47 -04006610 return big;
6611 }
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006612
Selim Cinek5bf069a2015-11-10 19:14:27 -05006613 private void handleImage(RemoteViews contentView) {
Selim Cinek279fa862016-06-14 10:57:25 -07006614 if (mBuilder.mN.hasLargeIcon()) {
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006615 contentView.setViewLayoutMarginEndDimen(R.id.line1, 0);
6616 contentView.setViewLayoutMarginEndDimen(R.id.text, 0);
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006617 }
6618 }
6619
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006620 /**
6621 * @hide
6622 */
6623 @Override
6624 protected boolean hasProgress() {
6625 return false;
6626 }
Dan Sandler842dd772014-05-15 09:36:47 -04006627 }
Griff Hazen61a9e862014-05-22 16:05:19 -07006628
Selim Cinek593610c2016-02-16 18:42:57 -08006629 /**
6630 * Notification style for custom views that are decorated by the system
6631 *
6632 * <p>Instead of providing a notification that is completely custom, a developer can set this
6633 * style and still obtain system decorations like the notification header with the expand
6634 * affordance and actions.
6635 *
6636 * <p>Use {@link android.app.Notification.Builder#setCustomContentView(RemoteViews)},
6637 * {@link android.app.Notification.Builder#setCustomBigContentView(RemoteViews)} and
6638 * {@link android.app.Notification.Builder#setCustomHeadsUpContentView(RemoteViews)} to set the
6639 * corresponding custom views to display.
6640 *
6641 * To use this style with your Notification, feed it to
6642 * {@link Notification.Builder#setStyle(android.app.Notification.Style)} like so:
6643 * <pre class="prettyprint">
6644 * Notification noti = new Notification.Builder()
6645 * .setSmallIcon(R.drawable.ic_stat_player)
6646 * .setLargeIcon(albumArtBitmap))
6647 * .setCustomContentView(contentView);
6648 * .setStyle(<b>new Notification.DecoratedCustomViewStyle()</b>)
6649 * .build();
6650 * </pre>
6651 */
6652 public static class DecoratedCustomViewStyle extends Style {
6653
6654 public DecoratedCustomViewStyle() {
6655 }
6656
Selim Cinek593610c2016-02-16 18:42:57 -08006657 /**
6658 * @hide
6659 */
6660 public boolean displayCustomViewInline() {
6661 return true;
6662 }
6663
6664 /**
6665 * @hide
6666 */
6667 @Override
Selim Cinek7d1009b2017-01-25 15:28:28 -08006668 public RemoteViews makeContentView(boolean increasedHeight) {
Selim Cinek593610c2016-02-16 18:42:57 -08006669 return makeStandardTemplateWithCustomContent(mBuilder.mN.contentView);
6670 }
6671
6672 /**
6673 * @hide
6674 */
6675 @Override
6676 public RemoteViews makeBigContentView() {
6677 return makeDecoratedBigContentView();
6678 }
6679
6680 /**
6681 * @hide
6682 */
6683 @Override
Selim Cinek87ed69b2017-02-09 15:59:43 -08006684 public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
Selim Cinek593610c2016-02-16 18:42:57 -08006685 return makeDecoratedHeadsUpContentView();
6686 }
6687
Selim Cinek593610c2016-02-16 18:42:57 -08006688 private RemoteViews makeDecoratedHeadsUpContentView() {
6689 RemoteViews headsUpContentView = mBuilder.mN.headsUpContentView == null
6690 ? mBuilder.mN.contentView
6691 : mBuilder.mN.headsUpContentView;
6692 if (mBuilder.mActions.size() == 0) {
6693 return makeStandardTemplateWithCustomContent(headsUpContentView);
6694 }
6695 RemoteViews remoteViews = mBuilder.applyStandardTemplateWithActions(
6696 mBuilder.getBigBaseLayoutResource());
Selim Cinek247fa012016-02-18 09:50:48 -08006697 buildIntoRemoteViewContent(remoteViews, headsUpContentView);
Selim Cinek593610c2016-02-16 18:42:57 -08006698 return remoteViews;
6699 }
6700
Selim Cinek593610c2016-02-16 18:42:57 -08006701 private RemoteViews makeStandardTemplateWithCustomContent(RemoteViews customContent) {
6702 RemoteViews remoteViews = mBuilder.applyStandardTemplate(
6703 mBuilder.getBaseLayoutResource());
Selim Cinek247fa012016-02-18 09:50:48 -08006704 buildIntoRemoteViewContent(remoteViews, customContent);
Selim Cinek593610c2016-02-16 18:42:57 -08006705 return remoteViews;
6706 }
6707
Selim Cinek593610c2016-02-16 18:42:57 -08006708 private RemoteViews makeDecoratedBigContentView() {
6709 RemoteViews bigContentView = mBuilder.mN.bigContentView == null
6710 ? mBuilder.mN.contentView
6711 : mBuilder.mN.bigContentView;
6712 if (mBuilder.mActions.size() == 0) {
6713 return makeStandardTemplateWithCustomContent(bigContentView);
6714 }
6715 RemoteViews remoteViews = mBuilder.applyStandardTemplateWithActions(
6716 mBuilder.getBigBaseLayoutResource());
Selim Cinek247fa012016-02-18 09:50:48 -08006717 buildIntoRemoteViewContent(remoteViews, bigContentView);
Selim Cinek593610c2016-02-16 18:42:57 -08006718 return remoteViews;
6719 }
Selim Cinek247fa012016-02-18 09:50:48 -08006720
6721 private void buildIntoRemoteViewContent(RemoteViews remoteViews,
6722 RemoteViews customContent) {
Adrian Roos5081c0d2016-02-26 16:04:19 -08006723 if (customContent != null) {
Selim Cinekf91017e2016-03-14 12:25:09 -07006724 // Need to clone customContent before adding, because otherwise it can no longer be
6725 // parceled independently of remoteViews.
Adrian Roos5081c0d2016-02-26 16:04:19 -08006726 customContent = customContent.clone();
Selim Cinekf91017e2016-03-14 12:25:09 -07006727 remoteViews.removeAllViews(R.id.notification_main_column);
6728 remoteViews.addView(R.id.notification_main_column, customContent);
Adrian Roos5081c0d2016-02-26 16:04:19 -08006729 }
Selim Cinek247fa012016-02-18 09:50:48 -08006730 // also update the end margin if there is an image
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006731 int endMargin = R.dimen.notification_content_margin_end;
Selim Cinek279fa862016-06-14 10:57:25 -07006732 if (mBuilder.mN.hasLargeIcon()) {
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006733 endMargin = R.dimen.notification_content_plus_picture_margin_end;
Selim Cinek247fa012016-02-18 09:50:48 -08006734 }
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006735 remoteViews.setViewLayoutMarginEndDimen(R.id.notification_main_column, endMargin);
Selim Cinek247fa012016-02-18 09:50:48 -08006736 }
Selim Cinek593610c2016-02-16 18:42:57 -08006737 }
6738
Selim Cinek03eb3b72016-02-18 10:39:45 -08006739 /**
6740 * Notification style for media custom views that are decorated by the system
6741 *
6742 * <p>Instead of providing a media notification that is completely custom, a developer can set
6743 * this style and still obtain system decorations like the notification header with the expand
6744 * affordance and actions.
6745 *
6746 * <p>Use {@link android.app.Notification.Builder#setCustomContentView(RemoteViews)},
6747 * {@link android.app.Notification.Builder#setCustomBigContentView(RemoteViews)} and
6748 * {@link android.app.Notification.Builder#setCustomHeadsUpContentView(RemoteViews)} to set the
6749 * corresponding custom views to display.
Selim Cinek99104832017-01-25 14:47:33 -08006750 * <p>
6751 * Contrary to {@link MediaStyle} a developer has to opt-in to the colorizing of the
6752 * notification by using {@link Notification.Builder#setColorized(boolean)}.
6753 * <p>
Selim Cinek03eb3b72016-02-18 10:39:45 -08006754 * To use this style with your Notification, feed it to
6755 * {@link Notification.Builder#setStyle(android.app.Notification.Style)} like so:
6756 * <pre class="prettyprint">
6757 * Notification noti = new Notification.Builder()
6758 * .setSmallIcon(R.drawable.ic_stat_player)
6759 * .setLargeIcon(albumArtBitmap))
6760 * .setCustomContentView(contentView);
6761 * .setStyle(<b>new Notification.DecoratedMediaCustomViewStyle()</b>
6762 * .setMediaSession(mySession))
6763 * .build();
6764 * </pre>
6765 *
6766 * @see android.app.Notification.DecoratedCustomViewStyle
6767 * @see android.app.Notification.MediaStyle
6768 */
6769 public static class DecoratedMediaCustomViewStyle extends MediaStyle {
6770
6771 public DecoratedMediaCustomViewStyle() {
6772 }
6773
Selim Cinek03eb3b72016-02-18 10:39:45 -08006774 /**
6775 * @hide
6776 */
6777 public boolean displayCustomViewInline() {
6778 return true;
6779 }
6780
6781 /**
6782 * @hide
6783 */
6784 @Override
Selim Cinek7d1009b2017-01-25 15:28:28 -08006785 public RemoteViews makeContentView(boolean increasedHeight) {
6786 RemoteViews remoteViews = super.makeContentView(false /* increasedHeight */);
Selim Cinek03eb3b72016-02-18 10:39:45 -08006787 return buildIntoRemoteView(remoteViews, R.id.notification_content_container,
6788 mBuilder.mN.contentView);
6789 }
6790
6791 /**
6792 * @hide
6793 */
6794 @Override
6795 public RemoteViews makeBigContentView() {
6796 RemoteViews customRemoteView = mBuilder.mN.bigContentView != null
6797 ? mBuilder.mN.bigContentView
6798 : mBuilder.mN.contentView;
6799 return makeBigContentViewWithCustomContent(customRemoteView);
6800 }
6801
6802 private RemoteViews makeBigContentViewWithCustomContent(RemoteViews customRemoteView) {
6803 RemoteViews remoteViews = super.makeBigContentView();
6804 if (remoteViews != null) {
6805 return buildIntoRemoteView(remoteViews, R.id.notification_main_column,
6806 customRemoteView);
6807 } else if (customRemoteView != mBuilder.mN.contentView){
Selim Cinek7d1009b2017-01-25 15:28:28 -08006808 remoteViews = super.makeContentView(false /* increasedHeight */);
Selim Cinek03eb3b72016-02-18 10:39:45 -08006809 return buildIntoRemoteView(remoteViews, R.id.notification_content_container,
6810 customRemoteView);
6811 } else {
6812 return null;
6813 }
6814 }
6815
6816 /**
6817 * @hide
6818 */
6819 @Override
Selim Cinek87ed69b2017-02-09 15:59:43 -08006820 public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
Selim Cinek03eb3b72016-02-18 10:39:45 -08006821 RemoteViews customRemoteView = mBuilder.mN.headsUpContentView != null
6822 ? mBuilder.mN.headsUpContentView
6823 : mBuilder.mN.contentView;
6824 return makeBigContentViewWithCustomContent(customRemoteView);
6825 }
6826
6827 private RemoteViews buildIntoRemoteView(RemoteViews remoteViews, int id,
6828 RemoteViews customContent) {
Adrian Roos5081c0d2016-02-26 16:04:19 -08006829 if (customContent != null) {
Selim Cinekf91017e2016-03-14 12:25:09 -07006830 // Need to clone customContent before adding, because otherwise it can no longer be
6831 // parceled independently of remoteViews.
Adrian Roos5081c0d2016-02-26 16:04:19 -08006832 customContent = customContent.clone();
Selim Cinekf91017e2016-03-14 12:25:09 -07006833 remoteViews.removeAllViews(id);
6834 remoteViews.addView(id, customContent);
Adrian Roos5081c0d2016-02-26 16:04:19 -08006835 }
Selim Cinek03eb3b72016-02-18 10:39:45 -08006836 return remoteViews;
6837 }
6838 }
6839
Christoph Studer4600f9b2014-07-22 22:44:43 +02006840 // When adding a new Style subclass here, don't forget to update
6841 // Builder.getNotificationStyleClass.
6842
Griff Hazen61a9e862014-05-22 16:05:19 -07006843 /**
6844 * Extender interface for use with {@link Builder#extend}. Extenders may be used to add
6845 * metadata or change options on a notification builder.
6846 */
6847 public interface Extender {
6848 /**
6849 * Apply this extender to a notification builder.
6850 * @param builder the builder to be modified.
6851 * @return the build object for chaining.
6852 */
6853 public Builder extend(Builder builder);
6854 }
6855
6856 /**
6857 * Helper class to add wearable extensions to notifications.
6858 * <p class="note"> See
6859 * <a href="{@docRoot}wear/notifications/creating.html">Creating Notifications
6860 * for Android Wear</a> for more information on how to use this class.
6861 * <p>
6862 * To create a notification with wearable extensions:
6863 * <ol>
6864 * <li>Create a {@link android.app.Notification.Builder}, setting any desired
6865 * properties.
6866 * <li>Create a {@link android.app.Notification.WearableExtender}.
6867 * <li>Set wearable-specific properties using the
6868 * {@code add} and {@code set} methods of {@link android.app.Notification.WearableExtender}.
6869 * <li>Call {@link android.app.Notification.Builder#extend} to apply the extensions to a
6870 * notification.
6871 * <li>Post the notification to the notification system with the
6872 * {@code NotificationManager.notify(...)} methods.
6873 * </ol>
6874 *
6875 * <pre class="prettyprint">
6876 * Notification notif = new Notification.Builder(mContext)
6877 * .setContentTitle(&quot;New mail from &quot; + sender.toString())
6878 * .setContentText(subject)
6879 * .setSmallIcon(R.drawable.new_mail)
6880 * .extend(new Notification.WearableExtender()
6881 * .setContentIcon(R.drawable.new_mail))
6882 * .build();
6883 * NotificationManager notificationManger =
6884 * (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
6885 * notificationManger.notify(0, notif);</pre>
6886 *
6887 * <p>Wearable extensions can be accessed on an existing notification by using the
6888 * {@code WearableExtender(Notification)} constructor,
6889 * and then using the {@code get} methods to access values.
6890 *
6891 * <pre class="prettyprint">
6892 * Notification.WearableExtender wearableExtender = new Notification.WearableExtender(
6893 * notification);
Griff Hazen14f57992014-05-26 09:07:14 -07006894 * List&lt;Notification&gt; pages = wearableExtender.getPages();</pre>
Griff Hazen61a9e862014-05-22 16:05:19 -07006895 */
6896 public static final class WearableExtender implements Extender {
6897 /**
6898 * Sentinel value for an action index that is unset.
6899 */
6900 public static final int UNSET_ACTION_INDEX = -1;
6901
6902 /**
6903 * Size value for use with {@link #setCustomSizePreset} to show this notification with
6904 * default sizing.
6905 * <p>For custom display notifications created using {@link #setDisplayIntent},
Paul Soulosaa4f4bf2015-08-04 11:59:45 -07006906 * the default is {@link #SIZE_MEDIUM}. All other notifications size automatically based
Griff Hazen61a9e862014-05-22 16:05:19 -07006907 * on their content.
6908 */
6909 public static final int SIZE_DEFAULT = 0;
6910
6911 /**
6912 * Size value for use with {@link #setCustomSizePreset} to show this notification
6913 * with an extra small size.
6914 * <p>This value is only applicable for custom display notifications created using
6915 * {@link #setDisplayIntent}.
6916 */
6917 public static final int SIZE_XSMALL = 1;
6918
6919 /**
6920 * Size value for use with {@link #setCustomSizePreset} to show this notification
6921 * with a small size.
6922 * <p>This value is only applicable for custom display notifications created using
6923 * {@link #setDisplayIntent}.
6924 */
6925 public static final int SIZE_SMALL = 2;
6926
6927 /**
6928 * Size value for use with {@link #setCustomSizePreset} to show this notification
6929 * with a medium size.
6930 * <p>This value is only applicable for custom display notifications created using
6931 * {@link #setDisplayIntent}.
6932 */
6933 public static final int SIZE_MEDIUM = 3;
6934
6935 /**
6936 * Size value for use with {@link #setCustomSizePreset} to show this notification
6937 * with a large size.
6938 * <p>This value is only applicable for custom display notifications created using
6939 * {@link #setDisplayIntent}.
6940 */
6941 public static final int SIZE_LARGE = 4;
6942
Griff Hazend5f11f92014-05-27 15:40:09 -07006943 /**
6944 * Size value for use with {@link #setCustomSizePreset} to show this notification
6945 * full screen.
6946 * <p>This value is only applicable for custom display notifications created using
6947 * {@link #setDisplayIntent}.
6948 */
6949 public static final int SIZE_FULL_SCREEN = 5;
6950
Griff Hazen5f2edfc2014-09-29 16:28:44 -07006951 /**
6952 * Sentinel value for use with {@link #setHintScreenTimeout} to keep the screen on for a
6953 * short amount of time when this notification is displayed on the screen. This
6954 * is the default value.
6955 */
6956 public static final int SCREEN_TIMEOUT_SHORT = 0;
6957
6958 /**
6959 * Sentinel value for use with {@link #setHintScreenTimeout} to keep the screen on
6960 * for a longer amount of time when this notification is displayed on the screen.
6961 */
6962 public static final int SCREEN_TIMEOUT_LONG = -1;
6963
Griff Hazen61a9e862014-05-22 16:05:19 -07006964 /** Notification extra which contains wearable extensions */
6965 private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS";
6966
Pete Gastaf6781d2014-10-07 15:17:05 -04006967 // Keys within EXTRA_WEARABLE_EXTENSIONS for wearable options.
Griff Hazen61a9e862014-05-22 16:05:19 -07006968 private static final String KEY_ACTIONS = "actions";
6969 private static final String KEY_FLAGS = "flags";
6970 private static final String KEY_DISPLAY_INTENT = "displayIntent";
6971 private static final String KEY_PAGES = "pages";
6972 private static final String KEY_BACKGROUND = "background";
6973 private static final String KEY_CONTENT_ICON = "contentIcon";
6974 private static final String KEY_CONTENT_ICON_GRAVITY = "contentIconGravity";
6975 private static final String KEY_CONTENT_ACTION_INDEX = "contentActionIndex";
6976 private static final String KEY_CUSTOM_SIZE_PRESET = "customSizePreset";
6977 private static final String KEY_CUSTOM_CONTENT_HEIGHT = "customContentHeight";
6978 private static final String KEY_GRAVITY = "gravity";
Griff Hazen5f2edfc2014-09-29 16:28:44 -07006979 private static final String KEY_HINT_SCREEN_TIMEOUT = "hintScreenTimeout";
Nadia Benbernou948627e2016-04-14 14:41:08 -04006980 private static final String KEY_DISMISSAL_ID = "dismissalId";
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04006981 private static final String KEY_BRIDGE_TAG = "bridgeTag";
Griff Hazen61a9e862014-05-22 16:05:19 -07006982
6983 // Flags bitwise-ored to mFlags
6984 private static final int FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE = 0x1;
6985 private static final int FLAG_HINT_HIDE_ICON = 1 << 1;
6986 private static final int FLAG_HINT_SHOW_BACKGROUND_ONLY = 1 << 2;
6987 private static final int FLAG_START_SCROLL_BOTTOM = 1 << 3;
Griff Hazen5f2edfc2014-09-29 16:28:44 -07006988 private static final int FLAG_HINT_AVOID_BACKGROUND_CLIPPING = 1 << 4;
Alex Hills4bcb06b2016-04-05 14:26:25 -04006989 private static final int FLAG_BIG_PICTURE_AMBIENT = 1 << 5;
Alex Hills9ab3a232016-04-05 14:54:56 -04006990 private static final int FLAG_HINT_CONTENT_INTENT_LAUNCHES_ACTIVITY = 1 << 6;
Griff Hazen61a9e862014-05-22 16:05:19 -07006991
6992 // Default value for flags integer
6993 private static final int DEFAULT_FLAGS = FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE;
6994
6995 private static final int DEFAULT_CONTENT_ICON_GRAVITY = Gravity.END;
6996 private static final int DEFAULT_GRAVITY = Gravity.BOTTOM;
6997
6998 private ArrayList<Action> mActions = new ArrayList<Action>();
6999 private int mFlags = DEFAULT_FLAGS;
7000 private PendingIntent mDisplayIntent;
7001 private ArrayList<Notification> mPages = new ArrayList<Notification>();
7002 private Bitmap mBackground;
7003 private int mContentIcon;
7004 private int mContentIconGravity = DEFAULT_CONTENT_ICON_GRAVITY;
7005 private int mContentActionIndex = UNSET_ACTION_INDEX;
7006 private int mCustomSizePreset = SIZE_DEFAULT;
7007 private int mCustomContentHeight;
7008 private int mGravity = DEFAULT_GRAVITY;
Griff Hazen5f2edfc2014-09-29 16:28:44 -07007009 private int mHintScreenTimeout;
Nadia Benbernou948627e2016-04-14 14:41:08 -04007010 private String mDismissalId;
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04007011 private String mBridgeTag;
Griff Hazen61a9e862014-05-22 16:05:19 -07007012
7013 /**
7014 * Create a {@link android.app.Notification.WearableExtender} with default
7015 * options.
7016 */
7017 public WearableExtender() {
7018 }
7019
7020 public WearableExtender(Notification notif) {
7021 Bundle wearableBundle = notif.extras.getBundle(EXTRA_WEARABLE_EXTENSIONS);
7022 if (wearableBundle != null) {
7023 List<Action> actions = wearableBundle.getParcelableArrayList(KEY_ACTIONS);
7024 if (actions != null) {
7025 mActions.addAll(actions);
7026 }
7027
7028 mFlags = wearableBundle.getInt(KEY_FLAGS, DEFAULT_FLAGS);
7029 mDisplayIntent = wearableBundle.getParcelable(KEY_DISPLAY_INTENT);
7030
7031 Notification[] pages = getNotificationArrayFromBundle(
7032 wearableBundle, KEY_PAGES);
7033 if (pages != null) {
7034 Collections.addAll(mPages, pages);
7035 }
7036
7037 mBackground = wearableBundle.getParcelable(KEY_BACKGROUND);
7038 mContentIcon = wearableBundle.getInt(KEY_CONTENT_ICON);
7039 mContentIconGravity = wearableBundle.getInt(KEY_CONTENT_ICON_GRAVITY,
7040 DEFAULT_CONTENT_ICON_GRAVITY);
7041 mContentActionIndex = wearableBundle.getInt(KEY_CONTENT_ACTION_INDEX,
7042 UNSET_ACTION_INDEX);
7043 mCustomSizePreset = wearableBundle.getInt(KEY_CUSTOM_SIZE_PRESET,
7044 SIZE_DEFAULT);
7045 mCustomContentHeight = wearableBundle.getInt(KEY_CUSTOM_CONTENT_HEIGHT);
7046 mGravity = wearableBundle.getInt(KEY_GRAVITY, DEFAULT_GRAVITY);
Griff Hazen5f2edfc2014-09-29 16:28:44 -07007047 mHintScreenTimeout = wearableBundle.getInt(KEY_HINT_SCREEN_TIMEOUT);
Nadia Benbernou948627e2016-04-14 14:41:08 -04007048 mDismissalId = wearableBundle.getString(KEY_DISMISSAL_ID);
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04007049 mBridgeTag = wearableBundle.getString(KEY_BRIDGE_TAG);
Griff Hazen61a9e862014-05-22 16:05:19 -07007050 }
7051 }
7052
7053 /**
7054 * Apply wearable extensions to a notification that is being built. This is typically
7055 * called by the {@link android.app.Notification.Builder#extend} method of
7056 * {@link android.app.Notification.Builder}.
7057 */
7058 @Override
7059 public Notification.Builder extend(Notification.Builder builder) {
7060 Bundle wearableBundle = new Bundle();
7061
7062 if (!mActions.isEmpty()) {
7063 wearableBundle.putParcelableArrayList(KEY_ACTIONS, mActions);
7064 }
7065 if (mFlags != DEFAULT_FLAGS) {
7066 wearableBundle.putInt(KEY_FLAGS, mFlags);
7067 }
7068 if (mDisplayIntent != null) {
7069 wearableBundle.putParcelable(KEY_DISPLAY_INTENT, mDisplayIntent);
7070 }
7071 if (!mPages.isEmpty()) {
7072 wearableBundle.putParcelableArray(KEY_PAGES, mPages.toArray(
7073 new Notification[mPages.size()]));
7074 }
7075 if (mBackground != null) {
7076 wearableBundle.putParcelable(KEY_BACKGROUND, mBackground);
7077 }
7078 if (mContentIcon != 0) {
7079 wearableBundle.putInt(KEY_CONTENT_ICON, mContentIcon);
7080 }
7081 if (mContentIconGravity != DEFAULT_CONTENT_ICON_GRAVITY) {
7082 wearableBundle.putInt(KEY_CONTENT_ICON_GRAVITY, mContentIconGravity);
7083 }
7084 if (mContentActionIndex != UNSET_ACTION_INDEX) {
7085 wearableBundle.putInt(KEY_CONTENT_ACTION_INDEX,
7086 mContentActionIndex);
7087 }
7088 if (mCustomSizePreset != SIZE_DEFAULT) {
7089 wearableBundle.putInt(KEY_CUSTOM_SIZE_PRESET, mCustomSizePreset);
7090 }
7091 if (mCustomContentHeight != 0) {
7092 wearableBundle.putInt(KEY_CUSTOM_CONTENT_HEIGHT, mCustomContentHeight);
7093 }
7094 if (mGravity != DEFAULT_GRAVITY) {
7095 wearableBundle.putInt(KEY_GRAVITY, mGravity);
7096 }
Griff Hazen5f2edfc2014-09-29 16:28:44 -07007097 if (mHintScreenTimeout != 0) {
7098 wearableBundle.putInt(KEY_HINT_SCREEN_TIMEOUT, mHintScreenTimeout);
7099 }
Nadia Benbernou948627e2016-04-14 14:41:08 -04007100 if (mDismissalId != null) {
7101 wearableBundle.putString(KEY_DISMISSAL_ID, mDismissalId);
7102 }
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04007103 if (mBridgeTag != null) {
7104 wearableBundle.putString(KEY_BRIDGE_TAG, mBridgeTag);
7105 }
Griff Hazen61a9e862014-05-22 16:05:19 -07007106
7107 builder.getExtras().putBundle(EXTRA_WEARABLE_EXTENSIONS, wearableBundle);
7108 return builder;
7109 }
7110
7111 @Override
7112 public WearableExtender clone() {
7113 WearableExtender that = new WearableExtender();
7114 that.mActions = new ArrayList<Action>(this.mActions);
7115 that.mFlags = this.mFlags;
7116 that.mDisplayIntent = this.mDisplayIntent;
7117 that.mPages = new ArrayList<Notification>(this.mPages);
7118 that.mBackground = this.mBackground;
7119 that.mContentIcon = this.mContentIcon;
7120 that.mContentIconGravity = this.mContentIconGravity;
7121 that.mContentActionIndex = this.mContentActionIndex;
7122 that.mCustomSizePreset = this.mCustomSizePreset;
7123 that.mCustomContentHeight = this.mCustomContentHeight;
7124 that.mGravity = this.mGravity;
Griff Hazen5f2edfc2014-09-29 16:28:44 -07007125 that.mHintScreenTimeout = this.mHintScreenTimeout;
Nadia Benbernou948627e2016-04-14 14:41:08 -04007126 that.mDismissalId = this.mDismissalId;
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04007127 that.mBridgeTag = this.mBridgeTag;
Griff Hazen61a9e862014-05-22 16:05:19 -07007128 return that;
7129 }
7130
7131 /**
7132 * Add a wearable action to this notification.
7133 *
7134 * <p>When wearable actions are added using this method, the set of actions that
7135 * show on a wearable device splits from devices that only show actions added
7136 * using {@link android.app.Notification.Builder#addAction}. This allows for customization
7137 * of which actions display on different devices.
7138 *
7139 * @param action the action to add to this notification
7140 * @return this object for method chaining
7141 * @see android.app.Notification.Action
7142 */
7143 public WearableExtender addAction(Action action) {
7144 mActions.add(action);
7145 return this;
7146 }
7147
7148 /**
7149 * Adds wearable actions to this notification.
7150 *
7151 * <p>When wearable actions are added using this method, the set of actions that
7152 * show on a wearable device splits from devices that only show actions added
7153 * using {@link android.app.Notification.Builder#addAction}. This allows for customization
7154 * of which actions display on different devices.
7155 *
7156 * @param actions the actions to add to this notification
7157 * @return this object for method chaining
7158 * @see android.app.Notification.Action
7159 */
7160 public WearableExtender addActions(List<Action> actions) {
7161 mActions.addAll(actions);
7162 return this;
7163 }
7164
7165 /**
7166 * Clear all wearable actions present on this builder.
7167 * @return this object for method chaining.
7168 * @see #addAction
7169 */
7170 public WearableExtender clearActions() {
7171 mActions.clear();
7172 return this;
7173 }
7174
7175 /**
7176 * Get the wearable actions present on this notification.
7177 */
7178 public List<Action> getActions() {
7179 return mActions;
7180 }
7181
7182 /**
7183 * Set an intent to launch inside of an activity view when displaying
Griff Hazen14f57992014-05-26 09:07:14 -07007184 * this notification. The {@link PendingIntent} provided should be for an activity.
7185 *
7186 * <pre class="prettyprint">
7187 * Intent displayIntent = new Intent(context, MyDisplayActivity.class);
7188 * PendingIntent displayPendingIntent = PendingIntent.getActivity(context,
7189 * 0, displayIntent, PendingIntent.FLAG_UPDATE_CURRENT);
7190 * Notification notif = new Notification.Builder(context)
7191 * .extend(new Notification.WearableExtender()
7192 * .setDisplayIntent(displayPendingIntent)
7193 * .setCustomSizePreset(Notification.WearableExtender.SIZE_MEDIUM))
7194 * .build();</pre>
7195 *
7196 * <p>The activity to launch needs to allow embedding, must be exported, and
Griff Hazen831ca9d2014-06-17 00:38:38 -07007197 * should have an empty task affinity. It is also recommended to use the device
7198 * default light theme.
Griff Hazen14f57992014-05-26 09:07:14 -07007199 *
7200 * <p>Example AndroidManifest.xml entry:
7201 * <pre class="prettyprint">
7202 * &lt;activity android:name=&quot;com.example.MyDisplayActivity&quot;
7203 * android:exported=&quot;true&quot;
7204 * android:allowEmbedded=&quot;true&quot;
Griff Hazen831ca9d2014-06-17 00:38:38 -07007205 * android:taskAffinity=&quot;&quot;
7206 * android:theme=&quot;@android:style/Theme.DeviceDefault.Light&quot; /&gt;</pre>
Griff Hazen61a9e862014-05-22 16:05:19 -07007207 *
7208 * @param intent the {@link PendingIntent} for an activity
7209 * @return this object for method chaining
7210 * @see android.app.Notification.WearableExtender#getDisplayIntent
7211 */
7212 public WearableExtender setDisplayIntent(PendingIntent intent) {
7213 mDisplayIntent = intent;
7214 return this;
7215 }
7216
7217 /**
7218 * Get the intent to launch inside of an activity view when displaying this
7219 * notification. This {@code PendingIntent} should be for an activity.
7220 */
7221 public PendingIntent getDisplayIntent() {
7222 return mDisplayIntent;
7223 }
7224
7225 /**
7226 * Add an additional page of content to display with this notification. The current
7227 * notification forms the first page, and pages added using this function form
7228 * subsequent pages. This field can be used to separate a notification into multiple
7229 * sections.
7230 *
7231 * @param page the notification to add as another page
7232 * @return this object for method chaining
7233 * @see android.app.Notification.WearableExtender#getPages
7234 */
7235 public WearableExtender addPage(Notification page) {
7236 mPages.add(page);
7237 return this;
7238 }
7239
7240 /**
7241 * Add additional pages of content to display with this notification. The current
7242 * notification forms the first page, and pages added using this function form
7243 * subsequent pages. This field can be used to separate a notification into multiple
7244 * sections.
7245 *
7246 * @param pages a list of notifications
7247 * @return this object for method chaining
7248 * @see android.app.Notification.WearableExtender#getPages
7249 */
7250 public WearableExtender addPages(List<Notification> pages) {
7251 mPages.addAll(pages);
7252 return this;
7253 }
7254
7255 /**
7256 * Clear all additional pages present on this builder.
7257 * @return this object for method chaining.
7258 * @see #addPage
7259 */
7260 public WearableExtender clearPages() {
7261 mPages.clear();
7262 return this;
7263 }
7264
7265 /**
7266 * Get the array of additional pages of content for displaying this notification. The
7267 * current notification forms the first page, and elements within this array form
7268 * subsequent pages. This field can be used to separate a notification into multiple
7269 * sections.
7270 * @return the pages for this notification
7271 */
7272 public List<Notification> getPages() {
7273 return mPages;
7274 }
7275
7276 /**
7277 * Set a background image to be displayed behind the notification content.
7278 * Contrary to the {@link android.app.Notification.BigPictureStyle}, this background
7279 * will work with any notification style.
7280 *
7281 * @param background the background bitmap
7282 * @return this object for method chaining
7283 * @see android.app.Notification.WearableExtender#getBackground
7284 */
7285 public WearableExtender setBackground(Bitmap background) {
7286 mBackground = background;
7287 return this;
7288 }
7289
7290 /**
7291 * Get a background image to be displayed behind the notification content.
7292 * Contrary to the {@link android.app.Notification.BigPictureStyle}, this background
7293 * will work with any notification style.
7294 *
7295 * @return the background image
7296 * @see android.app.Notification.WearableExtender#setBackground
7297 */
7298 public Bitmap getBackground() {
7299 return mBackground;
7300 }
7301
7302 /**
7303 * Set an icon that goes with the content of this notification.
7304 */
7305 public WearableExtender setContentIcon(int icon) {
7306 mContentIcon = icon;
7307 return this;
7308 }
7309
7310 /**
7311 * Get an icon that goes with the content of this notification.
7312 */
7313 public int getContentIcon() {
7314 return mContentIcon;
7315 }
7316
7317 /**
7318 * Set the gravity that the content icon should have within the notification display.
7319 * Supported values include {@link android.view.Gravity#START} and
7320 * {@link android.view.Gravity#END}. The default value is {@link android.view.Gravity#END}.
7321 * @see #setContentIcon
7322 */
7323 public WearableExtender setContentIconGravity(int contentIconGravity) {
7324 mContentIconGravity = contentIconGravity;
7325 return this;
7326 }
7327
7328 /**
7329 * Get the gravity that the content icon should have within the notification display.
7330 * Supported values include {@link android.view.Gravity#START} and
7331 * {@link android.view.Gravity#END}. The default value is {@link android.view.Gravity#END}.
7332 * @see #getContentIcon
7333 */
7334 public int getContentIconGravity() {
7335 return mContentIconGravity;
7336 }
7337
7338 /**
7339 * Set an action from this notification's actions to be clickable with the content of
Griff Hazen14f57992014-05-26 09:07:14 -07007340 * this notification. This action will no longer display separately from the
7341 * notification's content.
7342 *
Griff Hazenca48d352014-05-28 22:37:13 -07007343 * <p>For notifications with multiple pages, child pages can also have content actions
Griff Hazen14f57992014-05-26 09:07:14 -07007344 * set, although the list of available actions comes from the main notification and not
7345 * from the child page's notification.
7346 *
7347 * @param actionIndex The index of the action to hoist onto the current notification page.
7348 * If wearable actions were added to the main notification, this index
7349 * will apply to that list, otherwise it will apply to the regular
7350 * actions list.
Griff Hazen61a9e862014-05-22 16:05:19 -07007351 */
7352 public WearableExtender setContentAction(int actionIndex) {
7353 mContentActionIndex = actionIndex;
7354 return this;
7355 }
7356
7357 /**
Griff Hazenca48d352014-05-28 22:37:13 -07007358 * Get the index of the notification action, if any, that was specified as being clickable
7359 * with the content of this notification. This action will no longer display separately
Griff Hazen14f57992014-05-26 09:07:14 -07007360 * from the notification's content.
Griff Hazen61a9e862014-05-22 16:05:19 -07007361 *
Griff Hazenca48d352014-05-28 22:37:13 -07007362 * <p>For notifications with multiple pages, child pages can also have content actions
Griff Hazen14f57992014-05-26 09:07:14 -07007363 * set, although the list of available actions comes from the main notification and not
7364 * from the child page's notification.
7365 *
7366 * <p>If wearable specific actions were added to the main notification, this index will
7367 * apply to that list, otherwise it will apply to the regular actions list.
Griff Hazenca48d352014-05-28 22:37:13 -07007368 *
7369 * @return the action index or {@link #UNSET_ACTION_INDEX} if no action was selected.
Griff Hazen61a9e862014-05-22 16:05:19 -07007370 */
7371 public int getContentAction() {
7372 return mContentActionIndex;
7373 }
7374
7375 /**
7376 * Set the gravity that this notification should have within the available viewport space.
7377 * Supported values include {@link android.view.Gravity#TOP},
7378 * {@link android.view.Gravity#CENTER_VERTICAL} and {@link android.view.Gravity#BOTTOM}.
7379 * The default value is {@link android.view.Gravity#BOTTOM}.
7380 */
7381 public WearableExtender setGravity(int gravity) {
7382 mGravity = gravity;
7383 return this;
7384 }
7385
7386 /**
7387 * Get the gravity that this notification should have within the available viewport space.
7388 * Supported values include {@link android.view.Gravity#TOP},
7389 * {@link android.view.Gravity#CENTER_VERTICAL} and {@link android.view.Gravity#BOTTOM}.
7390 * The default value is {@link android.view.Gravity#BOTTOM}.
7391 */
7392 public int getGravity() {
7393 return mGravity;
7394 }
7395
7396 /**
7397 * Set the custom size preset for the display of this notification out of the available
7398 * presets found in {@link android.app.Notification.WearableExtender}, e.g.
7399 * {@link #SIZE_LARGE}.
7400 * <p>Some custom size presets are only applicable for custom display notifications created
7401 * using {@link android.app.Notification.WearableExtender#setDisplayIntent}. Check the
7402 * documentation for the preset in question. See also
7403 * {@link #setCustomContentHeight} and {@link #getCustomSizePreset}.
7404 */
7405 public WearableExtender setCustomSizePreset(int sizePreset) {
7406 mCustomSizePreset = sizePreset;
7407 return this;
7408 }
7409
7410 /**
7411 * Get the custom size preset for the display of this notification out of the available
7412 * presets found in {@link android.app.Notification.WearableExtender}, e.g.
7413 * {@link #SIZE_LARGE}.
7414 * <p>Some custom size presets are only applicable for custom display notifications created
7415 * using {@link #setDisplayIntent}. Check the documentation for the preset in question.
7416 * See also {@link #setCustomContentHeight} and {@link #setCustomSizePreset}.
7417 */
7418 public int getCustomSizePreset() {
7419 return mCustomSizePreset;
7420 }
7421
7422 /**
7423 * Set the custom height in pixels for the display of this notification's content.
7424 * <p>This option is only available for custom display notifications created
7425 * using {@link android.app.Notification.WearableExtender#setDisplayIntent}. See also
7426 * {@link android.app.Notification.WearableExtender#setCustomSizePreset} and
7427 * {@link #getCustomContentHeight}.
7428 */
7429 public WearableExtender setCustomContentHeight(int height) {
7430 mCustomContentHeight = height;
7431 return this;
7432 }
7433
7434 /**
7435 * Get the custom height in pixels for the display of this notification's content.
7436 * <p>This option is only available for custom display notifications created
7437 * using {@link #setDisplayIntent}. See also {@link #setCustomSizePreset} and
7438 * {@link #setCustomContentHeight}.
7439 */
7440 public int getCustomContentHeight() {
7441 return mCustomContentHeight;
7442 }
7443
7444 /**
7445 * Set whether the scrolling position for the contents of this notification should start
7446 * at the bottom of the contents instead of the top when the contents are too long to
7447 * display within the screen. Default is false (start scroll at the top).
7448 */
7449 public WearableExtender setStartScrollBottom(boolean startScrollBottom) {
7450 setFlag(FLAG_START_SCROLL_BOTTOM, startScrollBottom);
7451 return this;
7452 }
7453
7454 /**
7455 * Get whether the scrolling position for the contents of this notification should start
7456 * at the bottom of the contents instead of the top when the contents are too long to
7457 * display within the screen. Default is false (start scroll at the top).
7458 */
7459 public boolean getStartScrollBottom() {
7460 return (mFlags & FLAG_START_SCROLL_BOTTOM) != 0;
7461 }
7462
7463 /**
7464 * Set whether the content intent is available when the wearable device is not connected
7465 * to a companion device. The user can still trigger this intent when the wearable device
7466 * is offline, but a visual hint will indicate that the content intent may not be available.
7467 * Defaults to true.
7468 */
7469 public WearableExtender setContentIntentAvailableOffline(
7470 boolean contentIntentAvailableOffline) {
7471 setFlag(FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE, contentIntentAvailableOffline);
7472 return this;
7473 }
7474
7475 /**
7476 * Get whether the content intent is available when the wearable device is not connected
7477 * to a companion device. The user can still trigger this intent when the wearable device
7478 * is offline, but a visual hint will indicate that the content intent may not be available.
7479 * Defaults to true.
7480 */
7481 public boolean getContentIntentAvailableOffline() {
7482 return (mFlags & FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE) != 0;
7483 }
7484
7485 /**
7486 * Set a hint that this notification's icon should not be displayed.
7487 * @param hintHideIcon {@code true} to hide the icon, {@code false} otherwise.
7488 * @return this object for method chaining
7489 */
7490 public WearableExtender setHintHideIcon(boolean hintHideIcon) {
7491 setFlag(FLAG_HINT_HIDE_ICON, hintHideIcon);
7492 return this;
7493 }
7494
7495 /**
7496 * Get a hint that this notification's icon should not be displayed.
7497 * @return {@code true} if this icon should not be displayed, false otherwise.
7498 * The default value is {@code false} if this was never set.
7499 */
7500 public boolean getHintHideIcon() {
7501 return (mFlags & FLAG_HINT_HIDE_ICON) != 0;
7502 }
7503
7504 /**
7505 * Set a visual hint that only the background image of this notification should be
7506 * displayed, and other semantic content should be hidden. This hint is only applicable
7507 * to sub-pages added using {@link #addPage}.
7508 */
7509 public WearableExtender setHintShowBackgroundOnly(boolean hintShowBackgroundOnly) {
7510 setFlag(FLAG_HINT_SHOW_BACKGROUND_ONLY, hintShowBackgroundOnly);
7511 return this;
7512 }
7513
7514 /**
7515 * Get a visual hint that only the background image of this notification should be
7516 * displayed, and other semantic content should be hidden. This hint is only applicable
7517 * to sub-pages added using {@link android.app.Notification.WearableExtender#addPage}.
7518 */
7519 public boolean getHintShowBackgroundOnly() {
7520 return (mFlags & FLAG_HINT_SHOW_BACKGROUND_ONLY) != 0;
7521 }
7522
Griff Hazen5f2edfc2014-09-29 16:28:44 -07007523 /**
Griff Hazen9c5be4e2014-11-17 17:47:50 -08007524 * Set a hint that this notification's background should not be clipped if possible,
7525 * and should instead be resized to fully display on the screen, retaining the aspect
7526 * ratio of the image. This can be useful for images like barcodes or qr codes.
Griff Hazen5f2edfc2014-09-29 16:28:44 -07007527 * @param hintAvoidBackgroundClipping {@code true} to avoid clipping if possible.
7528 * @return this object for method chaining
7529 */
7530 public WearableExtender setHintAvoidBackgroundClipping(
7531 boolean hintAvoidBackgroundClipping) {
7532 setFlag(FLAG_HINT_AVOID_BACKGROUND_CLIPPING, hintAvoidBackgroundClipping);
7533 return this;
7534 }
7535
7536 /**
Griff Hazen9c5be4e2014-11-17 17:47:50 -08007537 * Get a hint that this notification's background should not be clipped if possible,
7538 * and should instead be resized to fully display on the screen, retaining the aspect
7539 * ratio of the image. This can be useful for images like barcodes or qr codes.
Griff Hazen5f2edfc2014-09-29 16:28:44 -07007540 * @return {@code true} if it's ok if the background is clipped on the screen, false
7541 * otherwise. The default value is {@code false} if this was never set.
7542 */
7543 public boolean getHintAvoidBackgroundClipping() {
7544 return (mFlags & FLAG_HINT_AVOID_BACKGROUND_CLIPPING) != 0;
7545 }
7546
7547 /**
7548 * Set a hint that the screen should remain on for at least this duration when
7549 * this notification is displayed on the screen.
7550 * @param timeout The requested screen timeout in milliseconds. Can also be either
7551 * {@link #SCREEN_TIMEOUT_SHORT} or {@link #SCREEN_TIMEOUT_LONG}.
7552 * @return this object for method chaining
7553 */
7554 public WearableExtender setHintScreenTimeout(int timeout) {
7555 mHintScreenTimeout = timeout;
7556 return this;
7557 }
7558
7559 /**
7560 * Get the duration, in milliseconds, that the screen should remain on for
7561 * when this notification is displayed.
7562 * @return the duration in milliseconds if > 0, or either one of the sentinel values
7563 * {@link #SCREEN_TIMEOUT_SHORT} or {@link #SCREEN_TIMEOUT_LONG}.
7564 */
7565 public int getHintScreenTimeout() {
7566 return mHintScreenTimeout;
7567 }
7568
Alex Hills9ab3a232016-04-05 14:54:56 -04007569 /**
Alex Hills4bcb06b2016-04-05 14:26:25 -04007570 * Set a hint that this notification's {@link BigPictureStyle} (if present) should be
7571 * converted to low-bit and displayed in ambient mode, especially useful for barcodes and
7572 * qr codes, as well as other simple black-and-white tickets.
7573 * @param hintAmbientBigPicture {@code true} to enable converstion and ambient.
7574 * @return this object for method chaining
7575 */
7576 public WearableExtender setHintAmbientBigPicture(boolean hintAmbientBigPicture) {
7577 setFlag(FLAG_BIG_PICTURE_AMBIENT, hintAmbientBigPicture);
7578 return this;
7579 }
7580
7581 /**
7582 * Get a hint that this notification's {@link BigPictureStyle} (if present) should be
7583 * converted to low-bit and displayed in ambient mode, especially useful for barcodes and
7584 * qr codes, as well as other simple black-and-white tickets.
7585 * @return {@code true} if it should be displayed in ambient, false otherwise
7586 * otherwise. The default value is {@code false} if this was never set.
7587 */
7588 public boolean getHintAmbientBigPicture() {
7589 return (mFlags & FLAG_BIG_PICTURE_AMBIENT) != 0;
7590 }
7591
7592 /**
Alex Hills9ab3a232016-04-05 14:54:56 -04007593 * Set a hint that this notification's content intent will launch an {@link Activity}
7594 * directly, telling the platform that it can generate the appropriate transitions.
7595 * @param hintContentIntentLaunchesActivity {@code true} if the content intent will launch
7596 * an activity and transitions should be generated, false otherwise.
7597 * @return this object for method chaining
7598 */
7599 public WearableExtender setHintContentIntentLaunchesActivity(
7600 boolean hintContentIntentLaunchesActivity) {
7601 setFlag(FLAG_HINT_CONTENT_INTENT_LAUNCHES_ACTIVITY, hintContentIntentLaunchesActivity);
7602 return this;
7603 }
7604
7605 /**
7606 * Get a hint that this notification's content intent will launch an {@link Activity}
7607 * directly, telling the platform that it can generate the appropriate transitions
7608 * @return {@code true} if the content intent will launch an activity and transitions should
7609 * be generated, false otherwise. The default value is {@code false} if this was never set.
7610 */
7611 public boolean getHintContentIntentLaunchesActivity() {
7612 return (mFlags & FLAG_HINT_CONTENT_INTENT_LAUNCHES_ACTIVITY) != 0;
7613 }
7614
Nadia Benbernou948627e2016-04-14 14:41:08 -04007615 /**
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04007616 * Sets the dismissal id for this notification. If a notification is posted with a
7617 * dismissal id, then when that notification is canceled, notifications on other wearables
7618 * and the paired Android phone having that same dismissal id will also be canceled. See
Nadia Benbernou948627e2016-04-14 14:41:08 -04007619 * <a href="{@docRoot}wear/notifications/index.html">Adding Wearable Features to
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04007620 * Notifications</a> for more information.
Nadia Benbernou948627e2016-04-14 14:41:08 -04007621 * @param dismissalId the dismissal id of the notification.
7622 * @return this object for method chaining
7623 */
7624 public WearableExtender setDismissalId(String dismissalId) {
7625 mDismissalId = dismissalId;
7626 return this;
7627 }
7628
7629 /**
7630 * Returns the dismissal id of the notification.
7631 * @return the dismissal id of the notification or null if it has not been set.
7632 */
7633 public String getDismissalId() {
7634 return mDismissalId;
7635 }
7636
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04007637 /**
7638 * Sets a bridge tag for this notification. A bridge tag can be set for notifications
7639 * posted from a phone to provide finer-grained control on what notifications are bridged
7640 * to wearables. See <a href="{@docRoot}wear/notifications/index.html">Adding Wearable
7641 * Features to Notifications</a> for more information.
7642 * @param bridgeTag the bridge tag of the notification.
7643 * @return this object for method chaining
7644 */
7645 public WearableExtender setBridgeTag(String bridgeTag) {
7646 mBridgeTag = bridgeTag;
7647 return this;
7648 }
7649
7650 /**
7651 * Returns the bridge tag of the notification.
7652 * @return the bridge tag or null if not present.
7653 */
7654 public String getBridgeTag() {
7655 return mBridgeTag;
7656 }
7657
Griff Hazen61a9e862014-05-22 16:05:19 -07007658 private void setFlag(int mask, boolean value) {
7659 if (value) {
7660 mFlags |= mask;
7661 } else {
7662 mFlags &= ~mask;
7663 }
7664 }
7665 }
7666
7667 /**
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08007668 * <p>Helper class to add Android Auto extensions to notifications. To create a notification
7669 * with car extensions:
7670 *
7671 * <ol>
7672 * <li>Create an {@link Notification.Builder}, setting any desired
7673 * properties.
7674 * <li>Create a {@link CarExtender}.
7675 * <li>Set car-specific properties using the {@code add} and {@code set} methods of
7676 * {@link CarExtender}.
7677 * <li>Call {@link Notification.Builder#extend(Notification.Extender)}
7678 * to apply the extensions to a notification.
7679 * </ol>
7680 *
7681 * <pre class="prettyprint">
7682 * Notification notification = new Notification.Builder(context)
7683 * ...
7684 * .extend(new CarExtender()
7685 * .set*(...))
7686 * .build();
7687 * </pre>
7688 *
7689 * <p>Car extensions can be accessed on an existing notification by using the
7690 * {@code CarExtender(Notification)} constructor, and then using the {@code get} methods
7691 * to access values.
7692 */
7693 public static final class CarExtender implements Extender {
7694 private static final String TAG = "CarExtender";
7695
7696 private static final String EXTRA_CAR_EXTENDER = "android.car.EXTENSIONS";
7697 private static final String EXTRA_LARGE_ICON = "large_icon";
7698 private static final String EXTRA_CONVERSATION = "car_conversation";
7699 private static final String EXTRA_COLOR = "app_color";
7700
7701 private Bitmap mLargeIcon;
7702 private UnreadConversation mUnreadConversation;
7703 private int mColor = Notification.COLOR_DEFAULT;
7704
7705 /**
7706 * Create a {@link CarExtender} with default options.
7707 */
7708 public CarExtender() {
7709 }
7710
7711 /**
7712 * Create a {@link CarExtender} from the CarExtender options of an existing Notification.
7713 *
7714 * @param notif The notification from which to copy options.
7715 */
7716 public CarExtender(Notification notif) {
7717 Bundle carBundle = notif.extras == null ?
7718 null : notif.extras.getBundle(EXTRA_CAR_EXTENDER);
7719 if (carBundle != null) {
7720 mLargeIcon = carBundle.getParcelable(EXTRA_LARGE_ICON);
7721 mColor = carBundle.getInt(EXTRA_COLOR, Notification.COLOR_DEFAULT);
7722
7723 Bundle b = carBundle.getBundle(EXTRA_CONVERSATION);
7724 mUnreadConversation = UnreadConversation.getUnreadConversationFromBundle(b);
7725 }
7726 }
7727
7728 /**
7729 * Apply car extensions to a notification that is being built. This is typically called by
7730 * the {@link Notification.Builder#extend(Notification.Extender)}
7731 * method of {@link Notification.Builder}.
7732 */
7733 @Override
7734 public Notification.Builder extend(Notification.Builder builder) {
7735 Bundle carExtensions = new Bundle();
7736
7737 if (mLargeIcon != null) {
7738 carExtensions.putParcelable(EXTRA_LARGE_ICON, mLargeIcon);
7739 }
7740 if (mColor != Notification.COLOR_DEFAULT) {
7741 carExtensions.putInt(EXTRA_COLOR, mColor);
7742 }
7743
7744 if (mUnreadConversation != null) {
7745 Bundle b = mUnreadConversation.getBundleForUnreadConversation();
7746 carExtensions.putBundle(EXTRA_CONVERSATION, b);
7747 }
7748
7749 builder.getExtras().putBundle(EXTRA_CAR_EXTENDER, carExtensions);
7750 return builder;
7751 }
7752
7753 /**
7754 * Sets the accent color to use when Android Auto presents the notification.
7755 *
7756 * Android Auto uses the color set with {@link Notification.Builder#setColor(int)}
7757 * to accent the displayed notification. However, not all colors are acceptable in an
7758 * automotive setting. This method can be used to override the color provided in the
7759 * notification in such a situation.
7760 */
Tor Norbye80756e32015-03-02 09:39:27 -08007761 public CarExtender setColor(@ColorInt int color) {
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08007762 mColor = color;
7763 return this;
7764 }
7765
7766 /**
7767 * Gets the accent color.
7768 *
Julia Reynoldsd9228f12015-10-20 10:37:27 -04007769 * @see #setColor
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08007770 */
Tor Norbye80756e32015-03-02 09:39:27 -08007771 @ColorInt
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08007772 public int getColor() {
7773 return mColor;
7774 }
7775
7776 /**
7777 * Sets the large icon of the car notification.
7778 *
7779 * If no large icon is set in the extender, Android Auto will display the icon
7780 * specified by {@link Notification.Builder#setLargeIcon(android.graphics.Bitmap)}
7781 *
7782 * @param largeIcon The large icon to use in the car notification.
7783 * @return This object for method chaining.
7784 */
7785 public CarExtender setLargeIcon(Bitmap largeIcon) {
7786 mLargeIcon = largeIcon;
7787 return this;
7788 }
7789
7790 /**
7791 * Gets the large icon used in this car notification, or null if no icon has been set.
7792 *
7793 * @return The large icon for the car notification.
7794 * @see CarExtender#setLargeIcon
7795 */
7796 public Bitmap getLargeIcon() {
7797 return mLargeIcon;
7798 }
7799
7800 /**
7801 * Sets the unread conversation in a message notification.
7802 *
7803 * @param unreadConversation The unread part of the conversation this notification conveys.
7804 * @return This object for method chaining.
7805 */
7806 public CarExtender setUnreadConversation(UnreadConversation unreadConversation) {
7807 mUnreadConversation = unreadConversation;
7808 return this;
7809 }
7810
7811 /**
7812 * Returns the unread conversation conveyed by this notification.
7813 * @see #setUnreadConversation(UnreadConversation)
7814 */
7815 public UnreadConversation getUnreadConversation() {
7816 return mUnreadConversation;
7817 }
7818
7819 /**
7820 * A class which holds the unread messages from a conversation.
7821 */
7822 public static class UnreadConversation {
7823 private static final String KEY_AUTHOR = "author";
7824 private static final String KEY_TEXT = "text";
7825 private static final String KEY_MESSAGES = "messages";
7826 private static final String KEY_REMOTE_INPUT = "remote_input";
7827 private static final String KEY_ON_REPLY = "on_reply";
7828 private static final String KEY_ON_READ = "on_read";
7829 private static final String KEY_PARTICIPANTS = "participants";
7830 private static final String KEY_TIMESTAMP = "timestamp";
7831
7832 private final String[] mMessages;
7833 private final RemoteInput mRemoteInput;
7834 private final PendingIntent mReplyPendingIntent;
7835 private final PendingIntent mReadPendingIntent;
7836 private final String[] mParticipants;
7837 private final long mLatestTimestamp;
7838
7839 UnreadConversation(String[] messages, RemoteInput remoteInput,
7840 PendingIntent replyPendingIntent, PendingIntent readPendingIntent,
7841 String[] participants, long latestTimestamp) {
7842 mMessages = messages;
7843 mRemoteInput = remoteInput;
7844 mReadPendingIntent = readPendingIntent;
7845 mReplyPendingIntent = replyPendingIntent;
7846 mParticipants = participants;
7847 mLatestTimestamp = latestTimestamp;
7848 }
7849
7850 /**
7851 * Gets the list of messages conveyed by this notification.
7852 */
7853 public String[] getMessages() {
7854 return mMessages;
7855 }
7856
7857 /**
7858 * Gets the remote input that will be used to convey the response to a message list, or
7859 * null if no such remote input exists.
7860 */
7861 public RemoteInput getRemoteInput() {
7862 return mRemoteInput;
7863 }
7864
7865 /**
7866 * Gets the pending intent that will be triggered when the user replies to this
7867 * notification.
7868 */
7869 public PendingIntent getReplyPendingIntent() {
7870 return mReplyPendingIntent;
7871 }
7872
7873 /**
7874 * Gets the pending intent that Android Auto will send after it reads aloud all messages
7875 * in this object's message list.
7876 */
7877 public PendingIntent getReadPendingIntent() {
7878 return mReadPendingIntent;
7879 }
7880
7881 /**
7882 * Gets the participants in the conversation.
7883 */
7884 public String[] getParticipants() {
7885 return mParticipants;
7886 }
7887
7888 /**
7889 * Gets the firs participant in the conversation.
7890 */
7891 public String getParticipant() {
7892 return mParticipants.length > 0 ? mParticipants[0] : null;
7893 }
7894
7895 /**
7896 * Gets the timestamp of the conversation.
7897 */
7898 public long getLatestTimestamp() {
7899 return mLatestTimestamp;
7900 }
7901
7902 Bundle getBundleForUnreadConversation() {
7903 Bundle b = new Bundle();
7904 String author = null;
7905 if (mParticipants != null && mParticipants.length > 1) {
7906 author = mParticipants[0];
7907 }
7908 Parcelable[] messages = new Parcelable[mMessages.length];
7909 for (int i = 0; i < messages.length; i++) {
7910 Bundle m = new Bundle();
7911 m.putString(KEY_TEXT, mMessages[i]);
7912 m.putString(KEY_AUTHOR, author);
7913 messages[i] = m;
7914 }
7915 b.putParcelableArray(KEY_MESSAGES, messages);
7916 if (mRemoteInput != null) {
7917 b.putParcelable(KEY_REMOTE_INPUT, mRemoteInput);
7918 }
7919 b.putParcelable(KEY_ON_REPLY, mReplyPendingIntent);
7920 b.putParcelable(KEY_ON_READ, mReadPendingIntent);
7921 b.putStringArray(KEY_PARTICIPANTS, mParticipants);
7922 b.putLong(KEY_TIMESTAMP, mLatestTimestamp);
7923 return b;
7924 }
7925
7926 static UnreadConversation getUnreadConversationFromBundle(Bundle b) {
7927 if (b == null) {
7928 return null;
7929 }
7930 Parcelable[] parcelableMessages = b.getParcelableArray(KEY_MESSAGES);
7931 String[] messages = null;
7932 if (parcelableMessages != null) {
7933 String[] tmp = new String[parcelableMessages.length];
7934 boolean success = true;
7935 for (int i = 0; i < tmp.length; i++) {
7936 if (!(parcelableMessages[i] instanceof Bundle)) {
7937 success = false;
7938 break;
7939 }
7940 tmp[i] = ((Bundle) parcelableMessages[i]).getString(KEY_TEXT);
7941 if (tmp[i] == null) {
7942 success = false;
7943 break;
7944 }
7945 }
7946 if (success) {
7947 messages = tmp;
7948 } else {
7949 return null;
7950 }
7951 }
7952
7953 PendingIntent onRead = b.getParcelable(KEY_ON_READ);
7954 PendingIntent onReply = b.getParcelable(KEY_ON_REPLY);
7955
7956 RemoteInput remoteInput = b.getParcelable(KEY_REMOTE_INPUT);
7957
7958 String[] participants = b.getStringArray(KEY_PARTICIPANTS);
7959 if (participants == null || participants.length != 1) {
7960 return null;
7961 }
7962
7963 return new UnreadConversation(messages,
7964 remoteInput,
7965 onReply,
7966 onRead,
7967 participants, b.getLong(KEY_TIMESTAMP));
7968 }
7969 };
7970
7971 /**
7972 * Builder class for {@link CarExtender.UnreadConversation} objects.
7973 */
7974 public static class Builder {
7975 private final List<String> mMessages = new ArrayList<String>();
7976 private final String mParticipant;
7977 private RemoteInput mRemoteInput;
7978 private PendingIntent mReadPendingIntent;
7979 private PendingIntent mReplyPendingIntent;
7980 private long mLatestTimestamp;
7981
7982 /**
7983 * Constructs a new builder for {@link CarExtender.UnreadConversation}.
7984 *
7985 * @param name The name of the other participant in the conversation.
7986 */
7987 public Builder(String name) {
7988 mParticipant = name;
7989 }
7990
7991 /**
7992 * Appends a new unread message to the list of messages for this conversation.
7993 *
7994 * The messages should be added from oldest to newest.
7995 *
7996 * @param message The text of the new unread message.
7997 * @return This object for method chaining.
7998 */
7999 public Builder addMessage(String message) {
8000 mMessages.add(message);
8001 return this;
8002 }
8003
8004 /**
8005 * Sets the pending intent and remote input which will convey the reply to this
8006 * notification.
8007 *
8008 * @param pendingIntent The pending intent which will be triggered on a reply.
8009 * @param remoteInput The remote input parcelable which will carry the reply.
8010 * @return This object for method chaining.
8011 *
8012 * @see CarExtender.UnreadConversation#getRemoteInput
8013 * @see CarExtender.UnreadConversation#getReplyPendingIntent
8014 */
8015 public Builder setReplyAction(
8016 PendingIntent pendingIntent, RemoteInput remoteInput) {
8017 mRemoteInput = remoteInput;
8018 mReplyPendingIntent = pendingIntent;
8019
8020 return this;
8021 }
8022
8023 /**
8024 * Sets the pending intent that will be sent once the messages in this notification
8025 * are read.
8026 *
8027 * @param pendingIntent The pending intent to use.
8028 * @return This object for method chaining.
8029 */
8030 public Builder setReadPendingIntent(PendingIntent pendingIntent) {
8031 mReadPendingIntent = pendingIntent;
8032 return this;
8033 }
8034
8035 /**
8036 * Sets the timestamp of the most recent message in an unread conversation.
8037 *
8038 * If a messaging notification has been posted by your application and has not
8039 * yet been cancelled, posting a later notification with the same id and tag
8040 * but without a newer timestamp may result in Android Auto not displaying a
8041 * heads up notification for the later notification.
8042 *
8043 * @param timestamp The timestamp of the most recent message in the conversation.
8044 * @return This object for method chaining.
8045 */
8046 public Builder setLatestTimestamp(long timestamp) {
8047 mLatestTimestamp = timestamp;
8048 return this;
8049 }
8050
8051 /**
8052 * Builds a new unread conversation object.
8053 *
8054 * @return The new unread conversation object.
8055 */
8056 public UnreadConversation build() {
8057 String[] messages = mMessages.toArray(new String[mMessages.size()]);
8058 String[] participants = { mParticipant };
8059 return new UnreadConversation(messages, mRemoteInput, mReplyPendingIntent,
8060 mReadPendingIntent, participants, mLatestTimestamp);
8061 }
8062 }
8063 }
8064
8065 /**
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08008066 * <p>Helper class to add Android TV extensions to notifications. To create a notification
8067 * with a TV extension:
8068 *
8069 * <ol>
8070 * <li>Create an {@link Notification.Builder}, setting any desired properties.
8071 * <li>Create a {@link TvExtender}.
8072 * <li>Set TV-specific properties using the {@code set} methods of
8073 * {@link TvExtender}.
8074 * <li>Call {@link Notification.Builder#extend(Notification.Extender)}
8075 * to apply the extension to a notification.
8076 * </ol>
8077 *
8078 * <pre class="prettyprint">
8079 * Notification notification = new Notification.Builder(context)
8080 * ...
8081 * .extend(new TvExtender()
8082 * .set*(...))
8083 * .build();
8084 * </pre>
8085 *
8086 * <p>TV extensions can be accessed on an existing notification by using the
8087 * {@code TvExtender(Notification)} constructor, and then using the {@code get} methods
8088 * to access values.
8089 *
8090 * @hide
8091 */
8092 @SystemApi
8093 public static final class TvExtender implements Extender {
8094 private static final String TAG = "TvExtender";
8095
8096 private static final String EXTRA_TV_EXTENDER = "android.tv.EXTENSIONS";
8097 private static final String EXTRA_FLAGS = "flags";
8098 private static final String EXTRA_CONTENT_INTENT = "content_intent";
8099 private static final String EXTRA_DELETE_INTENT = "delete_intent";
Dmitri Plotnikovb8a04ee2017-01-20 10:39:22 -08008100 private static final String EXTRA_CHANNEL_ID = "channel_id";
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08008101
8102 // Flags bitwise-ored to mFlags
8103 private static final int FLAG_AVAILABLE_ON_TV = 0x1;
8104
8105 private int mFlags;
Dmitri Plotnikovb8a04ee2017-01-20 10:39:22 -08008106 private String mChannelId;
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08008107 private PendingIntent mContentIntent;
8108 private PendingIntent mDeleteIntent;
8109
8110 /**
8111 * Create a {@link TvExtender} with default options.
8112 */
8113 public TvExtender() {
8114 mFlags = FLAG_AVAILABLE_ON_TV;
8115 }
8116
8117 /**
8118 * Create a {@link TvExtender} from the TvExtender options of an existing Notification.
8119 *
8120 * @param notif The notification from which to copy options.
8121 */
8122 public TvExtender(Notification notif) {
8123 Bundle bundle = notif.extras == null ?
8124 null : notif.extras.getBundle(EXTRA_TV_EXTENDER);
8125 if (bundle != null) {
8126 mFlags = bundle.getInt(EXTRA_FLAGS);
Dmitri Plotnikovb8a04ee2017-01-20 10:39:22 -08008127 mChannelId = bundle.getString(EXTRA_CHANNEL_ID);
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08008128 mContentIntent = bundle.getParcelable(EXTRA_CONTENT_INTENT);
8129 mDeleteIntent = bundle.getParcelable(EXTRA_DELETE_INTENT);
8130 }
8131 }
8132
8133 /**
8134 * Apply a TV extension to a notification that is being built. This is typically called by
8135 * the {@link Notification.Builder#extend(Notification.Extender)}
8136 * method of {@link Notification.Builder}.
8137 */
8138 @Override
8139 public Notification.Builder extend(Notification.Builder builder) {
8140 Bundle bundle = new Bundle();
8141
8142 bundle.putInt(EXTRA_FLAGS, mFlags);
Dmitri Plotnikovb8a04ee2017-01-20 10:39:22 -08008143 bundle.putString(EXTRA_CHANNEL_ID, mChannelId);
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08008144 if (mContentIntent != null) {
8145 bundle.putParcelable(EXTRA_CONTENT_INTENT, mContentIntent);
8146 }
8147
8148 if (mDeleteIntent != null) {
8149 bundle.putParcelable(EXTRA_DELETE_INTENT, mDeleteIntent);
8150 }
8151
8152 builder.getExtras().putBundle(EXTRA_TV_EXTENDER, bundle);
8153 return builder;
8154 }
8155
8156 /**
8157 * Returns true if this notification should be shown on TV. This method return true
8158 * if the notification was extended with a TvExtender.
8159 */
8160 public boolean isAvailableOnTv() {
8161 return (mFlags & FLAG_AVAILABLE_ON_TV) != 0;
8162 }
8163
8164 /**
Dmitri Plotnikovb8a04ee2017-01-20 10:39:22 -08008165 * Specifies the channel the notification should be delivered on when shown on TV.
8166 * It can be different from the channel that the notification is delivered to when
8167 * posting on a non-TV device.
8168 */
8169 public TvExtender setChannel(String channelId) {
8170 mChannelId = channelId;
8171 return this;
8172 }
8173
8174 /**
Julia Reynoldsbad42972017-04-25 13:52:49 -04008175 * Specifies the channel the notification should be delivered on when shown on TV.
8176 * It can be different from the channel that the notification is delivered to when
8177 * posting on a non-TV device.
8178 */
8179 public TvExtender setChannelId(String channelId) {
8180 mChannelId = channelId;
8181 return this;
8182 }
8183
Jeff Sharkey000ce802017-04-29 13:13:27 -06008184 /** @removed */
8185 @Deprecated
Dmitri Plotnikovb8a04ee2017-01-20 10:39:22 -08008186 public String getChannel() {
8187 return mChannelId;
8188 }
8189
8190 /**
Julia Reynoldsbad42972017-04-25 13:52:49 -04008191 * Returns the id of the channel this notification posts to on TV.
8192 */
8193 public String getChannelId() {
8194 return mChannelId;
8195 }
8196
8197 /**
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08008198 * Supplies a {@link PendingIntent} to be sent when the notification is selected on TV.
8199 * If provided, it is used instead of the content intent specified
8200 * at the level of Notification.
8201 */
8202 public TvExtender setContentIntent(PendingIntent intent) {
8203 mContentIntent = intent;
8204 return this;
8205 }
8206
8207 /**
8208 * Returns the TV-specific content intent. If this method returns null, the
8209 * main content intent on the notification should be used.
8210 *
8211 * @see {@link Notification#contentIntent}
8212 */
8213 public PendingIntent getContentIntent() {
8214 return mContentIntent;
8215 }
8216
8217 /**
8218 * Supplies a {@link PendingIntent} to send when the notification is cleared explicitly
8219 * by the user on TV. If provided, it is used instead of the delete intent specified
8220 * at the level of Notification.
8221 */
8222 public TvExtender setDeleteIntent(PendingIntent intent) {
8223 mDeleteIntent = intent;
8224 return this;
8225 }
8226
8227 /**
8228 * Returns the TV-specific delete intent. If this method returns null, the
8229 * main delete intent on the notification should be used.
8230 *
8231 * @see {@link Notification#deleteIntent}
8232 */
8233 public PendingIntent getDeleteIntent() {
8234 return mDeleteIntent;
8235 }
8236 }
8237
8238 /**
Griff Hazen61a9e862014-05-22 16:05:19 -07008239 * Get an array of Notification objects from a parcelable array bundle field.
8240 * Update the bundle to have a typed array so fetches in the future don't need
8241 * to do an array copy.
8242 */
8243 private static Notification[] getNotificationArrayFromBundle(Bundle bundle, String key) {
8244 Parcelable[] array = bundle.getParcelableArray(key);
8245 if (array instanceof Notification[] || array == null) {
8246 return (Notification[]) array;
8247 }
8248 Notification[] typedArray = Arrays.copyOf(array, array.length,
8249 Notification[].class);
8250 bundle.putParcelableArray(key, typedArray);
8251 return typedArray;
8252 }
Christoph Studer4600f9b2014-07-22 22:44:43 +02008253
8254 private static class BuilderRemoteViews extends RemoteViews {
8255 public BuilderRemoteViews(Parcel parcel) {
8256 super(parcel);
8257 }
8258
Kenny Guy77320062014-08-27 21:37:15 +01008259 public BuilderRemoteViews(ApplicationInfo appInfo, int layoutId) {
8260 super(appInfo, layoutId);
Christoph Studer4600f9b2014-07-22 22:44:43 +02008261 }
8262
8263 @Override
8264 public BuilderRemoteViews clone() {
8265 Parcel p = Parcel.obtain();
8266 writeToParcel(p, 0);
8267 p.setDataPosition(0);
8268 BuilderRemoteViews brv = new BuilderRemoteViews(p);
8269 p.recycle();
8270 return brv;
8271 }
8272 }
Adrian Roos70d7aa32017-01-11 15:39:06 -08008273
8274 private static class StandardTemplateParams {
8275 boolean hasProgress = true;
8276 boolean ambient = false;
8277 CharSequence title;
8278 CharSequence text;
8279
8280 final StandardTemplateParams reset() {
8281 hasProgress = true;
8282 ambient = false;
8283 title = null;
8284 text = null;
8285 return this;
8286 }
8287
8288 final StandardTemplateParams hasProgress(boolean hasProgress) {
8289 this.hasProgress = hasProgress;
8290 return this;
8291 }
8292
8293 final StandardTemplateParams title(CharSequence title) {
8294 this.title = title;
8295 return this;
8296 }
8297
8298 final StandardTemplateParams text(CharSequence text) {
8299 this.text = text;
8300 return this;
8301 }
8302
8303 final StandardTemplateParams ambient(boolean ambient) {
Adrian Roos0bc3f6a2017-03-06 11:54:05 -08008304 Preconditions.checkState(title == null && text == null, "must set ambient before text");
Adrian Roos70d7aa32017-01-11 15:39:06 -08008305 this.ambient = ambient;
8306 return this;
8307 }
8308
8309 final StandardTemplateParams fillTextsFrom(Builder b) {
8310 Bundle extras = b.mN.extras;
Adrian Roos0bc3f6a2017-03-06 11:54:05 -08008311 title = b.processLegacyText(extras.getCharSequence(EXTRA_TITLE), ambient);
8312 text = b.processLegacyText(extras.getCharSequence(EXTRA_TEXT), ambient);
Adrian Roos70d7aa32017-01-11 15:39:06 -08008313 return this;
8314 }
8315 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08008316}