blob: 1079b02b9d9187d0216ea898a194e1613f897b1c [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.app;
18
Selim Cinek389edcd2017-05-11 19:16:44 -070019import static com.android.internal.util.NotificationColorUtil.satisfiesTextContrast;
20
Tor Norbye80756e32015-03-02 09:39:27 -080021import android.annotation.ColorInt;
Tor Norbye7b9c9122013-05-30 16:48:33 -070022import android.annotation.DrawableRes;
Tor Norbyed9273d62013-05-30 15:59:53 -070023import android.annotation.IntDef;
Alex Hillsfd590442016-10-07 09:52:44 -040024import android.annotation.NonNull;
Jeff Sharkeyd86b8fe2017-06-02 17:36:26 -060025import android.annotation.RequiresPermission;
Daniel Sandler01df1c62014-06-09 10:54:01 -040026import android.annotation.SdkConstant;
27import android.annotation.SdkConstant.SdkConstantType;
Julia Reynoldse46bb372016-03-17 11:05:58 -040028import android.annotation.SystemApi;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080029import android.content.Context;
30import android.content.Intent;
Kenny Guy77320062014-08-27 21:37:15 +010031import android.content.pm.ApplicationInfo;
Dan Sandler732bd6c2016-04-12 14:20:32 -040032import android.content.pm.PackageManager;
Christoph Studer4600f9b2014-07-22 22:44:43 +020033import android.content.pm.PackageManager.NameNotFoundException;
Julia Reynolds13d898c2017-02-02 12:22:05 -050034import android.content.pm.ShortcutInfo;
Jorim Jaggief72a192014-08-26 21:57:46 +020035import android.content.res.ColorStateList;
Anthony Chenad4d1582017-04-10 16:07:58 -070036import android.content.res.Configuration;
37import android.content.res.Resources;
Joe Onoratoef1e7762010-09-17 18:38:38 -040038import android.graphics.Bitmap;
Kenny Guy8a0101b2014-05-08 23:34:12 +010039import android.graphics.Canvas;
Adrian Roosc1a80b02016-04-05 14:54:55 -070040import android.graphics.Color;
Jorim Jaggi5c2d8462014-03-21 17:37:00 +010041import android.graphics.PorterDuff;
Kenny Guy8a0101b2014-05-08 23:34:12 +010042import android.graphics.drawable.Drawable;
Dan Sandlerd63f9322015-05-06 15:18:49 -040043import android.graphics.drawable.Icon;
John Spurlockc0650f022014-07-19 13:22:39 -040044import android.media.AudioAttributes;
Jeff Sharkey098d5802012-04-26 17:30:34 -070045import android.media.AudioManager;
Jean-Michel Trivi2f7511f2016-11-28 15:40:27 -080046import android.media.PlayerBase;
Jeff Browndba34ba2014-06-24 20:46:03 -070047import android.media.session.MediaSession;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080048import android.net.Uri;
Daniel Sandlerdcbaf662013-04-26 16:23:09 -040049import android.os.BadParcelableException;
Christoph Studer239f8352014-08-25 15:13:18 +020050import android.os.Build;
Daniel Sandler2561b0b2012-02-13 21:04:12 -050051import android.os.Bundle;
Dianne Hackborn98305522017-05-05 17:53:53 -070052import android.os.IBinder;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080053import android.os.Parcel;
54import android.os.Parcelable;
Daniel Sandlera2985ed2012-04-03 16:42:00 -040055import android.os.SystemClock;
Selim Cinek6743c0b2017-01-18 18:24:01 -080056import android.os.SystemProperties;
Jeff Sharkey6d515712012-09-20 16:06:08 -070057import android.os.UserHandle;
Adrian Roosc1a80b02016-04-05 14:54:55 -070058import android.text.BidiFormatter;
Selim Cinek60a54252016-02-26 17:03:25 -080059import android.text.SpannableStringBuilder;
60import android.text.Spanned;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080061import android.text.TextUtils;
Selim Cinek60a54252016-02-26 17:03:25 -080062import android.text.style.AbsoluteSizeSpan;
Selim Cinek981962e2016-07-20 20:41:58 -070063import android.text.style.BackgroundColorSpan;
Selim Cinek89991a22016-03-07 19:51:54 -080064import android.text.style.CharacterStyle;
Selim Cinek981962e2016-07-20 20:41:58 -070065import android.text.style.ForegroundColorSpan;
Selim Cinek60a54252016-02-26 17:03:25 -080066import android.text.style.RelativeSizeSpan;
67import android.text.style.TextAppearanceSpan;
Svet Ganovddb94882016-06-23 19:55:24 -070068import android.util.ArraySet;
Daniel Sandlerdcbaf662013-04-26 16:23:09 -040069import android.util.Log;
Dan Sandler50128532015-12-08 15:42:41 -050070import android.util.SparseArray;
Griff Hazen61a9e862014-05-22 16:05:19 -070071import android.view.Gravity;
Selim Cinekea4bef72015-12-02 15:51:10 -080072import android.view.NotificationHeaderView;
Joe Onorato8595a3d2010-11-19 18:12:07 -080073import android.view.View;
Selim Cinek954cc232016-05-20 13:29:23 -070074import android.view.ViewGroup;
Jeff Sharkey1c400132011-08-05 14:50:13 -070075import android.widget.ProgressBar;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080076import android.widget.RemoteViews;
77
Griff Hazen959591e2014-05-15 22:26:18 -070078import com.android.internal.R;
Selim Cinek389edcd2017-05-11 19:16:44 -070079import com.android.internal.annotations.VisibleForTesting;
Svet Ganovddb94882016-06-23 19:55:24 -070080import com.android.internal.util.ArrayUtils;
Griff Hazenc091ba82014-05-16 10:13:26 -070081import com.android.internal.util.NotificationColorUtil;
Adrian Roos0bc3f6a2017-03-06 11:54:05 -080082import com.android.internal.util.Preconditions;
Griff Hazen959591e2014-05-15 22:26:18 -070083
Tor Norbyed9273d62013-05-30 15:59:53 -070084import java.lang.annotation.Retention;
85import java.lang.annotation.RetentionPolicy;
Christoph Studer4600f9b2014-07-22 22:44:43 +020086import java.lang.reflect.Constructor;
Daniel Sandler2561b0b2012-02-13 21:04:12 -050087import java.util.ArrayList;
Griff Hazen61a9e862014-05-22 16:05:19 -070088import java.util.Arrays;
Griff Hazen5cadc3b2014-05-20 09:55:39 -070089import java.util.Collections;
Griff Hazen61a9e862014-05-22 16:05:19 -070090import java.util.List;
Dan Sandler50128532015-12-08 15:42:41 -050091import java.util.Set;
Joe Onorato561d3852010-11-20 18:09:34 -080092
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080093/**
94 * A class that represents how a persistent notification is to be presented to
95 * the user using the {@link android.app.NotificationManager}.
96 *
Joe Onoratocb109a02011-01-18 17:57:41 -080097 * <p>The {@link Notification.Builder Notification.Builder} has been added to make it
98 * easier to construct Notifications.</p>
99 *
Joe Fernandez558459f2011-10-13 16:47:36 -0700100 * <div class="special reference">
101 * <h3>Developer Guides</h3>
102 * <p>For a guide to creating notifications, read the
103 * <a href="{@docRoot}guide/topics/ui/notifiers/notifications.html">Status Bar Notifications</a>
104 * developer guide.</p>
105 * </div>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800106 */
107public class Notification implements Parcelable
108{
Daniel Sandlerdcbaf662013-04-26 16:23:09 -0400109 private static final String TAG = "Notification";
110
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800111 /**
Daniel Sandler01df1c62014-06-09 10:54:01 -0400112 * An activity that provides a user interface for adjusting notification preferences for its
Julia Reynolds3aedded2017-03-31 14:42:09 -0400113 * containing application.
Daniel Sandler01df1c62014-06-09 10:54:01 -0400114 */
115 @SdkConstant(SdkConstantType.INTENT_CATEGORY)
116 public static final String INTENT_CATEGORY_NOTIFICATION_PREFERENCES
117 = "android.intent.category.NOTIFICATION_PREFERENCES";
118
119 /**
Julia Reynolds2619b5e2017-02-09 09:58:15 -0500120 * Optional extra for {@link #INTENT_CATEGORY_NOTIFICATION_PREFERENCES}. If provided, will
121 * contain a {@link NotificationChannel#getId() channel id} that can be used to narrow down
Julia Reynolds3aedded2017-03-31 14:42:09 -0400122 * what settings should be shown in the target app.
Julia Reynolds2619b5e2017-02-09 09:58:15 -0500123 */
124 public static final String EXTRA_CHANNEL_ID = "android.intent.extra.CHANNEL_ID";
125
126 /**
Julia Reynolds3aedded2017-03-31 14:42:09 -0400127 * Optional extra for {@link #INTENT_CATEGORY_NOTIFICATION_PREFERENCES}. If provided, will
128 * contain the tag provided to {@link NotificationManager#notify(String, int, Notification)}
129 * that can be used to narrow down what settings should be shown in the target app.
130 */
131 public static final String EXTRA_NOTIFICATION_TAG = "android.intent.extra.NOTIFICATION_TAG";
132
133 /**
134 * Optional extra for {@link #INTENT_CATEGORY_NOTIFICATION_PREFERENCES}. If provided, will
135 * contain the id provided to {@link NotificationManager#notify(String, int, Notification)}
136 * that can be used to narrow down what settings should be shown in the target app.
137 */
138 public static final String EXTRA_NOTIFICATION_ID = "android.intent.extra.NOTIFICATION_ID";
139
140 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800141 * Use all default values (where applicable).
142 */
143 public static final int DEFAULT_ALL = ~0;
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500144
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800145 /**
146 * Use the default notification sound. This will ignore any given
147 * {@link #sound}.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500148 *
Chris Wren47c20a12014-06-18 17:27:29 -0400149 * <p>
150 * A notification that is noisy is more likely to be presented as a heads-up notification.
151 * </p>
152 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800153 * @see #defaults
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500154 */
155
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800156 public static final int DEFAULT_SOUND = 1;
157
158 /**
159 * Use the default notification vibrate. This will ignore any given
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500160 * {@link #vibrate}. Using phone vibration requires the
Scott Mainb8b36452009-04-26 15:50:49 -0700161 * {@link android.Manifest.permission#VIBRATE VIBRATE} permission.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500162 *
Chris Wren47c20a12014-06-18 17:27:29 -0400163 * <p>
164 * A notification that vibrates is more likely to be presented as a heads-up notification.
165 * </p>
166 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800167 * @see #defaults
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500168 */
169
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800170 public static final int DEFAULT_VIBRATE = 2;
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500171
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800172 /**
173 * Use the default notification lights. This will ignore the
174 * {@link #FLAG_SHOW_LIGHTS} bit, and {@link #ledARGB}, {@link #ledOffMS}, or
175 * {@link #ledOnMS}.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500176 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800177 * @see #defaults
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500178 */
179
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800180 public static final int DEFAULT_LIGHTS = 4;
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500181
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800182 /**
Christoph Studer535ec612014-09-03 15:47:47 +0200183 * Maximum length of CharSequences accepted by Builder and friends.
184 *
185 * <p>
186 * Avoids spamming the system with overly large strings such as full e-mails.
187 */
188 private static final int MAX_CHARSEQUENCE_LENGTH = 5 * 1024;
189
190 /**
Adrian Roose458aa82015-12-08 16:17:19 -0800191 * Maximum entries of reply text that are accepted by Builder and friends.
192 */
193 private static final int MAX_REPLY_HISTORY = 5;
194
195 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500196 * A timestamp related to this notification, in milliseconds since the epoch.
Joe Malin8d40d042012-11-05 11:36:40 -0800197 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500198 * Default value: {@link System#currentTimeMillis() Now}.
199 *
200 * Choose a timestamp that will be most relevant to the user. For most finite events, this
201 * corresponds to the time the event happened (or will happen, in the case of events that have
202 * yet to occur but about which the user is being informed). Indefinite events should be
Joe Malin8d40d042012-11-05 11:36:40 -0800203 * timestamped according to when the activity began.
204 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500205 * Some examples:
Joe Malin8d40d042012-11-05 11:36:40 -0800206 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500207 * <ul>
208 * <li>Notification of a new chat message should be stamped when the message was received.</li>
209 * <li>Notification of an ongoing file download (with a progress bar, for example) should be stamped when the download started.</li>
210 * <li>Notification of a completed file download should be stamped when the download finished.</li>
211 * <li>Notification of an upcoming meeting should be stamped with the time the meeting will begin (that is, in the future).</li>
212 * <li>Notification of an ongoing stopwatch (increasing timer) should be stamped with the watch's start time.
213 * <li>Notification of an ongoing countdown timer should be stamped with the timer's end time.
Joe Malin8d40d042012-11-05 11:36:40 -0800214 * </ul>
215 *
Selim Cinek0ff1ce602016-04-05 18:27:16 -0700216 * For apps targeting {@link android.os.Build.VERSION_CODES#N} and above, this time is not shown
217 * anymore by default and must be opted into by using
218 * {@link android.app.Notification.Builder#setShowWhen(boolean)}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800219 */
220 public long when;
221
222 /**
Selim Cinekb85f36fd2016-04-20 18:46:36 -0700223 * The creation time of the notification
224 */
225 private long creationTime;
226
227 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800228 * The resource id of a drawable to use as the icon in the status bar.
Dan Sandler86647982015-05-13 23:41:13 -0400229 *
230 * @deprecated Use {@link Builder#setSmallIcon(Icon)} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800231 */
Dan Sandler86647982015-05-13 23:41:13 -0400232 @Deprecated
Tor Norbye7b9c9122013-05-30 16:48:33 -0700233 @DrawableRes
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800234 public int icon;
235
236 /**
Joe Onorato46439ce2010-11-19 13:56:21 -0800237 * If the icon in the status bar is to have more than one level, you can set this. Otherwise,
238 * leave it at its default value of 0.
239 *
240 * @see android.widget.ImageView#setImageLevel
Griff Hazen959591e2014-05-15 22:26:18 -0700241 * @see android.graphics.drawable.Drawable#setLevel
Joe Onorato46439ce2010-11-19 13:56:21 -0800242 */
243 public int iconLevel;
244
245 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500246 * The number of events that this notification represents. For example, in a new mail
247 * notification, this could be the number of unread messages.
Joe Malin8d40d042012-11-05 11:36:40 -0800248 *
Julia Reynolds30331982017-04-27 10:12:50 -0400249 * The system may or may not use this field to modify the appearance of the notification.
Julia Reynolds13d898c2017-02-02 12:22:05 -0500250 * Starting with {@link android.os.Build.VERSION_CODES#O}, the number may be displayed as a
251 * badge icon in Launchers that support badging.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800252 */
Julia Reynolds30331982017-04-27 10:12:50 -0400253 public int number = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800254
255 /**
256 * The intent to execute when the expanded status entry is clicked. If
257 * this is an activity, it must include the
258 * {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} flag, which requires
Scott Main7aee61f2011-02-08 11:25:01 -0800259 * that you take care of task management as described in the
260 * <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back
Dianne Hackborn6ceca582012-01-10 15:24:26 -0800261 * Stack</a> document. In particular, make sure to read the notification section
262 * <a href="{@docRoot}guide/topics/ui/notifiers/notifications.html#HandlingNotifications">Handling
263 * Notifications</a> for the correct ways to launch an application from a
264 * notification.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800265 */
266 public PendingIntent contentIntent;
267
268 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500269 * The intent to execute when the notification is explicitly dismissed by the user, either with
270 * the "Clear All" button or by swiping it away individually.
271 *
272 * This probably shouldn't be launching an activity since several of those will be sent
273 * at the same time.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800274 */
275 public PendingIntent deleteIntent;
276
277 /**
Dianne Hackborn170bae72010-09-03 15:14:28 -0700278 * An intent to launch instead of posting the notification to the status bar.
Joe Onoratocb109a02011-01-18 17:57:41 -0800279 *
Chris Wren47c20a12014-06-18 17:27:29 -0400280 * <p>
281 * The system UI may choose to display a heads-up notification, instead of
282 * launching this intent, while the user is using the device.
283 * </p>
284 *
Joe Onoratocb109a02011-01-18 17:57:41 -0800285 * @see Notification.Builder#setFullScreenIntent
Daniel Sandlere46cbd32010-06-17 10:35:26 -0400286 */
287 public PendingIntent fullScreenIntent;
288
289 /**
Dan Sandler5fcdf6e2014-07-18 11:31:15 -0400290 * Text that summarizes this notification for accessibility services.
291 *
292 * As of the L release, this text is no longer shown on screen, but it is still useful to
293 * accessibility services (where it serves as an audible announcement of the notification's
294 * appearance).
Joe Onoratoef1e7762010-09-17 18:38:38 -0400295 *
Joe Onorato46439ce2010-11-19 13:56:21 -0800296 * @see #tickerView
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800297 */
298 public CharSequence tickerText;
299
300 /**
Dan Sandler5fcdf6e2014-07-18 11:31:15 -0400301 * Formerly, a view showing the {@link #tickerText}.
302 *
303 * No longer displayed in the status bar as of API 21.
Joe Onoratoef1e7762010-09-17 18:38:38 -0400304 */
Dan Sandler5fcdf6e2014-07-18 11:31:15 -0400305 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -0800306 public RemoteViews tickerView;
Joe Onoratoef1e7762010-09-17 18:38:38 -0400307
308 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400309 * The view that will represent this notification in the notification list (which is pulled
310 * down from the status bar).
311 *
Julia Reynoldsd4ea7412016-02-17 14:00:56 -0500312 * As of N, this field may be null. The notification view is determined by the inputs
313 * to {@link Notification.Builder}; a custom RemoteViews can optionally be
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400314 * supplied with {@link Notification.Builder#setCustomContentView(RemoteViews)}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800315 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400316 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800317 public RemoteViews contentView;
318
Daniel Sandlera0a938c2012-03-15 08:42:37 -0400319 /**
Daniel Sandler4dfbe832012-04-11 14:51:46 -0400320 * A large-format version of {@link #contentView}, giving the Notification an
Daniel Sandlerf3b73432012-03-27 15:01:25 -0400321 * opportunity to show more detail. The system UI may choose to show this
322 * instead of the normal content view at its discretion.
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400323 *
Julia Reynoldsd4ea7412016-02-17 14:00:56 -0500324 * As of N, this field may be null. The expanded notification view is determined by the
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400325 * inputs to {@link Notification.Builder}; a custom RemoteViews can optionally be
326 * supplied with {@link Notification.Builder#setCustomBigContentView(RemoteViews)}.
Daniel Sandlerf3b73432012-03-27 15:01:25 -0400327 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400328 @Deprecated
Daniel Sandlerf3b73432012-03-27 15:01:25 -0400329 public RemoteViews bigContentView;
330
Chris Wren8fd39ec2014-02-27 17:43:26 -0500331
332 /**
Chris Wren47c20a12014-06-18 17:27:29 -0400333 * A medium-format version of {@link #contentView}, providing the Notification an
334 * opportunity to add action buttons to contentView. At its discretion, the system UI may
335 * choose to show this as a heads-up notification, which will pop up so the user can see
336 * it without leaving their current activity.
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400337 *
Julia Reynoldsd4ea7412016-02-17 14:00:56 -0500338 * As of N, this field may be null. The heads-up notification view is determined by the
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400339 * inputs to {@link Notification.Builder}; a custom RemoteViews can optionally be
340 * supplied with {@link Notification.Builder#setCustomHeadsUpContentView(RemoteViews)}.
Chris Wren8fd39ec2014-02-27 17:43:26 -0500341 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400342 @Deprecated
Chris Wren8fd39ec2014-02-27 17:43:26 -0500343 public RemoteViews headsUpContentView;
344
Daniel Sandlerf3b73432012-03-27 15:01:25 -0400345 /**
Dan Sandler86647982015-05-13 23:41:13 -0400346 * A large bitmap to be shown in the notification content area.
347 *
348 * @deprecated Use {@link Builder#setLargeIcon(Icon)} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800349 */
Dan Sandler86647982015-05-13 23:41:13 -0400350 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -0800351 public Bitmap largeIcon;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800352
353 /**
354 * The sound to play.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500355 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800356 * <p>
Chris Wren47c20a12014-06-18 17:27:29 -0400357 * A notification that is noisy is more likely to be presented as a heads-up notification.
358 * </p>
359 *
360 * <p>
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500361 * To play the default notification sound, see {@link #defaults}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800362 * </p>
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500363 * @deprecated use {@link NotificationChannel#getSound()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800364 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500365 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800366 public Uri sound;
367
368 /**
369 * Use this constant as the value for audioStreamType to request that
370 * the default stream type for notifications be used. Currently the
Jeff Sharkey098d5802012-04-26 17:30:34 -0700371 * default stream type is {@link AudioManager#STREAM_NOTIFICATION}.
John Spurlockc0650f022014-07-19 13:22:39 -0400372 *
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500373 * @deprecated Use {@link NotificationChannel#getAudioAttributes()} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800374 */
Jean-Michel Trivi81f871e2014-08-06 16:32:38 -0700375 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800376 public static final int STREAM_DEFAULT = -1;
377
378 /**
379 * The audio stream type to use when playing the sound.
380 * Should be one of the STREAM_ constants from
381 * {@link android.media.AudioManager}.
John Spurlockc0650f022014-07-19 13:22:39 -0400382 *
383 * @deprecated Use {@link #audioAttributes} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800384 */
Jean-Michel Trivi81f871e2014-08-06 16:32:38 -0700385 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800386 public int audioStreamType = STREAM_DEFAULT;
387
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800388 /**
John Spurlockc0650f022014-07-19 13:22:39 -0400389 * The default value of {@link #audioAttributes}.
390 */
391 public static final AudioAttributes AUDIO_ATTRIBUTES_DEFAULT = new AudioAttributes.Builder()
392 .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
393 .setUsage(AudioAttributes.USAGE_NOTIFICATION)
394 .build();
395
396 /**
397 * The {@link AudioAttributes audio attributes} to use when playing the sound.
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500398 *
399 * @deprecated use {@link NotificationChannel#getAudioAttributes()} instead.
John Spurlockc0650f022014-07-19 13:22:39 -0400400 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500401 @Deprecated
John Spurlockc0650f022014-07-19 13:22:39 -0400402 public AudioAttributes audioAttributes = AUDIO_ATTRIBUTES_DEFAULT;
403
404 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500405 * The pattern with which to vibrate.
406 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800407 * <p>
408 * To vibrate the default pattern, see {@link #defaults}.
409 * </p>
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500410 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800411 * @see android.os.Vibrator#vibrate(long[],int)
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500412 * @deprecated use {@link NotificationChannel#getVibrationPattern()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800413 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500414 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800415 public long[] vibrate;
416
417 /**
418 * The color of the led. The hardware will do its best approximation.
419 *
420 * @see #FLAG_SHOW_LIGHTS
421 * @see #flags
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500422 * @deprecated use {@link NotificationChannel#shouldShowLights()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800423 */
Tor Norbye80756e32015-03-02 09:39:27 -0800424 @ColorInt
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500425 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800426 public int ledARGB;
427
428 /**
429 * The number of milliseconds for the LED to be on while it's flashing.
430 * The hardware will do its best approximation.
431 *
432 * @see #FLAG_SHOW_LIGHTS
433 * @see #flags
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500434 * @deprecated use {@link NotificationChannel#shouldShowLights()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800435 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500436 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800437 public int ledOnMS;
438
439 /**
440 * The number of milliseconds for the LED to be off while it's flashing.
441 * The hardware will do its best approximation.
442 *
443 * @see #FLAG_SHOW_LIGHTS
444 * @see #flags
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500445 *
446 * @deprecated use {@link NotificationChannel#shouldShowLights()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800447 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500448 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800449 public int ledOffMS;
450
451 /**
452 * Specifies which values should be taken from the defaults.
453 * <p>
454 * To set, OR the desired from {@link #DEFAULT_SOUND},
455 * {@link #DEFAULT_VIBRATE}, {@link #DEFAULT_LIGHTS}. For all default
456 * values, use {@link #DEFAULT_ALL}.
457 * </p>
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500458 *
459 * @deprecated use {@link NotificationChannel#getSound()} and
460 * {@link NotificationChannel#shouldShowLights()} and
461 * {@link NotificationChannel#shouldVibrate()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800462 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500463 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800464 public int defaults;
465
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800466 /**
467 * Bit to be bitwise-ored into the {@link #flags} field that should be
468 * set if you want the LED on for this notification.
469 * <ul>
470 * <li>To turn the LED off, pass 0 in the alpha channel for colorARGB
471 * or 0 for both ledOnMS and ledOffMS.</li>
472 * <li>To turn the LED on, pass 1 for ledOnMS and 0 for ledOffMS.</li>
473 * <li>To flash the LED, pass the number of milliseconds that it should
474 * be on and off to ledOnMS and ledOffMS.</li>
475 * </ul>
476 * <p>
477 * Since hardware varies, you are not guaranteed that any of the values
478 * you pass are honored exactly. Use the system defaults (TODO) if possible
479 * because they will be set to values that work on any given hardware.
480 * <p>
481 * The alpha channel must be set for forward compatibility.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500482 *
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500483 * @deprecated use {@link NotificationChannel#shouldShowLights()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800484 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500485 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800486 public static final int FLAG_SHOW_LIGHTS = 0x00000001;
487
488 /**
489 * Bit to be bitwise-ored into the {@link #flags} field that should be
490 * set if this notification is in reference to something that is ongoing,
491 * like a phone call. It should not be set if this notification is in
492 * reference to something that happened at a particular point in time,
493 * like a missed phone call.
494 */
495 public static final int FLAG_ONGOING_EVENT = 0x00000002;
496
497 /**
498 * Bit to be bitwise-ored into the {@link #flags} field that if set,
Scott Mainb8b36452009-04-26 15:50:49 -0700499 * the audio will be repeated until the notification is
500 * cancelled or the notification window is opened.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800501 */
502 public static final int FLAG_INSISTENT = 0x00000004;
503
504 /**
505 * Bit to be bitwise-ored into the {@link #flags} field that should be
Griff Hazen293977b2014-04-28 08:37:20 -0700506 * set if you would only like the sound, vibrate and ticker to be played
507 * if the notification was not already showing.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800508 */
509 public static final int FLAG_ONLY_ALERT_ONCE = 0x00000008;
510
511 /**
512 * Bit to be bitwise-ored into the {@link #flags} field that should be
513 * set if the notification should be canceled when it is clicked by the
Daniel Sandler8aa9ae62012-12-04 23:31:47 -0500514 * user.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800515 */
516 public static final int FLAG_AUTO_CANCEL = 0x00000010;
517
518 /**
519 * Bit to be bitwise-ored into the {@link #flags} field that should be
520 * set if the notification should not be canceled when the user clicks
521 * the Clear all button.
522 */
523 public static final int FLAG_NO_CLEAR = 0x00000020;
524
Dianne Hackbornd8a43f62009-08-17 23:33:56 -0700525 /**
526 * Bit to be bitwise-ored into the {@link #flags} field that should be
527 * set if this notification represents a currently running service. This
528 * will normally be set for you by {@link Service#startForeground}.
529 */
530 public static final int FLAG_FOREGROUND_SERVICE = 0x00000040;
531
Daniel Sandlere46cbd32010-06-17 10:35:26 -0400532 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500533 * Obsolete flag indicating high-priority notifications; use the priority field instead.
Joe Malin8d40d042012-11-05 11:36:40 -0800534 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500535 * @deprecated Use {@link #priority} with a positive value.
Daniel Sandlere46cbd32010-06-17 10:35:26 -0400536 */
Aurimas Liutikas514c5ef2016-05-24 15:22:55 -0700537 @Deprecated
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500538 public static final int FLAG_HIGH_PRIORITY = 0x00000080;
Daniel Sandlere46cbd32010-06-17 10:35:26 -0400539
Griff Hazendfcb0802014-02-11 12:00:00 -0800540 /**
541 * Bit to be bitswise-ored into the {@link #flags} field that should be
542 * set if this notification is relevant to the current device only
543 * and it is not recommended that it bridge to other devices.
544 */
545 public static final int FLAG_LOCAL_ONLY = 0x00000100;
546
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700547 /**
548 * Bit to be bitswise-ored into the {@link #flags} field that should be
549 * set if this notification is the group summary for a group of notifications.
550 * Grouped notifications may display in a cluster or stack on devices which
551 * support such rendering. Requires a group key also be set using {@link Builder#setGroup}.
552 */
553 public static final int FLAG_GROUP_SUMMARY = 0x00000200;
554
Julia Reynoldse46bb372016-03-17 11:05:58 -0400555 /**
556 * Bit to be bitswise-ored into the {@link #flags} field that should be
557 * set if this notification is the group summary for an auto-group of notifications.
558 *
559 * @hide
560 */
561 @SystemApi
562 public static final int FLAG_AUTOGROUP_SUMMARY = 0x00000400;
563
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800564 public int flags;
565
Tor Norbyed9273d62013-05-30 15:59:53 -0700566 /** @hide */
567 @IntDef({PRIORITY_DEFAULT,PRIORITY_LOW,PRIORITY_MIN,PRIORITY_HIGH,PRIORITY_MAX})
568 @Retention(RetentionPolicy.SOURCE)
569 public @interface Priority {}
570
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800571 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500572 * Default notification {@link #priority}. If your application does not prioritize its own
573 * notifications, use this value for all notifications.
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500574 *
575 * @deprecated use {@link NotificationManager#IMPORTANCE_DEFAULT} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500576 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500577 @Deprecated
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500578 public static final int PRIORITY_DEFAULT = 0;
579
580 /**
581 * Lower {@link #priority}, for items that are less important. The UI may choose to show these
582 * items smaller, or at a different position in the list, compared with your app's
583 * {@link #PRIORITY_DEFAULT} items.
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500584 *
585 * @deprecated use {@link NotificationManager#IMPORTANCE_LOW} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500586 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500587 @Deprecated
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500588 public static final int PRIORITY_LOW = -1;
589
590 /**
591 * Lowest {@link #priority}; these items might not be shown to the user except under special
592 * circumstances, such as detailed notification logs.
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500593 *
594 * @deprecated use {@link NotificationManager#IMPORTANCE_MIN} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500595 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500596 @Deprecated
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500597 public static final int PRIORITY_MIN = -2;
598
599 /**
600 * Higher {@link #priority}, for more important notifications or alerts. The UI may choose to
601 * show these items larger, or at a different position in notification lists, compared with
602 * your app's {@link #PRIORITY_DEFAULT} items.
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500603 *
604 * @deprecated use {@link NotificationManager#IMPORTANCE_HIGH} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500605 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500606 @Deprecated
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500607 public static final int PRIORITY_HIGH = 1;
608
609 /**
610 * Highest {@link #priority}, for your application's most important items that require the
611 * user's prompt attention or input.
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500612 *
613 * @deprecated use {@link NotificationManager#IMPORTANCE_HIGH} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500614 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500615 @Deprecated
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500616 public static final int PRIORITY_MAX = 2;
617
618 /**
619 * Relative priority for this notification.
Joe Malin8d40d042012-11-05 11:36:40 -0800620 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500621 * Priority is an indication of how much of the user's valuable attention should be consumed by
622 * this notification. Low-priority notifications may be hidden from the user in certain
623 * situations, while the user might be interrupted for a higher-priority notification. The
Daniel Sandler6738eee2012-11-16 12:03:32 -0500624 * system will make a determination about how to interpret this priority when presenting
625 * the notification.
Chris Wren47c20a12014-06-18 17:27:29 -0400626 *
627 * <p>
628 * A notification that is at least {@link #PRIORITY_HIGH} is more likely to be presented
629 * as a heads-up notification.
630 * </p>
631 *
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500632 * @deprecated use {@link NotificationChannel#getImportance()} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500633 */
Tor Norbyed9273d62013-05-30 15:59:53 -0700634 @Priority
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500635 @Deprecated
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500636 public int priority;
Joe Malin8d40d042012-11-05 11:36:40 -0800637
Dan Sandler26e81cf2014-05-06 10:01:27 -0400638 /**
639 * Accent color (an ARGB integer like the constants in {@link android.graphics.Color})
640 * to be applied by the standard Style templates when presenting this notification.
641 *
642 * The current template design constructs a colorful header image by overlaying the
643 * {@link #icon} image (stenciled in white) atop a field of this color. Alpha components are
644 * ignored.
645 */
Tor Norbye80756e32015-03-02 09:39:27 -0800646 @ColorInt
Dan Sandler26e81cf2014-05-06 10:01:27 -0400647 public int color = COLOR_DEFAULT;
648
649 /**
650 * Special value of {@link #color} telling the system not to decorate this notification with
651 * any special color but instead use default colors when presenting this notification.
652 */
Tor Norbye80756e32015-03-02 09:39:27 -0800653 @ColorInt
Dan Sandler26e81cf2014-05-06 10:01:27 -0400654 public static final int COLOR_DEFAULT = 0; // AKA Color.TRANSPARENT
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600655
656 /**
Adrian Roos4ff3b122016-02-01 12:26:13 -0800657 * Special value of {@link #color} used as a place holder for an invalid color.
Selim Cinek99104832017-01-25 14:47:33 -0800658 * @hide
Adrian Roos4ff3b122016-02-01 12:26:13 -0800659 */
660 @ColorInt
Selim Cinek99104832017-01-25 14:47:33 -0800661 public static final int COLOR_INVALID = 1;
Adrian Roos4ff3b122016-02-01 12:26:13 -0800662
663 /**
Adrian Roosc1a80b02016-04-05 14:54:55 -0700664 * Sphere of visibility of this notification, which affects how and when the SystemUI reveals
665 * the notification's presence and contents in untrusted situations (namely, on the secure
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600666 * lockscreen).
667 *
668 * The default level, {@link #VISIBILITY_PRIVATE}, behaves exactly as notifications have always
669 * done on Android: The notification's {@link #icon} and {@link #tickerText} (if available) are
670 * shown in all situations, but the contents are only available if the device is unlocked for
671 * the appropriate user.
672 *
673 * A more permissive policy can be expressed by {@link #VISIBILITY_PUBLIC}; such a notification
674 * can be read even in an "insecure" context (that is, above a secure lockscreen).
675 * To modify the public version of this notification—for example, to redact some portions—see
676 * {@link Builder#setPublicVersion(Notification)}.
677 *
678 * Finally, a notification can be made {@link #VISIBILITY_SECRET}, which will suppress its icon
679 * and ticker until the user has bypassed the lockscreen.
680 */
Jeff Sharkey30e06bb2017-04-24 11:18:03 -0600681 public @Visibility int visibility;
682
683 /** @hide */
684 @IntDef(prefix = { "VISIBILITY_" }, value = {
685 VISIBILITY_PUBLIC,
686 VISIBILITY_PRIVATE,
687 VISIBILITY_SECRET,
688 })
689 @Retention(RetentionPolicy.SOURCE)
690 public @interface Visibility {}
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600691
Griff Hazenfc3922d2014-08-20 11:56:44 -0700692 /**
693 * Notification visibility: Show this notification in its entirety on all lockscreens.
694 *
695 * {@see #visibility}
696 */
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600697 public static final int VISIBILITY_PUBLIC = 1;
Griff Hazenfc3922d2014-08-20 11:56:44 -0700698
699 /**
700 * Notification visibility: Show this notification on all lockscreens, but conceal sensitive or
701 * private information on secure lockscreens.
702 *
703 * {@see #visibility}
704 */
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600705 public static final int VISIBILITY_PRIVATE = 0;
Griff Hazenfc3922d2014-08-20 11:56:44 -0700706
707 /**
708 * Notification visibility: Do not reveal any part of this notification on a secure lockscreen.
709 *
710 * {@see #visibility}
711 */
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600712 public static final int VISIBILITY_SECRET = -1;
713
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500714 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400715 * Notification category: incoming call (voice or video) or similar synchronous communication request.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500716 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400717 public static final String CATEGORY_CALL = "call";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500718
719 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400720 * Notification category: incoming direct message (SMS, instant message, etc.).
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500721 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400722 public static final String CATEGORY_MESSAGE = "msg";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500723
724 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400725 * Notification category: asynchronous bulk message (email).
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500726 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400727 public static final String CATEGORY_EMAIL = "email";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500728
729 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400730 * Notification category: calendar event.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500731 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400732 public static final String CATEGORY_EVENT = "event";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500733
734 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400735 * Notification category: promotion or advertisement.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500736 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400737 public static final String CATEGORY_PROMO = "promo";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500738
739 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400740 * Notification category: alarm or timer.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500741 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400742 public static final String CATEGORY_ALARM = "alarm";
743
744 /**
745 * Notification category: progress of a long-running background operation.
746 */
747 public static final String CATEGORY_PROGRESS = "progress";
748
749 /**
750 * Notification category: social network or sharing update.
751 */
752 public static final String CATEGORY_SOCIAL = "social";
753
754 /**
755 * Notification category: error in background operation or authentication status.
756 */
757 public static final String CATEGORY_ERROR = "err";
758
759 /**
760 * Notification category: media transport control for playback.
761 */
762 public static final String CATEGORY_TRANSPORT = "transport";
763
764 /**
765 * Notification category: system or device status update. Reserved for system use.
766 */
767 public static final String CATEGORY_SYSTEM = "sys";
768
769 /**
770 * Notification category: indication of running background service.
771 */
772 public static final String CATEGORY_SERVICE = "service";
773
774 /**
John Spurlock0a69c8c2014-03-21 13:30:57 -0400775 * Notification category: a specific, timely recommendation for a single thing.
776 * For example, a news app might want to recommend a news story it believes the user will
777 * want to read next.
778 */
779 public static final String CATEGORY_RECOMMENDATION = "recommendation";
780
781 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400782 * Notification category: ongoing information about device or contextual status.
783 */
784 public static final String CATEGORY_STATUS = "status";
785
786 /**
John Spurlock24d3dad2015-04-02 12:24:02 -0400787 * Notification category: user-scheduled reminder.
788 */
789 public static final String CATEGORY_REMINDER = "reminder";
790
791 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400792 * One of the predefined notification categories (see the <code>CATEGORY_*</code> constants)
793 * that best describes this Notification. May be used by the system for ranking and filtering.
794 */
795 public String category;
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500796
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700797 private String mGroupKey;
798
799 /**
800 * Get the key used to group this notification into a cluster or stack
801 * with other notifications on devices which support such rendering.
802 */
803 public String getGroup() {
804 return mGroupKey;
805 }
806
807 private String mSortKey;
808
809 /**
810 * Get a sort key that orders this notification among other notifications from the
811 * same package. This can be useful if an external sort was already applied and an app
812 * would like to preserve this. Notifications will be sorted lexicographically using this
813 * value, although providing different priorities in addition to providing sort key may
814 * cause this value to be ignored.
815 *
816 * <p>This sort key can also be used to order members of a notification group. See
817 * {@link Builder#setGroup}.
818 *
819 * @see String#compareTo(String)
820 */
821 public String getSortKey() {
822 return mSortKey;
823 }
824
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500825 /**
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400826 * Additional semantic data to be carried around with this Notification.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400827 * <p>
828 * The extras keys defined here are intended to capture the original inputs to {@link Builder}
829 * APIs, and are intended to be used by
830 * {@link android.service.notification.NotificationListenerService} implementations to extract
831 * detailed information from notification objects.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500832 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400833 public Bundle extras = new Bundle();
834
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400835 /**
Felipe Lemedd85da62016-06-28 11:29:54 -0700836 * All pending intents in the notification as the system needs to be able to access them but
837 * touching the extras bundle in the system process is not safe because the bundle may contain
Svet Ganovddb94882016-06-23 19:55:24 -0700838 * custom parcelable objects.
839 *
840 * @hide
841 */
Felipe Lemedd85da62016-06-28 11:29:54 -0700842 public ArraySet<PendingIntent> allPendingIntents;
Svet Ganovddb94882016-06-23 19:55:24 -0700843
844 /**
Dianne Hackborn98305522017-05-05 17:53:53 -0700845 * Token identifying the notification that is applying doze/bgcheck whitelisting to the
846 * pending intents inside of it, so only those will get the behavior.
847 *
848 * @hide
849 */
850 static public IBinder whitelistToken;
851
852 /**
853 * Must be set by a process to start associating tokens with Notification objects
854 * coming in to it. This is set by NotificationManagerService.
855 *
856 * @hide
857 */
858 static public IBinder processWhitelistToken;
859
860 /**
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400861 * {@link #extras} key: this is the title of the notification,
862 * as supplied to {@link Builder#setContentTitle(CharSequence)}.
863 */
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -0500864 public static final String EXTRA_TITLE = "android.title";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400865
866 /**
867 * {@link #extras} key: this is the title of the notification when shown in expanded form,
868 * e.g. as supplied to {@link BigTextStyle#setBigContentTitle(CharSequence)}.
869 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400870 public static final String EXTRA_TITLE_BIG = EXTRA_TITLE + ".big";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400871
872 /**
873 * {@link #extras} key: this is the main text payload, as supplied to
874 * {@link Builder#setContentText(CharSequence)}.
875 */
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -0500876 public static final String EXTRA_TEXT = "android.text";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400877
878 /**
879 * {@link #extras} key: this is a third line of text, as supplied to
880 * {@link Builder#setSubText(CharSequence)}.
881 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400882 public static final String EXTRA_SUB_TEXT = "android.subText";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400883
884 /**
Adrian Roose458aa82015-12-08 16:17:19 -0800885 * {@link #extras} key: this is the remote input history, as supplied to
886 * {@link Builder#setRemoteInputHistory(CharSequence[])}.
Adrian Roos005e7742016-04-13 15:02:20 -0700887 *
888 * Apps can fill this through {@link Builder#setRemoteInputHistory(CharSequence[])}
889 * with the most recent inputs that have been sent through a {@link RemoteInput} of this
890 * Notification and are expected to clear it once the it is no longer relevant (e.g. for chat
891 * notifications once the other party has responded).
892 *
893 * The extra with this key is of type CharSequence[] and contains the most recent entry at
894 * the 0 index, the second most recent at the 1 index, etc.
895 *
896 * @see Builder#setRemoteInputHistory(CharSequence[])
Adrian Roose458aa82015-12-08 16:17:19 -0800897 */
898 public static final String EXTRA_REMOTE_INPUT_HISTORY = "android.remoteInputHistory";
899
900 /**
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400901 * {@link #extras} key: this is a small piece of additional text as supplied to
902 * {@link Builder#setContentInfo(CharSequence)}.
903 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400904 public static final String EXTRA_INFO_TEXT = "android.infoText";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400905
906 /**
907 * {@link #extras} key: this is a line of summary information intended to be shown
908 * alongside expanded notifications, as supplied to (e.g.)
909 * {@link BigTextStyle#setSummaryText(CharSequence)}.
910 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400911 public static final String EXTRA_SUMMARY_TEXT = "android.summaryText";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400912
913 /**
Christoph Studer4600f9b2014-07-22 22:44:43 +0200914 * {@link #extras} key: this is the longer text shown in the big form of a
915 * {@link BigTextStyle} notification, as supplied to
916 * {@link BigTextStyle#bigText(CharSequence)}.
917 */
918 public static final String EXTRA_BIG_TEXT = "android.bigText";
919
920 /**
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400921 * {@link #extras} key: this is the resource ID of the notification's main small icon, as
922 * supplied to {@link Builder#setSmallIcon(int)}.
Julia Reynoldse071abd2017-03-22 10:52:11 -0400923 *
924 * @deprecated Use {@link #getSmallIcon()}, which supports a wider variety of icon sources.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400925 */
Julia Reynoldse071abd2017-03-22 10:52:11 -0400926 @Deprecated
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -0500927 public static final String EXTRA_SMALL_ICON = "android.icon";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400928
929 /**
930 * {@link #extras} key: this is a bitmap to be used instead of the small icon when showing the
931 * notification payload, as
932 * supplied to {@link Builder#setLargeIcon(android.graphics.Bitmap)}.
Julia Reynoldse071abd2017-03-22 10:52:11 -0400933 *
934 * @deprecated Use {@link #getLargeIcon()}, which supports a wider variety of icon sources.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400935 */
Julia Reynoldse071abd2017-03-22 10:52:11 -0400936 @Deprecated
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400937 public static final String EXTRA_LARGE_ICON = "android.largeIcon";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400938
939 /**
940 * {@link #extras} key: this is a bitmap to be used instead of the one from
941 * {@link Builder#setLargeIcon(android.graphics.Bitmap)} when the notification is
942 * shown in its expanded form, as supplied to
943 * {@link BigPictureStyle#bigLargeIcon(android.graphics.Bitmap)}.
944 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400945 public static final String EXTRA_LARGE_ICON_BIG = EXTRA_LARGE_ICON + ".big";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400946
947 /**
948 * {@link #extras} key: this is the progress value supplied to
949 * {@link Builder#setProgress(int, int, boolean)}.
950 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400951 public static final String EXTRA_PROGRESS = "android.progress";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400952
953 /**
954 * {@link #extras} key: this is the maximum value supplied to
955 * {@link Builder#setProgress(int, int, boolean)}.
956 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400957 public static final String EXTRA_PROGRESS_MAX = "android.progressMax";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400958
959 /**
960 * {@link #extras} key: whether the progress bar is indeterminate, supplied to
961 * {@link Builder#setProgress(int, int, boolean)}.
962 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400963 public static final String EXTRA_PROGRESS_INDETERMINATE = "android.progressIndeterminate";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400964
965 /**
966 * {@link #extras} key: whether {@link #when} should be shown as a count-up timer (specifically
967 * a {@link android.widget.Chronometer}) instead of a timestamp, as supplied to
968 * {@link Builder#setUsesChronometer(boolean)}.
969 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400970 public static final String EXTRA_SHOW_CHRONOMETER = "android.showChronometer";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400971
972 /**
Selim Cinek81c23aa2016-02-25 16:23:13 -0800973 * {@link #extras} key: whether the chronometer set on the notification should count down
974 * instead of counting up. Is only relevant if key {@link #EXTRA_SHOW_CHRONOMETER} is present.
Adrian Roos96b7e202016-05-17 13:50:38 -0700975 * This extra is a boolean. The default is false.
Selim Cinek81c23aa2016-02-25 16:23:13 -0800976 */
Adrian Roos96b7e202016-05-17 13:50:38 -0700977 public static final String EXTRA_CHRONOMETER_COUNT_DOWN = "android.chronometerCountDown";
Selim Cinek81c23aa2016-02-25 16:23:13 -0800978
979 /**
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400980 * {@link #extras} key: whether {@link #when} should be shown,
981 * as supplied to {@link Builder#setShowWhen(boolean)}.
982 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400983 public static final String EXTRA_SHOW_WHEN = "android.showWhen";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400984
985 /**
986 * {@link #extras} key: this is a bitmap to be shown in {@link BigPictureStyle} expanded
987 * notifications, supplied to {@link BigPictureStyle#bigPicture(android.graphics.Bitmap)}.
988 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400989 public static final String EXTRA_PICTURE = "android.picture";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400990
991 /**
992 * {@link #extras} key: An array of CharSequences to show in {@link InboxStyle} expanded
993 * notifications, each of which was supplied to {@link InboxStyle#addLine(CharSequence)}.
994 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400995 public static final String EXTRA_TEXT_LINES = "android.textLines";
Dan Sandler842dd772014-05-15 09:36:47 -0400996
997 /**
998 * {@link #extras} key: A string representing the name of the specific
999 * {@link android.app.Notification.Style} used to create this notification.
1000 */
Chris Wren91ad5632013-06-05 15:05:57 -04001001 public static final String EXTRA_TEMPLATE = "android.template";
Daniel Sandlerf45564e2013-04-15 15:05:08 -04001002
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001003 /**
Chris Wrene6c48932014-09-29 17:19:27 -04001004 * {@link #extras} key: A String array containing the people that this notification relates to,
1005 * each of which was supplied to {@link Builder#addPerson(String)}.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001006 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -04001007 public static final String EXTRA_PEOPLE = "android.people";
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001008
1009 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -04001010 * Allow certain system-generated notifications to appear before the device is provisioned.
1011 * Only available to notifications coming from the android package.
1012 * @hide
1013 */
Maurice Lam96c10032017-03-29 15:42:38 -07001014 @SystemApi
Jeff Sharkeyd86b8fe2017-06-02 17:36:26 -06001015 @RequiresPermission(android.Manifest.permission.NOTIFICATION_DURING_SETUP)
John Spurlockfd7f1e02014-03-18 16:41:57 -04001016 public static final String EXTRA_ALLOW_DURING_SETUP = "android.allowDuringSetup";
1017
1018 /**
Jose Limae9e3b3b2014-05-18 23:44:50 -07001019 * {@link #extras} key: A
1020 * {@link android.content.ContentUris content URI} pointing to an image that can be displayed
1021 * in the background when the notification is selected. The URI must point to an image stream
1022 * suitable for passing into
1023 * {@link android.graphics.BitmapFactory#decodeStream(java.io.InputStream)
1024 * BitmapFactory.decodeStream}; all other content types will be ignored. The content provider
1025 * URI used for this purpose must require no permissions to read the image data.
1026 */
1027 public static final String EXTRA_BACKGROUND_IMAGE_URI = "android.backgroundImageUri";
1028
1029 /**
Dan Sandler842dd772014-05-15 09:36:47 -04001030 * {@link #extras} key: A
Jeff Browndba34ba2014-06-24 20:46:03 -07001031 * {@link android.media.session.MediaSession.Token} associated with a
Dan Sandler842dd772014-05-15 09:36:47 -04001032 * {@link android.app.Notification.MediaStyle} notification.
1033 */
1034 public static final String EXTRA_MEDIA_SESSION = "android.mediaSession";
1035
1036 /**
Bryan Mawhinneye191f902014-07-22 12:50:09 +01001037 * {@link #extras} key: the indices of actions to be shown in the compact view,
1038 * as supplied to (e.g.) {@link MediaStyle#setShowActionsInCompactView(int...)}.
1039 */
1040 public static final String EXTRA_COMPACT_ACTIONS = "android.compactActions";
1041
Christoph Studer943aa672014-08-03 20:31:16 +02001042 /**
Alex Hillsfc737de2016-03-23 17:33:02 -04001043 * {@link #extras} key: the username to be displayed for all messages sent by the user including
1044 * direct replies
Adrian Roos96b7e202016-05-17 13:50:38 -07001045 * {@link android.app.Notification.MessagingStyle} notification. This extra is a
1046 * {@link CharSequence}
Alex Hillsfc737de2016-03-23 17:33:02 -04001047 */
1048 public static final String EXTRA_SELF_DISPLAY_NAME = "android.selfDisplayName";
1049
1050 /**
Adrian Roos96b7e202016-05-17 13:50:38 -07001051 * {@link #extras} key: a {@link CharSequence} to be displayed as the title to a conversation
Alex Hillsd9b04d92016-04-11 16:38:16 -04001052 * represented by a {@link android.app.Notification.MessagingStyle}
Alex Hillsfc737de2016-03-23 17:33:02 -04001053 */
Alex Hillsd9b04d92016-04-11 16:38:16 -04001054 public static final String EXTRA_CONVERSATION_TITLE = "android.conversationTitle";
Alex Hillsfc737de2016-03-23 17:33:02 -04001055
1056 /**
1057 * {@link #extras} key: an array of {@link android.app.Notification.MessagingStyle.Message}
1058 * bundles provided by a
Adrian Roos96b7e202016-05-17 13:50:38 -07001059 * {@link android.app.Notification.MessagingStyle} notification. This extra is a parcelable
1060 * array of bundles.
Alex Hillsfc737de2016-03-23 17:33:02 -04001061 */
1062 public static final String EXTRA_MESSAGES = "android.messages";
1063
1064 /**
Adrian Roos437cd562017-01-18 15:47:03 -08001065 * {@link #extras} key: an array of
1066 * {@link android.app.Notification.MessagingStyle#addHistoricMessage historic}
1067 * {@link android.app.Notification.MessagingStyle.Message} bundles provided by a
1068 * {@link android.app.Notification.MessagingStyle} notification. This extra is a parcelable
1069 * array of bundles.
1070 */
1071 public static final String EXTRA_HISTORIC_MESSAGES = "android.messages.historic";
1072
1073 /**
Selim Cinek7b9605b2017-01-19 17:36:00 -08001074 * {@link #extras} key: whether the notification should be colorized as
1075 * supplied to {@link Builder#setColorized(boolean)}}.
1076 */
1077 public static final String EXTRA_COLORIZED = "android.colorized";
1078
1079 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04001080 * @hide
1081 */
1082 public static final String EXTRA_BUILDER_APPLICATION_INFO = "android.appInfo";
1083
Selim Cinek247fa012016-02-18 09:50:48 -08001084 /**
1085 * @hide
1086 */
1087 public static final String EXTRA_CONTAINS_CUSTOM_VIEW = "android.contains.customView";
1088
Shane Brennan472a3b32016-12-12 15:28:10 -08001089 /**
1090 * {@link #extras} key: the audio contents of this notification.
1091 *
1092 * This is for use when rendering the notification on an audio-focused interface;
1093 * the audio contents are a complete sound sample that contains the contents/body of the
1094 * notification. This may be used in substitute of a Text-to-Speech reading of the
1095 * notification. For example if the notification represents a voice message this should point
1096 * to the audio of that message.
1097 *
1098 * The data stored under this key should be a String representation of a Uri that contains the
1099 * audio contents in one of the following formats: WAV, PCM 16-bit, AMR-WB.
1100 *
1101 * This extra is unnecessary if you are using {@code MessagingStyle} since each {@code Message}
1102 * has a field for holding data URI. That field can be used for audio.
1103 * See {@code Message#setData}.
1104 *
1105 * Example usage:
1106 * <pre>
1107 * {@code
1108 * Notification.Builder myBuilder = (build your Notification as normal);
1109 * myBuilder.getExtras().putString(EXTRA_AUDIO_CONTENTS_URI, myAudioUri.toString());
1110 * }
1111 * </pre>
1112 */
1113 public static final String EXTRA_AUDIO_CONTENTS_URI = "android.audioContents";
1114
Dan Sandler80eaa592016-04-14 23:34:54 -04001115 /** @hide */
1116 @SystemApi
Jeff Sharkeyd86b8fe2017-06-02 17:36:26 -06001117 @RequiresPermission(android.Manifest.permission.SUBSTITUTE_NOTIFICATION_APP_NAME)
Dan Sandler732bd6c2016-04-12 14:20:32 -04001118 public static final String EXTRA_SUBSTITUTE_APP_NAME = "android.substName";
1119
Dianne Hackbornfb5d4b52017-05-16 17:03:44 -07001120 /**
1121 * This is set on the notification shown by the activity manager about all apps
1122 * running in the background. It indicates that the notification should be shown
1123 * only if any of the given apps do not already have a {@link #FLAG_FOREGROUND_SERVICE}
1124 * notification currently visible to the user. This is a string array of all
1125 * package names of the apps.
1126 * @hide
1127 */
1128 public static final String EXTRA_FOREGROUND_APPS = "android.foregroundApps";
1129
Dan Sandlerd63f9322015-05-06 15:18:49 -04001130 private Icon mSmallIcon;
1131 private Icon mLargeIcon;
1132
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04001133 private String mChannelId;
Julia Reynolds2a128742016-11-28 14:29:25 -05001134 private long mTimeout;
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04001135
Julia Reynolds13d898c2017-02-02 12:22:05 -05001136 private String mShortcutId;
Julia Reynolds3aedded2017-03-31 14:42:09 -04001137 private CharSequence mSettingsText;
Julia Reynolds13d898c2017-02-02 12:22:05 -05001138
Julia Reynoldsa79c3712017-04-21 10:29:57 -04001139 /** @hide */
1140 @IntDef(prefix = { "GROUP_ALERT_" }, value = {
1141 GROUP_ALERT_ALL, GROUP_ALERT_CHILDREN, GROUP_ALERT_SUMMARY
1142 })
1143 @Retention(RetentionPolicy.SOURCE)
1144 public @interface GroupAlertBehavior {}
1145
1146 /**
1147 * Constant for {@link Builder#setGroupAlertBehavior(int)}, meaning that all notifications in a
1148 * group with sound or vibration ought to make sound or vibrate (respectively), so this
1149 * notification will not be muted when it is in a group.
1150 */
1151 public static final int GROUP_ALERT_ALL = 0;
1152
1153 /**
1154 * Constant for {@link Builder#setGroupAlertBehavior(int)}, meaning that all children
1155 * notification in a group should be silenced (no sound or vibration) even if they are posted
1156 * to a {@link NotificationChannel} that has sound and/or vibration. Use this constant to
1157 * mute this notification if this notification is a group child.
1158 *
1159 * <p> For example, you might want to use this constant if you post a number of children
1160 * notifications at once (say, after a periodic sync), and only need to notify the user
1161 * audibly once.
1162 */
1163 public static final int GROUP_ALERT_SUMMARY = 1;
1164
1165 /**
1166 * Constant for {@link Builder#setGroupAlertBehavior(int)}, meaning that the summary
1167 * notification in a group should be silenced (no sound or vibration) even if they are
1168 * posted to a {@link NotificationChannel} that has sound and/or vibration. Use this constant
1169 * to mute this notification if this notification is a group summary.
1170 *
1171 * <p>For example, you might want to use this constant if only the children notifications
1172 * in your group have content and the summary is only used to visually group notifications.
1173 */
1174 public static final int GROUP_ALERT_CHILDREN = 2;
1175
Julia Reynolds2f431e22017-06-07 14:12:09 +00001176 private int mGroupAlertBehavior = GROUP_ALERT_ALL;
Julia Reynoldsa79c3712017-04-21 10:29:57 -04001177
Julia Reynolds13d898c2017-02-02 12:22:05 -05001178 /**
1179 * If this notification is being shown as a badge, always show as a number.
1180 */
1181 public static final int BADGE_ICON_NONE = 0;
1182
1183 /**
1184 * If this notification is being shown as a badge, use the {@link #getSmallIcon()} to
1185 * represent this notification.
1186 */
1187 public static final int BADGE_ICON_SMALL = 1;
1188
1189 /**
1190 * If this notification is being shown as a badge, use the {@link #getLargeIcon()} to
1191 * represent this notification.
1192 */
1193 public static final int BADGE_ICON_LARGE = 2;
Julia Reynolds7d5b4da2017-03-20 10:18:49 -04001194 private int mBadgeIcon = BADGE_ICON_NONE;
Julia Reynolds13d898c2017-02-02 12:22:05 -05001195
Chris Wren51c75102013-07-16 20:49:17 -04001196 /**
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001197 * Structure to encapsulate a named action that can be shown as part of this notification.
1198 * It must include an icon, a label, and a {@link PendingIntent} to be fired when the action is
1199 * selected by the user.
1200 * <p>
Griff Hazen959591e2014-05-15 22:26:18 -07001201 * Apps should use {@link Notification.Builder#addAction(int, CharSequence, PendingIntent)}
1202 * or {@link Notification.Builder#addAction(Notification.Action)}
1203 * to attach actions.
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001204 */
Daniel Sandlerea2a3172013-02-20 22:24:20 -05001205 public static class Action implements Parcelable {
Shane Brennan472a3b32016-12-12 15:28:10 -08001206 /**
1207 * {@link #extras} key: Keys to a {@link Parcelable} {@link ArrayList} of
1208 * {@link RemoteInput}s.
1209 *
1210 * This is intended for {@link RemoteInput}s that only accept data, meaning
1211 * {@link RemoteInput#getAllowFreeFormInput} is false, {@link RemoteInput#getChoices}
1212 * is null or empty, and {@link RemoteInput#getAllowedDataTypes is non-null and not
1213 * empty. These {@link RemoteInput}s will be ignored by devices that do not
1214 * support non-text-based {@link RemoteInput}s. See {@link Builder#build}.
1215 *
1216 * You can test if a RemoteInput matches these constraints using
1217 * {@link RemoteInput#isDataOnly}.
1218 */
1219 private static final String EXTRA_DATA_ONLY_INPUTS = "android.extra.DATA_ONLY_INPUTS";
1220
Griff Hazen959591e2014-05-15 22:26:18 -07001221 private final Bundle mExtras;
Dan Sandler86647982015-05-13 23:41:13 -04001222 private Icon mIcon;
Griff Hazen61a9e862014-05-22 16:05:19 -07001223 private final RemoteInput[] mRemoteInputs;
Alex Hills1f27f882017-01-12 11:24:46 -05001224 private boolean mAllowGeneratedReplies = true;
Griff Hazen959591e2014-05-15 22:26:18 -07001225
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001226 /**
1227 * Small icon representing the action.
Dan Sandler86647982015-05-13 23:41:13 -04001228 *
1229 * @deprecated Use {@link Action#getIcon()} instead.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001230 */
Dan Sandler86647982015-05-13 23:41:13 -04001231 @Deprecated
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001232 public int icon;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001233
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001234 /**
1235 * Title of the action.
1236 */
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001237 public CharSequence title;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001238
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001239 /**
1240 * Intent to send when the user invokes this action. May be null, in which case the action
1241 * may be rendered in a disabled presentation by the system UI.
1242 */
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001243 public PendingIntent actionIntent;
Griff Hazen959591e2014-05-15 22:26:18 -07001244
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001245 private Action(Parcel in) {
Dan Sandler86647982015-05-13 23:41:13 -04001246 if (in.readInt() != 0) {
1247 mIcon = Icon.CREATOR.createFromParcel(in);
Dan Sandler68079d52015-07-22 10:45:30 -04001248 if (mIcon.getType() == Icon.TYPE_RESOURCE) {
1249 icon = mIcon.getResId();
1250 }
Dan Sandler86647982015-05-13 23:41:13 -04001251 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001252 title = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
1253 if (in.readInt() == 1) {
1254 actionIntent = PendingIntent.CREATOR.createFromParcel(in);
1255 }
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001256 mExtras = Bundle.setDefusable(in.readBundle(), true);
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001257 mRemoteInputs = in.createTypedArray(RemoteInput.CREATOR);
Alex Hills42b0c4d2016-04-26 13:35:36 -04001258 mAllowGeneratedReplies = in.readInt() == 1;
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001259 }
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001260
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001261 /**
Dan Sandler86647982015-05-13 23:41:13 -04001262 * @deprecated Use {@link android.app.Notification.Action.Builder}.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001263 */
Dan Sandler86647982015-05-13 23:41:13 -04001264 @Deprecated
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001265 public Action(int icon, CharSequence title, PendingIntent intent) {
Alex Hills1f27f882017-01-12 11:24:46 -05001266 this(Icon.createWithResource("", icon), title, intent, new Bundle(), null, true);
Griff Hazen959591e2014-05-15 22:26:18 -07001267 }
1268
Adrian Roos7af53622016-10-12 13:44:05 -07001269 /** Keep in sync with {@link Notification.Action.Builder#Builder(Action)}! */
Dan Sandler86647982015-05-13 23:41:13 -04001270 private Action(Icon icon, CharSequence title, PendingIntent intent, Bundle extras,
Alex Hills42b0c4d2016-04-26 13:35:36 -04001271 RemoteInput[] remoteInputs, boolean allowGeneratedReplies) {
Dan Sandler86647982015-05-13 23:41:13 -04001272 this.mIcon = icon;
Gus Prevasf5bff46f2015-08-24 10:34:28 -04001273 if (icon != null && icon.getType() == Icon.TYPE_RESOURCE) {
1274 this.icon = icon.getResId();
1275 }
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001276 this.title = title;
1277 this.actionIntent = intent;
Griff Hazen959591e2014-05-15 22:26:18 -07001278 this.mExtras = extras != null ? extras : new Bundle();
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001279 this.mRemoteInputs = remoteInputs;
Alex Hills42b0c4d2016-04-26 13:35:36 -04001280 this.mAllowGeneratedReplies = allowGeneratedReplies;
Griff Hazen959591e2014-05-15 22:26:18 -07001281 }
1282
1283 /**
Dan Sandler86647982015-05-13 23:41:13 -04001284 * Return an icon representing the action.
1285 */
1286 public Icon getIcon() {
1287 if (mIcon == null && icon != 0) {
1288 // you snuck an icon in here without using the builder; let's try to keep it
1289 mIcon = Icon.createWithResource("", icon);
1290 }
1291 return mIcon;
1292 }
1293
1294 /**
Griff Hazen959591e2014-05-15 22:26:18 -07001295 * Get additional metadata carried around with this Action.
1296 */
1297 public Bundle getExtras() {
1298 return mExtras;
1299 }
1300
1301 /**
Alex Hills42b0c4d2016-04-26 13:35:36 -04001302 * Return whether the platform should automatically generate possible replies for this
1303 * {@link Action}
1304 */
1305 public boolean getAllowGeneratedReplies() {
1306 return mAllowGeneratedReplies;
1307 }
1308
1309 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001310 * Get the list of inputs to be collected from the user when this action is sent.
Shane Brennan472a3b32016-12-12 15:28:10 -08001311 * May return null if no remote inputs were added. Only returns inputs which accept
1312 * a text input. For inputs which only accept data use {@link #getDataOnlyRemoteInputs}.
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001313 */
1314 public RemoteInput[] getRemoteInputs() {
1315 return mRemoteInputs;
1316 }
1317
1318 /**
Shane Brennan472a3b32016-12-12 15:28:10 -08001319 * Get the list of inputs to be collected from the user that ONLY accept data when this
1320 * action is sent. These remote inputs are guaranteed to return true on a call to
1321 * {@link RemoteInput#isDataOnly}.
1322 *
Shane Brennanf670d6c2017-04-25 13:05:45 -07001323 * Returns null if there are no data-only remote inputs.
Shane Brennan472a3b32016-12-12 15:28:10 -08001324 *
1325 * This method exists so that legacy RemoteInput collectors that pre-date the addition
1326 * of non-textual RemoteInputs do not access these remote inputs.
1327 */
1328 public RemoteInput[] getDataOnlyRemoteInputs() {
1329 return (RemoteInput[]) mExtras.getParcelableArray(EXTRA_DATA_ONLY_INPUTS);
1330 }
1331
1332 /**
Griff Hazen959591e2014-05-15 22:26:18 -07001333 * Builder class for {@link Action} objects.
1334 */
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001335 public static final class Builder {
Dan Sandler86647982015-05-13 23:41:13 -04001336 private final Icon mIcon;
Griff Hazen959591e2014-05-15 22:26:18 -07001337 private final CharSequence mTitle;
1338 private final PendingIntent mIntent;
Alex Hills1f27f882017-01-12 11:24:46 -05001339 private boolean mAllowGeneratedReplies = true;
Griff Hazen959591e2014-05-15 22:26:18 -07001340 private final Bundle mExtras;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001341 private ArrayList<RemoteInput> mRemoteInputs;
Griff Hazen959591e2014-05-15 22:26:18 -07001342
1343 /**
1344 * Construct a new builder for {@link Action} object.
1345 * @param icon icon to show for this action
1346 * @param title the title of the action
1347 * @param intent the {@link PendingIntent} to fire when users trigger this action
1348 */
Dan Sandler86647982015-05-13 23:41:13 -04001349 @Deprecated
Griff Hazen959591e2014-05-15 22:26:18 -07001350 public Builder(int icon, CharSequence title, PendingIntent intent) {
Adrian Roos7af53622016-10-12 13:44:05 -07001351 this(Icon.createWithResource("", icon), title, intent);
Dan Sandler86647982015-05-13 23:41:13 -04001352 }
1353
1354 /**
1355 * Construct a new builder for {@link Action} object.
1356 * @param icon icon to show for this action
1357 * @param title the title of the action
1358 * @param intent the {@link PendingIntent} to fire when users trigger this action
1359 */
1360 public Builder(Icon icon, CharSequence title, PendingIntent intent) {
Alex Hills1f27f882017-01-12 11:24:46 -05001361 this(icon, title, intent, new Bundle(), null, true);
Griff Hazen959591e2014-05-15 22:26:18 -07001362 }
1363
1364 /**
1365 * Construct a new builder for {@link Action} object using the fields from an
1366 * {@link Action}.
1367 * @param action the action to read fields from.
1368 */
1369 public Builder(Action action) {
Adrian Roos7af53622016-10-12 13:44:05 -07001370 this(action.getIcon(), action.title, action.actionIntent,
1371 new Bundle(action.mExtras), action.getRemoteInputs(),
1372 action.getAllowGeneratedReplies());
Griff Hazen959591e2014-05-15 22:26:18 -07001373 }
1374
Dan Sandler86647982015-05-13 23:41:13 -04001375 private Builder(Icon icon, CharSequence title, PendingIntent intent, Bundle extras,
Adrian Roos7af53622016-10-12 13:44:05 -07001376 RemoteInput[] remoteInputs, boolean allowGeneratedReplies) {
Griff Hazen959591e2014-05-15 22:26:18 -07001377 mIcon = icon;
1378 mTitle = title;
1379 mIntent = intent;
1380 mExtras = extras;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001381 if (remoteInputs != null) {
1382 mRemoteInputs = new ArrayList<RemoteInput>(remoteInputs.length);
1383 Collections.addAll(mRemoteInputs, remoteInputs);
1384 }
Adrian Roos7af53622016-10-12 13:44:05 -07001385 mAllowGeneratedReplies = allowGeneratedReplies;
Griff Hazen959591e2014-05-15 22:26:18 -07001386 }
1387
1388 /**
1389 * Merge additional metadata into this builder.
1390 *
1391 * <p>Values within the Bundle will replace existing extras values in this Builder.
1392 *
1393 * @see Notification.Action#extras
1394 */
1395 public Builder addExtras(Bundle extras) {
1396 if (extras != null) {
1397 mExtras.putAll(extras);
1398 }
1399 return this;
1400 }
1401
1402 /**
1403 * Get the metadata Bundle used by this Builder.
1404 *
1405 * <p>The returned Bundle is shared with this Builder.
1406 */
1407 public Bundle getExtras() {
1408 return mExtras;
1409 }
1410
1411 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001412 * Add an input to be collected from the user when this action is sent.
1413 * Response values can be retrieved from the fired intent by using the
1414 * {@link RemoteInput#getResultsFromIntent} function.
1415 * @param remoteInput a {@link RemoteInput} to add to the action
1416 * @return this object for method chaining
1417 */
1418 public Builder addRemoteInput(RemoteInput remoteInput) {
1419 if (mRemoteInputs == null) {
1420 mRemoteInputs = new ArrayList<RemoteInput>();
1421 }
1422 mRemoteInputs.add(remoteInput);
1423 return this;
1424 }
1425
1426 /**
Alex Hills42b0c4d2016-04-26 13:35:36 -04001427 * Set whether the platform should automatically generate possible replies to add to
1428 * {@link RemoteInput#getChoices()}. If the {@link Action} doesn't have a
1429 * {@link RemoteInput}, this has no effect.
1430 * @param allowGeneratedReplies {@code true} to allow generated replies, {@code false}
1431 * otherwise
1432 * @return this object for method chaining
Alex Hills1f27f882017-01-12 11:24:46 -05001433 * The default value is {@code true}
Alex Hills42b0c4d2016-04-26 13:35:36 -04001434 */
1435 public Builder setAllowGeneratedReplies(boolean allowGeneratedReplies) {
1436 mAllowGeneratedReplies = allowGeneratedReplies;
1437 return this;
1438 }
1439
1440 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001441 * Apply an extender to this action builder. Extenders may be used to add
1442 * metadata or change options on this builder.
1443 */
Griff Hazen61a9e862014-05-22 16:05:19 -07001444 public Builder extend(Extender extender) {
1445 extender.extend(this);
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001446 return this;
1447 }
1448
1449 /**
Griff Hazen959591e2014-05-15 22:26:18 -07001450 * Combine all of the options that have been set and return a new {@link Action}
1451 * object.
1452 * @return the built action
1453 */
1454 public Action build() {
Shane Brennan472a3b32016-12-12 15:28:10 -08001455 ArrayList<RemoteInput> dataOnlyInputs = new ArrayList<>();
1456 RemoteInput[] previousDataInputs =
1457 (RemoteInput[]) mExtras.getParcelableArray(EXTRA_DATA_ONLY_INPUTS);
Felipe Lemedfd11962017-01-18 18:38:46 -08001458 if (previousDataInputs != null) {
Shane Brennan472a3b32016-12-12 15:28:10 -08001459 for (RemoteInput input : previousDataInputs) {
1460 dataOnlyInputs.add(input);
1461 }
1462 }
1463 List<RemoteInput> textInputs = new ArrayList<>();
1464 if (mRemoteInputs != null) {
1465 for (RemoteInput input : mRemoteInputs) {
1466 if (input.isDataOnly()) {
1467 dataOnlyInputs.add(input);
1468 } else {
1469 textInputs.add(input);
1470 }
1471 }
1472 }
1473 if (!dataOnlyInputs.isEmpty()) {
1474 RemoteInput[] dataInputsArr =
1475 dataOnlyInputs.toArray(new RemoteInput[dataOnlyInputs.size()]);
1476 mExtras.putParcelableArray(EXTRA_DATA_ONLY_INPUTS, dataInputsArr);
1477 }
1478 RemoteInput[] textInputsArr = textInputs.isEmpty()
1479 ? null : textInputs.toArray(new RemoteInput[textInputs.size()]);
1480 return new Action(mIcon, mTitle, mIntent, mExtras, textInputsArr,
Alex Hills42b0c4d2016-04-26 13:35:36 -04001481 mAllowGeneratedReplies);
Griff Hazen959591e2014-05-15 22:26:18 -07001482 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001483 }
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001484
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001485 @Override
1486 public Action clone() {
1487 return new Action(
Dan Sandler86647982015-05-13 23:41:13 -04001488 getIcon(),
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001489 title,
1490 actionIntent, // safe to alias
Julia Reynolds53c934c2016-09-16 14:03:43 -04001491 mExtras == null ? new Bundle() : new Bundle(mExtras),
Alex Hills42b0c4d2016-04-26 13:35:36 -04001492 getRemoteInputs(),
1493 getAllowGeneratedReplies());
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001494 }
1495 @Override
1496 public int describeContents() {
1497 return 0;
1498 }
1499 @Override
1500 public void writeToParcel(Parcel out, int flags) {
Dan Sandler86647982015-05-13 23:41:13 -04001501 final Icon ic = getIcon();
1502 if (ic != null) {
1503 out.writeInt(1);
1504 ic.writeToParcel(out, 0);
1505 } else {
1506 out.writeInt(0);
1507 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001508 TextUtils.writeToParcel(title, out, flags);
1509 if (actionIntent != null) {
1510 out.writeInt(1);
1511 actionIntent.writeToParcel(out, flags);
1512 } else {
1513 out.writeInt(0);
1514 }
Griff Hazen959591e2014-05-15 22:26:18 -07001515 out.writeBundle(mExtras);
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001516 out.writeTypedArray(mRemoteInputs, flags);
Alex Hills42b0c4d2016-04-26 13:35:36 -04001517 out.writeInt(mAllowGeneratedReplies ? 1 : 0);
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001518 }
Griff Hazen959591e2014-05-15 22:26:18 -07001519 public static final Parcelable.Creator<Action> CREATOR =
1520 new Parcelable.Creator<Action>() {
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001521 public Action createFromParcel(Parcel in) {
1522 return new Action(in);
1523 }
1524 public Action[] newArray(int size) {
1525 return new Action[size];
1526 }
1527 };
Griff Hazen61a9e862014-05-22 16:05:19 -07001528
1529 /**
1530 * Extender interface for use with {@link Builder#extend}. Extenders may be used to add
1531 * metadata or change options on an action builder.
1532 */
1533 public interface Extender {
1534 /**
1535 * Apply this extender to a notification action builder.
1536 * @param builder the builder to be modified.
1537 * @return the build object for chaining.
1538 */
1539 public Builder extend(Builder builder);
1540 }
1541
1542 /**
1543 * Wearable extender for notification actions. To add extensions to an action,
1544 * create a new {@link android.app.Notification.Action.WearableExtender} object using
1545 * the {@code WearableExtender()} constructor and apply it to a
1546 * {@link android.app.Notification.Action.Builder} using
1547 * {@link android.app.Notification.Action.Builder#extend}.
1548 *
1549 * <pre class="prettyprint">
1550 * Notification.Action action = new Notification.Action.Builder(
1551 * R.drawable.archive_all, "Archive all", actionIntent)
Griff Hazen14f57992014-05-26 09:07:14 -07001552 * .extend(new Notification.Action.WearableExtender()
Griff Hazen61a9e862014-05-22 16:05:19 -07001553 * .setAvailableOffline(false))
Griff Hazen14f57992014-05-26 09:07:14 -07001554 * .build();</pre>
Griff Hazen61a9e862014-05-22 16:05:19 -07001555 */
1556 public static final class WearableExtender implements Extender {
1557 /** Notification action extra which contains wearable extensions */
1558 private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS";
1559
Pete Gastaf6781d2014-10-07 15:17:05 -04001560 // Keys within EXTRA_WEARABLE_EXTENSIONS for wearable options.
Griff Hazen61a9e862014-05-22 16:05:19 -07001561 private static final String KEY_FLAGS = "flags";
Pete Gastaf6781d2014-10-07 15:17:05 -04001562 private static final String KEY_IN_PROGRESS_LABEL = "inProgressLabel";
1563 private static final String KEY_CONFIRM_LABEL = "confirmLabel";
1564 private static final String KEY_CANCEL_LABEL = "cancelLabel";
Griff Hazen61a9e862014-05-22 16:05:19 -07001565
1566 // Flags bitwise-ored to mFlags
1567 private static final int FLAG_AVAILABLE_OFFLINE = 0x1;
Alex Hills9ab3a232016-04-05 14:54:56 -04001568 private static final int FLAG_HINT_LAUNCHES_ACTIVITY = 1 << 1;
Alex Hills9f087612016-06-07 09:08:59 -04001569 private static final int FLAG_HINT_DISPLAY_INLINE = 1 << 2;
Griff Hazen61a9e862014-05-22 16:05:19 -07001570
1571 // Default value for flags integer
1572 private static final int DEFAULT_FLAGS = FLAG_AVAILABLE_OFFLINE;
1573
1574 private int mFlags = DEFAULT_FLAGS;
1575
Pete Gastaf6781d2014-10-07 15:17:05 -04001576 private CharSequence mInProgressLabel;
1577 private CharSequence mConfirmLabel;
1578 private CharSequence mCancelLabel;
1579
Griff Hazen61a9e862014-05-22 16:05:19 -07001580 /**
1581 * Create a {@link android.app.Notification.Action.WearableExtender} with default
1582 * options.
1583 */
1584 public WearableExtender() {
1585 }
1586
1587 /**
1588 * Create a {@link android.app.Notification.Action.WearableExtender} by reading
1589 * wearable options present in an existing notification action.
1590 * @param action the notification action to inspect.
1591 */
1592 public WearableExtender(Action action) {
1593 Bundle wearableBundle = action.getExtras().getBundle(EXTRA_WEARABLE_EXTENSIONS);
1594 if (wearableBundle != null) {
1595 mFlags = wearableBundle.getInt(KEY_FLAGS, DEFAULT_FLAGS);
Pete Gastaf6781d2014-10-07 15:17:05 -04001596 mInProgressLabel = wearableBundle.getCharSequence(KEY_IN_PROGRESS_LABEL);
1597 mConfirmLabel = wearableBundle.getCharSequence(KEY_CONFIRM_LABEL);
1598 mCancelLabel = wearableBundle.getCharSequence(KEY_CANCEL_LABEL);
Griff Hazen61a9e862014-05-22 16:05:19 -07001599 }
1600 }
1601
1602 /**
1603 * Apply wearable extensions to a notification action that is being built. This is
1604 * typically called by the {@link android.app.Notification.Action.Builder#extend}
1605 * method of {@link android.app.Notification.Action.Builder}.
1606 */
1607 @Override
1608 public Action.Builder extend(Action.Builder builder) {
1609 Bundle wearableBundle = new Bundle();
1610
1611 if (mFlags != DEFAULT_FLAGS) {
1612 wearableBundle.putInt(KEY_FLAGS, mFlags);
1613 }
Pete Gastaf6781d2014-10-07 15:17:05 -04001614 if (mInProgressLabel != null) {
1615 wearableBundle.putCharSequence(KEY_IN_PROGRESS_LABEL, mInProgressLabel);
1616 }
1617 if (mConfirmLabel != null) {
1618 wearableBundle.putCharSequence(KEY_CONFIRM_LABEL, mConfirmLabel);
1619 }
1620 if (mCancelLabel != null) {
1621 wearableBundle.putCharSequence(KEY_CANCEL_LABEL, mCancelLabel);
1622 }
Griff Hazen61a9e862014-05-22 16:05:19 -07001623
1624 builder.getExtras().putBundle(EXTRA_WEARABLE_EXTENSIONS, wearableBundle);
1625 return builder;
1626 }
1627
1628 @Override
1629 public WearableExtender clone() {
1630 WearableExtender that = new WearableExtender();
1631 that.mFlags = this.mFlags;
Pete Gastaf6781d2014-10-07 15:17:05 -04001632 that.mInProgressLabel = this.mInProgressLabel;
1633 that.mConfirmLabel = this.mConfirmLabel;
1634 that.mCancelLabel = this.mCancelLabel;
Griff Hazen61a9e862014-05-22 16:05:19 -07001635 return that;
1636 }
1637
1638 /**
1639 * Set whether this action is available when the wearable device is not connected to
1640 * a companion device. The user can still trigger this action when the wearable device is
1641 * offline, but a visual hint will indicate that the action may not be available.
1642 * Defaults to true.
1643 */
1644 public WearableExtender setAvailableOffline(boolean availableOffline) {
1645 setFlag(FLAG_AVAILABLE_OFFLINE, availableOffline);
1646 return this;
1647 }
1648
1649 /**
1650 * Get whether this action is available when the wearable device is not connected to
1651 * a companion device. The user can still trigger this action when the wearable device is
1652 * offline, but a visual hint will indicate that the action may not be available.
1653 * Defaults to true.
1654 */
1655 public boolean isAvailableOffline() {
1656 return (mFlags & FLAG_AVAILABLE_OFFLINE) != 0;
1657 }
1658
1659 private void setFlag(int mask, boolean value) {
1660 if (value) {
1661 mFlags |= mask;
1662 } else {
1663 mFlags &= ~mask;
1664 }
1665 }
Pete Gastaf6781d2014-10-07 15:17:05 -04001666
1667 /**
1668 * Set a label to display while the wearable is preparing to automatically execute the
1669 * action. This is usually a 'ing' verb ending in ellipsis like "Sending..."
1670 *
1671 * @param label the label to display while the action is being prepared to execute
1672 * @return this object for method chaining
1673 */
1674 public WearableExtender setInProgressLabel(CharSequence label) {
1675 mInProgressLabel = label;
1676 return this;
1677 }
1678
1679 /**
1680 * Get the label to display while the wearable is preparing to automatically execute
1681 * the action. This is usually a 'ing' verb ending in ellipsis like "Sending..."
1682 *
1683 * @return the label to display while the action is being prepared to execute
1684 */
1685 public CharSequence getInProgressLabel() {
1686 return mInProgressLabel;
1687 }
1688
1689 /**
1690 * Set a label to display to confirm that the action should be executed.
1691 * This is usually an imperative verb like "Send".
1692 *
1693 * @param label the label to confirm the action should be executed
1694 * @return this object for method chaining
1695 */
1696 public WearableExtender setConfirmLabel(CharSequence label) {
1697 mConfirmLabel = label;
1698 return this;
1699 }
1700
1701 /**
1702 * Get the label to display to confirm that the action should be executed.
1703 * This is usually an imperative verb like "Send".
1704 *
1705 * @return the label to confirm the action should be executed
1706 */
1707 public CharSequence getConfirmLabel() {
1708 return mConfirmLabel;
1709 }
1710
1711 /**
1712 * Set a label to display to cancel the action.
1713 * This is usually an imperative verb, like "Cancel".
1714 *
1715 * @param label the label to display to cancel the action
1716 * @return this object for method chaining
1717 */
1718 public WearableExtender setCancelLabel(CharSequence label) {
1719 mCancelLabel = label;
1720 return this;
1721 }
1722
1723 /**
1724 * Get the label to display to cancel the action.
1725 * This is usually an imperative verb like "Cancel".
1726 *
1727 * @return the label to display to cancel the action
1728 */
1729 public CharSequence getCancelLabel() {
1730 return mCancelLabel;
1731 }
Alex Hills9ab3a232016-04-05 14:54:56 -04001732
1733 /**
1734 * Set a hint that this Action will launch an {@link Activity} directly, telling the
1735 * platform that it can generate the appropriate transitions.
1736 * @param hintLaunchesActivity {@code true} if the content intent will launch
1737 * an activity and transitions should be generated, false otherwise.
1738 * @return this object for method chaining
1739 */
Alex Hills4ec3ff42016-04-12 11:36:18 -04001740 public WearableExtender setHintLaunchesActivity(
Alex Hills9ab3a232016-04-05 14:54:56 -04001741 boolean hintLaunchesActivity) {
1742 setFlag(FLAG_HINT_LAUNCHES_ACTIVITY, hintLaunchesActivity);
1743 return this;
1744 }
1745
1746 /**
1747 * Get a hint that this Action will launch an {@link Activity} directly, telling the
1748 * platform that it can generate the appropriate transitions
1749 * @return {@code true} if the content intent will launch an activity and transitions
1750 * should be generated, false otherwise. The default value is {@code false} if this was
1751 * never set.
1752 */
Alex Hills4ec3ff42016-04-12 11:36:18 -04001753 public boolean getHintLaunchesActivity() {
Alex Hills9ab3a232016-04-05 14:54:56 -04001754 return (mFlags & FLAG_HINT_LAUNCHES_ACTIVITY) != 0;
1755 }
Alex Hills9f087612016-06-07 09:08:59 -04001756
1757 /**
1758 * Set a hint that this Action should be displayed inline.
1759 *
1760 * @param hintDisplayInline {@code true} if action should be displayed inline, false
1761 * otherwise
1762 * @return this object for method chaining
1763 */
1764 public WearableExtender setHintDisplayActionInline(
1765 boolean hintDisplayInline) {
1766 setFlag(FLAG_HINT_DISPLAY_INLINE, hintDisplayInline);
1767 return this;
1768 }
1769
1770 /**
1771 * Get a hint that this Action should be displayed inline.
1772 *
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08001773 * @return {@code true} if the Action should be displayed inline, {@code false}
Alex Hills9f087612016-06-07 09:08:59 -04001774 * otherwise. The default value is {@code false} if this was never set.
1775 */
1776 public boolean getHintDisplayActionInline() {
1777 return (mFlags & FLAG_HINT_DISPLAY_INLINE) != 0;
1778 }
Griff Hazen61a9e862014-05-22 16:05:19 -07001779 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001780 }
1781
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001782 /**
1783 * Array of all {@link Action} structures attached to this notification by
1784 * {@link Builder#addAction(int, CharSequence, PendingIntent)}. Mostly useful for instances of
1785 * {@link android.service.notification.NotificationListenerService} that provide an alternative
1786 * interface for invoking actions.
1787 */
Daniel Sandlerea2a3172013-02-20 22:24:20 -05001788 public Action[] actions;
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001789
1790 /**
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001791 * Replacement version of this notification whose content will be shown
1792 * in an insecure context such as atop a secure keyguard. See {@link #visibility}
1793 * and {@link #VISIBILITY_PUBLIC}.
1794 */
1795 public Notification publicVersion;
1796
1797 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001798 * Constructs a Notification object with default values.
Joe Onorato46439ce2010-11-19 13:56:21 -08001799 * You might want to consider using {@link Builder} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001800 */
1801 public Notification()
1802 {
1803 this.when = System.currentTimeMillis();
Selim Cinekb85f36fd2016-04-20 18:46:36 -07001804 this.creationTime = System.currentTimeMillis();
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001805 this.priority = PRIORITY_DEFAULT;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001806 }
1807
1808 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001809 * @hide
1810 */
1811 public Notification(Context context, int icon, CharSequence tickerText, long when,
1812 CharSequence contentTitle, CharSequence contentText, Intent contentIntent)
1813 {
Chris Wren1ce4b6d2015-06-11 10:19:43 -04001814 new Builder(context)
1815 .setWhen(when)
1816 .setSmallIcon(icon)
1817 .setTicker(tickerText)
1818 .setContentTitle(contentTitle)
1819 .setContentText(contentText)
1820 .setContentIntent(PendingIntent.getActivity(context, 0, contentIntent, 0))
1821 .buildInto(this);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001822 }
1823
1824 /**
1825 * Constructs a Notification object with the information needed to
1826 * have a status bar icon without the standard expanded view.
1827 *
1828 * @param icon The resource id of the icon to put in the status bar.
1829 * @param tickerText The text that flows by in the status bar when the notification first
1830 * activates.
1831 * @param when The time to show in the time field. In the System.currentTimeMillis
1832 * timebase.
Joe Onorato46439ce2010-11-19 13:56:21 -08001833 *
1834 * @deprecated Use {@link Builder} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001835 */
Joe Onorato46439ce2010-11-19 13:56:21 -08001836 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001837 public Notification(int icon, CharSequence tickerText, long when)
1838 {
1839 this.icon = icon;
1840 this.tickerText = tickerText;
1841 this.when = when;
Selim Cinekb85f36fd2016-04-20 18:46:36 -07001842 this.creationTime = System.currentTimeMillis();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001843 }
1844
1845 /**
1846 * Unflatten the notification from a parcel.
1847 */
Svet Ganovddb94882016-06-23 19:55:24 -07001848 @SuppressWarnings("unchecked")
1849 public Notification(Parcel parcel) {
1850 // IMPORTANT: Add unmarshaling code in readFromParcel as the pending
1851 // intents in extras are always written as the last entry.
1852 readFromParcelImpl(parcel);
1853 // Must be read last!
Felipe Lemedd85da62016-06-28 11:29:54 -07001854 allPendingIntents = (ArraySet<PendingIntent>) parcel.readArraySet(null);
Svet Ganovddb94882016-06-23 19:55:24 -07001855 }
1856
1857 private void readFromParcelImpl(Parcel parcel)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001858 {
1859 int version = parcel.readInt();
1860
Dianne Hackborn98305522017-05-05 17:53:53 -07001861 whitelistToken = parcel.readStrongBinder();
1862 if (whitelistToken == null) {
1863 whitelistToken = processWhitelistToken;
1864 }
1865 // Propagate this token to all pending intents that are unmarshalled from the parcel.
1866 parcel.setClassCookie(PendingIntent.class, whitelistToken);
1867
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001868 when = parcel.readLong();
Selim Cinekb85f36fd2016-04-20 18:46:36 -07001869 creationTime = parcel.readLong();
Dan Sandler3936e7a2015-05-19 20:59:12 -04001870 if (parcel.readInt() != 0) {
1871 mSmallIcon = Icon.CREATOR.createFromParcel(parcel);
Dan Sandler4e787062015-06-17 15:09:48 -04001872 if (mSmallIcon.getType() == Icon.TYPE_RESOURCE) {
1873 icon = mSmallIcon.getResId();
1874 }
Dan Sandler3936e7a2015-05-19 20:59:12 -04001875 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001876 number = parcel.readInt();
1877 if (parcel.readInt() != 0) {
1878 contentIntent = PendingIntent.CREATOR.createFromParcel(parcel);
1879 }
1880 if (parcel.readInt() != 0) {
1881 deleteIntent = PendingIntent.CREATOR.createFromParcel(parcel);
1882 }
1883 if (parcel.readInt() != 0) {
1884 tickerText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
1885 }
1886 if (parcel.readInt() != 0) {
Joe Onorato46439ce2010-11-19 13:56:21 -08001887 tickerView = RemoteViews.CREATOR.createFromParcel(parcel);
Joe Onoratoef1e7762010-09-17 18:38:38 -04001888 }
1889 if (parcel.readInt() != 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001890 contentView = RemoteViews.CREATOR.createFromParcel(parcel);
1891 }
Joe Onorato561d3852010-11-20 18:09:34 -08001892 if (parcel.readInt() != 0) {
Dan Sandlerd63f9322015-05-06 15:18:49 -04001893 mLargeIcon = Icon.CREATOR.createFromParcel(parcel);
Joe Onorato561d3852010-11-20 18:09:34 -08001894 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001895 defaults = parcel.readInt();
1896 flags = parcel.readInt();
1897 if (parcel.readInt() != 0) {
1898 sound = Uri.CREATOR.createFromParcel(parcel);
1899 }
1900
1901 audioStreamType = parcel.readInt();
John Spurlockc0650f022014-07-19 13:22:39 -04001902 if (parcel.readInt() != 0) {
1903 audioAttributes = AudioAttributes.CREATOR.createFromParcel(parcel);
1904 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001905 vibrate = parcel.createLongArray();
1906 ledARGB = parcel.readInt();
1907 ledOnMS = parcel.readInt();
1908 ledOffMS = parcel.readInt();
1909 iconLevel = parcel.readInt();
Daniel Sandlere46cbd32010-06-17 10:35:26 -04001910
1911 if (parcel.readInt() != 0) {
1912 fullScreenIntent = PendingIntent.CREATOR.createFromParcel(parcel);
1913 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001914
1915 priority = parcel.readInt();
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001916
John Spurlockfd7f1e02014-03-18 16:41:57 -04001917 category = parcel.readString();
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001918
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001919 mGroupKey = parcel.readString();
1920
1921 mSortKey = parcel.readString();
1922
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001923 extras = Bundle.setDefusable(parcel.readBundle(), true); // may be null
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001924
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001925 actions = parcel.createTypedArray(Action.CREATOR); // may be null
1926
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001927 if (parcel.readInt() != 0) {
Daniel Sandlerf3b73432012-03-27 15:01:25 -04001928 bigContentView = RemoteViews.CREATOR.createFromParcel(parcel);
1929 }
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001930
Chris Wren8fd39ec2014-02-27 17:43:26 -05001931 if (parcel.readInt() != 0) {
1932 headsUpContentView = RemoteViews.CREATOR.createFromParcel(parcel);
1933 }
1934
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001935 visibility = parcel.readInt();
1936
1937 if (parcel.readInt() != 0) {
1938 publicVersion = Notification.CREATOR.createFromParcel(parcel);
1939 }
Dan Sandler26e81cf2014-05-06 10:01:27 -04001940
1941 color = parcel.readInt();
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04001942
1943 if (parcel.readInt() != 0) {
1944 mChannelId = parcel.readString();
1945 }
Julia Reynolds2a128742016-11-28 14:29:25 -05001946 mTimeout = parcel.readLong();
Julia Reynolds13d898c2017-02-02 12:22:05 -05001947
1948 if (parcel.readInt() != 0) {
1949 mShortcutId = parcel.readString();
1950 }
1951
1952 mBadgeIcon = parcel.readInt();
Julia Reynolds3aedded2017-03-31 14:42:09 -04001953
1954 if (parcel.readInt() != 0) {
1955 mSettingsText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
1956 }
Julia Reynoldsa79c3712017-04-21 10:29:57 -04001957
1958 mGroupAlertBehavior = parcel.readInt();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001959 }
1960
Andy Stadler110988c2010-12-03 14:29:16 -08001961 @Override
Joe Onorato18e69df2010-05-17 22:26:12 -07001962 public Notification clone() {
1963 Notification that = new Notification();
Daniel Sandler1a497d32013-04-18 14:52:45 -04001964 cloneInto(that, true);
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001965 return that;
1966 }
Joe Onorato18e69df2010-05-17 22:26:12 -07001967
Daniel Sandler1a497d32013-04-18 14:52:45 -04001968 /**
1969 * Copy all (or if heavy is false, all except Bitmaps and RemoteViews) members
1970 * of this into that.
1971 * @hide
1972 */
1973 public void cloneInto(Notification that, boolean heavy) {
Dianne Hackborn98305522017-05-05 17:53:53 -07001974 that.whitelistToken = this.whitelistToken;
Joe Onorato18e69df2010-05-17 22:26:12 -07001975 that.when = this.when;
Selim Cinekb85f36fd2016-04-20 18:46:36 -07001976 that.creationTime = this.creationTime;
Dan Sandlerd63f9322015-05-06 15:18:49 -04001977 that.mSmallIcon = this.mSmallIcon;
Joe Onorato18e69df2010-05-17 22:26:12 -07001978 that.number = this.number;
1979
1980 // PendingIntents are global, so there's no reason (or way) to clone them.
1981 that.contentIntent = this.contentIntent;
1982 that.deleteIntent = this.deleteIntent;
Daniel Sandlere46cbd32010-06-17 10:35:26 -04001983 that.fullScreenIntent = this.fullScreenIntent;
Joe Onorato18e69df2010-05-17 22:26:12 -07001984
1985 if (this.tickerText != null) {
1986 that.tickerText = this.tickerText.toString();
1987 }
Daniel Sandler1a497d32013-04-18 14:52:45 -04001988 if (heavy && this.tickerView != null) {
Joe Onorato46439ce2010-11-19 13:56:21 -08001989 that.tickerView = this.tickerView.clone();
Joe Onoratoef1e7762010-09-17 18:38:38 -04001990 }
Daniel Sandler1a497d32013-04-18 14:52:45 -04001991 if (heavy && this.contentView != null) {
Joe Onorato18e69df2010-05-17 22:26:12 -07001992 that.contentView = this.contentView.clone();
1993 }
Dan Sandlerd63f9322015-05-06 15:18:49 -04001994 if (heavy && this.mLargeIcon != null) {
1995 that.mLargeIcon = this.mLargeIcon;
Joe Onorato561d3852010-11-20 18:09:34 -08001996 }
Jozef BABJAKa8b91832011-02-22 08:05:08 +01001997 that.iconLevel = this.iconLevel;
Joe Onorato18e69df2010-05-17 22:26:12 -07001998 that.sound = this.sound; // android.net.Uri is immutable
1999 that.audioStreamType = this.audioStreamType;
John Spurlockc0650f022014-07-19 13:22:39 -04002000 if (this.audioAttributes != null) {
2001 that.audioAttributes = new AudioAttributes.Builder(this.audioAttributes).build();
2002 }
Joe Onorato18e69df2010-05-17 22:26:12 -07002003
2004 final long[] vibrate = this.vibrate;
2005 if (vibrate != null) {
2006 final int N = vibrate.length;
2007 final long[] vib = that.vibrate = new long[N];
2008 System.arraycopy(vibrate, 0, vib, 0, N);
2009 }
2010
2011 that.ledARGB = this.ledARGB;
2012 that.ledOnMS = this.ledOnMS;
2013 that.ledOffMS = this.ledOffMS;
2014 that.defaults = this.defaults;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002015
Joe Onorato18e69df2010-05-17 22:26:12 -07002016 that.flags = this.flags;
2017
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002018 that.priority = this.priority;
Joe Malin8d40d042012-11-05 11:36:40 -08002019
John Spurlockfd7f1e02014-03-18 16:41:57 -04002020 that.category = this.category;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002021
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002022 that.mGroupKey = this.mGroupKey;
2023
2024 that.mSortKey = this.mSortKey;
2025
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002026 if (this.extras != null) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04002027 try {
2028 that.extras = new Bundle(this.extras);
2029 // will unparcel
2030 that.extras.size();
2031 } catch (BadParcelableException e) {
2032 Log.e(TAG, "could not unparcel extras from notification: " + this, e);
2033 that.extras = null;
2034 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002035 }
2036
Felipe Lemedd85da62016-06-28 11:29:54 -07002037 if (!ArrayUtils.isEmpty(allPendingIntents)) {
2038 that.allPendingIntents = new ArraySet<>(allPendingIntents);
Svet Ganovddb94882016-06-23 19:55:24 -07002039 }
2040
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002041 if (this.actions != null) {
2042 that.actions = new Action[this.actions.length];
2043 for(int i=0; i<this.actions.length; i++) {
liangweikang63b03b52017-03-16 19:22:15 +08002044 if ( this.actions[i] != null) {
2045 that.actions[i] = this.actions[i].clone();
2046 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002047 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002048 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002049
Daniel Sandler1a497d32013-04-18 14:52:45 -04002050 if (heavy && this.bigContentView != null) {
Daniel Sandlerf3b73432012-03-27 15:01:25 -04002051 that.bigContentView = this.bigContentView.clone();
2052 }
Daniel Sandler1a497d32013-04-18 14:52:45 -04002053
Chris Wren8fd39ec2014-02-27 17:43:26 -05002054 if (heavy && this.headsUpContentView != null) {
2055 that.headsUpContentView = this.headsUpContentView.clone();
2056 }
2057
Dan Sandler0bf2ed82013-12-21 23:33:41 -06002058 that.visibility = this.visibility;
2059
2060 if (this.publicVersion != null) {
2061 that.publicVersion = new Notification();
2062 this.publicVersion.cloneInto(that.publicVersion, heavy);
2063 }
2064
Dan Sandler26e81cf2014-05-06 10:01:27 -04002065 that.color = this.color;
2066
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04002067 that.mChannelId = this.mChannelId;
Julia Reynolds2a128742016-11-28 14:29:25 -05002068 that.mTimeout = this.mTimeout;
Julia Reynolds3aedded2017-03-31 14:42:09 -04002069 that.mShortcutId = this.mShortcutId;
2070 that.mBadgeIcon = this.mBadgeIcon;
2071 that.mSettingsText = this.mSettingsText;
Julia Reynoldsa79c3712017-04-21 10:29:57 -04002072 that.mGroupAlertBehavior = this.mGroupAlertBehavior;
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04002073
Daniel Sandler1a497d32013-04-18 14:52:45 -04002074 if (!heavy) {
2075 that.lightenPayload(); // will clean out extras
2076 }
2077 }
2078
2079 /**
2080 * Removes heavyweight parts of the Notification object for archival or for sending to
2081 * listeners when the full contents are not necessary.
2082 * @hide
2083 */
2084 public final void lightenPayload() {
2085 tickerView = null;
2086 contentView = null;
2087 bigContentView = null;
Chris Wren8fd39ec2014-02-27 17:43:26 -05002088 headsUpContentView = null;
Dan Sandlerd63f9322015-05-06 15:18:49 -04002089 mLargeIcon = null;
Dan Sandler50128532015-12-08 15:42:41 -05002090 if (extras != null && !extras.isEmpty()) {
2091 final Set<String> keyset = extras.keySet();
2092 final int N = keyset.size();
2093 final String[] keys = keyset.toArray(new String[N]);
2094 for (int i=0; i<N; i++) {
2095 final String key = keys[i];
Dmitri Plotnikov22281362017-01-30 11:16:21 -08002096 if (TvExtender.EXTRA_TV_EXTENDER.equals(key)) {
2097 continue;
2098 }
Dan Sandler50128532015-12-08 15:42:41 -05002099 final Object obj = extras.get(key);
2100 if (obj != null &&
2101 ( obj instanceof Parcelable
2102 || obj instanceof Parcelable[]
2103 || obj instanceof SparseArray
2104 || obj instanceof ArrayList)) {
2105 extras.remove(key);
2106 }
2107 }
Daniel Sandler1a497d32013-04-18 14:52:45 -04002108 }
Joe Onorato18e69df2010-05-17 22:26:12 -07002109 }
2110
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04002111 /**
2112 * Make sure this CharSequence is safe to put into a bundle, which basically
2113 * means it had better not be some custom Parcelable implementation.
2114 * @hide
2115 */
2116 public static CharSequence safeCharSequence(CharSequence cs) {
Christoph Studer535ec612014-09-03 15:47:47 +02002117 if (cs == null) return cs;
2118 if (cs.length() > MAX_CHARSEQUENCE_LENGTH) {
2119 cs = cs.subSequence(0, MAX_CHARSEQUENCE_LENGTH);
2120 }
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04002121 if (cs instanceof Parcelable) {
2122 Log.e(TAG, "warning: " + cs.getClass().getCanonicalName()
2123 + " instance is a custom Parcelable and not allowed in Notification");
2124 return cs.toString();
2125 }
Selim Cinek60a54252016-02-26 17:03:25 -08002126 return removeTextSizeSpans(cs);
2127 }
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04002128
Selim Cinek60a54252016-02-26 17:03:25 -08002129 private static CharSequence removeTextSizeSpans(CharSequence charSequence) {
2130 if (charSequence instanceof Spanned) {
2131 Spanned ss = (Spanned) charSequence;
2132 Object[] spans = ss.getSpans(0, ss.length(), Object.class);
2133 SpannableStringBuilder builder = new SpannableStringBuilder(ss.toString());
2134 for (Object span : spans) {
2135 Object resultSpan = span;
Selim Cinek89991a22016-03-07 19:51:54 -08002136 if (resultSpan instanceof CharacterStyle) {
2137 resultSpan = ((CharacterStyle) span).getUnderlying();
2138 }
2139 if (resultSpan instanceof TextAppearanceSpan) {
2140 TextAppearanceSpan originalSpan = (TextAppearanceSpan) resultSpan;
Selim Cinek60a54252016-02-26 17:03:25 -08002141 resultSpan = new TextAppearanceSpan(
2142 originalSpan.getFamily(),
2143 originalSpan.getTextStyle(),
2144 -1,
2145 originalSpan.getTextColor(),
2146 originalSpan.getLinkTextColor());
Selim Cinek89991a22016-03-07 19:51:54 -08002147 } else if (resultSpan instanceof RelativeSizeSpan
2148 || resultSpan instanceof AbsoluteSizeSpan) {
Selim Cinek60a54252016-02-26 17:03:25 -08002149 continue;
Selim Cinek89991a22016-03-07 19:51:54 -08002150 } else {
2151 resultSpan = span;
Selim Cinek60a54252016-02-26 17:03:25 -08002152 }
2153 builder.setSpan(resultSpan, ss.getSpanStart(span), ss.getSpanEnd(span),
2154 ss.getSpanFlags(span));
2155 }
2156 return builder;
2157 }
2158 return charSequence;
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04002159 }
2160
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002161 public int describeContents() {
2162 return 0;
2163 }
2164
2165 /**
Dan Sandler4e787062015-06-17 15:09:48 -04002166 * Flatten this notification into a parcel.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002167 */
Svet Ganovddb94882016-06-23 19:55:24 -07002168 public void writeToParcel(Parcel parcel, int flags) {
2169 // We need to mark all pending intents getting into the notification
2170 // system as being put there to later allow the notification ranker
2171 // to launch them and by doing so add the app to the battery saver white
2172 // list for a short period of time. The problem is that the system
2173 // cannot look into the extras as there may be parcelables there that
2174 // the platform does not know how to handle. To go around that we have
2175 // an explicit list of the pending intents in the extras bundle.
Felipe Lemedd85da62016-06-28 11:29:54 -07002176 final boolean collectPendingIntents = (allPendingIntents == null);
Svet Ganovddb94882016-06-23 19:55:24 -07002177 if (collectPendingIntents) {
2178 PendingIntent.setOnMarshaledListener(
2179 (PendingIntent intent, Parcel out, int outFlags) -> {
2180 if (parcel == out) {
Felipe Lemedd85da62016-06-28 11:29:54 -07002181 if (allPendingIntents == null) {
2182 allPendingIntents = new ArraySet<>();
Svet Ganovddb94882016-06-23 19:55:24 -07002183 }
Felipe Lemedd85da62016-06-28 11:29:54 -07002184 allPendingIntents.add(intent);
Svet Ganovddb94882016-06-23 19:55:24 -07002185 }
2186 });
2187 }
2188 try {
2189 // IMPORTANT: Add marshaling code in writeToParcelImpl as we
Julia Reynolds13d898c2017-02-02 12:22:05 -05002190 // want to intercept all pending events written to the parcel.
Svet Ganovddb94882016-06-23 19:55:24 -07002191 writeToParcelImpl(parcel, flags);
2192 // Must be written last!
Felipe Lemedd85da62016-06-28 11:29:54 -07002193 parcel.writeArraySet(allPendingIntents);
Svet Ganovddb94882016-06-23 19:55:24 -07002194 } finally {
2195 if (collectPendingIntents) {
2196 PendingIntent.setOnMarshaledListener(null);
2197 }
2198 }
2199 }
2200
2201 private void writeToParcelImpl(Parcel parcel, int flags) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002202 parcel.writeInt(1);
2203
Dianne Hackborn98305522017-05-05 17:53:53 -07002204 parcel.writeStrongBinder(whitelistToken);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002205 parcel.writeLong(when);
Selim Cinekb85f36fd2016-04-20 18:46:36 -07002206 parcel.writeLong(creationTime);
Dan Sandler4e787062015-06-17 15:09:48 -04002207 if (mSmallIcon == null && icon != 0) {
2208 // you snuck an icon in here without using the builder; let's try to keep it
2209 mSmallIcon = Icon.createWithResource("", icon);
2210 }
Dan Sandler3936e7a2015-05-19 20:59:12 -04002211 if (mSmallIcon != null) {
2212 parcel.writeInt(1);
2213 mSmallIcon.writeToParcel(parcel, 0);
2214 } else {
2215 parcel.writeInt(0);
2216 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002217 parcel.writeInt(number);
2218 if (contentIntent != null) {
2219 parcel.writeInt(1);
2220 contentIntent.writeToParcel(parcel, 0);
2221 } else {
2222 parcel.writeInt(0);
2223 }
2224 if (deleteIntent != null) {
2225 parcel.writeInt(1);
2226 deleteIntent.writeToParcel(parcel, 0);
2227 } else {
2228 parcel.writeInt(0);
2229 }
2230 if (tickerText != null) {
2231 parcel.writeInt(1);
2232 TextUtils.writeToParcel(tickerText, parcel, flags);
2233 } else {
2234 parcel.writeInt(0);
2235 }
Joe Onorato46439ce2010-11-19 13:56:21 -08002236 if (tickerView != null) {
Joe Onoratoef1e7762010-09-17 18:38:38 -04002237 parcel.writeInt(1);
Joe Onorato46439ce2010-11-19 13:56:21 -08002238 tickerView.writeToParcel(parcel, 0);
Joe Onoratoef1e7762010-09-17 18:38:38 -04002239 } else {
2240 parcel.writeInt(0);
2241 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002242 if (contentView != null) {
2243 parcel.writeInt(1);
2244 contentView.writeToParcel(parcel, 0);
2245 } else {
2246 parcel.writeInt(0);
2247 }
Selim Cinek279fa862016-06-14 10:57:25 -07002248 if (mLargeIcon == null && largeIcon != null) {
2249 // you snuck an icon in here without using the builder; let's try to keep it
2250 mLargeIcon = Icon.createWithBitmap(largeIcon);
2251 }
Dan Sandlerd63f9322015-05-06 15:18:49 -04002252 if (mLargeIcon != null) {
Joe Onorato561d3852010-11-20 18:09:34 -08002253 parcel.writeInt(1);
Dan Sandlerd63f9322015-05-06 15:18:49 -04002254 mLargeIcon.writeToParcel(parcel, 0);
Joe Onorato561d3852010-11-20 18:09:34 -08002255 } else {
2256 parcel.writeInt(0);
2257 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002258
2259 parcel.writeInt(defaults);
2260 parcel.writeInt(this.flags);
2261
2262 if (sound != null) {
2263 parcel.writeInt(1);
2264 sound.writeToParcel(parcel, 0);
2265 } else {
2266 parcel.writeInt(0);
2267 }
2268 parcel.writeInt(audioStreamType);
John Spurlockc0650f022014-07-19 13:22:39 -04002269
2270 if (audioAttributes != null) {
2271 parcel.writeInt(1);
2272 audioAttributes.writeToParcel(parcel, 0);
2273 } else {
2274 parcel.writeInt(0);
2275 }
2276
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002277 parcel.writeLongArray(vibrate);
2278 parcel.writeInt(ledARGB);
2279 parcel.writeInt(ledOnMS);
2280 parcel.writeInt(ledOffMS);
2281 parcel.writeInt(iconLevel);
Daniel Sandlere46cbd32010-06-17 10:35:26 -04002282
2283 if (fullScreenIntent != null) {
2284 parcel.writeInt(1);
2285 fullScreenIntent.writeToParcel(parcel, 0);
2286 } else {
2287 parcel.writeInt(0);
2288 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002289
2290 parcel.writeInt(priority);
Joe Malin8d40d042012-11-05 11:36:40 -08002291
John Spurlockfd7f1e02014-03-18 16:41:57 -04002292 parcel.writeString(category);
Joe Malin8d40d042012-11-05 11:36:40 -08002293
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002294 parcel.writeString(mGroupKey);
2295
2296 parcel.writeString(mSortKey);
2297
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002298 parcel.writeBundle(extras); // null ok
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002299
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002300 parcel.writeTypedArray(actions, 0); // null ok
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002301
Daniel Sandlerf3b73432012-03-27 15:01:25 -04002302 if (bigContentView != null) {
2303 parcel.writeInt(1);
2304 bigContentView.writeToParcel(parcel, 0);
2305 } else {
2306 parcel.writeInt(0);
2307 }
Dan Sandler0bf2ed82013-12-21 23:33:41 -06002308
Chris Wren8fd39ec2014-02-27 17:43:26 -05002309 if (headsUpContentView != null) {
2310 parcel.writeInt(1);
2311 headsUpContentView.writeToParcel(parcel, 0);
2312 } else {
2313 parcel.writeInt(0);
2314 }
2315
Dan Sandler0bf2ed82013-12-21 23:33:41 -06002316 parcel.writeInt(visibility);
2317
2318 if (publicVersion != null) {
2319 parcel.writeInt(1);
2320 publicVersion.writeToParcel(parcel, 0);
2321 } else {
2322 parcel.writeInt(0);
2323 }
Dan Sandler26e81cf2014-05-06 10:01:27 -04002324
2325 parcel.writeInt(color);
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04002326
2327 if (mChannelId != null) {
2328 parcel.writeInt(1);
2329 parcel.writeString(mChannelId);
2330 } else {
2331 parcel.writeInt(0);
2332 }
Julia Reynolds2a128742016-11-28 14:29:25 -05002333 parcel.writeLong(mTimeout);
Julia Reynolds13d898c2017-02-02 12:22:05 -05002334
2335 if (mShortcutId != null) {
2336 parcel.writeInt(1);
2337 parcel.writeString(mShortcutId);
2338 } else {
2339 parcel.writeInt(0);
2340 }
2341
2342 parcel.writeInt(mBadgeIcon);
Julia Reynolds3aedded2017-03-31 14:42:09 -04002343
2344 if (mSettingsText != null) {
2345 parcel.writeInt(1);
2346 TextUtils.writeToParcel(mSettingsText, parcel, flags);
2347 } else {
2348 parcel.writeInt(0);
2349 }
Julia Reynoldsa79c3712017-04-21 10:29:57 -04002350
2351 parcel.writeInt(mGroupAlertBehavior);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002352 }
2353
2354 /**
2355 * Parcelable.Creator that instantiates Notification objects
2356 */
2357 public static final Parcelable.Creator<Notification> CREATOR
2358 = new Parcelable.Creator<Notification>()
2359 {
2360 public Notification createFromParcel(Parcel parcel)
2361 {
2362 return new Notification(parcel);
2363 }
2364
2365 public Notification[] newArray(int size)
2366 {
2367 return new Notification[size];
2368 }
2369 };
2370
2371 /**
2372 * Sets the {@link #contentView} field to be a view with the standard "Latest Event"
2373 * layout.
2374 *
2375 * <p>Uses the {@link #icon} and {@link #when} fields to set the icon and time fields
2376 * in the view.</p>
2377 * @param context The context for your application / activity.
2378 * @param contentTitle The title that goes in the expanded entry.
2379 * @param contentText The text that goes in the expanded entry.
2380 * @param contentIntent The intent to launch when the user clicks the expanded notification.
2381 * If this is an activity, it must include the
2382 * {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} flag, which requires
Scott Main7aee61f2011-02-08 11:25:01 -08002383 * that you take care of task management as described in the
2384 * <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back
2385 * Stack</a> document.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002386 *
Joe Onorato46439ce2010-11-19 13:56:21 -08002387 * @deprecated Use {@link Builder} instead.
Chris Wrena05db382015-06-24 15:18:34 -04002388 * @removed
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002389 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002390 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002391 public void setLatestEventInfo(Context context,
2392 CharSequence contentTitle, CharSequence contentText, PendingIntent contentIntent) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002393 if (context.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1){
2394 Log.e(TAG, "setLatestEventInfo() is deprecated and you should feel deprecated.",
2395 new Throwable());
2396 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002397
Selim Cinek4ac6f602016-06-13 15:47:03 -07002398 if (context.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N) {
2399 extras.putBoolean(EXTRA_SHOW_WHEN, true);
2400 }
2401
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002402 // ensure that any information already set directly is preserved
2403 final Notification.Builder builder = new Notification.Builder(context, this);
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002404
2405 // now apply the latestEventInfo fields
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002406 if (contentTitle != null) {
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002407 builder.setContentTitle(contentTitle);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002408 }
2409 if (contentText != null) {
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002410 builder.setContentText(contentText);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002411 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002412 builder.setContentIntent(contentIntent);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002413
2414 builder.build(); // callers expect this notification to be ready to use
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002415 }
2416
Julia Reynoldsda303542015-11-23 14:00:20 -05002417 /**
2418 * @hide
2419 */
2420 public static void addFieldsFromContext(Context context, Notification notification) {
Julia Reynoldse071abd2017-03-22 10:52:11 -04002421 addFieldsFromContext(context.getApplicationInfo(), notification);
Jeff Sharkey012bc7b2016-04-11 16:30:27 -06002422 }
2423
2424 /**
2425 * @hide
2426 */
Julia Reynoldse071abd2017-03-22 10:52:11 -04002427 public static void addFieldsFromContext(ApplicationInfo ai, Notification notification) {
Jeff Sharkey012bc7b2016-04-11 16:30:27 -06002428 notification.extras.putParcelable(EXTRA_BUILDER_APPLICATION_INFO, ai);
Julia Reynoldsda303542015-11-23 14:00:20 -05002429 }
2430
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002431 @Override
2432 public String toString() {
2433 StringBuilder sb = new StringBuilder();
Julia Reynoldsb9e712e2017-04-17 10:31:03 -04002434 sb.append("Notification(channel=");
Julia Reynoldsbad42972017-04-25 13:52:49 -04002435 sb.append(getChannelId());
Julia Reynoldsb9e712e2017-04-17 10:31:03 -04002436 sb.append(" pri=");
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002437 sb.append(priority);
2438 sb.append(" contentView=");
Joe Onoratoc9596d62011-01-12 17:03:11 -08002439 if (contentView != null) {
2440 sb.append(contentView.getPackage());
2441 sb.append("/0x");
2442 sb.append(Integer.toHexString(contentView.getLayoutId()));
2443 } else {
2444 sb.append("null");
2445 }
2446 sb.append(" vibrate=");
Daniel Sandler6738eee2012-11-16 12:03:32 -05002447 if ((this.defaults & DEFAULT_VIBRATE) != 0) {
2448 sb.append("default");
2449 } else if (this.vibrate != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002450 int N = this.vibrate.length-1;
2451 sb.append("[");
2452 for (int i=0; i<N; i++) {
2453 sb.append(this.vibrate[i]);
2454 sb.append(',');
2455 }
Simon Schoar8cf97d92009-06-10 22:08:37 +02002456 if (N != -1) {
2457 sb.append(this.vibrate[N]);
2458 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002459 sb.append("]");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002460 } else {
2461 sb.append("null");
2462 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002463 sb.append(" sound=");
Daniel Sandler6738eee2012-11-16 12:03:32 -05002464 if ((this.defaults & DEFAULT_SOUND) != 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002465 sb.append("default");
Daniel Sandler6738eee2012-11-16 12:03:32 -05002466 } else if (this.sound != null) {
2467 sb.append(this.sound.toString());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002468 } else {
2469 sb.append("null");
2470 }
Chris Wren365b6d32015-07-16 10:39:26 -04002471 if (this.tickerText != null) {
2472 sb.append(" tick");
2473 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002474 sb.append(" defaults=0x");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002475 sb.append(Integer.toHexString(this.defaults));
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002476 sb.append(" flags=0x");
Daniel Sandlere46cbd32010-06-17 10:35:26 -04002477 sb.append(Integer.toHexString(this.flags));
Dan Sandler26e81cf2014-05-06 10:01:27 -04002478 sb.append(String.format(" color=0x%08x", this.color));
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002479 if (this.category != null) {
2480 sb.append(" category=");
2481 sb.append(this.category);
2482 }
2483 if (this.mGroupKey != null) {
2484 sb.append(" groupKey=");
2485 sb.append(this.mGroupKey);
2486 }
2487 if (this.mSortKey != null) {
2488 sb.append(" sortKey=");
2489 sb.append(this.mSortKey);
2490 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002491 if (actions != null) {
Dan Sandler1b718782014-07-18 12:43:45 -04002492 sb.append(" actions=");
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002493 sb.append(actions.length);
Dan Sandler1b718782014-07-18 12:43:45 -04002494 }
2495 sb.append(" vis=");
2496 sb.append(visibilityToString(this.visibility));
2497 if (this.publicVersion != null) {
2498 sb.append(" publicVersion=");
2499 sb.append(publicVersion.toString());
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002500 }
2501 sb.append(")");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002502 return sb.toString();
2503 }
Joe Onorato46439ce2010-11-19 13:56:21 -08002504
Dan Sandler1b718782014-07-18 12:43:45 -04002505 /**
2506 * {@hide}
2507 */
2508 public static String visibilityToString(int vis) {
2509 switch (vis) {
2510 case VISIBILITY_PRIVATE:
2511 return "PRIVATE";
2512 case VISIBILITY_PUBLIC:
2513 return "PUBLIC";
2514 case VISIBILITY_SECRET:
2515 return "SECRET";
2516 default:
2517 return "UNKNOWN(" + String.valueOf(vis) + ")";
2518 }
2519 }
2520
Joe Onoratocb109a02011-01-18 17:57:41 -08002521 /**
John Spurlock1d881a12015-03-18 19:21:54 -04002522 * {@hide}
2523 */
2524 public static String priorityToString(@Priority int pri) {
2525 switch (pri) {
2526 case PRIORITY_MIN:
2527 return "MIN";
2528 case PRIORITY_LOW:
2529 return "LOW";
2530 case PRIORITY_DEFAULT:
2531 return "DEFAULT";
2532 case PRIORITY_HIGH:
2533 return "HIGH";
2534 case PRIORITY_MAX:
2535 return "MAX";
2536 default:
2537 return "UNKNOWN(" + String.valueOf(pri) + ")";
2538 }
2539 }
2540
Jeff Sharkey000ce802017-04-29 13:13:27 -06002541 /** @removed */
2542 @Deprecated
Julia Reynolds37856052016-11-11 09:20:07 -05002543 public String getChannel() {
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04002544 return mChannelId;
2545 }
2546
2547 /**
Julia Reynoldsbad42972017-04-25 13:52:49 -04002548 * Returns the id of the channel this notification posts to.
2549 */
2550 public String getChannelId() {
2551 return mChannelId;
2552 }
2553
Jeff Sharkey000ce802017-04-29 13:13:27 -06002554 /** @removed */
2555 @Deprecated
Julia Reynolds2a128742016-11-28 14:29:25 -05002556 public long getTimeout() {
2557 return mTimeout;
2558 }
2559
2560 /**
Julia Reynoldsbad42972017-04-25 13:52:49 -04002561 * Returns the duration from posting after which this notification should be canceled by the
2562 * system, if it's not canceled already.
2563 */
2564 public long getTimeoutAfter() {
2565 return mTimeout;
2566 }
2567
2568 /**
Julia Reynoldse071abd2017-03-22 10:52:11 -04002569 * Returns what icon should be shown for this notification if it is being displayed in a
2570 * Launcher that supports badging. Will be one of {@link #BADGE_ICON_NONE},
2571 * {@link #BADGE_ICON_SMALL}, or {@link #BADGE_ICON_LARGE}.
2572 */
2573 public int getBadgeIconType() {
2574 return mBadgeIcon;
2575 }
2576
2577 /**
Julia Reynolds13d898c2017-02-02 12:22:05 -05002578 * Returns the {@link ShortcutInfo#getId() id} that this notification supersedes, if any.
Julia Reynoldsbad42972017-04-25 13:52:49 -04002579 *
2580 * <p>Used by some Launchers that display notification content to hide shortcuts that duplicate
2581 * notifications.
Julia Reynolds13d898c2017-02-02 12:22:05 -05002582 */
2583 public String getShortcutId() {
2584 return mShortcutId;
2585 }
2586
Julia Reynolds3aedded2017-03-31 14:42:09 -04002587
2588 /**
2589 * Returns the settings text provided to {@link Builder#setSettingsText(CharSequence)}.
2590 */
2591 public CharSequence getSettingsText() {
2592 return mSettingsText;
2593 }
2594
Julia Reynolds13d898c2017-02-02 12:22:05 -05002595 /**
Julia Reynoldsa79c3712017-04-21 10:29:57 -04002596 * Returns which type of notifications in a group are responsible for audibly alerting the
2597 * user. See {@link #GROUP_ALERT_ALL}, {@link #GROUP_ALERT_CHILDREN},
2598 * {@link #GROUP_ALERT_SUMMARY}.
2599 */
2600 public @GroupAlertBehavior int getGroupAlertBehavior() {
2601 return mGroupAlertBehavior;
2602 }
2603
2604 /**
Dan Sandlerd63f9322015-05-06 15:18:49 -04002605 * The small icon representing this notification in the status bar and content view.
2606 *
2607 * @return the small icon representing this notification.
2608 *
2609 * @see Builder#getSmallIcon()
2610 * @see Builder#setSmallIcon(Icon)
2611 */
2612 public Icon getSmallIcon() {
2613 return mSmallIcon;
2614 }
2615
2616 /**
2617 * Used when notifying to clean up legacy small icons.
2618 * @hide
2619 */
2620 public void setSmallIcon(Icon icon) {
2621 mSmallIcon = icon;
2622 }
2623
2624 /**
2625 * The large icon shown in this notification's content view.
2626 * @see Builder#getLargeIcon()
2627 * @see Builder#setLargeIcon(Icon)
2628 */
2629 public Icon getLargeIcon() {
2630 return mLargeIcon;
2631 }
2632
2633 /**
Christoph Studer4600f9b2014-07-22 22:44:43 +02002634 * @hide
2635 */
Christoph Studerc8db24b2014-07-25 17:50:30 +02002636 public boolean isGroupSummary() {
2637 return mGroupKey != null && (flags & FLAG_GROUP_SUMMARY) != 0;
2638 }
2639
2640 /**
2641 * @hide
2642 */
2643 public boolean isGroupChild() {
2644 return mGroupKey != null && (flags & FLAG_GROUP_SUMMARY) == 0;
2645 }
2646
2647 /**
Julia Reynolds30203152017-05-26 13:36:31 -04002648 * @hide
2649 */
2650 public boolean suppressAlertingDueToGrouping() {
2651 if (isGroupSummary()
2652 && getGroupAlertBehavior() == Notification.GROUP_ALERT_CHILDREN) {
2653 return true;
2654 } else if (isGroupChild()
2655 && getGroupAlertBehavior() == Notification.GROUP_ALERT_SUMMARY) {
2656 return true;
2657 }
2658 return false;
2659 }
2660
2661 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002662 * Builder class for {@link Notification} objects.
Joe Malin8d40d042012-11-05 11:36:40 -08002663 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002664 * Provides a convenient way to set the various fields of a {@link Notification} and generate
Scott Main183bf112012-08-13 19:12:13 -07002665 * content views using the platform's notification layout template. If your app supports
2666 * versions of Android as old as API level 4, you can instead use
2667 * {@link android.support.v4.app.NotificationCompat.Builder NotificationCompat.Builder},
2668 * available in the <a href="{@docRoot}tools/extras/support-library.html">Android Support
2669 * library</a>.
Joe Malin8d40d042012-11-05 11:36:40 -08002670 *
Scott Main183bf112012-08-13 19:12:13 -07002671 * <p>Example:
Joe Malin8d40d042012-11-05 11:36:40 -08002672 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002673 * <pre class="prettyprint">
Scott Main183bf112012-08-13 19:12:13 -07002674 * Notification noti = new Notification.Builder(mContext)
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002675 * .setContentTitle(&quot;New mail from &quot; + sender.toString())
2676 * .setContentText(subject)
2677 * .setSmallIcon(R.drawable.new_mail)
2678 * .setLargeIcon(aBitmap)
Chris Wrenfbd96ba2012-05-01 12:03:58 -04002679 * .build();
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002680 * </pre>
Joe Onoratocb109a02011-01-18 17:57:41 -08002681 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002682 public static class Builder {
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05002683 /**
2684 * @hide
2685 */
2686 public static final String EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT =
2687 "android.rebuild.contentViewActionCount";
2688 /**
2689 * @hide
2690 */
2691 public static final String EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT
2692 = "android.rebuild.bigViewActionCount";
2693 /**
2694 * @hide
2695 */
2696 public static final String EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT
2697 = "android.rebuild.hudViewActionCount";
2698
Daniel Sandler602ad1c2012-06-12 16:06:27 -04002699 private static final int MAX_ACTION_BUTTONS = 3;
Daniel Sandler8680bf82012-05-15 16:52:52 -04002700
Selim Cinek6743c0b2017-01-18 18:24:01 -08002701 private static final boolean USE_ONLY_TITLE_IN_LOW_PRIORITY_SUMMARY =
2702 SystemProperties.getBoolean("notifications.only_title", true);
2703
Selim Cinek389edcd2017-05-11 19:16:44 -07002704 /**
2705 * The lightness difference that has to be added to the primary text color to obtain the
2706 * secondary text color when the background is light.
2707 */
2708 private static final int LIGHTNESS_TEXT_DIFFERENCE_LIGHT = 20;
2709
2710 /**
2711 * The lightness difference that has to be added to the primary text color to obtain the
2712 * secondary text color when the background is dark.
2713 * A bit less then the above value, since it looks better on dark backgrounds.
2714 */
2715 private static final int LIGHTNESS_TEXT_DIFFERENCE_DARK = -10;
2716
Joe Onorato46439ce2010-11-19 13:56:21 -08002717 private Context mContext;
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002718 private Notification mN;
2719 private Bundle mUserExtras = new Bundle();
Chris Wrenfbd96ba2012-05-01 12:03:58 -04002720 private Style mStyle;
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002721 private ArrayList<Action> mActions = new ArrayList<Action>(MAX_ACTION_BUTTONS);
2722 private ArrayList<String> mPersonList = new ArrayList<String>();
2723 private NotificationColorUtil mColorUtil;
Selim Cinek7b9605b2017-01-19 17:36:00 -08002724 private boolean mIsLegacy;
2725 private boolean mIsLegacyInitialized;
Christoph Studer7ac80e62014-08-04 16:01:57 +02002726
2727 /**
Adrian Roos4ff3b122016-02-01 12:26:13 -08002728 * Caches a contrast-enhanced version of {@link #mCachedContrastColorIsFor}.
2729 */
2730 private int mCachedContrastColor = COLOR_INVALID;
2731 private int mCachedContrastColorIsFor = COLOR_INVALID;
Adrian Roos487374f2017-01-11 15:48:14 -08002732 /**
2733 * Caches a ambient version of {@link #mCachedContrastColorIsFor}.
2734 */
2735 private int mCachedAmbientColor = COLOR_INVALID;
2736 private int mCachedAmbientColorIsFor = COLOR_INVALID;
Adrian Roos4ff3b122016-02-01 12:26:13 -08002737
2738 /**
Adrian Roos70d7aa32017-01-11 15:39:06 -08002739 * Caches an instance of StandardTemplateParams. Note that this may have been used before,
2740 * so make sure to call {@link StandardTemplateParams#reset()} before using it.
2741 */
2742 StandardTemplateParams mParams = new StandardTemplateParams();
Selim Cinek7b9605b2017-01-19 17:36:00 -08002743 private int mTextColorsAreForBackground = COLOR_INVALID;
2744 private int mPrimaryTextColor = COLOR_INVALID;
2745 private int mSecondaryTextColor = COLOR_INVALID;
2746 private int mActionBarColor = COLOR_INVALID;
Selim Cinek5fb73f82017-04-20 16:55:38 -07002747 private int mBackgroundColor = COLOR_INVALID;
2748 private int mForegroundColor = COLOR_INVALID;
Selim Cinekac5f0272017-05-02 16:05:41 -07002749 private int mBackgroundColorHint = COLOR_INVALID;
Selim Cinek5d6ef8d2017-05-18 22:16:00 -07002750 /**
2751 * A temporary location where actions are stored. If != null the view originally has action
2752 * but doesn't have any for this inflation.
2753 */
2754 private ArrayList<Action> mOriginalActions;
Selim Cineka7679b62017-05-10 16:33:25 -07002755 private boolean mRebuildStyledRemoteViews;
Adrian Roos70d7aa32017-01-11 15:39:06 -08002756
Anthony Chenad4d1582017-04-10 16:07:58 -07002757 private boolean mTintActionButtons;
2758 private boolean mInNightMode;
2759
Adrian Roos70d7aa32017-01-11 15:39:06 -08002760 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002761 * Constructs a new Builder with the defaults:
Joe Onoratocb109a02011-01-18 17:57:41 -08002762 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002763 * @param context
2764 * A {@link Context} that will be used by the Builder to construct the
2765 * RemoteViews. The Context will not be held past the lifetime of this Builder
2766 * object.
Geoffrey Pitsch5caa2762017-01-12 09:35:54 -05002767 * @param channelId
2768 * The constructed Notification will be posted on this
2769 * {@link NotificationChannel}. To use a NotificationChannel, it must first be
2770 * created using {@link NotificationManager#createNotificationChannel}.
Joe Onoratocb109a02011-01-18 17:57:41 -08002771 */
Geoffrey Pitsch5caa2762017-01-12 09:35:54 -05002772 public Builder(Context context, String channelId) {
2773 this(context, (Notification) null);
2774 mN.mChannelId = channelId;
2775 }
2776
2777 /**
2778 * @deprecated use {@link Notification.Builder#Notification.Builder(Context, String)}
2779 * instead. All posted Notifications must specify a NotificationChannel Id.
2780 */
2781 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08002782 public Builder(Context context) {
Geoffrey Pitsch5caa2762017-01-12 09:35:54 -05002783 this(context, (Notification) null);
Joe Onorato46439ce2010-11-19 13:56:21 -08002784 }
2785
Joe Onoratocb109a02011-01-18 17:57:41 -08002786 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002787 * @hide
Christoph Studer4600f9b2014-07-22 22:44:43 +02002788 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002789 public Builder(Context context, Notification toAdopt) {
2790 mContext = context;
Anthony Chenad4d1582017-04-10 16:07:58 -07002791 Resources res = mContext.getResources();
2792 mTintActionButtons = res.getBoolean(R.bool.config_tintNotificationActionButtons);
2793
2794 if (res.getBoolean(R.bool.config_enableNightMode)) {
2795 Configuration currentConfig = res.getConfiguration();
2796 mInNightMode = (currentConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK)
2797 == Configuration.UI_MODE_NIGHT_YES;
2798 }
Christoph Studer4600f9b2014-07-22 22:44:43 +02002799
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002800 if (toAdopt == null) {
2801 mN = new Notification();
Selim Cinek0ff1ce602016-04-05 18:27:16 -07002802 if (context.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N) {
2803 mN.extras.putBoolean(EXTRA_SHOW_WHEN, true);
2804 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002805 mN.priority = PRIORITY_DEFAULT;
2806 mN.visibility = VISIBILITY_PRIVATE;
2807 } else {
2808 mN = toAdopt;
2809 if (mN.actions != null) {
2810 Collections.addAll(mActions, mN.actions);
Christoph Studer4600f9b2014-07-22 22:44:43 +02002811 }
2812
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002813 if (mN.extras.containsKey(EXTRA_PEOPLE)) {
2814 Collections.addAll(mPersonList, mN.extras.getStringArray(EXTRA_PEOPLE));
2815 }
2816
Selim Cinek4ac6f602016-06-13 15:47:03 -07002817 if (mN.getSmallIcon() == null && mN.icon != 0) {
2818 setSmallIcon(mN.icon);
2819 }
2820
2821 if (mN.getLargeIcon() == null && mN.largeIcon != null) {
2822 setLargeIcon(mN.largeIcon);
2823 }
2824
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002825 String templateClass = mN.extras.getString(EXTRA_TEMPLATE);
2826 if (!TextUtils.isEmpty(templateClass)) {
2827 final Class<? extends Style> styleClass
2828 = getNotificationStyleClass(templateClass);
2829 if (styleClass == null) {
2830 Log.d(TAG, "Unknown style class: " + templateClass);
2831 } else {
2832 try {
Adrian Roosc1a80b02016-04-05 14:54:55 -07002833 final Constructor<? extends Style> ctor =
2834 styleClass.getDeclaredConstructor();
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002835 ctor.setAccessible(true);
2836 final Style style = ctor.newInstance();
2837 style.restoreFromExtras(mN.extras);
2838
2839 if (style != null) {
2840 setStyle(style);
2841 }
2842 } catch (Throwable t) {
2843 Log.e(TAG, "Could not create Style", t);
2844 }
2845 }
2846 }
2847
2848 }
2849 }
2850
2851 private NotificationColorUtil getColorUtil() {
Selim Cinek99104832017-01-25 14:47:33 -08002852 if (mColorUtil == null) {
2853 mColorUtil = NotificationColorUtil.getInstance(mContext);
Christoph Studer4600f9b2014-07-22 22:44:43 +02002854 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002855 return mColorUtil;
Christoph Studer4600f9b2014-07-22 22:44:43 +02002856 }
2857
2858 /**
Julia Reynolds13d898c2017-02-02 12:22:05 -05002859 * If this notification is duplicative of a Launcher shortcut, sets the
2860 * {@link ShortcutInfo#getId() id} of the shortcut, in case the Launcher wants to hide
2861 * the shortcut.
2862 *
Julia Reynoldsbad42972017-04-25 13:52:49 -04002863 * This field will be ignored by Launchers that don't support badging, don't show
2864 * notification content, or don't show {@link android.content.pm.ShortcutManager shortcuts}.
Julia Reynolds13d898c2017-02-02 12:22:05 -05002865 *
2866 * @param shortcutId the {@link ShortcutInfo#getId() id} of the shortcut this notification
2867 * supersedes
2868 */
2869 public Builder setShortcutId(String shortcutId) {
2870 mN.mShortcutId = shortcutId;
2871 return this;
2872 }
2873
2874 /**
Julia Reynoldse071abd2017-03-22 10:52:11 -04002875 * Sets which icon to display as a badge for this notification.
2876 *
2877 * Must be one of {@link #BADGE_ICON_NONE}, {@link #BADGE_ICON_SMALL},
2878 * {@link #BADGE_ICON_LARGE}.
2879 *
2880 * Note: This value might be ignored, for launchers that don't support badge icons.
2881 */
Julia Reynolds612beb22017-03-30 10:48:30 -04002882 public Builder setBadgeIconType(int icon) {
Julia Reynoldse071abd2017-03-22 10:52:11 -04002883 mN.mBadgeIcon = icon;
2884 return this;
2885 }
2886
2887 /**
Julia Reynoldsa79c3712017-04-21 10:29:57 -04002888 * Sets the group alert behavior for this notification. Use this method to mute this
2889 * notification if alerts for this notification's group should be handled by a different
2890 * notification. This is only applicable for notifications that belong to a
2891 * {@link #setGroup(String) group}.
2892 *
2893 * <p> The default value is {@link #GROUP_ALERT_ALL}.</p>
2894 */
2895 public Builder setGroupAlertBehavior(@GroupAlertBehavior int groupAlertBehavior) {
2896 mN.mGroupAlertBehavior = groupAlertBehavior;
2897 return this;
2898 }
2899
Jeff Sharkey000ce802017-04-29 13:13:27 -06002900 /** @removed */
2901 @Deprecated
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04002902 public Builder setChannel(String channelId) {
2903 mN.mChannelId = channelId;
2904 return this;
2905 }
2906
2907 /**
Julia Reynoldsbad42972017-04-25 13:52:49 -04002908 * Specifies the channel the notification should be delivered on.
2909 */
2910 public Builder setChannelId(String channelId) {
2911 mN.mChannelId = channelId;
2912 return this;
2913 }
2914
Jeff Sharkey000ce802017-04-29 13:13:27 -06002915 /** @removed */
2916 @Deprecated
Julia Reynolds50989772017-02-23 14:32:16 -05002917 public Builder setTimeout(long durationMs) {
2918 mN.mTimeout = durationMs;
Julia Reynolds2a128742016-11-28 14:29:25 -05002919 return this;
2920 }
2921
2922 /**
Julia Reynoldsbad42972017-04-25 13:52:49 -04002923 * Specifies a duration in milliseconds after which this notification should be canceled,
2924 * if it is not already canceled.
2925 */
2926 public Builder setTimeoutAfter(long durationMs) {
2927 mN.mTimeout = durationMs;
2928 return this;
2929 }
2930
2931 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002932 * Add a timestamp pertaining to the notification (usually the time the event occurred).
Selim Cinek0ff1ce602016-04-05 18:27:16 -07002933 *
2934 * For apps targeting {@link android.os.Build.VERSION_CODES#N} and above, this time is not
2935 * shown anymore by default and must be opted into by using
2936 * {@link android.app.Notification.Builder#setShowWhen(boolean)}
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002937 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002938 * @see Notification#when
Joe Onoratocb109a02011-01-18 17:57:41 -08002939 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002940 public Builder setWhen(long when) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002941 mN.when = when;
Joe Onorato46439ce2010-11-19 13:56:21 -08002942 return this;
2943 }
2944
Joe Onoratocb109a02011-01-18 17:57:41 -08002945 /**
Griff Hazen50c11652014-05-16 09:46:31 -07002946 * Control whether the timestamp set with {@link #setWhen(long) setWhen} is shown
Daniel Sandler0c890492012-09-12 17:23:10 -07002947 * in the content view.
Selim Cinek0ff1ce602016-04-05 18:27:16 -07002948 * For apps targeting {@link android.os.Build.VERSION_CODES#N} and above, this defaults to
2949 * {@code false}. For earlier apps, the default is {@code true}.
Daniel Sandler0c890492012-09-12 17:23:10 -07002950 */
2951 public Builder setShowWhen(boolean show) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002952 mN.extras.putBoolean(EXTRA_SHOW_WHEN, show);
Daniel Sandler0c890492012-09-12 17:23:10 -07002953 return this;
2954 }
2955
2956 /**
Daniel Sandlerd33b8032012-05-10 11:41:48 -04002957 * Show the {@link Notification#when} field as a stopwatch.
Joe Malin8d40d042012-11-05 11:36:40 -08002958 *
2959 * Instead of presenting <code>when</code> as a timestamp, the notification will show an
Daniel Sandlerd33b8032012-05-10 11:41:48 -04002960 * automatically updating display of the minutes and seconds since <code>when</code>.
Daniel Sandlera2985ed2012-04-03 16:42:00 -04002961 *
Daniel Sandlerd33b8032012-05-10 11:41:48 -04002962 * Useful when showing an elapsed time (like an ongoing phone call).
2963 *
Selim Cinek81c23aa2016-02-25 16:23:13 -08002964 * The counter can also be set to count down to <code>when</code> when using
Adrian Roos96b7e202016-05-17 13:50:38 -07002965 * {@link #setChronometerCountDown(boolean)}.
Selim Cinek81c23aa2016-02-25 16:23:13 -08002966 *
Daniel Sandlerd33b8032012-05-10 11:41:48 -04002967 * @see android.widget.Chronometer
Daniel Sandlera2985ed2012-04-03 16:42:00 -04002968 * @see Notification#when
Adrian Roos96b7e202016-05-17 13:50:38 -07002969 * @see #setChronometerCountDown(boolean)
Daniel Sandlera2985ed2012-04-03 16:42:00 -04002970 */
2971 public Builder setUsesChronometer(boolean b) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002972 mN.extras.putBoolean(EXTRA_SHOW_CHRONOMETER, b);
Daniel Sandlera2985ed2012-04-03 16:42:00 -04002973 return this;
2974 }
2975
2976 /**
Selim Cinek81c23aa2016-02-25 16:23:13 -08002977 * Sets the Chronometer to count down instead of counting up.
2978 *
2979 * <p>This is only relevant if {@link #setUsesChronometer(boolean)} has been set to true.
2980 * If it isn't set the chronometer will count up.
2981 *
2982 * @see #setUsesChronometer(boolean)
2983 */
Adrian Roos96b7e202016-05-17 13:50:38 -07002984 public Builder setChronometerCountDown(boolean countDown) {
2985 mN.extras.putBoolean(EXTRA_CHRONOMETER_COUNT_DOWN, countDown);
Selim Cinek81c23aa2016-02-25 16:23:13 -08002986 return this;
2987 }
2988
2989 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002990 * Set the small icon resource, which will be used to represent the notification in the
2991 * status bar.
Joe Onoratocb109a02011-01-18 17:57:41 -08002992 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002993
2994 * The platform template for the expanded view will draw this icon in the left, unless a
2995 * {@link #setLargeIcon(Bitmap) large icon} has also been specified, in which case the small
2996 * icon will be moved to the right-hand side.
2997 *
2998
2999 * @param icon
3000 * A resource ID in the application's package of the drawable to use.
3001 * @see Notification#icon
Joe Onoratocb109a02011-01-18 17:57:41 -08003002 */
Tor Norbye7b9c9122013-05-30 16:48:33 -07003003 public Builder setSmallIcon(@DrawableRes int icon) {
Dan Sandlerd63f9322015-05-06 15:18:49 -04003004 return setSmallIcon(icon != 0
3005 ? Icon.createWithResource(mContext, icon)
3006 : null);
Joe Onorato46439ce2010-11-19 13:56:21 -08003007 }
3008
Joe Onoratocb109a02011-01-18 17:57:41 -08003009 /**
3010 * A variant of {@link #setSmallIcon(int) setSmallIcon(int)} that takes an additional
3011 * level parameter for when the icon is a {@link android.graphics.drawable.LevelListDrawable
3012 * LevelListDrawable}.
3013 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003014 * @param icon A resource ID in the application's package of the drawable to use.
Joe Onoratocb109a02011-01-18 17:57:41 -08003015 * @param level The level to use for the icon.
3016 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003017 * @see Notification#icon
3018 * @see Notification#iconLevel
Joe Onoratocb109a02011-01-18 17:57:41 -08003019 */
Tor Norbye7b9c9122013-05-30 16:48:33 -07003020 public Builder setSmallIcon(@DrawableRes int icon, int level) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003021 mN.iconLevel = level;
Dan Sandlerd63f9322015-05-06 15:18:49 -04003022 return setSmallIcon(icon);
3023 }
3024
3025 /**
3026 * Set the small icon, which will be used to represent the notification in the
3027 * status bar and content view (unless overriden there by a
3028 * {@link #setLargeIcon(Bitmap) large icon}).
3029 *
3030 * @param icon An Icon object to use.
3031 * @see Notification#icon
3032 */
3033 public Builder setSmallIcon(Icon icon) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003034 mN.setSmallIcon(icon);
3035 if (icon != null && icon.getType() == Icon.TYPE_RESOURCE) {
3036 mN.icon = icon.getResId();
3037 }
Joe Onorato46439ce2010-11-19 13:56:21 -08003038 return this;
3039 }
3040
Joe Onoratocb109a02011-01-18 17:57:41 -08003041 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003042 * Set the first line of text in the platform notification template.
Joe Onoratocb109a02011-01-18 17:57:41 -08003043 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003044 public Builder setContentTitle(CharSequence title) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003045 mN.extras.putCharSequence(EXTRA_TITLE, safeCharSequence(title));
Joe Onorato46439ce2010-11-19 13:56:21 -08003046 return this;
3047 }
3048
Joe Onoratocb109a02011-01-18 17:57:41 -08003049 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003050 * Set the second line of text in the platform notification template.
Joe Onoratocb109a02011-01-18 17:57:41 -08003051 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003052 public Builder setContentText(CharSequence text) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003053 mN.extras.putCharSequence(EXTRA_TEXT, safeCharSequence(text));
Joe Onorato46439ce2010-11-19 13:56:21 -08003054 return this;
3055 }
3056
Joe Onoratocb109a02011-01-18 17:57:41 -08003057 /**
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07003058 * This provides some additional information that is displayed in the notification. No
3059 * guarantees are given where exactly it is displayed.
3060 *
3061 * <p>This information should only be provided if it provides an essential
3062 * benefit to the understanding of the notification. The more text you provide the
3063 * less readable it becomes. For example, an email client should only provide the account
3064 * name here if more than one email account has been added.</p>
3065 *
3066 * <p>As of {@link android.os.Build.VERSION_CODES#N} this information is displayed in the
3067 * notification header area.
3068 *
3069 * On Android versions before {@link android.os.Build.VERSION_CODES#N}
3070 * this will be shown in the third line of text in the platform notification template.
3071 * You should not be using {@link #setProgress(int, int, boolean)} at the
3072 * same time on those versions; they occupy the same place.
3073 * </p>
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003074 */
3075 public Builder setSubText(CharSequence text) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003076 mN.extras.putCharSequence(EXTRA_SUB_TEXT, safeCharSequence(text));
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003077 return this;
3078 }
3079
3080 /**
Julia Reynolds3aedded2017-03-31 14:42:09 -04003081 * Provides text that will appear as a link to your application's settings.
3082 *
3083 * <p>This text does not appear within notification {@link Style templates} but may
3084 * appear when the user uses an affordance to learn more about the notification.
3085 * Additionally, this text will not appear unless you provide a valid link target by
3086 * handling {@link #INTENT_CATEGORY_NOTIFICATION_PREFERENCES}.
3087 *
3088 * <p>This text is meant to be concise description about what the user can customize
3089 * when they click on this link. The recommended maximum length is 40 characters.
3090 * @param text
3091 * @return
3092 */
3093 public Builder setSettingsText(CharSequence text) {
3094 mN.mSettingsText = safeCharSequence(text);
3095 return this;
3096 }
3097
3098 /**
Adrian Roose458aa82015-12-08 16:17:19 -08003099 * Set the remote input history.
3100 *
3101 * This should be set to the most recent inputs that have been sent
3102 * through a {@link RemoteInput} of this Notification and cleared once the it is no
3103 * longer relevant (e.g. for chat notifications once the other party has responded).
3104 *
3105 * The most recent input must be stored at the 0 index, the second most recent at the
3106 * 1 index, etc. Note that the system will limit both how far back the inputs will be shown
3107 * and how much of each individual input is shown.
3108 *
3109 * <p>Note: The reply text will only be shown on notifications that have least one action
3110 * with a {@code RemoteInput}.</p>
3111 */
3112 public Builder setRemoteInputHistory(CharSequence[] text) {
3113 if (text == null) {
3114 mN.extras.putCharSequenceArray(EXTRA_REMOTE_INPUT_HISTORY, null);
3115 } else {
3116 final int N = Math.min(MAX_REPLY_HISTORY, text.length);
3117 CharSequence[] safe = new CharSequence[N];
3118 for (int i = 0; i < N; i++) {
3119 safe[i] = safeCharSequence(text[i]);
3120 }
3121 mN.extras.putCharSequenceArray(EXTRA_REMOTE_INPUT_HISTORY, safe);
3122 }
3123 return this;
3124 }
3125
3126 /**
Julia Reynolds13d898c2017-02-02 12:22:05 -05003127 * Sets the number of items this notification represents. May be displayed as a badge count
3128 * for Launchers that support badging.
Joe Onoratocb109a02011-01-18 17:57:41 -08003129 */
Joe Onorato8595a3d2010-11-19 18:12:07 -08003130 public Builder setNumber(int number) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003131 mN.number = number;
Joe Onorato8595a3d2010-11-19 18:12:07 -08003132 return this;
3133 }
3134
Joe Onoratocb109a02011-01-18 17:57:41 -08003135 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003136 * A small piece of additional information pertaining to this notification.
3137 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003138 * The platform template will draw this on the last line of the notification, at the far
3139 * right (to the right of a smallIcon if it has been placed there).
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07003140 *
3141 * @deprecated use {@link #setSubText(CharSequence)} instead to set a text in the header.
3142 * For legacy apps targeting a version below {@link android.os.Build.VERSION_CODES#N} this
3143 * field will still show up, but the subtext will take precedence.
Joe Onoratocb109a02011-01-18 17:57:41 -08003144 */
Aurimas Liutikas514c5ef2016-05-24 15:22:55 -07003145 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08003146 public Builder setContentInfo(CharSequence info) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003147 mN.extras.putCharSequence(EXTRA_INFO_TEXT, safeCharSequence(info));
Joe Onorato46439ce2010-11-19 13:56:21 -08003148 return this;
3149 }
3150
Joe Onoratocb109a02011-01-18 17:57:41 -08003151 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003152 * Set the progress this notification represents.
3153 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003154 * The platform template will represent this using a {@link ProgressBar}.
Jeff Sharkey1c400132011-08-05 14:50:13 -07003155 */
3156 public Builder setProgress(int max, int progress, boolean indeterminate) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003157 mN.extras.putInt(EXTRA_PROGRESS, progress);
3158 mN.extras.putInt(EXTRA_PROGRESS_MAX, max);
3159 mN.extras.putBoolean(EXTRA_PROGRESS_INDETERMINATE, indeterminate);
Jeff Sharkey1c400132011-08-05 14:50:13 -07003160 return this;
3161 }
3162
3163 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003164 * Supply a custom RemoteViews to use instead of the platform template.
3165 *
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003166 * Use {@link #setCustomContentView(RemoteViews)} instead.
Joe Onoratocb109a02011-01-18 17:57:41 -08003167 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003168 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08003169 public Builder setContent(RemoteViews views) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003170 return setCustomContentView(views);
3171 }
3172
3173 /**
3174 * Supply custom RemoteViews to use instead of the platform template.
3175 *
3176 * This will override the layout that would otherwise be constructed by this Builder
3177 * object.
3178 */
3179 public Builder setCustomContentView(RemoteViews contentView) {
3180 mN.contentView = contentView;
3181 return this;
3182 }
3183
3184 /**
3185 * Supply custom RemoteViews to use instead of the platform template in the expanded form.
3186 *
3187 * This will override the expanded layout that would otherwise be constructed by this
3188 * Builder object.
3189 */
3190 public Builder setCustomBigContentView(RemoteViews contentView) {
3191 mN.bigContentView = contentView;
3192 return this;
3193 }
3194
3195 /**
3196 * Supply custom RemoteViews to use instead of the platform template in the heads up dialog.
3197 *
3198 * This will override the heads-up layout that would otherwise be constructed by this
3199 * Builder object.
3200 */
3201 public Builder setCustomHeadsUpContentView(RemoteViews contentView) {
3202 mN.headsUpContentView = contentView;
Joe Onorato46439ce2010-11-19 13:56:21 -08003203 return this;
3204 }
3205
Joe Onoratocb109a02011-01-18 17:57:41 -08003206 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003207 * Supply a {@link PendingIntent} to be sent when the notification is clicked.
3208 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003209 * As of {@link android.os.Build.VERSION_CODES#HONEYCOMB}, if this field is unset and you
3210 * have specified a custom RemoteViews with {@link #setContent(RemoteViews)}, you can use
3211 * {@link RemoteViews#setOnClickPendingIntent RemoteViews.setOnClickPendingIntent(int,PendingIntent)}
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003212 * to assign PendingIntents to individual views in that custom layout (i.e., to create
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003213 * clickable buttons inside the notification view).
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003214 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003215 * @see Notification#contentIntent Notification.contentIntent
Joe Onoratocb109a02011-01-18 17:57:41 -08003216 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003217 public Builder setContentIntent(PendingIntent intent) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003218 mN.contentIntent = intent;
Joe Onorato46439ce2010-11-19 13:56:21 -08003219 return this;
3220 }
3221
Joe Onoratocb109a02011-01-18 17:57:41 -08003222 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003223 * Supply a {@link PendingIntent} to send when the notification is cleared explicitly by the user.
3224 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003225 * @see Notification#deleteIntent
Joe Onoratocb109a02011-01-18 17:57:41 -08003226 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003227 public Builder setDeleteIntent(PendingIntent intent) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003228 mN.deleteIntent = intent;
Joe Onorato46439ce2010-11-19 13:56:21 -08003229 return this;
3230 }
3231
Joe Onoratocb109a02011-01-18 17:57:41 -08003232 /**
3233 * An intent to launch instead of posting the notification to the status bar.
3234 * Only for use with extremely high-priority notifications demanding the user's
3235 * <strong>immediate</strong> attention, such as an incoming phone call or
3236 * alarm clock that the user has explicitly set to a particular time.
3237 * If this facility is used for something else, please give the user an option
3238 * to turn it off and use a normal notification, as this can be extremely
3239 * disruptive.
3240 *
Chris Wren47c20a12014-06-18 17:27:29 -04003241 * <p>
3242 * The system UI may choose to display a heads-up notification, instead of
3243 * launching this intent, while the user is using the device.
3244 * </p>
3245 *
Joe Onoratocb109a02011-01-18 17:57:41 -08003246 * @param intent The pending intent to launch.
3247 * @param highPriority Passing true will cause this notification to be sent
3248 * even if other notifications are suppressed.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003249 *
3250 * @see Notification#fullScreenIntent
Joe Onoratocb109a02011-01-18 17:57:41 -08003251 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003252 public Builder setFullScreenIntent(PendingIntent intent, boolean highPriority) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003253 mN.fullScreenIntent = intent;
Joe Onorato46439ce2010-11-19 13:56:21 -08003254 setFlag(FLAG_HIGH_PRIORITY, highPriority);
3255 return this;
3256 }
3257
Joe Onoratocb109a02011-01-18 17:57:41 -08003258 /**
Dan Sandler5fcdf6e2014-07-18 11:31:15 -04003259 * Set the "ticker" text which is sent to accessibility services.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003260 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003261 * @see Notification#tickerText
Joe Onoratocb109a02011-01-18 17:57:41 -08003262 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003263 public Builder setTicker(CharSequence tickerText) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003264 mN.tickerText = safeCharSequence(tickerText);
Joe Onorato46439ce2010-11-19 13:56:21 -08003265 return this;
3266 }
3267
Joe Onoratocb109a02011-01-18 17:57:41 -08003268 /**
Dan Sandler5fcdf6e2014-07-18 11:31:15 -04003269 * Obsolete version of {@link #setTicker(CharSequence)}.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003270 *
Joe Onoratocb109a02011-01-18 17:57:41 -08003271 */
Dan Sandler5fcdf6e2014-07-18 11:31:15 -04003272 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08003273 public Builder setTicker(CharSequence tickerText, RemoteViews views) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003274 setTicker(tickerText);
3275 // views is ignored
Joe Onorato46439ce2010-11-19 13:56:21 -08003276 return this;
3277 }
3278
Joe Onoratocb109a02011-01-18 17:57:41 -08003279 /**
Dan Sandlerd63f9322015-05-06 15:18:49 -04003280 * Add a large icon to the notification content view.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003281 *
3282 * In the platform template, this image will be shown on the left of the notification view
Dan Sandlerd63f9322015-05-06 15:18:49 -04003283 * in place of the {@link #setSmallIcon(Icon) small icon} (which will be placed in a small
3284 * badge atop the large icon).
Dan Sandler08a04c12015-05-06 15:18:49 -04003285 */
Dan Sandlerd63f9322015-05-06 15:18:49 -04003286 public Builder setLargeIcon(Bitmap b) {
3287 return setLargeIcon(b != null ? Icon.createWithBitmap(b) : null);
3288 }
3289
3290 /**
3291 * Add a large icon to the notification content view.
3292 *
3293 * In the platform template, this image will be shown on the left of the notification view
3294 * in place of the {@link #setSmallIcon(Icon) small icon} (which will be placed in a small
3295 * badge atop the large icon).
3296 */
3297 public Builder setLargeIcon(Icon icon) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003298 mN.mLargeIcon = icon;
3299 mN.extras.putParcelable(EXTRA_LARGE_ICON, icon);
Joe Onorato46439ce2010-11-19 13:56:21 -08003300 return this;
3301 }
3302
Joe Onoratocb109a02011-01-18 17:57:41 -08003303 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003304 * Set the sound to play.
3305 *
John Spurlockc0650f022014-07-19 13:22:39 -04003306 * It will be played using the {@link #AUDIO_ATTRIBUTES_DEFAULT default audio attributes}
3307 * for notifications.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003308 *
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003309 * @deprecated use {@link NotificationChannel#setSound(Uri, AudioAttributes)} instead.
Joe Onoratocb109a02011-01-18 17:57:41 -08003310 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003311 @Deprecated
Joe Onorato52f80cd2010-11-21 15:34:48 -08003312 public Builder setSound(Uri sound) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003313 mN.sound = sound;
3314 mN.audioAttributes = AUDIO_ATTRIBUTES_DEFAULT;
Joe Onorato52f80cd2010-11-21 15:34:48 -08003315 return this;
3316 }
3317
Joe Onoratocb109a02011-01-18 17:57:41 -08003318 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003319 * Set the sound to play, along with a specific stream on which to play it.
Joe Onoratocb109a02011-01-18 17:57:41 -08003320 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003321 * See {@link android.media.AudioManager} for the <code>STREAM_</code> constants.
3322 *
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003323 * @deprecated use {@link NotificationChannel#setSound(Uri, AudioAttributes)}.
Joe Onoratocb109a02011-01-18 17:57:41 -08003324 */
Jean-Michel Trivi81f871e2014-08-06 16:32:38 -07003325 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08003326 public Builder setSound(Uri sound, int streamType) {
Jean-Michel Trivi2f7511f2016-11-28 15:40:27 -08003327 PlayerBase.deprecateStreamTypeForPlayback(streamType, "Notification", "setSound()");
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003328 mN.sound = sound;
3329 mN.audioStreamType = streamType;
Joe Onorato46439ce2010-11-19 13:56:21 -08003330 return this;
3331 }
3332
Joe Onoratocb109a02011-01-18 17:57:41 -08003333 /**
John Spurlockc0650f022014-07-19 13:22:39 -04003334 * Set the sound to play, along with specific {@link AudioAttributes audio attributes} to
3335 * use during playback.
3336 *
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003337 * @deprecated use {@link NotificationChannel#setSound(Uri, AudioAttributes)} instead.
John Spurlockc0650f022014-07-19 13:22:39 -04003338 * @see Notification#sound
3339 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003340 @Deprecated
John Spurlockc0650f022014-07-19 13:22:39 -04003341 public Builder setSound(Uri sound, AudioAttributes audioAttributes) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003342 mN.sound = sound;
3343 mN.audioAttributes = audioAttributes;
John Spurlockc0650f022014-07-19 13:22:39 -04003344 return this;
3345 }
3346
3347 /**
Joe Onoratocb109a02011-01-18 17:57:41 -08003348 * Set the vibration pattern to use.
3349 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003350 * See {@link android.os.Vibrator#vibrate(long[], int)} for a discussion of the
3351 * <code>pattern</code> parameter.
3352 *
Chris Wren47c20a12014-06-18 17:27:29 -04003353 * <p>
3354 * A notification that vibrates is more likely to be presented as a heads-up notification.
3355 * </p>
3356 *
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003357 * @deprecated use {@link NotificationChannel#setVibrationPattern(long[])} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003358 * @see Notification#vibrate
Joe Onoratocb109a02011-01-18 17:57:41 -08003359 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003360 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08003361 public Builder setVibrate(long[] pattern) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003362 mN.vibrate = pattern;
Joe Onorato46439ce2010-11-19 13:56:21 -08003363 return this;
3364 }
3365
Joe Onoratocb109a02011-01-18 17:57:41 -08003366 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003367 * Set the desired color for the indicator LED on the device, as well as the
3368 * blink duty cycle (specified in milliseconds).
3369 *
3370
3371 * Not all devices will honor all (or even any) of these values.
3372 *
Julia Reynolds529e3322017-02-06 08:33:01 -05003373 * @deprecated use {@link NotificationChannel#enableLights(boolean)} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003374 * @see Notification#ledARGB
3375 * @see Notification#ledOnMS
3376 * @see Notification#ledOffMS
Joe Onoratocb109a02011-01-18 17:57:41 -08003377 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003378 @Deprecated
Tor Norbye80756e32015-03-02 09:39:27 -08003379 public Builder setLights(@ColorInt int argb, int onMs, int offMs) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003380 mN.ledARGB = argb;
3381 mN.ledOnMS = onMs;
3382 mN.ledOffMS = offMs;
Julia Reynolds10ee1fc2015-11-09 11:04:55 -05003383 if (onMs != 0 || offMs != 0) {
3384 mN.flags |= FLAG_SHOW_LIGHTS;
3385 }
Joe Onorato46439ce2010-11-19 13:56:21 -08003386 return this;
3387 }
3388
Joe Onoratocb109a02011-01-18 17:57:41 -08003389 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003390 * Set whether this is an "ongoing" notification.
Joe Onoratocb109a02011-01-18 17:57:41 -08003391 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003392
3393 * Ongoing notifications cannot be dismissed by the user, so your application or service
3394 * must take care of canceling them.
3395 *
3396
3397 * They are typically used to indicate a background task that the user is actively engaged
3398 * with (e.g., playing music) or is pending in some way and therefore occupying the device
3399 * (e.g., a file download, sync operation, active network connection).
3400 *
3401
3402 * @see Notification#FLAG_ONGOING_EVENT
3403 * @see Service#setForeground(boolean)
Joe Onoratocb109a02011-01-18 17:57:41 -08003404 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003405 public Builder setOngoing(boolean ongoing) {
3406 setFlag(FLAG_ONGOING_EVENT, ongoing);
3407 return this;
3408 }
3409
Joe Onoratocb109a02011-01-18 17:57:41 -08003410 /**
Selim Cinek7b9605b2017-01-19 17:36:00 -08003411 * Set whether this notification should be colorized. When set, the color set with
3412 * {@link #setColor(int)} will be used as the background color of this notification.
3413 * <p>
Selim Cinek7b9605b2017-01-19 17:36:00 -08003414 * This should only be used for high priority ongoing tasks like navigation, an ongoing
3415 * call, or other similarly high-priority events for the user.
Selim Cinek99104832017-01-25 14:47:33 -08003416 * <p>
Selim Cinek22714f12017-04-13 16:23:53 -07003417 * For most styles, the coloring will only be applied if the notification is for a
3418 * foreground service notification.
Selim Cinek99104832017-01-25 14:47:33 -08003419 * However, for {@link MediaStyle} and {@link DecoratedMediaCustomViewStyle} notifications
Selim Cinek22714f12017-04-13 16:23:53 -07003420 * that have a media session attached there is no such requirement.
Selim Cinek7b9605b2017-01-19 17:36:00 -08003421 *
Selim Cinek7b9605b2017-01-19 17:36:00 -08003422 * @see Builder#setColor(int)
Selim Cinek99104832017-01-25 14:47:33 -08003423 * @see MediaStyle#setMediaSession(MediaSession.Token)
Selim Cinek7b9605b2017-01-19 17:36:00 -08003424 */
3425 public Builder setColorized(boolean colorize) {
3426 mN.extras.putBoolean(EXTRA_COLORIZED, colorize);
3427 return this;
3428 }
3429
3430 /**
Joe Onoratocb109a02011-01-18 17:57:41 -08003431 * Set this flag if you would only like the sound, vibrate
3432 * and ticker to be played if the notification is not already showing.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003433 *
3434 * @see Notification#FLAG_ONLY_ALERT_ONCE
Joe Onoratocb109a02011-01-18 17:57:41 -08003435 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003436 public Builder setOnlyAlertOnce(boolean onlyAlertOnce) {
3437 setFlag(FLAG_ONLY_ALERT_ONCE, onlyAlertOnce);
3438 return this;
3439 }
3440
Joe Onoratocb109a02011-01-18 17:57:41 -08003441 /**
Julia Reynolds04499532016-09-13 14:04:53 -04003442 * Make this notification automatically dismissed when the user touches it.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003443 *
3444 * @see Notification#FLAG_AUTO_CANCEL
Joe Onoratocb109a02011-01-18 17:57:41 -08003445 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003446 public Builder setAutoCancel(boolean autoCancel) {
Joe Onorato281d83f2011-01-04 17:13:10 -08003447 setFlag(FLAG_AUTO_CANCEL, autoCancel);
Joe Onorato46439ce2010-11-19 13:56:21 -08003448 return this;
3449 }
3450
Joe Onoratocb109a02011-01-18 17:57:41 -08003451 /**
Griff Hazendfcb0802014-02-11 12:00:00 -08003452 * Set whether or not this notification should not bridge to other devices.
3453 *
3454 * <p>Some notifications can be bridged to other devices for remote display.
3455 * This hint can be set to recommend this notification not be bridged.
3456 */
3457 public Builder setLocalOnly(boolean localOnly) {
3458 setFlag(FLAG_LOCAL_ONLY, localOnly);
3459 return this;
3460 }
3461
3462 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003463 * Set which notification properties will be inherited from system defaults.
Joe Onoratocb109a02011-01-18 17:57:41 -08003464 * <p>
3465 * The value should be one or more of the following fields combined with
3466 * bitwise-or:
3467 * {@link #DEFAULT_SOUND}, {@link #DEFAULT_VIBRATE}, {@link #DEFAULT_LIGHTS}.
3468 * <p>
3469 * For all default values, use {@link #DEFAULT_ALL}.
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003470 *
3471 * @deprecated use {@link NotificationChannel#enableVibration(boolean)} and
Julia Reynolds529e3322017-02-06 08:33:01 -05003472 * {@link NotificationChannel#enableLights(boolean)} and
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003473 * {@link NotificationChannel#setSound(Uri, AudioAttributes)} instead.
Joe Onoratocb109a02011-01-18 17:57:41 -08003474 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003475 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08003476 public Builder setDefaults(int defaults) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003477 mN.defaults = defaults;
Joe Onorato46439ce2010-11-19 13:56:21 -08003478 return this;
3479 }
3480
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003481 /**
3482 * Set the priority of this notification.
3483 *
3484 * @see Notification#priority
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003485 * @deprecated use {@link NotificationChannel#setImportance(int)} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003486 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003487 @Deprecated
Tor Norbyed9273d62013-05-30 15:59:53 -07003488 public Builder setPriority(@Priority int pri) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003489 mN.priority = pri;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003490 return this;
3491 }
Joe Malin8d40d042012-11-05 11:36:40 -08003492
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003493 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -04003494 * Set the notification category.
Joe Malin8d40d042012-11-05 11:36:40 -08003495 *
John Spurlockfd7f1e02014-03-18 16:41:57 -04003496 * @see Notification#category
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003497 */
John Spurlockfd7f1e02014-03-18 16:41:57 -04003498 public Builder setCategory(String category) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003499 mN.category = category;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003500 return this;
3501 }
3502
3503 /**
Chris Wrendde75302014-03-26 17:24:15 -04003504 * Add a person that is relevant to this notification.
3505 *
Chris Wrene6c48932014-09-29 17:19:27 -04003506 * <P>
3507 * Depending on user preferences, this annotation may allow the notification to pass
Julia Reynoldse071abd2017-03-22 10:52:11 -04003508 * through interruption filters, if this notification is of category {@link #CATEGORY_CALL}
3509 * or {@link #CATEGORY_MESSAGE}. The addition of people may also cause this notification to
3510 * appear more prominently in the user interface.
Chris Wrene6c48932014-09-29 17:19:27 -04003511 * </P>
3512 *
3513 * <P>
3514 * The person should be specified by the {@code String} representation of a
3515 * {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI}.
3516 * </P>
3517 *
3518 * <P>The system will also attempt to resolve {@code mailto:} and {@code tel:} schema
3519 * URIs. The path part of these URIs must exist in the contacts database, in the
3520 * appropriate column, or the reference will be discarded as invalid. Telephone schema
3521 * URIs will be resolved by {@link android.provider.ContactsContract.PhoneLookup}.
3522 * </P>
3523 *
3524 * @param uri A URI for the person.
Chris Wrendde75302014-03-26 17:24:15 -04003525 * @see Notification#EXTRA_PEOPLE
3526 */
Chris Wrene6c48932014-09-29 17:19:27 -04003527 public Builder addPerson(String uri) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003528 mPersonList.add(uri);
Chris Wrendde75302014-03-26 17:24:15 -04003529 return this;
3530 }
3531
3532 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003533 * Set this notification to be part of a group of notifications sharing the same key.
3534 * Grouped notifications may display in a cluster or stack on devices which
3535 * support such rendering.
3536 *
3537 * <p>To make this notification the summary for its group, also call
3538 * {@link #setGroupSummary}. A sort order can be specified for group members by using
3539 * {@link #setSortKey}.
3540 * @param groupKey The group key of the group.
3541 * @return this object for method chaining
3542 */
3543 public Builder setGroup(String groupKey) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003544 mN.mGroupKey = groupKey;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003545 return this;
3546 }
3547
3548 /**
3549 * Set this notification to be the group summary for a group of notifications.
3550 * Grouped notifications may display in a cluster or stack on devices which
Julia Reynolds04499532016-09-13 14:04:53 -04003551 * support such rendering. If thereRequires a group key also be set using {@link #setGroup}.
3552 * The group summary may be suppressed if too few notifications are included in the group.
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003553 * @param isGroupSummary Whether this notification should be a group summary.
3554 * @return this object for method chaining
3555 */
3556 public Builder setGroupSummary(boolean isGroupSummary) {
3557 setFlag(FLAG_GROUP_SUMMARY, isGroupSummary);
3558 return this;
3559 }
3560
3561 /**
3562 * Set a sort key that orders this notification among other notifications from the
3563 * same package. This can be useful if an external sort was already applied and an app
3564 * would like to preserve this. Notifications will be sorted lexicographically using this
3565 * value, although providing different priorities in addition to providing sort key may
3566 * cause this value to be ignored.
3567 *
3568 * <p>This sort key can also be used to order members of a notification group. See
Griff Hazen9e1379f2014-05-20 12:50:51 -07003569 * {@link #setGroup}.
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003570 *
3571 * @see String#compareTo(String)
3572 */
3573 public Builder setSortKey(String sortKey) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003574 mN.mSortKey = sortKey;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003575 return this;
3576 }
3577
3578 /**
Griff Hazen720042b2014-02-24 15:46:56 -08003579 * Merge additional metadata into this notification.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003580 *
Griff Hazen720042b2014-02-24 15:46:56 -08003581 * <p>Values within the Bundle will replace existing extras values in this Builder.
3582 *
3583 * @see Notification#extras
3584 */
Griff Hazen959591e2014-05-15 22:26:18 -07003585 public Builder addExtras(Bundle extras) {
3586 if (extras != null) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003587 mUserExtras.putAll(extras);
Griff Hazen720042b2014-02-24 15:46:56 -08003588 }
3589 return this;
3590 }
3591
3592 /**
3593 * Set metadata for this notification.
3594 *
3595 * <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 -04003596 * current contents are copied into the Notification each time {@link #build()} is
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003597 * called.
3598 *
Griff Hazen720042b2014-02-24 15:46:56 -08003599 * <p>Replaces any existing extras values with those from the provided Bundle.
3600 * Use {@link #addExtras} to merge in metadata instead.
3601 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003602 * @see Notification#extras
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003603 */
Griff Hazen959591e2014-05-15 22:26:18 -07003604 public Builder setExtras(Bundle extras) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003605 if (extras != null) {
3606 mUserExtras = extras;
3607 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003608 return this;
3609 }
3610
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003611 /**
Griff Hazen720042b2014-02-24 15:46:56 -08003612 * Get the current metadata Bundle used by this notification Builder.
3613 *
3614 * <p>The returned Bundle is shared with this Builder.
3615 *
3616 * <p>The current contents of this Bundle are copied into the Notification each time
3617 * {@link #build()} is called.
3618 *
3619 * @see Notification#extras
3620 */
3621 public Bundle getExtras() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003622 return mUserExtras;
3623 }
3624
3625 private Bundle getAllExtras() {
3626 final Bundle saveExtras = (Bundle) mUserExtras.clone();
3627 saveExtras.putAll(mN.extras);
3628 return saveExtras;
Griff Hazen720042b2014-02-24 15:46:56 -08003629 }
3630
3631 /**
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003632 * Add an action to this notification. Actions are typically displayed by
3633 * the system as a button adjacent to the notification content.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04003634 * <p>
3635 * Every action must have an icon (32dp square and matching the
3636 * <a href="{@docRoot}design/style/iconography.html#action-bar">Holo
3637 * Dark action bar</a> visual style), a textual label, and a {@link PendingIntent}.
3638 * <p>
3639 * A notification in its expanded form can display up to 3 actions, from left to right in
3640 * the order they were added. Actions will not be displayed when the notification is
3641 * collapsed, however, so be sure that any essential functions may be accessed by the user
3642 * in some other way (for example, in the Activity pointed to by {@link #contentIntent}).
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003643 *
3644 * @param icon Resource ID of a drawable that represents the action.
3645 * @param title Text describing the action.
3646 * @param intent PendingIntent to be fired when the action is invoked.
Dan Sandler86647982015-05-13 23:41:13 -04003647 *
3648 * @deprecated Use {@link #addAction(Action)} instead.
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003649 */
Dan Sandler86647982015-05-13 23:41:13 -04003650 @Deprecated
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003651 public Builder addAction(int icon, CharSequence title, PendingIntent intent) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04003652 mActions.add(new Action(icon, safeCharSequence(title), intent));
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003653 return this;
3654 }
3655
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003656 /**
Griff Hazen959591e2014-05-15 22:26:18 -07003657 * Add an action to this notification. Actions are typically displayed by
3658 * the system as a button adjacent to the notification content.
3659 * <p>
3660 * Every action must have an icon (32dp square and matching the
3661 * <a href="{@docRoot}design/style/iconography.html#action-bar">Holo
3662 * Dark action bar</a> visual style), a textual label, and a {@link PendingIntent}.
3663 * <p>
3664 * A notification in its expanded form can display up to 3 actions, from left to right in
3665 * the order they were added. Actions will not be displayed when the notification is
3666 * collapsed, however, so be sure that any essential functions may be accessed by the user
3667 * in some other way (for example, in the Activity pointed to by {@link #contentIntent}).
3668 *
3669 * @param action The action to add.
3670 */
3671 public Builder addAction(Action action) {
liangweikang63b03b52017-03-16 19:22:15 +08003672 if (action != null) {
3673 mActions.add(action);
3674 }
Griff Hazen959591e2014-05-15 22:26:18 -07003675 return this;
3676 }
3677
3678 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003679 * Alter the complete list of actions attached to this notification.
3680 * @see #addAction(Action).
3681 *
3682 * @param actions
3683 * @return
3684 */
3685 public Builder setActions(Action... actions) {
3686 mActions.clear();
3687 for (int i = 0; i < actions.length; i++) {
liangweikang63b03b52017-03-16 19:22:15 +08003688 if (actions[i] != null) {
3689 mActions.add(actions[i]);
3690 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003691 }
3692 return this;
3693 }
3694
3695 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003696 * Add a rich notification style to be applied at build time.
3697 *
3698 * @param style Object responsible for modifying the notification style.
3699 */
3700 public Builder setStyle(Style style) {
3701 if (mStyle != style) {
3702 mStyle = style;
Daniel Sandlerc08dea22012-06-28 08:35:24 -07003703 if (mStyle != null) {
3704 mStyle.setBuilder(this);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003705 mN.extras.putString(EXTRA_TEMPLATE, style.getClass().getName());
3706 } else {
3707 mN.extras.remove(EXTRA_TEMPLATE);
Daniel Sandlerc08dea22012-06-28 08:35:24 -07003708 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003709 }
3710 return this;
3711 }
3712
Dan Sandler0bf2ed82013-12-21 23:33:41 -06003713 /**
3714 * Specify the value of {@link #visibility}.
Griff Hazenb720abe2014-05-20 13:15:30 -07003715 *
Dan Sandler0bf2ed82013-12-21 23:33:41 -06003716 * @return The same Builder.
3717 */
Jeff Sharkey30e06bb2017-04-24 11:18:03 -06003718 public Builder setVisibility(@Visibility int visibility) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003719 mN.visibility = visibility;
Dan Sandler0bf2ed82013-12-21 23:33:41 -06003720 return this;
3721 }
3722
3723 /**
3724 * Supply a replacement Notification whose contents should be shown in insecure contexts
3725 * (i.e. atop the secure lockscreen). See {@link #visibility} and {@link #VISIBILITY_PUBLIC}.
3726 * @param n A replacement notification, presumably with some or all info redacted.
3727 * @return The same Builder.
3728 */
3729 public Builder setPublicVersion(Notification n) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003730 if (n != null) {
3731 mN.publicVersion = new Notification();
3732 n.cloneInto(mN.publicVersion, /*heavy=*/ true);
3733 } else {
3734 mN.publicVersion = null;
3735 }
Dan Sandler0bf2ed82013-12-21 23:33:41 -06003736 return this;
3737 }
3738
Griff Hazenb720abe2014-05-20 13:15:30 -07003739 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003740 * Apply an extender to this notification builder. Extenders may be used to add
3741 * metadata or change options on this builder.
3742 */
Griff Hazen61a9e862014-05-22 16:05:19 -07003743 public Builder extend(Extender extender) {
3744 extender.extend(this);
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003745 return this;
3746 }
3747
Dan Sandler4e787062015-06-17 15:09:48 -04003748 /**
3749 * @hide
3750 */
Julia Reynoldse46bb372016-03-17 11:05:58 -04003751 public Builder setFlag(int mask, boolean value) {
Joe Onorato46439ce2010-11-19 13:56:21 -08003752 if (value) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003753 mN.flags |= mask;
Joe Onorato46439ce2010-11-19 13:56:21 -08003754 } else {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003755 mN.flags &= ~mask;
Joe Onorato46439ce2010-11-19 13:56:21 -08003756 }
Julia Reynoldse46bb372016-03-17 11:05:58 -04003757 return this;
Joe Onorato46439ce2010-11-19 13:56:21 -08003758 }
3759
Dan Sandler26e81cf2014-05-06 10:01:27 -04003760 /**
3761 * Sets {@link Notification#color}.
3762 *
3763 * @param argb The accent color to use
3764 *
3765 * @return The same Builder.
3766 */
Tor Norbye80756e32015-03-02 09:39:27 -08003767 public Builder setColor(@ColorInt int argb) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003768 mN.color = argb;
Julia Reynolds10ee1fc2015-11-09 11:04:55 -05003769 sanitizeColor();
Dan Sandler26e81cf2014-05-06 10:01:27 -04003770 return this;
3771 }
3772
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003773 private Drawable getProfileBadgeDrawable() {
Chris Wren66619a22016-05-12 16:42:37 -04003774 if (mContext.getUserId() == UserHandle.USER_SYSTEM) {
3775 // This user can never be a badged profile,
3776 // and also includes USER_ALL system notifications.
3777 return null;
3778 }
Christoph Studer7ac80e62014-08-04 16:01:57 +02003779 // Note: This assumes that the current user can read the profile badge of the
3780 // originating user.
Selim Cineke6ff9462016-01-15 15:07:06 -08003781 return mContext.getPackageManager().getUserBadgeForDensityNoBackground(
Julia Reynoldsda303542015-11-23 14:00:20 -05003782 new UserHandle(mContext.getUserId()), 0);
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003783 }
3784
3785 private Bitmap getProfileBadge() {
3786 Drawable badge = getProfileBadgeDrawable();
Kenny Guy8a0101b2014-05-08 23:34:12 +01003787 if (badge == null) {
3788 return null;
3789 }
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003790 final int size = mContext.getResources().getDimensionPixelSize(
3791 R.dimen.notification_badge_size);
3792 Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
Kenny Guy8a0101b2014-05-08 23:34:12 +01003793 Canvas canvas = new Canvas(bitmap);
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003794 badge.setBounds(0, 0, size, size);
Kenny Guy8a0101b2014-05-08 23:34:12 +01003795 badge.draw(canvas);
3796 return bitmap;
3797 }
3798
Selim Cinekc848c3a2016-01-13 15:27:30 -08003799 private void bindProfileBadge(RemoteViews contentView) {
Kenny Guy98193ea2014-07-24 19:54:37 +01003800 Bitmap profileBadge = getProfileBadge();
3801
Kenny Guy98193ea2014-07-24 19:54:37 +01003802 if (profileBadge != null) {
Selim Cinekc848c3a2016-01-13 15:27:30 -08003803 contentView.setImageViewBitmap(R.id.profile_badge, profileBadge);
3804 contentView.setViewVisibility(R.id.profile_badge, View.VISIBLE);
Selim Cinek7b9605b2017-01-19 17:36:00 -08003805 if (isColorized()) {
3806 contentView.setDrawableParameters(R.id.profile_badge, false, -1,
3807 getPrimaryTextColor(), PorterDuff.Mode.SRC_ATOP, -1);
3808 }
Kenny Guy98193ea2014-07-24 19:54:37 +01003809 }
Kenny Guy98193ea2014-07-24 19:54:37 +01003810 }
3811
Christoph Studerfe718432014-09-01 18:21:18 +02003812 private void resetStandardTemplate(RemoteViews contentView) {
Selim Cinekeaa29ca2015-11-23 13:51:13 -08003813 resetNotificationHeader(contentView);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003814 resetContentMargins(contentView);
Christoph Studerfe718432014-09-01 18:21:18 +02003815 contentView.setViewVisibility(R.id.right_icon, View.GONE);
Selim Cinek860b6da2015-12-16 19:02:19 -08003816 contentView.setViewVisibility(R.id.title, View.GONE);
Christoph Studerfe718432014-09-01 18:21:18 +02003817 contentView.setTextViewText(R.id.title, null);
Selim Cinek41598732016-01-11 16:58:37 -08003818 contentView.setViewVisibility(R.id.text, View.GONE);
Christoph Studerfe718432014-09-01 18:21:18 +02003819 contentView.setTextViewText(R.id.text, null);
Selim Cinek29603462015-11-17 19:04:39 -08003820 contentView.setViewVisibility(R.id.text_line_1, View.GONE);
Selim Cinek41598732016-01-11 16:58:37 -08003821 contentView.setTextViewText(R.id.text_line_1, null);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003822 }
3823
Selim Cinekeaa29ca2015-11-23 13:51:13 -08003824 /**
3825 * Resets the notification header to its original state
3826 */
3827 private void resetNotificationHeader(RemoteViews contentView) {
Adrian Roosc4337a32016-08-02 18:30:34 -07003828 // Small icon doesn't need to be reset, as it's always set. Resetting would prevent
3829 // re-using the drawable when the notification is updated.
Selim Cinek7b836392015-12-04 20:02:59 -08003830 contentView.setBoolean(R.id.notification_header, "setExpanded", false);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003831 contentView.setTextViewText(R.id.app_name_text, null);
Christoph Studerca1db712014-09-10 17:31:33 +02003832 contentView.setViewVisibility(R.id.chronometer, View.GONE);
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07003833 contentView.setViewVisibility(R.id.header_text, View.GONE);
Adrian Roos9dfb78f2016-06-30 15:43:44 -07003834 contentView.setTextViewText(R.id.header_text, null);
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07003835 contentView.setViewVisibility(R.id.header_text_divider, View.GONE);
Selim Cinek29603462015-11-17 19:04:39 -08003836 contentView.setViewVisibility(R.id.time_divider, View.GONE);
Selim Cinekb85f36fd2016-04-20 18:46:36 -07003837 contentView.setViewVisibility(R.id.time, View.GONE);
Selim Cinekc848c3a2016-01-13 15:27:30 -08003838 contentView.setImageViewIcon(R.id.profile_badge, null);
3839 contentView.setViewVisibility(R.id.profile_badge, View.GONE);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003840 }
3841
3842 private void resetContentMargins(RemoteViews contentView) {
Adrian Roos2d5dbba2016-06-08 17:11:53 -07003843 contentView.setViewLayoutMarginEndDimen(R.id.line1, 0);
3844 contentView.setViewLayoutMarginEndDimen(R.id.text, 0);
Christoph Studerfe718432014-09-01 18:21:18 +02003845 }
3846
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003847 private RemoteViews applyStandardTemplate(int resId) {
Adrian Roos70d7aa32017-01-11 15:39:06 -08003848 return applyStandardTemplate(resId, mParams.reset().fillTextsFrom(this));
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02003849 }
3850
3851 /**
3852 * @param hasProgress whether the progress bar should be shown and set
3853 */
3854 private RemoteViews applyStandardTemplate(int resId, boolean hasProgress) {
Adrian Roos70d7aa32017-01-11 15:39:06 -08003855 return applyStandardTemplate(resId, mParams.reset().hasProgress(hasProgress)
3856 .fillTextsFrom(this));
Adrian Roosc1a80b02016-04-05 14:54:55 -07003857 }
3858
Adrian Roos70d7aa32017-01-11 15:39:06 -08003859 private RemoteViews applyStandardTemplate(int resId, StandardTemplateParams p) {
Kenny Guy77320062014-08-27 21:37:15 +01003860 RemoteViews contentView = new BuilderRemoteViews(mContext.getApplicationInfo(), resId);
Dan Sandler539aad42014-08-04 00:43:39 -04003861
Christoph Studerfe718432014-09-01 18:21:18 +02003862 resetStandardTemplate(contentView);
3863
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003864 final Bundle ex = mN.extras;
Selim Cinek7b9605b2017-01-19 17:36:00 -08003865 updateBackgroundColor(contentView);
Adrian Roos487374f2017-01-11 15:48:14 -08003866 bindNotificationHeader(contentView, p.ambient);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003867 bindLargeIcon(contentView);
Adrian Roos70d7aa32017-01-11 15:39:06 -08003868 boolean showProgress = handleProgressBar(p.hasProgress, contentView, ex);
3869 if (p.title != null) {
Selim Cinek860b6da2015-12-16 19:02:19 -08003870 contentView.setViewVisibility(R.id.title, View.VISIBLE);
Adrian Roos70d7aa32017-01-11 15:39:06 -08003871 contentView.setTextViewText(R.id.title, p.title);
Adrian Roos72171622017-01-27 10:32:06 -08003872 if (!p.ambient) {
3873 setTextViewColorPrimary(contentView, R.id.title);
3874 }
Selim Cinek954cc232016-05-20 13:29:23 -07003875 contentView.setViewLayoutWidth(R.id.title, showProgress
3876 ? ViewGroup.LayoutParams.WRAP_CONTENT
3877 : ViewGroup.LayoutParams.MATCH_PARENT);
Joe Onorato561d3852010-11-20 18:09:34 -08003878 }
Adrian Roos70d7aa32017-01-11 15:39:06 -08003879 if (p.text != null) {
Selim Cinek41598732016-01-11 16:58:37 -08003880 int textId = showProgress ? com.android.internal.R.id.text_line_1
3881 : com.android.internal.R.id.text;
Adrian Roos70d7aa32017-01-11 15:39:06 -08003882 contentView.setTextViewText(textId, p.text);
Adrian Roos72171622017-01-27 10:32:06 -08003883 if (!p.ambient) {
3884 setTextViewColorSecondary(contentView, textId);
3885 }
Selim Cinek41598732016-01-11 16:58:37 -08003886 contentView.setViewVisibility(textId, View.VISIBLE);
Joe Onorato561d3852010-11-20 18:09:34 -08003887 }
Selim Cinekc848c3a2016-01-13 15:27:30 -08003888
Selim Cinek279fa862016-06-14 10:57:25 -07003889 setContentMinHeight(contentView, showProgress || mN.hasLargeIcon());
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003890
Selim Cinek29603462015-11-17 19:04:39 -08003891 return contentView;
3892 }
3893
Selim Cinek7b9605b2017-01-19 17:36:00 -08003894 private void setTextViewColorPrimary(RemoteViews contentView, int id) {
3895 ensureColors();
3896 contentView.setTextColor(id, mPrimaryTextColor);
3897 }
3898
Selim Cinek389edcd2017-05-11 19:16:44 -07003899 /**
3900 * @return the primary text color
3901 * @hide
3902 */
3903 @VisibleForTesting
3904 public int getPrimaryTextColor() {
Selim Cinek7b9605b2017-01-19 17:36:00 -08003905 ensureColors();
3906 return mPrimaryTextColor;
3907 }
3908
Selim Cinek389edcd2017-05-11 19:16:44 -07003909 /**
3910 * @return the secondary text color
3911 * @hide
3912 */
3913 @VisibleForTesting
3914 public int getSecondaryTextColor() {
3915 ensureColors();
3916 return mSecondaryTextColor;
3917 }
3918
Selim Cinek7b9605b2017-01-19 17:36:00 -08003919 private int getActionBarColor() {
3920 ensureColors();
3921 return mActionBarColor;
3922 }
3923
Selim Cinek622c64a2017-04-17 17:10:05 -07003924 private int getActionBarColorDeEmphasized() {
3925 int backgroundColor = getBackgroundColor();
3926 return NotificationColorUtil.getShiftedColor(backgroundColor, 12);
3927 }
3928
Selim Cinek7b9605b2017-01-19 17:36:00 -08003929 private void setTextViewColorSecondary(RemoteViews contentView, int id) {
3930 ensureColors();
3931 contentView.setTextColor(id, mSecondaryTextColor);
3932 }
3933
3934 private void ensureColors() {
3935 int backgroundColor = getBackgroundColor();
3936 if (mPrimaryTextColor == COLOR_INVALID
3937 || mSecondaryTextColor == COLOR_INVALID
3938 || mActionBarColor == COLOR_INVALID
3939 || mTextColorsAreForBackground != backgroundColor) {
3940 mTextColorsAreForBackground = backgroundColor;
Selim Cinek5fb73f82017-04-20 16:55:38 -07003941 if (mForegroundColor == COLOR_INVALID || !isColorized()) {
3942 mPrimaryTextColor = NotificationColorUtil.resolvePrimaryColor(mContext,
3943 backgroundColor);
3944 mSecondaryTextColor = NotificationColorUtil.resolveSecondaryColor(mContext,
3945 backgroundColor);
Selim Cinekac5f0272017-05-02 16:05:41 -07003946 if (backgroundColor != COLOR_DEFAULT
3947 && (mBackgroundColorHint != COLOR_INVALID || isColorized())) {
3948 mPrimaryTextColor = NotificationColorUtil.findAlphaToMeetContrast(
3949 mPrimaryTextColor, backgroundColor, 4.5);
3950 mSecondaryTextColor = NotificationColorUtil.findAlphaToMeetContrast(
3951 mSecondaryTextColor, backgroundColor, 4.5);
3952 }
Selim Cinek5fb73f82017-04-20 16:55:38 -07003953 } else {
3954 double backLum = NotificationColorUtil.calculateLuminance(backgroundColor);
3955 double textLum = NotificationColorUtil.calculateLuminance(mForegroundColor);
3956 double contrast = NotificationColorUtil.calculateContrast(mForegroundColor,
3957 backgroundColor);
Selim Cinek389edcd2017-05-11 19:16:44 -07003958 // We only respect the given colors if worst case Black or White still has
3959 // contrast
3960 boolean backgroundLight = backLum > textLum
3961 && satisfiesTextContrast(backgroundColor, Color.BLACK)
3962 || backLum <= textLum
3963 && !satisfiesTextContrast(backgroundColor, Color.WHITE);
Selim Cinek5fb73f82017-04-20 16:55:38 -07003964 if (contrast < 4.5f) {
Selim Cinek389edcd2017-05-11 19:16:44 -07003965 if (backgroundLight) {
Selim Cinek5fb73f82017-04-20 16:55:38 -07003966 mSecondaryTextColor = NotificationColorUtil.findContrastColor(
3967 mForegroundColor,
3968 backgroundColor,
3969 true /* findFG */,
3970 4.5f);
3971 mPrimaryTextColor = NotificationColorUtil.changeColorLightness(
Selim Cinek389edcd2017-05-11 19:16:44 -07003972 mSecondaryTextColor, -LIGHTNESS_TEXT_DIFFERENCE_LIGHT);
Selim Cinek5fb73f82017-04-20 16:55:38 -07003973 } else {
3974 mSecondaryTextColor =
3975 NotificationColorUtil.findContrastColorAgainstDark(
3976 mForegroundColor,
3977 backgroundColor,
3978 true /* findFG */,
3979 4.5f);
3980 mPrimaryTextColor = NotificationColorUtil.changeColorLightness(
Selim Cinek389edcd2017-05-11 19:16:44 -07003981 mSecondaryTextColor, -LIGHTNESS_TEXT_DIFFERENCE_DARK);
Selim Cinek5fb73f82017-04-20 16:55:38 -07003982 }
3983 } else {
3984 mPrimaryTextColor = mForegroundColor;
3985 mSecondaryTextColor = NotificationColorUtil.changeColorLightness(
Selim Cinek389edcd2017-05-11 19:16:44 -07003986 mPrimaryTextColor, backgroundLight ? LIGHTNESS_TEXT_DIFFERENCE_LIGHT
3987 : LIGHTNESS_TEXT_DIFFERENCE_DARK);
Selim Cinek5fb73f82017-04-20 16:55:38 -07003988 if (NotificationColorUtil.calculateContrast(mSecondaryTextColor,
3989 backgroundColor) < 4.5f) {
3990 // oh well the secondary is not good enough
Selim Cinek389edcd2017-05-11 19:16:44 -07003991 if (backgroundLight) {
Selim Cinek5fb73f82017-04-20 16:55:38 -07003992 mSecondaryTextColor = NotificationColorUtil.findContrastColor(
3993 mSecondaryTextColor,
3994 backgroundColor,
3995 true /* findFG */,
3996 4.5f);
3997 } else {
3998 mSecondaryTextColor
3999 = NotificationColorUtil.findContrastColorAgainstDark(
4000 mSecondaryTextColor,
4001 backgroundColor,
4002 true /* findFG */,
4003 4.5f);
4004 }
4005 mPrimaryTextColor = NotificationColorUtil.changeColorLightness(
Selim Cinek389edcd2017-05-11 19:16:44 -07004006 mSecondaryTextColor, backgroundLight
4007 ? -LIGHTNESS_TEXT_DIFFERENCE_LIGHT
4008 : -LIGHTNESS_TEXT_DIFFERENCE_DARK);
Selim Cinek5fb73f82017-04-20 16:55:38 -07004009 }
4010 }
4011 }
Selim Cinek875ba9b2017-02-13 16:20:17 -08004012 mActionBarColor = NotificationColorUtil.resolveActionBarColor(mContext,
4013 backgroundColor);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004014 }
4015 }
4016
4017 private void updateBackgroundColor(RemoteViews contentView) {
4018 if (isColorized()) {
4019 contentView.setInt(R.id.status_bar_latest_event_content, "setBackgroundColor",
4020 getBackgroundColor());
4021 } else {
4022 // Clear it!
4023 contentView.setInt(R.id.status_bar_latest_event_content, "setBackgroundResource",
4024 0);
4025 }
4026 }
4027
Selim Cinek860b6da2015-12-16 19:02:19 -08004028 /**
4029 * @param remoteView the remote view to update the minheight in
4030 * @param hasMinHeight does it have a mimHeight
4031 * @hide
4032 */
4033 void setContentMinHeight(RemoteViews remoteView, boolean hasMinHeight) {
4034 int minHeight = 0;
4035 if (hasMinHeight) {
4036 // we need to set the minHeight of the notification
4037 minHeight = mContext.getResources().getDimensionPixelSize(
4038 com.android.internal.R.dimen.notification_min_content_height);
4039 }
4040 remoteView.setInt(R.id.notification_main_column, "setMinimumHeight", minHeight);
4041 }
4042
Selim Cinek29603462015-11-17 19:04:39 -08004043 private boolean handleProgressBar(boolean hasProgress, RemoteViews contentView, Bundle ex) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004044 final int max = ex.getInt(EXTRA_PROGRESS_MAX, 0);
4045 final int progress = ex.getInt(EXTRA_PROGRESS, 0);
4046 final boolean ind = ex.getBoolean(EXTRA_PROGRESS_INDETERMINATE);
4047 if (hasProgress && (max != 0 || ind)) {
Selim Cinek29603462015-11-17 19:04:39 -08004048 contentView.setViewVisibility(com.android.internal.R.id.progress, View.VISIBLE);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004049 contentView.setProgressBar(
Selim Cinek29603462015-11-17 19:04:39 -08004050 R.id.progress, max, progress, ind);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004051 contentView.setProgressBackgroundTintList(
4052 R.id.progress, ColorStateList.valueOf(mContext.getColor(
4053 R.color.notification_progress_background_color)));
Selim Cinek29603462015-11-17 19:04:39 -08004054 if (mN.color != COLOR_DEFAULT) {
Adrian Roos4ff3b122016-02-01 12:26:13 -08004055 ColorStateList colorStateList = ColorStateList.valueOf(resolveContrastColor());
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004056 contentView.setProgressTintList(R.id.progress, colorStateList);
4057 contentView.setProgressIndeterminateTintList(R.id.progress, colorStateList);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004058 }
Selim Cinek29603462015-11-17 19:04:39 -08004059 return true;
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004060 } else {
4061 contentView.setViewVisibility(R.id.progress, View.GONE);
Selim Cinek29603462015-11-17 19:04:39 -08004062 return false;
Jeff Sharkey1c400132011-08-05 14:50:13 -07004063 }
Joe Onorato561d3852010-11-20 18:09:34 -08004064 }
4065
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004066 private void bindLargeIcon(RemoteViews contentView) {
Selim Cinek279fa862016-06-14 10:57:25 -07004067 if (mN.mLargeIcon == null && mN.largeIcon != null) {
4068 mN.mLargeIcon = Icon.createWithBitmap(mN.largeIcon);
4069 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004070 if (mN.mLargeIcon != null) {
4071 contentView.setViewVisibility(R.id.right_icon, View.VISIBLE);
4072 contentView.setImageViewIcon(R.id.right_icon, mN.mLargeIcon);
4073 processLargeLegacyIcon(mN.mLargeIcon, contentView);
Adrian Roos2d5dbba2016-06-08 17:11:53 -07004074 int endMargin = R.dimen.notification_content_picture_margin;
4075 contentView.setViewLayoutMarginEndDimen(R.id.line1, endMargin);
4076 contentView.setViewLayoutMarginEndDimen(R.id.text, endMargin);
4077 contentView.setViewLayoutMarginEndDimen(R.id.progress, endMargin);
Selim Cinek5d6ef8d2017-05-18 22:16:00 -07004078 // Bind the reply action
4079 Action action = findReplyAction();
4080 contentView.setViewVisibility(R.id.reply_icon_action, action != null
4081 ? View.VISIBLE
4082 : View.GONE);
4083
4084 if (action != null) {
4085 int contrastColor = resolveContrastColor();
4086 contentView.setDrawableParameters(R.id.reply_icon_action,
4087 true /* targetBackground */,
4088 -1,
4089 contrastColor,
4090 PorterDuff.Mode.SRC_ATOP, -1);
4091 int iconColor = NotificationColorUtil.isColorLight(contrastColor)
4092 ? Color.BLACK : Color.WHITE;
4093 contentView.setDrawableParameters(R.id.reply_icon_action,
4094 false /* targetBackground */,
4095 -1,
4096 iconColor,
4097 PorterDuff.Mode.SRC_ATOP, -1);
4098 contentView.setOnClickPendingIntent(R.id.right_icon,
4099 action.actionIntent);
4100 contentView.setOnClickPendingIntent(R.id.reply_icon_action,
4101 action.actionIntent);
4102 contentView.setRemoteInputs(R.id.right_icon, action.mRemoteInputs);
4103 contentView.setRemoteInputs(R.id.reply_icon_action, action.mRemoteInputs);
4104
4105 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004106 }
Selim Cinek5d6ef8d2017-05-18 22:16:00 -07004107 contentView.setViewVisibility(R.id.right_icon_container, mN.mLargeIcon != null
4108 ? View.VISIBLE
4109 : View.GONE);
4110 }
4111
4112 private Action findReplyAction() {
4113 ArrayList<Action> actions = mActions;
4114 if (mOriginalActions != null) {
4115 actions = mOriginalActions;
4116 }
4117 int numActions = actions.size();
4118 for (int i = 0; i < numActions; i++) {
4119 Action action = actions.get(i);
4120 if (hasValidRemoteInput(action)) {
4121 return action;
4122 }
4123 }
4124 return null;
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004125 }
4126
Adrian Roos487374f2017-01-11 15:48:14 -08004127 private void bindNotificationHeader(RemoteViews contentView, boolean ambient) {
4128 bindSmallIcon(contentView, ambient);
4129 bindHeaderAppName(contentView, ambient);
4130 if (!ambient) {
4131 // Ambient view does not have these
4132 bindHeaderText(contentView);
4133 bindHeaderChronometerAndTime(contentView);
Adrian Roos487374f2017-01-11 15:48:14 -08004134 bindProfileBadge(contentView);
4135 }
Adrian Roosd83e9992017-03-16 15:17:57 -07004136 bindExpandButton(contentView);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004137 }
4138
4139 private void bindExpandButton(RemoteViews contentView) {
Selim Cinek99104832017-01-25 14:47:33 -08004140 int color = getPrimaryHighlightColor();
Selim Cinek7b9605b2017-01-19 17:36:00 -08004141 contentView.setDrawableParameters(R.id.expand_button, false, -1, color,
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004142 PorterDuff.Mode.SRC_ATOP, -1);
Selim Cinekea4bef72015-12-02 15:51:10 -08004143 contentView.setInt(R.id.notification_header, "setOriginalNotificationColor",
Selim Cinek7b9605b2017-01-19 17:36:00 -08004144 color);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004145 }
4146
Selim Cinek99104832017-01-25 14:47:33 -08004147 /**
4148 * @return the color that is used as the first primary highlight color. This is applied
4149 * in several places like the action buttons or the app name in the header.
4150 */
4151 private int getPrimaryHighlightColor() {
4152 return isColorized() ? getPrimaryTextColor() : resolveContrastColor();
4153 }
4154
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004155 private void bindHeaderChronometerAndTime(RemoteViews contentView) {
4156 if (showsTimeOrChronometer()) {
Selim Cinek29603462015-11-17 19:04:39 -08004157 contentView.setViewVisibility(R.id.time_divider, View.VISIBLE);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004158 setTextViewColorSecondary(contentView, R.id.time_divider);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004159 if (mN.extras.getBoolean(EXTRA_SHOW_CHRONOMETER)) {
4160 contentView.setViewVisibility(R.id.chronometer, View.VISIBLE);
4161 contentView.setLong(R.id.chronometer, "setBase",
4162 mN.when + (SystemClock.elapsedRealtime() - System.currentTimeMillis()));
4163 contentView.setBoolean(R.id.chronometer, "setStarted", true);
Adrian Roos96b7e202016-05-17 13:50:38 -07004164 boolean countsDown = mN.extras.getBoolean(EXTRA_CHRONOMETER_COUNT_DOWN);
Selim Cinekc3b752e2016-04-20 16:13:59 -07004165 contentView.setChronometerCountDown(R.id.chronometer, countsDown);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004166 setTextViewColorSecondary(contentView, R.id.chronometer);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004167 } else {
4168 contentView.setViewVisibility(R.id.time, View.VISIBLE);
4169 contentView.setLong(R.id.time, "setTime", mN.when);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004170 setTextViewColorSecondary(contentView, R.id.time);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004171 }
Selim Cinekb85f36fd2016-04-20 18:46:36 -07004172 } else {
4173 // We still want a time to be set but gone, such that we can show and hide it
4174 // on demand in case it's a child notification without anything in the header
4175 contentView.setLong(R.id.time, "setTime", mN.when != 0 ? mN.when : mN.creationTime);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004176 }
4177 }
4178
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07004179 private void bindHeaderText(RemoteViews contentView) {
4180 CharSequence headerText = mN.extras.getCharSequence(EXTRA_SUB_TEXT);
4181 if (headerText == null && mStyle != null && mStyle.mSummaryTextSet
Selim Cinek03d0d652015-11-13 13:18:09 -05004182 && mStyle.hasSummaryInHeader()) {
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07004183 headerText = mStyle.mSummaryText;
Selim Cinek03d0d652015-11-13 13:18:09 -05004184 }
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07004185 if (headerText == null
4186 && mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N
4187 && mN.extras.getCharSequence(EXTRA_INFO_TEXT) != null) {
4188 headerText = mN.extras.getCharSequence(EXTRA_INFO_TEXT);
4189 }
4190 if (headerText != null) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004191 // TODO: Remove the span entirely to only have the string with propper formating.
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07004192 contentView.setTextViewText(R.id.header_text, processLegacyText(headerText));
Selim Cinek7b9605b2017-01-19 17:36:00 -08004193 setTextViewColorSecondary(contentView, R.id.header_text);
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07004194 contentView.setViewVisibility(R.id.header_text, View.VISIBLE);
4195 contentView.setViewVisibility(R.id.header_text_divider, View.VISIBLE);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004196 setTextViewColorSecondary(contentView, R.id.header_text_divider);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004197 }
4198 }
4199
Adrian Rooseba05822016-04-22 17:09:27 -07004200 /**
4201 * @hide
4202 */
4203 public String loadHeaderAppName() {
Dan Sandler732bd6c2016-04-12 14:20:32 -04004204 CharSequence name = null;
4205 final PackageManager pm = mContext.getPackageManager();
4206 if (mN.extras.containsKey(EXTRA_SUBSTITUTE_APP_NAME)) {
4207 // only system packages which lump together a bunch of unrelated stuff
4208 // may substitute a different name to make the purpose of the
4209 // notification more clear. the correct package label should always
4210 // be accessible via SystemUI.
4211 final String pkg = mContext.getPackageName();
4212 final String subName = mN.extras.getString(EXTRA_SUBSTITUTE_APP_NAME);
4213 if (PackageManager.PERMISSION_GRANTED == pm.checkPermission(
4214 android.Manifest.permission.SUBSTITUTE_NOTIFICATION_APP_NAME, pkg)) {
4215 name = subName;
4216 } else {
4217 Log.w(TAG, "warning: pkg "
4218 + pkg + " attempting to substitute app name '" + subName
4219 + "' without holding perm "
4220 + android.Manifest.permission.SUBSTITUTE_NOTIFICATION_APP_NAME);
4221 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004222 }
Dan Sandler732bd6c2016-04-12 14:20:32 -04004223 if (TextUtils.isEmpty(name)) {
4224 name = pm.getApplicationLabel(mContext.getApplicationInfo());
4225 }
4226 if (TextUtils.isEmpty(name)) {
4227 // still nothing?
4228 return null;
4229 }
4230
4231 return String.valueOf(name);
4232 }
Adrian Roos487374f2017-01-11 15:48:14 -08004233 private void bindHeaderAppName(RemoteViews contentView, boolean ambient) {
Dan Sandler732bd6c2016-04-12 14:20:32 -04004234 contentView.setTextViewText(R.id.app_name_text, loadHeaderAppName());
Adrian Roos72171622017-01-27 10:32:06 -08004235 if (isColorized() && !ambient) {
Selim Cinek7b9605b2017-01-19 17:36:00 -08004236 setTextViewColorPrimary(contentView, R.id.app_name_text);
4237 } else {
4238 contentView.setTextColor(R.id.app_name_text,
4239 ambient ? resolveAmbientColor() : resolveContrastColor());
4240 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004241 }
4242
Adrian Roos487374f2017-01-11 15:48:14 -08004243 private void bindSmallIcon(RemoteViews contentView, boolean ambient) {
Selim Cinek279fa862016-06-14 10:57:25 -07004244 if (mN.mSmallIcon == null && mN.icon != 0) {
4245 mN.mSmallIcon = Icon.createWithResource(mContext, mN.icon);
4246 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004247 contentView.setImageViewIcon(R.id.icon, mN.mSmallIcon);
Adrian Roos9b45a152016-06-28 13:32:29 -07004248 contentView.setDrawableParameters(R.id.icon, false /* targetBackground */,
4249 -1 /* alpha */, -1 /* colorFilter */, null /* mode */, mN.iconLevel);
Adrian Roos487374f2017-01-11 15:48:14 -08004250 processSmallIconColor(mN.mSmallIcon, contentView, ambient);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004251 }
4252
Jorim Jaggi445d3c02014-08-19 22:33:42 +02004253 /**
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004254 * @return true if the built notification will show the time or the chronometer; false
4255 * otherwise
4256 */
4257 private boolean showsTimeOrChronometer() {
Selim Cinekc2c0b042016-05-18 17:13:46 -07004258 return mN.showsTime() || mN.showsChronometer();
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004259 }
4260
Christoph Studerfe718432014-09-01 18:21:18 +02004261 private void resetStandardTemplateWithActions(RemoteViews big) {
Adrian Roos4c1fcc82016-03-31 14:39:39 -07004262 // actions_container is only reset when there are no actions to avoid focus issues with
4263 // remote inputs.
Christoph Studerfe718432014-09-01 18:21:18 +02004264 big.setViewVisibility(R.id.actions, View.GONE);
Christoph Studerfe718432014-09-01 18:21:18 +02004265 big.removeAllViews(R.id.actions);
Adrian Roose458aa82015-12-08 16:17:19 -08004266
4267 big.setViewVisibility(R.id.notification_material_reply_container, View.GONE);
4268 big.setTextViewText(R.id.notification_material_reply_text_1, null);
4269
4270 big.setViewVisibility(R.id.notification_material_reply_text_2, View.GONE);
4271 big.setTextViewText(R.id.notification_material_reply_text_2, null);
4272 big.setViewVisibility(R.id.notification_material_reply_text_3, View.GONE);
4273 big.setTextViewText(R.id.notification_material_reply_text_3, null);
Adrian Roosf852a422016-06-03 13:33:43 -07004274
4275 big.setViewLayoutMarginBottomDimen(R.id.notification_action_list_margin_target, 0);
Christoph Studerfe718432014-09-01 18:21:18 +02004276 }
4277
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004278 private RemoteViews applyStandardTemplateWithActions(int layoutId) {
Adrian Roos70d7aa32017-01-11 15:39:06 -08004279 return applyStandardTemplateWithActions(layoutId, mParams.reset().fillTextsFrom(this));
Adrian Roos48d746a2016-04-12 14:57:28 -07004280 }
4281
Adrian Roos70d7aa32017-01-11 15:39:06 -08004282 private RemoteViews applyStandardTemplateWithActions(int layoutId,
4283 StandardTemplateParams p) {
4284 RemoteViews big = applyStandardTemplate(layoutId, p);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004285
Christoph Studerfe718432014-09-01 18:21:18 +02004286 resetStandardTemplateWithActions(big);
4287
Adrian Roose458aa82015-12-08 16:17:19 -08004288 boolean validRemoteInput = false;
4289
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004290 int N = mActions.size();
Adrian Roos487374f2017-01-11 15:48:14 -08004291 boolean emphazisedMode = mN.fullScreenIntent != null && !p.ambient;
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004292 big.setBoolean(R.id.actions, "setEmphasizedMode", emphazisedMode);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004293 if (N > 0) {
Adrian Roos7052de52016-03-03 15:53:34 -08004294 big.setViewVisibility(R.id.actions_container, View.VISIBLE);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004295 big.setViewVisibility(R.id.actions, View.VISIBLE);
Adrian Roos487374f2017-01-11 15:48:14 -08004296 if (p.ambient) {
4297 big.setInt(R.id.actions, "setBackgroundColor", Color.TRANSPARENT);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004298 } else if (isColorized()) {
4299 big.setInt(R.id.actions, "setBackgroundColor", getActionBarColor());
4300 } else {
4301 big.setInt(R.id.actions, "setBackgroundColor", mContext.getColor(
4302 R.color.notification_action_list));
Adrian Roos487374f2017-01-11 15:48:14 -08004303 }
Adrian Roosf852a422016-06-03 13:33:43 -07004304 big.setViewLayoutMarginBottomDimen(R.id.notification_action_list_margin_target,
4305 R.dimen.notification_action_list_height);
Daniel Sandler8680bf82012-05-15 16:52:52 -04004306 if (N>MAX_ACTION_BUTTONS) N=MAX_ACTION_BUTTONS;
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004307 for (int i=0; i<N; i++) {
Adrian Roose458aa82015-12-08 16:17:19 -08004308 Action action = mActions.get(i);
4309 validRemoteInput |= hasValidRemoteInput(action);
4310
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004311 final RemoteViews button = generateActionButton(action, emphazisedMode,
Adrian Roos487374f2017-01-11 15:48:14 -08004312 i % 2 != 0, p.ambient);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004313 big.addView(R.id.actions, button);
4314 }
Adrian Roos4c1fcc82016-03-31 14:39:39 -07004315 } else {
4316 big.setViewVisibility(R.id.actions_container, View.GONE);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004317 }
Adrian Roose458aa82015-12-08 16:17:19 -08004318
4319 CharSequence[] replyText = mN.extras.getCharSequenceArray(EXTRA_REMOTE_INPUT_HISTORY);
Adrian Roos487374f2017-01-11 15:48:14 -08004320 if (!p.ambient && validRemoteInput && replyText != null
Adrian Roose458aa82015-12-08 16:17:19 -08004321 && replyText.length > 0 && !TextUtils.isEmpty(replyText[0])) {
4322 big.setViewVisibility(R.id.notification_material_reply_container, View.VISIBLE);
4323 big.setTextViewText(R.id.notification_material_reply_text_1, replyText[0]);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004324 setTextViewColorSecondary(big, R.id.notification_material_reply_text_1);
Adrian Roose458aa82015-12-08 16:17:19 -08004325
4326 if (replyText.length > 1 && !TextUtils.isEmpty(replyText[1])) {
4327 big.setViewVisibility(R.id.notification_material_reply_text_2, View.VISIBLE);
4328 big.setTextViewText(R.id.notification_material_reply_text_2, replyText[1]);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004329 setTextViewColorSecondary(big, R.id.notification_material_reply_text_2);
Adrian Roose458aa82015-12-08 16:17:19 -08004330
4331 if (replyText.length > 2 && !TextUtils.isEmpty(replyText[2])) {
4332 big.setViewVisibility(
4333 R.id.notification_material_reply_text_3, View.VISIBLE);
4334 big.setTextViewText(R.id.notification_material_reply_text_3, replyText[2]);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004335 setTextViewColorSecondary(big, R.id.notification_material_reply_text_3);
Adrian Roose458aa82015-12-08 16:17:19 -08004336 }
4337 }
4338 }
4339
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004340 return big;
4341 }
4342
Adrian Roose458aa82015-12-08 16:17:19 -08004343 private boolean hasValidRemoteInput(Action action) {
4344 if (TextUtils.isEmpty(action.title) || action.actionIntent == null) {
4345 // Weird actions
4346 return false;
4347 }
4348
4349 RemoteInput[] remoteInputs = action.getRemoteInputs();
4350 if (remoteInputs == null) {
4351 return false;
4352 }
4353
4354 for (RemoteInput r : remoteInputs) {
4355 CharSequence[] choices = r.getChoices();
4356 if (r.getAllowFreeFormInput() || (choices != null && choices.length != 0)) {
4357 return true;
4358 }
4359 }
4360 return false;
4361 }
4362
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004363 /**
4364 * Construct a RemoteViews for the final 1U notification layout. In order:
4365 * 1. Custom contentView from the caller
4366 * 2. Style's proposed content view
4367 * 3. Standard template view
4368 */
Julia Reynolds3b848122016-02-26 10:45:32 -05004369 public RemoteViews createContentView() {
Selim Cinek7d1009b2017-01-25 15:28:28 -08004370 return createContentView(false /* increasedheight */ );
4371 }
4372
4373 /**
4374 * Construct a RemoteViews for the smaller content view.
4375 *
4376 * @param increasedHeight true if this layout be created with an increased height. Some
4377 * styles may support showing more then just that basic 1U size
4378 * and the system may decide to render important notifications
4379 * slightly bigger even when collapsed.
4380 *
4381 * @hide
4382 */
4383 public RemoteViews createContentView(boolean increasedHeight) {
Selim Cineka7679b62017-05-10 16:33:25 -07004384 if (mN.contentView != null && useExistingRemoteView()) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004385 return mN.contentView;
4386 } else if (mStyle != null) {
Selim Cinek7d1009b2017-01-25 15:28:28 -08004387 final RemoteViews styleView = mStyle.makeContentView(increasedHeight);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004388 if (styleView != null) {
4389 return styleView;
4390 }
Joe Onorato46439ce2010-11-19 13:56:21 -08004391 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004392 return applyStandardTemplate(getBaseLayoutResource());
Joe Onorato46439ce2010-11-19 13:56:21 -08004393 }
4394
Selim Cineka7679b62017-05-10 16:33:25 -07004395 private boolean useExistingRemoteView() {
4396 return mStyle == null || (!mStyle.displayCustomViewInline()
4397 && !mRebuildStyledRemoteViews);
4398 }
4399
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004400 /**
4401 * Construct a RemoteViews for the final big notification layout.
4402 */
Julia Reynolds3b848122016-02-26 10:45:32 -05004403 public RemoteViews createBigContentView() {
Selim Cinek850a8542015-11-11 11:48:36 -05004404 RemoteViews result = null;
Selim Cineka7679b62017-05-10 16:33:25 -07004405 if (mN.bigContentView != null && useExistingRemoteView()) {
Julia Reynolds089e3e42015-11-18 09:59:57 -05004406 return mN.bigContentView;
4407 } else if (mStyle != null) {
Selim Cinek850a8542015-11-11 11:48:36 -05004408 result = mStyle.makeBigContentView();
Selim Cinek90dcf6d2015-11-18 20:24:13 -08004409 hideLine1Text(result);
Selim Cinekcc10bfb2016-02-10 16:24:21 -08004410 } else if (mActions.size() != 0) {
4411 result = applyStandardTemplateWithActions(getBigBaseLayoutResource());
Selim Cinek850a8542015-11-11 11:48:36 -05004412 }
Selim Cinek6743c0b2017-01-18 18:24:01 -08004413 makeHeaderExpanded(result);
Selim Cinek850a8542015-11-11 11:48:36 -05004414 return result;
4415 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004416
Selim Cinekeaa29ca2015-11-23 13:51:13 -08004417 /**
Selim Cinek414ad332017-02-24 19:06:12 -08004418 * Construct a RemoteViews for the final notification header only. This will not be
4419 * colorized.
Selim Cinekeaa29ca2015-11-23 13:51:13 -08004420 *
Adrian Roos6f6e1592017-05-02 16:22:53 -07004421 * @param ambient if true, generate the header for the ambient display layout.
Selim Cinekeaa29ca2015-11-23 13:51:13 -08004422 * @hide
4423 */
Adrian Roos6f6e1592017-05-02 16:22:53 -07004424 public RemoteViews makeNotificationHeader(boolean ambient) {
Selim Cinek414ad332017-02-24 19:06:12 -08004425 Boolean colorized = (Boolean) mN.extras.get(EXTRA_COLORIZED);
4426 mN.extras.putBoolean(EXTRA_COLORIZED, false);
Selim Cinekeaa29ca2015-11-23 13:51:13 -08004427 RemoteViews header = new BuilderRemoteViews(mContext.getApplicationInfo(),
Adrian Roos6f6e1592017-05-02 16:22:53 -07004428 ambient ? R.layout.notification_template_ambient_header
4429 : R.layout.notification_template_header);
Selim Cinekeaa29ca2015-11-23 13:51:13 -08004430 resetNotificationHeader(header);
Adrian Roos6f6e1592017-05-02 16:22:53 -07004431 bindNotificationHeader(header, ambient);
Selim Cinek414ad332017-02-24 19:06:12 -08004432 if (colorized != null) {
4433 mN.extras.putBoolean(EXTRA_COLORIZED, colorized);
4434 } else {
4435 mN.extras.remove(EXTRA_COLORIZED);
4436 }
Selim Cinekeaa29ca2015-11-23 13:51:13 -08004437 return header;
4438 }
4439
Adrian Roos487374f2017-01-11 15:48:14 -08004440 /**
4441 * Construct a RemoteViews for the ambient version of the notification.
4442 *
4443 * @hide
4444 */
4445 public RemoteViews makeAmbientNotification() {
4446 RemoteViews ambient = applyStandardTemplateWithActions(
4447 R.layout.notification_template_material_ambient,
Adrian Roos0bc3f6a2017-03-06 11:54:05 -08004448 mParams.reset().ambient(true).fillTextsFrom(this).hasProgress(false));
Adrian Roos487374f2017-01-11 15:48:14 -08004449 return ambient;
4450 }
4451
Selim Cinek29603462015-11-17 19:04:39 -08004452 private void hideLine1Text(RemoteViews result) {
Selim Cinekcc10bfb2016-02-10 16:24:21 -08004453 if (result != null) {
4454 result.setViewVisibility(R.id.text_line_1, View.GONE);
4455 }
Selim Cinek29603462015-11-17 19:04:39 -08004456 }
4457
Selim Cinek6743c0b2017-01-18 18:24:01 -08004458 /**
4459 * Adapt the Notification header if this view is used as an expanded view.
4460 *
4461 * @hide
4462 */
4463 public static void makeHeaderExpanded(RemoteViews result) {
Selim Cinekcc10bfb2016-02-10 16:24:21 -08004464 if (result != null) {
4465 result.setBoolean(R.id.notification_header, "setExpanded", true);
4466 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004467 }
4468
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004469 /**
4470 * Construct a RemoteViews for the final heads-up notification layout.
Selim Cinek87ed69b2017-02-09 15:59:43 -08004471 *
4472 * @param increasedHeight true if this layout be created with an increased height. Some
4473 * styles may support showing more then just that basic 1U size
4474 * and the system may decide to render important notifications
4475 * slightly bigger even when collapsed.
4476 *
4477 * @hide
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004478 */
Selim Cinek87ed69b2017-02-09 15:59:43 -08004479 public RemoteViews createHeadsUpContentView(boolean increasedHeight) {
Selim Cineka7679b62017-05-10 16:33:25 -07004480 if (mN.headsUpContentView != null && useExistingRemoteView()) {
Julia Reynolds089e3e42015-11-18 09:59:57 -05004481 return mN.headsUpContentView;
4482 } else if (mStyle != null) {
Selim Cinek87ed69b2017-02-09 15:59:43 -08004483 final RemoteViews styleView = mStyle.makeHeadsUpContentView(increasedHeight);
4484 if (styleView != null) {
4485 return styleView;
4486 }
Julia Reynolds089e3e42015-11-18 09:59:57 -05004487 } else if (mActions.size() == 0) {
4488 return null;
4489 }
4490
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004491 return applyStandardTemplateWithActions(getBigBaseLayoutResource());
Chris Wren8fd39ec2014-02-27 17:43:26 -05004492 }
4493
Selim Cinek624c02db2015-12-14 21:00:02 -08004494 /**
Selim Cinek87ed69b2017-02-09 15:59:43 -08004495 * Construct a RemoteViews for the final heads-up notification layout.
4496 */
4497 public RemoteViews createHeadsUpContentView() {
4498 return createHeadsUpContentView(false /* useIncreasedHeight */);
4499 }
4500
4501 /**
Selim Cinek624c02db2015-12-14 21:00:02 -08004502 * Construct a RemoteViews for the display in public contexts like on the lockscreen.
4503 *
4504 * @hide
4505 */
4506 public RemoteViews makePublicContentView() {
Adrian Roos1a1ecfc2017-04-17 11:17:59 -07004507 return makePublicView(false /* ambient */);
4508 }
4509
4510 /**
4511 * Construct a RemoteViews for the display in public contexts like on the lockscreen.
4512 *
4513 * @hide
4514 */
4515 public RemoteViews makePublicAmbientNotification() {
4516 return makePublicView(true /* ambient */);
4517 }
4518
4519 private RemoteViews makePublicView(boolean ambient) {
Selim Cinek624c02db2015-12-14 21:00:02 -08004520 if (mN.publicVersion != null) {
4521 final Builder builder = recoverBuilder(mContext, mN.publicVersion);
Adrian Roos1a1ecfc2017-04-17 11:17:59 -07004522 return ambient ? builder.makeAmbientNotification() : builder.createContentView();
Selim Cinek624c02db2015-12-14 21:00:02 -08004523 }
4524 Bundle savedBundle = mN.extras;
4525 Style style = mStyle;
4526 mStyle = null;
4527 Icon largeIcon = mN.mLargeIcon;
4528 mN.mLargeIcon = null;
Selim Cinek279fa862016-06-14 10:57:25 -07004529 Bitmap largeIconLegacy = mN.largeIcon;
4530 mN.largeIcon = null;
Adrian Roosb19b06b2017-05-02 18:54:40 -07004531 ArrayList<Action> actions = mActions;
4532 mActions = new ArrayList<>();
Selim Cinek624c02db2015-12-14 21:00:02 -08004533 Bundle publicExtras = new Bundle();
4534 publicExtras.putBoolean(EXTRA_SHOW_WHEN,
4535 savedBundle.getBoolean(EXTRA_SHOW_WHEN));
4536 publicExtras.putBoolean(EXTRA_SHOW_CHRONOMETER,
4537 savedBundle.getBoolean(EXTRA_SHOW_CHRONOMETER));
Adrian Roos96b7e202016-05-17 13:50:38 -07004538 publicExtras.putBoolean(EXTRA_CHRONOMETER_COUNT_DOWN,
4539 savedBundle.getBoolean(EXTRA_CHRONOMETER_COUNT_DOWN));
Selim Cinek624c02db2015-12-14 21:00:02 -08004540 publicExtras.putCharSequence(EXTRA_TITLE,
Adrian Roos1a1ecfc2017-04-17 11:17:59 -07004541 mContext.getString(com.android.internal.R.string.notification_hidden_text));
Selim Cinek624c02db2015-12-14 21:00:02 -08004542 mN.extras = publicExtras;
Adrian Roos1a1ecfc2017-04-17 11:17:59 -07004543 final RemoteViews view = ambient ? makeAmbientNotification()
4544 : applyStandardTemplate(getBaseLayoutResource());
Selim Cinek624c02db2015-12-14 21:00:02 -08004545 mN.extras = savedBundle;
4546 mN.mLargeIcon = largeIcon;
Selim Cinek279fa862016-06-14 10:57:25 -07004547 mN.largeIcon = largeIconLegacy;
Adrian Roosb19b06b2017-05-02 18:54:40 -07004548 mActions = actions;
Selim Cinek624c02db2015-12-14 21:00:02 -08004549 mStyle = style;
Adrian Roos1a1ecfc2017-04-17 11:17:59 -07004550 return view;
Selim Cinek624c02db2015-12-14 21:00:02 -08004551 }
4552
Selim Cinek6743c0b2017-01-18 18:24:01 -08004553 /**
4554 * Construct a content view for the display when low - priority
4555 *
4556 * @param useRegularSubtext uses the normal subtext set if there is one available. Otherwise
4557 * a new subtext is created consisting of the content of the
4558 * notification.
4559 * @hide
4560 */
4561 public RemoteViews makeLowPriorityContentView(boolean useRegularSubtext) {
4562 int color = mN.color;
4563 mN.color = COLOR_DEFAULT;
4564 CharSequence summary = mN.extras.getCharSequence(EXTRA_SUB_TEXT);
4565 if (!useRegularSubtext || TextUtils.isEmpty(summary)) {
4566 CharSequence newSummary = createSummaryText();
4567 if (!TextUtils.isEmpty(newSummary)) {
4568 mN.extras.putCharSequence(EXTRA_SUB_TEXT, newSummary);
4569 }
4570 }
Selim Cinek875ba9b2017-02-13 16:20:17 -08004571
Adrian Roos6f6e1592017-05-02 16:22:53 -07004572 RemoteViews header = makeNotificationHeader(false /* ambient */);
Selim Cinek1b554392017-02-28 17:22:49 -08004573 header.setBoolean(R.id.notification_header, "setAcceptAllTouches", true);
Selim Cinek6743c0b2017-01-18 18:24:01 -08004574 if (summary != null) {
4575 mN.extras.putCharSequence(EXTRA_SUB_TEXT, summary);
4576 } else {
4577 mN.extras.remove(EXTRA_SUB_TEXT);
4578 }
4579 mN.color = color;
4580 return header;
4581 }
Selim Cinek624c02db2015-12-14 21:00:02 -08004582
Selim Cinek6743c0b2017-01-18 18:24:01 -08004583 private CharSequence createSummaryText() {
4584 CharSequence titleText = mN.extras.getCharSequence(Notification.EXTRA_TITLE);
4585 if (USE_ONLY_TITLE_IN_LOW_PRIORITY_SUMMARY) {
4586 return titleText;
4587 }
4588 SpannableStringBuilder summary = new SpannableStringBuilder();
4589 if (titleText == null) {
4590 titleText = mN.extras.getCharSequence(Notification.EXTRA_TITLE_BIG);
4591 }
4592 BidiFormatter bidi = BidiFormatter.getInstance();
4593 if (titleText != null) {
4594 summary.append(bidi.unicodeWrap(titleText));
4595 }
4596 CharSequence contentText = mN.extras.getCharSequence(Notification.EXTRA_TEXT);
4597 if (titleText != null && contentText != null) {
4598 summary.append(bidi.unicodeWrap(mContext.getText(
4599 R.string.notification_header_divider_symbol_with_spaces)));
4600 }
4601 if (contentText != null) {
4602 summary.append(bidi.unicodeWrap(contentText));
4603 }
4604 return summary;
4605 }
Chris Wren8fd39ec2014-02-27 17:43:26 -05004606
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004607 private RemoteViews generateActionButton(Action action, boolean emphazisedMode,
Adrian Roos487374f2017-01-11 15:48:14 -08004608 boolean oddAction, boolean ambient) {
Daniel Sandler8680bf82012-05-15 16:52:52 -04004609 final boolean tombstone = (action.actionIntent == null);
Selim Cinekf33b1112015-07-15 17:45:11 -07004610 RemoteViews button = new BuilderRemoteViews(mContext.getApplicationInfo(),
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004611 emphazisedMode ? getEmphasizedActionLayoutResource()
4612 : tombstone ? getActionTombstoneLayoutResource()
4613 : getActionLayoutResource());
Daniel Sandler8680bf82012-05-15 16:52:52 -04004614 if (!tombstone) {
Daniel Sandlere5518842012-05-10 16:20:40 -04004615 button.setOnClickPendingIntent(R.id.action0, action.actionIntent);
Daniel Sandlere5518842012-05-10 16:20:40 -04004616 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004617 button.setContentDescription(R.id.action0, action.title);
Adrian Roosfe84e1f2015-11-04 15:55:39 -08004618 if (action.mRemoteInputs != null) {
4619 button.setRemoteInputs(R.id.action0, action.mRemoteInputs);
4620 }
Selim Cinek7b9605b2017-01-19 17:36:00 -08004621 // TODO: handle emphasized mode / actions right
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004622 if (emphazisedMode) {
Selim Cinek981962e2016-07-20 20:41:58 -07004623 // change the background bgColor
Selim Cinek622c64a2017-04-17 17:10:05 -07004624 int bgColor;
4625 if (isColorized()) {
4626 bgColor = oddAction ? getActionBarColor() : getActionBarColorDeEmphasized();
4627 } else {
4628 bgColor = mContext.getColor(oddAction ? R.color.notification_action_list
4629 : R.color.notification_action_list_dark);
4630 }
Selim Cinek981962e2016-07-20 20:41:58 -07004631 button.setDrawableParameters(R.id.button_holder, true, -1, bgColor,
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004632 PorterDuff.Mode.SRC_ATOP, -1);
Selim Cinek981962e2016-07-20 20:41:58 -07004633 CharSequence title = action.title;
4634 ColorStateList[] outResultColor = null;
4635 if (isLegacy()) {
4636 title = clearColorSpans(title);
4637 } else {
4638 outResultColor = new ColorStateList[1];
4639 title = ensureColorSpanContrast(title, bgColor, outResultColor);
4640 }
4641 button.setTextViewText(R.id.action0, title);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004642 setTextViewColorPrimary(button, R.id.action0);
Selim Cinek981962e2016-07-20 20:41:58 -07004643 if (outResultColor != null && outResultColor[0] != null) {
4644 // We need to set the text color as well since changing a text to uppercase
4645 // clears its spans.
4646 button.setTextColor(R.id.action0, outResultColor[0]);
Anthony Chenad4d1582017-04-10 16:07:58 -07004647 } else if (mN.color != COLOR_DEFAULT && !isColorized() && mTintActionButtons) {
Selim Cinek981962e2016-07-20 20:41:58 -07004648 button.setTextColor(R.id.action0,resolveContrastColor());
4649 }
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004650 } else {
Selim Cinek981962e2016-07-20 20:41:58 -07004651 button.setTextViewText(R.id.action0, processLegacyText(action.title));
Adrian Roos72171622017-01-27 10:32:06 -08004652 if (isColorized() && !ambient) {
Selim Cinek7b9605b2017-01-19 17:36:00 -08004653 setTextViewColorPrimary(button, R.id.action0);
Anthony Chenad4d1582017-04-10 16:07:58 -07004654 } else if (mN.color != COLOR_DEFAULT && mTintActionButtons) {
Adrian Roos487374f2017-01-11 15:48:14 -08004655 button.setTextColor(R.id.action0,
4656 ambient ? resolveAmbientColor() : resolveContrastColor());
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004657 }
Adrian Roosfe84e1f2015-11-04 15:55:39 -08004658 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004659 return button;
4660 }
4661
Joe Onoratocb109a02011-01-18 17:57:41 -08004662 /**
Selim Cinek981962e2016-07-20 20:41:58 -07004663 * Clears all color spans of a text
4664 * @param charSequence the input text
4665 * @return the same text but without color spans
4666 */
4667 private CharSequence clearColorSpans(CharSequence charSequence) {
4668 if (charSequence instanceof Spanned) {
4669 Spanned ss = (Spanned) charSequence;
4670 Object[] spans = ss.getSpans(0, ss.length(), Object.class);
4671 SpannableStringBuilder builder = new SpannableStringBuilder(ss.toString());
4672 for (Object span : spans) {
4673 Object resultSpan = span;
4674 if (resultSpan instanceof CharacterStyle) {
4675 resultSpan = ((CharacterStyle) span).getUnderlying();
4676 }
4677 if (resultSpan instanceof TextAppearanceSpan) {
4678 TextAppearanceSpan originalSpan = (TextAppearanceSpan) resultSpan;
4679 if (originalSpan.getTextColor() != null) {
4680 resultSpan = new TextAppearanceSpan(
4681 originalSpan.getFamily(),
4682 originalSpan.getTextStyle(),
4683 originalSpan.getTextSize(),
4684 null,
4685 originalSpan.getLinkTextColor());
4686 }
4687 } else if (resultSpan instanceof ForegroundColorSpan
4688 || (resultSpan instanceof BackgroundColorSpan)) {
4689 continue;
4690 } else {
4691 resultSpan = span;
4692 }
4693 builder.setSpan(resultSpan, ss.getSpanStart(span), ss.getSpanEnd(span),
4694 ss.getSpanFlags(span));
4695 }
4696 return builder;
4697 }
4698 return charSequence;
4699 }
4700
4701 /**
4702 * Ensures contrast on color spans against a background color. also returns the color of the
4703 * text if a span was found that spans over the whole text.
4704 *
4705 * @param charSequence the charSequence on which the spans are
4706 * @param background the background color to ensure the contrast against
4707 * @param outResultColor an array in which a color will be returned as the first element if
4708 * there exists a full length color span.
4709 * @return the contrasted charSequence
4710 */
4711 private CharSequence ensureColorSpanContrast(CharSequence charSequence, int background,
4712 ColorStateList[] outResultColor) {
4713 if (charSequence instanceof Spanned) {
4714 Spanned ss = (Spanned) charSequence;
4715 Object[] spans = ss.getSpans(0, ss.length(), Object.class);
4716 SpannableStringBuilder builder = new SpannableStringBuilder(ss.toString());
4717 for (Object span : spans) {
4718 Object resultSpan = span;
4719 int spanStart = ss.getSpanStart(span);
4720 int spanEnd = ss.getSpanEnd(span);
4721 boolean fullLength = (spanEnd - spanStart) == charSequence.length();
4722 if (resultSpan instanceof CharacterStyle) {
4723 resultSpan = ((CharacterStyle) span).getUnderlying();
4724 }
4725 if (resultSpan instanceof TextAppearanceSpan) {
4726 TextAppearanceSpan originalSpan = (TextAppearanceSpan) resultSpan;
4727 ColorStateList textColor = originalSpan.getTextColor();
4728 if (textColor != null) {
4729 int[] colors = textColor.getColors();
4730 int[] newColors = new int[colors.length];
4731 for (int i = 0; i < newColors.length; i++) {
4732 newColors[i] = NotificationColorUtil.ensureLargeTextContrast(
Anthony Chenad4d1582017-04-10 16:07:58 -07004733 colors[i], background, mInNightMode);
Selim Cinek981962e2016-07-20 20:41:58 -07004734 }
4735 textColor = new ColorStateList(textColor.getStates().clone(),
4736 newColors);
4737 resultSpan = new TextAppearanceSpan(
4738 originalSpan.getFamily(),
4739 originalSpan.getTextStyle(),
4740 originalSpan.getTextSize(),
4741 textColor,
4742 originalSpan.getLinkTextColor());
4743 if (fullLength) {
4744 outResultColor[0] = new ColorStateList(
4745 textColor.getStates().clone(), newColors);
4746 }
4747 }
4748 } else if (resultSpan instanceof ForegroundColorSpan) {
4749 ForegroundColorSpan originalSpan = (ForegroundColorSpan) resultSpan;
4750 int foregroundColor = originalSpan.getForegroundColor();
4751 foregroundColor = NotificationColorUtil.ensureLargeTextContrast(
Anthony Chenad4d1582017-04-10 16:07:58 -07004752 foregroundColor, background, mInNightMode);
Selim Cinek981962e2016-07-20 20:41:58 -07004753 resultSpan = new ForegroundColorSpan(foregroundColor);
4754 if (fullLength) {
4755 outResultColor[0] = ColorStateList.valueOf(foregroundColor);
4756 }
4757 } else {
4758 resultSpan = span;
4759 }
4760
4761 builder.setSpan(resultSpan, spanStart, spanEnd, ss.getSpanFlags(span));
4762 }
4763 return builder;
4764 }
4765 return charSequence;
4766 }
4767
4768 /**
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004769 * @return Whether we are currently building a notification from a legacy (an app that
Alan Viverette3cb07a462014-06-06 14:19:53 -07004770 * doesn't create material notifications by itself) app.
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004771 */
4772 private boolean isLegacy() {
Selim Cinek7b9605b2017-01-19 17:36:00 -08004773 if (!mIsLegacyInitialized) {
4774 mIsLegacy = mContext.getApplicationInfo().targetSdkVersion
4775 < Build.VERSION_CODES.LOLLIPOP;
4776 mIsLegacyInitialized = true;
4777 }
4778 return mIsLegacy;
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004779 }
4780
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004781 private CharSequence processLegacyText(CharSequence charSequence) {
Adrian Roos0bc3f6a2017-03-06 11:54:05 -08004782 return processLegacyText(charSequence, false /* ambient */);
4783 }
4784
4785 private CharSequence processLegacyText(CharSequence charSequence, boolean ambient) {
4786 boolean isAlreadyLightText = isLegacy() || textColorsNeedInversion();
4787 boolean wantLightText = ambient;
4788 if (isAlreadyLightText != wantLightText) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004789 return getColorUtil().invertCharSequenceColors(charSequence);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004790 } else {
4791 return charSequence;
4792 }
4793 }
4794
Dan Sandler26e81cf2014-05-06 10:01:27 -04004795 /**
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004796 * Apply any necessariy colors to the small icon
Dan Sandler26e81cf2014-05-06 10:01:27 -04004797 */
Adrian Roos487374f2017-01-11 15:48:14 -08004798 private void processSmallIconColor(Icon smallIcon, RemoteViews contentView,
4799 boolean ambient) {
Selim Cinekea4bef72015-12-02 15:51:10 -08004800 boolean colorable = !isLegacy() || getColorUtil().isGrayscaleIcon(mContext, smallIcon);
Selim Cinek99104832017-01-25 14:47:33 -08004801 int color = ambient ? resolveAmbientColor() : getPrimaryHighlightColor();
Selim Cinekea4bef72015-12-02 15:51:10 -08004802 if (colorable) {
Adrian Roos487374f2017-01-11 15:48:14 -08004803 contentView.setDrawableParameters(R.id.icon, false, -1, color,
Jorim Jaggi92df1f22014-12-16 19:44:41 +01004804 PorterDuff.Mode.SRC_ATOP, -1);
Selim Cinekea4bef72015-12-02 15:51:10 -08004805
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004806 }
Selim Cinekea4bef72015-12-02 15:51:10 -08004807 contentView.setInt(R.id.notification_header, "setOriginalIconColor",
Adrian Roos487374f2017-01-11 15:48:14 -08004808 colorable ? color : NotificationHeaderView.NO_COLOR);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004809 }
4810
Dan Sandler26e81cf2014-05-06 10:01:27 -04004811 /**
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004812 * Make the largeIcon dark if it's a fake smallIcon (that is,
Dan Sandler26e81cf2014-05-06 10:01:27 -04004813 * if it's grayscale).
4814 */
4815 // TODO: also check bounds, transparency, that sort of thing.
Dan Sandlerd63f9322015-05-06 15:18:49 -04004816 private void processLargeLegacyIcon(Icon largeIcon, RemoteViews contentView) {
4817 if (largeIcon != null && isLegacy()
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004818 && getColorUtil().isGrayscaleIcon(mContext, largeIcon)) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004819 // resolve color will fall back to the default when legacy
Adrian Roos4ff3b122016-02-01 12:26:13 -08004820 contentView.setDrawableParameters(R.id.icon, false, -1, resolveContrastColor(),
Dan Sandler190d58d2014-05-15 09:33:39 -04004821 PorterDuff.Mode.SRC_ATOP, -1);
Jorim Jaggi92df1f22014-12-16 19:44:41 +01004822 }
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004823 }
4824
Julia Reynolds10ee1fc2015-11-09 11:04:55 -05004825 private void sanitizeColor() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004826 if (mN.color != COLOR_DEFAULT) {
4827 mN.color |= 0xFF000000; // no alpha for custom colors
Jorim Jaggi74419312014-06-10 20:57:21 +02004828 }
Jorim Jaggi74419312014-06-10 20:57:21 +02004829 }
4830
Adrian Roos4ff3b122016-02-01 12:26:13 -08004831 int resolveContrastColor() {
4832 if (mCachedContrastColorIsFor == mN.color && mCachedContrastColor != COLOR_INVALID) {
4833 return mCachedContrastColor;
Dan Sandler26e81cf2014-05-06 10:01:27 -04004834 }
Adrian Roos4ff3b122016-02-01 12:26:13 -08004835
Selim Cinekac5f0272017-05-02 16:05:41 -07004836 int color;
4837 int background = mBackgroundColorHint;
4838 if (mBackgroundColorHint == COLOR_INVALID) {
4839 background = mContext.getColor(
4840 com.android.internal.R.color.notification_material_background_color);
4841 }
4842 if (mN.color == COLOR_DEFAULT) {
4843 ensureColors();
4844 color = mSecondaryTextColor;
4845 } else {
4846 color = NotificationColorUtil.resolveContrastColor(mContext, mN.color,
Anthony Chenad4d1582017-04-10 16:07:58 -07004847 background, mInNightMode);
Selim Cinekac5f0272017-05-02 16:05:41 -07004848 }
4849 if (Color.alpha(color) < 255) {
4850 // alpha doesn't go well for color filters, so let's blend it manually
4851 color = NotificationColorUtil.compositeColors(color, background);
4852 }
Adrian Roos4ff3b122016-02-01 12:26:13 -08004853 mCachedContrastColorIsFor = mN.color;
Selim Cinekac5f0272017-05-02 16:05:41 -07004854 return mCachedContrastColor = color;
Dan Sandler26e81cf2014-05-06 10:01:27 -04004855 }
4856
Adrian Roos487374f2017-01-11 15:48:14 -08004857 int resolveAmbientColor() {
4858 if (mCachedAmbientColorIsFor == mN.color && mCachedAmbientColorIsFor != COLOR_INVALID) {
4859 return mCachedAmbientColor;
4860 }
4861 final int contrasted = NotificationColorUtil.resolveAmbientColor(mContext, mN.color);
4862
4863 mCachedAmbientColorIsFor = mN.color;
4864 return mCachedAmbientColor = contrasted;
4865 }
4866
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004867 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004868 * Apply the unstyled operations and return a new {@link Notification} object.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04004869 * @hide
Joe Onoratocb109a02011-01-18 17:57:41 -08004870 */
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04004871 public Notification buildUnstyled() {
Daniel Sandlera0a938c2012-03-15 08:42:37 -04004872 if (mActions.size() > 0) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004873 mN.actions = new Action[mActions.size()];
4874 mActions.toArray(mN.actions);
Daniel Sandlera0a938c2012-03-15 08:42:37 -04004875 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004876 if (!mPersonList.isEmpty()) {
4877 mN.extras.putStringArray(EXTRA_PEOPLE,
4878 mPersonList.toArray(new String[mPersonList.size()]));
Dan Sandler0bf2ed82013-12-21 23:33:41 -06004879 }
Selim Cinek247fa012016-02-18 09:50:48 -08004880 if (mN.bigContentView != null || mN.contentView != null
4881 || mN.headsUpContentView != null) {
4882 mN.extras.putBoolean(EXTRA_CONTAINS_CUSTOM_VIEW, true);
4883 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004884 return mN;
Joe Onorato46439ce2010-11-19 13:56:21 -08004885 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004886
Julia Reynolds3b848122016-02-26 10:45:32 -05004887 /**
4888 * Creates a Builder from an existing notification so further changes can be made.
4889 * @param context The context for your application / activity.
4890 * @param n The notification to create a Builder from.
4891 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004892 public static Notification.Builder recoverBuilder(Context context, Notification n) {
Christoph Studer4600f9b2014-07-22 22:44:43 +02004893 // Re-create notification context so we can access app resources.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004894 ApplicationInfo applicationInfo = n.extras.getParcelable(
4895 EXTRA_BUILDER_APPLICATION_INFO);
Christoph Studer4600f9b2014-07-22 22:44:43 +02004896 Context builderContext;
Julia Reynoldsda303542015-11-23 14:00:20 -05004897 if (applicationInfo != null) {
4898 try {
4899 builderContext = context.createApplicationContext(applicationInfo,
4900 Context.CONTEXT_RESTRICTED);
4901 } catch (NameNotFoundException e) {
4902 Log.e(TAG, "ApplicationInfo " + applicationInfo + " not found");
4903 builderContext = context; // try with our context
4904 }
4905 } else {
4906 builderContext = context; // try with given context
Christoph Studer4600f9b2014-07-22 22:44:43 +02004907 }
4908
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004909 return new Builder(builderContext, n);
Christoph Studer4600f9b2014-07-22 22:44:43 +02004910 }
4911
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004912 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004913 * @deprecated Use {@link #build()} instead.
4914 */
4915 @Deprecated
4916 public Notification getNotification() {
4917 return build();
4918 }
4919
4920 /**
4921 * Combine all of the options that have been set and return a new {@link Notification}
4922 * object.
4923 */
4924 public Notification build() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004925 // first, add any extras from the calling code
4926 if (mUserExtras != null) {
4927 mN.extras = getAllExtras();
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07004928 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004929
Selim Cinekb85f36fd2016-04-20 18:46:36 -07004930 mN.creationTime = System.currentTimeMillis();
4931
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004932 // lazy stuff from mContext; see comment in Builder(Context, Notification)
Julia Reynoldsda303542015-11-23 14:00:20 -05004933 Notification.addFieldsFromContext(mContext, mN);
Christoph Studer943aa672014-08-03 20:31:16 +02004934
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004935 buildUnstyled();
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004936
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004937 if (mStyle != null) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004938 mStyle.buildStyled(mN);
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004939 }
Adrian Roos5081c0d2016-02-26 16:04:19 -08004940 if (mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N
Selim Cineka7679b62017-05-10 16:33:25 -07004941 && (useExistingRemoteView())) {
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004942 if (mN.contentView == null) {
Julia Reynolds3b848122016-02-26 10:45:32 -05004943 mN.contentView = createContentView();
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004944 mN.extras.putInt(EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT,
4945 mN.contentView.getSequenceNumber());
4946 }
4947 if (mN.bigContentView == null) {
Julia Reynolds3b848122016-02-26 10:45:32 -05004948 mN.bigContentView = createBigContentView();
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004949 if (mN.bigContentView != null) {
4950 mN.extras.putInt(EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT,
4951 mN.bigContentView.getSequenceNumber());
4952 }
4953 }
4954 if (mN.headsUpContentView == null) {
Julia Reynolds3b848122016-02-26 10:45:32 -05004955 mN.headsUpContentView = createHeadsUpContentView();
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004956 if (mN.headsUpContentView != null) {
4957 mN.extras.putInt(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT,
4958 mN.headsUpContentView.getSequenceNumber());
4959 }
4960 }
4961 }
4962
Julia Reynolds4c0c2022016-02-02 15:11:59 -05004963 if ((mN.defaults & DEFAULT_LIGHTS) != 0) {
4964 mN.flags |= FLAG_SHOW_LIGHTS;
4965 }
4966
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004967 return mN;
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004968 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05004969
4970 /**
4971 * Apply this Builder to an existing {@link Notification} object.
4972 *
4973 * @hide
4974 */
4975 public Notification buildInto(Notification n) {
Daniel Sandler1a497d32013-04-18 14:52:45 -04004976 build().cloneInto(n, true);
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05004977 return n;
4978 }
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004979
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004980 /**
Adrian Roos184bfe022016-03-03 13:41:44 -08004981 * Removes RemoteViews that were created for compatibility from {@param n}, if they did not
Julia Reynolds8a3b4592017-06-26 17:15:14 -04004982 * change. Also removes extenders on low ram devices, as
4983 * {@link android.service.notification.NotificationListenerService} services are disabled.
Adrian Roos184bfe022016-03-03 13:41:44 -08004984 *
4985 * @return {@param n}, if no stripping is needed, otherwise a stripped clone of {@param n}.
4986 *
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004987 * @hide
4988 */
Julia Reynolds8a3b4592017-06-26 17:15:14 -04004989 public static Notification maybeCloneStrippedForDelivery(Notification n, boolean isLowRam) {
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004990 String templateClass = n.extras.getString(EXTRA_TEMPLATE);
Adrian Roos184bfe022016-03-03 13:41:44 -08004991
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004992 // Only strip views for known Styles because we won't know how to
4993 // re-create them otherwise.
Julia Reynolds8a3b4592017-06-26 17:15:14 -04004994 if (!isLowRam
4995 && !TextUtils.isEmpty(templateClass)
Adrian Roos184bfe022016-03-03 13:41:44 -08004996 && getNotificationStyleClass(templateClass) == null) {
4997 return n;
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004998 }
Adrian Roos184bfe022016-03-03 13:41:44 -08004999
5000 // Only strip unmodified BuilderRemoteViews.
5001 boolean stripContentView = n.contentView instanceof BuilderRemoteViews &&
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05005002 n.extras.getInt(EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT, -1) ==
Adrian Roos184bfe022016-03-03 13:41:44 -08005003 n.contentView.getSequenceNumber();
5004 boolean stripBigContentView = n.bigContentView instanceof BuilderRemoteViews &&
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05005005 n.extras.getInt(EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT, -1) ==
Adrian Roos184bfe022016-03-03 13:41:44 -08005006 n.bigContentView.getSequenceNumber();
5007 boolean stripHeadsUpContentView = n.headsUpContentView instanceof BuilderRemoteViews &&
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05005008 n.extras.getInt(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT, -1) ==
Adrian Roos184bfe022016-03-03 13:41:44 -08005009 n.headsUpContentView.getSequenceNumber();
5010
5011 // Nothing to do here, no need to clone.
Julia Reynolds8a3b4592017-06-26 17:15:14 -04005012 if (!isLowRam
5013 && !stripContentView && !stripBigContentView && !stripHeadsUpContentView) {
Adrian Roos184bfe022016-03-03 13:41:44 -08005014 return n;
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05005015 }
Adrian Roos184bfe022016-03-03 13:41:44 -08005016
5017 Notification clone = n.clone();
5018 if (stripContentView) {
5019 clone.contentView = null;
5020 clone.extras.remove(EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT);
5021 }
5022 if (stripBigContentView) {
5023 clone.bigContentView = null;
5024 clone.extras.remove(EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT);
5025 }
5026 if (stripHeadsUpContentView) {
5027 clone.headsUpContentView = null;
5028 clone.extras.remove(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT);
5029 }
Julia Reynolds8a3b4592017-06-26 17:15:14 -04005030 if (isLowRam) {
5031 clone.extras.remove(Notification.TvExtender.EXTRA_TV_EXTENDER);
5032 clone.extras.remove(WearableExtender.EXTRA_WEARABLE_EXTENSIONS);
5033 clone.extras.remove(CarExtender.EXTRA_CAR_EXTENDER);
5034 }
Adrian Roos184bfe022016-03-03 13:41:44 -08005035 return clone;
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05005036 }
5037
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01005038 private int getBaseLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07005039 return R.layout.notification_template_material_base;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01005040 }
5041
5042 private int getBigBaseLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07005043 return R.layout.notification_template_material_big_base;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01005044 }
5045
5046 private int getBigPictureLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07005047 return R.layout.notification_template_material_big_picture;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01005048 }
5049
5050 private int getBigTextLayoutResource() {
Jorim Jaggi445d3c02014-08-19 22:33:42 +02005051 return R.layout.notification_template_material_big_text;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01005052 }
5053
5054 private int getInboxLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07005055 return R.layout.notification_template_material_inbox;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01005056 }
5057
Adrian Roosc1a80b02016-04-05 14:54:55 -07005058 private int getMessagingLayoutResource() {
5059 return R.layout.notification_template_material_messaging;
5060 }
5061
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01005062 private int getActionLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07005063 return R.layout.notification_material_action;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01005064 }
5065
Selim Cinek06e9e1f2016-07-08 17:14:16 -07005066 private int getEmphasizedActionLayoutResource() {
5067 return R.layout.notification_material_action_emphasized;
5068 }
5069
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01005070 private int getActionTombstoneLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07005071 return R.layout.notification_material_action_tombstone;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01005072 }
Selim Cinek7b9605b2017-01-19 17:36:00 -08005073
5074 private int getBackgroundColor() {
5075 if (isColorized()) {
Selim Cinek5fb73f82017-04-20 16:55:38 -07005076 return mBackgroundColor != COLOR_INVALID ? mBackgroundColor : mN.color;
Selim Cinek7b9605b2017-01-19 17:36:00 -08005077 } else {
Selim Cinekac5f0272017-05-02 16:05:41 -07005078 return mBackgroundColorHint != COLOR_INVALID ? mBackgroundColorHint
5079 : COLOR_DEFAULT;
Selim Cinek7b9605b2017-01-19 17:36:00 -08005080 }
5081 }
5082
5083 private boolean isColorized() {
5084 return mN.isColorized();
5085 }
Selim Cinek99104832017-01-25 14:47:33 -08005086
Anthony Chenad4d1582017-04-10 16:07:58 -07005087 private boolean shouldTintActionButtons() {
5088 return mTintActionButtons;
5089 }
5090
Selim Cinek99104832017-01-25 14:47:33 -08005091 private boolean textColorsNeedInversion() {
5092 if (mStyle == null || !MediaStyle.class.equals(mStyle.getClass())) {
5093 return false;
5094 }
5095 int targetSdkVersion = mContext.getApplicationInfo().targetSdkVersion;
5096 return targetSdkVersion > Build.VERSION_CODES.M
5097 && targetSdkVersion < Build.VERSION_CODES.O;
5098 }
Selim Cinek5fb73f82017-04-20 16:55:38 -07005099
5100 /**
5101 * Set a color palette to be used as the background and textColors
5102 *
5103 * @param backgroundColor the color to be used as the background
5104 * @param foregroundColor the color to be used as the foreground
5105 *
5106 * @hide
5107 */
5108 public void setColorPalette(int backgroundColor, int foregroundColor) {
5109 mBackgroundColor = backgroundColor;
5110 mForegroundColor = foregroundColor;
5111 mTextColorsAreForBackground = COLOR_INVALID;
5112 ensureColors();
5113 }
Selim Cinekac5f0272017-05-02 16:05:41 -07005114
5115 /**
5116 * Sets the background color for this notification to be a different one then the default.
5117 * This is mainly used to calculate contrast and won't necessarily be applied to the
5118 * background.
5119 *
5120 * @hide
5121 */
5122 public void setBackgroundColorHint(int backgroundColor) {
5123 mBackgroundColorHint = backgroundColor;
5124 }
Selim Cineka7679b62017-05-10 16:33:25 -07005125
5126
5127 /**
5128 * Forces all styled remoteViews to be built from scratch and not use any cached
5129 * RemoteViews.
5130 * This is needed for legacy apps that are baking in their remoteviews into the
5131 * notification.
5132 *
5133 * @hide
5134 */
5135 public void setRebuildStyledRemoteViews(boolean rebuild) {
5136 mRebuildStyledRemoteViews = rebuild;
5137 }
Selim Cinek7b9605b2017-01-19 17:36:00 -08005138 }
5139
5140 /**
Selim Cinek22714f12017-04-13 16:23:53 -07005141 * @return whether this notification is a foreground service notification
Selim Cinek7b9605b2017-01-19 17:36:00 -08005142 */
Selim Cinek22714f12017-04-13 16:23:53 -07005143 private boolean isForegroundService() {
5144 return (flags & Notification.FLAG_FOREGROUND_SERVICE) != 0;
Selim Cinek7b9605b2017-01-19 17:36:00 -08005145 }
5146
5147 /**
Selim Cinek99104832017-01-25 14:47:33 -08005148 * @return whether this notification has a media session attached
5149 * @hide
5150 */
5151 public boolean hasMediaSession() {
5152 return extras.getParcelable(Notification.EXTRA_MEDIA_SESSION) != null;
5153 }
5154
5155 /**
Selim Cinek7d1009b2017-01-25 15:28:28 -08005156 * @return the style class of this notification
5157 * @hide
5158 */
5159 public Class<? extends Notification.Style> getNotificationStyle() {
5160 String templateClass = extras.getString(Notification.EXTRA_TEMPLATE);
5161
5162 if (!TextUtils.isEmpty(templateClass)) {
5163 return Notification.getNotificationStyleClass(templateClass);
5164 }
5165 return null;
5166 }
5167
5168 /**
Selim Cinek22714f12017-04-13 16:23:53 -07005169 * @return true if this notification is colorized.
Selim Cinek7b9605b2017-01-19 17:36:00 -08005170 *
5171 * @hide
5172 */
5173 public boolean isColorized() {
Selim Cinek5fb73f82017-04-20 16:55:38 -07005174 if (isColorizedMedia()) {
5175 return true;
5176 }
5177 return extras.getBoolean(EXTRA_COLORIZED) && isForegroundService();
5178 }
5179
5180 /**
5181 * @return true if this notification is colorized and it is a media notification
5182 *
5183 * @hide
5184 */
5185 public boolean isColorizedMedia() {
Selim Cinek99104832017-01-25 14:47:33 -08005186 Class<? extends Style> style = getNotificationStyle();
5187 if (MediaStyle.class.equals(style)) {
5188 Boolean colorized = (Boolean) extras.get(EXTRA_COLORIZED);
5189 if ((colorized == null || colorized) && hasMediaSession()) {
5190 return true;
5191 }
5192 } else if (DecoratedMediaCustomViewStyle.class.equals(style)) {
5193 if (extras.getBoolean(EXTRA_COLORIZED) && hasMediaSession()) {
5194 return true;
5195 }
5196 }
Selim Cinek5fb73f82017-04-20 16:55:38 -07005197 return false;
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005198 }
5199
Selim Cinek0847acd2017-04-24 19:48:29 -07005200
5201 /**
5202 * @return true if this is a media notification
5203 *
5204 * @hide
5205 */
5206 public boolean isMediaNotification() {
5207 Class<? extends Style> style = getNotificationStyle();
5208 if (MediaStyle.class.equals(style)) {
5209 return true;
5210 } else if (DecoratedMediaCustomViewStyle.class.equals(style)) {
5211 return true;
5212 }
5213 return false;
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005214 }
5215
Selim Cinek279fa862016-06-14 10:57:25 -07005216 private boolean hasLargeIcon() {
5217 return mLargeIcon != null || largeIcon != null;
5218 }
5219
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005220 /**
Selim Cinekc2c0b042016-05-18 17:13:46 -07005221 * @return true if the notification will show the time; false otherwise
Selim Cinekb85f36fd2016-04-20 18:46:36 -07005222 * @hide
5223 */
Selim Cinekc2c0b042016-05-18 17:13:46 -07005224 public boolean showsTime() {
Selim Cinekb85f36fd2016-04-20 18:46:36 -07005225 return when != 0 && extras.getBoolean(EXTRA_SHOW_WHEN);
5226 }
5227
5228 /**
Selim Cinekc2c0b042016-05-18 17:13:46 -07005229 * @return true if the notification will show a chronometer; false otherwise
5230 * @hide
5231 */
5232 public boolean showsChronometer() {
5233 return when != 0 && extras.getBoolean(EXTRA_SHOW_CHRONOMETER);
5234 }
5235
5236 /**
Julia Reynolds7ca33072017-06-29 13:58:24 -04005237 * @removed
Julia Reynolds4a02afb2016-12-13 13:39:52 -05005238 */
5239 @SystemApi
5240 public static Class<? extends Style> getNotificationStyleClass(String templateClass) {
5241 Class<? extends Style>[] classes = new Class[] {
5242 BigTextStyle.class, BigPictureStyle.class, InboxStyle.class, MediaStyle.class,
5243 DecoratedCustomViewStyle.class, DecoratedMediaCustomViewStyle.class,
5244 MessagingStyle.class };
5245 for (Class<? extends Style> innerClass : classes) {
5246 if (templateClass.equals(innerClass.getName())) {
5247 return innerClass;
5248 }
5249 }
5250 return null;
5251 }
5252
5253 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005254 * An object that can apply a rich notification style to a {@link Notification.Builder}
5255 * object.
5256 */
Griff Hazendfcb0802014-02-11 12:00:00 -08005257 public static abstract class Style {
Chris Wrend6297db2012-05-03 16:20:13 -04005258 private CharSequence mBigContentTitle;
Jorim Jaggi457a10d2014-09-08 16:18:23 +02005259
5260 /**
5261 * @hide
5262 */
5263 protected CharSequence mSummaryText = null;
5264
5265 /**
5266 * @hide
5267 */
5268 protected boolean mSummaryTextSet = false;
Chris Wrend6297db2012-05-03 16:20:13 -04005269
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005270 protected Builder mBuilder;
5271
Chris Wrend6297db2012-05-03 16:20:13 -04005272 /**
5273 * Overrides ContentTitle in the big form of the template.
5274 * This defaults to the value passed to setContentTitle().
5275 */
5276 protected void internalSetBigContentTitle(CharSequence title) {
5277 mBigContentTitle = title;
5278 }
5279
5280 /**
5281 * Set the first line of text after the detail section in the big form of the template.
5282 */
5283 protected void internalSetSummaryText(CharSequence cs) {
5284 mSummaryText = cs;
Daniel Sandler619738c2012-06-07 16:33:08 -04005285 mSummaryTextSet = true;
Chris Wrend6297db2012-05-03 16:20:13 -04005286 }
5287
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005288 public void setBuilder(Builder builder) {
5289 if (mBuilder != builder) {
5290 mBuilder = builder;
Daniel Sandlerc08dea22012-06-28 08:35:24 -07005291 if (mBuilder != null) {
5292 mBuilder.setStyle(this);
5293 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005294 }
5295 }
5296
Chris Wrend6297db2012-05-03 16:20:13 -04005297 protected void checkBuilder() {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005298 if (mBuilder == null) {
5299 throw new IllegalArgumentException("Style requires a valid Builder object");
5300 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005301 }
Chris Wrend6297db2012-05-03 16:20:13 -04005302
5303 protected RemoteViews getStandardView(int layoutId) {
5304 checkBuilder();
5305
Christoph Studer4600f9b2014-07-22 22:44:43 +02005306 // Nasty.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005307 CharSequence oldBuilderContentTitle =
5308 mBuilder.getAllExtras().getCharSequence(EXTRA_TITLE);
Chris Wrend6297db2012-05-03 16:20:13 -04005309 if (mBigContentTitle != null) {
5310 mBuilder.setContentTitle(mBigContentTitle);
5311 }
5312
Chris Wrend6297db2012-05-03 16:20:13 -04005313 RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(layoutId);
5314
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005315 mBuilder.getAllExtras().putCharSequence(EXTRA_TITLE, oldBuilderContentTitle);
Christoph Studer4600f9b2014-07-22 22:44:43 +02005316
Chris Wrend6297db2012-05-03 16:20:13 -04005317 if (mBigContentTitle != null && mBigContentTitle.equals("")) {
5318 contentView.setViewVisibility(R.id.line1, View.GONE);
Chris Wren67dc9a02012-05-16 01:03:20 -04005319 } else {
5320 contentView.setViewVisibility(R.id.line1, View.VISIBLE);
Chris Wrend6297db2012-05-03 16:20:13 -04005321 }
5322
Chris Wrend6297db2012-05-03 16:20:13 -04005323 return contentView;
5324 }
5325
Daniel Sandlerf45564e2013-04-15 15:05:08 -04005326 /**
Selim Cinek7d1009b2017-01-25 15:28:28 -08005327 * Construct a Style-specific RemoteViews for the collapsed notification layout.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005328 * The default implementation has nothing additional to add.
Selim Cinek7d1009b2017-01-25 15:28:28 -08005329 *
5330 * @param increasedHeight true if this layout be created with an increased height.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005331 * @hide
5332 */
Selim Cinek7d1009b2017-01-25 15:28:28 -08005333 public RemoteViews makeContentView(boolean increasedHeight) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005334 return null;
5335 }
5336
5337 /**
5338 * Construct a Style-specific RemoteViews for the final big notification layout.
5339 * @hide
5340 */
5341 public RemoteViews makeBigContentView() {
5342 return null;
5343 }
5344
5345 /**
5346 * Construct a Style-specific RemoteViews for the final HUN layout.
Selim Cinek87ed69b2017-02-09 15:59:43 -08005347 *
5348 * @param increasedHeight true if this layout be created with an increased height.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005349 * @hide
5350 */
Selim Cinek87ed69b2017-02-09 15:59:43 -08005351 public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005352 return null;
5353 }
5354
5355 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005356 * Apply any style-specific extras to this notification before shipping it out.
Daniel Sandlerf45564e2013-04-15 15:05:08 -04005357 * @hide
5358 */
5359 public void addExtras(Bundle extras) {
5360 if (mSummaryTextSet) {
5361 extras.putCharSequence(EXTRA_SUMMARY_TEXT, mSummaryText);
5362 }
5363 if (mBigContentTitle != null) {
5364 extras.putCharSequence(EXTRA_TITLE_BIG, mBigContentTitle);
5365 }
Chris Wren91ad5632013-06-05 15:05:57 -04005366 extras.putString(EXTRA_TEMPLATE, this.getClass().getName());
Daniel Sandlerf45564e2013-04-15 15:05:08 -04005367 }
5368
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04005369 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005370 * Reconstruct the internal state of this Style object from extras.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04005371 * @hide
5372 */
Christoph Studer4600f9b2014-07-22 22:44:43 +02005373 protected void restoreFromExtras(Bundle extras) {
5374 if (extras.containsKey(EXTRA_SUMMARY_TEXT)) {
5375 mSummaryText = extras.getCharSequence(EXTRA_SUMMARY_TEXT);
5376 mSummaryTextSet = true;
5377 }
5378 if (extras.containsKey(EXTRA_TITLE_BIG)) {
5379 mBigContentTitle = extras.getCharSequence(EXTRA_TITLE_BIG);
5380 }
5381 }
5382
5383
5384 /**
5385 * @hide
5386 */
5387 public Notification buildStyled(Notification wip) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005388 addExtras(wip.extras);
Christoph Studer4600f9b2014-07-22 22:44:43 +02005389 return wip;
5390 }
5391
Daniel Sandler0ec46202015-06-24 01:27:05 -04005392 /**
5393 * @hide
5394 */
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07005395 public void purgeResources() {}
5396
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04005397 /**
5398 * Calls {@link android.app.Notification.Builder#build()} on the Builder this Style is
5399 * attached to.
5400 *
5401 * @return the fully constructed Notification.
5402 */
5403 public Notification build() {
5404 checkBuilder();
5405 return mBuilder.build();
5406 }
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02005407
5408 /**
5409 * @hide
5410 * @return true if the style positions the progress bar on the second line; false if the
5411 * style hides the progress bar
5412 */
5413 protected boolean hasProgress() {
5414 return true;
5415 }
Selim Cinek03d0d652015-11-13 13:18:09 -05005416
5417 /**
5418 * @hide
5419 * @return Whether we should put the summary be put into the notification header
5420 */
5421 public boolean hasSummaryInHeader() {
5422 return true;
5423 }
Selim Cinek593610c2016-02-16 18:42:57 -08005424
5425 /**
5426 * @hide
5427 * @return Whether custom content views are displayed inline in the style
5428 */
5429 public boolean displayCustomViewInline() {
5430 return false;
5431 }
Joe Onorato46439ce2010-11-19 13:56:21 -08005432 }
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005433
5434 /**
Daniel Sandler4dfbe832012-04-11 14:51:46 -04005435 * Helper class for generating large-format notifications that include a large image attachment.
Joe Malin8d40d042012-11-05 11:36:40 -08005436 *
Robert Ly91c5ce32014-06-08 15:37:00 -07005437 * Here's how you'd set the <code>BigPictureStyle</code> on a notification:
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005438 * <pre class="prettyprint">
Robert Ly91c5ce32014-06-08 15:37:00 -07005439 * Notification notif = new Notification.Builder(mContext)
5440 * .setContentTitle(&quot;New photo from &quot; + sender.toString())
5441 * .setContentText(subject)
5442 * .setSmallIcon(R.drawable.new_post)
5443 * .setLargeIcon(aBitmap)
5444 * .setStyle(new Notification.BigPictureStyle()
5445 * .bigPicture(aBigBitmap))
5446 * .build();
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005447 * </pre>
Joe Malin8d40d042012-11-05 11:36:40 -08005448 *
Daniel Sandler4dfbe832012-04-11 14:51:46 -04005449 * @see Notification#bigContentView
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005450 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005451 public static class BigPictureStyle extends Style {
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005452 private Bitmap mPicture;
Dan Sandlerd63f9322015-05-06 15:18:49 -04005453 private Icon mBigLargeIcon;
Chris Wren3745a3d2012-05-22 15:11:52 -04005454 private boolean mBigLargeIconSet = false;
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005455
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005456 public BigPictureStyle() {
5457 }
5458
Adrian Roosf5faf9d2016-05-23 13:56:15 -07005459 /**
5460 * @deprecated use {@code BigPictureStyle()}.
5461 */
5462 @Deprecated
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005463 public BigPictureStyle(Builder builder) {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005464 setBuilder(builder);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005465 }
5466
Chris Wrend6297db2012-05-03 16:20:13 -04005467 /**
5468 * Overrides ContentTitle in the big form of the template.
5469 * This defaults to the value passed to setContentTitle().
5470 */
5471 public BigPictureStyle setBigContentTitle(CharSequence title) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04005472 internalSetBigContentTitle(safeCharSequence(title));
Chris Wrend6297db2012-05-03 16:20:13 -04005473 return this;
5474 }
5475
5476 /**
5477 * Set the first line of text after the detail section in the big form of the template.
5478 */
5479 public BigPictureStyle setSummaryText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04005480 internalSetSummaryText(safeCharSequence(cs));
Chris Wrend6297db2012-05-03 16:20:13 -04005481 return this;
5482 }
5483
Chris Wren0bd664d2012-08-01 13:56:56 -04005484 /**
5485 * Provide the bitmap to be used as the payload for the BigPicture notification.
5486 */
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005487 public BigPictureStyle bigPicture(Bitmap b) {
5488 mPicture = b;
5489 return this;
5490 }
5491
Chris Wren3745a3d2012-05-22 15:11:52 -04005492 /**
Chris Wren3745a3d2012-05-22 15:11:52 -04005493 * Override the large icon when the big notification is shown.
5494 */
5495 public BigPictureStyle bigLargeIcon(Bitmap b) {
Dan Sandlerd63f9322015-05-06 15:18:49 -04005496 return bigLargeIcon(b != null ? Icon.createWithBitmap(b) : null);
5497 }
5498
5499 /**
5500 * Override the large icon when the big notification is shown.
5501 */
5502 public BigPictureStyle bigLargeIcon(Icon icon) {
Chris Wren3745a3d2012-05-22 15:11:52 -04005503 mBigLargeIconSet = true;
Dan Sandlerd63f9322015-05-06 15:18:49 -04005504 mBigLargeIcon = icon;
Chris Wren3745a3d2012-05-22 15:11:52 -04005505 return this;
5506 }
5507
Riley Andrews0394a0c2015-11-03 23:36:52 -08005508 /** @hide */
5509 public static final int MIN_ASHMEM_BITMAP_SIZE = 128 * (1 << 10);
5510
Daniel Sandler0ec46202015-06-24 01:27:05 -04005511 /**
5512 * @hide
5513 */
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07005514 @Override
5515 public void purgeResources() {
5516 super.purgeResources();
Riley Andrews8cee7c12015-11-01 23:36:04 -08005517 if (mPicture != null &&
5518 mPicture.isMutable() &&
Riley Andrews0394a0c2015-11-03 23:36:52 -08005519 mPicture.getAllocationByteCount() >= MIN_ASHMEM_BITMAP_SIZE) {
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07005520 mPicture = mPicture.createAshmemBitmap();
5521 }
5522 if (mBigLargeIcon != null) {
5523 mBigLargeIcon.convertToAshmem();
5524 }
5525 }
Christoph Studer5c510ee2014-12-15 16:32:27 +01005526
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005527 /**
5528 * @hide
5529 */
5530 public RemoteViews makeBigContentView() {
5531 // Replace mN.mLargeIcon with mBigLargeIcon if mBigLargeIconSet
Christoph Studer5c510ee2014-12-15 16:32:27 +01005532 // This covers the following cases:
5533 // 1. mBigLargeIconSet -> mBigLargeIcon (null or non-null) applies, overrides
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005534 // mN.mLargeIcon
5535 // 2. !mBigLargeIconSet -> mN.mLargeIcon applies
Dan Sandlerd63f9322015-05-06 15:18:49 -04005536 Icon oldLargeIcon = null;
Selim Cineke99acb22016-08-04 12:55:48 -07005537 Bitmap largeIconLegacy = null;
Christoph Studer5c510ee2014-12-15 16:32:27 +01005538 if (mBigLargeIconSet) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005539 oldLargeIcon = mBuilder.mN.mLargeIcon;
5540 mBuilder.mN.mLargeIcon = mBigLargeIcon;
Selim Cineke99acb22016-08-04 12:55:48 -07005541 // The legacy largeIcon might not allow us to clear the image, as it's taken in
5542 // replacement if the other one is null. Because we're restoring these legacy icons
5543 // for old listeners, this is in general non-null.
5544 largeIconLegacy = mBuilder.mN.largeIcon;
5545 mBuilder.mN.largeIcon = null;
Christoph Studer5c510ee2014-12-15 16:32:27 +01005546 }
5547
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01005548 RemoteViews contentView = getStandardView(mBuilder.getBigPictureLayoutResource());
Selim Cinek03d0d652015-11-13 13:18:09 -05005549 if (mSummaryTextSet) {
5550 contentView.setTextViewText(R.id.text, mBuilder.processLegacyText(mSummaryText));
Selim Cinek7b9605b2017-01-19 17:36:00 -08005551 mBuilder.setTextViewColorSecondary(contentView, R.id.text);
Selim Cinekc848c3a2016-01-13 15:27:30 -08005552 contentView.setViewVisibility(R.id.text, View.VISIBLE);
Selim Cinek03d0d652015-11-13 13:18:09 -05005553 }
Selim Cinek279fa862016-06-14 10:57:25 -07005554 mBuilder.setContentMinHeight(contentView, mBuilder.mN.hasLargeIcon());
Selim Cinek53e64a42015-11-16 10:40:56 -08005555
Christoph Studer5c510ee2014-12-15 16:32:27 +01005556 if (mBigLargeIconSet) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005557 mBuilder.mN.mLargeIcon = oldLargeIcon;
Selim Cineke99acb22016-08-04 12:55:48 -07005558 mBuilder.mN.largeIcon = largeIconLegacy;
Christoph Studer5c510ee2014-12-15 16:32:27 +01005559 }
5560
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005561 contentView.setImageViewBitmap(R.id.big_picture, mPicture);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005562 return contentView;
5563 }
5564
Daniel Sandlerf45564e2013-04-15 15:05:08 -04005565 /**
5566 * @hide
5567 */
5568 public void addExtras(Bundle extras) {
5569 super.addExtras(extras);
5570
5571 if (mBigLargeIconSet) {
5572 extras.putParcelable(EXTRA_LARGE_ICON_BIG, mBigLargeIcon);
5573 }
5574 extras.putParcelable(EXTRA_PICTURE, mPicture);
5575 }
5576
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04005577 /**
5578 * @hide
5579 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005580 @Override
Christoph Studer4600f9b2014-07-22 22:44:43 +02005581 protected void restoreFromExtras(Bundle extras) {
5582 super.restoreFromExtras(extras);
5583
5584 if (extras.containsKey(EXTRA_LARGE_ICON_BIG)) {
Christoph Studer5c510ee2014-12-15 16:32:27 +01005585 mBigLargeIconSet = true;
Christoph Studer4600f9b2014-07-22 22:44:43 +02005586 mBigLargeIcon = extras.getParcelable(EXTRA_LARGE_ICON_BIG);
Chris Wren3745a3d2012-05-22 15:11:52 -04005587 }
Christoph Studer4600f9b2014-07-22 22:44:43 +02005588 mPicture = extras.getParcelable(EXTRA_PICTURE);
5589 }
Selim Cinek03d0d652015-11-13 13:18:09 -05005590
5591 /**
5592 * @hide
5593 */
5594 @Override
5595 public boolean hasSummaryInHeader() {
5596 return false;
5597 }
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005598 }
5599
5600 /**
Daniel Sandler4dfbe832012-04-11 14:51:46 -04005601 * Helper class for generating large-format notifications that include a lot of text.
Joe Malin8d40d042012-11-05 11:36:40 -08005602 *
Robert Ly91c5ce32014-06-08 15:37:00 -07005603 * Here's how you'd set the <code>BigTextStyle</code> on a notification:
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005604 * <pre class="prettyprint">
Robert Ly91c5ce32014-06-08 15:37:00 -07005605 * Notification notif = new Notification.Builder(mContext)
5606 * .setContentTitle(&quot;New mail from &quot; + sender.toString())
5607 * .setContentText(subject)
5608 * .setSmallIcon(R.drawable.new_mail)
5609 * .setLargeIcon(aBitmap)
5610 * .setStyle(new Notification.BigTextStyle()
5611 * .bigText(aVeryLongString))
5612 * .build();
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005613 * </pre>
Joe Malin8d40d042012-11-05 11:36:40 -08005614 *
Daniel Sandler4dfbe832012-04-11 14:51:46 -04005615 * @see Notification#bigContentView
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005616 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005617 public static class BigTextStyle extends Style {
Jorim Jaggi457a10d2014-09-08 16:18:23 +02005618
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005619 private CharSequence mBigText;
5620
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005621 public BigTextStyle() {
5622 }
5623
Adrian Roosf5faf9d2016-05-23 13:56:15 -07005624 /**
5625 * @deprecated use {@code BigTextStyle()}.
5626 */
5627 @Deprecated
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005628 public BigTextStyle(Builder builder) {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005629 setBuilder(builder);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005630 }
5631
Chris Wrend6297db2012-05-03 16:20:13 -04005632 /**
5633 * Overrides ContentTitle in the big form of the template.
5634 * This defaults to the value passed to setContentTitle().
5635 */
5636 public BigTextStyle setBigContentTitle(CharSequence title) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04005637 internalSetBigContentTitle(safeCharSequence(title));
Chris Wrend6297db2012-05-03 16:20:13 -04005638 return this;
5639 }
5640
5641 /**
5642 * Set the first line of text after the detail section in the big form of the template.
5643 */
5644 public BigTextStyle setSummaryText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04005645 internalSetSummaryText(safeCharSequence(cs));
Chris Wrend6297db2012-05-03 16:20:13 -04005646 return this;
5647 }
5648
Chris Wren0bd664d2012-08-01 13:56:56 -04005649 /**
5650 * Provide the longer text to be displayed in the big form of the
5651 * template in place of the content text.
5652 */
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005653 public BigTextStyle bigText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04005654 mBigText = safeCharSequence(cs);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005655 return this;
5656 }
5657
Daniel Sandlerf45564e2013-04-15 15:05:08 -04005658 /**
5659 * @hide
5660 */
5661 public void addExtras(Bundle extras) {
5662 super.addExtras(extras);
5663
Christoph Studer4600f9b2014-07-22 22:44:43 +02005664 extras.putCharSequence(EXTRA_BIG_TEXT, mBigText);
5665 }
5666
5667 /**
5668 * @hide
5669 */
5670 @Override
5671 protected void restoreFromExtras(Bundle extras) {
5672 super.restoreFromExtras(extras);
5673
5674 mBigText = extras.getCharSequence(EXTRA_BIG_TEXT);
Daniel Sandlerf45564e2013-04-15 15:05:08 -04005675 }
5676
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005677 /**
Selim Cinek7d1009b2017-01-25 15:28:28 -08005678 * @param increasedHeight true if this layout be created with an increased height.
5679 *
5680 * @hide
5681 */
5682 @Override
5683 public RemoteViews makeContentView(boolean increasedHeight) {
5684 if (increasedHeight) {
Selim Cinek5d6ef8d2017-05-18 22:16:00 -07005685 mBuilder.mOriginalActions = mBuilder.mActions;
Selim Cinek7d1009b2017-01-25 15:28:28 -08005686 mBuilder.mActions = new ArrayList<>();
5687 RemoteViews remoteViews = makeBigContentView();
Selim Cinek5d6ef8d2017-05-18 22:16:00 -07005688 mBuilder.mActions = mBuilder.mOriginalActions;
5689 mBuilder.mOriginalActions = null;
Selim Cinek7d1009b2017-01-25 15:28:28 -08005690 return remoteViews;
5691 }
5692 return super.makeContentView(increasedHeight);
5693 }
5694
5695 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005696 * @hide
5697 */
Selim Cinek87ed69b2017-02-09 15:59:43 -08005698 @Override
5699 public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
5700 if (increasedHeight && mBuilder.mActions.size() > 0) {
5701 return makeBigContentView();
5702 }
5703 return super.makeHeadsUpContentView(increasedHeight);
5704 }
5705
5706 /**
5707 * @hide
5708 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005709 public RemoteViews makeBigContentView() {
Christoph Studer4600f9b2014-07-22 22:44:43 +02005710
5711 // Nasty
Selim Cinek75998782016-04-26 10:39:17 -07005712 CharSequence text = mBuilder.getAllExtras().getCharSequence(EXTRA_TEXT);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005713 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, null);
Daniel Sandler916ad912012-06-13 12:17:07 -04005714
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01005715 RemoteViews contentView = getStandardView(mBuilder.getBigTextLayoutResource());
Joe Malin8d40d042012-11-05 11:36:40 -08005716
Selim Cinek75998782016-04-26 10:39:17 -07005717 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, text);
Christoph Studer4600f9b2014-07-22 22:44:43 +02005718
Selim Cinek3a2c4b92015-12-17 17:01:17 -08005719 CharSequence bigTextText = mBuilder.processLegacyText(mBigText);
Selim Cinek75998782016-04-26 10:39:17 -07005720 if (TextUtils.isEmpty(bigTextText)) {
5721 // In case the bigtext is null / empty fall back to the normal text to avoid a weird
5722 // experience
5723 bigTextText = mBuilder.processLegacyText(text);
5724 }
Adrian Roosb1f427c2016-05-26 12:27:15 -07005725 applyBigTextContentView(mBuilder, contentView, bigTextText);
Selim Cinek4fb12d32015-11-19 18:10:48 -08005726
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005727 return contentView;
5728 }
5729
Adrian Roosb1f427c2016-05-26 12:27:15 -07005730 static void applyBigTextContentView(Builder builder,
5731 RemoteViews contentView, CharSequence bigTextText) {
5732 contentView.setTextViewText(R.id.big_text, bigTextText);
Selim Cinek7b9605b2017-01-19 17:36:00 -08005733 builder.setTextViewColorSecondary(contentView, R.id.big_text);
Adrian Roosb1f427c2016-05-26 12:27:15 -07005734 contentView.setViewVisibility(R.id.big_text,
5735 TextUtils.isEmpty(bigTextText) ? View.GONE : View.VISIBLE);
Selim Cinek279fa862016-06-14 10:57:25 -07005736 contentView.setBoolean(R.id.big_text, "setHasImage", builder.mN.hasLargeIcon());
Adrian Roosb1f427c2016-05-26 12:27:15 -07005737 }
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005738 }
Daniel Sandler879c5e02012-04-17 16:46:51 -04005739
5740 /**
Alex Hillsfc737de2016-03-23 17:33:02 -04005741 * Helper class for generating large-format notifications that include multiple back-and-forth
5742 * messages of varying types between any number of people.
5743 *
5744 * <br>
5745 * If the platform does not provide large-format notifications, this method has no effect. The
5746 * user will always see the normal notification view.
5747 * <br>
5748 * This class is a "rebuilder": It attaches to a Builder object and modifies its behavior, like
5749 * so:
5750 * <pre class="prettyprint">
5751 *
5752 * Notification noti = new Notification.Builder()
5753 * .setContentTitle(&quot;2 new messages wtih &quot; + sender.toString())
5754 * .setContentText(subject)
5755 * .setSmallIcon(R.drawable.new_message)
5756 * .setLargeIcon(aBitmap)
5757 * .setStyle(new Notification.MessagingStyle(resources.getString(R.string.reply_name))
5758 * .addMessage(messages[0].getText(), messages[0].getTime(), messages[0].getSender())
5759 * .addMessage(messages[1].getText(), messages[1].getTime(), messages[1].getSender()))
5760 * .build();
5761 * </pre>
5762 */
5763 public static class MessagingStyle extends Style {
5764
5765 /**
5766 * The maximum number of messages that will be retained in the Notification itself (the
5767 * number displayed is up to the platform).
5768 */
5769 public static final int MAXIMUM_RETAINED_MESSAGES = 25;
5770
5771 CharSequence mUserDisplayName;
5772 CharSequence mConversationTitle;
Alex Hillsd9b04d92016-04-11 16:38:16 -04005773 List<Message> mMessages = new ArrayList<>();
Adrian Roos437cd562017-01-18 15:47:03 -08005774 List<Message> mHistoricMessages = new ArrayList<>();
Alex Hillsfc737de2016-03-23 17:33:02 -04005775
5776 MessagingStyle() {
5777 }
5778
5779 /**
Alex Hillsfd590442016-10-07 09:52:44 -04005780 * @param userDisplayName Required - the name to be displayed for any replies sent by the
5781 * user before the posting app reposts the notification with those messages after they've
5782 * been actually sent and in previous messages sent by the user added in
Alex Hillsfc737de2016-03-23 17:33:02 -04005783 * {@link #addMessage(Notification.MessagingStyle.Message)}
5784 */
Alex Hillsfd590442016-10-07 09:52:44 -04005785 public MessagingStyle(@NonNull CharSequence userDisplayName) {
Alex Hillsfc737de2016-03-23 17:33:02 -04005786 mUserDisplayName = userDisplayName;
5787 }
5788
5789 /**
5790 * Returns the name to be displayed for any replies sent by the user
5791 */
5792 public CharSequence getUserDisplayName() {
5793 return mUserDisplayName;
5794 }
5795
5796 /**
Alex Hillsfc737de2016-03-23 17:33:02 -04005797 * Sets the title to be displayed on this conversation. This should only be used for
5798 * group messaging and left unset for one-on-one conversations.
5799 * @param conversationTitle
5800 * @return this object for method chaining.
5801 */
5802 public MessagingStyle setConversationTitle(CharSequence conversationTitle) {
5803 mConversationTitle = conversationTitle;
5804 return this;
5805 }
5806
5807 /**
5808 * Return the title to be displayed on this conversation. Can be <code>null</code> and
5809 * should be for one-on-one conversations
5810 */
5811 public CharSequence getConversationTitle() {
5812 return mConversationTitle;
5813 }
5814
5815 /**
5816 * Adds a message for display by this notification. Convenience call for a simple
5817 * {@link Message} in {@link #addMessage(Notification.MessagingStyle.Message)}.
5818 * @param text A {@link CharSequence} to be displayed as the message content
5819 * @param timestamp Time at which the message arrived
5820 * @param sender A {@link CharSequence} to be used for displaying the name of the
5821 * sender. Should be <code>null</code> for messages by the current user, in which case
5822 * the platform will insert {@link #getUserDisplayName()}.
5823 * Should be unique amongst all individuals in the conversation, and should be
5824 * consistent during re-posts of the notification.
5825 *
5826 * @see Message#Message(CharSequence, long, CharSequence)
5827 *
5828 * @return this object for method chaining
5829 */
5830 public MessagingStyle addMessage(CharSequence text, long timestamp, CharSequence sender) {
Adrian Roos437cd562017-01-18 15:47:03 -08005831 return addMessage(new Message(text, timestamp, sender));
Alex Hillsfc737de2016-03-23 17:33:02 -04005832 }
5833
5834 /**
5835 * Adds a {@link Message} for display in this notification.
Adrian Roos437cd562017-01-18 15:47:03 -08005836 *
5837 * <p>The messages should be added in chronologic order, i.e. the oldest first,
5838 * the newest last.
5839 *
Alex Hillsfc737de2016-03-23 17:33:02 -04005840 * @param message The {@link Message} to be displayed
5841 * @return this object for method chaining
5842 */
5843 public MessagingStyle addMessage(Message message) {
5844 mMessages.add(message);
5845 if (mMessages.size() > MAXIMUM_RETAINED_MESSAGES) {
5846 mMessages.remove(0);
5847 }
5848 return this;
5849 }
5850
5851 /**
Adrian Roos437cd562017-01-18 15:47:03 -08005852 * Adds a {@link Message} for historic context in this notification.
5853 *
5854 * <p>Messages should be added as historic if they are not the main subject of the
5855 * notification but may give context to a conversation. The system may choose to present
5856 * them only when relevant, e.g. when replying to a message through a {@link RemoteInput}.
5857 *
5858 * <p>The messages should be added in chronologic order, i.e. the oldest first,
5859 * the newest last.
5860 *
5861 * @param message The historic {@link Message} to be added
5862 * @return this object for method chaining
5863 */
5864 public MessagingStyle addHistoricMessage(Message message) {
5865 mHistoricMessages.add(message);
5866 if (mHistoricMessages.size() > MAXIMUM_RETAINED_MESSAGES) {
5867 mHistoricMessages.remove(0);
5868 }
5869 return this;
5870 }
5871
5872 /**
Alex Hillsfc737de2016-03-23 17:33:02 -04005873 * Gets the list of {@code Message} objects that represent the notification
5874 */
5875 public List<Message> getMessages() {
5876 return mMessages;
5877 }
5878
5879 /**
Adrian Roos437cd562017-01-18 15:47:03 -08005880 * Gets the list of historic {@code Message}s in the notification.
5881 */
5882 public List<Message> getHistoricMessages() {
5883 return mHistoricMessages;
5884 }
5885
5886 /**
Alex Hillsfc737de2016-03-23 17:33:02 -04005887 * @hide
5888 */
5889 @Override
5890 public void addExtras(Bundle extras) {
5891 super.addExtras(extras);
5892 if (mUserDisplayName != null) {
5893 extras.putCharSequence(EXTRA_SELF_DISPLAY_NAME, mUserDisplayName);
5894 }
5895 if (mConversationTitle != null) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04005896 extras.putCharSequence(EXTRA_CONVERSATION_TITLE, mConversationTitle);
Alex Hillsfc737de2016-03-23 17:33:02 -04005897 }
Alex Hillsd9b04d92016-04-11 16:38:16 -04005898 if (!mMessages.isEmpty()) { extras.putParcelableArray(EXTRA_MESSAGES,
5899 Message.getBundleArrayForMessages(mMessages));
Alex Hillsfc737de2016-03-23 17:33:02 -04005900 }
Adrian Roos437cd562017-01-18 15:47:03 -08005901 if (!mHistoricMessages.isEmpty()) { extras.putParcelableArray(EXTRA_HISTORIC_MESSAGES,
5902 Message.getBundleArrayForMessages(mHistoricMessages));
5903 }
Adrian Roos33fbd2c2016-05-27 15:35:28 -07005904
5905 fixTitleAndTextExtras(extras);
5906 }
5907
5908 private void fixTitleAndTextExtras(Bundle extras) {
5909 Message m = findLatestIncomingMessage();
5910 CharSequence text = (m == null) ? null : m.mText;
5911 CharSequence sender = m == null ? null
5912 : TextUtils.isEmpty(m.mSender) ? mUserDisplayName : m.mSender;
5913 CharSequence title;
5914 if (!TextUtils.isEmpty(mConversationTitle)) {
5915 if (!TextUtils.isEmpty(sender)) {
5916 BidiFormatter bidi = BidiFormatter.getInstance();
5917 title = mBuilder.mContext.getString(
5918 com.android.internal.R.string.notification_messaging_title_template,
5919 bidi.unicodeWrap(mConversationTitle), bidi.unicodeWrap(m.mSender));
5920 } else {
5921 title = mConversationTitle;
5922 }
5923 } else {
5924 title = sender;
5925 }
5926
5927 if (title != null) {
5928 extras.putCharSequence(EXTRA_TITLE, title);
5929 }
5930 if (text != null) {
5931 extras.putCharSequence(EXTRA_TEXT, text);
5932 }
Alex Hillsfc737de2016-03-23 17:33:02 -04005933 }
5934
5935 /**
5936 * @hide
5937 */
5938 @Override
5939 protected void restoreFromExtras(Bundle extras) {
5940 super.restoreFromExtras(extras);
5941
5942 mMessages.clear();
Adrian Roos437cd562017-01-18 15:47:03 -08005943 mHistoricMessages.clear();
Adrian Roos96b7e202016-05-17 13:50:38 -07005944 mUserDisplayName = extras.getCharSequence(EXTRA_SELF_DISPLAY_NAME);
5945 mConversationTitle = extras.getCharSequence(EXTRA_CONVERSATION_TITLE);
Adrian Roos437cd562017-01-18 15:47:03 -08005946 Parcelable[] messages = extras.getParcelableArray(EXTRA_MESSAGES);
5947 if (messages != null && messages instanceof Parcelable[]) {
5948 mMessages = Message.getMessagesFromBundleArray(messages);
5949 }
5950 Parcelable[] histMessages = extras.getParcelableArray(EXTRA_HISTORIC_MESSAGES);
5951 if (histMessages != null && histMessages instanceof Parcelable[]) {
5952 mHistoricMessages = Message.getMessagesFromBundleArray(histMessages);
Alex Hillsfc737de2016-03-23 17:33:02 -04005953 }
5954 }
5955
5956 /**
5957 * @hide
5958 */
Adrian Roosc1a80b02016-04-05 14:54:55 -07005959 @Override
Selim Cinek7d1009b2017-01-25 15:28:28 -08005960 public RemoteViews makeContentView(boolean increasedHeight) {
5961 if (!increasedHeight) {
5962 Message m = findLatestIncomingMessage();
5963 CharSequence title = mConversationTitle != null
5964 ? mConversationTitle
5965 : (m == null) ? null : m.mSender;
5966 CharSequence text = (m == null)
5967 ? null
5968 : mConversationTitle != null ? makeMessageLine(m, mBuilder) : m.mText;
Alex Hillsfc737de2016-03-23 17:33:02 -04005969
Selim Cinek7d1009b2017-01-25 15:28:28 -08005970 return mBuilder.applyStandardTemplate(mBuilder.getBaseLayoutResource(),
5971 mBuilder.mParams.reset().hasProgress(false).title(title).text(text));
5972 } else {
Selim Cinek5d6ef8d2017-05-18 22:16:00 -07005973 mBuilder.mOriginalActions = mBuilder.mActions;
Selim Cinek7d1009b2017-01-25 15:28:28 -08005974 mBuilder.mActions = new ArrayList<>();
5975 RemoteViews remoteViews = makeBigContentView();
Selim Cinek5d6ef8d2017-05-18 22:16:00 -07005976 mBuilder.mActions = mBuilder.mOriginalActions;
5977 mBuilder.mOriginalActions = null;
Selim Cinek7d1009b2017-01-25 15:28:28 -08005978 return remoteViews;
5979 }
Adrian Roosc1a80b02016-04-05 14:54:55 -07005980 }
5981
5982 private Message findLatestIncomingMessage() {
5983 for (int i = mMessages.size() - 1; i >= 0; i--) {
5984 Message m = mMessages.get(i);
5985 // Incoming messages have a non-empty sender.
5986 if (!TextUtils.isEmpty(m.mSender)) {
5987 return m;
5988 }
5989 }
Adrian Roos33fbd2c2016-05-27 15:35:28 -07005990 if (!mMessages.isEmpty()) {
5991 // No incoming messages, fall back to outgoing message
5992 return mMessages.get(mMessages.size() - 1);
5993 }
Adrian Roosc1a80b02016-04-05 14:54:55 -07005994 return null;
5995 }
5996
5997 /**
5998 * @hide
5999 */
6000 @Override
6001 public RemoteViews makeBigContentView() {
6002 CharSequence title = !TextUtils.isEmpty(super.mBigContentTitle)
6003 ? super.mBigContentTitle
6004 : mConversationTitle;
6005 boolean hasTitle = !TextUtils.isEmpty(title);
6006
Adrian Roosfeafa052016-06-01 17:09:45 -07006007 if (mMessages.size() == 1) {
6008 // Special case for a single message: Use the big text style
6009 // so the collapsed and expanded versions match nicely.
6010 CharSequence bigTitle;
6011 CharSequence text;
6012 if (hasTitle) {
6013 bigTitle = title;
Selim Cinek7b9605b2017-01-19 17:36:00 -08006014 text = makeMessageLine(mMessages.get(0), mBuilder);
Adrian Roosfeafa052016-06-01 17:09:45 -07006015 } else {
6016 bigTitle = mMessages.get(0).mSender;
6017 text = mMessages.get(0).mText;
6018 }
Adrian Roosb1f427c2016-05-26 12:27:15 -07006019 RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(
6020 mBuilder.getBigTextLayoutResource(),
Adrian Roos70d7aa32017-01-11 15:39:06 -08006021 mBuilder.mParams.reset().hasProgress(false).title(bigTitle).text(null));
Adrian Roosb1f427c2016-05-26 12:27:15 -07006022 BigTextStyle.applyBigTextContentView(mBuilder, contentView, text);
6023 return contentView;
6024 }
6025
Adrian Roos48d746a2016-04-12 14:57:28 -07006026 RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(
Adrian Roosc1a80b02016-04-05 14:54:55 -07006027 mBuilder.getMessagingLayoutResource(),
Adrian Roos70d7aa32017-01-11 15:39:06 -08006028 mBuilder.mParams.reset().hasProgress(false).title(title).text(null));
Adrian Roosc1a80b02016-04-05 14:54:55 -07006029
6030 int[] rowIds = {R.id.inbox_text0, R.id.inbox_text1, R.id.inbox_text2, R.id.inbox_text3,
6031 R.id.inbox_text4, R.id.inbox_text5, R.id.inbox_text6};
6032
6033 // Make sure all rows are gone in case we reuse a view.
6034 for (int rowId : rowIds) {
6035 contentView.setViewVisibility(rowId, View.GONE);
6036 }
6037
6038 int i=0;
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006039 contentView.setViewLayoutMarginBottomDimen(R.id.line1,
6040 hasTitle ? R.dimen.notification_messaging_spacing : 0);
Adrian Roosc1a80b02016-04-05 14:54:55 -07006041 contentView.setInt(R.id.notification_messaging, "setNumIndentLines",
Selim Cinek279fa862016-06-14 10:57:25 -07006042 !mBuilder.mN.hasLargeIcon() ? 0 : (hasTitle ? 1 : 2));
Adrian Roosc1a80b02016-04-05 14:54:55 -07006043
Adrian Roosfeafa052016-06-01 17:09:45 -07006044 int contractedChildId = View.NO_ID;
6045 Message contractedMessage = findLatestIncomingMessage();
Adrian Roos437cd562017-01-18 15:47:03 -08006046 int firstHistoricMessage = Math.max(0, mHistoricMessages.size()
6047 - (rowIds.length - mMessages.size()));
6048 while (firstHistoricMessage + i < mHistoricMessages.size() && i < rowIds.length) {
6049 Message m = mHistoricMessages.get(firstHistoricMessage + i);
6050 int rowId = rowIds[i];
6051
Selim Cinek7b9605b2017-01-19 17:36:00 -08006052 contentView.setTextViewText(rowId, makeMessageLine(m, mBuilder));
Adrian Roos437cd562017-01-18 15:47:03 -08006053
6054 if (contractedMessage == m) {
6055 contractedChildId = rowId;
6056 }
6057
6058 i++;
6059 }
6060
Adrian Roosc1a80b02016-04-05 14:54:55 -07006061 int firstMessage = Math.max(0, mMessages.size() - rowIds.length);
6062 while (firstMessage + i < mMessages.size() && i < rowIds.length) {
6063 Message m = mMessages.get(firstMessage + i);
6064 int rowId = rowIds[i];
6065
6066 contentView.setViewVisibility(rowId, View.VISIBLE);
Selim Cinek7b9605b2017-01-19 17:36:00 -08006067 contentView.setTextViewText(rowId, makeMessageLine(m, mBuilder));
6068 mBuilder.setTextViewColorSecondary(contentView, rowId);
Adrian Roosc1a80b02016-04-05 14:54:55 -07006069
Adrian Roosfeafa052016-06-01 17:09:45 -07006070 if (contractedMessage == m) {
6071 contractedChildId = rowId;
6072 }
6073
Adrian Roosc1a80b02016-04-05 14:54:55 -07006074 i++;
6075 }
Adrian Roos437cd562017-01-18 15:47:03 -08006076 // Clear the remaining views for reapply. Ensures that historic message views can
6077 // reliably be identified as being GONE and having non-null text.
6078 while (i < rowIds.length) {
6079 int rowId = rowIds[i];
6080 contentView.setTextViewText(rowId, null);
6081 i++;
6082 }
6083
Adrian Roosfeafa052016-06-01 17:09:45 -07006084 // Record this here to allow transformation between the contracted and expanded views.
6085 contentView.setInt(R.id.notification_messaging, "setContractedChildId",
6086 contractedChildId);
Alex Hillsfc737de2016-03-23 17:33:02 -04006087 return contentView;
6088 }
6089
Selim Cinek7b9605b2017-01-19 17:36:00 -08006090 private CharSequence makeMessageLine(Message m, Builder builder) {
Adrian Roosc1a80b02016-04-05 14:54:55 -07006091 BidiFormatter bidi = BidiFormatter.getInstance();
6092 SpannableStringBuilder sb = new SpannableStringBuilder();
Selim Cinek7b9605b2017-01-19 17:36:00 -08006093 boolean colorize = builder.isColorized();
Anthony Chenad4d1582017-04-10 16:07:58 -07006094 TextAppearanceSpan colorSpan;
6095 CharSequence messageName;
Adrian Roosc1a80b02016-04-05 14:54:55 -07006096 if (TextUtils.isEmpty(m.mSender)) {
6097 CharSequence replyName = mUserDisplayName == null ? "" : mUserDisplayName;
6098 sb.append(bidi.unicodeWrap(replyName),
Selim Cinek7b9605b2017-01-19 17:36:00 -08006099 makeFontColorSpan(colorize
6100 ? builder.getPrimaryTextColor()
6101 : mBuilder.resolveContrastColor()),
Adrian Roosc1a80b02016-04-05 14:54:55 -07006102 0 /* flags */);
6103 } else {
6104 sb.append(bidi.unicodeWrap(m.mSender),
Selim Cinek7b9605b2017-01-19 17:36:00 -08006105 makeFontColorSpan(colorize
6106 ? builder.getPrimaryTextColor()
6107 : Color.BLACK),
Adrian Roosc1a80b02016-04-05 14:54:55 -07006108 0 /* flags */);
6109 }
6110 CharSequence text = m.mText == null ? "" : m.mText;
6111 sb.append(" ").append(bidi.unicodeWrap(text));
6112 return sb;
6113 }
6114
Adrian Roosdedd1df2016-04-26 16:38:47 -07006115 /**
6116 * @hide
6117 */
6118 @Override
Selim Cinek87ed69b2017-02-09 15:59:43 -08006119 public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
6120 if (increasedHeight) {
6121 return makeBigContentView();
6122 }
Adrian Roosdedd1df2016-04-26 16:38:47 -07006123 Message m = findLatestIncomingMessage();
6124 CharSequence title = mConversationTitle != null
6125 ? mConversationTitle
6126 : (m == null) ? null : m.mSender;
6127 CharSequence text = (m == null)
6128 ? null
Selim Cinek7b9605b2017-01-19 17:36:00 -08006129 : mConversationTitle != null ? makeMessageLine(m, mBuilder) : m.mText;
Adrian Roosdedd1df2016-04-26 16:38:47 -07006130
6131 return mBuilder.applyStandardTemplateWithActions(mBuilder.getBigBaseLayoutResource(),
Adrian Roos70d7aa32017-01-11 15:39:06 -08006132 mBuilder.mParams.reset().hasProgress(false).title(title).text(text));
Adrian Roosdedd1df2016-04-26 16:38:47 -07006133 }
6134
Adrian Roosc1a80b02016-04-05 14:54:55 -07006135 private static TextAppearanceSpan makeFontColorSpan(int color) {
6136 return new TextAppearanceSpan(null, 0, 0,
6137 ColorStateList.valueOf(color), null);
6138 }
6139
Alex Hillsd9b04d92016-04-11 16:38:16 -04006140 public static final class Message {
6141
6142 static final String KEY_TEXT = "text";
6143 static final String KEY_TIMESTAMP = "time";
6144 static final String KEY_SENDER = "sender";
6145 static final String KEY_DATA_MIME_TYPE = "type";
6146 static final String KEY_DATA_URI= "uri";
Shane Brennan5a871862017-03-11 13:14:17 -08006147 static final String KEY_EXTRAS_BUNDLE = "extras";
Alex Hillsfc737de2016-03-23 17:33:02 -04006148
6149 private final CharSequence mText;
6150 private final long mTimestamp;
6151 private final CharSequence mSender;
6152
Shane Brennan5a871862017-03-11 13:14:17 -08006153 private Bundle mExtras = new Bundle();
Alex Hillsfc737de2016-03-23 17:33:02 -04006154 private String mDataMimeType;
6155 private Uri mDataUri;
6156
6157 /**
6158 * Constructor
6159 * @param text A {@link CharSequence} to be displayed as the message content
6160 * @param timestamp Time at which the message arrived
6161 * @param sender A {@link CharSequence} to be used for displaying the name of the
6162 * sender. Should be <code>null</code> for messages by the current user, in which case
6163 * the platform will insert {@link MessagingStyle#getUserDisplayName()}.
6164 * Should be unique amongst all individuals in the conversation, and should be
6165 * consistent during re-posts of the notification.
6166 */
6167 public Message(CharSequence text, long timestamp, CharSequence sender){
6168 mText = text;
6169 mTimestamp = timestamp;
6170 mSender = sender;
6171 }
6172
6173 /**
6174 * Sets a binary blob of data and an associated MIME type for a message. In the case
6175 * where the platform doesn't support the MIME type, the original text provided in the
6176 * constructor will be used.
6177 * @param dataMimeType The MIME type of the content. See
6178 * <a href="{@docRoot}notifications/messaging.html"> for the list of supported MIME
6179 * types on Android and Android Wear.
6180 * @param dataUri The uri containing the content whose type is given by the MIME type.
6181 * <p class="note">
6182 * <ol>
6183 * <li>Notification Listeners including the System UI need permission to access the
6184 * data the Uri points to. The recommended ways to do this are:</li>
6185 * <li>Store the data in your own ContentProvider, making sure that other apps have
6186 * the correct permission to access your provider. The preferred mechanism for
6187 * providing access is to use per-URI permissions which are temporary and only
6188 * grant access to the receiving application. An easy way to create a
6189 * ContentProvider like this is to use the FileProvider helper class.</li>
6190 * <li>Use the system MediaStore. The MediaStore is primarily aimed at video, audio
6191 * and image MIME types, however beginning with Android 3.0 (API level 11) it can
6192 * also store non-media types (see MediaStore.Files for more info). Files can be
6193 * inserted into the MediaStore using scanFile() after which a content:// style
6194 * Uri suitable for sharing is passed to the provided onScanCompleted() callback.
6195 * Note that once added to the system MediaStore the content is accessible to any
6196 * app on the device.</li>
6197 * </ol>
6198 * @return this object for method chaining
6199 */
6200 public Message setData(String dataMimeType, Uri dataUri) {
6201 mDataMimeType = dataMimeType;
6202 mDataUri = dataUri;
6203 return this;
6204 }
6205
Alex Hillsfc737de2016-03-23 17:33:02 -04006206 /**
6207 * Get the text to be used for this message, or the fallback text if a type and content
6208 * Uri have been set
6209 */
6210 public CharSequence getText() {
6211 return mText;
6212 }
6213
6214 /**
6215 * Get the time at which this message arrived
6216 */
6217 public long getTimestamp() {
6218 return mTimestamp;
6219 }
6220
6221 /**
Shane Brennan5a871862017-03-11 13:14:17 -08006222 * Get the extras Bundle for this message.
6223 */
6224 public Bundle getExtras() {
6225 return mExtras;
6226 }
6227
6228 /**
Alex Hillsfc737de2016-03-23 17:33:02 -04006229 * Get the text used to display the contact's name in the messaging experience
6230 */
6231 public CharSequence getSender() {
6232 return mSender;
6233 }
6234
6235 /**
6236 * Get the MIME type of the data pointed to by the Uri
6237 */
6238 public String getDataMimeType() {
6239 return mDataMimeType;
6240 }
6241
6242 /**
6243 * Get the the Uri pointing to the content of the message. Can be null, in which case
6244 * {@see #getText()} is used.
6245 */
6246 public Uri getDataUri() {
6247 return mDataUri;
6248 }
6249
Alex Hillsd9b04d92016-04-11 16:38:16 -04006250 private Bundle toBundle() {
6251 Bundle bundle = new Bundle();
Alex Hillsfc737de2016-03-23 17:33:02 -04006252 if (mText != null) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04006253 bundle.putCharSequence(KEY_TEXT, mText);
Alex Hillsfc737de2016-03-23 17:33:02 -04006254 }
Alex Hillsd9b04d92016-04-11 16:38:16 -04006255 bundle.putLong(KEY_TIMESTAMP, mTimestamp);
Alex Hillsfc737de2016-03-23 17:33:02 -04006256 if (mSender != null) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04006257 bundle.putCharSequence(KEY_SENDER, mSender);
Alex Hillsfc737de2016-03-23 17:33:02 -04006258 }
6259 if (mDataMimeType != null) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04006260 bundle.putString(KEY_DATA_MIME_TYPE, mDataMimeType);
Alex Hillsfc737de2016-03-23 17:33:02 -04006261 }
6262 if (mDataUri != null) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04006263 bundle.putParcelable(KEY_DATA_URI, mDataUri);
Alex Hillsfc737de2016-03-23 17:33:02 -04006264 }
Shane Brennan5a871862017-03-11 13:14:17 -08006265 if (mExtras != null) {
6266 bundle.putBundle(KEY_EXTRAS_BUNDLE, mExtras);
6267 }
Alex Hillsd9b04d92016-04-11 16:38:16 -04006268 return bundle;
Alex Hillsfc737de2016-03-23 17:33:02 -04006269 }
6270
Alex Hillsd9b04d92016-04-11 16:38:16 -04006271 static Bundle[] getBundleArrayForMessages(List<Message> messages) {
6272 Bundle[] bundles = new Bundle[messages.size()];
6273 final int N = messages.size();
6274 for (int i = 0; i < N; i++) {
6275 bundles[i] = messages.get(i).toBundle();
6276 }
6277 return bundles;
6278 }
6279
Adrian Roosdedd1df2016-04-26 16:38:47 -07006280 static List<Message> getMessagesFromBundleArray(Parcelable[] bundles) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04006281 List<Message> messages = new ArrayList<>(bundles.length);
6282 for (int i = 0; i < bundles.length; i++) {
Adrian Roosdedd1df2016-04-26 16:38:47 -07006283 if (bundles[i] instanceof Bundle) {
6284 Message message = getMessageFromBundle((Bundle)bundles[i]);
6285 if (message != null) {
6286 messages.add(message);
6287 }
Alex Hillsd9b04d92016-04-11 16:38:16 -04006288 }
6289 }
6290 return messages;
6291 }
6292
6293 static Message getMessageFromBundle(Bundle bundle) {
6294 try {
Adrian Roosfbddd2c2016-05-13 12:57:20 -07006295 if (!bundle.containsKey(KEY_TEXT) || !bundle.containsKey(KEY_TIMESTAMP)) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04006296 return null;
6297 } else {
6298 Message message = new Message(bundle.getCharSequence(KEY_TEXT),
6299 bundle.getLong(KEY_TIMESTAMP), bundle.getCharSequence(KEY_SENDER));
6300 if (bundle.containsKey(KEY_DATA_MIME_TYPE) &&
6301 bundle.containsKey(KEY_DATA_URI)) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04006302 message.setData(bundle.getString(KEY_DATA_MIME_TYPE),
6303 (Uri) bundle.getParcelable(KEY_DATA_URI));
Alex Hillsfc737de2016-03-23 17:33:02 -04006304 }
Shane Brennan5a871862017-03-11 13:14:17 -08006305 if (bundle.containsKey(KEY_EXTRAS_BUNDLE)) {
6306 message.getExtras().putAll(bundle.getBundle(KEY_EXTRAS_BUNDLE));
6307 }
Alex Hillsd9b04d92016-04-11 16:38:16 -04006308 return message;
6309 }
6310 } catch (ClassCastException e) {
6311 return null;
6312 }
6313 }
Alex Hillsfc737de2016-03-23 17:33:02 -04006314 }
6315 }
6316
6317 /**
Daniel Sandler879c5e02012-04-17 16:46:51 -04006318 * Helper class for generating large-format notifications that include a list of (up to 5) strings.
Joe Malin8d40d042012-11-05 11:36:40 -08006319 *
Robert Ly91c5ce32014-06-08 15:37:00 -07006320 * Here's how you'd set the <code>InboxStyle</code> on a notification:
Daniel Sandler879c5e02012-04-17 16:46:51 -04006321 * <pre class="prettyprint">
Robert Ly91c5ce32014-06-08 15:37:00 -07006322 * Notification notif = new Notification.Builder(mContext)
6323 * .setContentTitle(&quot;5 New mails from &quot; + sender.toString())
6324 * .setContentText(subject)
6325 * .setSmallIcon(R.drawable.new_mail)
6326 * .setLargeIcon(aBitmap)
6327 * .setStyle(new Notification.InboxStyle()
6328 * .addLine(str1)
6329 * .addLine(str2)
6330 * .setContentTitle(&quot;&quot;)
6331 * .setSummaryText(&quot;+3 more&quot;))
6332 * .build();
Daniel Sandler879c5e02012-04-17 16:46:51 -04006333 * </pre>
Joe Malin8d40d042012-11-05 11:36:40 -08006334 *
Daniel Sandler879c5e02012-04-17 16:46:51 -04006335 * @see Notification#bigContentView
6336 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04006337 public static class InboxStyle extends Style {
Daniel Sandler879c5e02012-04-17 16:46:51 -04006338 private ArrayList<CharSequence> mTexts = new ArrayList<CharSequence>(5);
6339
Chris Wrenfbd96ba2012-05-01 12:03:58 -04006340 public InboxStyle() {
6341 }
6342
Adrian Roosf5faf9d2016-05-23 13:56:15 -07006343 /**
6344 * @deprecated use {@code InboxStyle()}.
6345 */
6346 @Deprecated
Daniel Sandler879c5e02012-04-17 16:46:51 -04006347 public InboxStyle(Builder builder) {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04006348 setBuilder(builder);
Daniel Sandler879c5e02012-04-17 16:46:51 -04006349 }
6350
Chris Wrend6297db2012-05-03 16:20:13 -04006351 /**
6352 * Overrides ContentTitle in the big form of the template.
6353 * This defaults to the value passed to setContentTitle().
6354 */
6355 public InboxStyle setBigContentTitle(CharSequence title) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04006356 internalSetBigContentTitle(safeCharSequence(title));
Chris Wrend6297db2012-05-03 16:20:13 -04006357 return this;
6358 }
6359
6360 /**
6361 * Set the first line of text after the detail section in the big form of the template.
6362 */
6363 public InboxStyle setSummaryText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04006364 internalSetSummaryText(safeCharSequence(cs));
Chris Wrend6297db2012-05-03 16:20:13 -04006365 return this;
6366 }
6367
Chris Wren0bd664d2012-08-01 13:56:56 -04006368 /**
6369 * Append a line to the digest section of the Inbox notification.
6370 */
Daniel Sandler879c5e02012-04-17 16:46:51 -04006371 public InboxStyle addLine(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04006372 mTexts.add(safeCharSequence(cs));
Daniel Sandler879c5e02012-04-17 16:46:51 -04006373 return this;
6374 }
6375
Daniel Sandlerf45564e2013-04-15 15:05:08 -04006376 /**
6377 * @hide
6378 */
6379 public void addExtras(Bundle extras) {
6380 super.addExtras(extras);
Christoph Studer4600f9b2014-07-22 22:44:43 +02006381
Daniel Sandlerf45564e2013-04-15 15:05:08 -04006382 CharSequence[] a = new CharSequence[mTexts.size()];
6383 extras.putCharSequenceArray(EXTRA_TEXT_LINES, mTexts.toArray(a));
6384 }
6385
Christoph Studer4600f9b2014-07-22 22:44:43 +02006386 /**
6387 * @hide
6388 */
6389 @Override
6390 protected void restoreFromExtras(Bundle extras) {
6391 super.restoreFromExtras(extras);
6392
6393 mTexts.clear();
6394 if (extras.containsKey(EXTRA_TEXT_LINES)) {
6395 Collections.addAll(mTexts, extras.getCharSequenceArray(EXTRA_TEXT_LINES));
6396 }
6397 }
6398
Julia Reynoldsd9228f12015-10-20 10:37:27 -04006399 /**
6400 * @hide
6401 */
6402 public RemoteViews makeBigContentView() {
Selim Cinekc848c3a2016-01-13 15:27:30 -08006403 // Remove the content text so it disappears unless you have a summary
Christoph Studer4600f9b2014-07-22 22:44:43 +02006404 // Nasty
Julia Reynoldsd9228f12015-10-20 10:37:27 -04006405 CharSequence oldBuilderContentText = mBuilder.mN.extras.getCharSequence(EXTRA_TEXT);
6406 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, null);
Christoph Studer4600f9b2014-07-22 22:44:43 +02006407
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01006408 RemoteViews contentView = getStandardView(mBuilder.getInboxLayoutResource());
Daniel Sandler619738c2012-06-07 16:33:08 -04006409
Julia Reynoldsd9228f12015-10-20 10:37:27 -04006410 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, oldBuilderContentText);
Christoph Studer4600f9b2014-07-22 22:44:43 +02006411
Chris Wrend6297db2012-05-03 16:20:13 -04006412 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 -04006413 R.id.inbox_text4, R.id.inbox_text5, R.id.inbox_text6};
Chris Wrend6297db2012-05-03 16:20:13 -04006414
Chris Wren4ed80d52012-05-17 09:30:03 -04006415 // Make sure all rows are gone in case we reuse a view.
6416 for (int rowId : rowIds) {
6417 contentView.setViewVisibility(rowId, View.GONE);
6418 }
6419
Daniel Sandler879c5e02012-04-17 16:46:51 -04006420 int i=0;
Selim Cinek07c80172016-04-21 16:40:47 -07006421 int topPadding = mBuilder.mContext.getResources().getDimensionPixelSize(
6422 R.dimen.notification_inbox_item_top_padding);
Selim Cinek247fa012016-02-18 09:50:48 -08006423 boolean first = true;
Selim Cinek07c80172016-04-21 16:40:47 -07006424 int onlyViewId = 0;
6425 int maxRows = rowIds.length;
6426 if (mBuilder.mActions.size() > 0) {
6427 maxRows--;
6428 }
6429 while (i < mTexts.size() && i < maxRows) {
Daniel Sandler879c5e02012-04-17 16:46:51 -04006430 CharSequence str = mTexts.get(i);
Selim Cinek07c80172016-04-21 16:40:47 -07006431 if (!TextUtils.isEmpty(str)) {
Daniel Sandler879c5e02012-04-17 16:46:51 -04006432 contentView.setViewVisibility(rowIds[i], View.VISIBLE);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01006433 contentView.setTextViewText(rowIds[i], mBuilder.processLegacyText(str));
Selim Cinek7b9605b2017-01-19 17:36:00 -08006434 mBuilder.setTextViewColorSecondary(contentView, rowIds[i]);
Selim Cinek07c80172016-04-21 16:40:47 -07006435 contentView.setViewPadding(rowIds[i], 0, topPadding, 0, 0);
Selim Cinek247fa012016-02-18 09:50:48 -08006436 handleInboxImageMargin(contentView, rowIds[i], first);
Selim Cinek07c80172016-04-21 16:40:47 -07006437 if (first) {
6438 onlyViewId = rowIds[i];
6439 } else {
6440 onlyViewId = 0;
6441 }
Selim Cinek247fa012016-02-18 09:50:48 -08006442 first = false;
Daniel Sandler879c5e02012-04-17 16:46:51 -04006443 }
6444 i++;
6445 }
Selim Cinek07c80172016-04-21 16:40:47 -07006446 if (onlyViewId != 0) {
6447 // We only have 1 entry, lets make it look like the normal Text of a Bigtext
6448 topPadding = mBuilder.mContext.getResources().getDimensionPixelSize(
6449 R.dimen.notification_text_margin_top);
6450 contentView.setViewPadding(onlyViewId, 0, topPadding, 0, 0);
6451 }
Selim Cinek1e0bf612015-11-20 15:57:26 -08006452
Daniel Sandler879c5e02012-04-17 16:46:51 -04006453 return contentView;
6454 }
Selim Cinek1e0bf612015-11-20 15:57:26 -08006455
Selim Cinek247fa012016-02-18 09:50:48 -08006456 private void handleInboxImageMargin(RemoteViews contentView, int id, boolean first) {
Selim Cinek1e0bf612015-11-20 15:57:26 -08006457 int endMargin = 0;
Selim Cinek247fa012016-02-18 09:50:48 -08006458 if (first) {
6459 final int max = mBuilder.mN.extras.getInt(EXTRA_PROGRESS_MAX, 0);
6460 final boolean ind = mBuilder.mN.extras.getBoolean(EXTRA_PROGRESS_INDETERMINATE);
6461 boolean hasProgress = max != 0 || ind;
Selim Cinek279fa862016-06-14 10:57:25 -07006462 if (mBuilder.mN.hasLargeIcon() && !hasProgress) {
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006463 endMargin = R.dimen.notification_content_picture_margin;
Selim Cinek247fa012016-02-18 09:50:48 -08006464 }
Selim Cinek1e0bf612015-11-20 15:57:26 -08006465 }
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006466 contentView.setViewLayoutMarginEndDimen(id, endMargin);
Selim Cinek1e0bf612015-11-20 15:57:26 -08006467 }
Daniel Sandler879c5e02012-04-17 16:46:51 -04006468 }
Dan Sandler842dd772014-05-15 09:36:47 -04006469
6470 /**
6471 * Notification style for media playback notifications.
6472 *
6473 * In the expanded form, {@link Notification#bigContentView}, up to 5
6474 * {@link Notification.Action}s specified with
Dan Sandler86647982015-05-13 23:41:13 -04006475 * {@link Notification.Builder#addAction(Action) addAction} will be
Dan Sandler842dd772014-05-15 09:36:47 -04006476 * shown as icon-only pushbuttons, suitable for transport controls. The Bitmap given to
6477 * {@link Notification.Builder#setLargeIcon(android.graphics.Bitmap) setLargeIcon()} will be
6478 * treated as album artwork.
Selim Cinek99104832017-01-25 14:47:33 -08006479 * <p>
Dan Sandler842dd772014-05-15 09:36:47 -04006480 * Unlike the other styles provided here, MediaStyle can also modify the standard-size
6481 * {@link Notification#contentView}; by providing action indices to
Christoph Studerfde6f4d2014-12-12 13:23:26 +01006482 * {@link #setShowActionsInCompactView(int...)} you can promote up to 3 actions to be displayed
Dan Sandler842dd772014-05-15 09:36:47 -04006483 * in the standard view alongside the usual content.
Selim Cinek99104832017-01-25 14:47:33 -08006484 * <p>
Bryan Mawhinney6be8de32014-07-18 10:35:12 +01006485 * Notifications created with MediaStyle will have their category set to
6486 * {@link Notification#CATEGORY_TRANSPORT CATEGORY_TRANSPORT} unless you set a different
6487 * category using {@link Notification.Builder#setCategory(String) setCategory()}.
Selim Cinek99104832017-01-25 14:47:33 -08006488 * <p>
Jeff Browndba34ba2014-06-24 20:46:03 -07006489 * Finally, if you attach a {@link android.media.session.MediaSession.Token} using
6490 * {@link android.app.Notification.MediaStyle#setMediaSession(MediaSession.Token)},
Dan Sandler842dd772014-05-15 09:36:47 -04006491 * the System UI can identify this as a notification representing an active media session
6492 * and respond accordingly (by showing album artwork in the lockscreen, for example).
6493 *
Selim Cinek99104832017-01-25 14:47:33 -08006494 * <p>
6495 * Starting at {@link android.os.Build.VERSION_CODES#O Android O} any notification that has a
6496 * media session attached with {@link #setMediaSession(MediaSession.Token)} will be colorized.
6497 * You can opt-out of this behavior by using {@link Notification.Builder#setColorized(boolean)}.
6498 * <p>
6499 *
Dan Sandler842dd772014-05-15 09:36:47 -04006500 * To use this style with your Notification, feed it to
6501 * {@link Notification.Builder#setStyle(android.app.Notification.Style)} like so:
6502 * <pre class="prettyprint">
6503 * Notification noti = new Notification.Builder()
6504 * .setSmallIcon(R.drawable.ic_stat_player)
Christoph Studere935fe92014-11-24 14:18:06 +01006505 * .setContentTitle(&quot;Track title&quot;)
6506 * .setContentText(&quot;Artist - Album&quot;)
6507 * .setLargeIcon(albumArtBitmap))
Bryan Mawhinney6be8de32014-07-18 10:35:12 +01006508 * .setStyle(<b>new Notification.MediaStyle()</b>
6509 * .setMediaSession(mySession))
Dan Sandler842dd772014-05-15 09:36:47 -04006510 * .build();
6511 * </pre>
6512 *
6513 * @see Notification#bigContentView
Selim Cinek99104832017-01-25 14:47:33 -08006514 * @see Notification.Builder#setColorized(boolean)
Dan Sandler842dd772014-05-15 09:36:47 -04006515 */
6516 public static class MediaStyle extends Style {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006517 static final int MAX_MEDIA_BUTTONS_IN_COMPACT = 3;
Dan Sandler842dd772014-05-15 09:36:47 -04006518 static final int MAX_MEDIA_BUTTONS = 5;
6519
6520 private int[] mActionsToShowInCompact = null;
Jeff Browndba34ba2014-06-24 20:46:03 -07006521 private MediaSession.Token mToken;
Dan Sandler842dd772014-05-15 09:36:47 -04006522
6523 public MediaStyle() {
6524 }
6525
Adrian Roosf5faf9d2016-05-23 13:56:15 -07006526 /**
6527 * @deprecated use {@code MediaStyle()}.
6528 */
6529 @Deprecated
Dan Sandler842dd772014-05-15 09:36:47 -04006530 public MediaStyle(Builder builder) {
6531 setBuilder(builder);
6532 }
6533
6534 /**
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006535 * 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 -04006536 * notification view.
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006537 *
6538 * @param actions the indices of the actions to show in the compact notification view
Dan Sandler842dd772014-05-15 09:36:47 -04006539 */
6540 public MediaStyle setShowActionsInCompactView(int...actions) {
6541 mActionsToShowInCompact = actions;
6542 return this;
6543 }
6544
6545 /**
Jeff Browndba34ba2014-06-24 20:46:03 -07006546 * Attach a {@link android.media.session.MediaSession.Token} to this Notification
6547 * to provide additional playback information and control to the SystemUI.
Dan Sandler842dd772014-05-15 09:36:47 -04006548 */
Jeff Browndba34ba2014-06-24 20:46:03 -07006549 public MediaStyle setMediaSession(MediaSession.Token token) {
Dan Sandler842dd772014-05-15 09:36:47 -04006550 mToken = token;
6551 return this;
6552 }
6553
Christoph Studer4600f9b2014-07-22 22:44:43 +02006554 /**
6555 * @hide
6556 */
Dan Sandler842dd772014-05-15 09:36:47 -04006557 @Override
6558 public Notification buildStyled(Notification wip) {
Christoph Studer4600f9b2014-07-22 22:44:43 +02006559 super.buildStyled(wip);
Bryan Mawhinney6be8de32014-07-18 10:35:12 +01006560 if (wip.category == null) {
6561 wip.category = Notification.CATEGORY_TRANSPORT;
6562 }
Dan Sandler842dd772014-05-15 09:36:47 -04006563 return wip;
6564 }
6565
Christoph Studer4600f9b2014-07-22 22:44:43 +02006566 /**
6567 * @hide
6568 */
6569 @Override
Selim Cinek7d1009b2017-01-25 15:28:28 -08006570 public RemoteViews makeContentView(boolean increasedHeight) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04006571 return makeMediaContentView();
Christoph Studer4600f9b2014-07-22 22:44:43 +02006572 }
6573
6574 /**
6575 * @hide
6576 */
6577 @Override
Julia Reynoldsd9228f12015-10-20 10:37:27 -04006578 public RemoteViews makeBigContentView() {
6579 return makeMediaBigContentView();
Christoph Studer4600f9b2014-07-22 22:44:43 +02006580 }
6581
Selim Cinekcc10bfb2016-02-10 16:24:21 -08006582 /**
6583 * @hide
6584 */
6585 @Override
Selim Cinek87ed69b2017-02-09 15:59:43 -08006586 public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
Selim Cinekcc10bfb2016-02-10 16:24:21 -08006587 RemoteViews expanded = makeMediaBigContentView();
6588 return expanded != null ? expanded : makeMediaContentView();
6589 }
6590
Dan Sandler842dd772014-05-15 09:36:47 -04006591 /** @hide */
6592 @Override
6593 public void addExtras(Bundle extras) {
6594 super.addExtras(extras);
6595
6596 if (mToken != null) {
6597 extras.putParcelable(EXTRA_MEDIA_SESSION, mToken);
6598 }
Bryan Mawhinneye191f902014-07-22 12:50:09 +01006599 if (mActionsToShowInCompact != null) {
6600 extras.putIntArray(EXTRA_COMPACT_ACTIONS, mActionsToShowInCompact);
6601 }
Dan Sandler842dd772014-05-15 09:36:47 -04006602 }
6603
Christoph Studer4600f9b2014-07-22 22:44:43 +02006604 /**
6605 * @hide
6606 */
6607 @Override
6608 protected void restoreFromExtras(Bundle extras) {
6609 super.restoreFromExtras(extras);
6610
6611 if (extras.containsKey(EXTRA_MEDIA_SESSION)) {
6612 mToken = extras.getParcelable(EXTRA_MEDIA_SESSION);
6613 }
6614 if (extras.containsKey(EXTRA_COMPACT_ACTIONS)) {
6615 mActionsToShowInCompact = extras.getIntArray(EXTRA_COMPACT_ACTIONS);
6616 }
6617 }
6618
Selim Cinek5bf069a2015-11-10 19:14:27 -05006619 private RemoteViews generateMediaActionButton(Action action, int color) {
Dan Sandler842dd772014-05-15 09:36:47 -04006620 final boolean tombstone = (action.actionIntent == null);
Selim Cinekf33b1112015-07-15 17:45:11 -07006621 RemoteViews button = new BuilderRemoteViews(mBuilder.mContext.getApplicationInfo(),
Alan Viverette3cb07a462014-06-06 14:19:53 -07006622 R.layout.notification_material_media_action);
Dan Sandler68079d52015-07-22 10:45:30 -04006623 button.setImageViewIcon(R.id.action0, action.getIcon());
Anthony Chenad4d1582017-04-10 16:07:58 -07006624
6625 // If the action buttons should not be tinted, then just use the default
6626 // notification color. Otherwise, just use the passed-in color.
6627 int tintColor = mBuilder.shouldTintActionButtons() || mBuilder.isColorized()
6628 ? color
6629 : NotificationColorUtil.resolveColor(mBuilder.mContext,
6630 Notification.COLOR_DEFAULT);
6631
6632 button.setDrawableParameters(R.id.action0, false, -1, tintColor,
6633 PorterDuff.Mode.SRC_ATOP, -1);
Dan Sandler842dd772014-05-15 09:36:47 -04006634 if (!tombstone) {
6635 button.setOnClickPendingIntent(R.id.action0, action.actionIntent);
6636 }
6637 button.setContentDescription(R.id.action0, action.title);
6638 return button;
6639 }
6640
6641 private RemoteViews makeMediaContentView() {
6642 RemoteViews view = mBuilder.applyStandardTemplate(
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006643 R.layout.notification_template_material_media, false /* hasProgress */);
Dan Sandler842dd772014-05-15 09:36:47 -04006644
6645 final int numActions = mBuilder.mActions.size();
6646 final int N = mActionsToShowInCompact == null
6647 ? 0
6648 : Math.min(mActionsToShowInCompact.length, MAX_MEDIA_BUTTONS_IN_COMPACT);
6649 if (N > 0) {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006650 view.removeAllViews(com.android.internal.R.id.media_actions);
Dan Sandler842dd772014-05-15 09:36:47 -04006651 for (int i = 0; i < N; i++) {
6652 if (i >= numActions) {
6653 throw new IllegalArgumentException(String.format(
6654 "setShowActionsInCompactView: action %d out of bounds (max %d)",
6655 i, numActions - 1));
6656 }
6657
6658 final Action action = mBuilder.mActions.get(mActionsToShowInCompact[i]);
Selim Cinek5bf069a2015-11-10 19:14:27 -05006659 final RemoteViews button = generateMediaActionButton(action,
Selim Cinek99104832017-01-25 14:47:33 -08006660 getPrimaryHighlightColor());
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006661 view.addView(com.android.internal.R.id.media_actions, button);
Dan Sandler842dd772014-05-15 09:36:47 -04006662 }
6663 }
Selim Cinekfdc738f2016-01-27 20:04:27 -08006664 handleImage(view);
6665 // handle the content margin
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006666 int endMargin = R.dimen.notification_content_margin_end;
Selim Cinek279fa862016-06-14 10:57:25 -07006667 if (mBuilder.mN.hasLargeIcon()) {
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006668 endMargin = R.dimen.notification_content_plus_picture_margin_end;
Selim Cinekfdc738f2016-01-27 20:04:27 -08006669 }
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006670 view.setViewLayoutMarginEndDimen(R.id.notification_main_column, endMargin);
Dan Sandler842dd772014-05-15 09:36:47 -04006671 return view;
6672 }
6673
Selim Cinek99104832017-01-25 14:47:33 -08006674 private int getPrimaryHighlightColor() {
6675 return mBuilder.getPrimaryHighlightColor();
6676 }
6677
Dan Sandler842dd772014-05-15 09:36:47 -04006678 private RemoteViews makeMediaBigContentView() {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006679 final int actionCount = Math.min(mBuilder.mActions.size(), MAX_MEDIA_BUTTONS);
Selim Cinekcc10bfb2016-02-10 16:24:21 -08006680 // Dont add an expanded view if there is no more content to be revealed
6681 int actionsInCompact = mActionsToShowInCompact == null
6682 ? 0
6683 : Math.min(mActionsToShowInCompact.length, MAX_MEDIA_BUTTONS_IN_COMPACT);
Selim Cinek279fa862016-06-14 10:57:25 -07006684 if (!mBuilder.mN.hasLargeIcon() && actionCount <= actionsInCompact) {
Selim Cinekcc10bfb2016-02-10 16:24:21 -08006685 return null;
6686 }
Selim Cinek5bf069a2015-11-10 19:14:27 -05006687 RemoteViews big = mBuilder.applyStandardTemplate(
6688 R.layout.notification_template_material_big_media,
6689 false);
Dan Sandler842dd772014-05-15 09:36:47 -04006690
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006691 if (actionCount > 0) {
6692 big.removeAllViews(com.android.internal.R.id.media_actions);
6693 for (int i = 0; i < actionCount; i++) {
Selim Cinek5bf069a2015-11-10 19:14:27 -05006694 final RemoteViews button = generateMediaActionButton(mBuilder.mActions.get(i),
Selim Cinek99104832017-01-25 14:47:33 -08006695 getPrimaryHighlightColor());
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006696 big.addView(com.android.internal.R.id.media_actions, button);
Dan Sandler842dd772014-05-15 09:36:47 -04006697 }
6698 }
Selim Cinek5bf069a2015-11-10 19:14:27 -05006699 handleImage(big);
Dan Sandler842dd772014-05-15 09:36:47 -04006700 return big;
6701 }
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006702
Selim Cinek5bf069a2015-11-10 19:14:27 -05006703 private void handleImage(RemoteViews contentView) {
Selim Cinek279fa862016-06-14 10:57:25 -07006704 if (mBuilder.mN.hasLargeIcon()) {
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006705 contentView.setViewLayoutMarginEndDimen(R.id.line1, 0);
6706 contentView.setViewLayoutMarginEndDimen(R.id.text, 0);
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006707 }
6708 }
6709
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006710 /**
6711 * @hide
6712 */
6713 @Override
6714 protected boolean hasProgress() {
6715 return false;
6716 }
Dan Sandler842dd772014-05-15 09:36:47 -04006717 }
Griff Hazen61a9e862014-05-22 16:05:19 -07006718
Selim Cinek593610c2016-02-16 18:42:57 -08006719 /**
6720 * Notification style for custom views that are decorated by the system
6721 *
6722 * <p>Instead of providing a notification that is completely custom, a developer can set this
6723 * style and still obtain system decorations like the notification header with the expand
6724 * affordance and actions.
6725 *
6726 * <p>Use {@link android.app.Notification.Builder#setCustomContentView(RemoteViews)},
6727 * {@link android.app.Notification.Builder#setCustomBigContentView(RemoteViews)} and
6728 * {@link android.app.Notification.Builder#setCustomHeadsUpContentView(RemoteViews)} to set the
6729 * corresponding custom views to display.
6730 *
6731 * To use this style with your Notification, feed it to
6732 * {@link Notification.Builder#setStyle(android.app.Notification.Style)} like so:
6733 * <pre class="prettyprint">
6734 * Notification noti = new Notification.Builder()
6735 * .setSmallIcon(R.drawable.ic_stat_player)
6736 * .setLargeIcon(albumArtBitmap))
6737 * .setCustomContentView(contentView);
6738 * .setStyle(<b>new Notification.DecoratedCustomViewStyle()</b>)
6739 * .build();
6740 * </pre>
6741 */
6742 public static class DecoratedCustomViewStyle extends Style {
6743
6744 public DecoratedCustomViewStyle() {
6745 }
6746
Selim Cinek593610c2016-02-16 18:42:57 -08006747 /**
6748 * @hide
6749 */
6750 public boolean displayCustomViewInline() {
6751 return true;
6752 }
6753
6754 /**
6755 * @hide
6756 */
6757 @Override
Selim Cinek7d1009b2017-01-25 15:28:28 -08006758 public RemoteViews makeContentView(boolean increasedHeight) {
Selim Cinek593610c2016-02-16 18:42:57 -08006759 return makeStandardTemplateWithCustomContent(mBuilder.mN.contentView);
6760 }
6761
6762 /**
6763 * @hide
6764 */
6765 @Override
6766 public RemoteViews makeBigContentView() {
6767 return makeDecoratedBigContentView();
6768 }
6769
6770 /**
6771 * @hide
6772 */
6773 @Override
Selim Cinek87ed69b2017-02-09 15:59:43 -08006774 public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
Selim Cinek593610c2016-02-16 18:42:57 -08006775 return makeDecoratedHeadsUpContentView();
6776 }
6777
Selim Cinek593610c2016-02-16 18:42:57 -08006778 private RemoteViews makeDecoratedHeadsUpContentView() {
6779 RemoteViews headsUpContentView = mBuilder.mN.headsUpContentView == null
6780 ? mBuilder.mN.contentView
6781 : mBuilder.mN.headsUpContentView;
6782 if (mBuilder.mActions.size() == 0) {
6783 return makeStandardTemplateWithCustomContent(headsUpContentView);
6784 }
6785 RemoteViews remoteViews = mBuilder.applyStandardTemplateWithActions(
6786 mBuilder.getBigBaseLayoutResource());
Selim Cinek247fa012016-02-18 09:50:48 -08006787 buildIntoRemoteViewContent(remoteViews, headsUpContentView);
Selim Cinek593610c2016-02-16 18:42:57 -08006788 return remoteViews;
6789 }
6790
Selim Cinek593610c2016-02-16 18:42:57 -08006791 private RemoteViews makeStandardTemplateWithCustomContent(RemoteViews customContent) {
6792 RemoteViews remoteViews = mBuilder.applyStandardTemplate(
6793 mBuilder.getBaseLayoutResource());
Selim Cinek247fa012016-02-18 09:50:48 -08006794 buildIntoRemoteViewContent(remoteViews, customContent);
Selim Cinek593610c2016-02-16 18:42:57 -08006795 return remoteViews;
6796 }
6797
Selim Cinek593610c2016-02-16 18:42:57 -08006798 private RemoteViews makeDecoratedBigContentView() {
6799 RemoteViews bigContentView = mBuilder.mN.bigContentView == null
6800 ? mBuilder.mN.contentView
6801 : mBuilder.mN.bigContentView;
6802 if (mBuilder.mActions.size() == 0) {
6803 return makeStandardTemplateWithCustomContent(bigContentView);
6804 }
6805 RemoteViews remoteViews = mBuilder.applyStandardTemplateWithActions(
6806 mBuilder.getBigBaseLayoutResource());
Selim Cinek247fa012016-02-18 09:50:48 -08006807 buildIntoRemoteViewContent(remoteViews, bigContentView);
Selim Cinek593610c2016-02-16 18:42:57 -08006808 return remoteViews;
6809 }
Selim Cinek247fa012016-02-18 09:50:48 -08006810
6811 private void buildIntoRemoteViewContent(RemoteViews remoteViews,
6812 RemoteViews customContent) {
Adrian Roos5081c0d2016-02-26 16:04:19 -08006813 if (customContent != null) {
Selim Cinekf91017e2016-03-14 12:25:09 -07006814 // Need to clone customContent before adding, because otherwise it can no longer be
6815 // parceled independently of remoteViews.
Adrian Roos5081c0d2016-02-26 16:04:19 -08006816 customContent = customContent.clone();
Anthony Chen8f5f3582017-04-11 11:18:37 -07006817 remoteViews.removeAllViewsExceptId(R.id.notification_main_column, R.id.progress);
6818 remoteViews.addView(R.id.notification_main_column, customContent, 0 /* index */);
Adrian Roos5081c0d2016-02-26 16:04:19 -08006819 }
Selim Cinek247fa012016-02-18 09:50:48 -08006820 // also update the end margin if there is an image
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006821 int endMargin = R.dimen.notification_content_margin_end;
Selim Cinek279fa862016-06-14 10:57:25 -07006822 if (mBuilder.mN.hasLargeIcon()) {
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006823 endMargin = R.dimen.notification_content_plus_picture_margin_end;
Selim Cinek247fa012016-02-18 09:50:48 -08006824 }
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006825 remoteViews.setViewLayoutMarginEndDimen(R.id.notification_main_column, endMargin);
Selim Cinek247fa012016-02-18 09:50:48 -08006826 }
Selim Cinek593610c2016-02-16 18:42:57 -08006827 }
6828
Selim Cinek03eb3b72016-02-18 10:39:45 -08006829 /**
6830 * Notification style for media custom views that are decorated by the system
6831 *
6832 * <p>Instead of providing a media notification that is completely custom, a developer can set
6833 * this style and still obtain system decorations like the notification header with the expand
6834 * affordance and actions.
6835 *
6836 * <p>Use {@link android.app.Notification.Builder#setCustomContentView(RemoteViews)},
6837 * {@link android.app.Notification.Builder#setCustomBigContentView(RemoteViews)} and
6838 * {@link android.app.Notification.Builder#setCustomHeadsUpContentView(RemoteViews)} to set the
6839 * corresponding custom views to display.
Selim Cinek99104832017-01-25 14:47:33 -08006840 * <p>
6841 * Contrary to {@link MediaStyle} a developer has to opt-in to the colorizing of the
6842 * notification by using {@link Notification.Builder#setColorized(boolean)}.
6843 * <p>
Selim Cinek03eb3b72016-02-18 10:39:45 -08006844 * To use this style with your Notification, feed it to
6845 * {@link Notification.Builder#setStyle(android.app.Notification.Style)} like so:
6846 * <pre class="prettyprint">
6847 * Notification noti = new Notification.Builder()
6848 * .setSmallIcon(R.drawable.ic_stat_player)
6849 * .setLargeIcon(albumArtBitmap))
6850 * .setCustomContentView(contentView);
6851 * .setStyle(<b>new Notification.DecoratedMediaCustomViewStyle()</b>
6852 * .setMediaSession(mySession))
6853 * .build();
6854 * </pre>
6855 *
6856 * @see android.app.Notification.DecoratedCustomViewStyle
6857 * @see android.app.Notification.MediaStyle
6858 */
6859 public static class DecoratedMediaCustomViewStyle extends MediaStyle {
6860
6861 public DecoratedMediaCustomViewStyle() {
6862 }
6863
Selim Cinek03eb3b72016-02-18 10:39:45 -08006864 /**
6865 * @hide
6866 */
6867 public boolean displayCustomViewInline() {
6868 return true;
6869 }
6870
6871 /**
6872 * @hide
6873 */
6874 @Override
Selim Cinek7d1009b2017-01-25 15:28:28 -08006875 public RemoteViews makeContentView(boolean increasedHeight) {
6876 RemoteViews remoteViews = super.makeContentView(false /* increasedHeight */);
Selim Cinek03eb3b72016-02-18 10:39:45 -08006877 return buildIntoRemoteView(remoteViews, R.id.notification_content_container,
6878 mBuilder.mN.contentView);
6879 }
6880
6881 /**
6882 * @hide
6883 */
6884 @Override
6885 public RemoteViews makeBigContentView() {
6886 RemoteViews customRemoteView = mBuilder.mN.bigContentView != null
6887 ? mBuilder.mN.bigContentView
6888 : mBuilder.mN.contentView;
6889 return makeBigContentViewWithCustomContent(customRemoteView);
6890 }
6891
6892 private RemoteViews makeBigContentViewWithCustomContent(RemoteViews customRemoteView) {
6893 RemoteViews remoteViews = super.makeBigContentView();
6894 if (remoteViews != null) {
6895 return buildIntoRemoteView(remoteViews, R.id.notification_main_column,
6896 customRemoteView);
6897 } else if (customRemoteView != mBuilder.mN.contentView){
Selim Cinek7d1009b2017-01-25 15:28:28 -08006898 remoteViews = super.makeContentView(false /* increasedHeight */);
Selim Cinek03eb3b72016-02-18 10:39:45 -08006899 return buildIntoRemoteView(remoteViews, R.id.notification_content_container,
6900 customRemoteView);
6901 } else {
6902 return null;
6903 }
6904 }
6905
6906 /**
6907 * @hide
6908 */
6909 @Override
Selim Cinek87ed69b2017-02-09 15:59:43 -08006910 public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
Selim Cinek03eb3b72016-02-18 10:39:45 -08006911 RemoteViews customRemoteView = mBuilder.mN.headsUpContentView != null
6912 ? mBuilder.mN.headsUpContentView
6913 : mBuilder.mN.contentView;
6914 return makeBigContentViewWithCustomContent(customRemoteView);
6915 }
6916
6917 private RemoteViews buildIntoRemoteView(RemoteViews remoteViews, int id,
6918 RemoteViews customContent) {
Adrian Roos5081c0d2016-02-26 16:04:19 -08006919 if (customContent != null) {
Selim Cinekf91017e2016-03-14 12:25:09 -07006920 // Need to clone customContent before adding, because otherwise it can no longer be
6921 // parceled independently of remoteViews.
Adrian Roos5081c0d2016-02-26 16:04:19 -08006922 customContent = customContent.clone();
Selim Cinekf91017e2016-03-14 12:25:09 -07006923 remoteViews.removeAllViews(id);
6924 remoteViews.addView(id, customContent);
Adrian Roos5081c0d2016-02-26 16:04:19 -08006925 }
Selim Cinek03eb3b72016-02-18 10:39:45 -08006926 return remoteViews;
6927 }
6928 }
6929
Christoph Studer4600f9b2014-07-22 22:44:43 +02006930 // When adding a new Style subclass here, don't forget to update
6931 // Builder.getNotificationStyleClass.
6932
Griff Hazen61a9e862014-05-22 16:05:19 -07006933 /**
6934 * Extender interface for use with {@link Builder#extend}. Extenders may be used to add
6935 * metadata or change options on a notification builder.
6936 */
6937 public interface Extender {
6938 /**
6939 * Apply this extender to a notification builder.
6940 * @param builder the builder to be modified.
6941 * @return the build object for chaining.
6942 */
6943 public Builder extend(Builder builder);
6944 }
6945
6946 /**
6947 * Helper class to add wearable extensions to notifications.
6948 * <p class="note"> See
6949 * <a href="{@docRoot}wear/notifications/creating.html">Creating Notifications
6950 * for Android Wear</a> for more information on how to use this class.
6951 * <p>
6952 * To create a notification with wearable extensions:
6953 * <ol>
6954 * <li>Create a {@link android.app.Notification.Builder}, setting any desired
6955 * properties.
6956 * <li>Create a {@link android.app.Notification.WearableExtender}.
6957 * <li>Set wearable-specific properties using the
6958 * {@code add} and {@code set} methods of {@link android.app.Notification.WearableExtender}.
6959 * <li>Call {@link android.app.Notification.Builder#extend} to apply the extensions to a
6960 * notification.
6961 * <li>Post the notification to the notification system with the
6962 * {@code NotificationManager.notify(...)} methods.
6963 * </ol>
6964 *
6965 * <pre class="prettyprint">
6966 * Notification notif = new Notification.Builder(mContext)
6967 * .setContentTitle(&quot;New mail from &quot; + sender.toString())
6968 * .setContentText(subject)
6969 * .setSmallIcon(R.drawable.new_mail)
6970 * .extend(new Notification.WearableExtender()
6971 * .setContentIcon(R.drawable.new_mail))
6972 * .build();
6973 * NotificationManager notificationManger =
6974 * (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
6975 * notificationManger.notify(0, notif);</pre>
6976 *
6977 * <p>Wearable extensions can be accessed on an existing notification by using the
6978 * {@code WearableExtender(Notification)} constructor,
6979 * and then using the {@code get} methods to access values.
6980 *
6981 * <pre class="prettyprint">
6982 * Notification.WearableExtender wearableExtender = new Notification.WearableExtender(
6983 * notification);
Griff Hazen14f57992014-05-26 09:07:14 -07006984 * List&lt;Notification&gt; pages = wearableExtender.getPages();</pre>
Griff Hazen61a9e862014-05-22 16:05:19 -07006985 */
6986 public static final class WearableExtender implements Extender {
6987 /**
6988 * Sentinel value for an action index that is unset.
6989 */
6990 public static final int UNSET_ACTION_INDEX = -1;
6991
6992 /**
6993 * Size value for use with {@link #setCustomSizePreset} to show this notification with
6994 * default sizing.
6995 * <p>For custom display notifications created using {@link #setDisplayIntent},
Paul Soulosaa4f4bf2015-08-04 11:59:45 -07006996 * the default is {@link #SIZE_MEDIUM}. All other notifications size automatically based
Griff Hazen61a9e862014-05-22 16:05:19 -07006997 * on their content.
6998 */
6999 public static final int SIZE_DEFAULT = 0;
7000
7001 /**
7002 * Size value for use with {@link #setCustomSizePreset} to show this notification
7003 * with an extra small size.
7004 * <p>This value is only applicable for custom display notifications created using
7005 * {@link #setDisplayIntent}.
7006 */
7007 public static final int SIZE_XSMALL = 1;
7008
7009 /**
7010 * Size value for use with {@link #setCustomSizePreset} to show this notification
7011 * with a small size.
7012 * <p>This value is only applicable for custom display notifications created using
7013 * {@link #setDisplayIntent}.
7014 */
7015 public static final int SIZE_SMALL = 2;
7016
7017 /**
7018 * Size value for use with {@link #setCustomSizePreset} to show this notification
7019 * with a medium size.
7020 * <p>This value is only applicable for custom display notifications created using
7021 * {@link #setDisplayIntent}.
7022 */
7023 public static final int SIZE_MEDIUM = 3;
7024
7025 /**
7026 * Size value for use with {@link #setCustomSizePreset} to show this notification
7027 * with a large size.
7028 * <p>This value is only applicable for custom display notifications created using
7029 * {@link #setDisplayIntent}.
7030 */
7031 public static final int SIZE_LARGE = 4;
7032
Griff Hazend5f11f92014-05-27 15:40:09 -07007033 /**
7034 * Size value for use with {@link #setCustomSizePreset} to show this notification
7035 * full screen.
7036 * <p>This value is only applicable for custom display notifications created using
7037 * {@link #setDisplayIntent}.
7038 */
7039 public static final int SIZE_FULL_SCREEN = 5;
7040
Griff Hazen5f2edfc2014-09-29 16:28:44 -07007041 /**
7042 * Sentinel value for use with {@link #setHintScreenTimeout} to keep the screen on for a
7043 * short amount of time when this notification is displayed on the screen. This
7044 * is the default value.
7045 */
7046 public static final int SCREEN_TIMEOUT_SHORT = 0;
7047
7048 /**
7049 * Sentinel value for use with {@link #setHintScreenTimeout} to keep the screen on
7050 * for a longer amount of time when this notification is displayed on the screen.
7051 */
7052 public static final int SCREEN_TIMEOUT_LONG = -1;
7053
Griff Hazen61a9e862014-05-22 16:05:19 -07007054 /** Notification extra which contains wearable extensions */
7055 private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS";
7056
Pete Gastaf6781d2014-10-07 15:17:05 -04007057 // Keys within EXTRA_WEARABLE_EXTENSIONS for wearable options.
Griff Hazen61a9e862014-05-22 16:05:19 -07007058 private static final String KEY_ACTIONS = "actions";
7059 private static final String KEY_FLAGS = "flags";
7060 private static final String KEY_DISPLAY_INTENT = "displayIntent";
7061 private static final String KEY_PAGES = "pages";
7062 private static final String KEY_BACKGROUND = "background";
7063 private static final String KEY_CONTENT_ICON = "contentIcon";
7064 private static final String KEY_CONTENT_ICON_GRAVITY = "contentIconGravity";
7065 private static final String KEY_CONTENT_ACTION_INDEX = "contentActionIndex";
7066 private static final String KEY_CUSTOM_SIZE_PRESET = "customSizePreset";
7067 private static final String KEY_CUSTOM_CONTENT_HEIGHT = "customContentHeight";
7068 private static final String KEY_GRAVITY = "gravity";
Griff Hazen5f2edfc2014-09-29 16:28:44 -07007069 private static final String KEY_HINT_SCREEN_TIMEOUT = "hintScreenTimeout";
Nadia Benbernou948627e2016-04-14 14:41:08 -04007070 private static final String KEY_DISMISSAL_ID = "dismissalId";
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04007071 private static final String KEY_BRIDGE_TAG = "bridgeTag";
Griff Hazen61a9e862014-05-22 16:05:19 -07007072
7073 // Flags bitwise-ored to mFlags
7074 private static final int FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE = 0x1;
7075 private static final int FLAG_HINT_HIDE_ICON = 1 << 1;
7076 private static final int FLAG_HINT_SHOW_BACKGROUND_ONLY = 1 << 2;
7077 private static final int FLAG_START_SCROLL_BOTTOM = 1 << 3;
Griff Hazen5f2edfc2014-09-29 16:28:44 -07007078 private static final int FLAG_HINT_AVOID_BACKGROUND_CLIPPING = 1 << 4;
Alex Hills4bcb06b2016-04-05 14:26:25 -04007079 private static final int FLAG_BIG_PICTURE_AMBIENT = 1 << 5;
Alex Hills9ab3a232016-04-05 14:54:56 -04007080 private static final int FLAG_HINT_CONTENT_INTENT_LAUNCHES_ACTIVITY = 1 << 6;
Griff Hazen61a9e862014-05-22 16:05:19 -07007081
7082 // Default value for flags integer
7083 private static final int DEFAULT_FLAGS = FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE;
7084
7085 private static final int DEFAULT_CONTENT_ICON_GRAVITY = Gravity.END;
7086 private static final int DEFAULT_GRAVITY = Gravity.BOTTOM;
7087
7088 private ArrayList<Action> mActions = new ArrayList<Action>();
7089 private int mFlags = DEFAULT_FLAGS;
7090 private PendingIntent mDisplayIntent;
7091 private ArrayList<Notification> mPages = new ArrayList<Notification>();
7092 private Bitmap mBackground;
7093 private int mContentIcon;
7094 private int mContentIconGravity = DEFAULT_CONTENT_ICON_GRAVITY;
7095 private int mContentActionIndex = UNSET_ACTION_INDEX;
7096 private int mCustomSizePreset = SIZE_DEFAULT;
7097 private int mCustomContentHeight;
7098 private int mGravity = DEFAULT_GRAVITY;
Griff Hazen5f2edfc2014-09-29 16:28:44 -07007099 private int mHintScreenTimeout;
Nadia Benbernou948627e2016-04-14 14:41:08 -04007100 private String mDismissalId;
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04007101 private String mBridgeTag;
Griff Hazen61a9e862014-05-22 16:05:19 -07007102
7103 /**
7104 * Create a {@link android.app.Notification.WearableExtender} with default
7105 * options.
7106 */
7107 public WearableExtender() {
7108 }
7109
7110 public WearableExtender(Notification notif) {
7111 Bundle wearableBundle = notif.extras.getBundle(EXTRA_WEARABLE_EXTENSIONS);
7112 if (wearableBundle != null) {
7113 List<Action> actions = wearableBundle.getParcelableArrayList(KEY_ACTIONS);
7114 if (actions != null) {
7115 mActions.addAll(actions);
7116 }
7117
7118 mFlags = wearableBundle.getInt(KEY_FLAGS, DEFAULT_FLAGS);
7119 mDisplayIntent = wearableBundle.getParcelable(KEY_DISPLAY_INTENT);
7120
7121 Notification[] pages = getNotificationArrayFromBundle(
7122 wearableBundle, KEY_PAGES);
7123 if (pages != null) {
7124 Collections.addAll(mPages, pages);
7125 }
7126
7127 mBackground = wearableBundle.getParcelable(KEY_BACKGROUND);
7128 mContentIcon = wearableBundle.getInt(KEY_CONTENT_ICON);
7129 mContentIconGravity = wearableBundle.getInt(KEY_CONTENT_ICON_GRAVITY,
7130 DEFAULT_CONTENT_ICON_GRAVITY);
7131 mContentActionIndex = wearableBundle.getInt(KEY_CONTENT_ACTION_INDEX,
7132 UNSET_ACTION_INDEX);
7133 mCustomSizePreset = wearableBundle.getInt(KEY_CUSTOM_SIZE_PRESET,
7134 SIZE_DEFAULT);
7135 mCustomContentHeight = wearableBundle.getInt(KEY_CUSTOM_CONTENT_HEIGHT);
7136 mGravity = wearableBundle.getInt(KEY_GRAVITY, DEFAULT_GRAVITY);
Griff Hazen5f2edfc2014-09-29 16:28:44 -07007137 mHintScreenTimeout = wearableBundle.getInt(KEY_HINT_SCREEN_TIMEOUT);
Nadia Benbernou948627e2016-04-14 14:41:08 -04007138 mDismissalId = wearableBundle.getString(KEY_DISMISSAL_ID);
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04007139 mBridgeTag = wearableBundle.getString(KEY_BRIDGE_TAG);
Griff Hazen61a9e862014-05-22 16:05:19 -07007140 }
7141 }
7142
7143 /**
7144 * Apply wearable extensions to a notification that is being built. This is typically
7145 * called by the {@link android.app.Notification.Builder#extend} method of
7146 * {@link android.app.Notification.Builder}.
7147 */
7148 @Override
7149 public Notification.Builder extend(Notification.Builder builder) {
7150 Bundle wearableBundle = new Bundle();
7151
7152 if (!mActions.isEmpty()) {
7153 wearableBundle.putParcelableArrayList(KEY_ACTIONS, mActions);
7154 }
7155 if (mFlags != DEFAULT_FLAGS) {
7156 wearableBundle.putInt(KEY_FLAGS, mFlags);
7157 }
7158 if (mDisplayIntent != null) {
7159 wearableBundle.putParcelable(KEY_DISPLAY_INTENT, mDisplayIntent);
7160 }
7161 if (!mPages.isEmpty()) {
7162 wearableBundle.putParcelableArray(KEY_PAGES, mPages.toArray(
7163 new Notification[mPages.size()]));
7164 }
7165 if (mBackground != null) {
7166 wearableBundle.putParcelable(KEY_BACKGROUND, mBackground);
7167 }
7168 if (mContentIcon != 0) {
7169 wearableBundle.putInt(KEY_CONTENT_ICON, mContentIcon);
7170 }
7171 if (mContentIconGravity != DEFAULT_CONTENT_ICON_GRAVITY) {
7172 wearableBundle.putInt(KEY_CONTENT_ICON_GRAVITY, mContentIconGravity);
7173 }
7174 if (mContentActionIndex != UNSET_ACTION_INDEX) {
7175 wearableBundle.putInt(KEY_CONTENT_ACTION_INDEX,
7176 mContentActionIndex);
7177 }
7178 if (mCustomSizePreset != SIZE_DEFAULT) {
7179 wearableBundle.putInt(KEY_CUSTOM_SIZE_PRESET, mCustomSizePreset);
7180 }
7181 if (mCustomContentHeight != 0) {
7182 wearableBundle.putInt(KEY_CUSTOM_CONTENT_HEIGHT, mCustomContentHeight);
7183 }
7184 if (mGravity != DEFAULT_GRAVITY) {
7185 wearableBundle.putInt(KEY_GRAVITY, mGravity);
7186 }
Griff Hazen5f2edfc2014-09-29 16:28:44 -07007187 if (mHintScreenTimeout != 0) {
7188 wearableBundle.putInt(KEY_HINT_SCREEN_TIMEOUT, mHintScreenTimeout);
7189 }
Nadia Benbernou948627e2016-04-14 14:41:08 -04007190 if (mDismissalId != null) {
7191 wearableBundle.putString(KEY_DISMISSAL_ID, mDismissalId);
7192 }
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04007193 if (mBridgeTag != null) {
7194 wearableBundle.putString(KEY_BRIDGE_TAG, mBridgeTag);
7195 }
Griff Hazen61a9e862014-05-22 16:05:19 -07007196
7197 builder.getExtras().putBundle(EXTRA_WEARABLE_EXTENSIONS, wearableBundle);
7198 return builder;
7199 }
7200
7201 @Override
7202 public WearableExtender clone() {
7203 WearableExtender that = new WearableExtender();
7204 that.mActions = new ArrayList<Action>(this.mActions);
7205 that.mFlags = this.mFlags;
7206 that.mDisplayIntent = this.mDisplayIntent;
7207 that.mPages = new ArrayList<Notification>(this.mPages);
7208 that.mBackground = this.mBackground;
7209 that.mContentIcon = this.mContentIcon;
7210 that.mContentIconGravity = this.mContentIconGravity;
7211 that.mContentActionIndex = this.mContentActionIndex;
7212 that.mCustomSizePreset = this.mCustomSizePreset;
7213 that.mCustomContentHeight = this.mCustomContentHeight;
7214 that.mGravity = this.mGravity;
Griff Hazen5f2edfc2014-09-29 16:28:44 -07007215 that.mHintScreenTimeout = this.mHintScreenTimeout;
Nadia Benbernou948627e2016-04-14 14:41:08 -04007216 that.mDismissalId = this.mDismissalId;
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04007217 that.mBridgeTag = this.mBridgeTag;
Griff Hazen61a9e862014-05-22 16:05:19 -07007218 return that;
7219 }
7220
7221 /**
7222 * Add a wearable action to this notification.
7223 *
7224 * <p>When wearable actions are added using this method, the set of actions that
7225 * show on a wearable device splits from devices that only show actions added
7226 * using {@link android.app.Notification.Builder#addAction}. This allows for customization
7227 * of which actions display on different devices.
7228 *
7229 * @param action the action to add to this notification
7230 * @return this object for method chaining
7231 * @see android.app.Notification.Action
7232 */
7233 public WearableExtender addAction(Action action) {
7234 mActions.add(action);
7235 return this;
7236 }
7237
7238 /**
7239 * Adds wearable actions to this notification.
7240 *
7241 * <p>When wearable actions are added using this method, the set of actions that
7242 * show on a wearable device splits from devices that only show actions added
7243 * using {@link android.app.Notification.Builder#addAction}. This allows for customization
7244 * of which actions display on different devices.
7245 *
7246 * @param actions the actions to add to this notification
7247 * @return this object for method chaining
7248 * @see android.app.Notification.Action
7249 */
7250 public WearableExtender addActions(List<Action> actions) {
7251 mActions.addAll(actions);
7252 return this;
7253 }
7254
7255 /**
7256 * Clear all wearable actions present on this builder.
7257 * @return this object for method chaining.
7258 * @see #addAction
7259 */
7260 public WearableExtender clearActions() {
7261 mActions.clear();
7262 return this;
7263 }
7264
7265 /**
7266 * Get the wearable actions present on this notification.
7267 */
7268 public List<Action> getActions() {
7269 return mActions;
7270 }
7271
7272 /**
7273 * Set an intent to launch inside of an activity view when displaying
Griff Hazen14f57992014-05-26 09:07:14 -07007274 * this notification. The {@link PendingIntent} provided should be for an activity.
7275 *
7276 * <pre class="prettyprint">
7277 * Intent displayIntent = new Intent(context, MyDisplayActivity.class);
7278 * PendingIntent displayPendingIntent = PendingIntent.getActivity(context,
7279 * 0, displayIntent, PendingIntent.FLAG_UPDATE_CURRENT);
7280 * Notification notif = new Notification.Builder(context)
7281 * .extend(new Notification.WearableExtender()
7282 * .setDisplayIntent(displayPendingIntent)
7283 * .setCustomSizePreset(Notification.WearableExtender.SIZE_MEDIUM))
7284 * .build();</pre>
7285 *
7286 * <p>The activity to launch needs to allow embedding, must be exported, and
Griff Hazen831ca9d2014-06-17 00:38:38 -07007287 * should have an empty task affinity. It is also recommended to use the device
7288 * default light theme.
Griff Hazen14f57992014-05-26 09:07:14 -07007289 *
7290 * <p>Example AndroidManifest.xml entry:
7291 * <pre class="prettyprint">
7292 * &lt;activity android:name=&quot;com.example.MyDisplayActivity&quot;
7293 * android:exported=&quot;true&quot;
7294 * android:allowEmbedded=&quot;true&quot;
Griff Hazen831ca9d2014-06-17 00:38:38 -07007295 * android:taskAffinity=&quot;&quot;
7296 * android:theme=&quot;@android:style/Theme.DeviceDefault.Light&quot; /&gt;</pre>
Griff Hazen61a9e862014-05-22 16:05:19 -07007297 *
7298 * @param intent the {@link PendingIntent} for an activity
7299 * @return this object for method chaining
7300 * @see android.app.Notification.WearableExtender#getDisplayIntent
7301 */
7302 public WearableExtender setDisplayIntent(PendingIntent intent) {
7303 mDisplayIntent = intent;
7304 return this;
7305 }
7306
7307 /**
7308 * Get the intent to launch inside of an activity view when displaying this
7309 * notification. This {@code PendingIntent} should be for an activity.
7310 */
7311 public PendingIntent getDisplayIntent() {
7312 return mDisplayIntent;
7313 }
7314
7315 /**
7316 * Add an additional page of content to display with this notification. The current
7317 * notification forms the first page, and pages added using this function form
7318 * subsequent pages. This field can be used to separate a notification into multiple
7319 * sections.
7320 *
7321 * @param page the notification to add as another page
7322 * @return this object for method chaining
7323 * @see android.app.Notification.WearableExtender#getPages
7324 */
7325 public WearableExtender addPage(Notification page) {
7326 mPages.add(page);
7327 return this;
7328 }
7329
7330 /**
7331 * Add additional pages of content to display with this notification. The current
7332 * notification forms the first page, and pages added using this function form
7333 * subsequent pages. This field can be used to separate a notification into multiple
7334 * sections.
7335 *
7336 * @param pages a list of notifications
7337 * @return this object for method chaining
7338 * @see android.app.Notification.WearableExtender#getPages
7339 */
7340 public WearableExtender addPages(List<Notification> pages) {
7341 mPages.addAll(pages);
7342 return this;
7343 }
7344
7345 /**
7346 * Clear all additional pages present on this builder.
7347 * @return this object for method chaining.
7348 * @see #addPage
7349 */
7350 public WearableExtender clearPages() {
7351 mPages.clear();
7352 return this;
7353 }
7354
7355 /**
7356 * Get the array of additional pages of content for displaying this notification. The
7357 * current notification forms the first page, and elements within this array form
7358 * subsequent pages. This field can be used to separate a notification into multiple
7359 * sections.
7360 * @return the pages for this notification
7361 */
7362 public List<Notification> getPages() {
7363 return mPages;
7364 }
7365
7366 /**
7367 * Set a background image to be displayed behind the notification content.
7368 * Contrary to the {@link android.app.Notification.BigPictureStyle}, this background
7369 * will work with any notification style.
7370 *
7371 * @param background the background bitmap
7372 * @return this object for method chaining
7373 * @see android.app.Notification.WearableExtender#getBackground
7374 */
7375 public WearableExtender setBackground(Bitmap background) {
7376 mBackground = background;
7377 return this;
7378 }
7379
7380 /**
7381 * Get a background image to be displayed behind the notification content.
7382 * Contrary to the {@link android.app.Notification.BigPictureStyle}, this background
7383 * will work with any notification style.
7384 *
7385 * @return the background image
7386 * @see android.app.Notification.WearableExtender#setBackground
7387 */
7388 public Bitmap getBackground() {
7389 return mBackground;
7390 }
7391
7392 /**
7393 * Set an icon that goes with the content of this notification.
7394 */
7395 public WearableExtender setContentIcon(int icon) {
7396 mContentIcon = icon;
7397 return this;
7398 }
7399
7400 /**
7401 * Get an icon that goes with the content of this notification.
7402 */
7403 public int getContentIcon() {
7404 return mContentIcon;
7405 }
7406
7407 /**
7408 * Set the gravity that the content icon should have within the notification display.
7409 * Supported values include {@link android.view.Gravity#START} and
7410 * {@link android.view.Gravity#END}. The default value is {@link android.view.Gravity#END}.
7411 * @see #setContentIcon
7412 */
7413 public WearableExtender setContentIconGravity(int contentIconGravity) {
7414 mContentIconGravity = contentIconGravity;
7415 return this;
7416 }
7417
7418 /**
7419 * Get the gravity that the content icon should have within the notification display.
7420 * Supported values include {@link android.view.Gravity#START} and
7421 * {@link android.view.Gravity#END}. The default value is {@link android.view.Gravity#END}.
7422 * @see #getContentIcon
7423 */
7424 public int getContentIconGravity() {
7425 return mContentIconGravity;
7426 }
7427
7428 /**
7429 * Set an action from this notification's actions to be clickable with the content of
Griff Hazen14f57992014-05-26 09:07:14 -07007430 * this notification. This action will no longer display separately from the
7431 * notification's content.
7432 *
Griff Hazenca48d352014-05-28 22:37:13 -07007433 * <p>For notifications with multiple pages, child pages can also have content actions
Griff Hazen14f57992014-05-26 09:07:14 -07007434 * set, although the list of available actions comes from the main notification and not
7435 * from the child page's notification.
7436 *
7437 * @param actionIndex The index of the action to hoist onto the current notification page.
7438 * If wearable actions were added to the main notification, this index
7439 * will apply to that list, otherwise it will apply to the regular
7440 * actions list.
Griff Hazen61a9e862014-05-22 16:05:19 -07007441 */
7442 public WearableExtender setContentAction(int actionIndex) {
7443 mContentActionIndex = actionIndex;
7444 return this;
7445 }
7446
7447 /**
Griff Hazenca48d352014-05-28 22:37:13 -07007448 * Get the index of the notification action, if any, that was specified as being clickable
7449 * with the content of this notification. This action will no longer display separately
Griff Hazen14f57992014-05-26 09:07:14 -07007450 * from the notification's content.
Griff Hazen61a9e862014-05-22 16:05:19 -07007451 *
Griff Hazenca48d352014-05-28 22:37:13 -07007452 * <p>For notifications with multiple pages, child pages can also have content actions
Griff Hazen14f57992014-05-26 09:07:14 -07007453 * set, although the list of available actions comes from the main notification and not
7454 * from the child page's notification.
7455 *
7456 * <p>If wearable specific actions were added to the main notification, this index will
7457 * apply to that list, otherwise it will apply to the regular actions list.
Griff Hazenca48d352014-05-28 22:37:13 -07007458 *
7459 * @return the action index or {@link #UNSET_ACTION_INDEX} if no action was selected.
Griff Hazen61a9e862014-05-22 16:05:19 -07007460 */
7461 public int getContentAction() {
7462 return mContentActionIndex;
7463 }
7464
7465 /**
7466 * Set the gravity that this notification should have within the available viewport space.
7467 * Supported values include {@link android.view.Gravity#TOP},
7468 * {@link android.view.Gravity#CENTER_VERTICAL} and {@link android.view.Gravity#BOTTOM}.
7469 * The default value is {@link android.view.Gravity#BOTTOM}.
7470 */
7471 public WearableExtender setGravity(int gravity) {
7472 mGravity = gravity;
7473 return this;
7474 }
7475
7476 /**
7477 * Get the gravity that this notification should have within the available viewport space.
7478 * Supported values include {@link android.view.Gravity#TOP},
7479 * {@link android.view.Gravity#CENTER_VERTICAL} and {@link android.view.Gravity#BOTTOM}.
7480 * The default value is {@link android.view.Gravity#BOTTOM}.
7481 */
7482 public int getGravity() {
7483 return mGravity;
7484 }
7485
7486 /**
7487 * Set the custom size preset for the display of this notification out of the available
7488 * presets found in {@link android.app.Notification.WearableExtender}, e.g.
7489 * {@link #SIZE_LARGE}.
7490 * <p>Some custom size presets are only applicable for custom display notifications created
7491 * using {@link android.app.Notification.WearableExtender#setDisplayIntent}. Check the
7492 * documentation for the preset in question. See also
7493 * {@link #setCustomContentHeight} and {@link #getCustomSizePreset}.
7494 */
7495 public WearableExtender setCustomSizePreset(int sizePreset) {
7496 mCustomSizePreset = sizePreset;
7497 return this;
7498 }
7499
7500 /**
7501 * Get the custom size preset for the display of this notification out of the available
7502 * presets found in {@link android.app.Notification.WearableExtender}, e.g.
7503 * {@link #SIZE_LARGE}.
7504 * <p>Some custom size presets are only applicable for custom display notifications created
7505 * using {@link #setDisplayIntent}. Check the documentation for the preset in question.
7506 * See also {@link #setCustomContentHeight} and {@link #setCustomSizePreset}.
7507 */
7508 public int getCustomSizePreset() {
7509 return mCustomSizePreset;
7510 }
7511
7512 /**
7513 * Set the custom height in pixels for the display of this notification's content.
7514 * <p>This option is only available for custom display notifications created
7515 * using {@link android.app.Notification.WearableExtender#setDisplayIntent}. See also
7516 * {@link android.app.Notification.WearableExtender#setCustomSizePreset} and
7517 * {@link #getCustomContentHeight}.
7518 */
7519 public WearableExtender setCustomContentHeight(int height) {
7520 mCustomContentHeight = height;
7521 return this;
7522 }
7523
7524 /**
7525 * Get the custom height in pixels for the display of this notification's content.
7526 * <p>This option is only available for custom display notifications created
7527 * using {@link #setDisplayIntent}. See also {@link #setCustomSizePreset} and
7528 * {@link #setCustomContentHeight}.
7529 */
7530 public int getCustomContentHeight() {
7531 return mCustomContentHeight;
7532 }
7533
7534 /**
7535 * Set whether the scrolling position for the contents of this notification should start
7536 * at the bottom of the contents instead of the top when the contents are too long to
7537 * display within the screen. Default is false (start scroll at the top).
7538 */
7539 public WearableExtender setStartScrollBottom(boolean startScrollBottom) {
7540 setFlag(FLAG_START_SCROLL_BOTTOM, startScrollBottom);
7541 return this;
7542 }
7543
7544 /**
7545 * Get whether the scrolling position for the contents of this notification should start
7546 * at the bottom of the contents instead of the top when the contents are too long to
7547 * display within the screen. Default is false (start scroll at the top).
7548 */
7549 public boolean getStartScrollBottom() {
7550 return (mFlags & FLAG_START_SCROLL_BOTTOM) != 0;
7551 }
7552
7553 /**
7554 * Set whether the content intent is available when the wearable device is not connected
7555 * to a companion device. The user can still trigger this intent when the wearable device
7556 * is offline, but a visual hint will indicate that the content intent may not be available.
7557 * Defaults to true.
7558 */
7559 public WearableExtender setContentIntentAvailableOffline(
7560 boolean contentIntentAvailableOffline) {
7561 setFlag(FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE, contentIntentAvailableOffline);
7562 return this;
7563 }
7564
7565 /**
7566 * Get whether the content intent is available when the wearable device is not connected
7567 * to a companion device. The user can still trigger this intent when the wearable device
7568 * is offline, but a visual hint will indicate that the content intent may not be available.
7569 * Defaults to true.
7570 */
7571 public boolean getContentIntentAvailableOffline() {
7572 return (mFlags & FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE) != 0;
7573 }
7574
7575 /**
7576 * Set a hint that this notification's icon should not be displayed.
7577 * @param hintHideIcon {@code true} to hide the icon, {@code false} otherwise.
7578 * @return this object for method chaining
7579 */
7580 public WearableExtender setHintHideIcon(boolean hintHideIcon) {
7581 setFlag(FLAG_HINT_HIDE_ICON, hintHideIcon);
7582 return this;
7583 }
7584
7585 /**
7586 * Get a hint that this notification's icon should not be displayed.
7587 * @return {@code true} if this icon should not be displayed, false otherwise.
7588 * The default value is {@code false} if this was never set.
7589 */
7590 public boolean getHintHideIcon() {
7591 return (mFlags & FLAG_HINT_HIDE_ICON) != 0;
7592 }
7593
7594 /**
7595 * Set a visual hint that only the background image of this notification should be
7596 * displayed, and other semantic content should be hidden. This hint is only applicable
7597 * to sub-pages added using {@link #addPage}.
7598 */
7599 public WearableExtender setHintShowBackgroundOnly(boolean hintShowBackgroundOnly) {
7600 setFlag(FLAG_HINT_SHOW_BACKGROUND_ONLY, hintShowBackgroundOnly);
7601 return this;
7602 }
7603
7604 /**
7605 * Get a visual hint that only the background image of this notification should be
7606 * displayed, and other semantic content should be hidden. This hint is only applicable
7607 * to sub-pages added using {@link android.app.Notification.WearableExtender#addPage}.
7608 */
7609 public boolean getHintShowBackgroundOnly() {
7610 return (mFlags & FLAG_HINT_SHOW_BACKGROUND_ONLY) != 0;
7611 }
7612
Griff Hazen5f2edfc2014-09-29 16:28:44 -07007613 /**
Griff Hazen9c5be4e2014-11-17 17:47:50 -08007614 * Set a hint that this notification's background should not be clipped if possible,
7615 * and should instead be resized to fully display on the screen, retaining the aspect
7616 * ratio of the image. This can be useful for images like barcodes or qr codes.
Griff Hazen5f2edfc2014-09-29 16:28:44 -07007617 * @param hintAvoidBackgroundClipping {@code true} to avoid clipping if possible.
7618 * @return this object for method chaining
7619 */
7620 public WearableExtender setHintAvoidBackgroundClipping(
7621 boolean hintAvoidBackgroundClipping) {
7622 setFlag(FLAG_HINT_AVOID_BACKGROUND_CLIPPING, hintAvoidBackgroundClipping);
7623 return this;
7624 }
7625
7626 /**
Griff Hazen9c5be4e2014-11-17 17:47:50 -08007627 * Get a hint that this notification's background should not be clipped if possible,
7628 * and should instead be resized to fully display on the screen, retaining the aspect
7629 * ratio of the image. This can be useful for images like barcodes or qr codes.
Griff Hazen5f2edfc2014-09-29 16:28:44 -07007630 * @return {@code true} if it's ok if the background is clipped on the screen, false
7631 * otherwise. The default value is {@code false} if this was never set.
7632 */
7633 public boolean getHintAvoidBackgroundClipping() {
7634 return (mFlags & FLAG_HINT_AVOID_BACKGROUND_CLIPPING) != 0;
7635 }
7636
7637 /**
7638 * Set a hint that the screen should remain on for at least this duration when
7639 * this notification is displayed on the screen.
7640 * @param timeout The requested screen timeout in milliseconds. Can also be either
7641 * {@link #SCREEN_TIMEOUT_SHORT} or {@link #SCREEN_TIMEOUT_LONG}.
7642 * @return this object for method chaining
7643 */
7644 public WearableExtender setHintScreenTimeout(int timeout) {
7645 mHintScreenTimeout = timeout;
7646 return this;
7647 }
7648
7649 /**
7650 * Get the duration, in milliseconds, that the screen should remain on for
7651 * when this notification is displayed.
7652 * @return the duration in milliseconds if > 0, or either one of the sentinel values
7653 * {@link #SCREEN_TIMEOUT_SHORT} or {@link #SCREEN_TIMEOUT_LONG}.
7654 */
7655 public int getHintScreenTimeout() {
7656 return mHintScreenTimeout;
7657 }
7658
Alex Hills9ab3a232016-04-05 14:54:56 -04007659 /**
Alex Hills4bcb06b2016-04-05 14:26:25 -04007660 * Set a hint that this notification's {@link BigPictureStyle} (if present) should be
7661 * converted to low-bit and displayed in ambient mode, especially useful for barcodes and
7662 * qr codes, as well as other simple black-and-white tickets.
7663 * @param hintAmbientBigPicture {@code true} to enable converstion and ambient.
7664 * @return this object for method chaining
7665 */
7666 public WearableExtender setHintAmbientBigPicture(boolean hintAmbientBigPicture) {
7667 setFlag(FLAG_BIG_PICTURE_AMBIENT, hintAmbientBigPicture);
7668 return this;
7669 }
7670
7671 /**
7672 * Get a hint that this notification's {@link BigPictureStyle} (if present) should be
7673 * converted to low-bit and displayed in ambient mode, especially useful for barcodes and
7674 * qr codes, as well as other simple black-and-white tickets.
7675 * @return {@code true} if it should be displayed in ambient, false otherwise
7676 * otherwise. The default value is {@code false} if this was never set.
7677 */
7678 public boolean getHintAmbientBigPicture() {
7679 return (mFlags & FLAG_BIG_PICTURE_AMBIENT) != 0;
7680 }
7681
7682 /**
Alex Hills9ab3a232016-04-05 14:54:56 -04007683 * Set a hint that this notification's content intent will launch an {@link Activity}
7684 * directly, telling the platform that it can generate the appropriate transitions.
7685 * @param hintContentIntentLaunchesActivity {@code true} if the content intent will launch
7686 * an activity and transitions should be generated, false otherwise.
7687 * @return this object for method chaining
7688 */
7689 public WearableExtender setHintContentIntentLaunchesActivity(
7690 boolean hintContentIntentLaunchesActivity) {
7691 setFlag(FLAG_HINT_CONTENT_INTENT_LAUNCHES_ACTIVITY, hintContentIntentLaunchesActivity);
7692 return this;
7693 }
7694
7695 /**
7696 * Get a hint that this notification's content intent will launch an {@link Activity}
7697 * directly, telling the platform that it can generate the appropriate transitions
7698 * @return {@code true} if the content intent will launch an activity and transitions should
7699 * be generated, false otherwise. The default value is {@code false} if this was never set.
7700 */
7701 public boolean getHintContentIntentLaunchesActivity() {
7702 return (mFlags & FLAG_HINT_CONTENT_INTENT_LAUNCHES_ACTIVITY) != 0;
7703 }
7704
Nadia Benbernou948627e2016-04-14 14:41:08 -04007705 /**
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04007706 * Sets the dismissal id for this notification. If a notification is posted with a
7707 * dismissal id, then when that notification is canceled, notifications on other wearables
7708 * and the paired Android phone having that same dismissal id will also be canceled. See
Nadia Benbernou948627e2016-04-14 14:41:08 -04007709 * <a href="{@docRoot}wear/notifications/index.html">Adding Wearable Features to
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04007710 * Notifications</a> for more information.
Nadia Benbernou948627e2016-04-14 14:41:08 -04007711 * @param dismissalId the dismissal id of the notification.
7712 * @return this object for method chaining
7713 */
7714 public WearableExtender setDismissalId(String dismissalId) {
7715 mDismissalId = dismissalId;
7716 return this;
7717 }
7718
7719 /**
7720 * Returns the dismissal id of the notification.
7721 * @return the dismissal id of the notification or null if it has not been set.
7722 */
7723 public String getDismissalId() {
7724 return mDismissalId;
7725 }
7726
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04007727 /**
7728 * Sets a bridge tag for this notification. A bridge tag can be set for notifications
7729 * posted from a phone to provide finer-grained control on what notifications are bridged
7730 * to wearables. See <a href="{@docRoot}wear/notifications/index.html">Adding Wearable
7731 * Features to Notifications</a> for more information.
7732 * @param bridgeTag the bridge tag of the notification.
7733 * @return this object for method chaining
7734 */
7735 public WearableExtender setBridgeTag(String bridgeTag) {
7736 mBridgeTag = bridgeTag;
7737 return this;
7738 }
7739
7740 /**
7741 * Returns the bridge tag of the notification.
7742 * @return the bridge tag or null if not present.
7743 */
7744 public String getBridgeTag() {
7745 return mBridgeTag;
7746 }
7747
Griff Hazen61a9e862014-05-22 16:05:19 -07007748 private void setFlag(int mask, boolean value) {
7749 if (value) {
7750 mFlags |= mask;
7751 } else {
7752 mFlags &= ~mask;
7753 }
7754 }
7755 }
7756
7757 /**
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08007758 * <p>Helper class to add Android Auto extensions to notifications. To create a notification
7759 * with car extensions:
7760 *
7761 * <ol>
7762 * <li>Create an {@link Notification.Builder}, setting any desired
7763 * properties.
7764 * <li>Create a {@link CarExtender}.
7765 * <li>Set car-specific properties using the {@code add} and {@code set} methods of
7766 * {@link CarExtender}.
7767 * <li>Call {@link Notification.Builder#extend(Notification.Extender)}
7768 * to apply the extensions to a notification.
7769 * </ol>
7770 *
7771 * <pre class="prettyprint">
7772 * Notification notification = new Notification.Builder(context)
7773 * ...
7774 * .extend(new CarExtender()
7775 * .set*(...))
7776 * .build();
7777 * </pre>
7778 *
7779 * <p>Car extensions can be accessed on an existing notification by using the
7780 * {@code CarExtender(Notification)} constructor, and then using the {@code get} methods
7781 * to access values.
7782 */
7783 public static final class CarExtender implements Extender {
7784 private static final String TAG = "CarExtender";
7785
7786 private static final String EXTRA_CAR_EXTENDER = "android.car.EXTENSIONS";
7787 private static final String EXTRA_LARGE_ICON = "large_icon";
7788 private static final String EXTRA_CONVERSATION = "car_conversation";
7789 private static final String EXTRA_COLOR = "app_color";
7790
7791 private Bitmap mLargeIcon;
7792 private UnreadConversation mUnreadConversation;
7793 private int mColor = Notification.COLOR_DEFAULT;
7794
7795 /**
7796 * Create a {@link CarExtender} with default options.
7797 */
7798 public CarExtender() {
7799 }
7800
7801 /**
7802 * Create a {@link CarExtender} from the CarExtender options of an existing Notification.
7803 *
7804 * @param notif The notification from which to copy options.
7805 */
7806 public CarExtender(Notification notif) {
7807 Bundle carBundle = notif.extras == null ?
7808 null : notif.extras.getBundle(EXTRA_CAR_EXTENDER);
7809 if (carBundle != null) {
7810 mLargeIcon = carBundle.getParcelable(EXTRA_LARGE_ICON);
7811 mColor = carBundle.getInt(EXTRA_COLOR, Notification.COLOR_DEFAULT);
7812
7813 Bundle b = carBundle.getBundle(EXTRA_CONVERSATION);
7814 mUnreadConversation = UnreadConversation.getUnreadConversationFromBundle(b);
7815 }
7816 }
7817
7818 /**
7819 * Apply car extensions to a notification that is being built. This is typically called by
7820 * the {@link Notification.Builder#extend(Notification.Extender)}
7821 * method of {@link Notification.Builder}.
7822 */
7823 @Override
7824 public Notification.Builder extend(Notification.Builder builder) {
7825 Bundle carExtensions = new Bundle();
7826
7827 if (mLargeIcon != null) {
7828 carExtensions.putParcelable(EXTRA_LARGE_ICON, mLargeIcon);
7829 }
7830 if (mColor != Notification.COLOR_DEFAULT) {
7831 carExtensions.putInt(EXTRA_COLOR, mColor);
7832 }
7833
7834 if (mUnreadConversation != null) {
7835 Bundle b = mUnreadConversation.getBundleForUnreadConversation();
7836 carExtensions.putBundle(EXTRA_CONVERSATION, b);
7837 }
7838
7839 builder.getExtras().putBundle(EXTRA_CAR_EXTENDER, carExtensions);
7840 return builder;
7841 }
7842
7843 /**
7844 * Sets the accent color to use when Android Auto presents the notification.
7845 *
7846 * Android Auto uses the color set with {@link Notification.Builder#setColor(int)}
7847 * to accent the displayed notification. However, not all colors are acceptable in an
7848 * automotive setting. This method can be used to override the color provided in the
7849 * notification in such a situation.
7850 */
Tor Norbye80756e32015-03-02 09:39:27 -08007851 public CarExtender setColor(@ColorInt int color) {
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08007852 mColor = color;
7853 return this;
7854 }
7855
7856 /**
7857 * Gets the accent color.
7858 *
Julia Reynoldsd9228f12015-10-20 10:37:27 -04007859 * @see #setColor
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08007860 */
Tor Norbye80756e32015-03-02 09:39:27 -08007861 @ColorInt
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08007862 public int getColor() {
7863 return mColor;
7864 }
7865
7866 /**
7867 * Sets the large icon of the car notification.
7868 *
7869 * If no large icon is set in the extender, Android Auto will display the icon
7870 * specified by {@link Notification.Builder#setLargeIcon(android.graphics.Bitmap)}
7871 *
7872 * @param largeIcon The large icon to use in the car notification.
7873 * @return This object for method chaining.
7874 */
7875 public CarExtender setLargeIcon(Bitmap largeIcon) {
7876 mLargeIcon = largeIcon;
7877 return this;
7878 }
7879
7880 /**
7881 * Gets the large icon used in this car notification, or null if no icon has been set.
7882 *
7883 * @return The large icon for the car notification.
7884 * @see CarExtender#setLargeIcon
7885 */
7886 public Bitmap getLargeIcon() {
7887 return mLargeIcon;
7888 }
7889
7890 /**
7891 * Sets the unread conversation in a message notification.
7892 *
7893 * @param unreadConversation The unread part of the conversation this notification conveys.
7894 * @return This object for method chaining.
7895 */
7896 public CarExtender setUnreadConversation(UnreadConversation unreadConversation) {
7897 mUnreadConversation = unreadConversation;
7898 return this;
7899 }
7900
7901 /**
7902 * Returns the unread conversation conveyed by this notification.
7903 * @see #setUnreadConversation(UnreadConversation)
7904 */
7905 public UnreadConversation getUnreadConversation() {
7906 return mUnreadConversation;
7907 }
7908
7909 /**
7910 * A class which holds the unread messages from a conversation.
7911 */
7912 public static class UnreadConversation {
7913 private static final String KEY_AUTHOR = "author";
7914 private static final String KEY_TEXT = "text";
7915 private static final String KEY_MESSAGES = "messages";
7916 private static final String KEY_REMOTE_INPUT = "remote_input";
7917 private static final String KEY_ON_REPLY = "on_reply";
7918 private static final String KEY_ON_READ = "on_read";
7919 private static final String KEY_PARTICIPANTS = "participants";
7920 private static final String KEY_TIMESTAMP = "timestamp";
7921
7922 private final String[] mMessages;
7923 private final RemoteInput mRemoteInput;
7924 private final PendingIntent mReplyPendingIntent;
7925 private final PendingIntent mReadPendingIntent;
7926 private final String[] mParticipants;
7927 private final long mLatestTimestamp;
7928
7929 UnreadConversation(String[] messages, RemoteInput remoteInput,
7930 PendingIntent replyPendingIntent, PendingIntent readPendingIntent,
7931 String[] participants, long latestTimestamp) {
7932 mMessages = messages;
7933 mRemoteInput = remoteInput;
7934 mReadPendingIntent = readPendingIntent;
7935 mReplyPendingIntent = replyPendingIntent;
7936 mParticipants = participants;
7937 mLatestTimestamp = latestTimestamp;
7938 }
7939
7940 /**
7941 * Gets the list of messages conveyed by this notification.
7942 */
7943 public String[] getMessages() {
7944 return mMessages;
7945 }
7946
7947 /**
7948 * Gets the remote input that will be used to convey the response to a message list, or
7949 * null if no such remote input exists.
7950 */
7951 public RemoteInput getRemoteInput() {
7952 return mRemoteInput;
7953 }
7954
7955 /**
7956 * Gets the pending intent that will be triggered when the user replies to this
7957 * notification.
7958 */
7959 public PendingIntent getReplyPendingIntent() {
7960 return mReplyPendingIntent;
7961 }
7962
7963 /**
7964 * Gets the pending intent that Android Auto will send after it reads aloud all messages
7965 * in this object's message list.
7966 */
7967 public PendingIntent getReadPendingIntent() {
7968 return mReadPendingIntent;
7969 }
7970
7971 /**
7972 * Gets the participants in the conversation.
7973 */
7974 public String[] getParticipants() {
7975 return mParticipants;
7976 }
7977
7978 /**
7979 * Gets the firs participant in the conversation.
7980 */
7981 public String getParticipant() {
7982 return mParticipants.length > 0 ? mParticipants[0] : null;
7983 }
7984
7985 /**
7986 * Gets the timestamp of the conversation.
7987 */
7988 public long getLatestTimestamp() {
7989 return mLatestTimestamp;
7990 }
7991
7992 Bundle getBundleForUnreadConversation() {
7993 Bundle b = new Bundle();
7994 String author = null;
7995 if (mParticipants != null && mParticipants.length > 1) {
7996 author = mParticipants[0];
7997 }
7998 Parcelable[] messages = new Parcelable[mMessages.length];
7999 for (int i = 0; i < messages.length; i++) {
8000 Bundle m = new Bundle();
8001 m.putString(KEY_TEXT, mMessages[i]);
8002 m.putString(KEY_AUTHOR, author);
8003 messages[i] = m;
8004 }
8005 b.putParcelableArray(KEY_MESSAGES, messages);
8006 if (mRemoteInput != null) {
8007 b.putParcelable(KEY_REMOTE_INPUT, mRemoteInput);
8008 }
8009 b.putParcelable(KEY_ON_REPLY, mReplyPendingIntent);
8010 b.putParcelable(KEY_ON_READ, mReadPendingIntent);
8011 b.putStringArray(KEY_PARTICIPANTS, mParticipants);
8012 b.putLong(KEY_TIMESTAMP, mLatestTimestamp);
8013 return b;
8014 }
8015
8016 static UnreadConversation getUnreadConversationFromBundle(Bundle b) {
8017 if (b == null) {
8018 return null;
8019 }
8020 Parcelable[] parcelableMessages = b.getParcelableArray(KEY_MESSAGES);
8021 String[] messages = null;
8022 if (parcelableMessages != null) {
8023 String[] tmp = new String[parcelableMessages.length];
8024 boolean success = true;
8025 for (int i = 0; i < tmp.length; i++) {
8026 if (!(parcelableMessages[i] instanceof Bundle)) {
8027 success = false;
8028 break;
8029 }
8030 tmp[i] = ((Bundle) parcelableMessages[i]).getString(KEY_TEXT);
8031 if (tmp[i] == null) {
8032 success = false;
8033 break;
8034 }
8035 }
8036 if (success) {
8037 messages = tmp;
8038 } else {
8039 return null;
8040 }
8041 }
8042
8043 PendingIntent onRead = b.getParcelable(KEY_ON_READ);
8044 PendingIntent onReply = b.getParcelable(KEY_ON_REPLY);
8045
8046 RemoteInput remoteInput = b.getParcelable(KEY_REMOTE_INPUT);
8047
8048 String[] participants = b.getStringArray(KEY_PARTICIPANTS);
8049 if (participants == null || participants.length != 1) {
8050 return null;
8051 }
8052
8053 return new UnreadConversation(messages,
8054 remoteInput,
8055 onReply,
8056 onRead,
8057 participants, b.getLong(KEY_TIMESTAMP));
8058 }
8059 };
8060
8061 /**
8062 * Builder class for {@link CarExtender.UnreadConversation} objects.
8063 */
8064 public static class Builder {
8065 private final List<String> mMessages = new ArrayList<String>();
8066 private final String mParticipant;
8067 private RemoteInput mRemoteInput;
8068 private PendingIntent mReadPendingIntent;
8069 private PendingIntent mReplyPendingIntent;
8070 private long mLatestTimestamp;
8071
8072 /**
8073 * Constructs a new builder for {@link CarExtender.UnreadConversation}.
8074 *
8075 * @param name The name of the other participant in the conversation.
8076 */
8077 public Builder(String name) {
8078 mParticipant = name;
8079 }
8080
8081 /**
8082 * Appends a new unread message to the list of messages for this conversation.
8083 *
8084 * The messages should be added from oldest to newest.
8085 *
8086 * @param message The text of the new unread message.
8087 * @return This object for method chaining.
8088 */
8089 public Builder addMessage(String message) {
8090 mMessages.add(message);
8091 return this;
8092 }
8093
8094 /**
8095 * Sets the pending intent and remote input which will convey the reply to this
8096 * notification.
8097 *
8098 * @param pendingIntent The pending intent which will be triggered on a reply.
8099 * @param remoteInput The remote input parcelable which will carry the reply.
8100 * @return This object for method chaining.
8101 *
8102 * @see CarExtender.UnreadConversation#getRemoteInput
8103 * @see CarExtender.UnreadConversation#getReplyPendingIntent
8104 */
8105 public Builder setReplyAction(
8106 PendingIntent pendingIntent, RemoteInput remoteInput) {
8107 mRemoteInput = remoteInput;
8108 mReplyPendingIntent = pendingIntent;
8109
8110 return this;
8111 }
8112
8113 /**
8114 * Sets the pending intent that will be sent once the messages in this notification
8115 * are read.
8116 *
8117 * @param pendingIntent The pending intent to use.
8118 * @return This object for method chaining.
8119 */
8120 public Builder setReadPendingIntent(PendingIntent pendingIntent) {
8121 mReadPendingIntent = pendingIntent;
8122 return this;
8123 }
8124
8125 /**
8126 * Sets the timestamp of the most recent message in an unread conversation.
8127 *
8128 * If a messaging notification has been posted by your application and has not
8129 * yet been cancelled, posting a later notification with the same id and tag
8130 * but without a newer timestamp may result in Android Auto not displaying a
8131 * heads up notification for the later notification.
8132 *
8133 * @param timestamp The timestamp of the most recent message in the conversation.
8134 * @return This object for method chaining.
8135 */
8136 public Builder setLatestTimestamp(long timestamp) {
8137 mLatestTimestamp = timestamp;
8138 return this;
8139 }
8140
8141 /**
8142 * Builds a new unread conversation object.
8143 *
8144 * @return The new unread conversation object.
8145 */
8146 public UnreadConversation build() {
8147 String[] messages = mMessages.toArray(new String[mMessages.size()]);
8148 String[] participants = { mParticipant };
8149 return new UnreadConversation(messages, mRemoteInput, mReplyPendingIntent,
8150 mReadPendingIntent, participants, mLatestTimestamp);
8151 }
8152 }
8153 }
8154
8155 /**
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08008156 * <p>Helper class to add Android TV extensions to notifications. To create a notification
8157 * with a TV extension:
8158 *
8159 * <ol>
8160 * <li>Create an {@link Notification.Builder}, setting any desired properties.
8161 * <li>Create a {@link TvExtender}.
8162 * <li>Set TV-specific properties using the {@code set} methods of
8163 * {@link TvExtender}.
8164 * <li>Call {@link Notification.Builder#extend(Notification.Extender)}
8165 * to apply the extension to a notification.
8166 * </ol>
8167 *
8168 * <pre class="prettyprint">
8169 * Notification notification = new Notification.Builder(context)
8170 * ...
8171 * .extend(new TvExtender()
8172 * .set*(...))
8173 * .build();
8174 * </pre>
8175 *
8176 * <p>TV extensions can be accessed on an existing notification by using the
8177 * {@code TvExtender(Notification)} constructor, and then using the {@code get} methods
8178 * to access values.
8179 *
8180 * @hide
8181 */
8182 @SystemApi
8183 public static final class TvExtender implements Extender {
8184 private static final String TAG = "TvExtender";
8185
8186 private static final String EXTRA_TV_EXTENDER = "android.tv.EXTENSIONS";
8187 private static final String EXTRA_FLAGS = "flags";
8188 private static final String EXTRA_CONTENT_INTENT = "content_intent";
8189 private static final String EXTRA_DELETE_INTENT = "delete_intent";
Dmitri Plotnikovb8a04ee2017-01-20 10:39:22 -08008190 private static final String EXTRA_CHANNEL_ID = "channel_id";
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08008191
8192 // Flags bitwise-ored to mFlags
8193 private static final int FLAG_AVAILABLE_ON_TV = 0x1;
8194
8195 private int mFlags;
Dmitri Plotnikovb8a04ee2017-01-20 10:39:22 -08008196 private String mChannelId;
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08008197 private PendingIntent mContentIntent;
8198 private PendingIntent mDeleteIntent;
8199
8200 /**
8201 * Create a {@link TvExtender} with default options.
8202 */
8203 public TvExtender() {
8204 mFlags = FLAG_AVAILABLE_ON_TV;
8205 }
8206
8207 /**
8208 * Create a {@link TvExtender} from the TvExtender options of an existing Notification.
8209 *
8210 * @param notif The notification from which to copy options.
8211 */
8212 public TvExtender(Notification notif) {
8213 Bundle bundle = notif.extras == null ?
8214 null : notif.extras.getBundle(EXTRA_TV_EXTENDER);
8215 if (bundle != null) {
8216 mFlags = bundle.getInt(EXTRA_FLAGS);
Dmitri Plotnikovb8a04ee2017-01-20 10:39:22 -08008217 mChannelId = bundle.getString(EXTRA_CHANNEL_ID);
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08008218 mContentIntent = bundle.getParcelable(EXTRA_CONTENT_INTENT);
8219 mDeleteIntent = bundle.getParcelable(EXTRA_DELETE_INTENT);
8220 }
8221 }
8222
8223 /**
8224 * Apply a TV extension to a notification that is being built. This is typically called by
8225 * the {@link Notification.Builder#extend(Notification.Extender)}
8226 * method of {@link Notification.Builder}.
8227 */
8228 @Override
8229 public Notification.Builder extend(Notification.Builder builder) {
8230 Bundle bundle = new Bundle();
8231
8232 bundle.putInt(EXTRA_FLAGS, mFlags);
Dmitri Plotnikovb8a04ee2017-01-20 10:39:22 -08008233 bundle.putString(EXTRA_CHANNEL_ID, mChannelId);
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08008234 if (mContentIntent != null) {
8235 bundle.putParcelable(EXTRA_CONTENT_INTENT, mContentIntent);
8236 }
8237
8238 if (mDeleteIntent != null) {
8239 bundle.putParcelable(EXTRA_DELETE_INTENT, mDeleteIntent);
8240 }
8241
8242 builder.getExtras().putBundle(EXTRA_TV_EXTENDER, bundle);
8243 return builder;
8244 }
8245
8246 /**
8247 * Returns true if this notification should be shown on TV. This method return true
8248 * if the notification was extended with a TvExtender.
8249 */
8250 public boolean isAvailableOnTv() {
8251 return (mFlags & FLAG_AVAILABLE_ON_TV) != 0;
8252 }
8253
8254 /**
Dmitri Plotnikovb8a04ee2017-01-20 10:39:22 -08008255 * Specifies the channel the notification should be delivered on when shown on TV.
8256 * It can be different from the channel that the notification is delivered to when
8257 * posting on a non-TV device.
8258 */
8259 public TvExtender setChannel(String channelId) {
8260 mChannelId = channelId;
8261 return this;
8262 }
8263
8264 /**
Julia Reynoldsbad42972017-04-25 13:52:49 -04008265 * Specifies the channel the notification should be delivered on when shown on TV.
8266 * It can be different from the channel that the notification is delivered to when
8267 * posting on a non-TV device.
8268 */
8269 public TvExtender setChannelId(String channelId) {
8270 mChannelId = channelId;
8271 return this;
8272 }
8273
Jeff Sharkey000ce802017-04-29 13:13:27 -06008274 /** @removed */
8275 @Deprecated
Dmitri Plotnikovb8a04ee2017-01-20 10:39:22 -08008276 public String getChannel() {
8277 return mChannelId;
8278 }
8279
8280 /**
Julia Reynoldsbad42972017-04-25 13:52:49 -04008281 * Returns the id of the channel this notification posts to on TV.
8282 */
8283 public String getChannelId() {
8284 return mChannelId;
8285 }
8286
8287 /**
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08008288 * Supplies a {@link PendingIntent} to be sent when the notification is selected on TV.
8289 * If provided, it is used instead of the content intent specified
8290 * at the level of Notification.
8291 */
8292 public TvExtender setContentIntent(PendingIntent intent) {
8293 mContentIntent = intent;
8294 return this;
8295 }
8296
8297 /**
8298 * Returns the TV-specific content intent. If this method returns null, the
8299 * main content intent on the notification should be used.
8300 *
8301 * @see {@link Notification#contentIntent}
8302 */
8303 public PendingIntent getContentIntent() {
8304 return mContentIntent;
8305 }
8306
8307 /**
8308 * Supplies a {@link PendingIntent} to send when the notification is cleared explicitly
8309 * by the user on TV. If provided, it is used instead of the delete intent specified
8310 * at the level of Notification.
8311 */
8312 public TvExtender setDeleteIntent(PendingIntent intent) {
8313 mDeleteIntent = intent;
8314 return this;
8315 }
8316
8317 /**
8318 * Returns the TV-specific delete intent. If this method returns null, the
8319 * main delete intent on the notification should be used.
8320 *
8321 * @see {@link Notification#deleteIntent}
8322 */
8323 public PendingIntent getDeleteIntent() {
8324 return mDeleteIntent;
8325 }
8326 }
8327
8328 /**
Griff Hazen61a9e862014-05-22 16:05:19 -07008329 * Get an array of Notification objects from a parcelable array bundle field.
8330 * Update the bundle to have a typed array so fetches in the future don't need
8331 * to do an array copy.
8332 */
8333 private static Notification[] getNotificationArrayFromBundle(Bundle bundle, String key) {
8334 Parcelable[] array = bundle.getParcelableArray(key);
8335 if (array instanceof Notification[] || array == null) {
8336 return (Notification[]) array;
8337 }
8338 Notification[] typedArray = Arrays.copyOf(array, array.length,
8339 Notification[].class);
8340 bundle.putParcelableArray(key, typedArray);
8341 return typedArray;
8342 }
Christoph Studer4600f9b2014-07-22 22:44:43 +02008343
8344 private static class BuilderRemoteViews extends RemoteViews {
8345 public BuilderRemoteViews(Parcel parcel) {
8346 super(parcel);
8347 }
8348
Kenny Guy77320062014-08-27 21:37:15 +01008349 public BuilderRemoteViews(ApplicationInfo appInfo, int layoutId) {
8350 super(appInfo, layoutId);
Christoph Studer4600f9b2014-07-22 22:44:43 +02008351 }
8352
8353 @Override
8354 public BuilderRemoteViews clone() {
8355 Parcel p = Parcel.obtain();
8356 writeToParcel(p, 0);
8357 p.setDataPosition(0);
8358 BuilderRemoteViews brv = new BuilderRemoteViews(p);
8359 p.recycle();
8360 return brv;
8361 }
8362 }
Adrian Roos70d7aa32017-01-11 15:39:06 -08008363
8364 private static class StandardTemplateParams {
8365 boolean hasProgress = true;
8366 boolean ambient = false;
8367 CharSequence title;
8368 CharSequence text;
8369
8370 final StandardTemplateParams reset() {
8371 hasProgress = true;
8372 ambient = false;
8373 title = null;
8374 text = null;
8375 return this;
8376 }
8377
8378 final StandardTemplateParams hasProgress(boolean hasProgress) {
8379 this.hasProgress = hasProgress;
8380 return this;
8381 }
8382
8383 final StandardTemplateParams title(CharSequence title) {
8384 this.title = title;
8385 return this;
8386 }
8387
8388 final StandardTemplateParams text(CharSequence text) {
8389 this.text = text;
8390 return this;
8391 }
8392
8393 final StandardTemplateParams ambient(boolean ambient) {
Adrian Roos0bc3f6a2017-03-06 11:54:05 -08008394 Preconditions.checkState(title == null && text == null, "must set ambient before text");
Adrian Roos70d7aa32017-01-11 15:39:06 -08008395 this.ambient = ambient;
8396 return this;
8397 }
8398
8399 final StandardTemplateParams fillTextsFrom(Builder b) {
8400 Bundle extras = b.mN.extras;
Adrian Roos0bc3f6a2017-03-06 11:54:05 -08008401 title = b.processLegacyText(extras.getCharSequence(EXTRA_TITLE), ambient);
8402 text = b.processLegacyText(extras.getCharSequence(EXTRA_TEXT), ambient);
Adrian Roos70d7aa32017-01-11 15:39:06 -08008403 return this;
8404 }
8405 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08008406}