blob: bf3d9c2355099035322e44cc1e4b6e7592075376 [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
Julia Reynolds4db59552017-06-30 13:34:01 -0400564 /**
565 * @hide
566 */
567 public static final int FLAG_CAN_COLORIZE = 0x00000800;
568
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800569 public int flags;
570
Tor Norbyed9273d62013-05-30 15:59:53 -0700571 /** @hide */
572 @IntDef({PRIORITY_DEFAULT,PRIORITY_LOW,PRIORITY_MIN,PRIORITY_HIGH,PRIORITY_MAX})
573 @Retention(RetentionPolicy.SOURCE)
574 public @interface Priority {}
575
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800576 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500577 * Default notification {@link #priority}. If your application does not prioritize its own
578 * notifications, use this value for all notifications.
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500579 *
580 * @deprecated use {@link NotificationManager#IMPORTANCE_DEFAULT} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500581 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500582 @Deprecated
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500583 public static final int PRIORITY_DEFAULT = 0;
584
585 /**
586 * Lower {@link #priority}, for items that are less important. The UI may choose to show these
587 * items smaller, or at a different position in the list, compared with your app's
588 * {@link #PRIORITY_DEFAULT} items.
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500589 *
590 * @deprecated use {@link NotificationManager#IMPORTANCE_LOW} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500591 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500592 @Deprecated
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500593 public static final int PRIORITY_LOW = -1;
594
595 /**
596 * Lowest {@link #priority}; these items might not be shown to the user except under special
597 * circumstances, such as detailed notification logs.
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500598 *
599 * @deprecated use {@link NotificationManager#IMPORTANCE_MIN} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500600 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500601 @Deprecated
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500602 public static final int PRIORITY_MIN = -2;
603
604 /**
605 * Higher {@link #priority}, for more important notifications or alerts. The UI may choose to
606 * show these items larger, or at a different position in notification lists, compared with
607 * your app's {@link #PRIORITY_DEFAULT} items.
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500608 *
609 * @deprecated use {@link NotificationManager#IMPORTANCE_HIGH} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500610 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500611 @Deprecated
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500612 public static final int PRIORITY_HIGH = 1;
613
614 /**
615 * Highest {@link #priority}, for your application's most important items that require the
616 * user's prompt attention or input.
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500617 *
618 * @deprecated use {@link NotificationManager#IMPORTANCE_HIGH} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500619 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500620 @Deprecated
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500621 public static final int PRIORITY_MAX = 2;
622
623 /**
624 * Relative priority for this notification.
Joe Malin8d40d042012-11-05 11:36:40 -0800625 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500626 * Priority is an indication of how much of the user's valuable attention should be consumed by
627 * this notification. Low-priority notifications may be hidden from the user in certain
628 * situations, while the user might be interrupted for a higher-priority notification. The
Daniel Sandler6738eee2012-11-16 12:03:32 -0500629 * system will make a determination about how to interpret this priority when presenting
630 * the notification.
Chris Wren47c20a12014-06-18 17:27:29 -0400631 *
632 * <p>
633 * A notification that is at least {@link #PRIORITY_HIGH} is more likely to be presented
634 * as a heads-up notification.
635 * </p>
636 *
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500637 * @deprecated use {@link NotificationChannel#getImportance()} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500638 */
Tor Norbyed9273d62013-05-30 15:59:53 -0700639 @Priority
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500640 @Deprecated
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500641 public int priority;
Joe Malin8d40d042012-11-05 11:36:40 -0800642
Dan Sandler26e81cf2014-05-06 10:01:27 -0400643 /**
644 * Accent color (an ARGB integer like the constants in {@link android.graphics.Color})
645 * to be applied by the standard Style templates when presenting this notification.
646 *
647 * The current template design constructs a colorful header image by overlaying the
648 * {@link #icon} image (stenciled in white) atop a field of this color. Alpha components are
649 * ignored.
650 */
Tor Norbye80756e32015-03-02 09:39:27 -0800651 @ColorInt
Dan Sandler26e81cf2014-05-06 10:01:27 -0400652 public int color = COLOR_DEFAULT;
653
654 /**
655 * Special value of {@link #color} telling the system not to decorate this notification with
656 * any special color but instead use default colors when presenting this notification.
657 */
Tor Norbye80756e32015-03-02 09:39:27 -0800658 @ColorInt
Dan Sandler26e81cf2014-05-06 10:01:27 -0400659 public static final int COLOR_DEFAULT = 0; // AKA Color.TRANSPARENT
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600660
661 /**
Adrian Roos4ff3b122016-02-01 12:26:13 -0800662 * Special value of {@link #color} used as a place holder for an invalid color.
Selim Cinek99104832017-01-25 14:47:33 -0800663 * @hide
Adrian Roos4ff3b122016-02-01 12:26:13 -0800664 */
665 @ColorInt
Selim Cinek99104832017-01-25 14:47:33 -0800666 public static final int COLOR_INVALID = 1;
Adrian Roos4ff3b122016-02-01 12:26:13 -0800667
668 /**
Adrian Roosc1a80b02016-04-05 14:54:55 -0700669 * Sphere of visibility of this notification, which affects how and when the SystemUI reveals
670 * the notification's presence and contents in untrusted situations (namely, on the secure
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600671 * lockscreen).
672 *
673 * The default level, {@link #VISIBILITY_PRIVATE}, behaves exactly as notifications have always
674 * done on Android: The notification's {@link #icon} and {@link #tickerText} (if available) are
675 * shown in all situations, but the contents are only available if the device is unlocked for
676 * the appropriate user.
677 *
678 * A more permissive policy can be expressed by {@link #VISIBILITY_PUBLIC}; such a notification
679 * can be read even in an "insecure" context (that is, above a secure lockscreen).
680 * To modify the public version of this notification—for example, to redact some portions—see
681 * {@link Builder#setPublicVersion(Notification)}.
682 *
683 * Finally, a notification can be made {@link #VISIBILITY_SECRET}, which will suppress its icon
684 * and ticker until the user has bypassed the lockscreen.
685 */
Jeff Sharkey30e06bb2017-04-24 11:18:03 -0600686 public @Visibility int visibility;
687
688 /** @hide */
689 @IntDef(prefix = { "VISIBILITY_" }, value = {
690 VISIBILITY_PUBLIC,
691 VISIBILITY_PRIVATE,
692 VISIBILITY_SECRET,
693 })
694 @Retention(RetentionPolicy.SOURCE)
695 public @interface Visibility {}
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600696
Griff Hazenfc3922d2014-08-20 11:56:44 -0700697 /**
698 * Notification visibility: Show this notification in its entirety on all lockscreens.
699 *
700 * {@see #visibility}
701 */
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600702 public static final int VISIBILITY_PUBLIC = 1;
Griff Hazenfc3922d2014-08-20 11:56:44 -0700703
704 /**
705 * Notification visibility: Show this notification on all lockscreens, but conceal sensitive or
706 * private information on secure lockscreens.
707 *
708 * {@see #visibility}
709 */
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600710 public static final int VISIBILITY_PRIVATE = 0;
Griff Hazenfc3922d2014-08-20 11:56:44 -0700711
712 /**
713 * Notification visibility: Do not reveal any part of this notification on a secure lockscreen.
714 *
715 * {@see #visibility}
716 */
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600717 public static final int VISIBILITY_SECRET = -1;
718
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500719 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400720 * Notification category: incoming call (voice or video) or similar synchronous communication request.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500721 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400722 public static final String CATEGORY_CALL = "call";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500723
724 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400725 * Notification category: incoming direct message (SMS, instant message, etc.).
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500726 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400727 public static final String CATEGORY_MESSAGE = "msg";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500728
729 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400730 * Notification category: asynchronous bulk message (email).
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500731 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400732 public static final String CATEGORY_EMAIL = "email";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500733
734 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400735 * Notification category: calendar event.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500736 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400737 public static final String CATEGORY_EVENT = "event";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500738
739 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400740 * Notification category: promotion or advertisement.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500741 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400742 public static final String CATEGORY_PROMO = "promo";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500743
744 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400745 * Notification category: alarm or timer.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500746 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400747 public static final String CATEGORY_ALARM = "alarm";
748
749 /**
750 * Notification category: progress of a long-running background operation.
751 */
752 public static final String CATEGORY_PROGRESS = "progress";
753
754 /**
755 * Notification category: social network or sharing update.
756 */
757 public static final String CATEGORY_SOCIAL = "social";
758
759 /**
760 * Notification category: error in background operation or authentication status.
761 */
762 public static final String CATEGORY_ERROR = "err";
763
764 /**
765 * Notification category: media transport control for playback.
766 */
767 public static final String CATEGORY_TRANSPORT = "transport";
768
769 /**
770 * Notification category: system or device status update. Reserved for system use.
771 */
772 public static final String CATEGORY_SYSTEM = "sys";
773
774 /**
775 * Notification category: indication of running background service.
776 */
777 public static final String CATEGORY_SERVICE = "service";
778
779 /**
John Spurlock0a69c8c2014-03-21 13:30:57 -0400780 * Notification category: a specific, timely recommendation for a single thing.
781 * For example, a news app might want to recommend a news story it believes the user will
782 * want to read next.
783 */
784 public static final String CATEGORY_RECOMMENDATION = "recommendation";
785
786 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400787 * Notification category: ongoing information about device or contextual status.
788 */
789 public static final String CATEGORY_STATUS = "status";
790
791 /**
John Spurlock24d3dad2015-04-02 12:24:02 -0400792 * Notification category: user-scheduled reminder.
793 */
794 public static final String CATEGORY_REMINDER = "reminder";
795
796 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400797 * One of the predefined notification categories (see the <code>CATEGORY_*</code> constants)
798 * that best describes this Notification. May be used by the system for ranking and filtering.
799 */
800 public String category;
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500801
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700802 private String mGroupKey;
803
804 /**
805 * Get the key used to group this notification into a cluster or stack
806 * with other notifications on devices which support such rendering.
807 */
808 public String getGroup() {
809 return mGroupKey;
810 }
811
812 private String mSortKey;
813
814 /**
815 * Get a sort key that orders this notification among other notifications from the
816 * same package. This can be useful if an external sort was already applied and an app
817 * would like to preserve this. Notifications will be sorted lexicographically using this
818 * value, although providing different priorities in addition to providing sort key may
819 * cause this value to be ignored.
820 *
821 * <p>This sort key can also be used to order members of a notification group. See
822 * {@link Builder#setGroup}.
823 *
824 * @see String#compareTo(String)
825 */
826 public String getSortKey() {
827 return mSortKey;
828 }
829
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500830 /**
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400831 * Additional semantic data to be carried around with this Notification.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400832 * <p>
833 * The extras keys defined here are intended to capture the original inputs to {@link Builder}
834 * APIs, and are intended to be used by
835 * {@link android.service.notification.NotificationListenerService} implementations to extract
836 * detailed information from notification objects.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500837 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400838 public Bundle extras = new Bundle();
839
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400840 /**
Felipe Lemedd85da62016-06-28 11:29:54 -0700841 * All pending intents in the notification as the system needs to be able to access them but
842 * touching the extras bundle in the system process is not safe because the bundle may contain
Svet Ganovddb94882016-06-23 19:55:24 -0700843 * custom parcelable objects.
844 *
845 * @hide
846 */
Felipe Lemedd85da62016-06-28 11:29:54 -0700847 public ArraySet<PendingIntent> allPendingIntents;
Svet Ganovddb94882016-06-23 19:55:24 -0700848
849 /**
Dianne Hackborn98305522017-05-05 17:53:53 -0700850 * Token identifying the notification that is applying doze/bgcheck whitelisting to the
851 * pending intents inside of it, so only those will get the behavior.
852 *
853 * @hide
854 */
855 static public IBinder whitelistToken;
856
857 /**
858 * Must be set by a process to start associating tokens with Notification objects
859 * coming in to it. This is set by NotificationManagerService.
860 *
861 * @hide
862 */
863 static public IBinder processWhitelistToken;
864
865 /**
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400866 * {@link #extras} key: this is the title of the notification,
867 * as supplied to {@link Builder#setContentTitle(CharSequence)}.
868 */
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -0500869 public static final String EXTRA_TITLE = "android.title";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400870
871 /**
872 * {@link #extras} key: this is the title of the notification when shown in expanded form,
873 * e.g. as supplied to {@link BigTextStyle#setBigContentTitle(CharSequence)}.
874 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400875 public static final String EXTRA_TITLE_BIG = EXTRA_TITLE + ".big";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400876
877 /**
878 * {@link #extras} key: this is the main text payload, as supplied to
879 * {@link Builder#setContentText(CharSequence)}.
880 */
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -0500881 public static final String EXTRA_TEXT = "android.text";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400882
883 /**
884 * {@link #extras} key: this is a third line of text, as supplied to
885 * {@link Builder#setSubText(CharSequence)}.
886 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400887 public static final String EXTRA_SUB_TEXT = "android.subText";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400888
889 /**
Adrian Roose458aa82015-12-08 16:17:19 -0800890 * {@link #extras} key: this is the remote input history, as supplied to
891 * {@link Builder#setRemoteInputHistory(CharSequence[])}.
Adrian Roos005e7742016-04-13 15:02:20 -0700892 *
893 * Apps can fill this through {@link Builder#setRemoteInputHistory(CharSequence[])}
894 * with the most recent inputs that have been sent through a {@link RemoteInput} of this
895 * Notification and are expected to clear it once the it is no longer relevant (e.g. for chat
896 * notifications once the other party has responded).
897 *
898 * The extra with this key is of type CharSequence[] and contains the most recent entry at
899 * the 0 index, the second most recent at the 1 index, etc.
900 *
901 * @see Builder#setRemoteInputHistory(CharSequence[])
Adrian Roose458aa82015-12-08 16:17:19 -0800902 */
903 public static final String EXTRA_REMOTE_INPUT_HISTORY = "android.remoteInputHistory";
904
905 /**
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400906 * {@link #extras} key: this is a small piece of additional text as supplied to
907 * {@link Builder#setContentInfo(CharSequence)}.
908 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400909 public static final String EXTRA_INFO_TEXT = "android.infoText";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400910
911 /**
912 * {@link #extras} key: this is a line of summary information intended to be shown
913 * alongside expanded notifications, as supplied to (e.g.)
914 * {@link BigTextStyle#setSummaryText(CharSequence)}.
915 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400916 public static final String EXTRA_SUMMARY_TEXT = "android.summaryText";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400917
918 /**
Christoph Studer4600f9b2014-07-22 22:44:43 +0200919 * {@link #extras} key: this is the longer text shown in the big form of a
920 * {@link BigTextStyle} notification, as supplied to
921 * {@link BigTextStyle#bigText(CharSequence)}.
922 */
923 public static final String EXTRA_BIG_TEXT = "android.bigText";
924
925 /**
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400926 * {@link #extras} key: this is the resource ID of the notification's main small icon, as
927 * supplied to {@link Builder#setSmallIcon(int)}.
Julia Reynoldse071abd2017-03-22 10:52:11 -0400928 *
929 * @deprecated Use {@link #getSmallIcon()}, which supports a wider variety of icon sources.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400930 */
Julia Reynoldse071abd2017-03-22 10:52:11 -0400931 @Deprecated
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -0500932 public static final String EXTRA_SMALL_ICON = "android.icon";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400933
934 /**
935 * {@link #extras} key: this is a bitmap to be used instead of the small icon when showing the
936 * notification payload, as
937 * supplied to {@link Builder#setLargeIcon(android.graphics.Bitmap)}.
Julia Reynoldse071abd2017-03-22 10:52:11 -0400938 *
939 * @deprecated Use {@link #getLargeIcon()}, which supports a wider variety of icon sources.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400940 */
Julia Reynoldse071abd2017-03-22 10:52:11 -0400941 @Deprecated
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400942 public static final String EXTRA_LARGE_ICON = "android.largeIcon";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400943
944 /**
945 * {@link #extras} key: this is a bitmap to be used instead of the one from
946 * {@link Builder#setLargeIcon(android.graphics.Bitmap)} when the notification is
947 * shown in its expanded form, as supplied to
948 * {@link BigPictureStyle#bigLargeIcon(android.graphics.Bitmap)}.
949 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400950 public static final String EXTRA_LARGE_ICON_BIG = EXTRA_LARGE_ICON + ".big";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400951
952 /**
953 * {@link #extras} key: this is the progress value supplied to
954 * {@link Builder#setProgress(int, int, boolean)}.
955 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400956 public static final String EXTRA_PROGRESS = "android.progress";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400957
958 /**
959 * {@link #extras} key: this is the maximum value supplied to
960 * {@link Builder#setProgress(int, int, boolean)}.
961 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400962 public static final String EXTRA_PROGRESS_MAX = "android.progressMax";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400963
964 /**
965 * {@link #extras} key: whether the progress bar is indeterminate, supplied to
966 * {@link Builder#setProgress(int, int, boolean)}.
967 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400968 public static final String EXTRA_PROGRESS_INDETERMINATE = "android.progressIndeterminate";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400969
970 /**
971 * {@link #extras} key: whether {@link #when} should be shown as a count-up timer (specifically
972 * a {@link android.widget.Chronometer}) instead of a timestamp, as supplied to
973 * {@link Builder#setUsesChronometer(boolean)}.
974 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400975 public static final String EXTRA_SHOW_CHRONOMETER = "android.showChronometer";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400976
977 /**
Selim Cinek81c23aa2016-02-25 16:23:13 -0800978 * {@link #extras} key: whether the chronometer set on the notification should count down
979 * instead of counting up. Is only relevant if key {@link #EXTRA_SHOW_CHRONOMETER} is present.
Adrian Roos96b7e202016-05-17 13:50:38 -0700980 * This extra is a boolean. The default is false.
Selim Cinek81c23aa2016-02-25 16:23:13 -0800981 */
Adrian Roos96b7e202016-05-17 13:50:38 -0700982 public static final String EXTRA_CHRONOMETER_COUNT_DOWN = "android.chronometerCountDown";
Selim Cinek81c23aa2016-02-25 16:23:13 -0800983
984 /**
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400985 * {@link #extras} key: whether {@link #when} should be shown,
986 * as supplied to {@link Builder#setShowWhen(boolean)}.
987 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400988 public static final String EXTRA_SHOW_WHEN = "android.showWhen";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400989
990 /**
991 * {@link #extras} key: this is a bitmap to be shown in {@link BigPictureStyle} expanded
992 * notifications, supplied to {@link BigPictureStyle#bigPicture(android.graphics.Bitmap)}.
993 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400994 public static final String EXTRA_PICTURE = "android.picture";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400995
996 /**
997 * {@link #extras} key: An array of CharSequences to show in {@link InboxStyle} expanded
998 * notifications, each of which was supplied to {@link InboxStyle#addLine(CharSequence)}.
999 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -04001000 public static final String EXTRA_TEXT_LINES = "android.textLines";
Dan Sandler842dd772014-05-15 09:36:47 -04001001
1002 /**
1003 * {@link #extras} key: A string representing the name of the specific
1004 * {@link android.app.Notification.Style} used to create this notification.
1005 */
Chris Wren91ad5632013-06-05 15:05:57 -04001006 public static final String EXTRA_TEMPLATE = "android.template";
Daniel Sandlerf45564e2013-04-15 15:05:08 -04001007
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001008 /**
Chris Wrene6c48932014-09-29 17:19:27 -04001009 * {@link #extras} key: A String array containing the people that this notification relates to,
1010 * each of which was supplied to {@link Builder#addPerson(String)}.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001011 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -04001012 public static final String EXTRA_PEOPLE = "android.people";
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001013
1014 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -04001015 * Allow certain system-generated notifications to appear before the device is provisioned.
1016 * Only available to notifications coming from the android package.
1017 * @hide
1018 */
Maurice Lam96c10032017-03-29 15:42:38 -07001019 @SystemApi
Jeff Sharkeyd86b8fe2017-06-02 17:36:26 -06001020 @RequiresPermission(android.Manifest.permission.NOTIFICATION_DURING_SETUP)
John Spurlockfd7f1e02014-03-18 16:41:57 -04001021 public static final String EXTRA_ALLOW_DURING_SETUP = "android.allowDuringSetup";
1022
1023 /**
Jose Limae9e3b3b2014-05-18 23:44:50 -07001024 * {@link #extras} key: A
1025 * {@link android.content.ContentUris content URI} pointing to an image that can be displayed
1026 * in the background when the notification is selected. The URI must point to an image stream
1027 * suitable for passing into
1028 * {@link android.graphics.BitmapFactory#decodeStream(java.io.InputStream)
1029 * BitmapFactory.decodeStream}; all other content types will be ignored. The content provider
1030 * URI used for this purpose must require no permissions to read the image data.
1031 */
1032 public static final String EXTRA_BACKGROUND_IMAGE_URI = "android.backgroundImageUri";
1033
1034 /**
Dan Sandler842dd772014-05-15 09:36:47 -04001035 * {@link #extras} key: A
Jeff Browndba34ba2014-06-24 20:46:03 -07001036 * {@link android.media.session.MediaSession.Token} associated with a
Dan Sandler842dd772014-05-15 09:36:47 -04001037 * {@link android.app.Notification.MediaStyle} notification.
1038 */
1039 public static final String EXTRA_MEDIA_SESSION = "android.mediaSession";
1040
1041 /**
Bryan Mawhinneye191f902014-07-22 12:50:09 +01001042 * {@link #extras} key: the indices of actions to be shown in the compact view,
1043 * as supplied to (e.g.) {@link MediaStyle#setShowActionsInCompactView(int...)}.
1044 */
1045 public static final String EXTRA_COMPACT_ACTIONS = "android.compactActions";
1046
Christoph Studer943aa672014-08-03 20:31:16 +02001047 /**
Alex Hillsfc737de2016-03-23 17:33:02 -04001048 * {@link #extras} key: the username to be displayed for all messages sent by the user including
1049 * direct replies
Adrian Roos96b7e202016-05-17 13:50:38 -07001050 * {@link android.app.Notification.MessagingStyle} notification. This extra is a
1051 * {@link CharSequence}
Alex Hillsfc737de2016-03-23 17:33:02 -04001052 */
1053 public static final String EXTRA_SELF_DISPLAY_NAME = "android.selfDisplayName";
1054
1055 /**
Adrian Roos96b7e202016-05-17 13:50:38 -07001056 * {@link #extras} key: a {@link CharSequence} to be displayed as the title to a conversation
Alex Hillsd9b04d92016-04-11 16:38:16 -04001057 * represented by a {@link android.app.Notification.MessagingStyle}
Alex Hillsfc737de2016-03-23 17:33:02 -04001058 */
Alex Hillsd9b04d92016-04-11 16:38:16 -04001059 public static final String EXTRA_CONVERSATION_TITLE = "android.conversationTitle";
Alex Hillsfc737de2016-03-23 17:33:02 -04001060
1061 /**
1062 * {@link #extras} key: an array of {@link android.app.Notification.MessagingStyle.Message}
1063 * bundles provided by a
Adrian Roos96b7e202016-05-17 13:50:38 -07001064 * {@link android.app.Notification.MessagingStyle} notification. This extra is a parcelable
1065 * array of bundles.
Alex Hillsfc737de2016-03-23 17:33:02 -04001066 */
1067 public static final String EXTRA_MESSAGES = "android.messages";
1068
1069 /**
Adrian Roos437cd562017-01-18 15:47:03 -08001070 * {@link #extras} key: an array of
1071 * {@link android.app.Notification.MessagingStyle#addHistoricMessage historic}
1072 * {@link android.app.Notification.MessagingStyle.Message} bundles provided by a
1073 * {@link android.app.Notification.MessagingStyle} notification. This extra is a parcelable
1074 * array of bundles.
1075 */
1076 public static final String EXTRA_HISTORIC_MESSAGES = "android.messages.historic";
1077
1078 /**
Selim Cinek7b9605b2017-01-19 17:36:00 -08001079 * {@link #extras} key: whether the notification should be colorized as
1080 * supplied to {@link Builder#setColorized(boolean)}}.
1081 */
1082 public static final String EXTRA_COLORIZED = "android.colorized";
1083
1084 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04001085 * @hide
1086 */
1087 public static final String EXTRA_BUILDER_APPLICATION_INFO = "android.appInfo";
1088
Selim Cinek247fa012016-02-18 09:50:48 -08001089 /**
1090 * @hide
1091 */
1092 public static final String EXTRA_CONTAINS_CUSTOM_VIEW = "android.contains.customView";
1093
Shane Brennan472a3b32016-12-12 15:28:10 -08001094 /**
Selim Cinekd0426622017-07-11 13:19:59 +02001095 * @hide
1096 */
1097 public static final String EXTRA_REDUCED_IMAGES = "android.reduced.images";
1098
1099 /**
Shane Brennan472a3b32016-12-12 15:28:10 -08001100 * {@link #extras} key: the audio contents of this notification.
1101 *
1102 * This is for use when rendering the notification on an audio-focused interface;
1103 * the audio contents are a complete sound sample that contains the contents/body of the
1104 * notification. This may be used in substitute of a Text-to-Speech reading of the
1105 * notification. For example if the notification represents a voice message this should point
1106 * to the audio of that message.
1107 *
1108 * The data stored under this key should be a String representation of a Uri that contains the
1109 * audio contents in one of the following formats: WAV, PCM 16-bit, AMR-WB.
1110 *
1111 * This extra is unnecessary if you are using {@code MessagingStyle} since each {@code Message}
1112 * has a field for holding data URI. That field can be used for audio.
1113 * See {@code Message#setData}.
1114 *
1115 * Example usage:
1116 * <pre>
1117 * {@code
1118 * Notification.Builder myBuilder = (build your Notification as normal);
1119 * myBuilder.getExtras().putString(EXTRA_AUDIO_CONTENTS_URI, myAudioUri.toString());
1120 * }
1121 * </pre>
1122 */
1123 public static final String EXTRA_AUDIO_CONTENTS_URI = "android.audioContents";
1124
Dan Sandler80eaa592016-04-14 23:34:54 -04001125 /** @hide */
1126 @SystemApi
Jeff Sharkeyd86b8fe2017-06-02 17:36:26 -06001127 @RequiresPermission(android.Manifest.permission.SUBSTITUTE_NOTIFICATION_APP_NAME)
Dan Sandler732bd6c2016-04-12 14:20:32 -04001128 public static final String EXTRA_SUBSTITUTE_APP_NAME = "android.substName";
1129
Dianne Hackbornfb5d4b52017-05-16 17:03:44 -07001130 /**
1131 * This is set on the notification shown by the activity manager about all apps
1132 * running in the background. It indicates that the notification should be shown
1133 * only if any of the given apps do not already have a {@link #FLAG_FOREGROUND_SERVICE}
1134 * notification currently visible to the user. This is a string array of all
1135 * package names of the apps.
1136 * @hide
1137 */
1138 public static final String EXTRA_FOREGROUND_APPS = "android.foregroundApps";
1139
Dan Sandlerd63f9322015-05-06 15:18:49 -04001140 private Icon mSmallIcon;
1141 private Icon mLargeIcon;
1142
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04001143 private String mChannelId;
Julia Reynolds2a128742016-11-28 14:29:25 -05001144 private long mTimeout;
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04001145
Julia Reynolds13d898c2017-02-02 12:22:05 -05001146 private String mShortcutId;
Julia Reynolds3aedded2017-03-31 14:42:09 -04001147 private CharSequence mSettingsText;
Julia Reynolds13d898c2017-02-02 12:22:05 -05001148
Julia Reynoldsa79c3712017-04-21 10:29:57 -04001149 /** @hide */
1150 @IntDef(prefix = { "GROUP_ALERT_" }, value = {
1151 GROUP_ALERT_ALL, GROUP_ALERT_CHILDREN, GROUP_ALERT_SUMMARY
1152 })
1153 @Retention(RetentionPolicy.SOURCE)
1154 public @interface GroupAlertBehavior {}
1155
1156 /**
1157 * Constant for {@link Builder#setGroupAlertBehavior(int)}, meaning that all notifications in a
1158 * group with sound or vibration ought to make sound or vibrate (respectively), so this
1159 * notification will not be muted when it is in a group.
1160 */
1161 public static final int GROUP_ALERT_ALL = 0;
1162
1163 /**
1164 * Constant for {@link Builder#setGroupAlertBehavior(int)}, meaning that all children
1165 * notification in a group should be silenced (no sound or vibration) even if they are posted
1166 * to a {@link NotificationChannel} that has sound and/or vibration. Use this constant to
Julia Reynolds399d9bf2017-08-11 12:52:14 -04001167 * mute this notification if this notification is a group child. This must be applied to all
1168 * children notifications you want to mute.
Julia Reynoldsa79c3712017-04-21 10:29:57 -04001169 *
1170 * <p> For example, you might want to use this constant if you post a number of children
1171 * notifications at once (say, after a periodic sync), and only need to notify the user
1172 * audibly once.
1173 */
1174 public static final int GROUP_ALERT_SUMMARY = 1;
1175
1176 /**
1177 * Constant for {@link Builder#setGroupAlertBehavior(int)}, meaning that the summary
1178 * notification in a group should be silenced (no sound or vibration) even if they are
1179 * posted to a {@link NotificationChannel} that has sound and/or vibration. Use this constant
1180 * to mute this notification if this notification is a group summary.
1181 *
1182 * <p>For example, you might want to use this constant if only the children notifications
Julia Reynolds399d9bf2017-08-11 12:52:14 -04001183 * in your group have content and the summary is only used to visually group notifications
1184 * rather than to alert the user that new information is available.
Julia Reynoldsa79c3712017-04-21 10:29:57 -04001185 */
1186 public static final int GROUP_ALERT_CHILDREN = 2;
1187
Julia Reynolds2f431e22017-06-07 14:12:09 +00001188 private int mGroupAlertBehavior = GROUP_ALERT_ALL;
Julia Reynoldsa79c3712017-04-21 10:29:57 -04001189
Julia Reynolds13d898c2017-02-02 12:22:05 -05001190 /**
1191 * If this notification is being shown as a badge, always show as a number.
1192 */
1193 public static final int BADGE_ICON_NONE = 0;
1194
1195 /**
1196 * If this notification is being shown as a badge, use the {@link #getSmallIcon()} to
1197 * represent this notification.
1198 */
1199 public static final int BADGE_ICON_SMALL = 1;
1200
1201 /**
1202 * If this notification is being shown as a badge, use the {@link #getLargeIcon()} to
1203 * represent this notification.
1204 */
1205 public static final int BADGE_ICON_LARGE = 2;
Julia Reynolds7d5b4da2017-03-20 10:18:49 -04001206 private int mBadgeIcon = BADGE_ICON_NONE;
Julia Reynolds13d898c2017-02-02 12:22:05 -05001207
Chris Wren51c75102013-07-16 20:49:17 -04001208 /**
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001209 * Structure to encapsulate a named action that can be shown as part of this notification.
1210 * It must include an icon, a label, and a {@link PendingIntent} to be fired when the action is
1211 * selected by the user.
1212 * <p>
Griff Hazen959591e2014-05-15 22:26:18 -07001213 * Apps should use {@link Notification.Builder#addAction(int, CharSequence, PendingIntent)}
1214 * or {@link Notification.Builder#addAction(Notification.Action)}
1215 * to attach actions.
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001216 */
Daniel Sandlerea2a3172013-02-20 22:24:20 -05001217 public static class Action implements Parcelable {
Shane Brennan472a3b32016-12-12 15:28:10 -08001218 /**
1219 * {@link #extras} key: Keys to a {@link Parcelable} {@link ArrayList} of
1220 * {@link RemoteInput}s.
1221 *
1222 * This is intended for {@link RemoteInput}s that only accept data, meaning
1223 * {@link RemoteInput#getAllowFreeFormInput} is false, {@link RemoteInput#getChoices}
1224 * is null or empty, and {@link RemoteInput#getAllowedDataTypes is non-null and not
1225 * empty. These {@link RemoteInput}s will be ignored by devices that do not
1226 * support non-text-based {@link RemoteInput}s. See {@link Builder#build}.
1227 *
1228 * You can test if a RemoteInput matches these constraints using
1229 * {@link RemoteInput#isDataOnly}.
1230 */
1231 private static final String EXTRA_DATA_ONLY_INPUTS = "android.extra.DATA_ONLY_INPUTS";
1232
Griff Hazen959591e2014-05-15 22:26:18 -07001233 private final Bundle mExtras;
Dan Sandler86647982015-05-13 23:41:13 -04001234 private Icon mIcon;
Griff Hazen61a9e862014-05-22 16:05:19 -07001235 private final RemoteInput[] mRemoteInputs;
Alex Hills1f27f882017-01-12 11:24:46 -05001236 private boolean mAllowGeneratedReplies = true;
Griff Hazen959591e2014-05-15 22:26:18 -07001237
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001238 /**
1239 * Small icon representing the action.
Dan Sandler86647982015-05-13 23:41:13 -04001240 *
1241 * @deprecated Use {@link Action#getIcon()} instead.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001242 */
Dan Sandler86647982015-05-13 23:41:13 -04001243 @Deprecated
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001244 public int icon;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001245
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001246 /**
1247 * Title of the action.
1248 */
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001249 public CharSequence title;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001250
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001251 /**
1252 * Intent to send when the user invokes this action. May be null, in which case the action
1253 * may be rendered in a disabled presentation by the system UI.
1254 */
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001255 public PendingIntent actionIntent;
Griff Hazen959591e2014-05-15 22:26:18 -07001256
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001257 private Action(Parcel in) {
Dan Sandler86647982015-05-13 23:41:13 -04001258 if (in.readInt() != 0) {
1259 mIcon = Icon.CREATOR.createFromParcel(in);
Dan Sandler68079d52015-07-22 10:45:30 -04001260 if (mIcon.getType() == Icon.TYPE_RESOURCE) {
1261 icon = mIcon.getResId();
1262 }
Dan Sandler86647982015-05-13 23:41:13 -04001263 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001264 title = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
1265 if (in.readInt() == 1) {
1266 actionIntent = PendingIntent.CREATOR.createFromParcel(in);
1267 }
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001268 mExtras = Bundle.setDefusable(in.readBundle(), true);
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001269 mRemoteInputs = in.createTypedArray(RemoteInput.CREATOR);
Alex Hills42b0c4d2016-04-26 13:35:36 -04001270 mAllowGeneratedReplies = in.readInt() == 1;
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001271 }
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001272
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001273 /**
Dan Sandler86647982015-05-13 23:41:13 -04001274 * @deprecated Use {@link android.app.Notification.Action.Builder}.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001275 */
Dan Sandler86647982015-05-13 23:41:13 -04001276 @Deprecated
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001277 public Action(int icon, CharSequence title, PendingIntent intent) {
Alex Hills1f27f882017-01-12 11:24:46 -05001278 this(Icon.createWithResource("", icon), title, intent, new Bundle(), null, true);
Griff Hazen959591e2014-05-15 22:26:18 -07001279 }
1280
Adrian Roos7af53622016-10-12 13:44:05 -07001281 /** Keep in sync with {@link Notification.Action.Builder#Builder(Action)}! */
Dan Sandler86647982015-05-13 23:41:13 -04001282 private Action(Icon icon, CharSequence title, PendingIntent intent, Bundle extras,
Alex Hills42b0c4d2016-04-26 13:35:36 -04001283 RemoteInput[] remoteInputs, boolean allowGeneratedReplies) {
Dan Sandler86647982015-05-13 23:41:13 -04001284 this.mIcon = icon;
Gus Prevasf5bff46f2015-08-24 10:34:28 -04001285 if (icon != null && icon.getType() == Icon.TYPE_RESOURCE) {
1286 this.icon = icon.getResId();
1287 }
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001288 this.title = title;
1289 this.actionIntent = intent;
Griff Hazen959591e2014-05-15 22:26:18 -07001290 this.mExtras = extras != null ? extras : new Bundle();
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001291 this.mRemoteInputs = remoteInputs;
Alex Hills42b0c4d2016-04-26 13:35:36 -04001292 this.mAllowGeneratedReplies = allowGeneratedReplies;
Griff Hazen959591e2014-05-15 22:26:18 -07001293 }
1294
1295 /**
Dan Sandler86647982015-05-13 23:41:13 -04001296 * Return an icon representing the action.
1297 */
1298 public Icon getIcon() {
1299 if (mIcon == null && icon != 0) {
1300 // you snuck an icon in here without using the builder; let's try to keep it
1301 mIcon = Icon.createWithResource("", icon);
1302 }
1303 return mIcon;
1304 }
1305
1306 /**
Griff Hazen959591e2014-05-15 22:26:18 -07001307 * Get additional metadata carried around with this Action.
1308 */
1309 public Bundle getExtras() {
1310 return mExtras;
1311 }
1312
1313 /**
Alex Hills42b0c4d2016-04-26 13:35:36 -04001314 * Return whether the platform should automatically generate possible replies for this
1315 * {@link Action}
1316 */
1317 public boolean getAllowGeneratedReplies() {
1318 return mAllowGeneratedReplies;
1319 }
1320
1321 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001322 * Get the list of inputs to be collected from the user when this action is sent.
Shane Brennan472a3b32016-12-12 15:28:10 -08001323 * May return null if no remote inputs were added. Only returns inputs which accept
1324 * a text input. For inputs which only accept data use {@link #getDataOnlyRemoteInputs}.
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001325 */
1326 public RemoteInput[] getRemoteInputs() {
1327 return mRemoteInputs;
1328 }
1329
1330 /**
Shane Brennan472a3b32016-12-12 15:28:10 -08001331 * Get the list of inputs to be collected from the user that ONLY accept data when this
1332 * action is sent. These remote inputs are guaranteed to return true on a call to
1333 * {@link RemoteInput#isDataOnly}.
1334 *
Shane Brennanf670d6c2017-04-25 13:05:45 -07001335 * Returns null if there are no data-only remote inputs.
Shane Brennan472a3b32016-12-12 15:28:10 -08001336 *
1337 * This method exists so that legacy RemoteInput collectors that pre-date the addition
1338 * of non-textual RemoteInputs do not access these remote inputs.
1339 */
1340 public RemoteInput[] getDataOnlyRemoteInputs() {
1341 return (RemoteInput[]) mExtras.getParcelableArray(EXTRA_DATA_ONLY_INPUTS);
1342 }
1343
1344 /**
Griff Hazen959591e2014-05-15 22:26:18 -07001345 * Builder class for {@link Action} objects.
1346 */
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001347 public static final class Builder {
Dan Sandler86647982015-05-13 23:41:13 -04001348 private final Icon mIcon;
Griff Hazen959591e2014-05-15 22:26:18 -07001349 private final CharSequence mTitle;
1350 private final PendingIntent mIntent;
Alex Hills1f27f882017-01-12 11:24:46 -05001351 private boolean mAllowGeneratedReplies = true;
Griff Hazen959591e2014-05-15 22:26:18 -07001352 private final Bundle mExtras;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001353 private ArrayList<RemoteInput> mRemoteInputs;
Griff Hazen959591e2014-05-15 22:26:18 -07001354
1355 /**
1356 * Construct a new builder for {@link Action} object.
1357 * @param icon icon to show for this action
1358 * @param title the title of the action
1359 * @param intent the {@link PendingIntent} to fire when users trigger this action
1360 */
Dan Sandler86647982015-05-13 23:41:13 -04001361 @Deprecated
Griff Hazen959591e2014-05-15 22:26:18 -07001362 public Builder(int icon, CharSequence title, PendingIntent intent) {
Adrian Roos7af53622016-10-12 13:44:05 -07001363 this(Icon.createWithResource("", icon), title, intent);
Dan Sandler86647982015-05-13 23:41:13 -04001364 }
1365
1366 /**
1367 * Construct a new builder for {@link Action} object.
1368 * @param icon icon to show for this action
1369 * @param title the title of the action
1370 * @param intent the {@link PendingIntent} to fire when users trigger this action
1371 */
1372 public Builder(Icon icon, CharSequence title, PendingIntent intent) {
Alex Hills1f27f882017-01-12 11:24:46 -05001373 this(icon, title, intent, new Bundle(), null, true);
Griff Hazen959591e2014-05-15 22:26:18 -07001374 }
1375
1376 /**
1377 * Construct a new builder for {@link Action} object using the fields from an
1378 * {@link Action}.
1379 * @param action the action to read fields from.
1380 */
1381 public Builder(Action action) {
Adrian Roos7af53622016-10-12 13:44:05 -07001382 this(action.getIcon(), action.title, action.actionIntent,
1383 new Bundle(action.mExtras), action.getRemoteInputs(),
1384 action.getAllowGeneratedReplies());
Griff Hazen959591e2014-05-15 22:26:18 -07001385 }
1386
Dan Sandler86647982015-05-13 23:41:13 -04001387 private Builder(Icon icon, CharSequence title, PendingIntent intent, Bundle extras,
Adrian Roos7af53622016-10-12 13:44:05 -07001388 RemoteInput[] remoteInputs, boolean allowGeneratedReplies) {
Griff Hazen959591e2014-05-15 22:26:18 -07001389 mIcon = icon;
1390 mTitle = title;
1391 mIntent = intent;
1392 mExtras = extras;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001393 if (remoteInputs != null) {
1394 mRemoteInputs = new ArrayList<RemoteInput>(remoteInputs.length);
1395 Collections.addAll(mRemoteInputs, remoteInputs);
1396 }
Adrian Roos7af53622016-10-12 13:44:05 -07001397 mAllowGeneratedReplies = allowGeneratedReplies;
Griff Hazen959591e2014-05-15 22:26:18 -07001398 }
1399
1400 /**
1401 * Merge additional metadata into this builder.
1402 *
1403 * <p>Values within the Bundle will replace existing extras values in this Builder.
1404 *
1405 * @see Notification.Action#extras
1406 */
1407 public Builder addExtras(Bundle extras) {
1408 if (extras != null) {
1409 mExtras.putAll(extras);
1410 }
1411 return this;
1412 }
1413
1414 /**
1415 * Get the metadata Bundle used by this Builder.
1416 *
1417 * <p>The returned Bundle is shared with this Builder.
1418 */
1419 public Bundle getExtras() {
1420 return mExtras;
1421 }
1422
1423 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001424 * Add an input to be collected from the user when this action is sent.
1425 * Response values can be retrieved from the fired intent by using the
1426 * {@link RemoteInput#getResultsFromIntent} function.
1427 * @param remoteInput a {@link RemoteInput} to add to the action
1428 * @return this object for method chaining
1429 */
1430 public Builder addRemoteInput(RemoteInput remoteInput) {
1431 if (mRemoteInputs == null) {
1432 mRemoteInputs = new ArrayList<RemoteInput>();
1433 }
1434 mRemoteInputs.add(remoteInput);
1435 return this;
1436 }
1437
1438 /**
Alex Hills42b0c4d2016-04-26 13:35:36 -04001439 * Set whether the platform should automatically generate possible replies to add to
1440 * {@link RemoteInput#getChoices()}. If the {@link Action} doesn't have a
1441 * {@link RemoteInput}, this has no effect.
1442 * @param allowGeneratedReplies {@code true} to allow generated replies, {@code false}
1443 * otherwise
1444 * @return this object for method chaining
Alex Hills1f27f882017-01-12 11:24:46 -05001445 * The default value is {@code true}
Alex Hills42b0c4d2016-04-26 13:35:36 -04001446 */
1447 public Builder setAllowGeneratedReplies(boolean allowGeneratedReplies) {
1448 mAllowGeneratedReplies = allowGeneratedReplies;
1449 return this;
1450 }
1451
1452 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001453 * Apply an extender to this action builder. Extenders may be used to add
1454 * metadata or change options on this builder.
1455 */
Griff Hazen61a9e862014-05-22 16:05:19 -07001456 public Builder extend(Extender extender) {
1457 extender.extend(this);
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001458 return this;
1459 }
1460
1461 /**
Griff Hazen959591e2014-05-15 22:26:18 -07001462 * Combine all of the options that have been set and return a new {@link Action}
1463 * object.
1464 * @return the built action
1465 */
1466 public Action build() {
Shane Brennan472a3b32016-12-12 15:28:10 -08001467 ArrayList<RemoteInput> dataOnlyInputs = new ArrayList<>();
1468 RemoteInput[] previousDataInputs =
1469 (RemoteInput[]) mExtras.getParcelableArray(EXTRA_DATA_ONLY_INPUTS);
Felipe Lemedfd11962017-01-18 18:38:46 -08001470 if (previousDataInputs != null) {
Shane Brennan472a3b32016-12-12 15:28:10 -08001471 for (RemoteInput input : previousDataInputs) {
1472 dataOnlyInputs.add(input);
1473 }
1474 }
1475 List<RemoteInput> textInputs = new ArrayList<>();
1476 if (mRemoteInputs != null) {
1477 for (RemoteInput input : mRemoteInputs) {
1478 if (input.isDataOnly()) {
1479 dataOnlyInputs.add(input);
1480 } else {
1481 textInputs.add(input);
1482 }
1483 }
1484 }
1485 if (!dataOnlyInputs.isEmpty()) {
1486 RemoteInput[] dataInputsArr =
1487 dataOnlyInputs.toArray(new RemoteInput[dataOnlyInputs.size()]);
1488 mExtras.putParcelableArray(EXTRA_DATA_ONLY_INPUTS, dataInputsArr);
1489 }
1490 RemoteInput[] textInputsArr = textInputs.isEmpty()
1491 ? null : textInputs.toArray(new RemoteInput[textInputs.size()]);
1492 return new Action(mIcon, mTitle, mIntent, mExtras, textInputsArr,
Alex Hills42b0c4d2016-04-26 13:35:36 -04001493 mAllowGeneratedReplies);
Griff Hazen959591e2014-05-15 22:26:18 -07001494 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001495 }
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001496
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001497 @Override
1498 public Action clone() {
1499 return new Action(
Dan Sandler86647982015-05-13 23:41:13 -04001500 getIcon(),
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001501 title,
1502 actionIntent, // safe to alias
Julia Reynolds53c934c2016-09-16 14:03:43 -04001503 mExtras == null ? new Bundle() : new Bundle(mExtras),
Alex Hills42b0c4d2016-04-26 13:35:36 -04001504 getRemoteInputs(),
1505 getAllowGeneratedReplies());
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001506 }
1507 @Override
1508 public int describeContents() {
1509 return 0;
1510 }
1511 @Override
1512 public void writeToParcel(Parcel out, int flags) {
Dan Sandler86647982015-05-13 23:41:13 -04001513 final Icon ic = getIcon();
1514 if (ic != null) {
1515 out.writeInt(1);
1516 ic.writeToParcel(out, 0);
1517 } else {
1518 out.writeInt(0);
1519 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001520 TextUtils.writeToParcel(title, out, flags);
1521 if (actionIntent != null) {
1522 out.writeInt(1);
1523 actionIntent.writeToParcel(out, flags);
1524 } else {
1525 out.writeInt(0);
1526 }
Griff Hazen959591e2014-05-15 22:26:18 -07001527 out.writeBundle(mExtras);
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001528 out.writeTypedArray(mRemoteInputs, flags);
Alex Hills42b0c4d2016-04-26 13:35:36 -04001529 out.writeInt(mAllowGeneratedReplies ? 1 : 0);
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001530 }
Griff Hazen959591e2014-05-15 22:26:18 -07001531 public static final Parcelable.Creator<Action> CREATOR =
1532 new Parcelable.Creator<Action>() {
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001533 public Action createFromParcel(Parcel in) {
1534 return new Action(in);
1535 }
1536 public Action[] newArray(int size) {
1537 return new Action[size];
1538 }
1539 };
Griff Hazen61a9e862014-05-22 16:05:19 -07001540
1541 /**
1542 * Extender interface for use with {@link Builder#extend}. Extenders may be used to add
1543 * metadata or change options on an action builder.
1544 */
1545 public interface Extender {
1546 /**
1547 * Apply this extender to a notification action builder.
1548 * @param builder the builder to be modified.
1549 * @return the build object for chaining.
1550 */
1551 public Builder extend(Builder builder);
1552 }
1553
1554 /**
1555 * Wearable extender for notification actions. To add extensions to an action,
1556 * create a new {@link android.app.Notification.Action.WearableExtender} object using
1557 * the {@code WearableExtender()} constructor and apply it to a
1558 * {@link android.app.Notification.Action.Builder} using
1559 * {@link android.app.Notification.Action.Builder#extend}.
1560 *
1561 * <pre class="prettyprint">
1562 * Notification.Action action = new Notification.Action.Builder(
1563 * R.drawable.archive_all, "Archive all", actionIntent)
Griff Hazen14f57992014-05-26 09:07:14 -07001564 * .extend(new Notification.Action.WearableExtender()
Griff Hazen61a9e862014-05-22 16:05:19 -07001565 * .setAvailableOffline(false))
Griff Hazen14f57992014-05-26 09:07:14 -07001566 * .build();</pre>
Griff Hazen61a9e862014-05-22 16:05:19 -07001567 */
1568 public static final class WearableExtender implements Extender {
1569 /** Notification action extra which contains wearable extensions */
1570 private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS";
1571
Pete Gastaf6781d2014-10-07 15:17:05 -04001572 // Keys within EXTRA_WEARABLE_EXTENSIONS for wearable options.
Griff Hazen61a9e862014-05-22 16:05:19 -07001573 private static final String KEY_FLAGS = "flags";
Pete Gastaf6781d2014-10-07 15:17:05 -04001574 private static final String KEY_IN_PROGRESS_LABEL = "inProgressLabel";
1575 private static final String KEY_CONFIRM_LABEL = "confirmLabel";
1576 private static final String KEY_CANCEL_LABEL = "cancelLabel";
Griff Hazen61a9e862014-05-22 16:05:19 -07001577
1578 // Flags bitwise-ored to mFlags
1579 private static final int FLAG_AVAILABLE_OFFLINE = 0x1;
Alex Hills9ab3a232016-04-05 14:54:56 -04001580 private static final int FLAG_HINT_LAUNCHES_ACTIVITY = 1 << 1;
Alex Hills9f087612016-06-07 09:08:59 -04001581 private static final int FLAG_HINT_DISPLAY_INLINE = 1 << 2;
Griff Hazen61a9e862014-05-22 16:05:19 -07001582
1583 // Default value for flags integer
1584 private static final int DEFAULT_FLAGS = FLAG_AVAILABLE_OFFLINE;
1585
1586 private int mFlags = DEFAULT_FLAGS;
1587
Pete Gastaf6781d2014-10-07 15:17:05 -04001588 private CharSequence mInProgressLabel;
1589 private CharSequence mConfirmLabel;
1590 private CharSequence mCancelLabel;
1591
Griff Hazen61a9e862014-05-22 16:05:19 -07001592 /**
1593 * Create a {@link android.app.Notification.Action.WearableExtender} with default
1594 * options.
1595 */
1596 public WearableExtender() {
1597 }
1598
1599 /**
1600 * Create a {@link android.app.Notification.Action.WearableExtender} by reading
1601 * wearable options present in an existing notification action.
1602 * @param action the notification action to inspect.
1603 */
1604 public WearableExtender(Action action) {
1605 Bundle wearableBundle = action.getExtras().getBundle(EXTRA_WEARABLE_EXTENSIONS);
1606 if (wearableBundle != null) {
1607 mFlags = wearableBundle.getInt(KEY_FLAGS, DEFAULT_FLAGS);
Pete Gastaf6781d2014-10-07 15:17:05 -04001608 mInProgressLabel = wearableBundle.getCharSequence(KEY_IN_PROGRESS_LABEL);
1609 mConfirmLabel = wearableBundle.getCharSequence(KEY_CONFIRM_LABEL);
1610 mCancelLabel = wearableBundle.getCharSequence(KEY_CANCEL_LABEL);
Griff Hazen61a9e862014-05-22 16:05:19 -07001611 }
1612 }
1613
1614 /**
1615 * Apply wearable extensions to a notification action that is being built. This is
1616 * typically called by the {@link android.app.Notification.Action.Builder#extend}
1617 * method of {@link android.app.Notification.Action.Builder}.
1618 */
1619 @Override
1620 public Action.Builder extend(Action.Builder builder) {
1621 Bundle wearableBundle = new Bundle();
1622
1623 if (mFlags != DEFAULT_FLAGS) {
1624 wearableBundle.putInt(KEY_FLAGS, mFlags);
1625 }
Pete Gastaf6781d2014-10-07 15:17:05 -04001626 if (mInProgressLabel != null) {
1627 wearableBundle.putCharSequence(KEY_IN_PROGRESS_LABEL, mInProgressLabel);
1628 }
1629 if (mConfirmLabel != null) {
1630 wearableBundle.putCharSequence(KEY_CONFIRM_LABEL, mConfirmLabel);
1631 }
1632 if (mCancelLabel != null) {
1633 wearableBundle.putCharSequence(KEY_CANCEL_LABEL, mCancelLabel);
1634 }
Griff Hazen61a9e862014-05-22 16:05:19 -07001635
1636 builder.getExtras().putBundle(EXTRA_WEARABLE_EXTENSIONS, wearableBundle);
1637 return builder;
1638 }
1639
1640 @Override
1641 public WearableExtender clone() {
1642 WearableExtender that = new WearableExtender();
1643 that.mFlags = this.mFlags;
Pete Gastaf6781d2014-10-07 15:17:05 -04001644 that.mInProgressLabel = this.mInProgressLabel;
1645 that.mConfirmLabel = this.mConfirmLabel;
1646 that.mCancelLabel = this.mCancelLabel;
Griff Hazen61a9e862014-05-22 16:05:19 -07001647 return that;
1648 }
1649
1650 /**
1651 * Set whether this action is available when the wearable device is not connected to
1652 * a companion device. The user can still trigger this action when the wearable device is
1653 * offline, but a visual hint will indicate that the action may not be available.
1654 * Defaults to true.
1655 */
1656 public WearableExtender setAvailableOffline(boolean availableOffline) {
1657 setFlag(FLAG_AVAILABLE_OFFLINE, availableOffline);
1658 return this;
1659 }
1660
1661 /**
1662 * Get whether this action is available when the wearable device is not connected to
1663 * a companion device. The user can still trigger this action when the wearable device is
1664 * offline, but a visual hint will indicate that the action may not be available.
1665 * Defaults to true.
1666 */
1667 public boolean isAvailableOffline() {
1668 return (mFlags & FLAG_AVAILABLE_OFFLINE) != 0;
1669 }
1670
1671 private void setFlag(int mask, boolean value) {
1672 if (value) {
1673 mFlags |= mask;
1674 } else {
1675 mFlags &= ~mask;
1676 }
1677 }
Pete Gastaf6781d2014-10-07 15:17:05 -04001678
1679 /**
1680 * Set a label to display while the wearable is preparing to automatically execute the
1681 * action. This is usually a 'ing' verb ending in ellipsis like "Sending..."
1682 *
1683 * @param label the label to display while the action is being prepared to execute
1684 * @return this object for method chaining
1685 */
1686 public WearableExtender setInProgressLabel(CharSequence label) {
1687 mInProgressLabel = label;
1688 return this;
1689 }
1690
1691 /**
1692 * Get the label to display while the wearable is preparing to automatically execute
1693 * the action. This is usually a 'ing' verb ending in ellipsis like "Sending..."
1694 *
1695 * @return the label to display while the action is being prepared to execute
1696 */
1697 public CharSequence getInProgressLabel() {
1698 return mInProgressLabel;
1699 }
1700
1701 /**
1702 * Set a label to display to confirm that the action should be executed.
1703 * This is usually an imperative verb like "Send".
1704 *
1705 * @param label the label to confirm the action should be executed
1706 * @return this object for method chaining
1707 */
1708 public WearableExtender setConfirmLabel(CharSequence label) {
1709 mConfirmLabel = label;
1710 return this;
1711 }
1712
1713 /**
1714 * Get the label to display to confirm that the action should be executed.
1715 * This is usually an imperative verb like "Send".
1716 *
1717 * @return the label to confirm the action should be executed
1718 */
1719 public CharSequence getConfirmLabel() {
1720 return mConfirmLabel;
1721 }
1722
1723 /**
1724 * Set a label to display to cancel the action.
1725 * This is usually an imperative verb, like "Cancel".
1726 *
1727 * @param label the label to display to cancel the action
1728 * @return this object for method chaining
1729 */
1730 public WearableExtender setCancelLabel(CharSequence label) {
1731 mCancelLabel = label;
1732 return this;
1733 }
1734
1735 /**
1736 * Get the label to display to cancel the action.
1737 * This is usually an imperative verb like "Cancel".
1738 *
1739 * @return the label to display to cancel the action
1740 */
1741 public CharSequence getCancelLabel() {
1742 return mCancelLabel;
1743 }
Alex Hills9ab3a232016-04-05 14:54:56 -04001744
1745 /**
1746 * Set a hint that this Action will launch an {@link Activity} directly, telling the
1747 * platform that it can generate the appropriate transitions.
1748 * @param hintLaunchesActivity {@code true} if the content intent will launch
1749 * an activity and transitions should be generated, false otherwise.
1750 * @return this object for method chaining
1751 */
Alex Hills4ec3ff42016-04-12 11:36:18 -04001752 public WearableExtender setHintLaunchesActivity(
Alex Hills9ab3a232016-04-05 14:54:56 -04001753 boolean hintLaunchesActivity) {
1754 setFlag(FLAG_HINT_LAUNCHES_ACTIVITY, hintLaunchesActivity);
1755 return this;
1756 }
1757
1758 /**
1759 * Get a hint that this Action will launch an {@link Activity} directly, telling the
1760 * platform that it can generate the appropriate transitions
1761 * @return {@code true} if the content intent will launch an activity and transitions
1762 * should be generated, false otherwise. The default value is {@code false} if this was
1763 * never set.
1764 */
Alex Hills4ec3ff42016-04-12 11:36:18 -04001765 public boolean getHintLaunchesActivity() {
Alex Hills9ab3a232016-04-05 14:54:56 -04001766 return (mFlags & FLAG_HINT_LAUNCHES_ACTIVITY) != 0;
1767 }
Alex Hills9f087612016-06-07 09:08:59 -04001768
1769 /**
1770 * Set a hint that this Action should be displayed inline.
1771 *
1772 * @param hintDisplayInline {@code true} if action should be displayed inline, false
1773 * otherwise
1774 * @return this object for method chaining
1775 */
1776 public WearableExtender setHintDisplayActionInline(
1777 boolean hintDisplayInline) {
1778 setFlag(FLAG_HINT_DISPLAY_INLINE, hintDisplayInline);
1779 return this;
1780 }
1781
1782 /**
1783 * Get a hint that this Action should be displayed inline.
1784 *
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08001785 * @return {@code true} if the Action should be displayed inline, {@code false}
Alex Hills9f087612016-06-07 09:08:59 -04001786 * otherwise. The default value is {@code false} if this was never set.
1787 */
1788 public boolean getHintDisplayActionInline() {
1789 return (mFlags & FLAG_HINT_DISPLAY_INLINE) != 0;
1790 }
Griff Hazen61a9e862014-05-22 16:05:19 -07001791 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001792 }
1793
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001794 /**
1795 * Array of all {@link Action} structures attached to this notification by
1796 * {@link Builder#addAction(int, CharSequence, PendingIntent)}. Mostly useful for instances of
1797 * {@link android.service.notification.NotificationListenerService} that provide an alternative
1798 * interface for invoking actions.
1799 */
Daniel Sandlerea2a3172013-02-20 22:24:20 -05001800 public Action[] actions;
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001801
1802 /**
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001803 * Replacement version of this notification whose content will be shown
1804 * in an insecure context such as atop a secure keyguard. See {@link #visibility}
1805 * and {@link #VISIBILITY_PUBLIC}.
1806 */
1807 public Notification publicVersion;
1808
1809 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001810 * Constructs a Notification object with default values.
Joe Onorato46439ce2010-11-19 13:56:21 -08001811 * You might want to consider using {@link Builder} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001812 */
1813 public Notification()
1814 {
1815 this.when = System.currentTimeMillis();
Selim Cinekb85f36fd2016-04-20 18:46:36 -07001816 this.creationTime = System.currentTimeMillis();
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001817 this.priority = PRIORITY_DEFAULT;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001818 }
1819
1820 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001821 * @hide
1822 */
1823 public Notification(Context context, int icon, CharSequence tickerText, long when,
1824 CharSequence contentTitle, CharSequence contentText, Intent contentIntent)
1825 {
Chris Wren1ce4b6d2015-06-11 10:19:43 -04001826 new Builder(context)
1827 .setWhen(when)
1828 .setSmallIcon(icon)
1829 .setTicker(tickerText)
1830 .setContentTitle(contentTitle)
1831 .setContentText(contentText)
1832 .setContentIntent(PendingIntent.getActivity(context, 0, contentIntent, 0))
1833 .buildInto(this);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001834 }
1835
1836 /**
1837 * Constructs a Notification object with the information needed to
1838 * have a status bar icon without the standard expanded view.
1839 *
1840 * @param icon The resource id of the icon to put in the status bar.
1841 * @param tickerText The text that flows by in the status bar when the notification first
1842 * activates.
1843 * @param when The time to show in the time field. In the System.currentTimeMillis
1844 * timebase.
Joe Onorato46439ce2010-11-19 13:56:21 -08001845 *
1846 * @deprecated Use {@link Builder} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001847 */
Joe Onorato46439ce2010-11-19 13:56:21 -08001848 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001849 public Notification(int icon, CharSequence tickerText, long when)
1850 {
1851 this.icon = icon;
1852 this.tickerText = tickerText;
1853 this.when = when;
Selim Cinekb85f36fd2016-04-20 18:46:36 -07001854 this.creationTime = System.currentTimeMillis();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001855 }
1856
1857 /**
1858 * Unflatten the notification from a parcel.
1859 */
Svet Ganovddb94882016-06-23 19:55:24 -07001860 @SuppressWarnings("unchecked")
1861 public Notification(Parcel parcel) {
1862 // IMPORTANT: Add unmarshaling code in readFromParcel as the pending
1863 // intents in extras are always written as the last entry.
1864 readFromParcelImpl(parcel);
1865 // Must be read last!
Felipe Lemedd85da62016-06-28 11:29:54 -07001866 allPendingIntents = (ArraySet<PendingIntent>) parcel.readArraySet(null);
Svet Ganovddb94882016-06-23 19:55:24 -07001867 }
1868
1869 private void readFromParcelImpl(Parcel parcel)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001870 {
1871 int version = parcel.readInt();
1872
Dianne Hackborn98305522017-05-05 17:53:53 -07001873 whitelistToken = parcel.readStrongBinder();
1874 if (whitelistToken == null) {
1875 whitelistToken = processWhitelistToken;
1876 }
1877 // Propagate this token to all pending intents that are unmarshalled from the parcel.
1878 parcel.setClassCookie(PendingIntent.class, whitelistToken);
1879
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001880 when = parcel.readLong();
Selim Cinekb85f36fd2016-04-20 18:46:36 -07001881 creationTime = parcel.readLong();
Dan Sandler3936e7a2015-05-19 20:59:12 -04001882 if (parcel.readInt() != 0) {
1883 mSmallIcon = Icon.CREATOR.createFromParcel(parcel);
Dan Sandler4e787062015-06-17 15:09:48 -04001884 if (mSmallIcon.getType() == Icon.TYPE_RESOURCE) {
1885 icon = mSmallIcon.getResId();
1886 }
Dan Sandler3936e7a2015-05-19 20:59:12 -04001887 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001888 number = parcel.readInt();
1889 if (parcel.readInt() != 0) {
1890 contentIntent = PendingIntent.CREATOR.createFromParcel(parcel);
1891 }
1892 if (parcel.readInt() != 0) {
1893 deleteIntent = PendingIntent.CREATOR.createFromParcel(parcel);
1894 }
1895 if (parcel.readInt() != 0) {
1896 tickerText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
1897 }
1898 if (parcel.readInt() != 0) {
Joe Onorato46439ce2010-11-19 13:56:21 -08001899 tickerView = RemoteViews.CREATOR.createFromParcel(parcel);
Joe Onoratoef1e7762010-09-17 18:38:38 -04001900 }
1901 if (parcel.readInt() != 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001902 contentView = RemoteViews.CREATOR.createFromParcel(parcel);
1903 }
Joe Onorato561d3852010-11-20 18:09:34 -08001904 if (parcel.readInt() != 0) {
Dan Sandlerd63f9322015-05-06 15:18:49 -04001905 mLargeIcon = Icon.CREATOR.createFromParcel(parcel);
Joe Onorato561d3852010-11-20 18:09:34 -08001906 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001907 defaults = parcel.readInt();
1908 flags = parcel.readInt();
1909 if (parcel.readInt() != 0) {
1910 sound = Uri.CREATOR.createFromParcel(parcel);
1911 }
1912
1913 audioStreamType = parcel.readInt();
John Spurlockc0650f022014-07-19 13:22:39 -04001914 if (parcel.readInt() != 0) {
1915 audioAttributes = AudioAttributes.CREATOR.createFromParcel(parcel);
1916 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001917 vibrate = parcel.createLongArray();
1918 ledARGB = parcel.readInt();
1919 ledOnMS = parcel.readInt();
1920 ledOffMS = parcel.readInt();
1921 iconLevel = parcel.readInt();
Daniel Sandlere46cbd32010-06-17 10:35:26 -04001922
1923 if (parcel.readInt() != 0) {
1924 fullScreenIntent = PendingIntent.CREATOR.createFromParcel(parcel);
1925 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001926
1927 priority = parcel.readInt();
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001928
John Spurlockfd7f1e02014-03-18 16:41:57 -04001929 category = parcel.readString();
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001930
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001931 mGroupKey = parcel.readString();
1932
1933 mSortKey = parcel.readString();
1934
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001935 extras = Bundle.setDefusable(parcel.readBundle(), true); // may be null
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001936
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001937 actions = parcel.createTypedArray(Action.CREATOR); // may be null
1938
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001939 if (parcel.readInt() != 0) {
Daniel Sandlerf3b73432012-03-27 15:01:25 -04001940 bigContentView = RemoteViews.CREATOR.createFromParcel(parcel);
1941 }
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001942
Chris Wren8fd39ec2014-02-27 17:43:26 -05001943 if (parcel.readInt() != 0) {
1944 headsUpContentView = RemoteViews.CREATOR.createFromParcel(parcel);
1945 }
1946
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001947 visibility = parcel.readInt();
1948
1949 if (parcel.readInt() != 0) {
1950 publicVersion = Notification.CREATOR.createFromParcel(parcel);
1951 }
Dan Sandler26e81cf2014-05-06 10:01:27 -04001952
1953 color = parcel.readInt();
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04001954
1955 if (parcel.readInt() != 0) {
1956 mChannelId = parcel.readString();
1957 }
Julia Reynolds2a128742016-11-28 14:29:25 -05001958 mTimeout = parcel.readLong();
Julia Reynolds13d898c2017-02-02 12:22:05 -05001959
1960 if (parcel.readInt() != 0) {
1961 mShortcutId = parcel.readString();
1962 }
1963
1964 mBadgeIcon = parcel.readInt();
Julia Reynolds3aedded2017-03-31 14:42:09 -04001965
1966 if (parcel.readInt() != 0) {
1967 mSettingsText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
1968 }
Julia Reynoldsa79c3712017-04-21 10:29:57 -04001969
1970 mGroupAlertBehavior = parcel.readInt();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001971 }
1972
Andy Stadler110988c2010-12-03 14:29:16 -08001973 @Override
Joe Onorato18e69df2010-05-17 22:26:12 -07001974 public Notification clone() {
1975 Notification that = new Notification();
Daniel Sandler1a497d32013-04-18 14:52:45 -04001976 cloneInto(that, true);
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001977 return that;
1978 }
Joe Onorato18e69df2010-05-17 22:26:12 -07001979
Daniel Sandler1a497d32013-04-18 14:52:45 -04001980 /**
1981 * Copy all (or if heavy is false, all except Bitmaps and RemoteViews) members
1982 * of this into that.
1983 * @hide
1984 */
1985 public void cloneInto(Notification that, boolean heavy) {
Dianne Hackborn98305522017-05-05 17:53:53 -07001986 that.whitelistToken = this.whitelistToken;
Joe Onorato18e69df2010-05-17 22:26:12 -07001987 that.when = this.when;
Selim Cinekb85f36fd2016-04-20 18:46:36 -07001988 that.creationTime = this.creationTime;
Dan Sandlerd63f9322015-05-06 15:18:49 -04001989 that.mSmallIcon = this.mSmallIcon;
Joe Onorato18e69df2010-05-17 22:26:12 -07001990 that.number = this.number;
1991
1992 // PendingIntents are global, so there's no reason (or way) to clone them.
1993 that.contentIntent = this.contentIntent;
1994 that.deleteIntent = this.deleteIntent;
Daniel Sandlere46cbd32010-06-17 10:35:26 -04001995 that.fullScreenIntent = this.fullScreenIntent;
Joe Onorato18e69df2010-05-17 22:26:12 -07001996
1997 if (this.tickerText != null) {
1998 that.tickerText = this.tickerText.toString();
1999 }
Daniel Sandler1a497d32013-04-18 14:52:45 -04002000 if (heavy && this.tickerView != null) {
Joe Onorato46439ce2010-11-19 13:56:21 -08002001 that.tickerView = this.tickerView.clone();
Joe Onoratoef1e7762010-09-17 18:38:38 -04002002 }
Daniel Sandler1a497d32013-04-18 14:52:45 -04002003 if (heavy && this.contentView != null) {
Joe Onorato18e69df2010-05-17 22:26:12 -07002004 that.contentView = this.contentView.clone();
2005 }
Dan Sandlerd63f9322015-05-06 15:18:49 -04002006 if (heavy && this.mLargeIcon != null) {
2007 that.mLargeIcon = this.mLargeIcon;
Joe Onorato561d3852010-11-20 18:09:34 -08002008 }
Jozef BABJAKa8b91832011-02-22 08:05:08 +01002009 that.iconLevel = this.iconLevel;
Joe Onorato18e69df2010-05-17 22:26:12 -07002010 that.sound = this.sound; // android.net.Uri is immutable
2011 that.audioStreamType = this.audioStreamType;
John Spurlockc0650f022014-07-19 13:22:39 -04002012 if (this.audioAttributes != null) {
2013 that.audioAttributes = new AudioAttributes.Builder(this.audioAttributes).build();
2014 }
Joe Onorato18e69df2010-05-17 22:26:12 -07002015
2016 final long[] vibrate = this.vibrate;
2017 if (vibrate != null) {
2018 final int N = vibrate.length;
2019 final long[] vib = that.vibrate = new long[N];
2020 System.arraycopy(vibrate, 0, vib, 0, N);
2021 }
2022
2023 that.ledARGB = this.ledARGB;
2024 that.ledOnMS = this.ledOnMS;
2025 that.ledOffMS = this.ledOffMS;
2026 that.defaults = this.defaults;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002027
Joe Onorato18e69df2010-05-17 22:26:12 -07002028 that.flags = this.flags;
2029
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002030 that.priority = this.priority;
Joe Malin8d40d042012-11-05 11:36:40 -08002031
John Spurlockfd7f1e02014-03-18 16:41:57 -04002032 that.category = this.category;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002033
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002034 that.mGroupKey = this.mGroupKey;
2035
2036 that.mSortKey = this.mSortKey;
2037
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002038 if (this.extras != null) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04002039 try {
2040 that.extras = new Bundle(this.extras);
2041 // will unparcel
2042 that.extras.size();
2043 } catch (BadParcelableException e) {
2044 Log.e(TAG, "could not unparcel extras from notification: " + this, e);
2045 that.extras = null;
2046 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002047 }
2048
Felipe Lemedd85da62016-06-28 11:29:54 -07002049 if (!ArrayUtils.isEmpty(allPendingIntents)) {
2050 that.allPendingIntents = new ArraySet<>(allPendingIntents);
Svet Ganovddb94882016-06-23 19:55:24 -07002051 }
2052
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002053 if (this.actions != null) {
2054 that.actions = new Action[this.actions.length];
2055 for(int i=0; i<this.actions.length; i++) {
liangweikang63b03b52017-03-16 19:22:15 +08002056 if ( this.actions[i] != null) {
2057 that.actions[i] = this.actions[i].clone();
2058 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002059 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002060 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002061
Daniel Sandler1a497d32013-04-18 14:52:45 -04002062 if (heavy && this.bigContentView != null) {
Daniel Sandlerf3b73432012-03-27 15:01:25 -04002063 that.bigContentView = this.bigContentView.clone();
2064 }
Daniel Sandler1a497d32013-04-18 14:52:45 -04002065
Chris Wren8fd39ec2014-02-27 17:43:26 -05002066 if (heavy && this.headsUpContentView != null) {
2067 that.headsUpContentView = this.headsUpContentView.clone();
2068 }
2069
Dan Sandler0bf2ed82013-12-21 23:33:41 -06002070 that.visibility = this.visibility;
2071
2072 if (this.publicVersion != null) {
2073 that.publicVersion = new Notification();
2074 this.publicVersion.cloneInto(that.publicVersion, heavy);
2075 }
2076
Dan Sandler26e81cf2014-05-06 10:01:27 -04002077 that.color = this.color;
2078
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04002079 that.mChannelId = this.mChannelId;
Julia Reynolds2a128742016-11-28 14:29:25 -05002080 that.mTimeout = this.mTimeout;
Julia Reynolds3aedded2017-03-31 14:42:09 -04002081 that.mShortcutId = this.mShortcutId;
2082 that.mBadgeIcon = this.mBadgeIcon;
2083 that.mSettingsText = this.mSettingsText;
Julia Reynoldsa79c3712017-04-21 10:29:57 -04002084 that.mGroupAlertBehavior = this.mGroupAlertBehavior;
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04002085
Daniel Sandler1a497d32013-04-18 14:52:45 -04002086 if (!heavy) {
2087 that.lightenPayload(); // will clean out extras
2088 }
2089 }
2090
2091 /**
2092 * Removes heavyweight parts of the Notification object for archival or for sending to
2093 * listeners when the full contents are not necessary.
2094 * @hide
2095 */
2096 public final void lightenPayload() {
2097 tickerView = null;
2098 contentView = null;
2099 bigContentView = null;
Chris Wren8fd39ec2014-02-27 17:43:26 -05002100 headsUpContentView = null;
Dan Sandlerd63f9322015-05-06 15:18:49 -04002101 mLargeIcon = null;
Dan Sandler50128532015-12-08 15:42:41 -05002102 if (extras != null && !extras.isEmpty()) {
2103 final Set<String> keyset = extras.keySet();
2104 final int N = keyset.size();
2105 final String[] keys = keyset.toArray(new String[N]);
2106 for (int i=0; i<N; i++) {
2107 final String key = keys[i];
Dmitri Plotnikov22281362017-01-30 11:16:21 -08002108 if (TvExtender.EXTRA_TV_EXTENDER.equals(key)) {
2109 continue;
2110 }
Dan Sandler50128532015-12-08 15:42:41 -05002111 final Object obj = extras.get(key);
2112 if (obj != null &&
2113 ( obj instanceof Parcelable
2114 || obj instanceof Parcelable[]
2115 || obj instanceof SparseArray
2116 || obj instanceof ArrayList)) {
2117 extras.remove(key);
2118 }
2119 }
Daniel Sandler1a497d32013-04-18 14:52:45 -04002120 }
Joe Onorato18e69df2010-05-17 22:26:12 -07002121 }
2122
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04002123 /**
2124 * Make sure this CharSequence is safe to put into a bundle, which basically
2125 * means it had better not be some custom Parcelable implementation.
2126 * @hide
2127 */
2128 public static CharSequence safeCharSequence(CharSequence cs) {
Christoph Studer535ec612014-09-03 15:47:47 +02002129 if (cs == null) return cs;
2130 if (cs.length() > MAX_CHARSEQUENCE_LENGTH) {
2131 cs = cs.subSequence(0, MAX_CHARSEQUENCE_LENGTH);
2132 }
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04002133 if (cs instanceof Parcelable) {
2134 Log.e(TAG, "warning: " + cs.getClass().getCanonicalName()
2135 + " instance is a custom Parcelable and not allowed in Notification");
2136 return cs.toString();
2137 }
Selim Cinek60a54252016-02-26 17:03:25 -08002138 return removeTextSizeSpans(cs);
2139 }
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04002140
Selim Cinek60a54252016-02-26 17:03:25 -08002141 private static CharSequence removeTextSizeSpans(CharSequence charSequence) {
2142 if (charSequence instanceof Spanned) {
2143 Spanned ss = (Spanned) charSequence;
2144 Object[] spans = ss.getSpans(0, ss.length(), Object.class);
2145 SpannableStringBuilder builder = new SpannableStringBuilder(ss.toString());
2146 for (Object span : spans) {
2147 Object resultSpan = span;
Selim Cinek89991a22016-03-07 19:51:54 -08002148 if (resultSpan instanceof CharacterStyle) {
2149 resultSpan = ((CharacterStyle) span).getUnderlying();
2150 }
2151 if (resultSpan instanceof TextAppearanceSpan) {
2152 TextAppearanceSpan originalSpan = (TextAppearanceSpan) resultSpan;
Selim Cinek60a54252016-02-26 17:03:25 -08002153 resultSpan = new TextAppearanceSpan(
2154 originalSpan.getFamily(),
2155 originalSpan.getTextStyle(),
2156 -1,
2157 originalSpan.getTextColor(),
2158 originalSpan.getLinkTextColor());
Selim Cinek89991a22016-03-07 19:51:54 -08002159 } else if (resultSpan instanceof RelativeSizeSpan
2160 || resultSpan instanceof AbsoluteSizeSpan) {
Selim Cinek60a54252016-02-26 17:03:25 -08002161 continue;
Selim Cinek89991a22016-03-07 19:51:54 -08002162 } else {
2163 resultSpan = span;
Selim Cinek60a54252016-02-26 17:03:25 -08002164 }
2165 builder.setSpan(resultSpan, ss.getSpanStart(span), ss.getSpanEnd(span),
2166 ss.getSpanFlags(span));
2167 }
2168 return builder;
2169 }
2170 return charSequence;
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04002171 }
2172
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002173 public int describeContents() {
2174 return 0;
2175 }
2176
2177 /**
Dan Sandler4e787062015-06-17 15:09:48 -04002178 * Flatten this notification into a parcel.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002179 */
Svet Ganovddb94882016-06-23 19:55:24 -07002180 public void writeToParcel(Parcel parcel, int flags) {
2181 // We need to mark all pending intents getting into the notification
2182 // system as being put there to later allow the notification ranker
2183 // to launch them and by doing so add the app to the battery saver white
2184 // list for a short period of time. The problem is that the system
2185 // cannot look into the extras as there may be parcelables there that
2186 // the platform does not know how to handle. To go around that we have
2187 // an explicit list of the pending intents in the extras bundle.
Felipe Lemedd85da62016-06-28 11:29:54 -07002188 final boolean collectPendingIntents = (allPendingIntents == null);
Svet Ganovddb94882016-06-23 19:55:24 -07002189 if (collectPendingIntents) {
2190 PendingIntent.setOnMarshaledListener(
2191 (PendingIntent intent, Parcel out, int outFlags) -> {
2192 if (parcel == out) {
Felipe Lemedd85da62016-06-28 11:29:54 -07002193 if (allPendingIntents == null) {
2194 allPendingIntents = new ArraySet<>();
Svet Ganovddb94882016-06-23 19:55:24 -07002195 }
Felipe Lemedd85da62016-06-28 11:29:54 -07002196 allPendingIntents.add(intent);
Svet Ganovddb94882016-06-23 19:55:24 -07002197 }
2198 });
2199 }
2200 try {
2201 // IMPORTANT: Add marshaling code in writeToParcelImpl as we
Julia Reynolds13d898c2017-02-02 12:22:05 -05002202 // want to intercept all pending events written to the parcel.
Svet Ganovddb94882016-06-23 19:55:24 -07002203 writeToParcelImpl(parcel, flags);
2204 // Must be written last!
Felipe Lemedd85da62016-06-28 11:29:54 -07002205 parcel.writeArraySet(allPendingIntents);
Svet Ganovddb94882016-06-23 19:55:24 -07002206 } finally {
2207 if (collectPendingIntents) {
2208 PendingIntent.setOnMarshaledListener(null);
2209 }
2210 }
2211 }
2212
2213 private void writeToParcelImpl(Parcel parcel, int flags) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002214 parcel.writeInt(1);
2215
Dianne Hackborn98305522017-05-05 17:53:53 -07002216 parcel.writeStrongBinder(whitelistToken);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002217 parcel.writeLong(when);
Selim Cinekb85f36fd2016-04-20 18:46:36 -07002218 parcel.writeLong(creationTime);
Dan Sandler4e787062015-06-17 15:09:48 -04002219 if (mSmallIcon == null && icon != 0) {
2220 // you snuck an icon in here without using the builder; let's try to keep it
2221 mSmallIcon = Icon.createWithResource("", icon);
2222 }
Dan Sandler3936e7a2015-05-19 20:59:12 -04002223 if (mSmallIcon != null) {
2224 parcel.writeInt(1);
2225 mSmallIcon.writeToParcel(parcel, 0);
2226 } else {
2227 parcel.writeInt(0);
2228 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002229 parcel.writeInt(number);
2230 if (contentIntent != null) {
2231 parcel.writeInt(1);
2232 contentIntent.writeToParcel(parcel, 0);
2233 } else {
2234 parcel.writeInt(0);
2235 }
2236 if (deleteIntent != null) {
2237 parcel.writeInt(1);
2238 deleteIntent.writeToParcel(parcel, 0);
2239 } else {
2240 parcel.writeInt(0);
2241 }
2242 if (tickerText != null) {
2243 parcel.writeInt(1);
2244 TextUtils.writeToParcel(tickerText, parcel, flags);
2245 } else {
2246 parcel.writeInt(0);
2247 }
Joe Onorato46439ce2010-11-19 13:56:21 -08002248 if (tickerView != null) {
Joe Onoratoef1e7762010-09-17 18:38:38 -04002249 parcel.writeInt(1);
Joe Onorato46439ce2010-11-19 13:56:21 -08002250 tickerView.writeToParcel(parcel, 0);
Joe Onoratoef1e7762010-09-17 18:38:38 -04002251 } else {
2252 parcel.writeInt(0);
2253 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002254 if (contentView != null) {
2255 parcel.writeInt(1);
2256 contentView.writeToParcel(parcel, 0);
2257 } else {
2258 parcel.writeInt(0);
2259 }
Selim Cinek279fa862016-06-14 10:57:25 -07002260 if (mLargeIcon == null && largeIcon != null) {
2261 // you snuck an icon in here without using the builder; let's try to keep it
2262 mLargeIcon = Icon.createWithBitmap(largeIcon);
2263 }
Dan Sandlerd63f9322015-05-06 15:18:49 -04002264 if (mLargeIcon != null) {
Joe Onorato561d3852010-11-20 18:09:34 -08002265 parcel.writeInt(1);
Dan Sandlerd63f9322015-05-06 15:18:49 -04002266 mLargeIcon.writeToParcel(parcel, 0);
Joe Onorato561d3852010-11-20 18:09:34 -08002267 } else {
2268 parcel.writeInt(0);
2269 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002270
2271 parcel.writeInt(defaults);
2272 parcel.writeInt(this.flags);
2273
2274 if (sound != null) {
2275 parcel.writeInt(1);
2276 sound.writeToParcel(parcel, 0);
2277 } else {
2278 parcel.writeInt(0);
2279 }
2280 parcel.writeInt(audioStreamType);
John Spurlockc0650f022014-07-19 13:22:39 -04002281
2282 if (audioAttributes != null) {
2283 parcel.writeInt(1);
2284 audioAttributes.writeToParcel(parcel, 0);
2285 } else {
2286 parcel.writeInt(0);
2287 }
2288
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002289 parcel.writeLongArray(vibrate);
2290 parcel.writeInt(ledARGB);
2291 parcel.writeInt(ledOnMS);
2292 parcel.writeInt(ledOffMS);
2293 parcel.writeInt(iconLevel);
Daniel Sandlere46cbd32010-06-17 10:35:26 -04002294
2295 if (fullScreenIntent != null) {
2296 parcel.writeInt(1);
2297 fullScreenIntent.writeToParcel(parcel, 0);
2298 } else {
2299 parcel.writeInt(0);
2300 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002301
2302 parcel.writeInt(priority);
Joe Malin8d40d042012-11-05 11:36:40 -08002303
John Spurlockfd7f1e02014-03-18 16:41:57 -04002304 parcel.writeString(category);
Joe Malin8d40d042012-11-05 11:36:40 -08002305
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002306 parcel.writeString(mGroupKey);
2307
2308 parcel.writeString(mSortKey);
2309
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002310 parcel.writeBundle(extras); // null ok
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002311
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002312 parcel.writeTypedArray(actions, 0); // null ok
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002313
Daniel Sandlerf3b73432012-03-27 15:01:25 -04002314 if (bigContentView != null) {
2315 parcel.writeInt(1);
2316 bigContentView.writeToParcel(parcel, 0);
2317 } else {
2318 parcel.writeInt(0);
2319 }
Dan Sandler0bf2ed82013-12-21 23:33:41 -06002320
Chris Wren8fd39ec2014-02-27 17:43:26 -05002321 if (headsUpContentView != null) {
2322 parcel.writeInt(1);
2323 headsUpContentView.writeToParcel(parcel, 0);
2324 } else {
2325 parcel.writeInt(0);
2326 }
2327
Dan Sandler0bf2ed82013-12-21 23:33:41 -06002328 parcel.writeInt(visibility);
2329
2330 if (publicVersion != null) {
2331 parcel.writeInt(1);
2332 publicVersion.writeToParcel(parcel, 0);
2333 } else {
2334 parcel.writeInt(0);
2335 }
Dan Sandler26e81cf2014-05-06 10:01:27 -04002336
2337 parcel.writeInt(color);
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04002338
2339 if (mChannelId != null) {
2340 parcel.writeInt(1);
2341 parcel.writeString(mChannelId);
2342 } else {
2343 parcel.writeInt(0);
2344 }
Julia Reynolds2a128742016-11-28 14:29:25 -05002345 parcel.writeLong(mTimeout);
Julia Reynolds13d898c2017-02-02 12:22:05 -05002346
2347 if (mShortcutId != null) {
2348 parcel.writeInt(1);
2349 parcel.writeString(mShortcutId);
2350 } else {
2351 parcel.writeInt(0);
2352 }
2353
2354 parcel.writeInt(mBadgeIcon);
Julia Reynolds3aedded2017-03-31 14:42:09 -04002355
2356 if (mSettingsText != null) {
2357 parcel.writeInt(1);
2358 TextUtils.writeToParcel(mSettingsText, parcel, flags);
2359 } else {
2360 parcel.writeInt(0);
2361 }
Julia Reynoldsa79c3712017-04-21 10:29:57 -04002362
2363 parcel.writeInt(mGroupAlertBehavior);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002364 }
2365
2366 /**
2367 * Parcelable.Creator that instantiates Notification objects
2368 */
2369 public static final Parcelable.Creator<Notification> CREATOR
2370 = new Parcelable.Creator<Notification>()
2371 {
2372 public Notification createFromParcel(Parcel parcel)
2373 {
2374 return new Notification(parcel);
2375 }
2376
2377 public Notification[] newArray(int size)
2378 {
2379 return new Notification[size];
2380 }
2381 };
2382
2383 /**
2384 * Sets the {@link #contentView} field to be a view with the standard "Latest Event"
2385 * layout.
2386 *
2387 * <p>Uses the {@link #icon} and {@link #when} fields to set the icon and time fields
2388 * in the view.</p>
2389 * @param context The context for your application / activity.
2390 * @param contentTitle The title that goes in the expanded entry.
2391 * @param contentText The text that goes in the expanded entry.
2392 * @param contentIntent The intent to launch when the user clicks the expanded notification.
2393 * If this is an activity, it must include the
2394 * {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} flag, which requires
Scott Main7aee61f2011-02-08 11:25:01 -08002395 * that you take care of task management as described in the
2396 * <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back
2397 * Stack</a> document.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002398 *
Joe Onorato46439ce2010-11-19 13:56:21 -08002399 * @deprecated Use {@link Builder} instead.
Chris Wrena05db382015-06-24 15:18:34 -04002400 * @removed
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002401 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002402 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002403 public void setLatestEventInfo(Context context,
2404 CharSequence contentTitle, CharSequence contentText, PendingIntent contentIntent) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002405 if (context.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1){
2406 Log.e(TAG, "setLatestEventInfo() is deprecated and you should feel deprecated.",
2407 new Throwable());
2408 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002409
Selim Cinek4ac6f602016-06-13 15:47:03 -07002410 if (context.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N) {
2411 extras.putBoolean(EXTRA_SHOW_WHEN, true);
2412 }
2413
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002414 // ensure that any information already set directly is preserved
2415 final Notification.Builder builder = new Notification.Builder(context, this);
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002416
2417 // now apply the latestEventInfo fields
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002418 if (contentTitle != null) {
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002419 builder.setContentTitle(contentTitle);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002420 }
2421 if (contentText != null) {
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002422 builder.setContentText(contentText);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002423 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002424 builder.setContentIntent(contentIntent);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002425
2426 builder.build(); // callers expect this notification to be ready to use
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002427 }
2428
Julia Reynoldsda303542015-11-23 14:00:20 -05002429 /**
2430 * @hide
2431 */
2432 public static void addFieldsFromContext(Context context, Notification notification) {
Julia Reynoldse071abd2017-03-22 10:52:11 -04002433 addFieldsFromContext(context.getApplicationInfo(), notification);
Jeff Sharkey012bc7b2016-04-11 16:30:27 -06002434 }
2435
2436 /**
2437 * @hide
2438 */
Julia Reynoldse071abd2017-03-22 10:52:11 -04002439 public static void addFieldsFromContext(ApplicationInfo ai, Notification notification) {
Jeff Sharkey012bc7b2016-04-11 16:30:27 -06002440 notification.extras.putParcelable(EXTRA_BUILDER_APPLICATION_INFO, ai);
Julia Reynoldsda303542015-11-23 14:00:20 -05002441 }
2442
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002443 @Override
2444 public String toString() {
2445 StringBuilder sb = new StringBuilder();
Julia Reynoldsb9e712e2017-04-17 10:31:03 -04002446 sb.append("Notification(channel=");
Julia Reynoldsbad42972017-04-25 13:52:49 -04002447 sb.append(getChannelId());
Julia Reynoldsb9e712e2017-04-17 10:31:03 -04002448 sb.append(" pri=");
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002449 sb.append(priority);
2450 sb.append(" contentView=");
Joe Onoratoc9596d62011-01-12 17:03:11 -08002451 if (contentView != null) {
2452 sb.append(contentView.getPackage());
2453 sb.append("/0x");
2454 sb.append(Integer.toHexString(contentView.getLayoutId()));
2455 } else {
2456 sb.append("null");
2457 }
2458 sb.append(" vibrate=");
Daniel Sandler6738eee2012-11-16 12:03:32 -05002459 if ((this.defaults & DEFAULT_VIBRATE) != 0) {
2460 sb.append("default");
2461 } else if (this.vibrate != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002462 int N = this.vibrate.length-1;
2463 sb.append("[");
2464 for (int i=0; i<N; i++) {
2465 sb.append(this.vibrate[i]);
2466 sb.append(',');
2467 }
Simon Schoar8cf97d92009-06-10 22:08:37 +02002468 if (N != -1) {
2469 sb.append(this.vibrate[N]);
2470 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002471 sb.append("]");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002472 } else {
2473 sb.append("null");
2474 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002475 sb.append(" sound=");
Daniel Sandler6738eee2012-11-16 12:03:32 -05002476 if ((this.defaults & DEFAULT_SOUND) != 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002477 sb.append("default");
Daniel Sandler6738eee2012-11-16 12:03:32 -05002478 } else if (this.sound != null) {
2479 sb.append(this.sound.toString());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002480 } else {
2481 sb.append("null");
2482 }
Chris Wren365b6d32015-07-16 10:39:26 -04002483 if (this.tickerText != null) {
2484 sb.append(" tick");
2485 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002486 sb.append(" defaults=0x");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002487 sb.append(Integer.toHexString(this.defaults));
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002488 sb.append(" flags=0x");
Daniel Sandlere46cbd32010-06-17 10:35:26 -04002489 sb.append(Integer.toHexString(this.flags));
Dan Sandler26e81cf2014-05-06 10:01:27 -04002490 sb.append(String.format(" color=0x%08x", this.color));
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002491 if (this.category != null) {
2492 sb.append(" category=");
2493 sb.append(this.category);
2494 }
2495 if (this.mGroupKey != null) {
2496 sb.append(" groupKey=");
2497 sb.append(this.mGroupKey);
2498 }
2499 if (this.mSortKey != null) {
2500 sb.append(" sortKey=");
2501 sb.append(this.mSortKey);
2502 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002503 if (actions != null) {
Dan Sandler1b718782014-07-18 12:43:45 -04002504 sb.append(" actions=");
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002505 sb.append(actions.length);
Dan Sandler1b718782014-07-18 12:43:45 -04002506 }
2507 sb.append(" vis=");
2508 sb.append(visibilityToString(this.visibility));
2509 if (this.publicVersion != null) {
2510 sb.append(" publicVersion=");
2511 sb.append(publicVersion.toString());
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002512 }
2513 sb.append(")");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002514 return sb.toString();
2515 }
Joe Onorato46439ce2010-11-19 13:56:21 -08002516
Dan Sandler1b718782014-07-18 12:43:45 -04002517 /**
2518 * {@hide}
2519 */
2520 public static String visibilityToString(int vis) {
2521 switch (vis) {
2522 case VISIBILITY_PRIVATE:
2523 return "PRIVATE";
2524 case VISIBILITY_PUBLIC:
2525 return "PUBLIC";
2526 case VISIBILITY_SECRET:
2527 return "SECRET";
2528 default:
2529 return "UNKNOWN(" + String.valueOf(vis) + ")";
2530 }
2531 }
2532
Joe Onoratocb109a02011-01-18 17:57:41 -08002533 /**
John Spurlock1d881a12015-03-18 19:21:54 -04002534 * {@hide}
2535 */
2536 public static String priorityToString(@Priority int pri) {
2537 switch (pri) {
2538 case PRIORITY_MIN:
2539 return "MIN";
2540 case PRIORITY_LOW:
2541 return "LOW";
2542 case PRIORITY_DEFAULT:
2543 return "DEFAULT";
2544 case PRIORITY_HIGH:
2545 return "HIGH";
2546 case PRIORITY_MAX:
2547 return "MAX";
2548 default:
2549 return "UNKNOWN(" + String.valueOf(pri) + ")";
2550 }
2551 }
2552
Julia Reynolds6ad0aec2017-07-05 08:47:03 -04002553 /**
2554 * @hide
2555 */
2556 public boolean hasCompletedProgress() {
2557 // not a progress notification; can't be complete
2558 if (!extras.containsKey(EXTRA_PROGRESS)
2559 || !extras.containsKey(EXTRA_PROGRESS_MAX)) {
2560 return false;
2561 }
2562 // many apps use max 0 for 'indeterminate'; not complete
2563 if (extras.getInt(EXTRA_PROGRESS_MAX) == 0) {
2564 return false;
2565 }
2566 return extras.getInt(EXTRA_PROGRESS) == extras.getInt(EXTRA_PROGRESS_MAX);
2567 }
2568
Jeff Sharkey000ce802017-04-29 13:13:27 -06002569 /** @removed */
2570 @Deprecated
Julia Reynolds37856052016-11-11 09:20:07 -05002571 public String getChannel() {
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04002572 return mChannelId;
2573 }
2574
2575 /**
Julia Reynoldsbad42972017-04-25 13:52:49 -04002576 * Returns the id of the channel this notification posts to.
2577 */
2578 public String getChannelId() {
2579 return mChannelId;
2580 }
2581
Jeff Sharkey000ce802017-04-29 13:13:27 -06002582 /** @removed */
2583 @Deprecated
Julia Reynolds2a128742016-11-28 14:29:25 -05002584 public long getTimeout() {
2585 return mTimeout;
2586 }
2587
2588 /**
Julia Reynoldsbad42972017-04-25 13:52:49 -04002589 * Returns the duration from posting after which this notification should be canceled by the
2590 * system, if it's not canceled already.
2591 */
2592 public long getTimeoutAfter() {
2593 return mTimeout;
2594 }
2595
2596 /**
Julia Reynoldse071abd2017-03-22 10:52:11 -04002597 * Returns what icon should be shown for this notification if it is being displayed in a
2598 * Launcher that supports badging. Will be one of {@link #BADGE_ICON_NONE},
2599 * {@link #BADGE_ICON_SMALL}, or {@link #BADGE_ICON_LARGE}.
2600 */
2601 public int getBadgeIconType() {
2602 return mBadgeIcon;
2603 }
2604
2605 /**
Julia Reynolds13d898c2017-02-02 12:22:05 -05002606 * Returns the {@link ShortcutInfo#getId() id} that this notification supersedes, if any.
Julia Reynoldsbad42972017-04-25 13:52:49 -04002607 *
2608 * <p>Used by some Launchers that display notification content to hide shortcuts that duplicate
2609 * notifications.
Julia Reynolds13d898c2017-02-02 12:22:05 -05002610 */
2611 public String getShortcutId() {
2612 return mShortcutId;
2613 }
2614
Julia Reynolds3aedded2017-03-31 14:42:09 -04002615
2616 /**
2617 * Returns the settings text provided to {@link Builder#setSettingsText(CharSequence)}.
2618 */
2619 public CharSequence getSettingsText() {
2620 return mSettingsText;
2621 }
2622
Julia Reynolds13d898c2017-02-02 12:22:05 -05002623 /**
Julia Reynoldsa79c3712017-04-21 10:29:57 -04002624 * Returns which type of notifications in a group are responsible for audibly alerting the
2625 * user. See {@link #GROUP_ALERT_ALL}, {@link #GROUP_ALERT_CHILDREN},
2626 * {@link #GROUP_ALERT_SUMMARY}.
2627 */
2628 public @GroupAlertBehavior int getGroupAlertBehavior() {
2629 return mGroupAlertBehavior;
2630 }
2631
2632 /**
Dan Sandlerd63f9322015-05-06 15:18:49 -04002633 * The small icon representing this notification in the status bar and content view.
2634 *
2635 * @return the small icon representing this notification.
2636 *
2637 * @see Builder#getSmallIcon()
2638 * @see Builder#setSmallIcon(Icon)
2639 */
2640 public Icon getSmallIcon() {
2641 return mSmallIcon;
2642 }
2643
2644 /**
2645 * Used when notifying to clean up legacy small icons.
2646 * @hide
2647 */
2648 public void setSmallIcon(Icon icon) {
2649 mSmallIcon = icon;
2650 }
2651
2652 /**
2653 * The large icon shown in this notification's content view.
2654 * @see Builder#getLargeIcon()
2655 * @see Builder#setLargeIcon(Icon)
2656 */
2657 public Icon getLargeIcon() {
2658 return mLargeIcon;
2659 }
2660
2661 /**
Christoph Studer4600f9b2014-07-22 22:44:43 +02002662 * @hide
2663 */
Christoph Studerc8db24b2014-07-25 17:50:30 +02002664 public boolean isGroupSummary() {
2665 return mGroupKey != null && (flags & FLAG_GROUP_SUMMARY) != 0;
2666 }
2667
2668 /**
2669 * @hide
2670 */
2671 public boolean isGroupChild() {
2672 return mGroupKey != null && (flags & FLAG_GROUP_SUMMARY) == 0;
2673 }
2674
2675 /**
Julia Reynolds30203152017-05-26 13:36:31 -04002676 * @hide
2677 */
2678 public boolean suppressAlertingDueToGrouping() {
2679 if (isGroupSummary()
2680 && getGroupAlertBehavior() == Notification.GROUP_ALERT_CHILDREN) {
2681 return true;
2682 } else if (isGroupChild()
2683 && getGroupAlertBehavior() == Notification.GROUP_ALERT_SUMMARY) {
2684 return true;
2685 }
2686 return false;
2687 }
2688
2689 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002690 * Builder class for {@link Notification} objects.
Joe Malin8d40d042012-11-05 11:36:40 -08002691 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002692 * Provides a convenient way to set the various fields of a {@link Notification} and generate
Scott Main183bf112012-08-13 19:12:13 -07002693 * content views using the platform's notification layout template. If your app supports
2694 * versions of Android as old as API level 4, you can instead use
2695 * {@link android.support.v4.app.NotificationCompat.Builder NotificationCompat.Builder},
2696 * available in the <a href="{@docRoot}tools/extras/support-library.html">Android Support
2697 * library</a>.
Joe Malin8d40d042012-11-05 11:36:40 -08002698 *
Scott Main183bf112012-08-13 19:12:13 -07002699 * <p>Example:
Joe Malin8d40d042012-11-05 11:36:40 -08002700 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002701 * <pre class="prettyprint">
Scott Main183bf112012-08-13 19:12:13 -07002702 * Notification noti = new Notification.Builder(mContext)
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002703 * .setContentTitle(&quot;New mail from &quot; + sender.toString())
2704 * .setContentText(subject)
2705 * .setSmallIcon(R.drawable.new_mail)
2706 * .setLargeIcon(aBitmap)
Chris Wrenfbd96ba2012-05-01 12:03:58 -04002707 * .build();
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002708 * </pre>
Joe Onoratocb109a02011-01-18 17:57:41 -08002709 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002710 public static class Builder {
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05002711 /**
2712 * @hide
2713 */
2714 public static final String EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT =
2715 "android.rebuild.contentViewActionCount";
2716 /**
2717 * @hide
2718 */
2719 public static final String EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT
2720 = "android.rebuild.bigViewActionCount";
2721 /**
2722 * @hide
2723 */
2724 public static final String EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT
2725 = "android.rebuild.hudViewActionCount";
2726
Daniel Sandler602ad1c2012-06-12 16:06:27 -04002727 private static final int MAX_ACTION_BUTTONS = 3;
Daniel Sandler8680bf82012-05-15 16:52:52 -04002728
Selim Cinek6743c0b2017-01-18 18:24:01 -08002729 private static final boolean USE_ONLY_TITLE_IN_LOW_PRIORITY_SUMMARY =
2730 SystemProperties.getBoolean("notifications.only_title", true);
2731
Selim Cinek389edcd2017-05-11 19:16:44 -07002732 /**
2733 * The lightness difference that has to be added to the primary text color to obtain the
2734 * secondary text color when the background is light.
2735 */
2736 private static final int LIGHTNESS_TEXT_DIFFERENCE_LIGHT = 20;
2737
2738 /**
2739 * The lightness difference that has to be added to the primary text color to obtain the
2740 * secondary text color when the background is dark.
2741 * A bit less then the above value, since it looks better on dark backgrounds.
2742 */
2743 private static final int LIGHTNESS_TEXT_DIFFERENCE_DARK = -10;
2744
Joe Onorato46439ce2010-11-19 13:56:21 -08002745 private Context mContext;
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002746 private Notification mN;
2747 private Bundle mUserExtras = new Bundle();
Chris Wrenfbd96ba2012-05-01 12:03:58 -04002748 private Style mStyle;
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002749 private ArrayList<Action> mActions = new ArrayList<Action>(MAX_ACTION_BUTTONS);
2750 private ArrayList<String> mPersonList = new ArrayList<String>();
2751 private NotificationColorUtil mColorUtil;
Selim Cinek7b9605b2017-01-19 17:36:00 -08002752 private boolean mIsLegacy;
2753 private boolean mIsLegacyInitialized;
Christoph Studer7ac80e62014-08-04 16:01:57 +02002754
2755 /**
Adrian Roos4ff3b122016-02-01 12:26:13 -08002756 * Caches a contrast-enhanced version of {@link #mCachedContrastColorIsFor}.
2757 */
2758 private int mCachedContrastColor = COLOR_INVALID;
2759 private int mCachedContrastColorIsFor = COLOR_INVALID;
Adrian Roos487374f2017-01-11 15:48:14 -08002760 /**
2761 * Caches a ambient version of {@link #mCachedContrastColorIsFor}.
2762 */
2763 private int mCachedAmbientColor = COLOR_INVALID;
2764 private int mCachedAmbientColorIsFor = COLOR_INVALID;
Adrian Roos4ff3b122016-02-01 12:26:13 -08002765
2766 /**
Adrian Roos70d7aa32017-01-11 15:39:06 -08002767 * Caches an instance of StandardTemplateParams. Note that this may have been used before,
2768 * so make sure to call {@link StandardTemplateParams#reset()} before using it.
2769 */
2770 StandardTemplateParams mParams = new StandardTemplateParams();
Selim Cinek7b9605b2017-01-19 17:36:00 -08002771 private int mTextColorsAreForBackground = COLOR_INVALID;
2772 private int mPrimaryTextColor = COLOR_INVALID;
2773 private int mSecondaryTextColor = COLOR_INVALID;
2774 private int mActionBarColor = COLOR_INVALID;
Selim Cinek5fb73f82017-04-20 16:55:38 -07002775 private int mBackgroundColor = COLOR_INVALID;
2776 private int mForegroundColor = COLOR_INVALID;
Selim Cinekac5f0272017-05-02 16:05:41 -07002777 private int mBackgroundColorHint = COLOR_INVALID;
Selim Cinek5d6ef8d2017-05-18 22:16:00 -07002778 /**
2779 * A temporary location where actions are stored. If != null the view originally has action
2780 * but doesn't have any for this inflation.
2781 */
2782 private ArrayList<Action> mOriginalActions;
Selim Cineka7679b62017-05-10 16:33:25 -07002783 private boolean mRebuildStyledRemoteViews;
Adrian Roos70d7aa32017-01-11 15:39:06 -08002784
Anthony Chenad4d1582017-04-10 16:07:58 -07002785 private boolean mTintActionButtons;
2786 private boolean mInNightMode;
2787
Adrian Roos70d7aa32017-01-11 15:39:06 -08002788 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002789 * Constructs a new Builder with the defaults:
Joe Onoratocb109a02011-01-18 17:57:41 -08002790 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002791 * @param context
2792 * A {@link Context} that will be used by the Builder to construct the
2793 * RemoteViews. The Context will not be held past the lifetime of this Builder
2794 * object.
Geoffrey Pitsch5caa2762017-01-12 09:35:54 -05002795 * @param channelId
2796 * The constructed Notification will be posted on this
2797 * {@link NotificationChannel}. To use a NotificationChannel, it must first be
2798 * created using {@link NotificationManager#createNotificationChannel}.
Joe Onoratocb109a02011-01-18 17:57:41 -08002799 */
Geoffrey Pitsch5caa2762017-01-12 09:35:54 -05002800 public Builder(Context context, String channelId) {
2801 this(context, (Notification) null);
2802 mN.mChannelId = channelId;
2803 }
2804
2805 /**
2806 * @deprecated use {@link Notification.Builder#Notification.Builder(Context, String)}
2807 * instead. All posted Notifications must specify a NotificationChannel Id.
2808 */
2809 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08002810 public Builder(Context context) {
Geoffrey Pitsch5caa2762017-01-12 09:35:54 -05002811 this(context, (Notification) null);
Joe Onorato46439ce2010-11-19 13:56:21 -08002812 }
2813
Joe Onoratocb109a02011-01-18 17:57:41 -08002814 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002815 * @hide
Christoph Studer4600f9b2014-07-22 22:44:43 +02002816 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002817 public Builder(Context context, Notification toAdopt) {
2818 mContext = context;
Anthony Chenad4d1582017-04-10 16:07:58 -07002819 Resources res = mContext.getResources();
2820 mTintActionButtons = res.getBoolean(R.bool.config_tintNotificationActionButtons);
2821
2822 if (res.getBoolean(R.bool.config_enableNightMode)) {
2823 Configuration currentConfig = res.getConfiguration();
2824 mInNightMode = (currentConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK)
2825 == Configuration.UI_MODE_NIGHT_YES;
2826 }
Christoph Studer4600f9b2014-07-22 22:44:43 +02002827
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002828 if (toAdopt == null) {
2829 mN = new Notification();
Selim Cinek0ff1ce602016-04-05 18:27:16 -07002830 if (context.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N) {
2831 mN.extras.putBoolean(EXTRA_SHOW_WHEN, true);
2832 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002833 mN.priority = PRIORITY_DEFAULT;
2834 mN.visibility = VISIBILITY_PRIVATE;
2835 } else {
2836 mN = toAdopt;
2837 if (mN.actions != null) {
2838 Collections.addAll(mActions, mN.actions);
Christoph Studer4600f9b2014-07-22 22:44:43 +02002839 }
2840
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002841 if (mN.extras.containsKey(EXTRA_PEOPLE)) {
2842 Collections.addAll(mPersonList, mN.extras.getStringArray(EXTRA_PEOPLE));
2843 }
2844
Selim Cinek4ac6f602016-06-13 15:47:03 -07002845 if (mN.getSmallIcon() == null && mN.icon != 0) {
2846 setSmallIcon(mN.icon);
2847 }
2848
2849 if (mN.getLargeIcon() == null && mN.largeIcon != null) {
2850 setLargeIcon(mN.largeIcon);
2851 }
2852
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002853 String templateClass = mN.extras.getString(EXTRA_TEMPLATE);
2854 if (!TextUtils.isEmpty(templateClass)) {
2855 final Class<? extends Style> styleClass
2856 = getNotificationStyleClass(templateClass);
2857 if (styleClass == null) {
2858 Log.d(TAG, "Unknown style class: " + templateClass);
2859 } else {
2860 try {
Adrian Roosc1a80b02016-04-05 14:54:55 -07002861 final Constructor<? extends Style> ctor =
2862 styleClass.getDeclaredConstructor();
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002863 ctor.setAccessible(true);
2864 final Style style = ctor.newInstance();
2865 style.restoreFromExtras(mN.extras);
2866
2867 if (style != null) {
2868 setStyle(style);
2869 }
2870 } catch (Throwable t) {
2871 Log.e(TAG, "Could not create Style", t);
2872 }
2873 }
2874 }
2875
2876 }
2877 }
2878
2879 private NotificationColorUtil getColorUtil() {
Selim Cinek99104832017-01-25 14:47:33 -08002880 if (mColorUtil == null) {
2881 mColorUtil = NotificationColorUtil.getInstance(mContext);
Christoph Studer4600f9b2014-07-22 22:44:43 +02002882 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002883 return mColorUtil;
Christoph Studer4600f9b2014-07-22 22:44:43 +02002884 }
2885
2886 /**
Julia Reynolds13d898c2017-02-02 12:22:05 -05002887 * If this notification is duplicative of a Launcher shortcut, sets the
2888 * {@link ShortcutInfo#getId() id} of the shortcut, in case the Launcher wants to hide
2889 * the shortcut.
2890 *
Julia Reynoldsbad42972017-04-25 13:52:49 -04002891 * This field will be ignored by Launchers that don't support badging, don't show
2892 * notification content, or don't show {@link android.content.pm.ShortcutManager shortcuts}.
Julia Reynolds13d898c2017-02-02 12:22:05 -05002893 *
2894 * @param shortcutId the {@link ShortcutInfo#getId() id} of the shortcut this notification
2895 * supersedes
2896 */
2897 public Builder setShortcutId(String shortcutId) {
2898 mN.mShortcutId = shortcutId;
2899 return this;
2900 }
2901
2902 /**
Julia Reynoldse071abd2017-03-22 10:52:11 -04002903 * Sets which icon to display as a badge for this notification.
2904 *
2905 * Must be one of {@link #BADGE_ICON_NONE}, {@link #BADGE_ICON_SMALL},
2906 * {@link #BADGE_ICON_LARGE}.
2907 *
2908 * Note: This value might be ignored, for launchers that don't support badge icons.
2909 */
Julia Reynolds612beb22017-03-30 10:48:30 -04002910 public Builder setBadgeIconType(int icon) {
Julia Reynoldse071abd2017-03-22 10:52:11 -04002911 mN.mBadgeIcon = icon;
2912 return this;
2913 }
2914
2915 /**
Julia Reynoldsa79c3712017-04-21 10:29:57 -04002916 * Sets the group alert behavior for this notification. Use this method to mute this
2917 * notification if alerts for this notification's group should be handled by a different
2918 * notification. This is only applicable for notifications that belong to a
Julia Reynolds399d9bf2017-08-11 12:52:14 -04002919 * {@link #setGroup(String) group}. This must be called on all notifications you want to
2920 * mute. For example, if you want only the summary of your group to make noise, all
2921 * children in the group should have the group alert behavior {@link #GROUP_ALERT_SUMMARY}.
Julia Reynoldsa79c3712017-04-21 10:29:57 -04002922 *
2923 * <p> The default value is {@link #GROUP_ALERT_ALL}.</p>
2924 */
2925 public Builder setGroupAlertBehavior(@GroupAlertBehavior int groupAlertBehavior) {
2926 mN.mGroupAlertBehavior = groupAlertBehavior;
2927 return this;
2928 }
2929
Jeff Sharkey000ce802017-04-29 13:13:27 -06002930 /** @removed */
2931 @Deprecated
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04002932 public Builder setChannel(String channelId) {
2933 mN.mChannelId = channelId;
2934 return this;
2935 }
2936
2937 /**
Julia Reynoldsbad42972017-04-25 13:52:49 -04002938 * Specifies the channel the notification should be delivered on.
2939 */
2940 public Builder setChannelId(String channelId) {
2941 mN.mChannelId = channelId;
2942 return this;
2943 }
2944
Jeff Sharkey000ce802017-04-29 13:13:27 -06002945 /** @removed */
2946 @Deprecated
Julia Reynolds50989772017-02-23 14:32:16 -05002947 public Builder setTimeout(long durationMs) {
2948 mN.mTimeout = durationMs;
Julia Reynolds2a128742016-11-28 14:29:25 -05002949 return this;
2950 }
2951
2952 /**
Julia Reynoldsbad42972017-04-25 13:52:49 -04002953 * Specifies a duration in milliseconds after which this notification should be canceled,
2954 * if it is not already canceled.
2955 */
2956 public Builder setTimeoutAfter(long durationMs) {
2957 mN.mTimeout = durationMs;
2958 return this;
2959 }
2960
2961 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002962 * Add a timestamp pertaining to the notification (usually the time the event occurred).
Selim Cinek0ff1ce602016-04-05 18:27:16 -07002963 *
2964 * For apps targeting {@link android.os.Build.VERSION_CODES#N} and above, this time is not
2965 * shown anymore by default and must be opted into by using
2966 * {@link android.app.Notification.Builder#setShowWhen(boolean)}
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002967 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002968 * @see Notification#when
Joe Onoratocb109a02011-01-18 17:57:41 -08002969 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002970 public Builder setWhen(long when) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002971 mN.when = when;
Joe Onorato46439ce2010-11-19 13:56:21 -08002972 return this;
2973 }
2974
Joe Onoratocb109a02011-01-18 17:57:41 -08002975 /**
Griff Hazen50c11652014-05-16 09:46:31 -07002976 * Control whether the timestamp set with {@link #setWhen(long) setWhen} is shown
Daniel Sandler0c890492012-09-12 17:23:10 -07002977 * in the content view.
Selim Cinek0ff1ce602016-04-05 18:27:16 -07002978 * For apps targeting {@link android.os.Build.VERSION_CODES#N} and above, this defaults to
2979 * {@code false}. For earlier apps, the default is {@code true}.
Daniel Sandler0c890492012-09-12 17:23:10 -07002980 */
2981 public Builder setShowWhen(boolean show) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002982 mN.extras.putBoolean(EXTRA_SHOW_WHEN, show);
Daniel Sandler0c890492012-09-12 17:23:10 -07002983 return this;
2984 }
2985
2986 /**
Daniel Sandlerd33b8032012-05-10 11:41:48 -04002987 * Show the {@link Notification#when} field as a stopwatch.
Joe Malin8d40d042012-11-05 11:36:40 -08002988 *
2989 * Instead of presenting <code>when</code> as a timestamp, the notification will show an
Daniel Sandlerd33b8032012-05-10 11:41:48 -04002990 * automatically updating display of the minutes and seconds since <code>when</code>.
Daniel Sandlera2985ed2012-04-03 16:42:00 -04002991 *
Daniel Sandlerd33b8032012-05-10 11:41:48 -04002992 * Useful when showing an elapsed time (like an ongoing phone call).
2993 *
Selim Cinek81c23aa2016-02-25 16:23:13 -08002994 * The counter can also be set to count down to <code>when</code> when using
Adrian Roos96b7e202016-05-17 13:50:38 -07002995 * {@link #setChronometerCountDown(boolean)}.
Selim Cinek81c23aa2016-02-25 16:23:13 -08002996 *
Daniel Sandlerd33b8032012-05-10 11:41:48 -04002997 * @see android.widget.Chronometer
Daniel Sandlera2985ed2012-04-03 16:42:00 -04002998 * @see Notification#when
Adrian Roos96b7e202016-05-17 13:50:38 -07002999 * @see #setChronometerCountDown(boolean)
Daniel Sandlera2985ed2012-04-03 16:42:00 -04003000 */
3001 public Builder setUsesChronometer(boolean b) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003002 mN.extras.putBoolean(EXTRA_SHOW_CHRONOMETER, b);
Daniel Sandlera2985ed2012-04-03 16:42:00 -04003003 return this;
3004 }
3005
3006 /**
Selim Cinek81c23aa2016-02-25 16:23:13 -08003007 * Sets the Chronometer to count down instead of counting up.
3008 *
3009 * <p>This is only relevant if {@link #setUsesChronometer(boolean)} has been set to true.
3010 * If it isn't set the chronometer will count up.
3011 *
3012 * @see #setUsesChronometer(boolean)
3013 */
Adrian Roos96b7e202016-05-17 13:50:38 -07003014 public Builder setChronometerCountDown(boolean countDown) {
3015 mN.extras.putBoolean(EXTRA_CHRONOMETER_COUNT_DOWN, countDown);
Selim Cinek81c23aa2016-02-25 16:23:13 -08003016 return this;
3017 }
3018
3019 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003020 * Set the small icon resource, which will be used to represent the notification in the
3021 * status bar.
Joe Onoratocb109a02011-01-18 17:57:41 -08003022 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003023
3024 * The platform template for the expanded view will draw this icon in the left, unless a
3025 * {@link #setLargeIcon(Bitmap) large icon} has also been specified, in which case the small
3026 * icon will be moved to the right-hand side.
3027 *
3028
3029 * @param icon
3030 * A resource ID in the application's package of the drawable to use.
3031 * @see Notification#icon
Joe Onoratocb109a02011-01-18 17:57:41 -08003032 */
Tor Norbye7b9c9122013-05-30 16:48:33 -07003033 public Builder setSmallIcon(@DrawableRes int icon) {
Dan Sandlerd63f9322015-05-06 15:18:49 -04003034 return setSmallIcon(icon != 0
3035 ? Icon.createWithResource(mContext, icon)
3036 : null);
Joe Onorato46439ce2010-11-19 13:56:21 -08003037 }
3038
Joe Onoratocb109a02011-01-18 17:57:41 -08003039 /**
3040 * A variant of {@link #setSmallIcon(int) setSmallIcon(int)} that takes an additional
3041 * level parameter for when the icon is a {@link android.graphics.drawable.LevelListDrawable
3042 * LevelListDrawable}.
3043 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003044 * @param icon A resource ID in the application's package of the drawable to use.
Joe Onoratocb109a02011-01-18 17:57:41 -08003045 * @param level The level to use for the icon.
3046 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003047 * @see Notification#icon
3048 * @see Notification#iconLevel
Joe Onoratocb109a02011-01-18 17:57:41 -08003049 */
Tor Norbye7b9c9122013-05-30 16:48:33 -07003050 public Builder setSmallIcon(@DrawableRes int icon, int level) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003051 mN.iconLevel = level;
Dan Sandlerd63f9322015-05-06 15:18:49 -04003052 return setSmallIcon(icon);
3053 }
3054
3055 /**
3056 * Set the small icon, which will be used to represent the notification in the
3057 * status bar and content view (unless overriden there by a
3058 * {@link #setLargeIcon(Bitmap) large icon}).
3059 *
3060 * @param icon An Icon object to use.
3061 * @see Notification#icon
3062 */
3063 public Builder setSmallIcon(Icon icon) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003064 mN.setSmallIcon(icon);
3065 if (icon != null && icon.getType() == Icon.TYPE_RESOURCE) {
3066 mN.icon = icon.getResId();
3067 }
Joe Onorato46439ce2010-11-19 13:56:21 -08003068 return this;
3069 }
3070
Joe Onoratocb109a02011-01-18 17:57:41 -08003071 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003072 * Set the first line of text in the platform notification template.
Joe Onoratocb109a02011-01-18 17:57:41 -08003073 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003074 public Builder setContentTitle(CharSequence title) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003075 mN.extras.putCharSequence(EXTRA_TITLE, safeCharSequence(title));
Joe Onorato46439ce2010-11-19 13:56:21 -08003076 return this;
3077 }
3078
Joe Onoratocb109a02011-01-18 17:57:41 -08003079 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003080 * Set the second line of text in the platform notification template.
Joe Onoratocb109a02011-01-18 17:57:41 -08003081 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003082 public Builder setContentText(CharSequence text) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003083 mN.extras.putCharSequence(EXTRA_TEXT, safeCharSequence(text));
Joe Onorato46439ce2010-11-19 13:56:21 -08003084 return this;
3085 }
3086
Joe Onoratocb109a02011-01-18 17:57:41 -08003087 /**
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07003088 * This provides some additional information that is displayed in the notification. No
3089 * guarantees are given where exactly it is displayed.
3090 *
3091 * <p>This information should only be provided if it provides an essential
3092 * benefit to the understanding of the notification. The more text you provide the
3093 * less readable it becomes. For example, an email client should only provide the account
3094 * name here if more than one email account has been added.</p>
3095 *
3096 * <p>As of {@link android.os.Build.VERSION_CODES#N} this information is displayed in the
3097 * notification header area.
3098 *
3099 * On Android versions before {@link android.os.Build.VERSION_CODES#N}
3100 * this will be shown in the third line of text in the platform notification template.
3101 * You should not be using {@link #setProgress(int, int, boolean)} at the
3102 * same time on those versions; they occupy the same place.
3103 * </p>
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003104 */
3105 public Builder setSubText(CharSequence text) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003106 mN.extras.putCharSequence(EXTRA_SUB_TEXT, safeCharSequence(text));
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003107 return this;
3108 }
3109
3110 /**
Julia Reynolds3aedded2017-03-31 14:42:09 -04003111 * Provides text that will appear as a link to your application's settings.
3112 *
3113 * <p>This text does not appear within notification {@link Style templates} but may
3114 * appear when the user uses an affordance to learn more about the notification.
3115 * Additionally, this text will not appear unless you provide a valid link target by
3116 * handling {@link #INTENT_CATEGORY_NOTIFICATION_PREFERENCES}.
3117 *
3118 * <p>This text is meant to be concise description about what the user can customize
3119 * when they click on this link. The recommended maximum length is 40 characters.
3120 * @param text
3121 * @return
3122 */
3123 public Builder setSettingsText(CharSequence text) {
3124 mN.mSettingsText = safeCharSequence(text);
3125 return this;
3126 }
3127
3128 /**
Adrian Roose458aa82015-12-08 16:17:19 -08003129 * Set the remote input history.
3130 *
3131 * This should be set to the most recent inputs that have been sent
3132 * through a {@link RemoteInput} of this Notification and cleared once the it is no
3133 * longer relevant (e.g. for chat notifications once the other party has responded).
3134 *
3135 * The most recent input must be stored at the 0 index, the second most recent at the
3136 * 1 index, etc. Note that the system will limit both how far back the inputs will be shown
3137 * and how much of each individual input is shown.
3138 *
3139 * <p>Note: The reply text will only be shown on notifications that have least one action
3140 * with a {@code RemoteInput}.</p>
3141 */
3142 public Builder setRemoteInputHistory(CharSequence[] text) {
3143 if (text == null) {
3144 mN.extras.putCharSequenceArray(EXTRA_REMOTE_INPUT_HISTORY, null);
3145 } else {
3146 final int N = Math.min(MAX_REPLY_HISTORY, text.length);
3147 CharSequence[] safe = new CharSequence[N];
3148 for (int i = 0; i < N; i++) {
3149 safe[i] = safeCharSequence(text[i]);
3150 }
3151 mN.extras.putCharSequenceArray(EXTRA_REMOTE_INPUT_HISTORY, safe);
3152 }
3153 return this;
3154 }
3155
3156 /**
Julia Reynolds13d898c2017-02-02 12:22:05 -05003157 * Sets the number of items this notification represents. May be displayed as a badge count
3158 * for Launchers that support badging.
Joe Onoratocb109a02011-01-18 17:57:41 -08003159 */
Joe Onorato8595a3d2010-11-19 18:12:07 -08003160 public Builder setNumber(int number) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003161 mN.number = number;
Joe Onorato8595a3d2010-11-19 18:12:07 -08003162 return this;
3163 }
3164
Joe Onoratocb109a02011-01-18 17:57:41 -08003165 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003166 * A small piece of additional information pertaining to this notification.
3167 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003168 * The platform template will draw this on the last line of the notification, at the far
3169 * right (to the right of a smallIcon if it has been placed there).
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07003170 *
3171 * @deprecated use {@link #setSubText(CharSequence)} instead to set a text in the header.
3172 * For legacy apps targeting a version below {@link android.os.Build.VERSION_CODES#N} this
3173 * field will still show up, but the subtext will take precedence.
Joe Onoratocb109a02011-01-18 17:57:41 -08003174 */
Aurimas Liutikas514c5ef2016-05-24 15:22:55 -07003175 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08003176 public Builder setContentInfo(CharSequence info) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003177 mN.extras.putCharSequence(EXTRA_INFO_TEXT, safeCharSequence(info));
Joe Onorato46439ce2010-11-19 13:56:21 -08003178 return this;
3179 }
3180
Joe Onoratocb109a02011-01-18 17:57:41 -08003181 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003182 * Set the progress this notification represents.
3183 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003184 * The platform template will represent this using a {@link ProgressBar}.
Jeff Sharkey1c400132011-08-05 14:50:13 -07003185 */
3186 public Builder setProgress(int max, int progress, boolean indeterminate) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003187 mN.extras.putInt(EXTRA_PROGRESS, progress);
3188 mN.extras.putInt(EXTRA_PROGRESS_MAX, max);
3189 mN.extras.putBoolean(EXTRA_PROGRESS_INDETERMINATE, indeterminate);
Jeff Sharkey1c400132011-08-05 14:50:13 -07003190 return this;
3191 }
3192
3193 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003194 * Supply a custom RemoteViews to use instead of the platform template.
3195 *
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003196 * Use {@link #setCustomContentView(RemoteViews)} instead.
Joe Onoratocb109a02011-01-18 17:57:41 -08003197 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003198 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08003199 public Builder setContent(RemoteViews views) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003200 return setCustomContentView(views);
3201 }
3202
3203 /**
3204 * Supply custom RemoteViews to use instead of the platform template.
3205 *
3206 * This will override the layout that would otherwise be constructed by this Builder
3207 * object.
3208 */
3209 public Builder setCustomContentView(RemoteViews contentView) {
3210 mN.contentView = contentView;
3211 return this;
3212 }
3213
3214 /**
3215 * Supply custom RemoteViews to use instead of the platform template in the expanded form.
3216 *
3217 * This will override the expanded layout that would otherwise be constructed by this
3218 * Builder object.
3219 */
3220 public Builder setCustomBigContentView(RemoteViews contentView) {
3221 mN.bigContentView = contentView;
3222 return this;
3223 }
3224
3225 /**
3226 * Supply custom RemoteViews to use instead of the platform template in the heads up dialog.
3227 *
3228 * This will override the heads-up layout that would otherwise be constructed by this
3229 * Builder object.
3230 */
3231 public Builder setCustomHeadsUpContentView(RemoteViews contentView) {
3232 mN.headsUpContentView = contentView;
Joe Onorato46439ce2010-11-19 13:56:21 -08003233 return this;
3234 }
3235
Joe Onoratocb109a02011-01-18 17:57:41 -08003236 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003237 * Supply a {@link PendingIntent} to be sent when the notification is clicked.
3238 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003239 * As of {@link android.os.Build.VERSION_CODES#HONEYCOMB}, if this field is unset and you
3240 * have specified a custom RemoteViews with {@link #setContent(RemoteViews)}, you can use
3241 * {@link RemoteViews#setOnClickPendingIntent RemoteViews.setOnClickPendingIntent(int,PendingIntent)}
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003242 * to assign PendingIntents to individual views in that custom layout (i.e., to create
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003243 * clickable buttons inside the notification view).
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003244 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003245 * @see Notification#contentIntent Notification.contentIntent
Joe Onoratocb109a02011-01-18 17:57:41 -08003246 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003247 public Builder setContentIntent(PendingIntent intent) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003248 mN.contentIntent = intent;
Joe Onorato46439ce2010-11-19 13:56:21 -08003249 return this;
3250 }
3251
Joe Onoratocb109a02011-01-18 17:57:41 -08003252 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003253 * Supply a {@link PendingIntent} to send when the notification is cleared explicitly by the user.
3254 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003255 * @see Notification#deleteIntent
Joe Onoratocb109a02011-01-18 17:57:41 -08003256 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003257 public Builder setDeleteIntent(PendingIntent intent) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003258 mN.deleteIntent = intent;
Joe Onorato46439ce2010-11-19 13:56:21 -08003259 return this;
3260 }
3261
Joe Onoratocb109a02011-01-18 17:57:41 -08003262 /**
3263 * An intent to launch instead of posting the notification to the status bar.
3264 * Only for use with extremely high-priority notifications demanding the user's
3265 * <strong>immediate</strong> attention, such as an incoming phone call or
3266 * alarm clock that the user has explicitly set to a particular time.
3267 * If this facility is used for something else, please give the user an option
3268 * to turn it off and use a normal notification, as this can be extremely
3269 * disruptive.
3270 *
Chris Wren47c20a12014-06-18 17:27:29 -04003271 * <p>
3272 * The system UI may choose to display a heads-up notification, instead of
3273 * launching this intent, while the user is using the device.
3274 * </p>
3275 *
Joe Onoratocb109a02011-01-18 17:57:41 -08003276 * @param intent The pending intent to launch.
3277 * @param highPriority Passing true will cause this notification to be sent
3278 * even if other notifications are suppressed.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003279 *
3280 * @see Notification#fullScreenIntent
Joe Onoratocb109a02011-01-18 17:57:41 -08003281 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003282 public Builder setFullScreenIntent(PendingIntent intent, boolean highPriority) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003283 mN.fullScreenIntent = intent;
Joe Onorato46439ce2010-11-19 13:56:21 -08003284 setFlag(FLAG_HIGH_PRIORITY, highPriority);
3285 return this;
3286 }
3287
Joe Onoratocb109a02011-01-18 17:57:41 -08003288 /**
Dan Sandler5fcdf6e2014-07-18 11:31:15 -04003289 * Set the "ticker" text which is sent to accessibility services.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003290 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003291 * @see Notification#tickerText
Joe Onoratocb109a02011-01-18 17:57:41 -08003292 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003293 public Builder setTicker(CharSequence tickerText) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003294 mN.tickerText = safeCharSequence(tickerText);
Joe Onorato46439ce2010-11-19 13:56:21 -08003295 return this;
3296 }
3297
Joe Onoratocb109a02011-01-18 17:57:41 -08003298 /**
Dan Sandler5fcdf6e2014-07-18 11:31:15 -04003299 * Obsolete version of {@link #setTicker(CharSequence)}.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003300 *
Joe Onoratocb109a02011-01-18 17:57:41 -08003301 */
Dan Sandler5fcdf6e2014-07-18 11:31:15 -04003302 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08003303 public Builder setTicker(CharSequence tickerText, RemoteViews views) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003304 setTicker(tickerText);
3305 // views is ignored
Joe Onorato46439ce2010-11-19 13:56:21 -08003306 return this;
3307 }
3308
Joe Onoratocb109a02011-01-18 17:57:41 -08003309 /**
Dan Sandlerd63f9322015-05-06 15:18:49 -04003310 * Add a large icon to the notification content view.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003311 *
3312 * In the platform template, this image will be shown on the left of the notification view
Dan Sandlerd63f9322015-05-06 15:18:49 -04003313 * in place of the {@link #setSmallIcon(Icon) small icon} (which will be placed in a small
3314 * badge atop the large icon).
Dan Sandler08a04c12015-05-06 15:18:49 -04003315 */
Dan Sandlerd63f9322015-05-06 15:18:49 -04003316 public Builder setLargeIcon(Bitmap b) {
3317 return setLargeIcon(b != null ? Icon.createWithBitmap(b) : null);
3318 }
3319
3320 /**
3321 * Add a large icon to the notification content view.
3322 *
3323 * In the platform template, this image will be shown on the left of the notification view
3324 * in place of the {@link #setSmallIcon(Icon) small icon} (which will be placed in a small
3325 * badge atop the large icon).
3326 */
3327 public Builder setLargeIcon(Icon icon) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003328 mN.mLargeIcon = icon;
3329 mN.extras.putParcelable(EXTRA_LARGE_ICON, icon);
Joe Onorato46439ce2010-11-19 13:56:21 -08003330 return this;
3331 }
3332
Joe Onoratocb109a02011-01-18 17:57:41 -08003333 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003334 * Set the sound to play.
3335 *
John Spurlockc0650f022014-07-19 13:22:39 -04003336 * It will be played using the {@link #AUDIO_ATTRIBUTES_DEFAULT default audio attributes}
3337 * for notifications.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003338 *
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003339 * @deprecated use {@link NotificationChannel#setSound(Uri, AudioAttributes)} instead.
Joe Onoratocb109a02011-01-18 17:57:41 -08003340 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003341 @Deprecated
Joe Onorato52f80cd2010-11-21 15:34:48 -08003342 public Builder setSound(Uri sound) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003343 mN.sound = sound;
3344 mN.audioAttributes = AUDIO_ATTRIBUTES_DEFAULT;
Joe Onorato52f80cd2010-11-21 15:34:48 -08003345 return this;
3346 }
3347
Joe Onoratocb109a02011-01-18 17:57:41 -08003348 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003349 * Set the sound to play, along with a specific stream on which to play it.
Joe Onoratocb109a02011-01-18 17:57:41 -08003350 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003351 * See {@link android.media.AudioManager} for the <code>STREAM_</code> constants.
3352 *
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003353 * @deprecated use {@link NotificationChannel#setSound(Uri, AudioAttributes)}.
Joe Onoratocb109a02011-01-18 17:57:41 -08003354 */
Jean-Michel Trivi81f871e2014-08-06 16:32:38 -07003355 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08003356 public Builder setSound(Uri sound, int streamType) {
Jean-Michel Trivi2f7511f2016-11-28 15:40:27 -08003357 PlayerBase.deprecateStreamTypeForPlayback(streamType, "Notification", "setSound()");
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003358 mN.sound = sound;
3359 mN.audioStreamType = streamType;
Joe Onorato46439ce2010-11-19 13:56:21 -08003360 return this;
3361 }
3362
Joe Onoratocb109a02011-01-18 17:57:41 -08003363 /**
John Spurlockc0650f022014-07-19 13:22:39 -04003364 * Set the sound to play, along with specific {@link AudioAttributes audio attributes} to
3365 * use during playback.
3366 *
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003367 * @deprecated use {@link NotificationChannel#setSound(Uri, AudioAttributes)} instead.
John Spurlockc0650f022014-07-19 13:22:39 -04003368 * @see Notification#sound
3369 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003370 @Deprecated
John Spurlockc0650f022014-07-19 13:22:39 -04003371 public Builder setSound(Uri sound, AudioAttributes audioAttributes) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003372 mN.sound = sound;
3373 mN.audioAttributes = audioAttributes;
John Spurlockc0650f022014-07-19 13:22:39 -04003374 return this;
3375 }
3376
3377 /**
Joe Onoratocb109a02011-01-18 17:57:41 -08003378 * Set the vibration pattern to use.
3379 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003380 * See {@link android.os.Vibrator#vibrate(long[], int)} for a discussion of the
3381 * <code>pattern</code> parameter.
3382 *
Chris Wren47c20a12014-06-18 17:27:29 -04003383 * <p>
3384 * A notification that vibrates is more likely to be presented as a heads-up notification.
3385 * </p>
3386 *
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003387 * @deprecated use {@link NotificationChannel#setVibrationPattern(long[])} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003388 * @see Notification#vibrate
Joe Onoratocb109a02011-01-18 17:57:41 -08003389 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003390 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08003391 public Builder setVibrate(long[] pattern) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003392 mN.vibrate = pattern;
Joe Onorato46439ce2010-11-19 13:56:21 -08003393 return this;
3394 }
3395
Joe Onoratocb109a02011-01-18 17:57:41 -08003396 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003397 * Set the desired color for the indicator LED on the device, as well as the
3398 * blink duty cycle (specified in milliseconds).
3399 *
3400
3401 * Not all devices will honor all (or even any) of these values.
3402 *
Julia Reynolds529e3322017-02-06 08:33:01 -05003403 * @deprecated use {@link NotificationChannel#enableLights(boolean)} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003404 * @see Notification#ledARGB
3405 * @see Notification#ledOnMS
3406 * @see Notification#ledOffMS
Joe Onoratocb109a02011-01-18 17:57:41 -08003407 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003408 @Deprecated
Tor Norbye80756e32015-03-02 09:39:27 -08003409 public Builder setLights(@ColorInt int argb, int onMs, int offMs) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003410 mN.ledARGB = argb;
3411 mN.ledOnMS = onMs;
3412 mN.ledOffMS = offMs;
Julia Reynolds10ee1fc2015-11-09 11:04:55 -05003413 if (onMs != 0 || offMs != 0) {
3414 mN.flags |= FLAG_SHOW_LIGHTS;
3415 }
Joe Onorato46439ce2010-11-19 13:56:21 -08003416 return this;
3417 }
3418
Joe Onoratocb109a02011-01-18 17:57:41 -08003419 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003420 * Set whether this is an "ongoing" notification.
Joe Onoratocb109a02011-01-18 17:57:41 -08003421 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003422
3423 * Ongoing notifications cannot be dismissed by the user, so your application or service
3424 * must take care of canceling them.
3425 *
3426
3427 * They are typically used to indicate a background task that the user is actively engaged
3428 * with (e.g., playing music) or is pending in some way and therefore occupying the device
3429 * (e.g., a file download, sync operation, active network connection).
3430 *
3431
3432 * @see Notification#FLAG_ONGOING_EVENT
3433 * @see Service#setForeground(boolean)
Joe Onoratocb109a02011-01-18 17:57:41 -08003434 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003435 public Builder setOngoing(boolean ongoing) {
3436 setFlag(FLAG_ONGOING_EVENT, ongoing);
3437 return this;
3438 }
3439
Joe Onoratocb109a02011-01-18 17:57:41 -08003440 /**
Selim Cinek7b9605b2017-01-19 17:36:00 -08003441 * Set whether this notification should be colorized. When set, the color set with
3442 * {@link #setColor(int)} will be used as the background color of this notification.
3443 * <p>
Selim Cinek7b9605b2017-01-19 17:36:00 -08003444 * This should only be used for high priority ongoing tasks like navigation, an ongoing
3445 * call, or other similarly high-priority events for the user.
Selim Cinek99104832017-01-25 14:47:33 -08003446 * <p>
Selim Cinek22714f12017-04-13 16:23:53 -07003447 * For most styles, the coloring will only be applied if the notification is for a
3448 * foreground service notification.
Selim Cinek99104832017-01-25 14:47:33 -08003449 * However, for {@link MediaStyle} and {@link DecoratedMediaCustomViewStyle} notifications
Selim Cinek22714f12017-04-13 16:23:53 -07003450 * that have a media session attached there is no such requirement.
Selim Cinek7b9605b2017-01-19 17:36:00 -08003451 *
Selim Cinek7b9605b2017-01-19 17:36:00 -08003452 * @see Builder#setColor(int)
Selim Cinek99104832017-01-25 14:47:33 -08003453 * @see MediaStyle#setMediaSession(MediaSession.Token)
Selim Cinek7b9605b2017-01-19 17:36:00 -08003454 */
3455 public Builder setColorized(boolean colorize) {
3456 mN.extras.putBoolean(EXTRA_COLORIZED, colorize);
3457 return this;
3458 }
3459
3460 /**
Joe Onoratocb109a02011-01-18 17:57:41 -08003461 * Set this flag if you would only like the sound, vibrate
3462 * and ticker to be played if the notification is not already showing.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003463 *
3464 * @see Notification#FLAG_ONLY_ALERT_ONCE
Joe Onoratocb109a02011-01-18 17:57:41 -08003465 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003466 public Builder setOnlyAlertOnce(boolean onlyAlertOnce) {
3467 setFlag(FLAG_ONLY_ALERT_ONCE, onlyAlertOnce);
3468 return this;
3469 }
3470
Joe Onoratocb109a02011-01-18 17:57:41 -08003471 /**
Julia Reynolds04499532016-09-13 14:04:53 -04003472 * Make this notification automatically dismissed when the user touches it.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003473 *
3474 * @see Notification#FLAG_AUTO_CANCEL
Joe Onoratocb109a02011-01-18 17:57:41 -08003475 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003476 public Builder setAutoCancel(boolean autoCancel) {
Joe Onorato281d83f2011-01-04 17:13:10 -08003477 setFlag(FLAG_AUTO_CANCEL, autoCancel);
Joe Onorato46439ce2010-11-19 13:56:21 -08003478 return this;
3479 }
3480
Joe Onoratocb109a02011-01-18 17:57:41 -08003481 /**
Griff Hazendfcb0802014-02-11 12:00:00 -08003482 * Set whether or not this notification should not bridge to other devices.
3483 *
3484 * <p>Some notifications can be bridged to other devices for remote display.
3485 * This hint can be set to recommend this notification not be bridged.
3486 */
3487 public Builder setLocalOnly(boolean localOnly) {
3488 setFlag(FLAG_LOCAL_ONLY, localOnly);
3489 return this;
3490 }
3491
3492 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003493 * Set which notification properties will be inherited from system defaults.
Joe Onoratocb109a02011-01-18 17:57:41 -08003494 * <p>
3495 * The value should be one or more of the following fields combined with
3496 * bitwise-or:
3497 * {@link #DEFAULT_SOUND}, {@link #DEFAULT_VIBRATE}, {@link #DEFAULT_LIGHTS}.
3498 * <p>
3499 * For all default values, use {@link #DEFAULT_ALL}.
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003500 *
3501 * @deprecated use {@link NotificationChannel#enableVibration(boolean)} and
Julia Reynolds529e3322017-02-06 08:33:01 -05003502 * {@link NotificationChannel#enableLights(boolean)} and
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003503 * {@link NotificationChannel#setSound(Uri, AudioAttributes)} instead.
Joe Onoratocb109a02011-01-18 17:57:41 -08003504 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003505 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08003506 public Builder setDefaults(int defaults) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003507 mN.defaults = defaults;
Joe Onorato46439ce2010-11-19 13:56:21 -08003508 return this;
3509 }
3510
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003511 /**
3512 * Set the priority of this notification.
3513 *
3514 * @see Notification#priority
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003515 * @deprecated use {@link NotificationChannel#setImportance(int)} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003516 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003517 @Deprecated
Tor Norbyed9273d62013-05-30 15:59:53 -07003518 public Builder setPriority(@Priority int pri) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003519 mN.priority = pri;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003520 return this;
3521 }
Joe Malin8d40d042012-11-05 11:36:40 -08003522
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003523 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -04003524 * Set the notification category.
Joe Malin8d40d042012-11-05 11:36:40 -08003525 *
John Spurlockfd7f1e02014-03-18 16:41:57 -04003526 * @see Notification#category
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003527 */
John Spurlockfd7f1e02014-03-18 16:41:57 -04003528 public Builder setCategory(String category) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003529 mN.category = category;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003530 return this;
3531 }
3532
3533 /**
Chris Wrendde75302014-03-26 17:24:15 -04003534 * Add a person that is relevant to this notification.
3535 *
Chris Wrene6c48932014-09-29 17:19:27 -04003536 * <P>
3537 * Depending on user preferences, this annotation may allow the notification to pass
Julia Reynoldse071abd2017-03-22 10:52:11 -04003538 * through interruption filters, if this notification is of category {@link #CATEGORY_CALL}
3539 * or {@link #CATEGORY_MESSAGE}. The addition of people may also cause this notification to
3540 * appear more prominently in the user interface.
Chris Wrene6c48932014-09-29 17:19:27 -04003541 * </P>
3542 *
3543 * <P>
3544 * The person should be specified by the {@code String} representation of a
3545 * {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI}.
3546 * </P>
3547 *
3548 * <P>The system will also attempt to resolve {@code mailto:} and {@code tel:} schema
3549 * URIs. The path part of these URIs must exist in the contacts database, in the
3550 * appropriate column, or the reference will be discarded as invalid. Telephone schema
3551 * URIs will be resolved by {@link android.provider.ContactsContract.PhoneLookup}.
3552 * </P>
3553 *
3554 * @param uri A URI for the person.
Chris Wrendde75302014-03-26 17:24:15 -04003555 * @see Notification#EXTRA_PEOPLE
3556 */
Chris Wrene6c48932014-09-29 17:19:27 -04003557 public Builder addPerson(String uri) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003558 mPersonList.add(uri);
Chris Wrendde75302014-03-26 17:24:15 -04003559 return this;
3560 }
3561
3562 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003563 * Set this notification to be part of a group of notifications sharing the same key.
3564 * Grouped notifications may display in a cluster or stack on devices which
3565 * support such rendering.
3566 *
3567 * <p>To make this notification the summary for its group, also call
3568 * {@link #setGroupSummary}. A sort order can be specified for group members by using
3569 * {@link #setSortKey}.
3570 * @param groupKey The group key of the group.
3571 * @return this object for method chaining
3572 */
3573 public Builder setGroup(String groupKey) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003574 mN.mGroupKey = groupKey;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003575 return this;
3576 }
3577
3578 /**
3579 * Set this notification to be the group summary for a group of notifications.
3580 * Grouped notifications may display in a cluster or stack on devices which
Julia Reynolds04499532016-09-13 14:04:53 -04003581 * support such rendering. If thereRequires a group key also be set using {@link #setGroup}.
3582 * The group summary may be suppressed if too few notifications are included in the group.
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003583 * @param isGroupSummary Whether this notification should be a group summary.
3584 * @return this object for method chaining
3585 */
3586 public Builder setGroupSummary(boolean isGroupSummary) {
3587 setFlag(FLAG_GROUP_SUMMARY, isGroupSummary);
3588 return this;
3589 }
3590
3591 /**
3592 * Set a sort key that orders this notification among other notifications from the
3593 * same package. This can be useful if an external sort was already applied and an app
3594 * would like to preserve this. Notifications will be sorted lexicographically using this
3595 * value, although providing different priorities in addition to providing sort key may
3596 * cause this value to be ignored.
3597 *
3598 * <p>This sort key can also be used to order members of a notification group. See
Griff Hazen9e1379f2014-05-20 12:50:51 -07003599 * {@link #setGroup}.
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003600 *
3601 * @see String#compareTo(String)
3602 */
3603 public Builder setSortKey(String sortKey) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003604 mN.mSortKey = sortKey;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003605 return this;
3606 }
3607
3608 /**
Griff Hazen720042b2014-02-24 15:46:56 -08003609 * Merge additional metadata into this notification.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003610 *
Griff Hazen720042b2014-02-24 15:46:56 -08003611 * <p>Values within the Bundle will replace existing extras values in this Builder.
3612 *
3613 * @see Notification#extras
3614 */
Griff Hazen959591e2014-05-15 22:26:18 -07003615 public Builder addExtras(Bundle extras) {
3616 if (extras != null) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003617 mUserExtras.putAll(extras);
Griff Hazen720042b2014-02-24 15:46:56 -08003618 }
3619 return this;
3620 }
3621
3622 /**
3623 * Set metadata for this notification.
3624 *
3625 * <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 -04003626 * current contents are copied into the Notification each time {@link #build()} is
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003627 * called.
3628 *
Griff Hazen720042b2014-02-24 15:46:56 -08003629 * <p>Replaces any existing extras values with those from the provided Bundle.
3630 * Use {@link #addExtras} to merge in metadata instead.
3631 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003632 * @see Notification#extras
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003633 */
Griff Hazen959591e2014-05-15 22:26:18 -07003634 public Builder setExtras(Bundle extras) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003635 if (extras != null) {
3636 mUserExtras = extras;
3637 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003638 return this;
3639 }
3640
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003641 /**
Griff Hazen720042b2014-02-24 15:46:56 -08003642 * Get the current metadata Bundle used by this notification Builder.
3643 *
3644 * <p>The returned Bundle is shared with this Builder.
3645 *
3646 * <p>The current contents of this Bundle are copied into the Notification each time
3647 * {@link #build()} is called.
3648 *
3649 * @see Notification#extras
3650 */
3651 public Bundle getExtras() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003652 return mUserExtras;
3653 }
3654
3655 private Bundle getAllExtras() {
3656 final Bundle saveExtras = (Bundle) mUserExtras.clone();
3657 saveExtras.putAll(mN.extras);
3658 return saveExtras;
Griff Hazen720042b2014-02-24 15:46:56 -08003659 }
3660
3661 /**
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003662 * Add an action to this notification. Actions are typically displayed by
3663 * the system as a button adjacent to the notification content.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04003664 * <p>
3665 * Every action must have an icon (32dp square and matching the
3666 * <a href="{@docRoot}design/style/iconography.html#action-bar">Holo
3667 * Dark action bar</a> visual style), a textual label, and a {@link PendingIntent}.
3668 * <p>
3669 * A notification in its expanded form can display up to 3 actions, from left to right in
3670 * the order they were added. Actions will not be displayed when the notification is
3671 * collapsed, however, so be sure that any essential functions may be accessed by the user
3672 * in some other way (for example, in the Activity pointed to by {@link #contentIntent}).
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003673 *
3674 * @param icon Resource ID of a drawable that represents the action.
3675 * @param title Text describing the action.
3676 * @param intent PendingIntent to be fired when the action is invoked.
Dan Sandler86647982015-05-13 23:41:13 -04003677 *
3678 * @deprecated Use {@link #addAction(Action)} instead.
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003679 */
Dan Sandler86647982015-05-13 23:41:13 -04003680 @Deprecated
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003681 public Builder addAction(int icon, CharSequence title, PendingIntent intent) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04003682 mActions.add(new Action(icon, safeCharSequence(title), intent));
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003683 return this;
3684 }
3685
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003686 /**
Griff Hazen959591e2014-05-15 22:26:18 -07003687 * Add an action to this notification. Actions are typically displayed by
3688 * the system as a button adjacent to the notification content.
3689 * <p>
3690 * Every action must have an icon (32dp square and matching the
3691 * <a href="{@docRoot}design/style/iconography.html#action-bar">Holo
3692 * Dark action bar</a> visual style), a textual label, and a {@link PendingIntent}.
3693 * <p>
3694 * A notification in its expanded form can display up to 3 actions, from left to right in
3695 * the order they were added. Actions will not be displayed when the notification is
3696 * collapsed, however, so be sure that any essential functions may be accessed by the user
3697 * in some other way (for example, in the Activity pointed to by {@link #contentIntent}).
3698 *
3699 * @param action The action to add.
3700 */
3701 public Builder addAction(Action action) {
liangweikang63b03b52017-03-16 19:22:15 +08003702 if (action != null) {
3703 mActions.add(action);
3704 }
Griff Hazen959591e2014-05-15 22:26:18 -07003705 return this;
3706 }
3707
3708 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003709 * Alter the complete list of actions attached to this notification.
3710 * @see #addAction(Action).
3711 *
3712 * @param actions
3713 * @return
3714 */
3715 public Builder setActions(Action... actions) {
3716 mActions.clear();
3717 for (int i = 0; i < actions.length; i++) {
liangweikang63b03b52017-03-16 19:22:15 +08003718 if (actions[i] != null) {
3719 mActions.add(actions[i]);
3720 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003721 }
3722 return this;
3723 }
3724
3725 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003726 * Add a rich notification style to be applied at build time.
3727 *
3728 * @param style Object responsible for modifying the notification style.
3729 */
3730 public Builder setStyle(Style style) {
3731 if (mStyle != style) {
3732 mStyle = style;
Daniel Sandlerc08dea22012-06-28 08:35:24 -07003733 if (mStyle != null) {
3734 mStyle.setBuilder(this);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003735 mN.extras.putString(EXTRA_TEMPLATE, style.getClass().getName());
3736 } else {
3737 mN.extras.remove(EXTRA_TEMPLATE);
Daniel Sandlerc08dea22012-06-28 08:35:24 -07003738 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003739 }
3740 return this;
3741 }
3742
Dan Sandler0bf2ed82013-12-21 23:33:41 -06003743 /**
3744 * Specify the value of {@link #visibility}.
Griff Hazenb720abe2014-05-20 13:15:30 -07003745 *
Dan Sandler0bf2ed82013-12-21 23:33:41 -06003746 * @return The same Builder.
3747 */
Jeff Sharkey30e06bb2017-04-24 11:18:03 -06003748 public Builder setVisibility(@Visibility int visibility) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003749 mN.visibility = visibility;
Dan Sandler0bf2ed82013-12-21 23:33:41 -06003750 return this;
3751 }
3752
3753 /**
3754 * Supply a replacement Notification whose contents should be shown in insecure contexts
3755 * (i.e. atop the secure lockscreen). See {@link #visibility} and {@link #VISIBILITY_PUBLIC}.
3756 * @param n A replacement notification, presumably with some or all info redacted.
3757 * @return The same Builder.
3758 */
3759 public Builder setPublicVersion(Notification n) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003760 if (n != null) {
3761 mN.publicVersion = new Notification();
3762 n.cloneInto(mN.publicVersion, /*heavy=*/ true);
3763 } else {
3764 mN.publicVersion = null;
3765 }
Dan Sandler0bf2ed82013-12-21 23:33:41 -06003766 return this;
3767 }
3768
Griff Hazenb720abe2014-05-20 13:15:30 -07003769 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003770 * Apply an extender to this notification builder. Extenders may be used to add
3771 * metadata or change options on this builder.
3772 */
Griff Hazen61a9e862014-05-22 16:05:19 -07003773 public Builder extend(Extender extender) {
3774 extender.extend(this);
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003775 return this;
3776 }
3777
Dan Sandler4e787062015-06-17 15:09:48 -04003778 /**
3779 * @hide
3780 */
Julia Reynoldse46bb372016-03-17 11:05:58 -04003781 public Builder setFlag(int mask, boolean value) {
Joe Onorato46439ce2010-11-19 13:56:21 -08003782 if (value) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003783 mN.flags |= mask;
Joe Onorato46439ce2010-11-19 13:56:21 -08003784 } else {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003785 mN.flags &= ~mask;
Joe Onorato46439ce2010-11-19 13:56:21 -08003786 }
Julia Reynoldse46bb372016-03-17 11:05:58 -04003787 return this;
Joe Onorato46439ce2010-11-19 13:56:21 -08003788 }
3789
Dan Sandler26e81cf2014-05-06 10:01:27 -04003790 /**
3791 * Sets {@link Notification#color}.
3792 *
3793 * @param argb The accent color to use
3794 *
3795 * @return The same Builder.
3796 */
Tor Norbye80756e32015-03-02 09:39:27 -08003797 public Builder setColor(@ColorInt int argb) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003798 mN.color = argb;
Julia Reynolds10ee1fc2015-11-09 11:04:55 -05003799 sanitizeColor();
Dan Sandler26e81cf2014-05-06 10:01:27 -04003800 return this;
3801 }
3802
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003803 private Drawable getProfileBadgeDrawable() {
Chris Wren66619a22016-05-12 16:42:37 -04003804 if (mContext.getUserId() == UserHandle.USER_SYSTEM) {
3805 // This user can never be a badged profile,
3806 // and also includes USER_ALL system notifications.
3807 return null;
3808 }
Christoph Studer7ac80e62014-08-04 16:01:57 +02003809 // Note: This assumes that the current user can read the profile badge of the
3810 // originating user.
Selim Cineke6ff9462016-01-15 15:07:06 -08003811 return mContext.getPackageManager().getUserBadgeForDensityNoBackground(
Julia Reynoldsda303542015-11-23 14:00:20 -05003812 new UserHandle(mContext.getUserId()), 0);
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003813 }
3814
3815 private Bitmap getProfileBadge() {
3816 Drawable badge = getProfileBadgeDrawable();
Kenny Guy8a0101b2014-05-08 23:34:12 +01003817 if (badge == null) {
3818 return null;
3819 }
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003820 final int size = mContext.getResources().getDimensionPixelSize(
3821 R.dimen.notification_badge_size);
3822 Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
Kenny Guy8a0101b2014-05-08 23:34:12 +01003823 Canvas canvas = new Canvas(bitmap);
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003824 badge.setBounds(0, 0, size, size);
Kenny Guy8a0101b2014-05-08 23:34:12 +01003825 badge.draw(canvas);
3826 return bitmap;
3827 }
3828
Selim Cinekc848c3a2016-01-13 15:27:30 -08003829 private void bindProfileBadge(RemoteViews contentView) {
Kenny Guy98193ea2014-07-24 19:54:37 +01003830 Bitmap profileBadge = getProfileBadge();
3831
Kenny Guy98193ea2014-07-24 19:54:37 +01003832 if (profileBadge != null) {
Selim Cinekc848c3a2016-01-13 15:27:30 -08003833 contentView.setImageViewBitmap(R.id.profile_badge, profileBadge);
3834 contentView.setViewVisibility(R.id.profile_badge, View.VISIBLE);
Selim Cinek7b9605b2017-01-19 17:36:00 -08003835 if (isColorized()) {
3836 contentView.setDrawableParameters(R.id.profile_badge, false, -1,
3837 getPrimaryTextColor(), PorterDuff.Mode.SRC_ATOP, -1);
3838 }
Kenny Guy98193ea2014-07-24 19:54:37 +01003839 }
Kenny Guy98193ea2014-07-24 19:54:37 +01003840 }
3841
Christoph Studerfe718432014-09-01 18:21:18 +02003842 private void resetStandardTemplate(RemoteViews contentView) {
Selim Cinekeaa29ca2015-11-23 13:51:13 -08003843 resetNotificationHeader(contentView);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003844 resetContentMargins(contentView);
Christoph Studerfe718432014-09-01 18:21:18 +02003845 contentView.setViewVisibility(R.id.right_icon, View.GONE);
Selim Cinek860b6da2015-12-16 19:02:19 -08003846 contentView.setViewVisibility(R.id.title, View.GONE);
Christoph Studerfe718432014-09-01 18:21:18 +02003847 contentView.setTextViewText(R.id.title, null);
Selim Cinek41598732016-01-11 16:58:37 -08003848 contentView.setViewVisibility(R.id.text, View.GONE);
Christoph Studerfe718432014-09-01 18:21:18 +02003849 contentView.setTextViewText(R.id.text, null);
Selim Cinek29603462015-11-17 19:04:39 -08003850 contentView.setViewVisibility(R.id.text_line_1, View.GONE);
Selim Cinek41598732016-01-11 16:58:37 -08003851 contentView.setTextViewText(R.id.text_line_1, null);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003852 }
3853
Selim Cinekeaa29ca2015-11-23 13:51:13 -08003854 /**
3855 * Resets the notification header to its original state
3856 */
3857 private void resetNotificationHeader(RemoteViews contentView) {
Adrian Roosc4337a32016-08-02 18:30:34 -07003858 // Small icon doesn't need to be reset, as it's always set. Resetting would prevent
3859 // re-using the drawable when the notification is updated.
Selim Cinek7b836392015-12-04 20:02:59 -08003860 contentView.setBoolean(R.id.notification_header, "setExpanded", false);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003861 contentView.setTextViewText(R.id.app_name_text, null);
Christoph Studerca1db712014-09-10 17:31:33 +02003862 contentView.setViewVisibility(R.id.chronometer, View.GONE);
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07003863 contentView.setViewVisibility(R.id.header_text, View.GONE);
Adrian Roos9dfb78f2016-06-30 15:43:44 -07003864 contentView.setTextViewText(R.id.header_text, null);
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07003865 contentView.setViewVisibility(R.id.header_text_divider, View.GONE);
Selim Cinek29603462015-11-17 19:04:39 -08003866 contentView.setViewVisibility(R.id.time_divider, View.GONE);
Selim Cinekb85f36fd2016-04-20 18:46:36 -07003867 contentView.setViewVisibility(R.id.time, View.GONE);
Selim Cinekc848c3a2016-01-13 15:27:30 -08003868 contentView.setImageViewIcon(R.id.profile_badge, null);
3869 contentView.setViewVisibility(R.id.profile_badge, View.GONE);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003870 }
3871
3872 private void resetContentMargins(RemoteViews contentView) {
Adrian Roos2d5dbba2016-06-08 17:11:53 -07003873 contentView.setViewLayoutMarginEndDimen(R.id.line1, 0);
3874 contentView.setViewLayoutMarginEndDimen(R.id.text, 0);
Christoph Studerfe718432014-09-01 18:21:18 +02003875 }
3876
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003877 private RemoteViews applyStandardTemplate(int resId) {
Adrian Roos70d7aa32017-01-11 15:39:06 -08003878 return applyStandardTemplate(resId, mParams.reset().fillTextsFrom(this));
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02003879 }
3880
3881 /**
3882 * @param hasProgress whether the progress bar should be shown and set
3883 */
3884 private RemoteViews applyStandardTemplate(int resId, boolean hasProgress) {
Adrian Roos70d7aa32017-01-11 15:39:06 -08003885 return applyStandardTemplate(resId, mParams.reset().hasProgress(hasProgress)
3886 .fillTextsFrom(this));
Adrian Roosc1a80b02016-04-05 14:54:55 -07003887 }
3888
Adrian Roos70d7aa32017-01-11 15:39:06 -08003889 private RemoteViews applyStandardTemplate(int resId, StandardTemplateParams p) {
Kenny Guy77320062014-08-27 21:37:15 +01003890 RemoteViews contentView = new BuilderRemoteViews(mContext.getApplicationInfo(), resId);
Dan Sandler539aad42014-08-04 00:43:39 -04003891
Christoph Studerfe718432014-09-01 18:21:18 +02003892 resetStandardTemplate(contentView);
3893
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003894 final Bundle ex = mN.extras;
Selim Cinek7b9605b2017-01-19 17:36:00 -08003895 updateBackgroundColor(contentView);
Adrian Roos487374f2017-01-11 15:48:14 -08003896 bindNotificationHeader(contentView, p.ambient);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003897 bindLargeIcon(contentView);
Adrian Roos70d7aa32017-01-11 15:39:06 -08003898 boolean showProgress = handleProgressBar(p.hasProgress, contentView, ex);
3899 if (p.title != null) {
Selim Cinek860b6da2015-12-16 19:02:19 -08003900 contentView.setViewVisibility(R.id.title, View.VISIBLE);
Adrian Roos70d7aa32017-01-11 15:39:06 -08003901 contentView.setTextViewText(R.id.title, p.title);
Adrian Roos72171622017-01-27 10:32:06 -08003902 if (!p.ambient) {
3903 setTextViewColorPrimary(contentView, R.id.title);
3904 }
Selim Cinek954cc232016-05-20 13:29:23 -07003905 contentView.setViewLayoutWidth(R.id.title, showProgress
3906 ? ViewGroup.LayoutParams.WRAP_CONTENT
3907 : ViewGroup.LayoutParams.MATCH_PARENT);
Joe Onorato561d3852010-11-20 18:09:34 -08003908 }
Adrian Roos70d7aa32017-01-11 15:39:06 -08003909 if (p.text != null) {
Selim Cinek41598732016-01-11 16:58:37 -08003910 int textId = showProgress ? com.android.internal.R.id.text_line_1
3911 : com.android.internal.R.id.text;
Adrian Roos70d7aa32017-01-11 15:39:06 -08003912 contentView.setTextViewText(textId, p.text);
Adrian Roos72171622017-01-27 10:32:06 -08003913 if (!p.ambient) {
3914 setTextViewColorSecondary(contentView, textId);
3915 }
Selim Cinek41598732016-01-11 16:58:37 -08003916 contentView.setViewVisibility(textId, View.VISIBLE);
Joe Onorato561d3852010-11-20 18:09:34 -08003917 }
Selim Cinekc848c3a2016-01-13 15:27:30 -08003918
Selim Cinek279fa862016-06-14 10:57:25 -07003919 setContentMinHeight(contentView, showProgress || mN.hasLargeIcon());
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003920
Selim Cinek29603462015-11-17 19:04:39 -08003921 return contentView;
3922 }
3923
Selim Cinek7b9605b2017-01-19 17:36:00 -08003924 private void setTextViewColorPrimary(RemoteViews contentView, int id) {
3925 ensureColors();
3926 contentView.setTextColor(id, mPrimaryTextColor);
3927 }
3928
Selim Cinek389edcd2017-05-11 19:16:44 -07003929 /**
3930 * @return the primary text color
3931 * @hide
3932 */
3933 @VisibleForTesting
3934 public int getPrimaryTextColor() {
Selim Cinek7b9605b2017-01-19 17:36:00 -08003935 ensureColors();
3936 return mPrimaryTextColor;
3937 }
3938
Selim Cinek389edcd2017-05-11 19:16:44 -07003939 /**
3940 * @return the secondary text color
3941 * @hide
3942 */
3943 @VisibleForTesting
3944 public int getSecondaryTextColor() {
3945 ensureColors();
3946 return mSecondaryTextColor;
3947 }
3948
Selim Cinek7b9605b2017-01-19 17:36:00 -08003949 private int getActionBarColor() {
3950 ensureColors();
3951 return mActionBarColor;
3952 }
3953
Selim Cinek622c64a2017-04-17 17:10:05 -07003954 private int getActionBarColorDeEmphasized() {
3955 int backgroundColor = getBackgroundColor();
3956 return NotificationColorUtil.getShiftedColor(backgroundColor, 12);
3957 }
3958
Selim Cinek7b9605b2017-01-19 17:36:00 -08003959 private void setTextViewColorSecondary(RemoteViews contentView, int id) {
3960 ensureColors();
3961 contentView.setTextColor(id, mSecondaryTextColor);
3962 }
3963
3964 private void ensureColors() {
3965 int backgroundColor = getBackgroundColor();
3966 if (mPrimaryTextColor == COLOR_INVALID
3967 || mSecondaryTextColor == COLOR_INVALID
3968 || mActionBarColor == COLOR_INVALID
3969 || mTextColorsAreForBackground != backgroundColor) {
3970 mTextColorsAreForBackground = backgroundColor;
Selim Cinek5fb73f82017-04-20 16:55:38 -07003971 if (mForegroundColor == COLOR_INVALID || !isColorized()) {
3972 mPrimaryTextColor = NotificationColorUtil.resolvePrimaryColor(mContext,
3973 backgroundColor);
3974 mSecondaryTextColor = NotificationColorUtil.resolveSecondaryColor(mContext,
3975 backgroundColor);
Selim Cinekac5f0272017-05-02 16:05:41 -07003976 if (backgroundColor != COLOR_DEFAULT
3977 && (mBackgroundColorHint != COLOR_INVALID || isColorized())) {
3978 mPrimaryTextColor = NotificationColorUtil.findAlphaToMeetContrast(
3979 mPrimaryTextColor, backgroundColor, 4.5);
3980 mSecondaryTextColor = NotificationColorUtil.findAlphaToMeetContrast(
3981 mSecondaryTextColor, backgroundColor, 4.5);
3982 }
Selim Cinek5fb73f82017-04-20 16:55:38 -07003983 } else {
3984 double backLum = NotificationColorUtil.calculateLuminance(backgroundColor);
3985 double textLum = NotificationColorUtil.calculateLuminance(mForegroundColor);
3986 double contrast = NotificationColorUtil.calculateContrast(mForegroundColor,
3987 backgroundColor);
Selim Cinek389edcd2017-05-11 19:16:44 -07003988 // We only respect the given colors if worst case Black or White still has
3989 // contrast
3990 boolean backgroundLight = backLum > textLum
3991 && satisfiesTextContrast(backgroundColor, Color.BLACK)
3992 || backLum <= textLum
3993 && !satisfiesTextContrast(backgroundColor, Color.WHITE);
Selim Cinek5fb73f82017-04-20 16:55:38 -07003994 if (contrast < 4.5f) {
Selim Cinek389edcd2017-05-11 19:16:44 -07003995 if (backgroundLight) {
Selim Cinek5fb73f82017-04-20 16:55:38 -07003996 mSecondaryTextColor = NotificationColorUtil.findContrastColor(
3997 mForegroundColor,
3998 backgroundColor,
3999 true /* findFG */,
4000 4.5f);
4001 mPrimaryTextColor = NotificationColorUtil.changeColorLightness(
Selim Cinek389edcd2017-05-11 19:16:44 -07004002 mSecondaryTextColor, -LIGHTNESS_TEXT_DIFFERENCE_LIGHT);
Selim Cinek5fb73f82017-04-20 16:55:38 -07004003 } else {
4004 mSecondaryTextColor =
4005 NotificationColorUtil.findContrastColorAgainstDark(
4006 mForegroundColor,
4007 backgroundColor,
4008 true /* findFG */,
4009 4.5f);
4010 mPrimaryTextColor = NotificationColorUtil.changeColorLightness(
Selim Cinek389edcd2017-05-11 19:16:44 -07004011 mSecondaryTextColor, -LIGHTNESS_TEXT_DIFFERENCE_DARK);
Selim Cinek5fb73f82017-04-20 16:55:38 -07004012 }
4013 } else {
4014 mPrimaryTextColor = mForegroundColor;
4015 mSecondaryTextColor = NotificationColorUtil.changeColorLightness(
Selim Cinek389edcd2017-05-11 19:16:44 -07004016 mPrimaryTextColor, backgroundLight ? LIGHTNESS_TEXT_DIFFERENCE_LIGHT
4017 : LIGHTNESS_TEXT_DIFFERENCE_DARK);
Selim Cinek5fb73f82017-04-20 16:55:38 -07004018 if (NotificationColorUtil.calculateContrast(mSecondaryTextColor,
4019 backgroundColor) < 4.5f) {
4020 // oh well the secondary is not good enough
Selim Cinek389edcd2017-05-11 19:16:44 -07004021 if (backgroundLight) {
Selim Cinek5fb73f82017-04-20 16:55:38 -07004022 mSecondaryTextColor = NotificationColorUtil.findContrastColor(
4023 mSecondaryTextColor,
4024 backgroundColor,
4025 true /* findFG */,
4026 4.5f);
4027 } else {
4028 mSecondaryTextColor
4029 = NotificationColorUtil.findContrastColorAgainstDark(
4030 mSecondaryTextColor,
4031 backgroundColor,
4032 true /* findFG */,
4033 4.5f);
4034 }
4035 mPrimaryTextColor = NotificationColorUtil.changeColorLightness(
Selim Cinek389edcd2017-05-11 19:16:44 -07004036 mSecondaryTextColor, backgroundLight
4037 ? -LIGHTNESS_TEXT_DIFFERENCE_LIGHT
4038 : -LIGHTNESS_TEXT_DIFFERENCE_DARK);
Selim Cinek5fb73f82017-04-20 16:55:38 -07004039 }
4040 }
4041 }
Selim Cinek875ba9b2017-02-13 16:20:17 -08004042 mActionBarColor = NotificationColorUtil.resolveActionBarColor(mContext,
4043 backgroundColor);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004044 }
4045 }
4046
4047 private void updateBackgroundColor(RemoteViews contentView) {
4048 if (isColorized()) {
4049 contentView.setInt(R.id.status_bar_latest_event_content, "setBackgroundColor",
4050 getBackgroundColor());
4051 } else {
4052 // Clear it!
4053 contentView.setInt(R.id.status_bar_latest_event_content, "setBackgroundResource",
4054 0);
4055 }
4056 }
4057
Selim Cinek860b6da2015-12-16 19:02:19 -08004058 /**
4059 * @param remoteView the remote view to update the minheight in
4060 * @param hasMinHeight does it have a mimHeight
4061 * @hide
4062 */
4063 void setContentMinHeight(RemoteViews remoteView, boolean hasMinHeight) {
4064 int minHeight = 0;
4065 if (hasMinHeight) {
4066 // we need to set the minHeight of the notification
4067 minHeight = mContext.getResources().getDimensionPixelSize(
4068 com.android.internal.R.dimen.notification_min_content_height);
4069 }
4070 remoteView.setInt(R.id.notification_main_column, "setMinimumHeight", minHeight);
4071 }
4072
Selim Cinek29603462015-11-17 19:04:39 -08004073 private boolean handleProgressBar(boolean hasProgress, RemoteViews contentView, Bundle ex) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004074 final int max = ex.getInt(EXTRA_PROGRESS_MAX, 0);
4075 final int progress = ex.getInt(EXTRA_PROGRESS, 0);
4076 final boolean ind = ex.getBoolean(EXTRA_PROGRESS_INDETERMINATE);
4077 if (hasProgress && (max != 0 || ind)) {
Selim Cinek29603462015-11-17 19:04:39 -08004078 contentView.setViewVisibility(com.android.internal.R.id.progress, View.VISIBLE);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004079 contentView.setProgressBar(
Selim Cinek29603462015-11-17 19:04:39 -08004080 R.id.progress, max, progress, ind);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004081 contentView.setProgressBackgroundTintList(
4082 R.id.progress, ColorStateList.valueOf(mContext.getColor(
4083 R.color.notification_progress_background_color)));
Selim Cinek29603462015-11-17 19:04:39 -08004084 if (mN.color != COLOR_DEFAULT) {
Adrian Roos4ff3b122016-02-01 12:26:13 -08004085 ColorStateList colorStateList = ColorStateList.valueOf(resolveContrastColor());
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004086 contentView.setProgressTintList(R.id.progress, colorStateList);
4087 contentView.setProgressIndeterminateTintList(R.id.progress, colorStateList);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004088 }
Selim Cinek29603462015-11-17 19:04:39 -08004089 return true;
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004090 } else {
4091 contentView.setViewVisibility(R.id.progress, View.GONE);
Selim Cinek29603462015-11-17 19:04:39 -08004092 return false;
Jeff Sharkey1c400132011-08-05 14:50:13 -07004093 }
Joe Onorato561d3852010-11-20 18:09:34 -08004094 }
4095
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004096 private void bindLargeIcon(RemoteViews contentView) {
Selim Cinek279fa862016-06-14 10:57:25 -07004097 if (mN.mLargeIcon == null && mN.largeIcon != null) {
4098 mN.mLargeIcon = Icon.createWithBitmap(mN.largeIcon);
4099 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004100 if (mN.mLargeIcon != null) {
4101 contentView.setViewVisibility(R.id.right_icon, View.VISIBLE);
4102 contentView.setImageViewIcon(R.id.right_icon, mN.mLargeIcon);
4103 processLargeLegacyIcon(mN.mLargeIcon, contentView);
Adrian Roos2d5dbba2016-06-08 17:11:53 -07004104 int endMargin = R.dimen.notification_content_picture_margin;
4105 contentView.setViewLayoutMarginEndDimen(R.id.line1, endMargin);
4106 contentView.setViewLayoutMarginEndDimen(R.id.text, endMargin);
4107 contentView.setViewLayoutMarginEndDimen(R.id.progress, endMargin);
Selim Cinek5d6ef8d2017-05-18 22:16:00 -07004108 // Bind the reply action
4109 Action action = findReplyAction();
4110 contentView.setViewVisibility(R.id.reply_icon_action, action != null
4111 ? View.VISIBLE
4112 : View.GONE);
4113
4114 if (action != null) {
4115 int contrastColor = resolveContrastColor();
4116 contentView.setDrawableParameters(R.id.reply_icon_action,
4117 true /* targetBackground */,
4118 -1,
4119 contrastColor,
4120 PorterDuff.Mode.SRC_ATOP, -1);
4121 int iconColor = NotificationColorUtil.isColorLight(contrastColor)
4122 ? Color.BLACK : Color.WHITE;
4123 contentView.setDrawableParameters(R.id.reply_icon_action,
4124 false /* targetBackground */,
4125 -1,
4126 iconColor,
4127 PorterDuff.Mode.SRC_ATOP, -1);
4128 contentView.setOnClickPendingIntent(R.id.right_icon,
4129 action.actionIntent);
4130 contentView.setOnClickPendingIntent(R.id.reply_icon_action,
4131 action.actionIntent);
4132 contentView.setRemoteInputs(R.id.right_icon, action.mRemoteInputs);
4133 contentView.setRemoteInputs(R.id.reply_icon_action, action.mRemoteInputs);
4134
4135 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004136 }
Selim Cinek5d6ef8d2017-05-18 22:16:00 -07004137 contentView.setViewVisibility(R.id.right_icon_container, mN.mLargeIcon != null
4138 ? View.VISIBLE
4139 : View.GONE);
4140 }
4141
4142 private Action findReplyAction() {
4143 ArrayList<Action> actions = mActions;
4144 if (mOriginalActions != null) {
4145 actions = mOriginalActions;
4146 }
4147 int numActions = actions.size();
4148 for (int i = 0; i < numActions; i++) {
4149 Action action = actions.get(i);
4150 if (hasValidRemoteInput(action)) {
4151 return action;
4152 }
4153 }
4154 return null;
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004155 }
4156
Adrian Roos487374f2017-01-11 15:48:14 -08004157 private void bindNotificationHeader(RemoteViews contentView, boolean ambient) {
4158 bindSmallIcon(contentView, ambient);
4159 bindHeaderAppName(contentView, ambient);
4160 if (!ambient) {
4161 // Ambient view does not have these
4162 bindHeaderText(contentView);
4163 bindHeaderChronometerAndTime(contentView);
Adrian Roos487374f2017-01-11 15:48:14 -08004164 bindProfileBadge(contentView);
4165 }
Adrian Roosd83e9992017-03-16 15:17:57 -07004166 bindExpandButton(contentView);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004167 }
4168
4169 private void bindExpandButton(RemoteViews contentView) {
Selim Cinek99104832017-01-25 14:47:33 -08004170 int color = getPrimaryHighlightColor();
Selim Cinek7b9605b2017-01-19 17:36:00 -08004171 contentView.setDrawableParameters(R.id.expand_button, false, -1, color,
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004172 PorterDuff.Mode.SRC_ATOP, -1);
Selim Cinekea4bef72015-12-02 15:51:10 -08004173 contentView.setInt(R.id.notification_header, "setOriginalNotificationColor",
Selim Cinek7b9605b2017-01-19 17:36:00 -08004174 color);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004175 }
4176
Selim Cinek99104832017-01-25 14:47:33 -08004177 /**
4178 * @return the color that is used as the first primary highlight color. This is applied
4179 * in several places like the action buttons or the app name in the header.
4180 */
4181 private int getPrimaryHighlightColor() {
4182 return isColorized() ? getPrimaryTextColor() : resolveContrastColor();
4183 }
4184
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004185 private void bindHeaderChronometerAndTime(RemoteViews contentView) {
4186 if (showsTimeOrChronometer()) {
Selim Cinek29603462015-11-17 19:04:39 -08004187 contentView.setViewVisibility(R.id.time_divider, View.VISIBLE);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004188 setTextViewColorSecondary(contentView, R.id.time_divider);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004189 if (mN.extras.getBoolean(EXTRA_SHOW_CHRONOMETER)) {
4190 contentView.setViewVisibility(R.id.chronometer, View.VISIBLE);
4191 contentView.setLong(R.id.chronometer, "setBase",
4192 mN.when + (SystemClock.elapsedRealtime() - System.currentTimeMillis()));
4193 contentView.setBoolean(R.id.chronometer, "setStarted", true);
Adrian Roos96b7e202016-05-17 13:50:38 -07004194 boolean countsDown = mN.extras.getBoolean(EXTRA_CHRONOMETER_COUNT_DOWN);
Selim Cinekc3b752e2016-04-20 16:13:59 -07004195 contentView.setChronometerCountDown(R.id.chronometer, countsDown);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004196 setTextViewColorSecondary(contentView, R.id.chronometer);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004197 } else {
4198 contentView.setViewVisibility(R.id.time, View.VISIBLE);
4199 contentView.setLong(R.id.time, "setTime", mN.when);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004200 setTextViewColorSecondary(contentView, R.id.time);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004201 }
Selim Cinekb85f36fd2016-04-20 18:46:36 -07004202 } else {
4203 // We still want a time to be set but gone, such that we can show and hide it
4204 // on demand in case it's a child notification without anything in the header
4205 contentView.setLong(R.id.time, "setTime", mN.when != 0 ? mN.when : mN.creationTime);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004206 }
4207 }
4208
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07004209 private void bindHeaderText(RemoteViews contentView) {
4210 CharSequence headerText = mN.extras.getCharSequence(EXTRA_SUB_TEXT);
4211 if (headerText == null && mStyle != null && mStyle.mSummaryTextSet
Selim Cinek03d0d652015-11-13 13:18:09 -05004212 && mStyle.hasSummaryInHeader()) {
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07004213 headerText = mStyle.mSummaryText;
Selim Cinek03d0d652015-11-13 13:18:09 -05004214 }
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07004215 if (headerText == null
4216 && mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N
4217 && mN.extras.getCharSequence(EXTRA_INFO_TEXT) != null) {
4218 headerText = mN.extras.getCharSequence(EXTRA_INFO_TEXT);
4219 }
4220 if (headerText != null) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004221 // TODO: Remove the span entirely to only have the string with propper formating.
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07004222 contentView.setTextViewText(R.id.header_text, processLegacyText(headerText));
Selim Cinek7b9605b2017-01-19 17:36:00 -08004223 setTextViewColorSecondary(contentView, R.id.header_text);
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07004224 contentView.setViewVisibility(R.id.header_text, View.VISIBLE);
4225 contentView.setViewVisibility(R.id.header_text_divider, View.VISIBLE);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004226 setTextViewColorSecondary(contentView, R.id.header_text_divider);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004227 }
4228 }
4229
Adrian Rooseba05822016-04-22 17:09:27 -07004230 /**
4231 * @hide
4232 */
4233 public String loadHeaderAppName() {
Dan Sandler732bd6c2016-04-12 14:20:32 -04004234 CharSequence name = null;
4235 final PackageManager pm = mContext.getPackageManager();
4236 if (mN.extras.containsKey(EXTRA_SUBSTITUTE_APP_NAME)) {
4237 // only system packages which lump together a bunch of unrelated stuff
4238 // may substitute a different name to make the purpose of the
4239 // notification more clear. the correct package label should always
4240 // be accessible via SystemUI.
4241 final String pkg = mContext.getPackageName();
4242 final String subName = mN.extras.getString(EXTRA_SUBSTITUTE_APP_NAME);
4243 if (PackageManager.PERMISSION_GRANTED == pm.checkPermission(
4244 android.Manifest.permission.SUBSTITUTE_NOTIFICATION_APP_NAME, pkg)) {
4245 name = subName;
4246 } else {
4247 Log.w(TAG, "warning: pkg "
4248 + pkg + " attempting to substitute app name '" + subName
4249 + "' without holding perm "
4250 + android.Manifest.permission.SUBSTITUTE_NOTIFICATION_APP_NAME);
4251 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004252 }
Dan Sandler732bd6c2016-04-12 14:20:32 -04004253 if (TextUtils.isEmpty(name)) {
4254 name = pm.getApplicationLabel(mContext.getApplicationInfo());
4255 }
4256 if (TextUtils.isEmpty(name)) {
4257 // still nothing?
4258 return null;
4259 }
4260
4261 return String.valueOf(name);
4262 }
Adrian Roos487374f2017-01-11 15:48:14 -08004263 private void bindHeaderAppName(RemoteViews contentView, boolean ambient) {
Dan Sandler732bd6c2016-04-12 14:20:32 -04004264 contentView.setTextViewText(R.id.app_name_text, loadHeaderAppName());
Adrian Roos72171622017-01-27 10:32:06 -08004265 if (isColorized() && !ambient) {
Selim Cinek7b9605b2017-01-19 17:36:00 -08004266 setTextViewColorPrimary(contentView, R.id.app_name_text);
4267 } else {
4268 contentView.setTextColor(R.id.app_name_text,
4269 ambient ? resolveAmbientColor() : resolveContrastColor());
4270 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004271 }
4272
Adrian Roos487374f2017-01-11 15:48:14 -08004273 private void bindSmallIcon(RemoteViews contentView, boolean ambient) {
Selim Cinek279fa862016-06-14 10:57:25 -07004274 if (mN.mSmallIcon == null && mN.icon != 0) {
4275 mN.mSmallIcon = Icon.createWithResource(mContext, mN.icon);
4276 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004277 contentView.setImageViewIcon(R.id.icon, mN.mSmallIcon);
Adrian Roos9b45a152016-06-28 13:32:29 -07004278 contentView.setDrawableParameters(R.id.icon, false /* targetBackground */,
4279 -1 /* alpha */, -1 /* colorFilter */, null /* mode */, mN.iconLevel);
Adrian Roos487374f2017-01-11 15:48:14 -08004280 processSmallIconColor(mN.mSmallIcon, contentView, ambient);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004281 }
4282
Jorim Jaggi445d3c02014-08-19 22:33:42 +02004283 /**
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004284 * @return true if the built notification will show the time or the chronometer; false
4285 * otherwise
4286 */
4287 private boolean showsTimeOrChronometer() {
Selim Cinekc2c0b042016-05-18 17:13:46 -07004288 return mN.showsTime() || mN.showsChronometer();
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004289 }
4290
Christoph Studerfe718432014-09-01 18:21:18 +02004291 private void resetStandardTemplateWithActions(RemoteViews big) {
Adrian Roos4c1fcc82016-03-31 14:39:39 -07004292 // actions_container is only reset when there are no actions to avoid focus issues with
4293 // remote inputs.
Christoph Studerfe718432014-09-01 18:21:18 +02004294 big.setViewVisibility(R.id.actions, View.GONE);
Christoph Studerfe718432014-09-01 18:21:18 +02004295 big.removeAllViews(R.id.actions);
Adrian Roose458aa82015-12-08 16:17:19 -08004296
4297 big.setViewVisibility(R.id.notification_material_reply_container, View.GONE);
4298 big.setTextViewText(R.id.notification_material_reply_text_1, null);
4299
4300 big.setViewVisibility(R.id.notification_material_reply_text_2, View.GONE);
4301 big.setTextViewText(R.id.notification_material_reply_text_2, null);
4302 big.setViewVisibility(R.id.notification_material_reply_text_3, View.GONE);
4303 big.setTextViewText(R.id.notification_material_reply_text_3, null);
Adrian Roosf852a422016-06-03 13:33:43 -07004304
4305 big.setViewLayoutMarginBottomDimen(R.id.notification_action_list_margin_target, 0);
Christoph Studerfe718432014-09-01 18:21:18 +02004306 }
4307
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004308 private RemoteViews applyStandardTemplateWithActions(int layoutId) {
Adrian Roos70d7aa32017-01-11 15:39:06 -08004309 return applyStandardTemplateWithActions(layoutId, mParams.reset().fillTextsFrom(this));
Adrian Roos48d746a2016-04-12 14:57:28 -07004310 }
4311
Adrian Roos70d7aa32017-01-11 15:39:06 -08004312 private RemoteViews applyStandardTemplateWithActions(int layoutId,
4313 StandardTemplateParams p) {
4314 RemoteViews big = applyStandardTemplate(layoutId, p);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004315
Christoph Studerfe718432014-09-01 18:21:18 +02004316 resetStandardTemplateWithActions(big);
4317
Adrian Roose458aa82015-12-08 16:17:19 -08004318 boolean validRemoteInput = false;
4319
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004320 int N = mActions.size();
Adrian Roos487374f2017-01-11 15:48:14 -08004321 boolean emphazisedMode = mN.fullScreenIntent != null && !p.ambient;
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004322 big.setBoolean(R.id.actions, "setEmphasizedMode", emphazisedMode);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004323 if (N > 0) {
Adrian Roos7052de52016-03-03 15:53:34 -08004324 big.setViewVisibility(R.id.actions_container, View.VISIBLE);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004325 big.setViewVisibility(R.id.actions, View.VISIBLE);
Adrian Roos487374f2017-01-11 15:48:14 -08004326 if (p.ambient) {
4327 big.setInt(R.id.actions, "setBackgroundColor", Color.TRANSPARENT);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004328 } else if (isColorized()) {
4329 big.setInt(R.id.actions, "setBackgroundColor", getActionBarColor());
4330 } else {
4331 big.setInt(R.id.actions, "setBackgroundColor", mContext.getColor(
4332 R.color.notification_action_list));
Adrian Roos487374f2017-01-11 15:48:14 -08004333 }
Adrian Roosf852a422016-06-03 13:33:43 -07004334 big.setViewLayoutMarginBottomDimen(R.id.notification_action_list_margin_target,
4335 R.dimen.notification_action_list_height);
Daniel Sandler8680bf82012-05-15 16:52:52 -04004336 if (N>MAX_ACTION_BUTTONS) N=MAX_ACTION_BUTTONS;
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004337 for (int i=0; i<N; i++) {
Adrian Roose458aa82015-12-08 16:17:19 -08004338 Action action = mActions.get(i);
4339 validRemoteInput |= hasValidRemoteInput(action);
4340
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004341 final RemoteViews button = generateActionButton(action, emphazisedMode,
Adrian Roos487374f2017-01-11 15:48:14 -08004342 i % 2 != 0, p.ambient);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004343 big.addView(R.id.actions, button);
4344 }
Adrian Roos4c1fcc82016-03-31 14:39:39 -07004345 } else {
4346 big.setViewVisibility(R.id.actions_container, View.GONE);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004347 }
Adrian Roose458aa82015-12-08 16:17:19 -08004348
4349 CharSequence[] replyText = mN.extras.getCharSequenceArray(EXTRA_REMOTE_INPUT_HISTORY);
Adrian Roos487374f2017-01-11 15:48:14 -08004350 if (!p.ambient && validRemoteInput && replyText != null
Adrian Roose458aa82015-12-08 16:17:19 -08004351 && replyText.length > 0 && !TextUtils.isEmpty(replyText[0])) {
4352 big.setViewVisibility(R.id.notification_material_reply_container, View.VISIBLE);
4353 big.setTextViewText(R.id.notification_material_reply_text_1, replyText[0]);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004354 setTextViewColorSecondary(big, R.id.notification_material_reply_text_1);
Adrian Roose458aa82015-12-08 16:17:19 -08004355
4356 if (replyText.length > 1 && !TextUtils.isEmpty(replyText[1])) {
4357 big.setViewVisibility(R.id.notification_material_reply_text_2, View.VISIBLE);
4358 big.setTextViewText(R.id.notification_material_reply_text_2, replyText[1]);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004359 setTextViewColorSecondary(big, R.id.notification_material_reply_text_2);
Adrian Roose458aa82015-12-08 16:17:19 -08004360
4361 if (replyText.length > 2 && !TextUtils.isEmpty(replyText[2])) {
4362 big.setViewVisibility(
4363 R.id.notification_material_reply_text_3, View.VISIBLE);
4364 big.setTextViewText(R.id.notification_material_reply_text_3, replyText[2]);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004365 setTextViewColorSecondary(big, R.id.notification_material_reply_text_3);
Adrian Roose458aa82015-12-08 16:17:19 -08004366 }
4367 }
4368 }
4369
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004370 return big;
4371 }
4372
Adrian Roose458aa82015-12-08 16:17:19 -08004373 private boolean hasValidRemoteInput(Action action) {
4374 if (TextUtils.isEmpty(action.title) || action.actionIntent == null) {
4375 // Weird actions
4376 return false;
4377 }
4378
4379 RemoteInput[] remoteInputs = action.getRemoteInputs();
4380 if (remoteInputs == null) {
4381 return false;
4382 }
4383
4384 for (RemoteInput r : remoteInputs) {
4385 CharSequence[] choices = r.getChoices();
4386 if (r.getAllowFreeFormInput() || (choices != null && choices.length != 0)) {
4387 return true;
4388 }
4389 }
4390 return false;
4391 }
4392
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004393 /**
4394 * Construct a RemoteViews for the final 1U notification layout. In order:
4395 * 1. Custom contentView from the caller
4396 * 2. Style's proposed content view
4397 * 3. Standard template view
4398 */
Julia Reynolds3b848122016-02-26 10:45:32 -05004399 public RemoteViews createContentView() {
Selim Cinek7d1009b2017-01-25 15:28:28 -08004400 return createContentView(false /* increasedheight */ );
4401 }
4402
4403 /**
4404 * Construct a RemoteViews for the smaller content view.
4405 *
4406 * @param increasedHeight true if this layout be created with an increased height. Some
4407 * styles may support showing more then just that basic 1U size
4408 * and the system may decide to render important notifications
4409 * slightly bigger even when collapsed.
4410 *
4411 * @hide
4412 */
4413 public RemoteViews createContentView(boolean increasedHeight) {
Selim Cineka7679b62017-05-10 16:33:25 -07004414 if (mN.contentView != null && useExistingRemoteView()) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004415 return mN.contentView;
4416 } else if (mStyle != null) {
Selim Cinek7d1009b2017-01-25 15:28:28 -08004417 final RemoteViews styleView = mStyle.makeContentView(increasedHeight);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004418 if (styleView != null) {
4419 return styleView;
4420 }
Joe Onorato46439ce2010-11-19 13:56:21 -08004421 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004422 return applyStandardTemplate(getBaseLayoutResource());
Joe Onorato46439ce2010-11-19 13:56:21 -08004423 }
4424
Selim Cineka7679b62017-05-10 16:33:25 -07004425 private boolean useExistingRemoteView() {
4426 return mStyle == null || (!mStyle.displayCustomViewInline()
4427 && !mRebuildStyledRemoteViews);
4428 }
4429
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004430 /**
4431 * Construct a RemoteViews for the final big notification layout.
4432 */
Julia Reynolds3b848122016-02-26 10:45:32 -05004433 public RemoteViews createBigContentView() {
Selim Cinek850a8542015-11-11 11:48:36 -05004434 RemoteViews result = null;
Selim Cineka7679b62017-05-10 16:33:25 -07004435 if (mN.bigContentView != null && useExistingRemoteView()) {
Julia Reynolds089e3e42015-11-18 09:59:57 -05004436 return mN.bigContentView;
4437 } else if (mStyle != null) {
Selim Cinek850a8542015-11-11 11:48:36 -05004438 result = mStyle.makeBigContentView();
Selim Cinek90dcf6d2015-11-18 20:24:13 -08004439 hideLine1Text(result);
Selim Cinekcc10bfb2016-02-10 16:24:21 -08004440 } else if (mActions.size() != 0) {
4441 result = applyStandardTemplateWithActions(getBigBaseLayoutResource());
Selim Cinek850a8542015-11-11 11:48:36 -05004442 }
Selim Cinek6743c0b2017-01-18 18:24:01 -08004443 makeHeaderExpanded(result);
Selim Cinek850a8542015-11-11 11:48:36 -05004444 return result;
4445 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004446
Selim Cinekeaa29ca2015-11-23 13:51:13 -08004447 /**
Selim Cinek414ad332017-02-24 19:06:12 -08004448 * Construct a RemoteViews for the final notification header only. This will not be
4449 * colorized.
Selim Cinekeaa29ca2015-11-23 13:51:13 -08004450 *
Adrian Roos6f6e1592017-05-02 16:22:53 -07004451 * @param ambient if true, generate the header for the ambient display layout.
Selim Cinekeaa29ca2015-11-23 13:51:13 -08004452 * @hide
4453 */
Adrian Roos6f6e1592017-05-02 16:22:53 -07004454 public RemoteViews makeNotificationHeader(boolean ambient) {
Selim Cinek414ad332017-02-24 19:06:12 -08004455 Boolean colorized = (Boolean) mN.extras.get(EXTRA_COLORIZED);
4456 mN.extras.putBoolean(EXTRA_COLORIZED, false);
Selim Cinekeaa29ca2015-11-23 13:51:13 -08004457 RemoteViews header = new BuilderRemoteViews(mContext.getApplicationInfo(),
Adrian Roos6f6e1592017-05-02 16:22:53 -07004458 ambient ? R.layout.notification_template_ambient_header
4459 : R.layout.notification_template_header);
Selim Cinekeaa29ca2015-11-23 13:51:13 -08004460 resetNotificationHeader(header);
Adrian Roos6f6e1592017-05-02 16:22:53 -07004461 bindNotificationHeader(header, ambient);
Selim Cinek414ad332017-02-24 19:06:12 -08004462 if (colorized != null) {
4463 mN.extras.putBoolean(EXTRA_COLORIZED, colorized);
4464 } else {
4465 mN.extras.remove(EXTRA_COLORIZED);
4466 }
Selim Cinekeaa29ca2015-11-23 13:51:13 -08004467 return header;
4468 }
4469
Adrian Roos487374f2017-01-11 15:48:14 -08004470 /**
4471 * Construct a RemoteViews for the ambient version of the notification.
4472 *
4473 * @hide
4474 */
4475 public RemoteViews makeAmbientNotification() {
4476 RemoteViews ambient = applyStandardTemplateWithActions(
4477 R.layout.notification_template_material_ambient,
Adrian Roos0bc3f6a2017-03-06 11:54:05 -08004478 mParams.reset().ambient(true).fillTextsFrom(this).hasProgress(false));
Adrian Roos487374f2017-01-11 15:48:14 -08004479 return ambient;
4480 }
4481
Selim Cinek29603462015-11-17 19:04:39 -08004482 private void hideLine1Text(RemoteViews result) {
Selim Cinekcc10bfb2016-02-10 16:24:21 -08004483 if (result != null) {
4484 result.setViewVisibility(R.id.text_line_1, View.GONE);
4485 }
Selim Cinek29603462015-11-17 19:04:39 -08004486 }
4487
Selim Cinek6743c0b2017-01-18 18:24:01 -08004488 /**
4489 * Adapt the Notification header if this view is used as an expanded view.
4490 *
4491 * @hide
4492 */
4493 public static void makeHeaderExpanded(RemoteViews result) {
Selim Cinekcc10bfb2016-02-10 16:24:21 -08004494 if (result != null) {
4495 result.setBoolean(R.id.notification_header, "setExpanded", true);
4496 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004497 }
4498
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004499 /**
4500 * Construct a RemoteViews for the final heads-up notification layout.
Selim Cinek87ed69b2017-02-09 15:59:43 -08004501 *
4502 * @param increasedHeight true if this layout be created with an increased height. Some
4503 * styles may support showing more then just that basic 1U size
4504 * and the system may decide to render important notifications
4505 * slightly bigger even when collapsed.
4506 *
4507 * @hide
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004508 */
Selim Cinek87ed69b2017-02-09 15:59:43 -08004509 public RemoteViews createHeadsUpContentView(boolean increasedHeight) {
Selim Cineka7679b62017-05-10 16:33:25 -07004510 if (mN.headsUpContentView != null && useExistingRemoteView()) {
Julia Reynolds089e3e42015-11-18 09:59:57 -05004511 return mN.headsUpContentView;
4512 } else if (mStyle != null) {
Selim Cinek87ed69b2017-02-09 15:59:43 -08004513 final RemoteViews styleView = mStyle.makeHeadsUpContentView(increasedHeight);
4514 if (styleView != null) {
4515 return styleView;
4516 }
Julia Reynolds089e3e42015-11-18 09:59:57 -05004517 } else if (mActions.size() == 0) {
4518 return null;
4519 }
4520
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004521 return applyStandardTemplateWithActions(getBigBaseLayoutResource());
Chris Wren8fd39ec2014-02-27 17:43:26 -05004522 }
4523
Selim Cinek624c02db2015-12-14 21:00:02 -08004524 /**
Selim Cinek87ed69b2017-02-09 15:59:43 -08004525 * Construct a RemoteViews for the final heads-up notification layout.
4526 */
4527 public RemoteViews createHeadsUpContentView() {
4528 return createHeadsUpContentView(false /* useIncreasedHeight */);
4529 }
4530
4531 /**
Selim Cinek624c02db2015-12-14 21:00:02 -08004532 * Construct a RemoteViews for the display in public contexts like on the lockscreen.
4533 *
4534 * @hide
4535 */
4536 public RemoteViews makePublicContentView() {
Adrian Roos1a1ecfc2017-04-17 11:17:59 -07004537 return makePublicView(false /* ambient */);
4538 }
4539
4540 /**
4541 * Construct a RemoteViews for the display in public contexts like on the lockscreen.
4542 *
4543 * @hide
4544 */
4545 public RemoteViews makePublicAmbientNotification() {
4546 return makePublicView(true /* ambient */);
4547 }
4548
4549 private RemoteViews makePublicView(boolean ambient) {
Selim Cinek624c02db2015-12-14 21:00:02 -08004550 if (mN.publicVersion != null) {
4551 final Builder builder = recoverBuilder(mContext, mN.publicVersion);
Adrian Roos1a1ecfc2017-04-17 11:17:59 -07004552 return ambient ? builder.makeAmbientNotification() : builder.createContentView();
Selim Cinek624c02db2015-12-14 21:00:02 -08004553 }
4554 Bundle savedBundle = mN.extras;
4555 Style style = mStyle;
4556 mStyle = null;
4557 Icon largeIcon = mN.mLargeIcon;
4558 mN.mLargeIcon = null;
Selim Cinek279fa862016-06-14 10:57:25 -07004559 Bitmap largeIconLegacy = mN.largeIcon;
4560 mN.largeIcon = null;
Adrian Roosb19b06b2017-05-02 18:54:40 -07004561 ArrayList<Action> actions = mActions;
4562 mActions = new ArrayList<>();
Selim Cinek624c02db2015-12-14 21:00:02 -08004563 Bundle publicExtras = new Bundle();
4564 publicExtras.putBoolean(EXTRA_SHOW_WHEN,
4565 savedBundle.getBoolean(EXTRA_SHOW_WHEN));
4566 publicExtras.putBoolean(EXTRA_SHOW_CHRONOMETER,
4567 savedBundle.getBoolean(EXTRA_SHOW_CHRONOMETER));
Adrian Roos96b7e202016-05-17 13:50:38 -07004568 publicExtras.putBoolean(EXTRA_CHRONOMETER_COUNT_DOWN,
4569 savedBundle.getBoolean(EXTRA_CHRONOMETER_COUNT_DOWN));
Selim Cinek624c02db2015-12-14 21:00:02 -08004570 mN.extras = publicExtras;
Selim Cinek499c20f2017-07-20 14:06:09 -07004571 RemoteViews view;
4572 if (ambient) {
4573 publicExtras.putCharSequence(EXTRA_TITLE,
4574 mContext.getString(com.android.internal.R.string.notification_hidden_text));
4575 view = makeAmbientNotification();
4576 } else{
4577 view = makeNotificationHeader(false /* ambient */);
4578 view.setBoolean(R.id.notification_header, "setExpandOnlyOnButton", true);
4579 }
Selim Cinek624c02db2015-12-14 21:00:02 -08004580 mN.extras = savedBundle;
4581 mN.mLargeIcon = largeIcon;
Selim Cinek279fa862016-06-14 10:57:25 -07004582 mN.largeIcon = largeIconLegacy;
Adrian Roosb19b06b2017-05-02 18:54:40 -07004583 mActions = actions;
Selim Cinek624c02db2015-12-14 21:00:02 -08004584 mStyle = style;
Adrian Roos1a1ecfc2017-04-17 11:17:59 -07004585 return view;
Selim Cinek624c02db2015-12-14 21:00:02 -08004586 }
4587
Selim Cinek6743c0b2017-01-18 18:24:01 -08004588 /**
4589 * Construct a content view for the display when low - priority
4590 *
4591 * @param useRegularSubtext uses the normal subtext set if there is one available. Otherwise
4592 * a new subtext is created consisting of the content of the
4593 * notification.
4594 * @hide
4595 */
4596 public RemoteViews makeLowPriorityContentView(boolean useRegularSubtext) {
4597 int color = mN.color;
4598 mN.color = COLOR_DEFAULT;
4599 CharSequence summary = mN.extras.getCharSequence(EXTRA_SUB_TEXT);
4600 if (!useRegularSubtext || TextUtils.isEmpty(summary)) {
4601 CharSequence newSummary = createSummaryText();
4602 if (!TextUtils.isEmpty(newSummary)) {
4603 mN.extras.putCharSequence(EXTRA_SUB_TEXT, newSummary);
4604 }
4605 }
Selim Cinek875ba9b2017-02-13 16:20:17 -08004606
Adrian Roos6f6e1592017-05-02 16:22:53 -07004607 RemoteViews header = makeNotificationHeader(false /* ambient */);
Selim Cinek1b554392017-02-28 17:22:49 -08004608 header.setBoolean(R.id.notification_header, "setAcceptAllTouches", true);
Selim Cinek6743c0b2017-01-18 18:24:01 -08004609 if (summary != null) {
4610 mN.extras.putCharSequence(EXTRA_SUB_TEXT, summary);
4611 } else {
4612 mN.extras.remove(EXTRA_SUB_TEXT);
4613 }
4614 mN.color = color;
4615 return header;
4616 }
Selim Cinek624c02db2015-12-14 21:00:02 -08004617
Selim Cinek6743c0b2017-01-18 18:24:01 -08004618 private CharSequence createSummaryText() {
4619 CharSequence titleText = mN.extras.getCharSequence(Notification.EXTRA_TITLE);
4620 if (USE_ONLY_TITLE_IN_LOW_PRIORITY_SUMMARY) {
4621 return titleText;
4622 }
4623 SpannableStringBuilder summary = new SpannableStringBuilder();
4624 if (titleText == null) {
4625 titleText = mN.extras.getCharSequence(Notification.EXTRA_TITLE_BIG);
4626 }
4627 BidiFormatter bidi = BidiFormatter.getInstance();
4628 if (titleText != null) {
4629 summary.append(bidi.unicodeWrap(titleText));
4630 }
4631 CharSequence contentText = mN.extras.getCharSequence(Notification.EXTRA_TEXT);
4632 if (titleText != null && contentText != null) {
4633 summary.append(bidi.unicodeWrap(mContext.getText(
4634 R.string.notification_header_divider_symbol_with_spaces)));
4635 }
4636 if (contentText != null) {
4637 summary.append(bidi.unicodeWrap(contentText));
4638 }
4639 return summary;
4640 }
Chris Wren8fd39ec2014-02-27 17:43:26 -05004641
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004642 private RemoteViews generateActionButton(Action action, boolean emphazisedMode,
Adrian Roos487374f2017-01-11 15:48:14 -08004643 boolean oddAction, boolean ambient) {
Daniel Sandler8680bf82012-05-15 16:52:52 -04004644 final boolean tombstone = (action.actionIntent == null);
Selim Cinekf33b1112015-07-15 17:45:11 -07004645 RemoteViews button = new BuilderRemoteViews(mContext.getApplicationInfo(),
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004646 emphazisedMode ? getEmphasizedActionLayoutResource()
4647 : tombstone ? getActionTombstoneLayoutResource()
4648 : getActionLayoutResource());
Daniel Sandler8680bf82012-05-15 16:52:52 -04004649 if (!tombstone) {
Daniel Sandlere5518842012-05-10 16:20:40 -04004650 button.setOnClickPendingIntent(R.id.action0, action.actionIntent);
Daniel Sandlere5518842012-05-10 16:20:40 -04004651 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004652 button.setContentDescription(R.id.action0, action.title);
Adrian Roosfe84e1f2015-11-04 15:55:39 -08004653 if (action.mRemoteInputs != null) {
4654 button.setRemoteInputs(R.id.action0, action.mRemoteInputs);
4655 }
Selim Cinek7b9605b2017-01-19 17:36:00 -08004656 // TODO: handle emphasized mode / actions right
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004657 if (emphazisedMode) {
Selim Cinek981962e2016-07-20 20:41:58 -07004658 // change the background bgColor
Selim Cinek622c64a2017-04-17 17:10:05 -07004659 int bgColor;
4660 if (isColorized()) {
4661 bgColor = oddAction ? getActionBarColor() : getActionBarColorDeEmphasized();
4662 } else {
4663 bgColor = mContext.getColor(oddAction ? R.color.notification_action_list
4664 : R.color.notification_action_list_dark);
4665 }
Selim Cinek981962e2016-07-20 20:41:58 -07004666 button.setDrawableParameters(R.id.button_holder, true, -1, bgColor,
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004667 PorterDuff.Mode.SRC_ATOP, -1);
Selim Cinek981962e2016-07-20 20:41:58 -07004668 CharSequence title = action.title;
4669 ColorStateList[] outResultColor = null;
4670 if (isLegacy()) {
4671 title = clearColorSpans(title);
4672 } else {
4673 outResultColor = new ColorStateList[1];
4674 title = ensureColorSpanContrast(title, bgColor, outResultColor);
4675 }
4676 button.setTextViewText(R.id.action0, title);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004677 setTextViewColorPrimary(button, R.id.action0);
Selim Cinek981962e2016-07-20 20:41:58 -07004678 if (outResultColor != null && outResultColor[0] != null) {
4679 // We need to set the text color as well since changing a text to uppercase
4680 // clears its spans.
4681 button.setTextColor(R.id.action0, outResultColor[0]);
Anthony Chenad4d1582017-04-10 16:07:58 -07004682 } else if (mN.color != COLOR_DEFAULT && !isColorized() && mTintActionButtons) {
Selim Cinek981962e2016-07-20 20:41:58 -07004683 button.setTextColor(R.id.action0,resolveContrastColor());
4684 }
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004685 } else {
Selim Cinek981962e2016-07-20 20:41:58 -07004686 button.setTextViewText(R.id.action0, processLegacyText(action.title));
Adrian Roos72171622017-01-27 10:32:06 -08004687 if (isColorized() && !ambient) {
Selim Cinek7b9605b2017-01-19 17:36:00 -08004688 setTextViewColorPrimary(button, R.id.action0);
Anthony Chenad4d1582017-04-10 16:07:58 -07004689 } else if (mN.color != COLOR_DEFAULT && mTintActionButtons) {
Adrian Roos487374f2017-01-11 15:48:14 -08004690 button.setTextColor(R.id.action0,
4691 ambient ? resolveAmbientColor() : resolveContrastColor());
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004692 }
Adrian Roosfe84e1f2015-11-04 15:55:39 -08004693 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004694 return button;
4695 }
4696
Joe Onoratocb109a02011-01-18 17:57:41 -08004697 /**
Selim Cinek981962e2016-07-20 20:41:58 -07004698 * Clears all color spans of a text
4699 * @param charSequence the input text
4700 * @return the same text but without color spans
4701 */
4702 private CharSequence clearColorSpans(CharSequence charSequence) {
4703 if (charSequence instanceof Spanned) {
4704 Spanned ss = (Spanned) charSequence;
4705 Object[] spans = ss.getSpans(0, ss.length(), Object.class);
4706 SpannableStringBuilder builder = new SpannableStringBuilder(ss.toString());
4707 for (Object span : spans) {
4708 Object resultSpan = span;
4709 if (resultSpan instanceof CharacterStyle) {
4710 resultSpan = ((CharacterStyle) span).getUnderlying();
4711 }
4712 if (resultSpan instanceof TextAppearanceSpan) {
4713 TextAppearanceSpan originalSpan = (TextAppearanceSpan) resultSpan;
4714 if (originalSpan.getTextColor() != null) {
4715 resultSpan = new TextAppearanceSpan(
4716 originalSpan.getFamily(),
4717 originalSpan.getTextStyle(),
4718 originalSpan.getTextSize(),
4719 null,
4720 originalSpan.getLinkTextColor());
4721 }
4722 } else if (resultSpan instanceof ForegroundColorSpan
4723 || (resultSpan instanceof BackgroundColorSpan)) {
4724 continue;
4725 } else {
4726 resultSpan = span;
4727 }
4728 builder.setSpan(resultSpan, ss.getSpanStart(span), ss.getSpanEnd(span),
4729 ss.getSpanFlags(span));
4730 }
4731 return builder;
4732 }
4733 return charSequence;
4734 }
4735
4736 /**
4737 * Ensures contrast on color spans against a background color. also returns the color of the
4738 * text if a span was found that spans over the whole text.
4739 *
4740 * @param charSequence the charSequence on which the spans are
4741 * @param background the background color to ensure the contrast against
4742 * @param outResultColor an array in which a color will be returned as the first element if
4743 * there exists a full length color span.
4744 * @return the contrasted charSequence
4745 */
4746 private CharSequence ensureColorSpanContrast(CharSequence charSequence, int background,
4747 ColorStateList[] outResultColor) {
4748 if (charSequence instanceof Spanned) {
4749 Spanned ss = (Spanned) charSequence;
4750 Object[] spans = ss.getSpans(0, ss.length(), Object.class);
4751 SpannableStringBuilder builder = new SpannableStringBuilder(ss.toString());
4752 for (Object span : spans) {
4753 Object resultSpan = span;
4754 int spanStart = ss.getSpanStart(span);
4755 int spanEnd = ss.getSpanEnd(span);
4756 boolean fullLength = (spanEnd - spanStart) == charSequence.length();
4757 if (resultSpan instanceof CharacterStyle) {
4758 resultSpan = ((CharacterStyle) span).getUnderlying();
4759 }
4760 if (resultSpan instanceof TextAppearanceSpan) {
4761 TextAppearanceSpan originalSpan = (TextAppearanceSpan) resultSpan;
4762 ColorStateList textColor = originalSpan.getTextColor();
4763 if (textColor != null) {
4764 int[] colors = textColor.getColors();
4765 int[] newColors = new int[colors.length];
4766 for (int i = 0; i < newColors.length; i++) {
4767 newColors[i] = NotificationColorUtil.ensureLargeTextContrast(
Anthony Chenad4d1582017-04-10 16:07:58 -07004768 colors[i], background, mInNightMode);
Selim Cinek981962e2016-07-20 20:41:58 -07004769 }
4770 textColor = new ColorStateList(textColor.getStates().clone(),
4771 newColors);
4772 resultSpan = new TextAppearanceSpan(
4773 originalSpan.getFamily(),
4774 originalSpan.getTextStyle(),
4775 originalSpan.getTextSize(),
4776 textColor,
4777 originalSpan.getLinkTextColor());
4778 if (fullLength) {
4779 outResultColor[0] = new ColorStateList(
4780 textColor.getStates().clone(), newColors);
4781 }
4782 }
4783 } else if (resultSpan instanceof ForegroundColorSpan) {
4784 ForegroundColorSpan originalSpan = (ForegroundColorSpan) resultSpan;
4785 int foregroundColor = originalSpan.getForegroundColor();
4786 foregroundColor = NotificationColorUtil.ensureLargeTextContrast(
Anthony Chenad4d1582017-04-10 16:07:58 -07004787 foregroundColor, background, mInNightMode);
Selim Cinek981962e2016-07-20 20:41:58 -07004788 resultSpan = new ForegroundColorSpan(foregroundColor);
4789 if (fullLength) {
4790 outResultColor[0] = ColorStateList.valueOf(foregroundColor);
4791 }
4792 } else {
4793 resultSpan = span;
4794 }
4795
4796 builder.setSpan(resultSpan, spanStart, spanEnd, ss.getSpanFlags(span));
4797 }
4798 return builder;
4799 }
4800 return charSequence;
4801 }
4802
4803 /**
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004804 * @return Whether we are currently building a notification from a legacy (an app that
Alan Viverette3cb07a462014-06-06 14:19:53 -07004805 * doesn't create material notifications by itself) app.
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004806 */
4807 private boolean isLegacy() {
Selim Cinek7b9605b2017-01-19 17:36:00 -08004808 if (!mIsLegacyInitialized) {
4809 mIsLegacy = mContext.getApplicationInfo().targetSdkVersion
4810 < Build.VERSION_CODES.LOLLIPOP;
4811 mIsLegacyInitialized = true;
4812 }
4813 return mIsLegacy;
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004814 }
4815
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004816 private CharSequence processLegacyText(CharSequence charSequence) {
Adrian Roos0bc3f6a2017-03-06 11:54:05 -08004817 return processLegacyText(charSequence, false /* ambient */);
4818 }
4819
4820 private CharSequence processLegacyText(CharSequence charSequence, boolean ambient) {
4821 boolean isAlreadyLightText = isLegacy() || textColorsNeedInversion();
4822 boolean wantLightText = ambient;
4823 if (isAlreadyLightText != wantLightText) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004824 return getColorUtil().invertCharSequenceColors(charSequence);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004825 } else {
4826 return charSequence;
4827 }
4828 }
4829
Dan Sandler26e81cf2014-05-06 10:01:27 -04004830 /**
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004831 * Apply any necessariy colors to the small icon
Dan Sandler26e81cf2014-05-06 10:01:27 -04004832 */
Adrian Roos487374f2017-01-11 15:48:14 -08004833 private void processSmallIconColor(Icon smallIcon, RemoteViews contentView,
4834 boolean ambient) {
Selim Cinekea4bef72015-12-02 15:51:10 -08004835 boolean colorable = !isLegacy() || getColorUtil().isGrayscaleIcon(mContext, smallIcon);
Selim Cinek99104832017-01-25 14:47:33 -08004836 int color = ambient ? resolveAmbientColor() : getPrimaryHighlightColor();
Selim Cinekea4bef72015-12-02 15:51:10 -08004837 if (colorable) {
Adrian Roos487374f2017-01-11 15:48:14 -08004838 contentView.setDrawableParameters(R.id.icon, false, -1, color,
Jorim Jaggi92df1f22014-12-16 19:44:41 +01004839 PorterDuff.Mode.SRC_ATOP, -1);
Selim Cinekea4bef72015-12-02 15:51:10 -08004840
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004841 }
Selim Cinekea4bef72015-12-02 15:51:10 -08004842 contentView.setInt(R.id.notification_header, "setOriginalIconColor",
Adrian Roos487374f2017-01-11 15:48:14 -08004843 colorable ? color : NotificationHeaderView.NO_COLOR);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004844 }
4845
Dan Sandler26e81cf2014-05-06 10:01:27 -04004846 /**
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004847 * Make the largeIcon dark if it's a fake smallIcon (that is,
Dan Sandler26e81cf2014-05-06 10:01:27 -04004848 * if it's grayscale).
4849 */
4850 // TODO: also check bounds, transparency, that sort of thing.
Dan Sandlerd63f9322015-05-06 15:18:49 -04004851 private void processLargeLegacyIcon(Icon largeIcon, RemoteViews contentView) {
4852 if (largeIcon != null && isLegacy()
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004853 && getColorUtil().isGrayscaleIcon(mContext, largeIcon)) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004854 // resolve color will fall back to the default when legacy
Adrian Roos4ff3b122016-02-01 12:26:13 -08004855 contentView.setDrawableParameters(R.id.icon, false, -1, resolveContrastColor(),
Dan Sandler190d58d2014-05-15 09:33:39 -04004856 PorterDuff.Mode.SRC_ATOP, -1);
Jorim Jaggi92df1f22014-12-16 19:44:41 +01004857 }
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004858 }
4859
Julia Reynolds10ee1fc2015-11-09 11:04:55 -05004860 private void sanitizeColor() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004861 if (mN.color != COLOR_DEFAULT) {
4862 mN.color |= 0xFF000000; // no alpha for custom colors
Jorim Jaggi74419312014-06-10 20:57:21 +02004863 }
Jorim Jaggi74419312014-06-10 20:57:21 +02004864 }
4865
Adrian Roos4ff3b122016-02-01 12:26:13 -08004866 int resolveContrastColor() {
4867 if (mCachedContrastColorIsFor == mN.color && mCachedContrastColor != COLOR_INVALID) {
4868 return mCachedContrastColor;
Dan Sandler26e81cf2014-05-06 10:01:27 -04004869 }
Adrian Roos4ff3b122016-02-01 12:26:13 -08004870
Selim Cinekac5f0272017-05-02 16:05:41 -07004871 int color;
4872 int background = mBackgroundColorHint;
4873 if (mBackgroundColorHint == COLOR_INVALID) {
4874 background = mContext.getColor(
4875 com.android.internal.R.color.notification_material_background_color);
4876 }
4877 if (mN.color == COLOR_DEFAULT) {
4878 ensureColors();
4879 color = mSecondaryTextColor;
4880 } else {
4881 color = NotificationColorUtil.resolveContrastColor(mContext, mN.color,
Anthony Chenad4d1582017-04-10 16:07:58 -07004882 background, mInNightMode);
Selim Cinekac5f0272017-05-02 16:05:41 -07004883 }
4884 if (Color.alpha(color) < 255) {
4885 // alpha doesn't go well for color filters, so let's blend it manually
4886 color = NotificationColorUtil.compositeColors(color, background);
4887 }
Adrian Roos4ff3b122016-02-01 12:26:13 -08004888 mCachedContrastColorIsFor = mN.color;
Selim Cinekac5f0272017-05-02 16:05:41 -07004889 return mCachedContrastColor = color;
Dan Sandler26e81cf2014-05-06 10:01:27 -04004890 }
4891
Adrian Roos487374f2017-01-11 15:48:14 -08004892 int resolveAmbientColor() {
4893 if (mCachedAmbientColorIsFor == mN.color && mCachedAmbientColorIsFor != COLOR_INVALID) {
4894 return mCachedAmbientColor;
4895 }
4896 final int contrasted = NotificationColorUtil.resolveAmbientColor(mContext, mN.color);
4897
4898 mCachedAmbientColorIsFor = mN.color;
4899 return mCachedAmbientColor = contrasted;
4900 }
4901
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004902 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004903 * Apply the unstyled operations and return a new {@link Notification} object.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04004904 * @hide
Joe Onoratocb109a02011-01-18 17:57:41 -08004905 */
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04004906 public Notification buildUnstyled() {
Daniel Sandlera0a938c2012-03-15 08:42:37 -04004907 if (mActions.size() > 0) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004908 mN.actions = new Action[mActions.size()];
4909 mActions.toArray(mN.actions);
Daniel Sandlera0a938c2012-03-15 08:42:37 -04004910 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004911 if (!mPersonList.isEmpty()) {
4912 mN.extras.putStringArray(EXTRA_PEOPLE,
4913 mPersonList.toArray(new String[mPersonList.size()]));
Dan Sandler0bf2ed82013-12-21 23:33:41 -06004914 }
Selim Cinek247fa012016-02-18 09:50:48 -08004915 if (mN.bigContentView != null || mN.contentView != null
4916 || mN.headsUpContentView != null) {
4917 mN.extras.putBoolean(EXTRA_CONTAINS_CUSTOM_VIEW, true);
4918 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004919 return mN;
Joe Onorato46439ce2010-11-19 13:56:21 -08004920 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004921
Julia Reynolds3b848122016-02-26 10:45:32 -05004922 /**
4923 * Creates a Builder from an existing notification so further changes can be made.
4924 * @param context The context for your application / activity.
4925 * @param n The notification to create a Builder from.
4926 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004927 public static Notification.Builder recoverBuilder(Context context, Notification n) {
Christoph Studer4600f9b2014-07-22 22:44:43 +02004928 // Re-create notification context so we can access app resources.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004929 ApplicationInfo applicationInfo = n.extras.getParcelable(
4930 EXTRA_BUILDER_APPLICATION_INFO);
Christoph Studer4600f9b2014-07-22 22:44:43 +02004931 Context builderContext;
Julia Reynoldsda303542015-11-23 14:00:20 -05004932 if (applicationInfo != null) {
4933 try {
4934 builderContext = context.createApplicationContext(applicationInfo,
4935 Context.CONTEXT_RESTRICTED);
4936 } catch (NameNotFoundException e) {
4937 Log.e(TAG, "ApplicationInfo " + applicationInfo + " not found");
4938 builderContext = context; // try with our context
4939 }
4940 } else {
4941 builderContext = context; // try with given context
Christoph Studer4600f9b2014-07-22 22:44:43 +02004942 }
4943
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004944 return new Builder(builderContext, n);
Christoph Studer4600f9b2014-07-22 22:44:43 +02004945 }
4946
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004947 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004948 * @deprecated Use {@link #build()} instead.
4949 */
4950 @Deprecated
4951 public Notification getNotification() {
4952 return build();
4953 }
4954
4955 /**
4956 * Combine all of the options that have been set and return a new {@link Notification}
4957 * object.
4958 */
4959 public Notification build() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004960 // first, add any extras from the calling code
4961 if (mUserExtras != null) {
4962 mN.extras = getAllExtras();
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07004963 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004964
Selim Cinekb85f36fd2016-04-20 18:46:36 -07004965 mN.creationTime = System.currentTimeMillis();
4966
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004967 // lazy stuff from mContext; see comment in Builder(Context, Notification)
Julia Reynoldsda303542015-11-23 14:00:20 -05004968 Notification.addFieldsFromContext(mContext, mN);
Christoph Studer943aa672014-08-03 20:31:16 +02004969
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004970 buildUnstyled();
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004971
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004972 if (mStyle != null) {
Selim Cinekd0426622017-07-11 13:19:59 +02004973 mStyle.reduceImageSizes(mContext);
4974 mStyle.purgeResources();
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004975 mStyle.buildStyled(mN);
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004976 }
Selim Cinekd0426622017-07-11 13:19:59 +02004977 mN.reduceImageSizes(mContext);
4978
Adrian Roos5081c0d2016-02-26 16:04:19 -08004979 if (mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N
Selim Cineka7679b62017-05-10 16:33:25 -07004980 && (useExistingRemoteView())) {
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004981 if (mN.contentView == null) {
Julia Reynolds3b848122016-02-26 10:45:32 -05004982 mN.contentView = createContentView();
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004983 mN.extras.putInt(EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT,
4984 mN.contentView.getSequenceNumber());
4985 }
4986 if (mN.bigContentView == null) {
Julia Reynolds3b848122016-02-26 10:45:32 -05004987 mN.bigContentView = createBigContentView();
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004988 if (mN.bigContentView != null) {
4989 mN.extras.putInt(EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT,
4990 mN.bigContentView.getSequenceNumber());
4991 }
4992 }
4993 if (mN.headsUpContentView == null) {
Julia Reynolds3b848122016-02-26 10:45:32 -05004994 mN.headsUpContentView = createHeadsUpContentView();
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004995 if (mN.headsUpContentView != null) {
4996 mN.extras.putInt(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT,
4997 mN.headsUpContentView.getSequenceNumber());
4998 }
4999 }
5000 }
5001
Julia Reynolds4c0c2022016-02-02 15:11:59 -05005002 if ((mN.defaults & DEFAULT_LIGHTS) != 0) {
5003 mN.flags |= FLAG_SHOW_LIGHTS;
5004 }
5005
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005006 return mN;
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005007 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05005008
5009 /**
5010 * Apply this Builder to an existing {@link Notification} object.
5011 *
5012 * @hide
5013 */
5014 public Notification buildInto(Notification n) {
Daniel Sandler1a497d32013-04-18 14:52:45 -04005015 build().cloneInto(n, true);
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05005016 return n;
5017 }
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01005018
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05005019 /**
Adrian Roos184bfe022016-03-03 13:41:44 -08005020 * Removes RemoteViews that were created for compatibility from {@param n}, if they did not
Julia Reynolds8a3b4592017-06-26 17:15:14 -04005021 * change. Also removes extenders on low ram devices, as
5022 * {@link android.service.notification.NotificationListenerService} services are disabled.
Adrian Roos184bfe022016-03-03 13:41:44 -08005023 *
5024 * @return {@param n}, if no stripping is needed, otherwise a stripped clone of {@param n}.
5025 *
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05005026 * @hide
5027 */
Julia Reynolds8a3b4592017-06-26 17:15:14 -04005028 public static Notification maybeCloneStrippedForDelivery(Notification n, boolean isLowRam) {
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05005029 String templateClass = n.extras.getString(EXTRA_TEMPLATE);
Adrian Roos184bfe022016-03-03 13:41:44 -08005030
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05005031 // Only strip views for known Styles because we won't know how to
5032 // re-create them otherwise.
Julia Reynolds8a3b4592017-06-26 17:15:14 -04005033 if (!isLowRam
5034 && !TextUtils.isEmpty(templateClass)
Adrian Roos184bfe022016-03-03 13:41:44 -08005035 && getNotificationStyleClass(templateClass) == null) {
5036 return n;
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05005037 }
Adrian Roos184bfe022016-03-03 13:41:44 -08005038
5039 // Only strip unmodified BuilderRemoteViews.
5040 boolean stripContentView = n.contentView instanceof BuilderRemoteViews &&
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05005041 n.extras.getInt(EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT, -1) ==
Adrian Roos184bfe022016-03-03 13:41:44 -08005042 n.contentView.getSequenceNumber();
5043 boolean stripBigContentView = n.bigContentView instanceof BuilderRemoteViews &&
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05005044 n.extras.getInt(EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT, -1) ==
Adrian Roos184bfe022016-03-03 13:41:44 -08005045 n.bigContentView.getSequenceNumber();
5046 boolean stripHeadsUpContentView = n.headsUpContentView instanceof BuilderRemoteViews &&
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05005047 n.extras.getInt(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT, -1) ==
Adrian Roos184bfe022016-03-03 13:41:44 -08005048 n.headsUpContentView.getSequenceNumber();
5049
5050 // Nothing to do here, no need to clone.
Julia Reynolds8a3b4592017-06-26 17:15:14 -04005051 if (!isLowRam
5052 && !stripContentView && !stripBigContentView && !stripHeadsUpContentView) {
Adrian Roos184bfe022016-03-03 13:41:44 -08005053 return n;
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05005054 }
Adrian Roos184bfe022016-03-03 13:41:44 -08005055
5056 Notification clone = n.clone();
5057 if (stripContentView) {
5058 clone.contentView = null;
5059 clone.extras.remove(EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT);
5060 }
5061 if (stripBigContentView) {
5062 clone.bigContentView = null;
5063 clone.extras.remove(EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT);
5064 }
5065 if (stripHeadsUpContentView) {
5066 clone.headsUpContentView = null;
5067 clone.extras.remove(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT);
5068 }
Julia Reynolds8a3b4592017-06-26 17:15:14 -04005069 if (isLowRam) {
5070 clone.extras.remove(Notification.TvExtender.EXTRA_TV_EXTENDER);
5071 clone.extras.remove(WearableExtender.EXTRA_WEARABLE_EXTENSIONS);
5072 clone.extras.remove(CarExtender.EXTRA_CAR_EXTENDER);
5073 }
Adrian Roos184bfe022016-03-03 13:41:44 -08005074 return clone;
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05005075 }
5076
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01005077 private int getBaseLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07005078 return R.layout.notification_template_material_base;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01005079 }
5080
5081 private int getBigBaseLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07005082 return R.layout.notification_template_material_big_base;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01005083 }
5084
5085 private int getBigPictureLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07005086 return R.layout.notification_template_material_big_picture;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01005087 }
5088
5089 private int getBigTextLayoutResource() {
Jorim Jaggi445d3c02014-08-19 22:33:42 +02005090 return R.layout.notification_template_material_big_text;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01005091 }
5092
5093 private int getInboxLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07005094 return R.layout.notification_template_material_inbox;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01005095 }
5096
Adrian Roosc1a80b02016-04-05 14:54:55 -07005097 private int getMessagingLayoutResource() {
5098 return R.layout.notification_template_material_messaging;
5099 }
5100
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01005101 private int getActionLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07005102 return R.layout.notification_material_action;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01005103 }
5104
Selim Cinek06e9e1f2016-07-08 17:14:16 -07005105 private int getEmphasizedActionLayoutResource() {
5106 return R.layout.notification_material_action_emphasized;
5107 }
5108
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01005109 private int getActionTombstoneLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07005110 return R.layout.notification_material_action_tombstone;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01005111 }
Selim Cinek7b9605b2017-01-19 17:36:00 -08005112
5113 private int getBackgroundColor() {
5114 if (isColorized()) {
Selim Cinek5fb73f82017-04-20 16:55:38 -07005115 return mBackgroundColor != COLOR_INVALID ? mBackgroundColor : mN.color;
Selim Cinek7b9605b2017-01-19 17:36:00 -08005116 } else {
Selim Cinekac5f0272017-05-02 16:05:41 -07005117 return mBackgroundColorHint != COLOR_INVALID ? mBackgroundColorHint
5118 : COLOR_DEFAULT;
Selim Cinek7b9605b2017-01-19 17:36:00 -08005119 }
5120 }
5121
5122 private boolean isColorized() {
5123 return mN.isColorized();
5124 }
Selim Cinek99104832017-01-25 14:47:33 -08005125
Anthony Chenad4d1582017-04-10 16:07:58 -07005126 private boolean shouldTintActionButtons() {
5127 return mTintActionButtons;
5128 }
5129
Selim Cinek99104832017-01-25 14:47:33 -08005130 private boolean textColorsNeedInversion() {
5131 if (mStyle == null || !MediaStyle.class.equals(mStyle.getClass())) {
5132 return false;
5133 }
5134 int targetSdkVersion = mContext.getApplicationInfo().targetSdkVersion;
5135 return targetSdkVersion > Build.VERSION_CODES.M
5136 && targetSdkVersion < Build.VERSION_CODES.O;
5137 }
Selim Cinek5fb73f82017-04-20 16:55:38 -07005138
5139 /**
5140 * Set a color palette to be used as the background and textColors
5141 *
5142 * @param backgroundColor the color to be used as the background
5143 * @param foregroundColor the color to be used as the foreground
5144 *
5145 * @hide
5146 */
5147 public void setColorPalette(int backgroundColor, int foregroundColor) {
5148 mBackgroundColor = backgroundColor;
5149 mForegroundColor = foregroundColor;
5150 mTextColorsAreForBackground = COLOR_INVALID;
5151 ensureColors();
5152 }
Selim Cinekac5f0272017-05-02 16:05:41 -07005153
5154 /**
5155 * Sets the background color for this notification to be a different one then the default.
5156 * This is mainly used to calculate contrast and won't necessarily be applied to the
5157 * background.
5158 *
5159 * @hide
5160 */
5161 public void setBackgroundColorHint(int backgroundColor) {
5162 mBackgroundColorHint = backgroundColor;
5163 }
Selim Cineka7679b62017-05-10 16:33:25 -07005164
5165
5166 /**
5167 * Forces all styled remoteViews to be built from scratch and not use any cached
5168 * RemoteViews.
5169 * This is needed for legacy apps that are baking in their remoteviews into the
5170 * notification.
5171 *
5172 * @hide
5173 */
5174 public void setRebuildStyledRemoteViews(boolean rebuild) {
5175 mRebuildStyledRemoteViews = rebuild;
5176 }
Selim Cinek7b9605b2017-01-19 17:36:00 -08005177 }
5178
5179 /**
Selim Cinekd0426622017-07-11 13:19:59 +02005180 * Reduces the image sizes to conform to a maximum allowed size. This also processes all custom
5181 * remote views.
5182 *
5183 * @hide
5184 */
5185 void reduceImageSizes(Context context) {
5186 if (extras.getBoolean(EXTRA_REDUCED_IMAGES)) {
5187 return;
5188 }
5189 if (mLargeIcon != null || largeIcon != null) {
5190 Resources resources = context.getResources();
5191 Class<? extends Style> style = getNotificationStyle();
5192 int maxWidth = resources.getDimensionPixelSize(R.dimen.notification_right_icon_size);
5193 int maxHeight = maxWidth;
5194 if (MediaStyle.class.equals(style)
5195 || DecoratedMediaCustomViewStyle.class.equals(style)) {
5196 maxHeight = resources.getDimensionPixelSize(
5197 R.dimen.notification_media_image_max_height);
5198 maxWidth = resources.getDimensionPixelSize(
5199 R.dimen.notification_media_image_max_width);
5200 }
5201 if (mLargeIcon != null) {
5202 mLargeIcon.scaleDownIfNecessary(maxWidth, maxHeight);
5203 }
5204 if (largeIcon != null) {
5205 largeIcon = Icon.scaleDownIfNecessary(largeIcon, maxWidth, maxHeight);
5206 }
5207 }
5208 reduceImageSizesForRemoteView(contentView, context);
5209 reduceImageSizesForRemoteView(headsUpContentView, context);
5210 reduceImageSizesForRemoteView(bigContentView, context);
5211 extras.putBoolean(EXTRA_REDUCED_IMAGES, true);
5212 }
5213
5214 private void reduceImageSizesForRemoteView(RemoteViews remoteView, Context context) {
5215 if (remoteView != null) {
5216 Resources resources = context.getResources();
5217 int maxWidth = resources.getDimensionPixelSize(
5218 R.dimen.notification_custom_view_max_image_width);
5219 int maxHeight = resources.getDimensionPixelSize(
5220 R.dimen.notification_custom_view_max_image_height);
5221 remoteView.reduceImageSizes(maxWidth, maxHeight);
5222 }
5223 }
5224
5225 /**
Selim Cinek22714f12017-04-13 16:23:53 -07005226 * @return whether this notification is a foreground service notification
Selim Cinek7b9605b2017-01-19 17:36:00 -08005227 */
Selim Cinek22714f12017-04-13 16:23:53 -07005228 private boolean isForegroundService() {
5229 return (flags & Notification.FLAG_FOREGROUND_SERVICE) != 0;
Selim Cinek7b9605b2017-01-19 17:36:00 -08005230 }
5231
5232 /**
Selim Cinek99104832017-01-25 14:47:33 -08005233 * @return whether this notification has a media session attached
5234 * @hide
5235 */
5236 public boolean hasMediaSession() {
5237 return extras.getParcelable(Notification.EXTRA_MEDIA_SESSION) != null;
5238 }
5239
5240 /**
Selim Cinek7d1009b2017-01-25 15:28:28 -08005241 * @return the style class of this notification
5242 * @hide
5243 */
5244 public Class<? extends Notification.Style> getNotificationStyle() {
5245 String templateClass = extras.getString(Notification.EXTRA_TEMPLATE);
5246
5247 if (!TextUtils.isEmpty(templateClass)) {
5248 return Notification.getNotificationStyleClass(templateClass);
5249 }
5250 return null;
5251 }
5252
5253 /**
Selim Cinek22714f12017-04-13 16:23:53 -07005254 * @return true if this notification is colorized.
Selim Cinek7b9605b2017-01-19 17:36:00 -08005255 *
5256 * @hide
5257 */
5258 public boolean isColorized() {
Selim Cinek5fb73f82017-04-20 16:55:38 -07005259 if (isColorizedMedia()) {
5260 return true;
5261 }
Julia Reynolds4db59552017-06-30 13:34:01 -04005262 return extras.getBoolean(EXTRA_COLORIZED)
5263 && (hasColorizedPermission() || isForegroundService());
5264 }
5265
5266 /**
5267 * Returns whether an app can colorize due to the android.permission.USE_COLORIZED_NOTIFICATIONS
5268 * permission. The permission is checked when a notification is enqueued.
5269 */
5270 private boolean hasColorizedPermission() {
5271 return (flags & Notification.FLAG_CAN_COLORIZE) != 0;
Selim Cinek5fb73f82017-04-20 16:55:38 -07005272 }
5273
5274 /**
5275 * @return true if this notification is colorized and it is a media notification
5276 *
5277 * @hide
5278 */
5279 public boolean isColorizedMedia() {
Selim Cinek99104832017-01-25 14:47:33 -08005280 Class<? extends Style> style = getNotificationStyle();
5281 if (MediaStyle.class.equals(style)) {
5282 Boolean colorized = (Boolean) extras.get(EXTRA_COLORIZED);
5283 if ((colorized == null || colorized) && hasMediaSession()) {
5284 return true;
5285 }
5286 } else if (DecoratedMediaCustomViewStyle.class.equals(style)) {
5287 if (extras.getBoolean(EXTRA_COLORIZED) && hasMediaSession()) {
5288 return true;
5289 }
5290 }
Selim Cinek5fb73f82017-04-20 16:55:38 -07005291 return false;
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005292 }
5293
Selim Cinek0847acd2017-04-24 19:48:29 -07005294
5295 /**
5296 * @return true if this is a media notification
5297 *
5298 * @hide
5299 */
5300 public boolean isMediaNotification() {
5301 Class<? extends Style> style = getNotificationStyle();
5302 if (MediaStyle.class.equals(style)) {
5303 return true;
5304 } else if (DecoratedMediaCustomViewStyle.class.equals(style)) {
5305 return true;
5306 }
5307 return false;
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005308 }
5309
Selim Cinek279fa862016-06-14 10:57:25 -07005310 private boolean hasLargeIcon() {
5311 return mLargeIcon != null || largeIcon != null;
5312 }
5313
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005314 /**
Selim Cinekc2c0b042016-05-18 17:13:46 -07005315 * @return true if the notification will show the time; false otherwise
Selim Cinekb85f36fd2016-04-20 18:46:36 -07005316 * @hide
5317 */
Selim Cinekc2c0b042016-05-18 17:13:46 -07005318 public boolean showsTime() {
Selim Cinekb85f36fd2016-04-20 18:46:36 -07005319 return when != 0 && extras.getBoolean(EXTRA_SHOW_WHEN);
5320 }
5321
5322 /**
Selim Cinekc2c0b042016-05-18 17:13:46 -07005323 * @return true if the notification will show a chronometer; false otherwise
5324 * @hide
5325 */
5326 public boolean showsChronometer() {
5327 return when != 0 && extras.getBoolean(EXTRA_SHOW_CHRONOMETER);
5328 }
5329
5330 /**
Julia Reynolds7ca33072017-06-29 13:58:24 -04005331 * @removed
Julia Reynolds4a02afb2016-12-13 13:39:52 -05005332 */
5333 @SystemApi
5334 public static Class<? extends Style> getNotificationStyleClass(String templateClass) {
5335 Class<? extends Style>[] classes = new Class[] {
5336 BigTextStyle.class, BigPictureStyle.class, InboxStyle.class, MediaStyle.class,
5337 DecoratedCustomViewStyle.class, DecoratedMediaCustomViewStyle.class,
5338 MessagingStyle.class };
5339 for (Class<? extends Style> innerClass : classes) {
5340 if (templateClass.equals(innerClass.getName())) {
5341 return innerClass;
5342 }
5343 }
5344 return null;
5345 }
5346
5347 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005348 * An object that can apply a rich notification style to a {@link Notification.Builder}
5349 * object.
5350 */
Griff Hazendfcb0802014-02-11 12:00:00 -08005351 public static abstract class Style {
Chris Wrend6297db2012-05-03 16:20:13 -04005352 private CharSequence mBigContentTitle;
Jorim Jaggi457a10d2014-09-08 16:18:23 +02005353
5354 /**
5355 * @hide
5356 */
5357 protected CharSequence mSummaryText = null;
5358
5359 /**
5360 * @hide
5361 */
5362 protected boolean mSummaryTextSet = false;
Chris Wrend6297db2012-05-03 16:20:13 -04005363
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005364 protected Builder mBuilder;
5365
Chris Wrend6297db2012-05-03 16:20:13 -04005366 /**
5367 * Overrides ContentTitle in the big form of the template.
5368 * This defaults to the value passed to setContentTitle().
5369 */
5370 protected void internalSetBigContentTitle(CharSequence title) {
5371 mBigContentTitle = title;
5372 }
5373
5374 /**
5375 * Set the first line of text after the detail section in the big form of the template.
5376 */
5377 protected void internalSetSummaryText(CharSequence cs) {
5378 mSummaryText = cs;
Daniel Sandler619738c2012-06-07 16:33:08 -04005379 mSummaryTextSet = true;
Chris Wrend6297db2012-05-03 16:20:13 -04005380 }
5381
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005382 public void setBuilder(Builder builder) {
5383 if (mBuilder != builder) {
5384 mBuilder = builder;
Daniel Sandlerc08dea22012-06-28 08:35:24 -07005385 if (mBuilder != null) {
5386 mBuilder.setStyle(this);
5387 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005388 }
5389 }
5390
Chris Wrend6297db2012-05-03 16:20:13 -04005391 protected void checkBuilder() {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005392 if (mBuilder == null) {
5393 throw new IllegalArgumentException("Style requires a valid Builder object");
5394 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005395 }
Chris Wrend6297db2012-05-03 16:20:13 -04005396
5397 protected RemoteViews getStandardView(int layoutId) {
5398 checkBuilder();
5399
Christoph Studer4600f9b2014-07-22 22:44:43 +02005400 // Nasty.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005401 CharSequence oldBuilderContentTitle =
5402 mBuilder.getAllExtras().getCharSequence(EXTRA_TITLE);
Chris Wrend6297db2012-05-03 16:20:13 -04005403 if (mBigContentTitle != null) {
5404 mBuilder.setContentTitle(mBigContentTitle);
5405 }
5406
Chris Wrend6297db2012-05-03 16:20:13 -04005407 RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(layoutId);
5408
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005409 mBuilder.getAllExtras().putCharSequence(EXTRA_TITLE, oldBuilderContentTitle);
Christoph Studer4600f9b2014-07-22 22:44:43 +02005410
Chris Wrend6297db2012-05-03 16:20:13 -04005411 if (mBigContentTitle != null && mBigContentTitle.equals("")) {
5412 contentView.setViewVisibility(R.id.line1, View.GONE);
Chris Wren67dc9a02012-05-16 01:03:20 -04005413 } else {
5414 contentView.setViewVisibility(R.id.line1, View.VISIBLE);
Chris Wrend6297db2012-05-03 16:20:13 -04005415 }
5416
Chris Wrend6297db2012-05-03 16:20:13 -04005417 return contentView;
5418 }
5419
Daniel Sandlerf45564e2013-04-15 15:05:08 -04005420 /**
Selim Cinek7d1009b2017-01-25 15:28:28 -08005421 * Construct a Style-specific RemoteViews for the collapsed notification layout.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005422 * The default implementation has nothing additional to add.
Selim Cinek7d1009b2017-01-25 15:28:28 -08005423 *
5424 * @param increasedHeight true if this layout be created with an increased height.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005425 * @hide
5426 */
Selim Cinek7d1009b2017-01-25 15:28:28 -08005427 public RemoteViews makeContentView(boolean increasedHeight) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005428 return null;
5429 }
5430
5431 /**
5432 * Construct a Style-specific RemoteViews for the final big notification layout.
5433 * @hide
5434 */
5435 public RemoteViews makeBigContentView() {
5436 return null;
5437 }
5438
5439 /**
5440 * Construct a Style-specific RemoteViews for the final HUN layout.
Selim Cinek87ed69b2017-02-09 15:59:43 -08005441 *
5442 * @param increasedHeight true if this layout be created with an increased height.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005443 * @hide
5444 */
Selim Cinek87ed69b2017-02-09 15:59:43 -08005445 public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005446 return null;
5447 }
5448
5449 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005450 * Apply any style-specific extras to this notification before shipping it out.
Daniel Sandlerf45564e2013-04-15 15:05:08 -04005451 * @hide
5452 */
5453 public void addExtras(Bundle extras) {
5454 if (mSummaryTextSet) {
5455 extras.putCharSequence(EXTRA_SUMMARY_TEXT, mSummaryText);
5456 }
5457 if (mBigContentTitle != null) {
5458 extras.putCharSequence(EXTRA_TITLE_BIG, mBigContentTitle);
5459 }
Chris Wren91ad5632013-06-05 15:05:57 -04005460 extras.putString(EXTRA_TEMPLATE, this.getClass().getName());
Daniel Sandlerf45564e2013-04-15 15:05:08 -04005461 }
5462
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04005463 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005464 * Reconstruct the internal state of this Style object from extras.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04005465 * @hide
5466 */
Christoph Studer4600f9b2014-07-22 22:44:43 +02005467 protected void restoreFromExtras(Bundle extras) {
5468 if (extras.containsKey(EXTRA_SUMMARY_TEXT)) {
5469 mSummaryText = extras.getCharSequence(EXTRA_SUMMARY_TEXT);
5470 mSummaryTextSet = true;
5471 }
5472 if (extras.containsKey(EXTRA_TITLE_BIG)) {
5473 mBigContentTitle = extras.getCharSequence(EXTRA_TITLE_BIG);
5474 }
5475 }
5476
5477
5478 /**
5479 * @hide
5480 */
5481 public Notification buildStyled(Notification wip) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005482 addExtras(wip.extras);
Christoph Studer4600f9b2014-07-22 22:44:43 +02005483 return wip;
5484 }
5485
Daniel Sandler0ec46202015-06-24 01:27:05 -04005486 /**
5487 * @hide
5488 */
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07005489 public void purgeResources() {}
5490
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04005491 /**
5492 * Calls {@link android.app.Notification.Builder#build()} on the Builder this Style is
5493 * attached to.
5494 *
5495 * @return the fully constructed Notification.
5496 */
5497 public Notification build() {
5498 checkBuilder();
5499 return mBuilder.build();
5500 }
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02005501
5502 /**
5503 * @hide
5504 * @return true if the style positions the progress bar on the second line; false if the
5505 * style hides the progress bar
5506 */
5507 protected boolean hasProgress() {
5508 return true;
5509 }
Selim Cinek03d0d652015-11-13 13:18:09 -05005510
5511 /**
5512 * @hide
5513 * @return Whether we should put the summary be put into the notification header
5514 */
5515 public boolean hasSummaryInHeader() {
5516 return true;
5517 }
Selim Cinek593610c2016-02-16 18:42:57 -08005518
5519 /**
5520 * @hide
5521 * @return Whether custom content views are displayed inline in the style
5522 */
5523 public boolean displayCustomViewInline() {
5524 return false;
5525 }
Selim Cinekd0426622017-07-11 13:19:59 +02005526
5527 /**
5528 * Reduces the image sizes contained in this style.
5529 *
5530 * @hide
5531 */
5532 public void reduceImageSizes(Context context) {
5533 }
Joe Onorato46439ce2010-11-19 13:56:21 -08005534 }
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005535
5536 /**
Daniel Sandler4dfbe832012-04-11 14:51:46 -04005537 * Helper class for generating large-format notifications that include a large image attachment.
Joe Malin8d40d042012-11-05 11:36:40 -08005538 *
Robert Ly91c5ce32014-06-08 15:37:00 -07005539 * Here's how you'd set the <code>BigPictureStyle</code> on a notification:
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005540 * <pre class="prettyprint">
Robert Ly91c5ce32014-06-08 15:37:00 -07005541 * Notification notif = new Notification.Builder(mContext)
5542 * .setContentTitle(&quot;New photo from &quot; + sender.toString())
5543 * .setContentText(subject)
5544 * .setSmallIcon(R.drawable.new_post)
5545 * .setLargeIcon(aBitmap)
5546 * .setStyle(new Notification.BigPictureStyle()
5547 * .bigPicture(aBigBitmap))
5548 * .build();
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005549 * </pre>
Joe Malin8d40d042012-11-05 11:36:40 -08005550 *
Daniel Sandler4dfbe832012-04-11 14:51:46 -04005551 * @see Notification#bigContentView
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005552 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005553 public static class BigPictureStyle extends Style {
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005554 private Bitmap mPicture;
Dan Sandlerd63f9322015-05-06 15:18:49 -04005555 private Icon mBigLargeIcon;
Chris Wren3745a3d2012-05-22 15:11:52 -04005556 private boolean mBigLargeIconSet = false;
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005557
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005558 public BigPictureStyle() {
5559 }
5560
Adrian Roosf5faf9d2016-05-23 13:56:15 -07005561 /**
5562 * @deprecated use {@code BigPictureStyle()}.
5563 */
5564 @Deprecated
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005565 public BigPictureStyle(Builder builder) {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005566 setBuilder(builder);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005567 }
5568
Chris Wrend6297db2012-05-03 16:20:13 -04005569 /**
5570 * Overrides ContentTitle in the big form of the template.
5571 * This defaults to the value passed to setContentTitle().
5572 */
5573 public BigPictureStyle setBigContentTitle(CharSequence title) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04005574 internalSetBigContentTitle(safeCharSequence(title));
Chris Wrend6297db2012-05-03 16:20:13 -04005575 return this;
5576 }
5577
5578 /**
5579 * Set the first line of text after the detail section in the big form of the template.
5580 */
5581 public BigPictureStyle setSummaryText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04005582 internalSetSummaryText(safeCharSequence(cs));
Chris Wrend6297db2012-05-03 16:20:13 -04005583 return this;
5584 }
5585
Chris Wren0bd664d2012-08-01 13:56:56 -04005586 /**
5587 * Provide the bitmap to be used as the payload for the BigPicture notification.
5588 */
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005589 public BigPictureStyle bigPicture(Bitmap b) {
5590 mPicture = b;
5591 return this;
5592 }
5593
Chris Wren3745a3d2012-05-22 15:11:52 -04005594 /**
Chris Wren3745a3d2012-05-22 15:11:52 -04005595 * Override the large icon when the big notification is shown.
5596 */
5597 public BigPictureStyle bigLargeIcon(Bitmap b) {
Dan Sandlerd63f9322015-05-06 15:18:49 -04005598 return bigLargeIcon(b != null ? Icon.createWithBitmap(b) : null);
5599 }
5600
5601 /**
5602 * Override the large icon when the big notification is shown.
5603 */
5604 public BigPictureStyle bigLargeIcon(Icon icon) {
Chris Wren3745a3d2012-05-22 15:11:52 -04005605 mBigLargeIconSet = true;
Dan Sandlerd63f9322015-05-06 15:18:49 -04005606 mBigLargeIcon = icon;
Chris Wren3745a3d2012-05-22 15:11:52 -04005607 return this;
5608 }
5609
Riley Andrews0394a0c2015-11-03 23:36:52 -08005610 /** @hide */
5611 public static final int MIN_ASHMEM_BITMAP_SIZE = 128 * (1 << 10);
5612
Daniel Sandler0ec46202015-06-24 01:27:05 -04005613 /**
5614 * @hide
5615 */
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07005616 @Override
5617 public void purgeResources() {
5618 super.purgeResources();
Riley Andrews8cee7c12015-11-01 23:36:04 -08005619 if (mPicture != null &&
5620 mPicture.isMutable() &&
Riley Andrews0394a0c2015-11-03 23:36:52 -08005621 mPicture.getAllocationByteCount() >= MIN_ASHMEM_BITMAP_SIZE) {
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07005622 mPicture = mPicture.createAshmemBitmap();
5623 }
5624 if (mBigLargeIcon != null) {
5625 mBigLargeIcon.convertToAshmem();
5626 }
5627 }
Christoph Studer5c510ee2014-12-15 16:32:27 +01005628
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005629 /**
5630 * @hide
5631 */
Selim Cinekd0426622017-07-11 13:19:59 +02005632 @Override
5633 public void reduceImageSizes(Context context) {
5634 super.reduceImageSizes(context);
5635 Resources resources = context.getResources();
5636 if (mPicture != null) {
5637 int maxPictureWidth = resources.getDimensionPixelSize(
5638 R.dimen.notification_big_picture_max_height);
5639 int maxPictureHeight = resources.getDimensionPixelSize(
5640 R.dimen.notification_big_picture_max_width);
5641 mPicture = Icon.scaleDownIfNecessary(mPicture, maxPictureWidth, maxPictureHeight);
5642 }
5643 if (mBigLargeIcon != null) {
5644 int rightIconSize = resources.getDimensionPixelSize(
5645 R.dimen.notification_right_icon_size);
5646 mBigLargeIcon.scaleDownIfNecessary(rightIconSize, rightIconSize);
5647 }
5648 }
5649
5650 /**
5651 * @hide
5652 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005653 public RemoteViews makeBigContentView() {
5654 // Replace mN.mLargeIcon with mBigLargeIcon if mBigLargeIconSet
Christoph Studer5c510ee2014-12-15 16:32:27 +01005655 // This covers the following cases:
5656 // 1. mBigLargeIconSet -> mBigLargeIcon (null or non-null) applies, overrides
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005657 // mN.mLargeIcon
5658 // 2. !mBigLargeIconSet -> mN.mLargeIcon applies
Dan Sandlerd63f9322015-05-06 15:18:49 -04005659 Icon oldLargeIcon = null;
Selim Cineke99acb22016-08-04 12:55:48 -07005660 Bitmap largeIconLegacy = null;
Christoph Studer5c510ee2014-12-15 16:32:27 +01005661 if (mBigLargeIconSet) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005662 oldLargeIcon = mBuilder.mN.mLargeIcon;
5663 mBuilder.mN.mLargeIcon = mBigLargeIcon;
Selim Cineke99acb22016-08-04 12:55:48 -07005664 // The legacy largeIcon might not allow us to clear the image, as it's taken in
5665 // replacement if the other one is null. Because we're restoring these legacy icons
5666 // for old listeners, this is in general non-null.
5667 largeIconLegacy = mBuilder.mN.largeIcon;
5668 mBuilder.mN.largeIcon = null;
Christoph Studer5c510ee2014-12-15 16:32:27 +01005669 }
5670
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01005671 RemoteViews contentView = getStandardView(mBuilder.getBigPictureLayoutResource());
Selim Cinek03d0d652015-11-13 13:18:09 -05005672 if (mSummaryTextSet) {
5673 contentView.setTextViewText(R.id.text, mBuilder.processLegacyText(mSummaryText));
Selim Cinek7b9605b2017-01-19 17:36:00 -08005674 mBuilder.setTextViewColorSecondary(contentView, R.id.text);
Selim Cinekc848c3a2016-01-13 15:27:30 -08005675 contentView.setViewVisibility(R.id.text, View.VISIBLE);
Selim Cinek03d0d652015-11-13 13:18:09 -05005676 }
Selim Cinek279fa862016-06-14 10:57:25 -07005677 mBuilder.setContentMinHeight(contentView, mBuilder.mN.hasLargeIcon());
Selim Cinek53e64a42015-11-16 10:40:56 -08005678
Christoph Studer5c510ee2014-12-15 16:32:27 +01005679 if (mBigLargeIconSet) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005680 mBuilder.mN.mLargeIcon = oldLargeIcon;
Selim Cineke99acb22016-08-04 12:55:48 -07005681 mBuilder.mN.largeIcon = largeIconLegacy;
Christoph Studer5c510ee2014-12-15 16:32:27 +01005682 }
5683
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005684 contentView.setImageViewBitmap(R.id.big_picture, mPicture);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005685 return contentView;
5686 }
5687
Daniel Sandlerf45564e2013-04-15 15:05:08 -04005688 /**
5689 * @hide
5690 */
5691 public void addExtras(Bundle extras) {
5692 super.addExtras(extras);
5693
5694 if (mBigLargeIconSet) {
5695 extras.putParcelable(EXTRA_LARGE_ICON_BIG, mBigLargeIcon);
5696 }
5697 extras.putParcelable(EXTRA_PICTURE, mPicture);
5698 }
5699
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04005700 /**
5701 * @hide
5702 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005703 @Override
Christoph Studer4600f9b2014-07-22 22:44:43 +02005704 protected void restoreFromExtras(Bundle extras) {
5705 super.restoreFromExtras(extras);
5706
5707 if (extras.containsKey(EXTRA_LARGE_ICON_BIG)) {
Christoph Studer5c510ee2014-12-15 16:32:27 +01005708 mBigLargeIconSet = true;
Christoph Studer4600f9b2014-07-22 22:44:43 +02005709 mBigLargeIcon = extras.getParcelable(EXTRA_LARGE_ICON_BIG);
Chris Wren3745a3d2012-05-22 15:11:52 -04005710 }
Christoph Studer4600f9b2014-07-22 22:44:43 +02005711 mPicture = extras.getParcelable(EXTRA_PICTURE);
5712 }
Selim Cinek03d0d652015-11-13 13:18:09 -05005713
5714 /**
5715 * @hide
5716 */
5717 @Override
5718 public boolean hasSummaryInHeader() {
5719 return false;
5720 }
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005721 }
5722
5723 /**
Daniel Sandler4dfbe832012-04-11 14:51:46 -04005724 * Helper class for generating large-format notifications that include a lot of text.
Joe Malin8d40d042012-11-05 11:36:40 -08005725 *
Robert Ly91c5ce32014-06-08 15:37:00 -07005726 * Here's how you'd set the <code>BigTextStyle</code> on a notification:
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005727 * <pre class="prettyprint">
Robert Ly91c5ce32014-06-08 15:37:00 -07005728 * Notification notif = new Notification.Builder(mContext)
5729 * .setContentTitle(&quot;New mail from &quot; + sender.toString())
5730 * .setContentText(subject)
5731 * .setSmallIcon(R.drawable.new_mail)
5732 * .setLargeIcon(aBitmap)
5733 * .setStyle(new Notification.BigTextStyle()
5734 * .bigText(aVeryLongString))
5735 * .build();
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005736 * </pre>
Joe Malin8d40d042012-11-05 11:36:40 -08005737 *
Daniel Sandler4dfbe832012-04-11 14:51:46 -04005738 * @see Notification#bigContentView
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005739 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005740 public static class BigTextStyle extends Style {
Jorim Jaggi457a10d2014-09-08 16:18:23 +02005741
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005742 private CharSequence mBigText;
5743
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005744 public BigTextStyle() {
5745 }
5746
Adrian Roosf5faf9d2016-05-23 13:56:15 -07005747 /**
5748 * @deprecated use {@code BigTextStyle()}.
5749 */
5750 @Deprecated
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005751 public BigTextStyle(Builder builder) {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005752 setBuilder(builder);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005753 }
5754
Chris Wrend6297db2012-05-03 16:20:13 -04005755 /**
5756 * Overrides ContentTitle in the big form of the template.
5757 * This defaults to the value passed to setContentTitle().
5758 */
5759 public BigTextStyle setBigContentTitle(CharSequence title) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04005760 internalSetBigContentTitle(safeCharSequence(title));
Chris Wrend6297db2012-05-03 16:20:13 -04005761 return this;
5762 }
5763
5764 /**
5765 * Set the first line of text after the detail section in the big form of the template.
5766 */
5767 public BigTextStyle setSummaryText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04005768 internalSetSummaryText(safeCharSequence(cs));
Chris Wrend6297db2012-05-03 16:20:13 -04005769 return this;
5770 }
5771
Chris Wren0bd664d2012-08-01 13:56:56 -04005772 /**
5773 * Provide the longer text to be displayed in the big form of the
5774 * template in place of the content text.
5775 */
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005776 public BigTextStyle bigText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04005777 mBigText = safeCharSequence(cs);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005778 return this;
5779 }
5780
Daniel Sandlerf45564e2013-04-15 15:05:08 -04005781 /**
5782 * @hide
5783 */
5784 public void addExtras(Bundle extras) {
5785 super.addExtras(extras);
5786
Christoph Studer4600f9b2014-07-22 22:44:43 +02005787 extras.putCharSequence(EXTRA_BIG_TEXT, mBigText);
5788 }
5789
5790 /**
5791 * @hide
5792 */
5793 @Override
5794 protected void restoreFromExtras(Bundle extras) {
5795 super.restoreFromExtras(extras);
5796
5797 mBigText = extras.getCharSequence(EXTRA_BIG_TEXT);
Daniel Sandlerf45564e2013-04-15 15:05:08 -04005798 }
5799
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005800 /**
Selim Cinek7d1009b2017-01-25 15:28:28 -08005801 * @param increasedHeight true if this layout be created with an increased height.
5802 *
5803 * @hide
5804 */
5805 @Override
5806 public RemoteViews makeContentView(boolean increasedHeight) {
5807 if (increasedHeight) {
Selim Cinek5d6ef8d2017-05-18 22:16:00 -07005808 mBuilder.mOriginalActions = mBuilder.mActions;
Selim Cinek7d1009b2017-01-25 15:28:28 -08005809 mBuilder.mActions = new ArrayList<>();
5810 RemoteViews remoteViews = makeBigContentView();
Selim Cinek5d6ef8d2017-05-18 22:16:00 -07005811 mBuilder.mActions = mBuilder.mOriginalActions;
5812 mBuilder.mOriginalActions = null;
Selim Cinek7d1009b2017-01-25 15:28:28 -08005813 return remoteViews;
5814 }
5815 return super.makeContentView(increasedHeight);
5816 }
5817
5818 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005819 * @hide
5820 */
Selim Cinek87ed69b2017-02-09 15:59:43 -08005821 @Override
5822 public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
5823 if (increasedHeight && mBuilder.mActions.size() > 0) {
5824 return makeBigContentView();
5825 }
5826 return super.makeHeadsUpContentView(increasedHeight);
5827 }
5828
5829 /**
5830 * @hide
5831 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005832 public RemoteViews makeBigContentView() {
Christoph Studer4600f9b2014-07-22 22:44:43 +02005833
5834 // Nasty
Selim Cinek75998782016-04-26 10:39:17 -07005835 CharSequence text = mBuilder.getAllExtras().getCharSequence(EXTRA_TEXT);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005836 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, null);
Daniel Sandler916ad912012-06-13 12:17:07 -04005837
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01005838 RemoteViews contentView = getStandardView(mBuilder.getBigTextLayoutResource());
Joe Malin8d40d042012-11-05 11:36:40 -08005839
Selim Cinek75998782016-04-26 10:39:17 -07005840 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, text);
Christoph Studer4600f9b2014-07-22 22:44:43 +02005841
Selim Cinek3a2c4b92015-12-17 17:01:17 -08005842 CharSequence bigTextText = mBuilder.processLegacyText(mBigText);
Selim Cinek75998782016-04-26 10:39:17 -07005843 if (TextUtils.isEmpty(bigTextText)) {
5844 // In case the bigtext is null / empty fall back to the normal text to avoid a weird
5845 // experience
5846 bigTextText = mBuilder.processLegacyText(text);
5847 }
Adrian Roosb1f427c2016-05-26 12:27:15 -07005848 applyBigTextContentView(mBuilder, contentView, bigTextText);
Selim Cinek4fb12d32015-11-19 18:10:48 -08005849
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005850 return contentView;
5851 }
5852
Adrian Roosb1f427c2016-05-26 12:27:15 -07005853 static void applyBigTextContentView(Builder builder,
5854 RemoteViews contentView, CharSequence bigTextText) {
5855 contentView.setTextViewText(R.id.big_text, bigTextText);
Selim Cinek7b9605b2017-01-19 17:36:00 -08005856 builder.setTextViewColorSecondary(contentView, R.id.big_text);
Adrian Roosb1f427c2016-05-26 12:27:15 -07005857 contentView.setViewVisibility(R.id.big_text,
5858 TextUtils.isEmpty(bigTextText) ? View.GONE : View.VISIBLE);
Selim Cinek279fa862016-06-14 10:57:25 -07005859 contentView.setBoolean(R.id.big_text, "setHasImage", builder.mN.hasLargeIcon());
Adrian Roosb1f427c2016-05-26 12:27:15 -07005860 }
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005861 }
Daniel Sandler879c5e02012-04-17 16:46:51 -04005862
5863 /**
Alex Hillsfc737de2016-03-23 17:33:02 -04005864 * Helper class for generating large-format notifications that include multiple back-and-forth
5865 * messages of varying types between any number of people.
5866 *
5867 * <br>
5868 * If the platform does not provide large-format notifications, this method has no effect. The
5869 * user will always see the normal notification view.
5870 * <br>
5871 * This class is a "rebuilder": It attaches to a Builder object and modifies its behavior, like
5872 * so:
5873 * <pre class="prettyprint">
5874 *
5875 * Notification noti = new Notification.Builder()
5876 * .setContentTitle(&quot;2 new messages wtih &quot; + sender.toString())
5877 * .setContentText(subject)
5878 * .setSmallIcon(R.drawable.new_message)
5879 * .setLargeIcon(aBitmap)
5880 * .setStyle(new Notification.MessagingStyle(resources.getString(R.string.reply_name))
5881 * .addMessage(messages[0].getText(), messages[0].getTime(), messages[0].getSender())
5882 * .addMessage(messages[1].getText(), messages[1].getTime(), messages[1].getSender()))
5883 * .build();
5884 * </pre>
5885 */
5886 public static class MessagingStyle extends Style {
5887
5888 /**
5889 * The maximum number of messages that will be retained in the Notification itself (the
5890 * number displayed is up to the platform).
5891 */
5892 public static final int MAXIMUM_RETAINED_MESSAGES = 25;
5893
5894 CharSequence mUserDisplayName;
5895 CharSequence mConversationTitle;
Alex Hillsd9b04d92016-04-11 16:38:16 -04005896 List<Message> mMessages = new ArrayList<>();
Adrian Roos437cd562017-01-18 15:47:03 -08005897 List<Message> mHistoricMessages = new ArrayList<>();
Alex Hillsfc737de2016-03-23 17:33:02 -04005898
5899 MessagingStyle() {
5900 }
5901
5902 /**
Alex Hillsfd590442016-10-07 09:52:44 -04005903 * @param userDisplayName Required - the name to be displayed for any replies sent by the
5904 * user before the posting app reposts the notification with those messages after they've
5905 * been actually sent and in previous messages sent by the user added in
Alex Hillsfc737de2016-03-23 17:33:02 -04005906 * {@link #addMessage(Notification.MessagingStyle.Message)}
5907 */
Alex Hillsfd590442016-10-07 09:52:44 -04005908 public MessagingStyle(@NonNull CharSequence userDisplayName) {
Alex Hillsfc737de2016-03-23 17:33:02 -04005909 mUserDisplayName = userDisplayName;
5910 }
5911
5912 /**
5913 * Returns the name to be displayed for any replies sent by the user
5914 */
5915 public CharSequence getUserDisplayName() {
5916 return mUserDisplayName;
5917 }
5918
5919 /**
Alex Hillsfc737de2016-03-23 17:33:02 -04005920 * Sets the title to be displayed on this conversation. This should only be used for
5921 * group messaging and left unset for one-on-one conversations.
5922 * @param conversationTitle
5923 * @return this object for method chaining.
5924 */
5925 public MessagingStyle setConversationTitle(CharSequence conversationTitle) {
5926 mConversationTitle = conversationTitle;
5927 return this;
5928 }
5929
5930 /**
5931 * Return the title to be displayed on this conversation. Can be <code>null</code> and
5932 * should be for one-on-one conversations
5933 */
5934 public CharSequence getConversationTitle() {
5935 return mConversationTitle;
5936 }
5937
5938 /**
5939 * Adds a message for display by this notification. Convenience call for a simple
5940 * {@link Message} in {@link #addMessage(Notification.MessagingStyle.Message)}.
5941 * @param text A {@link CharSequence} to be displayed as the message content
5942 * @param timestamp Time at which the message arrived
5943 * @param sender A {@link CharSequence} to be used for displaying the name of the
5944 * sender. Should be <code>null</code> for messages by the current user, in which case
5945 * the platform will insert {@link #getUserDisplayName()}.
5946 * Should be unique amongst all individuals in the conversation, and should be
5947 * consistent during re-posts of the notification.
5948 *
5949 * @see Message#Message(CharSequence, long, CharSequence)
5950 *
5951 * @return this object for method chaining
5952 */
5953 public MessagingStyle addMessage(CharSequence text, long timestamp, CharSequence sender) {
Adrian Roos437cd562017-01-18 15:47:03 -08005954 return addMessage(new Message(text, timestamp, sender));
Alex Hillsfc737de2016-03-23 17:33:02 -04005955 }
5956
5957 /**
5958 * Adds a {@link Message} for display in this notification.
Adrian Roos437cd562017-01-18 15:47:03 -08005959 *
5960 * <p>The messages should be added in chronologic order, i.e. the oldest first,
5961 * the newest last.
5962 *
Alex Hillsfc737de2016-03-23 17:33:02 -04005963 * @param message The {@link Message} to be displayed
5964 * @return this object for method chaining
5965 */
5966 public MessagingStyle addMessage(Message message) {
5967 mMessages.add(message);
5968 if (mMessages.size() > MAXIMUM_RETAINED_MESSAGES) {
5969 mMessages.remove(0);
5970 }
5971 return this;
5972 }
5973
5974 /**
Adrian Roos437cd562017-01-18 15:47:03 -08005975 * Adds a {@link Message} for historic context in this notification.
5976 *
5977 * <p>Messages should be added as historic if they are not the main subject of the
5978 * notification but may give context to a conversation. The system may choose to present
5979 * them only when relevant, e.g. when replying to a message through a {@link RemoteInput}.
5980 *
5981 * <p>The messages should be added in chronologic order, i.e. the oldest first,
5982 * the newest last.
5983 *
5984 * @param message The historic {@link Message} to be added
5985 * @return this object for method chaining
5986 */
5987 public MessagingStyle addHistoricMessage(Message message) {
5988 mHistoricMessages.add(message);
5989 if (mHistoricMessages.size() > MAXIMUM_RETAINED_MESSAGES) {
5990 mHistoricMessages.remove(0);
5991 }
5992 return this;
5993 }
5994
5995 /**
Alex Hillsfc737de2016-03-23 17:33:02 -04005996 * Gets the list of {@code Message} objects that represent the notification
5997 */
5998 public List<Message> getMessages() {
5999 return mMessages;
6000 }
6001
6002 /**
Adrian Roos437cd562017-01-18 15:47:03 -08006003 * Gets the list of historic {@code Message}s in the notification.
6004 */
6005 public List<Message> getHistoricMessages() {
6006 return mHistoricMessages;
6007 }
6008
6009 /**
Alex Hillsfc737de2016-03-23 17:33:02 -04006010 * @hide
6011 */
6012 @Override
6013 public void addExtras(Bundle extras) {
6014 super.addExtras(extras);
6015 if (mUserDisplayName != null) {
6016 extras.putCharSequence(EXTRA_SELF_DISPLAY_NAME, mUserDisplayName);
6017 }
6018 if (mConversationTitle != null) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04006019 extras.putCharSequence(EXTRA_CONVERSATION_TITLE, mConversationTitle);
Alex Hillsfc737de2016-03-23 17:33:02 -04006020 }
Alex Hillsd9b04d92016-04-11 16:38:16 -04006021 if (!mMessages.isEmpty()) { extras.putParcelableArray(EXTRA_MESSAGES,
6022 Message.getBundleArrayForMessages(mMessages));
Alex Hillsfc737de2016-03-23 17:33:02 -04006023 }
Adrian Roos437cd562017-01-18 15:47:03 -08006024 if (!mHistoricMessages.isEmpty()) { extras.putParcelableArray(EXTRA_HISTORIC_MESSAGES,
6025 Message.getBundleArrayForMessages(mHistoricMessages));
6026 }
Adrian Roos33fbd2c2016-05-27 15:35:28 -07006027
6028 fixTitleAndTextExtras(extras);
6029 }
6030
6031 private void fixTitleAndTextExtras(Bundle extras) {
6032 Message m = findLatestIncomingMessage();
6033 CharSequence text = (m == null) ? null : m.mText;
6034 CharSequence sender = m == null ? null
6035 : TextUtils.isEmpty(m.mSender) ? mUserDisplayName : m.mSender;
6036 CharSequence title;
6037 if (!TextUtils.isEmpty(mConversationTitle)) {
6038 if (!TextUtils.isEmpty(sender)) {
6039 BidiFormatter bidi = BidiFormatter.getInstance();
6040 title = mBuilder.mContext.getString(
6041 com.android.internal.R.string.notification_messaging_title_template,
6042 bidi.unicodeWrap(mConversationTitle), bidi.unicodeWrap(m.mSender));
6043 } else {
6044 title = mConversationTitle;
6045 }
6046 } else {
6047 title = sender;
6048 }
6049
6050 if (title != null) {
6051 extras.putCharSequence(EXTRA_TITLE, title);
6052 }
6053 if (text != null) {
6054 extras.putCharSequence(EXTRA_TEXT, text);
6055 }
Alex Hillsfc737de2016-03-23 17:33:02 -04006056 }
6057
6058 /**
6059 * @hide
6060 */
6061 @Override
6062 protected void restoreFromExtras(Bundle extras) {
6063 super.restoreFromExtras(extras);
6064
6065 mMessages.clear();
Adrian Roos437cd562017-01-18 15:47:03 -08006066 mHistoricMessages.clear();
Adrian Roos96b7e202016-05-17 13:50:38 -07006067 mUserDisplayName = extras.getCharSequence(EXTRA_SELF_DISPLAY_NAME);
6068 mConversationTitle = extras.getCharSequence(EXTRA_CONVERSATION_TITLE);
Adrian Roos437cd562017-01-18 15:47:03 -08006069 Parcelable[] messages = extras.getParcelableArray(EXTRA_MESSAGES);
6070 if (messages != null && messages instanceof Parcelable[]) {
6071 mMessages = Message.getMessagesFromBundleArray(messages);
6072 }
6073 Parcelable[] histMessages = extras.getParcelableArray(EXTRA_HISTORIC_MESSAGES);
6074 if (histMessages != null && histMessages instanceof Parcelable[]) {
6075 mHistoricMessages = Message.getMessagesFromBundleArray(histMessages);
Alex Hillsfc737de2016-03-23 17:33:02 -04006076 }
6077 }
6078
6079 /**
6080 * @hide
6081 */
Adrian Roosc1a80b02016-04-05 14:54:55 -07006082 @Override
Selim Cinek7d1009b2017-01-25 15:28:28 -08006083 public RemoteViews makeContentView(boolean increasedHeight) {
6084 if (!increasedHeight) {
6085 Message m = findLatestIncomingMessage();
6086 CharSequence title = mConversationTitle != null
6087 ? mConversationTitle
6088 : (m == null) ? null : m.mSender;
6089 CharSequence text = (m == null)
6090 ? null
6091 : mConversationTitle != null ? makeMessageLine(m, mBuilder) : m.mText;
Alex Hillsfc737de2016-03-23 17:33:02 -04006092
Selim Cinek7d1009b2017-01-25 15:28:28 -08006093 return mBuilder.applyStandardTemplate(mBuilder.getBaseLayoutResource(),
6094 mBuilder.mParams.reset().hasProgress(false).title(title).text(text));
6095 } else {
Selim Cinek5d6ef8d2017-05-18 22:16:00 -07006096 mBuilder.mOriginalActions = mBuilder.mActions;
Selim Cinek7d1009b2017-01-25 15:28:28 -08006097 mBuilder.mActions = new ArrayList<>();
6098 RemoteViews remoteViews = makeBigContentView();
Selim Cinek5d6ef8d2017-05-18 22:16:00 -07006099 mBuilder.mActions = mBuilder.mOriginalActions;
6100 mBuilder.mOriginalActions = null;
Selim Cinek7d1009b2017-01-25 15:28:28 -08006101 return remoteViews;
6102 }
Adrian Roosc1a80b02016-04-05 14:54:55 -07006103 }
6104
6105 private Message findLatestIncomingMessage() {
6106 for (int i = mMessages.size() - 1; i >= 0; i--) {
6107 Message m = mMessages.get(i);
6108 // Incoming messages have a non-empty sender.
6109 if (!TextUtils.isEmpty(m.mSender)) {
6110 return m;
6111 }
6112 }
Adrian Roos33fbd2c2016-05-27 15:35:28 -07006113 if (!mMessages.isEmpty()) {
6114 // No incoming messages, fall back to outgoing message
6115 return mMessages.get(mMessages.size() - 1);
6116 }
Adrian Roosc1a80b02016-04-05 14:54:55 -07006117 return null;
6118 }
6119
6120 /**
6121 * @hide
6122 */
6123 @Override
6124 public RemoteViews makeBigContentView() {
6125 CharSequence title = !TextUtils.isEmpty(super.mBigContentTitle)
6126 ? super.mBigContentTitle
6127 : mConversationTitle;
6128 boolean hasTitle = !TextUtils.isEmpty(title);
6129
Adrian Roosfeafa052016-06-01 17:09:45 -07006130 if (mMessages.size() == 1) {
6131 // Special case for a single message: Use the big text style
6132 // so the collapsed and expanded versions match nicely.
6133 CharSequence bigTitle;
6134 CharSequence text;
6135 if (hasTitle) {
6136 bigTitle = title;
Selim Cinek7b9605b2017-01-19 17:36:00 -08006137 text = makeMessageLine(mMessages.get(0), mBuilder);
Adrian Roosfeafa052016-06-01 17:09:45 -07006138 } else {
6139 bigTitle = mMessages.get(0).mSender;
6140 text = mMessages.get(0).mText;
6141 }
Adrian Roosb1f427c2016-05-26 12:27:15 -07006142 RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(
6143 mBuilder.getBigTextLayoutResource(),
Adrian Roos70d7aa32017-01-11 15:39:06 -08006144 mBuilder.mParams.reset().hasProgress(false).title(bigTitle).text(null));
Adrian Roosb1f427c2016-05-26 12:27:15 -07006145 BigTextStyle.applyBigTextContentView(mBuilder, contentView, text);
6146 return contentView;
6147 }
6148
Adrian Roos48d746a2016-04-12 14:57:28 -07006149 RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(
Adrian Roosc1a80b02016-04-05 14:54:55 -07006150 mBuilder.getMessagingLayoutResource(),
Adrian Roos70d7aa32017-01-11 15:39:06 -08006151 mBuilder.mParams.reset().hasProgress(false).title(title).text(null));
Adrian Roosc1a80b02016-04-05 14:54:55 -07006152
6153 int[] rowIds = {R.id.inbox_text0, R.id.inbox_text1, R.id.inbox_text2, R.id.inbox_text3,
6154 R.id.inbox_text4, R.id.inbox_text5, R.id.inbox_text6};
6155
6156 // Make sure all rows are gone in case we reuse a view.
6157 for (int rowId : rowIds) {
6158 contentView.setViewVisibility(rowId, View.GONE);
6159 }
6160
6161 int i=0;
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006162 contentView.setViewLayoutMarginBottomDimen(R.id.line1,
6163 hasTitle ? R.dimen.notification_messaging_spacing : 0);
Adrian Roosc1a80b02016-04-05 14:54:55 -07006164 contentView.setInt(R.id.notification_messaging, "setNumIndentLines",
Selim Cinek279fa862016-06-14 10:57:25 -07006165 !mBuilder.mN.hasLargeIcon() ? 0 : (hasTitle ? 1 : 2));
Adrian Roosc1a80b02016-04-05 14:54:55 -07006166
Adrian Roosfeafa052016-06-01 17:09:45 -07006167 int contractedChildId = View.NO_ID;
6168 Message contractedMessage = findLatestIncomingMessage();
Adrian Roos437cd562017-01-18 15:47:03 -08006169 int firstHistoricMessage = Math.max(0, mHistoricMessages.size()
6170 - (rowIds.length - mMessages.size()));
6171 while (firstHistoricMessage + i < mHistoricMessages.size() && i < rowIds.length) {
6172 Message m = mHistoricMessages.get(firstHistoricMessage + i);
6173 int rowId = rowIds[i];
6174
Selim Cinek7b9605b2017-01-19 17:36:00 -08006175 contentView.setTextViewText(rowId, makeMessageLine(m, mBuilder));
Adrian Roos437cd562017-01-18 15:47:03 -08006176
6177 if (contractedMessage == m) {
6178 contractedChildId = rowId;
6179 }
6180
6181 i++;
6182 }
6183
Adrian Roosc1a80b02016-04-05 14:54:55 -07006184 int firstMessage = Math.max(0, mMessages.size() - rowIds.length);
6185 while (firstMessage + i < mMessages.size() && i < rowIds.length) {
6186 Message m = mMessages.get(firstMessage + i);
6187 int rowId = rowIds[i];
6188
6189 contentView.setViewVisibility(rowId, View.VISIBLE);
Selim Cinek7b9605b2017-01-19 17:36:00 -08006190 contentView.setTextViewText(rowId, makeMessageLine(m, mBuilder));
6191 mBuilder.setTextViewColorSecondary(contentView, rowId);
Adrian Roosc1a80b02016-04-05 14:54:55 -07006192
Adrian Roosfeafa052016-06-01 17:09:45 -07006193 if (contractedMessage == m) {
6194 contractedChildId = rowId;
6195 }
6196
Adrian Roosc1a80b02016-04-05 14:54:55 -07006197 i++;
6198 }
Adrian Roos437cd562017-01-18 15:47:03 -08006199 // Clear the remaining views for reapply. Ensures that historic message views can
6200 // reliably be identified as being GONE and having non-null text.
6201 while (i < rowIds.length) {
6202 int rowId = rowIds[i];
6203 contentView.setTextViewText(rowId, null);
6204 i++;
6205 }
6206
Adrian Roosfeafa052016-06-01 17:09:45 -07006207 // Record this here to allow transformation between the contracted and expanded views.
6208 contentView.setInt(R.id.notification_messaging, "setContractedChildId",
6209 contractedChildId);
Alex Hillsfc737de2016-03-23 17:33:02 -04006210 return contentView;
6211 }
6212
Selim Cinek7b9605b2017-01-19 17:36:00 -08006213 private CharSequence makeMessageLine(Message m, Builder builder) {
Adrian Roosc1a80b02016-04-05 14:54:55 -07006214 BidiFormatter bidi = BidiFormatter.getInstance();
6215 SpannableStringBuilder sb = new SpannableStringBuilder();
Selim Cinek7b9605b2017-01-19 17:36:00 -08006216 boolean colorize = builder.isColorized();
Anthony Chenad4d1582017-04-10 16:07:58 -07006217 TextAppearanceSpan colorSpan;
6218 CharSequence messageName;
Adrian Roosc1a80b02016-04-05 14:54:55 -07006219 if (TextUtils.isEmpty(m.mSender)) {
6220 CharSequence replyName = mUserDisplayName == null ? "" : mUserDisplayName;
6221 sb.append(bidi.unicodeWrap(replyName),
Selim Cinek7b9605b2017-01-19 17:36:00 -08006222 makeFontColorSpan(colorize
6223 ? builder.getPrimaryTextColor()
6224 : mBuilder.resolveContrastColor()),
Adrian Roosc1a80b02016-04-05 14:54:55 -07006225 0 /* flags */);
6226 } else {
6227 sb.append(bidi.unicodeWrap(m.mSender),
Selim Cinek7b9605b2017-01-19 17:36:00 -08006228 makeFontColorSpan(colorize
6229 ? builder.getPrimaryTextColor()
6230 : Color.BLACK),
Adrian Roosc1a80b02016-04-05 14:54:55 -07006231 0 /* flags */);
6232 }
6233 CharSequence text = m.mText == null ? "" : m.mText;
6234 sb.append(" ").append(bidi.unicodeWrap(text));
6235 return sb;
6236 }
6237
Adrian Roosdedd1df2016-04-26 16:38:47 -07006238 /**
6239 * @hide
6240 */
6241 @Override
Selim Cinek87ed69b2017-02-09 15:59:43 -08006242 public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
6243 if (increasedHeight) {
6244 return makeBigContentView();
6245 }
Adrian Roosdedd1df2016-04-26 16:38:47 -07006246 Message m = findLatestIncomingMessage();
6247 CharSequence title = mConversationTitle != null
6248 ? mConversationTitle
6249 : (m == null) ? null : m.mSender;
6250 CharSequence text = (m == null)
6251 ? null
Selim Cinek7b9605b2017-01-19 17:36:00 -08006252 : mConversationTitle != null ? makeMessageLine(m, mBuilder) : m.mText;
Adrian Roosdedd1df2016-04-26 16:38:47 -07006253
6254 return mBuilder.applyStandardTemplateWithActions(mBuilder.getBigBaseLayoutResource(),
Adrian Roos70d7aa32017-01-11 15:39:06 -08006255 mBuilder.mParams.reset().hasProgress(false).title(title).text(text));
Adrian Roosdedd1df2016-04-26 16:38:47 -07006256 }
6257
Adrian Roosc1a80b02016-04-05 14:54:55 -07006258 private static TextAppearanceSpan makeFontColorSpan(int color) {
6259 return new TextAppearanceSpan(null, 0, 0,
6260 ColorStateList.valueOf(color), null);
6261 }
6262
Alex Hillsd9b04d92016-04-11 16:38:16 -04006263 public static final class Message {
6264
6265 static final String KEY_TEXT = "text";
6266 static final String KEY_TIMESTAMP = "time";
6267 static final String KEY_SENDER = "sender";
6268 static final String KEY_DATA_MIME_TYPE = "type";
6269 static final String KEY_DATA_URI= "uri";
Shane Brennan5a871862017-03-11 13:14:17 -08006270 static final String KEY_EXTRAS_BUNDLE = "extras";
Alex Hillsfc737de2016-03-23 17:33:02 -04006271
6272 private final CharSequence mText;
6273 private final long mTimestamp;
6274 private final CharSequence mSender;
6275
Shane Brennan5a871862017-03-11 13:14:17 -08006276 private Bundle mExtras = new Bundle();
Alex Hillsfc737de2016-03-23 17:33:02 -04006277 private String mDataMimeType;
6278 private Uri mDataUri;
6279
6280 /**
6281 * Constructor
6282 * @param text A {@link CharSequence} to be displayed as the message content
6283 * @param timestamp Time at which the message arrived
6284 * @param sender A {@link CharSequence} to be used for displaying the name of the
6285 * sender. Should be <code>null</code> for messages by the current user, in which case
6286 * the platform will insert {@link MessagingStyle#getUserDisplayName()}.
6287 * Should be unique amongst all individuals in the conversation, and should be
6288 * consistent during re-posts of the notification.
6289 */
6290 public Message(CharSequence text, long timestamp, CharSequence sender){
6291 mText = text;
6292 mTimestamp = timestamp;
6293 mSender = sender;
6294 }
6295
6296 /**
6297 * Sets a binary blob of data and an associated MIME type for a message. In the case
6298 * where the platform doesn't support the MIME type, the original text provided in the
6299 * constructor will be used.
6300 * @param dataMimeType The MIME type of the content. See
6301 * <a href="{@docRoot}notifications/messaging.html"> for the list of supported MIME
6302 * types on Android and Android Wear.
6303 * @param dataUri The uri containing the content whose type is given by the MIME type.
6304 * <p class="note">
6305 * <ol>
6306 * <li>Notification Listeners including the System UI need permission to access the
6307 * data the Uri points to. The recommended ways to do this are:</li>
6308 * <li>Store the data in your own ContentProvider, making sure that other apps have
6309 * the correct permission to access your provider. The preferred mechanism for
6310 * providing access is to use per-URI permissions which are temporary and only
6311 * grant access to the receiving application. An easy way to create a
6312 * ContentProvider like this is to use the FileProvider helper class.</li>
6313 * <li>Use the system MediaStore. The MediaStore is primarily aimed at video, audio
6314 * and image MIME types, however beginning with Android 3.0 (API level 11) it can
6315 * also store non-media types (see MediaStore.Files for more info). Files can be
6316 * inserted into the MediaStore using scanFile() after which a content:// style
6317 * Uri suitable for sharing is passed to the provided onScanCompleted() callback.
6318 * Note that once added to the system MediaStore the content is accessible to any
6319 * app on the device.</li>
6320 * </ol>
6321 * @return this object for method chaining
6322 */
6323 public Message setData(String dataMimeType, Uri dataUri) {
6324 mDataMimeType = dataMimeType;
6325 mDataUri = dataUri;
6326 return this;
6327 }
6328
Alex Hillsfc737de2016-03-23 17:33:02 -04006329 /**
6330 * Get the text to be used for this message, or the fallback text if a type and content
6331 * Uri have been set
6332 */
6333 public CharSequence getText() {
6334 return mText;
6335 }
6336
6337 /**
6338 * Get the time at which this message arrived
6339 */
6340 public long getTimestamp() {
6341 return mTimestamp;
6342 }
6343
6344 /**
Shane Brennan5a871862017-03-11 13:14:17 -08006345 * Get the extras Bundle for this message.
6346 */
6347 public Bundle getExtras() {
6348 return mExtras;
6349 }
6350
6351 /**
Alex Hillsfc737de2016-03-23 17:33:02 -04006352 * Get the text used to display the contact's name in the messaging experience
6353 */
6354 public CharSequence getSender() {
6355 return mSender;
6356 }
6357
6358 /**
6359 * Get the MIME type of the data pointed to by the Uri
6360 */
6361 public String getDataMimeType() {
6362 return mDataMimeType;
6363 }
6364
6365 /**
6366 * Get the the Uri pointing to the content of the message. Can be null, in which case
6367 * {@see #getText()} is used.
6368 */
6369 public Uri getDataUri() {
6370 return mDataUri;
6371 }
6372
Alex Hillsd9b04d92016-04-11 16:38:16 -04006373 private Bundle toBundle() {
6374 Bundle bundle = new Bundle();
Alex Hillsfc737de2016-03-23 17:33:02 -04006375 if (mText != null) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04006376 bundle.putCharSequence(KEY_TEXT, mText);
Alex Hillsfc737de2016-03-23 17:33:02 -04006377 }
Alex Hillsd9b04d92016-04-11 16:38:16 -04006378 bundle.putLong(KEY_TIMESTAMP, mTimestamp);
Alex Hillsfc737de2016-03-23 17:33:02 -04006379 if (mSender != null) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04006380 bundle.putCharSequence(KEY_SENDER, mSender);
Alex Hillsfc737de2016-03-23 17:33:02 -04006381 }
6382 if (mDataMimeType != null) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04006383 bundle.putString(KEY_DATA_MIME_TYPE, mDataMimeType);
Alex Hillsfc737de2016-03-23 17:33:02 -04006384 }
6385 if (mDataUri != null) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04006386 bundle.putParcelable(KEY_DATA_URI, mDataUri);
Alex Hillsfc737de2016-03-23 17:33:02 -04006387 }
Shane Brennan5a871862017-03-11 13:14:17 -08006388 if (mExtras != null) {
6389 bundle.putBundle(KEY_EXTRAS_BUNDLE, mExtras);
6390 }
Alex Hillsd9b04d92016-04-11 16:38:16 -04006391 return bundle;
Alex Hillsfc737de2016-03-23 17:33:02 -04006392 }
6393
Alex Hillsd9b04d92016-04-11 16:38:16 -04006394 static Bundle[] getBundleArrayForMessages(List<Message> messages) {
6395 Bundle[] bundles = new Bundle[messages.size()];
6396 final int N = messages.size();
6397 for (int i = 0; i < N; i++) {
6398 bundles[i] = messages.get(i).toBundle();
6399 }
6400 return bundles;
6401 }
6402
Adrian Roosdedd1df2016-04-26 16:38:47 -07006403 static List<Message> getMessagesFromBundleArray(Parcelable[] bundles) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04006404 List<Message> messages = new ArrayList<>(bundles.length);
6405 for (int i = 0; i < bundles.length; i++) {
Adrian Roosdedd1df2016-04-26 16:38:47 -07006406 if (bundles[i] instanceof Bundle) {
6407 Message message = getMessageFromBundle((Bundle)bundles[i]);
6408 if (message != null) {
6409 messages.add(message);
6410 }
Alex Hillsd9b04d92016-04-11 16:38:16 -04006411 }
6412 }
6413 return messages;
6414 }
6415
6416 static Message getMessageFromBundle(Bundle bundle) {
6417 try {
Adrian Roosfbddd2c2016-05-13 12:57:20 -07006418 if (!bundle.containsKey(KEY_TEXT) || !bundle.containsKey(KEY_TIMESTAMP)) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04006419 return null;
6420 } else {
6421 Message message = new Message(bundle.getCharSequence(KEY_TEXT),
6422 bundle.getLong(KEY_TIMESTAMP), bundle.getCharSequence(KEY_SENDER));
6423 if (bundle.containsKey(KEY_DATA_MIME_TYPE) &&
6424 bundle.containsKey(KEY_DATA_URI)) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04006425 message.setData(bundle.getString(KEY_DATA_MIME_TYPE),
6426 (Uri) bundle.getParcelable(KEY_DATA_URI));
Alex Hillsfc737de2016-03-23 17:33:02 -04006427 }
Shane Brennan5a871862017-03-11 13:14:17 -08006428 if (bundle.containsKey(KEY_EXTRAS_BUNDLE)) {
6429 message.getExtras().putAll(bundle.getBundle(KEY_EXTRAS_BUNDLE));
6430 }
Alex Hillsd9b04d92016-04-11 16:38:16 -04006431 return message;
6432 }
6433 } catch (ClassCastException e) {
6434 return null;
6435 }
6436 }
Alex Hillsfc737de2016-03-23 17:33:02 -04006437 }
6438 }
6439
6440 /**
Daniel Sandler879c5e02012-04-17 16:46:51 -04006441 * Helper class for generating large-format notifications that include a list of (up to 5) strings.
Joe Malin8d40d042012-11-05 11:36:40 -08006442 *
Robert Ly91c5ce32014-06-08 15:37:00 -07006443 * Here's how you'd set the <code>InboxStyle</code> on a notification:
Daniel Sandler879c5e02012-04-17 16:46:51 -04006444 * <pre class="prettyprint">
Robert Ly91c5ce32014-06-08 15:37:00 -07006445 * Notification notif = new Notification.Builder(mContext)
6446 * .setContentTitle(&quot;5 New mails from &quot; + sender.toString())
6447 * .setContentText(subject)
6448 * .setSmallIcon(R.drawable.new_mail)
6449 * .setLargeIcon(aBitmap)
6450 * .setStyle(new Notification.InboxStyle()
6451 * .addLine(str1)
6452 * .addLine(str2)
6453 * .setContentTitle(&quot;&quot;)
6454 * .setSummaryText(&quot;+3 more&quot;))
6455 * .build();
Daniel Sandler879c5e02012-04-17 16:46:51 -04006456 * </pre>
Joe Malin8d40d042012-11-05 11:36:40 -08006457 *
Daniel Sandler879c5e02012-04-17 16:46:51 -04006458 * @see Notification#bigContentView
6459 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04006460 public static class InboxStyle extends Style {
Daniel Sandler879c5e02012-04-17 16:46:51 -04006461 private ArrayList<CharSequence> mTexts = new ArrayList<CharSequence>(5);
6462
Chris Wrenfbd96ba2012-05-01 12:03:58 -04006463 public InboxStyle() {
6464 }
6465
Adrian Roosf5faf9d2016-05-23 13:56:15 -07006466 /**
6467 * @deprecated use {@code InboxStyle()}.
6468 */
6469 @Deprecated
Daniel Sandler879c5e02012-04-17 16:46:51 -04006470 public InboxStyle(Builder builder) {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04006471 setBuilder(builder);
Daniel Sandler879c5e02012-04-17 16:46:51 -04006472 }
6473
Chris Wrend6297db2012-05-03 16:20:13 -04006474 /**
6475 * Overrides ContentTitle in the big form of the template.
6476 * This defaults to the value passed to setContentTitle().
6477 */
6478 public InboxStyle setBigContentTitle(CharSequence title) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04006479 internalSetBigContentTitle(safeCharSequence(title));
Chris Wrend6297db2012-05-03 16:20:13 -04006480 return this;
6481 }
6482
6483 /**
6484 * Set the first line of text after the detail section in the big form of the template.
6485 */
6486 public InboxStyle setSummaryText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04006487 internalSetSummaryText(safeCharSequence(cs));
Chris Wrend6297db2012-05-03 16:20:13 -04006488 return this;
6489 }
6490
Chris Wren0bd664d2012-08-01 13:56:56 -04006491 /**
6492 * Append a line to the digest section of the Inbox notification.
6493 */
Daniel Sandler879c5e02012-04-17 16:46:51 -04006494 public InboxStyle addLine(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04006495 mTexts.add(safeCharSequence(cs));
Daniel Sandler879c5e02012-04-17 16:46:51 -04006496 return this;
6497 }
6498
Daniel Sandlerf45564e2013-04-15 15:05:08 -04006499 /**
6500 * @hide
6501 */
6502 public void addExtras(Bundle extras) {
6503 super.addExtras(extras);
Christoph Studer4600f9b2014-07-22 22:44:43 +02006504
Daniel Sandlerf45564e2013-04-15 15:05:08 -04006505 CharSequence[] a = new CharSequence[mTexts.size()];
6506 extras.putCharSequenceArray(EXTRA_TEXT_LINES, mTexts.toArray(a));
6507 }
6508
Christoph Studer4600f9b2014-07-22 22:44:43 +02006509 /**
6510 * @hide
6511 */
6512 @Override
6513 protected void restoreFromExtras(Bundle extras) {
6514 super.restoreFromExtras(extras);
6515
6516 mTexts.clear();
6517 if (extras.containsKey(EXTRA_TEXT_LINES)) {
6518 Collections.addAll(mTexts, extras.getCharSequenceArray(EXTRA_TEXT_LINES));
6519 }
6520 }
6521
Julia Reynoldsd9228f12015-10-20 10:37:27 -04006522 /**
6523 * @hide
6524 */
6525 public RemoteViews makeBigContentView() {
Selim Cinekc848c3a2016-01-13 15:27:30 -08006526 // Remove the content text so it disappears unless you have a summary
Christoph Studer4600f9b2014-07-22 22:44:43 +02006527 // Nasty
Julia Reynoldsd9228f12015-10-20 10:37:27 -04006528 CharSequence oldBuilderContentText = mBuilder.mN.extras.getCharSequence(EXTRA_TEXT);
6529 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, null);
Christoph Studer4600f9b2014-07-22 22:44:43 +02006530
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01006531 RemoteViews contentView = getStandardView(mBuilder.getInboxLayoutResource());
Daniel Sandler619738c2012-06-07 16:33:08 -04006532
Julia Reynoldsd9228f12015-10-20 10:37:27 -04006533 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, oldBuilderContentText);
Christoph Studer4600f9b2014-07-22 22:44:43 +02006534
Chris Wrend6297db2012-05-03 16:20:13 -04006535 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 -04006536 R.id.inbox_text4, R.id.inbox_text5, R.id.inbox_text6};
Chris Wrend6297db2012-05-03 16:20:13 -04006537
Chris Wren4ed80d52012-05-17 09:30:03 -04006538 // Make sure all rows are gone in case we reuse a view.
6539 for (int rowId : rowIds) {
6540 contentView.setViewVisibility(rowId, View.GONE);
6541 }
6542
Daniel Sandler879c5e02012-04-17 16:46:51 -04006543 int i=0;
Selim Cinek07c80172016-04-21 16:40:47 -07006544 int topPadding = mBuilder.mContext.getResources().getDimensionPixelSize(
6545 R.dimen.notification_inbox_item_top_padding);
Selim Cinek247fa012016-02-18 09:50:48 -08006546 boolean first = true;
Selim Cinek07c80172016-04-21 16:40:47 -07006547 int onlyViewId = 0;
6548 int maxRows = rowIds.length;
6549 if (mBuilder.mActions.size() > 0) {
6550 maxRows--;
6551 }
6552 while (i < mTexts.size() && i < maxRows) {
Daniel Sandler879c5e02012-04-17 16:46:51 -04006553 CharSequence str = mTexts.get(i);
Selim Cinek07c80172016-04-21 16:40:47 -07006554 if (!TextUtils.isEmpty(str)) {
Daniel Sandler879c5e02012-04-17 16:46:51 -04006555 contentView.setViewVisibility(rowIds[i], View.VISIBLE);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01006556 contentView.setTextViewText(rowIds[i], mBuilder.processLegacyText(str));
Selim Cinek7b9605b2017-01-19 17:36:00 -08006557 mBuilder.setTextViewColorSecondary(contentView, rowIds[i]);
Selim Cinek07c80172016-04-21 16:40:47 -07006558 contentView.setViewPadding(rowIds[i], 0, topPadding, 0, 0);
Selim Cinek247fa012016-02-18 09:50:48 -08006559 handleInboxImageMargin(contentView, rowIds[i], first);
Selim Cinek07c80172016-04-21 16:40:47 -07006560 if (first) {
6561 onlyViewId = rowIds[i];
6562 } else {
6563 onlyViewId = 0;
6564 }
Selim Cinek247fa012016-02-18 09:50:48 -08006565 first = false;
Daniel Sandler879c5e02012-04-17 16:46:51 -04006566 }
6567 i++;
6568 }
Selim Cinek07c80172016-04-21 16:40:47 -07006569 if (onlyViewId != 0) {
6570 // We only have 1 entry, lets make it look like the normal Text of a Bigtext
6571 topPadding = mBuilder.mContext.getResources().getDimensionPixelSize(
6572 R.dimen.notification_text_margin_top);
6573 contentView.setViewPadding(onlyViewId, 0, topPadding, 0, 0);
6574 }
Selim Cinek1e0bf612015-11-20 15:57:26 -08006575
Daniel Sandler879c5e02012-04-17 16:46:51 -04006576 return contentView;
6577 }
Selim Cinek1e0bf612015-11-20 15:57:26 -08006578
Selim Cinek247fa012016-02-18 09:50:48 -08006579 private void handleInboxImageMargin(RemoteViews contentView, int id, boolean first) {
Selim Cinek1e0bf612015-11-20 15:57:26 -08006580 int endMargin = 0;
Selim Cinek247fa012016-02-18 09:50:48 -08006581 if (first) {
6582 final int max = mBuilder.mN.extras.getInt(EXTRA_PROGRESS_MAX, 0);
6583 final boolean ind = mBuilder.mN.extras.getBoolean(EXTRA_PROGRESS_INDETERMINATE);
6584 boolean hasProgress = max != 0 || ind;
Selim Cinek279fa862016-06-14 10:57:25 -07006585 if (mBuilder.mN.hasLargeIcon() && !hasProgress) {
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006586 endMargin = R.dimen.notification_content_picture_margin;
Selim Cinek247fa012016-02-18 09:50:48 -08006587 }
Selim Cinek1e0bf612015-11-20 15:57:26 -08006588 }
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006589 contentView.setViewLayoutMarginEndDimen(id, endMargin);
Selim Cinek1e0bf612015-11-20 15:57:26 -08006590 }
Daniel Sandler879c5e02012-04-17 16:46:51 -04006591 }
Dan Sandler842dd772014-05-15 09:36:47 -04006592
6593 /**
6594 * Notification style for media playback notifications.
6595 *
6596 * In the expanded form, {@link Notification#bigContentView}, up to 5
6597 * {@link Notification.Action}s specified with
Dan Sandler86647982015-05-13 23:41:13 -04006598 * {@link Notification.Builder#addAction(Action) addAction} will be
Dan Sandler842dd772014-05-15 09:36:47 -04006599 * shown as icon-only pushbuttons, suitable for transport controls. The Bitmap given to
6600 * {@link Notification.Builder#setLargeIcon(android.graphics.Bitmap) setLargeIcon()} will be
6601 * treated as album artwork.
Selim Cinek99104832017-01-25 14:47:33 -08006602 * <p>
Dan Sandler842dd772014-05-15 09:36:47 -04006603 * Unlike the other styles provided here, MediaStyle can also modify the standard-size
6604 * {@link Notification#contentView}; by providing action indices to
Christoph Studerfde6f4d2014-12-12 13:23:26 +01006605 * {@link #setShowActionsInCompactView(int...)} you can promote up to 3 actions to be displayed
Dan Sandler842dd772014-05-15 09:36:47 -04006606 * in the standard view alongside the usual content.
Selim Cinek99104832017-01-25 14:47:33 -08006607 * <p>
Bryan Mawhinney6be8de32014-07-18 10:35:12 +01006608 * Notifications created with MediaStyle will have their category set to
6609 * {@link Notification#CATEGORY_TRANSPORT CATEGORY_TRANSPORT} unless you set a different
6610 * category using {@link Notification.Builder#setCategory(String) setCategory()}.
Selim Cinek99104832017-01-25 14:47:33 -08006611 * <p>
Jeff Browndba34ba2014-06-24 20:46:03 -07006612 * Finally, if you attach a {@link android.media.session.MediaSession.Token} using
6613 * {@link android.app.Notification.MediaStyle#setMediaSession(MediaSession.Token)},
Dan Sandler842dd772014-05-15 09:36:47 -04006614 * the System UI can identify this as a notification representing an active media session
6615 * and respond accordingly (by showing album artwork in the lockscreen, for example).
6616 *
Selim Cinek99104832017-01-25 14:47:33 -08006617 * <p>
6618 * Starting at {@link android.os.Build.VERSION_CODES#O Android O} any notification that has a
6619 * media session attached with {@link #setMediaSession(MediaSession.Token)} will be colorized.
6620 * You can opt-out of this behavior by using {@link Notification.Builder#setColorized(boolean)}.
6621 * <p>
6622 *
Dan Sandler842dd772014-05-15 09:36:47 -04006623 * To use this style with your Notification, feed it to
6624 * {@link Notification.Builder#setStyle(android.app.Notification.Style)} like so:
6625 * <pre class="prettyprint">
6626 * Notification noti = new Notification.Builder()
6627 * .setSmallIcon(R.drawable.ic_stat_player)
Christoph Studere935fe92014-11-24 14:18:06 +01006628 * .setContentTitle(&quot;Track title&quot;)
6629 * .setContentText(&quot;Artist - Album&quot;)
6630 * .setLargeIcon(albumArtBitmap))
Bryan Mawhinney6be8de32014-07-18 10:35:12 +01006631 * .setStyle(<b>new Notification.MediaStyle()</b>
6632 * .setMediaSession(mySession))
Dan Sandler842dd772014-05-15 09:36:47 -04006633 * .build();
6634 * </pre>
6635 *
6636 * @see Notification#bigContentView
Selim Cinek99104832017-01-25 14:47:33 -08006637 * @see Notification.Builder#setColorized(boolean)
Dan Sandler842dd772014-05-15 09:36:47 -04006638 */
6639 public static class MediaStyle extends Style {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006640 static final int MAX_MEDIA_BUTTONS_IN_COMPACT = 3;
Dan Sandler842dd772014-05-15 09:36:47 -04006641 static final int MAX_MEDIA_BUTTONS = 5;
6642
6643 private int[] mActionsToShowInCompact = null;
Jeff Browndba34ba2014-06-24 20:46:03 -07006644 private MediaSession.Token mToken;
Dan Sandler842dd772014-05-15 09:36:47 -04006645
6646 public MediaStyle() {
6647 }
6648
Adrian Roosf5faf9d2016-05-23 13:56:15 -07006649 /**
6650 * @deprecated use {@code MediaStyle()}.
6651 */
6652 @Deprecated
Dan Sandler842dd772014-05-15 09:36:47 -04006653 public MediaStyle(Builder builder) {
6654 setBuilder(builder);
6655 }
6656
6657 /**
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006658 * 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 -04006659 * notification view.
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006660 *
6661 * @param actions the indices of the actions to show in the compact notification view
Dan Sandler842dd772014-05-15 09:36:47 -04006662 */
6663 public MediaStyle setShowActionsInCompactView(int...actions) {
6664 mActionsToShowInCompact = actions;
6665 return this;
6666 }
6667
6668 /**
Jeff Browndba34ba2014-06-24 20:46:03 -07006669 * Attach a {@link android.media.session.MediaSession.Token} to this Notification
6670 * to provide additional playback information and control to the SystemUI.
Dan Sandler842dd772014-05-15 09:36:47 -04006671 */
Jeff Browndba34ba2014-06-24 20:46:03 -07006672 public MediaStyle setMediaSession(MediaSession.Token token) {
Dan Sandler842dd772014-05-15 09:36:47 -04006673 mToken = token;
6674 return this;
6675 }
6676
Christoph Studer4600f9b2014-07-22 22:44:43 +02006677 /**
6678 * @hide
6679 */
Dan Sandler842dd772014-05-15 09:36:47 -04006680 @Override
6681 public Notification buildStyled(Notification wip) {
Christoph Studer4600f9b2014-07-22 22:44:43 +02006682 super.buildStyled(wip);
Bryan Mawhinney6be8de32014-07-18 10:35:12 +01006683 if (wip.category == null) {
6684 wip.category = Notification.CATEGORY_TRANSPORT;
6685 }
Dan Sandler842dd772014-05-15 09:36:47 -04006686 return wip;
6687 }
6688
Christoph Studer4600f9b2014-07-22 22:44:43 +02006689 /**
6690 * @hide
6691 */
6692 @Override
Selim Cinek7d1009b2017-01-25 15:28:28 -08006693 public RemoteViews makeContentView(boolean increasedHeight) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04006694 return makeMediaContentView();
Christoph Studer4600f9b2014-07-22 22:44:43 +02006695 }
6696
6697 /**
6698 * @hide
6699 */
6700 @Override
Julia Reynoldsd9228f12015-10-20 10:37:27 -04006701 public RemoteViews makeBigContentView() {
6702 return makeMediaBigContentView();
Christoph Studer4600f9b2014-07-22 22:44:43 +02006703 }
6704
Selim Cinekcc10bfb2016-02-10 16:24:21 -08006705 /**
6706 * @hide
6707 */
6708 @Override
Selim Cinek87ed69b2017-02-09 15:59:43 -08006709 public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
Selim Cinekcc10bfb2016-02-10 16:24:21 -08006710 RemoteViews expanded = makeMediaBigContentView();
6711 return expanded != null ? expanded : makeMediaContentView();
6712 }
6713
Dan Sandler842dd772014-05-15 09:36:47 -04006714 /** @hide */
6715 @Override
6716 public void addExtras(Bundle extras) {
6717 super.addExtras(extras);
6718
6719 if (mToken != null) {
6720 extras.putParcelable(EXTRA_MEDIA_SESSION, mToken);
6721 }
Bryan Mawhinneye191f902014-07-22 12:50:09 +01006722 if (mActionsToShowInCompact != null) {
6723 extras.putIntArray(EXTRA_COMPACT_ACTIONS, mActionsToShowInCompact);
6724 }
Dan Sandler842dd772014-05-15 09:36:47 -04006725 }
6726
Christoph Studer4600f9b2014-07-22 22:44:43 +02006727 /**
6728 * @hide
6729 */
6730 @Override
6731 protected void restoreFromExtras(Bundle extras) {
6732 super.restoreFromExtras(extras);
6733
6734 if (extras.containsKey(EXTRA_MEDIA_SESSION)) {
6735 mToken = extras.getParcelable(EXTRA_MEDIA_SESSION);
6736 }
6737 if (extras.containsKey(EXTRA_COMPACT_ACTIONS)) {
6738 mActionsToShowInCompact = extras.getIntArray(EXTRA_COMPACT_ACTIONS);
6739 }
6740 }
6741
Selim Cinek5bf069a2015-11-10 19:14:27 -05006742 private RemoteViews generateMediaActionButton(Action action, int color) {
Dan Sandler842dd772014-05-15 09:36:47 -04006743 final boolean tombstone = (action.actionIntent == null);
Selim Cinekf33b1112015-07-15 17:45:11 -07006744 RemoteViews button = new BuilderRemoteViews(mBuilder.mContext.getApplicationInfo(),
Alan Viverette3cb07a462014-06-06 14:19:53 -07006745 R.layout.notification_material_media_action);
Dan Sandler68079d52015-07-22 10:45:30 -04006746 button.setImageViewIcon(R.id.action0, action.getIcon());
Anthony Chenad4d1582017-04-10 16:07:58 -07006747
6748 // If the action buttons should not be tinted, then just use the default
6749 // notification color. Otherwise, just use the passed-in color.
6750 int tintColor = mBuilder.shouldTintActionButtons() || mBuilder.isColorized()
6751 ? color
6752 : NotificationColorUtil.resolveColor(mBuilder.mContext,
6753 Notification.COLOR_DEFAULT);
6754
6755 button.setDrawableParameters(R.id.action0, false, -1, tintColor,
6756 PorterDuff.Mode.SRC_ATOP, -1);
Dan Sandler842dd772014-05-15 09:36:47 -04006757 if (!tombstone) {
6758 button.setOnClickPendingIntent(R.id.action0, action.actionIntent);
6759 }
6760 button.setContentDescription(R.id.action0, action.title);
6761 return button;
6762 }
6763
6764 private RemoteViews makeMediaContentView() {
6765 RemoteViews view = mBuilder.applyStandardTemplate(
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006766 R.layout.notification_template_material_media, false /* hasProgress */);
Dan Sandler842dd772014-05-15 09:36:47 -04006767
6768 final int numActions = mBuilder.mActions.size();
6769 final int N = mActionsToShowInCompact == null
6770 ? 0
6771 : Math.min(mActionsToShowInCompact.length, MAX_MEDIA_BUTTONS_IN_COMPACT);
6772 if (N > 0) {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006773 view.removeAllViews(com.android.internal.R.id.media_actions);
Dan Sandler842dd772014-05-15 09:36:47 -04006774 for (int i = 0; i < N; i++) {
6775 if (i >= numActions) {
6776 throw new IllegalArgumentException(String.format(
6777 "setShowActionsInCompactView: action %d out of bounds (max %d)",
6778 i, numActions - 1));
6779 }
6780
6781 final Action action = mBuilder.mActions.get(mActionsToShowInCompact[i]);
Selim Cinek5bf069a2015-11-10 19:14:27 -05006782 final RemoteViews button = generateMediaActionButton(action,
Selim Cinek99104832017-01-25 14:47:33 -08006783 getPrimaryHighlightColor());
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006784 view.addView(com.android.internal.R.id.media_actions, button);
Dan Sandler842dd772014-05-15 09:36:47 -04006785 }
6786 }
Selim Cinekfdc738f2016-01-27 20:04:27 -08006787 handleImage(view);
6788 // handle the content margin
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006789 int endMargin = R.dimen.notification_content_margin_end;
Selim Cinek279fa862016-06-14 10:57:25 -07006790 if (mBuilder.mN.hasLargeIcon()) {
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006791 endMargin = R.dimen.notification_content_plus_picture_margin_end;
Selim Cinekfdc738f2016-01-27 20:04:27 -08006792 }
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006793 view.setViewLayoutMarginEndDimen(R.id.notification_main_column, endMargin);
Dan Sandler842dd772014-05-15 09:36:47 -04006794 return view;
6795 }
6796
Selim Cinek99104832017-01-25 14:47:33 -08006797 private int getPrimaryHighlightColor() {
6798 return mBuilder.getPrimaryHighlightColor();
6799 }
6800
Dan Sandler842dd772014-05-15 09:36:47 -04006801 private RemoteViews makeMediaBigContentView() {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006802 final int actionCount = Math.min(mBuilder.mActions.size(), MAX_MEDIA_BUTTONS);
Selim Cinekcc10bfb2016-02-10 16:24:21 -08006803 // Dont add an expanded view if there is no more content to be revealed
6804 int actionsInCompact = mActionsToShowInCompact == null
6805 ? 0
6806 : Math.min(mActionsToShowInCompact.length, MAX_MEDIA_BUTTONS_IN_COMPACT);
Selim Cinek279fa862016-06-14 10:57:25 -07006807 if (!mBuilder.mN.hasLargeIcon() && actionCount <= actionsInCompact) {
Selim Cinekcc10bfb2016-02-10 16:24:21 -08006808 return null;
6809 }
Selim Cinek5bf069a2015-11-10 19:14:27 -05006810 RemoteViews big = mBuilder.applyStandardTemplate(
6811 R.layout.notification_template_material_big_media,
6812 false);
Dan Sandler842dd772014-05-15 09:36:47 -04006813
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006814 if (actionCount > 0) {
6815 big.removeAllViews(com.android.internal.R.id.media_actions);
6816 for (int i = 0; i < actionCount; i++) {
Selim Cinek5bf069a2015-11-10 19:14:27 -05006817 final RemoteViews button = generateMediaActionButton(mBuilder.mActions.get(i),
Selim Cinek99104832017-01-25 14:47:33 -08006818 getPrimaryHighlightColor());
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006819 big.addView(com.android.internal.R.id.media_actions, button);
Dan Sandler842dd772014-05-15 09:36:47 -04006820 }
6821 }
Selim Cinek5bf069a2015-11-10 19:14:27 -05006822 handleImage(big);
Dan Sandler842dd772014-05-15 09:36:47 -04006823 return big;
6824 }
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006825
Selim Cinek5bf069a2015-11-10 19:14:27 -05006826 private void handleImage(RemoteViews contentView) {
Selim Cinek279fa862016-06-14 10:57:25 -07006827 if (mBuilder.mN.hasLargeIcon()) {
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006828 contentView.setViewLayoutMarginEndDimen(R.id.line1, 0);
6829 contentView.setViewLayoutMarginEndDimen(R.id.text, 0);
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006830 }
6831 }
6832
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006833 /**
6834 * @hide
6835 */
6836 @Override
6837 protected boolean hasProgress() {
6838 return false;
6839 }
Dan Sandler842dd772014-05-15 09:36:47 -04006840 }
Griff Hazen61a9e862014-05-22 16:05:19 -07006841
Selim Cinek593610c2016-02-16 18:42:57 -08006842 /**
6843 * Notification style for custom views that are decorated by the system
6844 *
6845 * <p>Instead of providing a notification that is completely custom, a developer can set this
6846 * style and still obtain system decorations like the notification header with the expand
6847 * affordance and actions.
6848 *
6849 * <p>Use {@link android.app.Notification.Builder#setCustomContentView(RemoteViews)},
6850 * {@link android.app.Notification.Builder#setCustomBigContentView(RemoteViews)} and
6851 * {@link android.app.Notification.Builder#setCustomHeadsUpContentView(RemoteViews)} to set the
6852 * corresponding custom views to display.
6853 *
6854 * To use this style with your Notification, feed it to
6855 * {@link Notification.Builder#setStyle(android.app.Notification.Style)} like so:
6856 * <pre class="prettyprint">
6857 * Notification noti = new Notification.Builder()
6858 * .setSmallIcon(R.drawable.ic_stat_player)
6859 * .setLargeIcon(albumArtBitmap))
6860 * .setCustomContentView(contentView);
6861 * .setStyle(<b>new Notification.DecoratedCustomViewStyle()</b>)
6862 * .build();
6863 * </pre>
6864 */
6865 public static class DecoratedCustomViewStyle extends Style {
6866
6867 public DecoratedCustomViewStyle() {
6868 }
6869
Selim Cinek593610c2016-02-16 18:42:57 -08006870 /**
6871 * @hide
6872 */
6873 public boolean displayCustomViewInline() {
6874 return true;
6875 }
6876
6877 /**
6878 * @hide
6879 */
6880 @Override
Selim Cinek7d1009b2017-01-25 15:28:28 -08006881 public RemoteViews makeContentView(boolean increasedHeight) {
Selim Cinek593610c2016-02-16 18:42:57 -08006882 return makeStandardTemplateWithCustomContent(mBuilder.mN.contentView);
6883 }
6884
6885 /**
6886 * @hide
6887 */
6888 @Override
6889 public RemoteViews makeBigContentView() {
6890 return makeDecoratedBigContentView();
6891 }
6892
6893 /**
6894 * @hide
6895 */
6896 @Override
Selim Cinek87ed69b2017-02-09 15:59:43 -08006897 public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
Selim Cinek593610c2016-02-16 18:42:57 -08006898 return makeDecoratedHeadsUpContentView();
6899 }
6900
Selim Cinek593610c2016-02-16 18:42:57 -08006901 private RemoteViews makeDecoratedHeadsUpContentView() {
6902 RemoteViews headsUpContentView = mBuilder.mN.headsUpContentView == null
6903 ? mBuilder.mN.contentView
6904 : mBuilder.mN.headsUpContentView;
6905 if (mBuilder.mActions.size() == 0) {
6906 return makeStandardTemplateWithCustomContent(headsUpContentView);
6907 }
6908 RemoteViews remoteViews = mBuilder.applyStandardTemplateWithActions(
6909 mBuilder.getBigBaseLayoutResource());
Selim Cinek247fa012016-02-18 09:50:48 -08006910 buildIntoRemoteViewContent(remoteViews, headsUpContentView);
Selim Cinek593610c2016-02-16 18:42:57 -08006911 return remoteViews;
6912 }
6913
Selim Cinek593610c2016-02-16 18:42:57 -08006914 private RemoteViews makeStandardTemplateWithCustomContent(RemoteViews customContent) {
6915 RemoteViews remoteViews = mBuilder.applyStandardTemplate(
6916 mBuilder.getBaseLayoutResource());
Selim Cinek247fa012016-02-18 09:50:48 -08006917 buildIntoRemoteViewContent(remoteViews, customContent);
Selim Cinek593610c2016-02-16 18:42:57 -08006918 return remoteViews;
6919 }
6920
Selim Cinek593610c2016-02-16 18:42:57 -08006921 private RemoteViews makeDecoratedBigContentView() {
6922 RemoteViews bigContentView = mBuilder.mN.bigContentView == null
6923 ? mBuilder.mN.contentView
6924 : mBuilder.mN.bigContentView;
6925 if (mBuilder.mActions.size() == 0) {
6926 return makeStandardTemplateWithCustomContent(bigContentView);
6927 }
6928 RemoteViews remoteViews = mBuilder.applyStandardTemplateWithActions(
6929 mBuilder.getBigBaseLayoutResource());
Selim Cinek247fa012016-02-18 09:50:48 -08006930 buildIntoRemoteViewContent(remoteViews, bigContentView);
Selim Cinek593610c2016-02-16 18:42:57 -08006931 return remoteViews;
6932 }
Selim Cinek247fa012016-02-18 09:50:48 -08006933
6934 private void buildIntoRemoteViewContent(RemoteViews remoteViews,
6935 RemoteViews customContent) {
Adrian Roos5081c0d2016-02-26 16:04:19 -08006936 if (customContent != null) {
Selim Cinekf91017e2016-03-14 12:25:09 -07006937 // Need to clone customContent before adding, because otherwise it can no longer be
6938 // parceled independently of remoteViews.
Adrian Roos5081c0d2016-02-26 16:04:19 -08006939 customContent = customContent.clone();
Anthony Chen8f5f3582017-04-11 11:18:37 -07006940 remoteViews.removeAllViewsExceptId(R.id.notification_main_column, R.id.progress);
6941 remoteViews.addView(R.id.notification_main_column, customContent, 0 /* index */);
Adrian Roos5081c0d2016-02-26 16:04:19 -08006942 }
Selim Cinek247fa012016-02-18 09:50:48 -08006943 // also update the end margin if there is an image
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006944 int endMargin = R.dimen.notification_content_margin_end;
Selim Cinek279fa862016-06-14 10:57:25 -07006945 if (mBuilder.mN.hasLargeIcon()) {
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006946 endMargin = R.dimen.notification_content_plus_picture_margin_end;
Selim Cinek247fa012016-02-18 09:50:48 -08006947 }
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006948 remoteViews.setViewLayoutMarginEndDimen(R.id.notification_main_column, endMargin);
Selim Cinek247fa012016-02-18 09:50:48 -08006949 }
Selim Cinek593610c2016-02-16 18:42:57 -08006950 }
6951
Selim Cinek03eb3b72016-02-18 10:39:45 -08006952 /**
6953 * Notification style for media custom views that are decorated by the system
6954 *
6955 * <p>Instead of providing a media notification that is completely custom, a developer can set
6956 * this style and still obtain system decorations like the notification header with the expand
6957 * affordance and actions.
6958 *
6959 * <p>Use {@link android.app.Notification.Builder#setCustomContentView(RemoteViews)},
6960 * {@link android.app.Notification.Builder#setCustomBigContentView(RemoteViews)} and
6961 * {@link android.app.Notification.Builder#setCustomHeadsUpContentView(RemoteViews)} to set the
6962 * corresponding custom views to display.
Selim Cinek99104832017-01-25 14:47:33 -08006963 * <p>
6964 * Contrary to {@link MediaStyle} a developer has to opt-in to the colorizing of the
6965 * notification by using {@link Notification.Builder#setColorized(boolean)}.
6966 * <p>
Selim Cinek03eb3b72016-02-18 10:39:45 -08006967 * To use this style with your Notification, feed it to
6968 * {@link Notification.Builder#setStyle(android.app.Notification.Style)} like so:
6969 * <pre class="prettyprint">
6970 * Notification noti = new Notification.Builder()
6971 * .setSmallIcon(R.drawable.ic_stat_player)
6972 * .setLargeIcon(albumArtBitmap))
6973 * .setCustomContentView(contentView);
6974 * .setStyle(<b>new Notification.DecoratedMediaCustomViewStyle()</b>
6975 * .setMediaSession(mySession))
6976 * .build();
6977 * </pre>
6978 *
6979 * @see android.app.Notification.DecoratedCustomViewStyle
6980 * @see android.app.Notification.MediaStyle
6981 */
6982 public static class DecoratedMediaCustomViewStyle extends MediaStyle {
6983
6984 public DecoratedMediaCustomViewStyle() {
6985 }
6986
Selim Cinek03eb3b72016-02-18 10:39:45 -08006987 /**
6988 * @hide
6989 */
6990 public boolean displayCustomViewInline() {
6991 return true;
6992 }
6993
6994 /**
6995 * @hide
6996 */
6997 @Override
Selim Cinek7d1009b2017-01-25 15:28:28 -08006998 public RemoteViews makeContentView(boolean increasedHeight) {
6999 RemoteViews remoteViews = super.makeContentView(false /* increasedHeight */);
Selim Cinek03eb3b72016-02-18 10:39:45 -08007000 return buildIntoRemoteView(remoteViews, R.id.notification_content_container,
7001 mBuilder.mN.contentView);
7002 }
7003
7004 /**
7005 * @hide
7006 */
7007 @Override
7008 public RemoteViews makeBigContentView() {
7009 RemoteViews customRemoteView = mBuilder.mN.bigContentView != null
7010 ? mBuilder.mN.bigContentView
7011 : mBuilder.mN.contentView;
7012 return makeBigContentViewWithCustomContent(customRemoteView);
7013 }
7014
7015 private RemoteViews makeBigContentViewWithCustomContent(RemoteViews customRemoteView) {
7016 RemoteViews remoteViews = super.makeBigContentView();
7017 if (remoteViews != null) {
7018 return buildIntoRemoteView(remoteViews, R.id.notification_main_column,
7019 customRemoteView);
7020 } else if (customRemoteView != mBuilder.mN.contentView){
Selim Cinek7d1009b2017-01-25 15:28:28 -08007021 remoteViews = super.makeContentView(false /* increasedHeight */);
Selim Cinek03eb3b72016-02-18 10:39:45 -08007022 return buildIntoRemoteView(remoteViews, R.id.notification_content_container,
7023 customRemoteView);
7024 } else {
7025 return null;
7026 }
7027 }
7028
7029 /**
7030 * @hide
7031 */
7032 @Override
Selim Cinek87ed69b2017-02-09 15:59:43 -08007033 public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
Selim Cinek03eb3b72016-02-18 10:39:45 -08007034 RemoteViews customRemoteView = mBuilder.mN.headsUpContentView != null
7035 ? mBuilder.mN.headsUpContentView
7036 : mBuilder.mN.contentView;
7037 return makeBigContentViewWithCustomContent(customRemoteView);
7038 }
7039
7040 private RemoteViews buildIntoRemoteView(RemoteViews remoteViews, int id,
7041 RemoteViews customContent) {
Adrian Roos5081c0d2016-02-26 16:04:19 -08007042 if (customContent != null) {
Selim Cinekf91017e2016-03-14 12:25:09 -07007043 // Need to clone customContent before adding, because otherwise it can no longer be
7044 // parceled independently of remoteViews.
Adrian Roos5081c0d2016-02-26 16:04:19 -08007045 customContent = customContent.clone();
Selim Cinekf91017e2016-03-14 12:25:09 -07007046 remoteViews.removeAllViews(id);
7047 remoteViews.addView(id, customContent);
Adrian Roos5081c0d2016-02-26 16:04:19 -08007048 }
Selim Cinek03eb3b72016-02-18 10:39:45 -08007049 return remoteViews;
7050 }
7051 }
7052
Christoph Studer4600f9b2014-07-22 22:44:43 +02007053 // When adding a new Style subclass here, don't forget to update
7054 // Builder.getNotificationStyleClass.
7055
Griff Hazen61a9e862014-05-22 16:05:19 -07007056 /**
7057 * Extender interface for use with {@link Builder#extend}. Extenders may be used to add
7058 * metadata or change options on a notification builder.
7059 */
7060 public interface Extender {
7061 /**
7062 * Apply this extender to a notification builder.
7063 * @param builder the builder to be modified.
7064 * @return the build object for chaining.
7065 */
7066 public Builder extend(Builder builder);
7067 }
7068
7069 /**
7070 * Helper class to add wearable extensions to notifications.
7071 * <p class="note"> See
7072 * <a href="{@docRoot}wear/notifications/creating.html">Creating Notifications
7073 * for Android Wear</a> for more information on how to use this class.
7074 * <p>
7075 * To create a notification with wearable extensions:
7076 * <ol>
7077 * <li>Create a {@link android.app.Notification.Builder}, setting any desired
7078 * properties.
7079 * <li>Create a {@link android.app.Notification.WearableExtender}.
7080 * <li>Set wearable-specific properties using the
7081 * {@code add} and {@code set} methods of {@link android.app.Notification.WearableExtender}.
7082 * <li>Call {@link android.app.Notification.Builder#extend} to apply the extensions to a
7083 * notification.
7084 * <li>Post the notification to the notification system with the
7085 * {@code NotificationManager.notify(...)} methods.
7086 * </ol>
7087 *
7088 * <pre class="prettyprint">
7089 * Notification notif = new Notification.Builder(mContext)
7090 * .setContentTitle(&quot;New mail from &quot; + sender.toString())
7091 * .setContentText(subject)
7092 * .setSmallIcon(R.drawable.new_mail)
7093 * .extend(new Notification.WearableExtender()
7094 * .setContentIcon(R.drawable.new_mail))
7095 * .build();
7096 * NotificationManager notificationManger =
7097 * (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
7098 * notificationManger.notify(0, notif);</pre>
7099 *
7100 * <p>Wearable extensions can be accessed on an existing notification by using the
7101 * {@code WearableExtender(Notification)} constructor,
7102 * and then using the {@code get} methods to access values.
7103 *
7104 * <pre class="prettyprint">
7105 * Notification.WearableExtender wearableExtender = new Notification.WearableExtender(
7106 * notification);
Griff Hazen14f57992014-05-26 09:07:14 -07007107 * List&lt;Notification&gt; pages = wearableExtender.getPages();</pre>
Griff Hazen61a9e862014-05-22 16:05:19 -07007108 */
7109 public static final class WearableExtender implements Extender {
7110 /**
7111 * Sentinel value for an action index that is unset.
7112 */
7113 public static final int UNSET_ACTION_INDEX = -1;
7114
7115 /**
7116 * Size value for use with {@link #setCustomSizePreset} to show this notification with
7117 * default sizing.
7118 * <p>For custom display notifications created using {@link #setDisplayIntent},
Paul Soulosaa4f4bf2015-08-04 11:59:45 -07007119 * the default is {@link #SIZE_MEDIUM}. All other notifications size automatically based
Griff Hazen61a9e862014-05-22 16:05:19 -07007120 * on their content.
7121 */
7122 public static final int SIZE_DEFAULT = 0;
7123
7124 /**
7125 * Size value for use with {@link #setCustomSizePreset} to show this notification
7126 * with an extra small size.
7127 * <p>This value is only applicable for custom display notifications created using
7128 * {@link #setDisplayIntent}.
7129 */
7130 public static final int SIZE_XSMALL = 1;
7131
7132 /**
7133 * Size value for use with {@link #setCustomSizePreset} to show this notification
7134 * with a small size.
7135 * <p>This value is only applicable for custom display notifications created using
7136 * {@link #setDisplayIntent}.
7137 */
7138 public static final int SIZE_SMALL = 2;
7139
7140 /**
7141 * Size value for use with {@link #setCustomSizePreset} to show this notification
7142 * with a medium size.
7143 * <p>This value is only applicable for custom display notifications created using
7144 * {@link #setDisplayIntent}.
7145 */
7146 public static final int SIZE_MEDIUM = 3;
7147
7148 /**
7149 * Size value for use with {@link #setCustomSizePreset} to show this notification
7150 * with a large size.
7151 * <p>This value is only applicable for custom display notifications created using
7152 * {@link #setDisplayIntent}.
7153 */
7154 public static final int SIZE_LARGE = 4;
7155
Griff Hazend5f11f92014-05-27 15:40:09 -07007156 /**
7157 * Size value for use with {@link #setCustomSizePreset} to show this notification
7158 * full screen.
7159 * <p>This value is only applicable for custom display notifications created using
7160 * {@link #setDisplayIntent}.
7161 */
7162 public static final int SIZE_FULL_SCREEN = 5;
7163
Griff Hazen5f2edfc2014-09-29 16:28:44 -07007164 /**
7165 * Sentinel value for use with {@link #setHintScreenTimeout} to keep the screen on for a
7166 * short amount of time when this notification is displayed on the screen. This
7167 * is the default value.
7168 */
7169 public static final int SCREEN_TIMEOUT_SHORT = 0;
7170
7171 /**
7172 * Sentinel value for use with {@link #setHintScreenTimeout} to keep the screen on
7173 * for a longer amount of time when this notification is displayed on the screen.
7174 */
7175 public static final int SCREEN_TIMEOUT_LONG = -1;
7176
Griff Hazen61a9e862014-05-22 16:05:19 -07007177 /** Notification extra which contains wearable extensions */
7178 private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS";
7179
Pete Gastaf6781d2014-10-07 15:17:05 -04007180 // Keys within EXTRA_WEARABLE_EXTENSIONS for wearable options.
Griff Hazen61a9e862014-05-22 16:05:19 -07007181 private static final String KEY_ACTIONS = "actions";
7182 private static final String KEY_FLAGS = "flags";
7183 private static final String KEY_DISPLAY_INTENT = "displayIntent";
7184 private static final String KEY_PAGES = "pages";
7185 private static final String KEY_BACKGROUND = "background";
7186 private static final String KEY_CONTENT_ICON = "contentIcon";
7187 private static final String KEY_CONTENT_ICON_GRAVITY = "contentIconGravity";
7188 private static final String KEY_CONTENT_ACTION_INDEX = "contentActionIndex";
7189 private static final String KEY_CUSTOM_SIZE_PRESET = "customSizePreset";
7190 private static final String KEY_CUSTOM_CONTENT_HEIGHT = "customContentHeight";
7191 private static final String KEY_GRAVITY = "gravity";
Griff Hazen5f2edfc2014-09-29 16:28:44 -07007192 private static final String KEY_HINT_SCREEN_TIMEOUT = "hintScreenTimeout";
Nadia Benbernou948627e2016-04-14 14:41:08 -04007193 private static final String KEY_DISMISSAL_ID = "dismissalId";
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04007194 private static final String KEY_BRIDGE_TAG = "bridgeTag";
Griff Hazen61a9e862014-05-22 16:05:19 -07007195
7196 // Flags bitwise-ored to mFlags
7197 private static final int FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE = 0x1;
7198 private static final int FLAG_HINT_HIDE_ICON = 1 << 1;
7199 private static final int FLAG_HINT_SHOW_BACKGROUND_ONLY = 1 << 2;
7200 private static final int FLAG_START_SCROLL_BOTTOM = 1 << 3;
Griff Hazen5f2edfc2014-09-29 16:28:44 -07007201 private static final int FLAG_HINT_AVOID_BACKGROUND_CLIPPING = 1 << 4;
Alex Hills4bcb06b2016-04-05 14:26:25 -04007202 private static final int FLAG_BIG_PICTURE_AMBIENT = 1 << 5;
Alex Hills9ab3a232016-04-05 14:54:56 -04007203 private static final int FLAG_HINT_CONTENT_INTENT_LAUNCHES_ACTIVITY = 1 << 6;
Griff Hazen61a9e862014-05-22 16:05:19 -07007204
7205 // Default value for flags integer
7206 private static final int DEFAULT_FLAGS = FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE;
7207
7208 private static final int DEFAULT_CONTENT_ICON_GRAVITY = Gravity.END;
7209 private static final int DEFAULT_GRAVITY = Gravity.BOTTOM;
7210
7211 private ArrayList<Action> mActions = new ArrayList<Action>();
7212 private int mFlags = DEFAULT_FLAGS;
7213 private PendingIntent mDisplayIntent;
7214 private ArrayList<Notification> mPages = new ArrayList<Notification>();
7215 private Bitmap mBackground;
7216 private int mContentIcon;
7217 private int mContentIconGravity = DEFAULT_CONTENT_ICON_GRAVITY;
7218 private int mContentActionIndex = UNSET_ACTION_INDEX;
7219 private int mCustomSizePreset = SIZE_DEFAULT;
7220 private int mCustomContentHeight;
7221 private int mGravity = DEFAULT_GRAVITY;
Griff Hazen5f2edfc2014-09-29 16:28:44 -07007222 private int mHintScreenTimeout;
Nadia Benbernou948627e2016-04-14 14:41:08 -04007223 private String mDismissalId;
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04007224 private String mBridgeTag;
Griff Hazen61a9e862014-05-22 16:05:19 -07007225
7226 /**
7227 * Create a {@link android.app.Notification.WearableExtender} with default
7228 * options.
7229 */
7230 public WearableExtender() {
7231 }
7232
7233 public WearableExtender(Notification notif) {
7234 Bundle wearableBundle = notif.extras.getBundle(EXTRA_WEARABLE_EXTENSIONS);
7235 if (wearableBundle != null) {
7236 List<Action> actions = wearableBundle.getParcelableArrayList(KEY_ACTIONS);
7237 if (actions != null) {
7238 mActions.addAll(actions);
7239 }
7240
7241 mFlags = wearableBundle.getInt(KEY_FLAGS, DEFAULT_FLAGS);
7242 mDisplayIntent = wearableBundle.getParcelable(KEY_DISPLAY_INTENT);
7243
7244 Notification[] pages = getNotificationArrayFromBundle(
7245 wearableBundle, KEY_PAGES);
7246 if (pages != null) {
7247 Collections.addAll(mPages, pages);
7248 }
7249
7250 mBackground = wearableBundle.getParcelable(KEY_BACKGROUND);
7251 mContentIcon = wearableBundle.getInt(KEY_CONTENT_ICON);
7252 mContentIconGravity = wearableBundle.getInt(KEY_CONTENT_ICON_GRAVITY,
7253 DEFAULT_CONTENT_ICON_GRAVITY);
7254 mContentActionIndex = wearableBundle.getInt(KEY_CONTENT_ACTION_INDEX,
7255 UNSET_ACTION_INDEX);
7256 mCustomSizePreset = wearableBundle.getInt(KEY_CUSTOM_SIZE_PRESET,
7257 SIZE_DEFAULT);
7258 mCustomContentHeight = wearableBundle.getInt(KEY_CUSTOM_CONTENT_HEIGHT);
7259 mGravity = wearableBundle.getInt(KEY_GRAVITY, DEFAULT_GRAVITY);
Griff Hazen5f2edfc2014-09-29 16:28:44 -07007260 mHintScreenTimeout = wearableBundle.getInt(KEY_HINT_SCREEN_TIMEOUT);
Nadia Benbernou948627e2016-04-14 14:41:08 -04007261 mDismissalId = wearableBundle.getString(KEY_DISMISSAL_ID);
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04007262 mBridgeTag = wearableBundle.getString(KEY_BRIDGE_TAG);
Griff Hazen61a9e862014-05-22 16:05:19 -07007263 }
7264 }
7265
7266 /**
7267 * Apply wearable extensions to a notification that is being built. This is typically
7268 * called by the {@link android.app.Notification.Builder#extend} method of
7269 * {@link android.app.Notification.Builder}.
7270 */
7271 @Override
7272 public Notification.Builder extend(Notification.Builder builder) {
7273 Bundle wearableBundle = new Bundle();
7274
7275 if (!mActions.isEmpty()) {
7276 wearableBundle.putParcelableArrayList(KEY_ACTIONS, mActions);
7277 }
7278 if (mFlags != DEFAULT_FLAGS) {
7279 wearableBundle.putInt(KEY_FLAGS, mFlags);
7280 }
7281 if (mDisplayIntent != null) {
7282 wearableBundle.putParcelable(KEY_DISPLAY_INTENT, mDisplayIntent);
7283 }
7284 if (!mPages.isEmpty()) {
7285 wearableBundle.putParcelableArray(KEY_PAGES, mPages.toArray(
7286 new Notification[mPages.size()]));
7287 }
7288 if (mBackground != null) {
7289 wearableBundle.putParcelable(KEY_BACKGROUND, mBackground);
7290 }
7291 if (mContentIcon != 0) {
7292 wearableBundle.putInt(KEY_CONTENT_ICON, mContentIcon);
7293 }
7294 if (mContentIconGravity != DEFAULT_CONTENT_ICON_GRAVITY) {
7295 wearableBundle.putInt(KEY_CONTENT_ICON_GRAVITY, mContentIconGravity);
7296 }
7297 if (mContentActionIndex != UNSET_ACTION_INDEX) {
7298 wearableBundle.putInt(KEY_CONTENT_ACTION_INDEX,
7299 mContentActionIndex);
7300 }
7301 if (mCustomSizePreset != SIZE_DEFAULT) {
7302 wearableBundle.putInt(KEY_CUSTOM_SIZE_PRESET, mCustomSizePreset);
7303 }
7304 if (mCustomContentHeight != 0) {
7305 wearableBundle.putInt(KEY_CUSTOM_CONTENT_HEIGHT, mCustomContentHeight);
7306 }
7307 if (mGravity != DEFAULT_GRAVITY) {
7308 wearableBundle.putInt(KEY_GRAVITY, mGravity);
7309 }
Griff Hazen5f2edfc2014-09-29 16:28:44 -07007310 if (mHintScreenTimeout != 0) {
7311 wearableBundle.putInt(KEY_HINT_SCREEN_TIMEOUT, mHintScreenTimeout);
7312 }
Nadia Benbernou948627e2016-04-14 14:41:08 -04007313 if (mDismissalId != null) {
7314 wearableBundle.putString(KEY_DISMISSAL_ID, mDismissalId);
7315 }
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04007316 if (mBridgeTag != null) {
7317 wearableBundle.putString(KEY_BRIDGE_TAG, mBridgeTag);
7318 }
Griff Hazen61a9e862014-05-22 16:05:19 -07007319
7320 builder.getExtras().putBundle(EXTRA_WEARABLE_EXTENSIONS, wearableBundle);
7321 return builder;
7322 }
7323
7324 @Override
7325 public WearableExtender clone() {
7326 WearableExtender that = new WearableExtender();
7327 that.mActions = new ArrayList<Action>(this.mActions);
7328 that.mFlags = this.mFlags;
7329 that.mDisplayIntent = this.mDisplayIntent;
7330 that.mPages = new ArrayList<Notification>(this.mPages);
7331 that.mBackground = this.mBackground;
7332 that.mContentIcon = this.mContentIcon;
7333 that.mContentIconGravity = this.mContentIconGravity;
7334 that.mContentActionIndex = this.mContentActionIndex;
7335 that.mCustomSizePreset = this.mCustomSizePreset;
7336 that.mCustomContentHeight = this.mCustomContentHeight;
7337 that.mGravity = this.mGravity;
Griff Hazen5f2edfc2014-09-29 16:28:44 -07007338 that.mHintScreenTimeout = this.mHintScreenTimeout;
Nadia Benbernou948627e2016-04-14 14:41:08 -04007339 that.mDismissalId = this.mDismissalId;
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04007340 that.mBridgeTag = this.mBridgeTag;
Griff Hazen61a9e862014-05-22 16:05:19 -07007341 return that;
7342 }
7343
7344 /**
7345 * Add a wearable action to this notification.
7346 *
7347 * <p>When wearable actions are added using this method, the set of actions that
7348 * show on a wearable device splits from devices that only show actions added
7349 * using {@link android.app.Notification.Builder#addAction}. This allows for customization
7350 * of which actions display on different devices.
7351 *
7352 * @param action the action to add to this notification
7353 * @return this object for method chaining
7354 * @see android.app.Notification.Action
7355 */
7356 public WearableExtender addAction(Action action) {
7357 mActions.add(action);
7358 return this;
7359 }
7360
7361 /**
7362 * Adds wearable actions to this notification.
7363 *
7364 * <p>When wearable actions are added using this method, the set of actions that
7365 * show on a wearable device splits from devices that only show actions added
7366 * using {@link android.app.Notification.Builder#addAction}. This allows for customization
7367 * of which actions display on different devices.
7368 *
7369 * @param actions the actions to add to this notification
7370 * @return this object for method chaining
7371 * @see android.app.Notification.Action
7372 */
7373 public WearableExtender addActions(List<Action> actions) {
7374 mActions.addAll(actions);
7375 return this;
7376 }
7377
7378 /**
7379 * Clear all wearable actions present on this builder.
7380 * @return this object for method chaining.
7381 * @see #addAction
7382 */
7383 public WearableExtender clearActions() {
7384 mActions.clear();
7385 return this;
7386 }
7387
7388 /**
7389 * Get the wearable actions present on this notification.
7390 */
7391 public List<Action> getActions() {
7392 return mActions;
7393 }
7394
7395 /**
7396 * Set an intent to launch inside of an activity view when displaying
Griff Hazen14f57992014-05-26 09:07:14 -07007397 * this notification. The {@link PendingIntent} provided should be for an activity.
7398 *
7399 * <pre class="prettyprint">
7400 * Intent displayIntent = new Intent(context, MyDisplayActivity.class);
7401 * PendingIntent displayPendingIntent = PendingIntent.getActivity(context,
7402 * 0, displayIntent, PendingIntent.FLAG_UPDATE_CURRENT);
7403 * Notification notif = new Notification.Builder(context)
7404 * .extend(new Notification.WearableExtender()
7405 * .setDisplayIntent(displayPendingIntent)
7406 * .setCustomSizePreset(Notification.WearableExtender.SIZE_MEDIUM))
7407 * .build();</pre>
7408 *
7409 * <p>The activity to launch needs to allow embedding, must be exported, and
Griff Hazen831ca9d2014-06-17 00:38:38 -07007410 * should have an empty task affinity. It is also recommended to use the device
7411 * default light theme.
Griff Hazen14f57992014-05-26 09:07:14 -07007412 *
7413 * <p>Example AndroidManifest.xml entry:
7414 * <pre class="prettyprint">
7415 * &lt;activity android:name=&quot;com.example.MyDisplayActivity&quot;
7416 * android:exported=&quot;true&quot;
7417 * android:allowEmbedded=&quot;true&quot;
Griff Hazen831ca9d2014-06-17 00:38:38 -07007418 * android:taskAffinity=&quot;&quot;
7419 * android:theme=&quot;@android:style/Theme.DeviceDefault.Light&quot; /&gt;</pre>
Griff Hazen61a9e862014-05-22 16:05:19 -07007420 *
7421 * @param intent the {@link PendingIntent} for an activity
7422 * @return this object for method chaining
7423 * @see android.app.Notification.WearableExtender#getDisplayIntent
7424 */
7425 public WearableExtender setDisplayIntent(PendingIntent intent) {
7426 mDisplayIntent = intent;
7427 return this;
7428 }
7429
7430 /**
7431 * Get the intent to launch inside of an activity view when displaying this
7432 * notification. This {@code PendingIntent} should be for an activity.
7433 */
7434 public PendingIntent getDisplayIntent() {
7435 return mDisplayIntent;
7436 }
7437
7438 /**
7439 * Add an additional page of content to display with this notification. The current
7440 * notification forms the first page, and pages added using this function form
7441 * subsequent pages. This field can be used to separate a notification into multiple
7442 * sections.
7443 *
7444 * @param page the notification to add as another page
7445 * @return this object for method chaining
7446 * @see android.app.Notification.WearableExtender#getPages
7447 */
7448 public WearableExtender addPage(Notification page) {
7449 mPages.add(page);
7450 return this;
7451 }
7452
7453 /**
7454 * Add additional pages of content to display with this notification. The current
7455 * notification forms the first page, and pages added using this function form
7456 * subsequent pages. This field can be used to separate a notification into multiple
7457 * sections.
7458 *
7459 * @param pages a list of notifications
7460 * @return this object for method chaining
7461 * @see android.app.Notification.WearableExtender#getPages
7462 */
7463 public WearableExtender addPages(List<Notification> pages) {
7464 mPages.addAll(pages);
7465 return this;
7466 }
7467
7468 /**
7469 * Clear all additional pages present on this builder.
7470 * @return this object for method chaining.
7471 * @see #addPage
7472 */
7473 public WearableExtender clearPages() {
7474 mPages.clear();
7475 return this;
7476 }
7477
7478 /**
7479 * Get the array of additional pages of content for displaying this notification. The
7480 * current notification forms the first page, and elements within this array form
7481 * subsequent pages. This field can be used to separate a notification into multiple
7482 * sections.
7483 * @return the pages for this notification
7484 */
7485 public List<Notification> getPages() {
7486 return mPages;
7487 }
7488
7489 /**
7490 * Set a background image to be displayed behind the notification content.
7491 * Contrary to the {@link android.app.Notification.BigPictureStyle}, this background
7492 * will work with any notification style.
7493 *
7494 * @param background the background bitmap
7495 * @return this object for method chaining
7496 * @see android.app.Notification.WearableExtender#getBackground
7497 */
7498 public WearableExtender setBackground(Bitmap background) {
7499 mBackground = background;
7500 return this;
7501 }
7502
7503 /**
7504 * Get a background image to be displayed behind the notification content.
7505 * Contrary to the {@link android.app.Notification.BigPictureStyle}, this background
7506 * will work with any notification style.
7507 *
7508 * @return the background image
7509 * @see android.app.Notification.WearableExtender#setBackground
7510 */
7511 public Bitmap getBackground() {
7512 return mBackground;
7513 }
7514
7515 /**
7516 * Set an icon that goes with the content of this notification.
7517 */
7518 public WearableExtender setContentIcon(int icon) {
7519 mContentIcon = icon;
7520 return this;
7521 }
7522
7523 /**
7524 * Get an icon that goes with the content of this notification.
7525 */
7526 public int getContentIcon() {
7527 return mContentIcon;
7528 }
7529
7530 /**
7531 * Set the gravity that the content icon should have within the notification display.
7532 * Supported values include {@link android.view.Gravity#START} and
7533 * {@link android.view.Gravity#END}. The default value is {@link android.view.Gravity#END}.
7534 * @see #setContentIcon
7535 */
7536 public WearableExtender setContentIconGravity(int contentIconGravity) {
7537 mContentIconGravity = contentIconGravity;
7538 return this;
7539 }
7540
7541 /**
7542 * Get the gravity that the content icon should have within the notification display.
7543 * Supported values include {@link android.view.Gravity#START} and
7544 * {@link android.view.Gravity#END}. The default value is {@link android.view.Gravity#END}.
7545 * @see #getContentIcon
7546 */
7547 public int getContentIconGravity() {
7548 return mContentIconGravity;
7549 }
7550
7551 /**
7552 * Set an action from this notification's actions to be clickable with the content of
Griff Hazen14f57992014-05-26 09:07:14 -07007553 * this notification. This action will no longer display separately from the
7554 * notification's content.
7555 *
Griff Hazenca48d352014-05-28 22:37:13 -07007556 * <p>For notifications with multiple pages, child pages can also have content actions
Griff Hazen14f57992014-05-26 09:07:14 -07007557 * set, although the list of available actions comes from the main notification and not
7558 * from the child page's notification.
7559 *
7560 * @param actionIndex The index of the action to hoist onto the current notification page.
7561 * If wearable actions were added to the main notification, this index
7562 * will apply to that list, otherwise it will apply to the regular
7563 * actions list.
Griff Hazen61a9e862014-05-22 16:05:19 -07007564 */
7565 public WearableExtender setContentAction(int actionIndex) {
7566 mContentActionIndex = actionIndex;
7567 return this;
7568 }
7569
7570 /**
Griff Hazenca48d352014-05-28 22:37:13 -07007571 * Get the index of the notification action, if any, that was specified as being clickable
7572 * with the content of this notification. This action will no longer display separately
Griff Hazen14f57992014-05-26 09:07:14 -07007573 * from the notification's content.
Griff Hazen61a9e862014-05-22 16:05:19 -07007574 *
Griff Hazenca48d352014-05-28 22:37:13 -07007575 * <p>For notifications with multiple pages, child pages can also have content actions
Griff Hazen14f57992014-05-26 09:07:14 -07007576 * set, although the list of available actions comes from the main notification and not
7577 * from the child page's notification.
7578 *
7579 * <p>If wearable specific actions were added to the main notification, this index will
7580 * apply to that list, otherwise it will apply to the regular actions list.
Griff Hazenca48d352014-05-28 22:37:13 -07007581 *
7582 * @return the action index or {@link #UNSET_ACTION_INDEX} if no action was selected.
Griff Hazen61a9e862014-05-22 16:05:19 -07007583 */
7584 public int getContentAction() {
7585 return mContentActionIndex;
7586 }
7587
7588 /**
7589 * Set the gravity that this notification should have within the available viewport space.
7590 * Supported values include {@link android.view.Gravity#TOP},
7591 * {@link android.view.Gravity#CENTER_VERTICAL} and {@link android.view.Gravity#BOTTOM}.
7592 * The default value is {@link android.view.Gravity#BOTTOM}.
7593 */
7594 public WearableExtender setGravity(int gravity) {
7595 mGravity = gravity;
7596 return this;
7597 }
7598
7599 /**
7600 * Get the gravity that this notification should have within the available viewport space.
7601 * Supported values include {@link android.view.Gravity#TOP},
7602 * {@link android.view.Gravity#CENTER_VERTICAL} and {@link android.view.Gravity#BOTTOM}.
7603 * The default value is {@link android.view.Gravity#BOTTOM}.
7604 */
7605 public int getGravity() {
7606 return mGravity;
7607 }
7608
7609 /**
7610 * Set the custom size preset for the display of this notification out of the available
7611 * presets found in {@link android.app.Notification.WearableExtender}, e.g.
7612 * {@link #SIZE_LARGE}.
7613 * <p>Some custom size presets are only applicable for custom display notifications created
7614 * using {@link android.app.Notification.WearableExtender#setDisplayIntent}. Check the
7615 * documentation for the preset in question. See also
7616 * {@link #setCustomContentHeight} and {@link #getCustomSizePreset}.
7617 */
7618 public WearableExtender setCustomSizePreset(int sizePreset) {
7619 mCustomSizePreset = sizePreset;
7620 return this;
7621 }
7622
7623 /**
7624 * Get the custom size preset for the display of this notification out of the available
7625 * presets found in {@link android.app.Notification.WearableExtender}, e.g.
7626 * {@link #SIZE_LARGE}.
7627 * <p>Some custom size presets are only applicable for custom display notifications created
7628 * using {@link #setDisplayIntent}. Check the documentation for the preset in question.
7629 * See also {@link #setCustomContentHeight} and {@link #setCustomSizePreset}.
7630 */
7631 public int getCustomSizePreset() {
7632 return mCustomSizePreset;
7633 }
7634
7635 /**
7636 * Set the custom height in pixels for the display of this notification's content.
7637 * <p>This option is only available for custom display notifications created
7638 * using {@link android.app.Notification.WearableExtender#setDisplayIntent}. See also
7639 * {@link android.app.Notification.WearableExtender#setCustomSizePreset} and
7640 * {@link #getCustomContentHeight}.
7641 */
7642 public WearableExtender setCustomContentHeight(int height) {
7643 mCustomContentHeight = height;
7644 return this;
7645 }
7646
7647 /**
7648 * Get the custom height in pixels for the display of this notification's content.
7649 * <p>This option is only available for custom display notifications created
7650 * using {@link #setDisplayIntent}. See also {@link #setCustomSizePreset} and
7651 * {@link #setCustomContentHeight}.
7652 */
7653 public int getCustomContentHeight() {
7654 return mCustomContentHeight;
7655 }
7656
7657 /**
7658 * Set whether the scrolling position for the contents of this notification should start
7659 * at the bottom of the contents instead of the top when the contents are too long to
7660 * display within the screen. Default is false (start scroll at the top).
7661 */
7662 public WearableExtender setStartScrollBottom(boolean startScrollBottom) {
7663 setFlag(FLAG_START_SCROLL_BOTTOM, startScrollBottom);
7664 return this;
7665 }
7666
7667 /**
7668 * Get whether the scrolling position for the contents of this notification should start
7669 * at the bottom of the contents instead of the top when the contents are too long to
7670 * display within the screen. Default is false (start scroll at the top).
7671 */
7672 public boolean getStartScrollBottom() {
7673 return (mFlags & FLAG_START_SCROLL_BOTTOM) != 0;
7674 }
7675
7676 /**
7677 * Set whether the content intent is available when the wearable device is not connected
7678 * to a companion device. The user can still trigger this intent when the wearable device
7679 * is offline, but a visual hint will indicate that the content intent may not be available.
7680 * Defaults to true.
7681 */
7682 public WearableExtender setContentIntentAvailableOffline(
7683 boolean contentIntentAvailableOffline) {
7684 setFlag(FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE, contentIntentAvailableOffline);
7685 return this;
7686 }
7687
7688 /**
7689 * Get whether the content intent is available when the wearable device is not connected
7690 * to a companion device. The user can still trigger this intent when the wearable device
7691 * is offline, but a visual hint will indicate that the content intent may not be available.
7692 * Defaults to true.
7693 */
7694 public boolean getContentIntentAvailableOffline() {
7695 return (mFlags & FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE) != 0;
7696 }
7697
7698 /**
7699 * Set a hint that this notification's icon should not be displayed.
7700 * @param hintHideIcon {@code true} to hide the icon, {@code false} otherwise.
7701 * @return this object for method chaining
7702 */
7703 public WearableExtender setHintHideIcon(boolean hintHideIcon) {
7704 setFlag(FLAG_HINT_HIDE_ICON, hintHideIcon);
7705 return this;
7706 }
7707
7708 /**
7709 * Get a hint that this notification's icon should not be displayed.
7710 * @return {@code true} if this icon should not be displayed, false otherwise.
7711 * The default value is {@code false} if this was never set.
7712 */
7713 public boolean getHintHideIcon() {
7714 return (mFlags & FLAG_HINT_HIDE_ICON) != 0;
7715 }
7716
7717 /**
7718 * Set a visual hint that only the background image of this notification should be
7719 * displayed, and other semantic content should be hidden. This hint is only applicable
7720 * to sub-pages added using {@link #addPage}.
7721 */
7722 public WearableExtender setHintShowBackgroundOnly(boolean hintShowBackgroundOnly) {
7723 setFlag(FLAG_HINT_SHOW_BACKGROUND_ONLY, hintShowBackgroundOnly);
7724 return this;
7725 }
7726
7727 /**
7728 * Get a visual hint that only the background image of this notification should be
7729 * displayed, and other semantic content should be hidden. This hint is only applicable
7730 * to sub-pages added using {@link android.app.Notification.WearableExtender#addPage}.
7731 */
7732 public boolean getHintShowBackgroundOnly() {
7733 return (mFlags & FLAG_HINT_SHOW_BACKGROUND_ONLY) != 0;
7734 }
7735
Griff Hazen5f2edfc2014-09-29 16:28:44 -07007736 /**
Griff Hazen9c5be4e2014-11-17 17:47:50 -08007737 * Set a hint that this notification's background should not be clipped if possible,
7738 * and should instead be resized to fully display on the screen, retaining the aspect
7739 * ratio of the image. This can be useful for images like barcodes or qr codes.
Griff Hazen5f2edfc2014-09-29 16:28:44 -07007740 * @param hintAvoidBackgroundClipping {@code true} to avoid clipping if possible.
7741 * @return this object for method chaining
7742 */
7743 public WearableExtender setHintAvoidBackgroundClipping(
7744 boolean hintAvoidBackgroundClipping) {
7745 setFlag(FLAG_HINT_AVOID_BACKGROUND_CLIPPING, hintAvoidBackgroundClipping);
7746 return this;
7747 }
7748
7749 /**
Griff Hazen9c5be4e2014-11-17 17:47:50 -08007750 * Get a hint that this notification's background should not be clipped if possible,
7751 * and should instead be resized to fully display on the screen, retaining the aspect
7752 * ratio of the image. This can be useful for images like barcodes or qr codes.
Griff Hazen5f2edfc2014-09-29 16:28:44 -07007753 * @return {@code true} if it's ok if the background is clipped on the screen, false
7754 * otherwise. The default value is {@code false} if this was never set.
7755 */
7756 public boolean getHintAvoidBackgroundClipping() {
7757 return (mFlags & FLAG_HINT_AVOID_BACKGROUND_CLIPPING) != 0;
7758 }
7759
7760 /**
7761 * Set a hint that the screen should remain on for at least this duration when
7762 * this notification is displayed on the screen.
7763 * @param timeout The requested screen timeout in milliseconds. Can also be either
7764 * {@link #SCREEN_TIMEOUT_SHORT} or {@link #SCREEN_TIMEOUT_LONG}.
7765 * @return this object for method chaining
7766 */
7767 public WearableExtender setHintScreenTimeout(int timeout) {
7768 mHintScreenTimeout = timeout;
7769 return this;
7770 }
7771
7772 /**
7773 * Get the duration, in milliseconds, that the screen should remain on for
7774 * when this notification is displayed.
7775 * @return the duration in milliseconds if > 0, or either one of the sentinel values
7776 * {@link #SCREEN_TIMEOUT_SHORT} or {@link #SCREEN_TIMEOUT_LONG}.
7777 */
7778 public int getHintScreenTimeout() {
7779 return mHintScreenTimeout;
7780 }
7781
Alex Hills9ab3a232016-04-05 14:54:56 -04007782 /**
Alex Hills4bcb06b2016-04-05 14:26:25 -04007783 * Set a hint that this notification's {@link BigPictureStyle} (if present) should be
7784 * converted to low-bit and displayed in ambient mode, especially useful for barcodes and
7785 * qr codes, as well as other simple black-and-white tickets.
7786 * @param hintAmbientBigPicture {@code true} to enable converstion and ambient.
7787 * @return this object for method chaining
7788 */
7789 public WearableExtender setHintAmbientBigPicture(boolean hintAmbientBigPicture) {
7790 setFlag(FLAG_BIG_PICTURE_AMBIENT, hintAmbientBigPicture);
7791 return this;
7792 }
7793
7794 /**
7795 * Get a hint that this notification's {@link BigPictureStyle} (if present) should be
7796 * converted to low-bit and displayed in ambient mode, especially useful for barcodes and
7797 * qr codes, as well as other simple black-and-white tickets.
7798 * @return {@code true} if it should be displayed in ambient, false otherwise
7799 * otherwise. The default value is {@code false} if this was never set.
7800 */
7801 public boolean getHintAmbientBigPicture() {
7802 return (mFlags & FLAG_BIG_PICTURE_AMBIENT) != 0;
7803 }
7804
7805 /**
Alex Hills9ab3a232016-04-05 14:54:56 -04007806 * Set a hint that this notification's content intent will launch an {@link Activity}
7807 * directly, telling the platform that it can generate the appropriate transitions.
7808 * @param hintContentIntentLaunchesActivity {@code true} if the content intent will launch
7809 * an activity and transitions should be generated, false otherwise.
7810 * @return this object for method chaining
7811 */
7812 public WearableExtender setHintContentIntentLaunchesActivity(
7813 boolean hintContentIntentLaunchesActivity) {
7814 setFlag(FLAG_HINT_CONTENT_INTENT_LAUNCHES_ACTIVITY, hintContentIntentLaunchesActivity);
7815 return this;
7816 }
7817
7818 /**
7819 * Get a hint that this notification's content intent will launch an {@link Activity}
7820 * directly, telling the platform that it can generate the appropriate transitions
7821 * @return {@code true} if the content intent will launch an activity and transitions should
7822 * be generated, false otherwise. The default value is {@code false} if this was never set.
7823 */
7824 public boolean getHintContentIntentLaunchesActivity() {
7825 return (mFlags & FLAG_HINT_CONTENT_INTENT_LAUNCHES_ACTIVITY) != 0;
7826 }
7827
Nadia Benbernou948627e2016-04-14 14:41:08 -04007828 /**
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04007829 * Sets the dismissal id for this notification. If a notification is posted with a
7830 * dismissal id, then when that notification is canceled, notifications on other wearables
7831 * and the paired Android phone having that same dismissal id will also be canceled. See
Nadia Benbernou948627e2016-04-14 14:41:08 -04007832 * <a href="{@docRoot}wear/notifications/index.html">Adding Wearable Features to
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04007833 * Notifications</a> for more information.
Nadia Benbernou948627e2016-04-14 14:41:08 -04007834 * @param dismissalId the dismissal id of the notification.
7835 * @return this object for method chaining
7836 */
7837 public WearableExtender setDismissalId(String dismissalId) {
7838 mDismissalId = dismissalId;
7839 return this;
7840 }
7841
7842 /**
7843 * Returns the dismissal id of the notification.
7844 * @return the dismissal id of the notification or null if it has not been set.
7845 */
7846 public String getDismissalId() {
7847 return mDismissalId;
7848 }
7849
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04007850 /**
7851 * Sets a bridge tag for this notification. A bridge tag can be set for notifications
7852 * posted from a phone to provide finer-grained control on what notifications are bridged
7853 * to wearables. See <a href="{@docRoot}wear/notifications/index.html">Adding Wearable
7854 * Features to Notifications</a> for more information.
7855 * @param bridgeTag the bridge tag of the notification.
7856 * @return this object for method chaining
7857 */
7858 public WearableExtender setBridgeTag(String bridgeTag) {
7859 mBridgeTag = bridgeTag;
7860 return this;
7861 }
7862
7863 /**
7864 * Returns the bridge tag of the notification.
7865 * @return the bridge tag or null if not present.
7866 */
7867 public String getBridgeTag() {
7868 return mBridgeTag;
7869 }
7870
Griff Hazen61a9e862014-05-22 16:05:19 -07007871 private void setFlag(int mask, boolean value) {
7872 if (value) {
7873 mFlags |= mask;
7874 } else {
7875 mFlags &= ~mask;
7876 }
7877 }
7878 }
7879
7880 /**
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08007881 * <p>Helper class to add Android Auto extensions to notifications. To create a notification
7882 * with car extensions:
7883 *
7884 * <ol>
7885 * <li>Create an {@link Notification.Builder}, setting any desired
7886 * properties.
7887 * <li>Create a {@link CarExtender}.
7888 * <li>Set car-specific properties using the {@code add} and {@code set} methods of
7889 * {@link CarExtender}.
7890 * <li>Call {@link Notification.Builder#extend(Notification.Extender)}
7891 * to apply the extensions to a notification.
7892 * </ol>
7893 *
7894 * <pre class="prettyprint">
7895 * Notification notification = new Notification.Builder(context)
7896 * ...
7897 * .extend(new CarExtender()
7898 * .set*(...))
7899 * .build();
7900 * </pre>
7901 *
7902 * <p>Car extensions can be accessed on an existing notification by using the
7903 * {@code CarExtender(Notification)} constructor, and then using the {@code get} methods
7904 * to access values.
7905 */
7906 public static final class CarExtender implements Extender {
7907 private static final String TAG = "CarExtender";
7908
7909 private static final String EXTRA_CAR_EXTENDER = "android.car.EXTENSIONS";
7910 private static final String EXTRA_LARGE_ICON = "large_icon";
7911 private static final String EXTRA_CONVERSATION = "car_conversation";
7912 private static final String EXTRA_COLOR = "app_color";
7913
7914 private Bitmap mLargeIcon;
7915 private UnreadConversation mUnreadConversation;
7916 private int mColor = Notification.COLOR_DEFAULT;
7917
7918 /**
7919 * Create a {@link CarExtender} with default options.
7920 */
7921 public CarExtender() {
7922 }
7923
7924 /**
7925 * Create a {@link CarExtender} from the CarExtender options of an existing Notification.
7926 *
7927 * @param notif The notification from which to copy options.
7928 */
7929 public CarExtender(Notification notif) {
7930 Bundle carBundle = notif.extras == null ?
7931 null : notif.extras.getBundle(EXTRA_CAR_EXTENDER);
7932 if (carBundle != null) {
7933 mLargeIcon = carBundle.getParcelable(EXTRA_LARGE_ICON);
7934 mColor = carBundle.getInt(EXTRA_COLOR, Notification.COLOR_DEFAULT);
7935
7936 Bundle b = carBundle.getBundle(EXTRA_CONVERSATION);
7937 mUnreadConversation = UnreadConversation.getUnreadConversationFromBundle(b);
7938 }
7939 }
7940
7941 /**
7942 * Apply car extensions to a notification that is being built. This is typically called by
7943 * the {@link Notification.Builder#extend(Notification.Extender)}
7944 * method of {@link Notification.Builder}.
7945 */
7946 @Override
7947 public Notification.Builder extend(Notification.Builder builder) {
7948 Bundle carExtensions = new Bundle();
7949
7950 if (mLargeIcon != null) {
7951 carExtensions.putParcelable(EXTRA_LARGE_ICON, mLargeIcon);
7952 }
7953 if (mColor != Notification.COLOR_DEFAULT) {
7954 carExtensions.putInt(EXTRA_COLOR, mColor);
7955 }
7956
7957 if (mUnreadConversation != null) {
7958 Bundle b = mUnreadConversation.getBundleForUnreadConversation();
7959 carExtensions.putBundle(EXTRA_CONVERSATION, b);
7960 }
7961
7962 builder.getExtras().putBundle(EXTRA_CAR_EXTENDER, carExtensions);
7963 return builder;
7964 }
7965
7966 /**
7967 * Sets the accent color to use when Android Auto presents the notification.
7968 *
7969 * Android Auto uses the color set with {@link Notification.Builder#setColor(int)}
7970 * to accent the displayed notification. However, not all colors are acceptable in an
7971 * automotive setting. This method can be used to override the color provided in the
7972 * notification in such a situation.
7973 */
Tor Norbye80756e32015-03-02 09:39:27 -08007974 public CarExtender setColor(@ColorInt int color) {
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08007975 mColor = color;
7976 return this;
7977 }
7978
7979 /**
7980 * Gets the accent color.
7981 *
Julia Reynoldsd9228f12015-10-20 10:37:27 -04007982 * @see #setColor
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08007983 */
Tor Norbye80756e32015-03-02 09:39:27 -08007984 @ColorInt
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08007985 public int getColor() {
7986 return mColor;
7987 }
7988
7989 /**
7990 * Sets the large icon of the car notification.
7991 *
7992 * If no large icon is set in the extender, Android Auto will display the icon
7993 * specified by {@link Notification.Builder#setLargeIcon(android.graphics.Bitmap)}
7994 *
7995 * @param largeIcon The large icon to use in the car notification.
7996 * @return This object for method chaining.
7997 */
7998 public CarExtender setLargeIcon(Bitmap largeIcon) {
7999 mLargeIcon = largeIcon;
8000 return this;
8001 }
8002
8003 /**
8004 * Gets the large icon used in this car notification, or null if no icon has been set.
8005 *
8006 * @return The large icon for the car notification.
8007 * @see CarExtender#setLargeIcon
8008 */
8009 public Bitmap getLargeIcon() {
8010 return mLargeIcon;
8011 }
8012
8013 /**
8014 * Sets the unread conversation in a message notification.
8015 *
8016 * @param unreadConversation The unread part of the conversation this notification conveys.
8017 * @return This object for method chaining.
8018 */
8019 public CarExtender setUnreadConversation(UnreadConversation unreadConversation) {
8020 mUnreadConversation = unreadConversation;
8021 return this;
8022 }
8023
8024 /**
8025 * Returns the unread conversation conveyed by this notification.
8026 * @see #setUnreadConversation(UnreadConversation)
8027 */
8028 public UnreadConversation getUnreadConversation() {
8029 return mUnreadConversation;
8030 }
8031
8032 /**
8033 * A class which holds the unread messages from a conversation.
8034 */
8035 public static class UnreadConversation {
8036 private static final String KEY_AUTHOR = "author";
8037 private static final String KEY_TEXT = "text";
8038 private static final String KEY_MESSAGES = "messages";
8039 private static final String KEY_REMOTE_INPUT = "remote_input";
8040 private static final String KEY_ON_REPLY = "on_reply";
8041 private static final String KEY_ON_READ = "on_read";
8042 private static final String KEY_PARTICIPANTS = "participants";
8043 private static final String KEY_TIMESTAMP = "timestamp";
8044
8045 private final String[] mMessages;
8046 private final RemoteInput mRemoteInput;
8047 private final PendingIntent mReplyPendingIntent;
8048 private final PendingIntent mReadPendingIntent;
8049 private final String[] mParticipants;
8050 private final long mLatestTimestamp;
8051
8052 UnreadConversation(String[] messages, RemoteInput remoteInput,
8053 PendingIntent replyPendingIntent, PendingIntent readPendingIntent,
8054 String[] participants, long latestTimestamp) {
8055 mMessages = messages;
8056 mRemoteInput = remoteInput;
8057 mReadPendingIntent = readPendingIntent;
8058 mReplyPendingIntent = replyPendingIntent;
8059 mParticipants = participants;
8060 mLatestTimestamp = latestTimestamp;
8061 }
8062
8063 /**
8064 * Gets the list of messages conveyed by this notification.
8065 */
8066 public String[] getMessages() {
8067 return mMessages;
8068 }
8069
8070 /**
8071 * Gets the remote input that will be used to convey the response to a message list, or
8072 * null if no such remote input exists.
8073 */
8074 public RemoteInput getRemoteInput() {
8075 return mRemoteInput;
8076 }
8077
8078 /**
8079 * Gets the pending intent that will be triggered when the user replies to this
8080 * notification.
8081 */
8082 public PendingIntent getReplyPendingIntent() {
8083 return mReplyPendingIntent;
8084 }
8085
8086 /**
8087 * Gets the pending intent that Android Auto will send after it reads aloud all messages
8088 * in this object's message list.
8089 */
8090 public PendingIntent getReadPendingIntent() {
8091 return mReadPendingIntent;
8092 }
8093
8094 /**
8095 * Gets the participants in the conversation.
8096 */
8097 public String[] getParticipants() {
8098 return mParticipants;
8099 }
8100
8101 /**
8102 * Gets the firs participant in the conversation.
8103 */
8104 public String getParticipant() {
8105 return mParticipants.length > 0 ? mParticipants[0] : null;
8106 }
8107
8108 /**
8109 * Gets the timestamp of the conversation.
8110 */
8111 public long getLatestTimestamp() {
8112 return mLatestTimestamp;
8113 }
8114
8115 Bundle getBundleForUnreadConversation() {
8116 Bundle b = new Bundle();
8117 String author = null;
8118 if (mParticipants != null && mParticipants.length > 1) {
8119 author = mParticipants[0];
8120 }
8121 Parcelable[] messages = new Parcelable[mMessages.length];
8122 for (int i = 0; i < messages.length; i++) {
8123 Bundle m = new Bundle();
8124 m.putString(KEY_TEXT, mMessages[i]);
8125 m.putString(KEY_AUTHOR, author);
8126 messages[i] = m;
8127 }
8128 b.putParcelableArray(KEY_MESSAGES, messages);
8129 if (mRemoteInput != null) {
8130 b.putParcelable(KEY_REMOTE_INPUT, mRemoteInput);
8131 }
8132 b.putParcelable(KEY_ON_REPLY, mReplyPendingIntent);
8133 b.putParcelable(KEY_ON_READ, mReadPendingIntent);
8134 b.putStringArray(KEY_PARTICIPANTS, mParticipants);
8135 b.putLong(KEY_TIMESTAMP, mLatestTimestamp);
8136 return b;
8137 }
8138
8139 static UnreadConversation getUnreadConversationFromBundle(Bundle b) {
8140 if (b == null) {
8141 return null;
8142 }
8143 Parcelable[] parcelableMessages = b.getParcelableArray(KEY_MESSAGES);
8144 String[] messages = null;
8145 if (parcelableMessages != null) {
8146 String[] tmp = new String[parcelableMessages.length];
8147 boolean success = true;
8148 for (int i = 0; i < tmp.length; i++) {
8149 if (!(parcelableMessages[i] instanceof Bundle)) {
8150 success = false;
8151 break;
8152 }
8153 tmp[i] = ((Bundle) parcelableMessages[i]).getString(KEY_TEXT);
8154 if (tmp[i] == null) {
8155 success = false;
8156 break;
8157 }
8158 }
8159 if (success) {
8160 messages = tmp;
8161 } else {
8162 return null;
8163 }
8164 }
8165
8166 PendingIntent onRead = b.getParcelable(KEY_ON_READ);
8167 PendingIntent onReply = b.getParcelable(KEY_ON_REPLY);
8168
8169 RemoteInput remoteInput = b.getParcelable(KEY_REMOTE_INPUT);
8170
8171 String[] participants = b.getStringArray(KEY_PARTICIPANTS);
8172 if (participants == null || participants.length != 1) {
8173 return null;
8174 }
8175
8176 return new UnreadConversation(messages,
8177 remoteInput,
8178 onReply,
8179 onRead,
8180 participants, b.getLong(KEY_TIMESTAMP));
8181 }
8182 };
8183
8184 /**
8185 * Builder class for {@link CarExtender.UnreadConversation} objects.
8186 */
8187 public static class Builder {
8188 private final List<String> mMessages = new ArrayList<String>();
8189 private final String mParticipant;
8190 private RemoteInput mRemoteInput;
8191 private PendingIntent mReadPendingIntent;
8192 private PendingIntent mReplyPendingIntent;
8193 private long mLatestTimestamp;
8194
8195 /**
8196 * Constructs a new builder for {@link CarExtender.UnreadConversation}.
8197 *
8198 * @param name The name of the other participant in the conversation.
8199 */
8200 public Builder(String name) {
8201 mParticipant = name;
8202 }
8203
8204 /**
8205 * Appends a new unread message to the list of messages for this conversation.
8206 *
8207 * The messages should be added from oldest to newest.
8208 *
8209 * @param message The text of the new unread message.
8210 * @return This object for method chaining.
8211 */
8212 public Builder addMessage(String message) {
8213 mMessages.add(message);
8214 return this;
8215 }
8216
8217 /**
8218 * Sets the pending intent and remote input which will convey the reply to this
8219 * notification.
8220 *
8221 * @param pendingIntent The pending intent which will be triggered on a reply.
8222 * @param remoteInput The remote input parcelable which will carry the reply.
8223 * @return This object for method chaining.
8224 *
8225 * @see CarExtender.UnreadConversation#getRemoteInput
8226 * @see CarExtender.UnreadConversation#getReplyPendingIntent
8227 */
8228 public Builder setReplyAction(
8229 PendingIntent pendingIntent, RemoteInput remoteInput) {
8230 mRemoteInput = remoteInput;
8231 mReplyPendingIntent = pendingIntent;
8232
8233 return this;
8234 }
8235
8236 /**
8237 * Sets the pending intent that will be sent once the messages in this notification
8238 * are read.
8239 *
8240 * @param pendingIntent The pending intent to use.
8241 * @return This object for method chaining.
8242 */
8243 public Builder setReadPendingIntent(PendingIntent pendingIntent) {
8244 mReadPendingIntent = pendingIntent;
8245 return this;
8246 }
8247
8248 /**
8249 * Sets the timestamp of the most recent message in an unread conversation.
8250 *
8251 * If a messaging notification has been posted by your application and has not
8252 * yet been cancelled, posting a later notification with the same id and tag
8253 * but without a newer timestamp may result in Android Auto not displaying a
8254 * heads up notification for the later notification.
8255 *
8256 * @param timestamp The timestamp of the most recent message in the conversation.
8257 * @return This object for method chaining.
8258 */
8259 public Builder setLatestTimestamp(long timestamp) {
8260 mLatestTimestamp = timestamp;
8261 return this;
8262 }
8263
8264 /**
8265 * Builds a new unread conversation object.
8266 *
8267 * @return The new unread conversation object.
8268 */
8269 public UnreadConversation build() {
8270 String[] messages = mMessages.toArray(new String[mMessages.size()]);
8271 String[] participants = { mParticipant };
8272 return new UnreadConversation(messages, mRemoteInput, mReplyPendingIntent,
8273 mReadPendingIntent, participants, mLatestTimestamp);
8274 }
8275 }
8276 }
8277
8278 /**
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08008279 * <p>Helper class to add Android TV extensions to notifications. To create a notification
8280 * with a TV extension:
8281 *
8282 * <ol>
8283 * <li>Create an {@link Notification.Builder}, setting any desired properties.
8284 * <li>Create a {@link TvExtender}.
8285 * <li>Set TV-specific properties using the {@code set} methods of
8286 * {@link TvExtender}.
8287 * <li>Call {@link Notification.Builder#extend(Notification.Extender)}
8288 * to apply the extension to a notification.
8289 * </ol>
8290 *
8291 * <pre class="prettyprint">
8292 * Notification notification = new Notification.Builder(context)
8293 * ...
8294 * .extend(new TvExtender()
8295 * .set*(...))
8296 * .build();
8297 * </pre>
8298 *
8299 * <p>TV extensions can be accessed on an existing notification by using the
8300 * {@code TvExtender(Notification)} constructor, and then using the {@code get} methods
8301 * to access values.
8302 *
8303 * @hide
8304 */
8305 @SystemApi
8306 public static final class TvExtender implements Extender {
8307 private static final String TAG = "TvExtender";
8308
8309 private static final String EXTRA_TV_EXTENDER = "android.tv.EXTENSIONS";
8310 private static final String EXTRA_FLAGS = "flags";
8311 private static final String EXTRA_CONTENT_INTENT = "content_intent";
8312 private static final String EXTRA_DELETE_INTENT = "delete_intent";
Dmitri Plotnikovb8a04ee2017-01-20 10:39:22 -08008313 private static final String EXTRA_CHANNEL_ID = "channel_id";
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08008314
8315 // Flags bitwise-ored to mFlags
8316 private static final int FLAG_AVAILABLE_ON_TV = 0x1;
8317
8318 private int mFlags;
Dmitri Plotnikovb8a04ee2017-01-20 10:39:22 -08008319 private String mChannelId;
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08008320 private PendingIntent mContentIntent;
8321 private PendingIntent mDeleteIntent;
8322
8323 /**
8324 * Create a {@link TvExtender} with default options.
8325 */
8326 public TvExtender() {
8327 mFlags = FLAG_AVAILABLE_ON_TV;
8328 }
8329
8330 /**
8331 * Create a {@link TvExtender} from the TvExtender options of an existing Notification.
8332 *
8333 * @param notif The notification from which to copy options.
8334 */
8335 public TvExtender(Notification notif) {
8336 Bundle bundle = notif.extras == null ?
8337 null : notif.extras.getBundle(EXTRA_TV_EXTENDER);
8338 if (bundle != null) {
8339 mFlags = bundle.getInt(EXTRA_FLAGS);
Dmitri Plotnikovb8a04ee2017-01-20 10:39:22 -08008340 mChannelId = bundle.getString(EXTRA_CHANNEL_ID);
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08008341 mContentIntent = bundle.getParcelable(EXTRA_CONTENT_INTENT);
8342 mDeleteIntent = bundle.getParcelable(EXTRA_DELETE_INTENT);
8343 }
8344 }
8345
8346 /**
8347 * Apply a TV extension to a notification that is being built. This is typically called by
8348 * the {@link Notification.Builder#extend(Notification.Extender)}
8349 * method of {@link Notification.Builder}.
8350 */
8351 @Override
8352 public Notification.Builder extend(Notification.Builder builder) {
8353 Bundle bundle = new Bundle();
8354
8355 bundle.putInt(EXTRA_FLAGS, mFlags);
Dmitri Plotnikovb8a04ee2017-01-20 10:39:22 -08008356 bundle.putString(EXTRA_CHANNEL_ID, mChannelId);
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08008357 if (mContentIntent != null) {
8358 bundle.putParcelable(EXTRA_CONTENT_INTENT, mContentIntent);
8359 }
8360
8361 if (mDeleteIntent != null) {
8362 bundle.putParcelable(EXTRA_DELETE_INTENT, mDeleteIntent);
8363 }
8364
8365 builder.getExtras().putBundle(EXTRA_TV_EXTENDER, bundle);
8366 return builder;
8367 }
8368
8369 /**
8370 * Returns true if this notification should be shown on TV. This method return true
8371 * if the notification was extended with a TvExtender.
8372 */
8373 public boolean isAvailableOnTv() {
8374 return (mFlags & FLAG_AVAILABLE_ON_TV) != 0;
8375 }
8376
8377 /**
Dmitri Plotnikovb8a04ee2017-01-20 10:39:22 -08008378 * Specifies the channel the notification should be delivered on when shown on TV.
8379 * It can be different from the channel that the notification is delivered to when
8380 * posting on a non-TV device.
8381 */
8382 public TvExtender setChannel(String channelId) {
8383 mChannelId = channelId;
8384 return this;
8385 }
8386
8387 /**
Julia Reynoldsbad42972017-04-25 13:52:49 -04008388 * Specifies the channel the notification should be delivered on when shown on TV.
8389 * It can be different from the channel that the notification is delivered to when
8390 * posting on a non-TV device.
8391 */
8392 public TvExtender setChannelId(String channelId) {
8393 mChannelId = channelId;
8394 return this;
8395 }
8396
Jeff Sharkey000ce802017-04-29 13:13:27 -06008397 /** @removed */
8398 @Deprecated
Dmitri Plotnikovb8a04ee2017-01-20 10:39:22 -08008399 public String getChannel() {
8400 return mChannelId;
8401 }
8402
8403 /**
Julia Reynoldsbad42972017-04-25 13:52:49 -04008404 * Returns the id of the channel this notification posts to on TV.
8405 */
8406 public String getChannelId() {
8407 return mChannelId;
8408 }
8409
8410 /**
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08008411 * Supplies a {@link PendingIntent} to be sent when the notification is selected on TV.
8412 * If provided, it is used instead of the content intent specified
8413 * at the level of Notification.
8414 */
8415 public TvExtender setContentIntent(PendingIntent intent) {
8416 mContentIntent = intent;
8417 return this;
8418 }
8419
8420 /**
8421 * Returns the TV-specific content intent. If this method returns null, the
8422 * main content intent on the notification should be used.
8423 *
8424 * @see {@link Notification#contentIntent}
8425 */
8426 public PendingIntent getContentIntent() {
8427 return mContentIntent;
8428 }
8429
8430 /**
8431 * Supplies a {@link PendingIntent} to send when the notification is cleared explicitly
8432 * by the user on TV. If provided, it is used instead of the delete intent specified
8433 * at the level of Notification.
8434 */
8435 public TvExtender setDeleteIntent(PendingIntent intent) {
8436 mDeleteIntent = intent;
8437 return this;
8438 }
8439
8440 /**
8441 * Returns the TV-specific delete intent. If this method returns null, the
8442 * main delete intent on the notification should be used.
8443 *
8444 * @see {@link Notification#deleteIntent}
8445 */
8446 public PendingIntent getDeleteIntent() {
8447 return mDeleteIntent;
8448 }
8449 }
8450
8451 /**
Griff Hazen61a9e862014-05-22 16:05:19 -07008452 * Get an array of Notification objects from a parcelable array bundle field.
8453 * Update the bundle to have a typed array so fetches in the future don't need
8454 * to do an array copy.
8455 */
8456 private static Notification[] getNotificationArrayFromBundle(Bundle bundle, String key) {
8457 Parcelable[] array = bundle.getParcelableArray(key);
8458 if (array instanceof Notification[] || array == null) {
8459 return (Notification[]) array;
8460 }
8461 Notification[] typedArray = Arrays.copyOf(array, array.length,
8462 Notification[].class);
8463 bundle.putParcelableArray(key, typedArray);
8464 return typedArray;
8465 }
Christoph Studer4600f9b2014-07-22 22:44:43 +02008466
8467 private static class BuilderRemoteViews extends RemoteViews {
8468 public BuilderRemoteViews(Parcel parcel) {
8469 super(parcel);
8470 }
8471
Kenny Guy77320062014-08-27 21:37:15 +01008472 public BuilderRemoteViews(ApplicationInfo appInfo, int layoutId) {
8473 super(appInfo, layoutId);
Christoph Studer4600f9b2014-07-22 22:44:43 +02008474 }
8475
8476 @Override
8477 public BuilderRemoteViews clone() {
8478 Parcel p = Parcel.obtain();
8479 writeToParcel(p, 0);
8480 p.setDataPosition(0);
8481 BuilderRemoteViews brv = new BuilderRemoteViews(p);
8482 p.recycle();
8483 return brv;
8484 }
8485 }
Adrian Roos70d7aa32017-01-11 15:39:06 -08008486
8487 private static class StandardTemplateParams {
8488 boolean hasProgress = true;
8489 boolean ambient = false;
8490 CharSequence title;
8491 CharSequence text;
8492
8493 final StandardTemplateParams reset() {
8494 hasProgress = true;
8495 ambient = false;
8496 title = null;
8497 text = null;
8498 return this;
8499 }
8500
8501 final StandardTemplateParams hasProgress(boolean hasProgress) {
8502 this.hasProgress = hasProgress;
8503 return this;
8504 }
8505
8506 final StandardTemplateParams title(CharSequence title) {
8507 this.title = title;
8508 return this;
8509 }
8510
8511 final StandardTemplateParams text(CharSequence text) {
8512 this.text = text;
8513 return this;
8514 }
8515
8516 final StandardTemplateParams ambient(boolean ambient) {
Adrian Roos0bc3f6a2017-03-06 11:54:05 -08008517 Preconditions.checkState(title == null && text == null, "must set ambient before text");
Adrian Roos70d7aa32017-01-11 15:39:06 -08008518 this.ambient = ambient;
8519 return this;
8520 }
8521
8522 final StandardTemplateParams fillTextsFrom(Builder b) {
8523 Bundle extras = b.mN.extras;
Adrian Roos0bc3f6a2017-03-06 11:54:05 -08008524 title = b.processLegacyText(extras.getCharSequence(EXTRA_TITLE), ambient);
8525 text = b.processLegacyText(extras.getCharSequence(EXTRA_TEXT), ambient);
Adrian Roos70d7aa32017-01-11 15:39:06 -08008526 return this;
8527 }
8528 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08008529}