blob: 4a794c9b8c06c611a3dbc6a31272e09d4d9f9707 [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 Cinek89991a22016-03-07 19:51:54 -080063import android.text.style.CharacterStyle;
Selim Cinek981962e2016-07-20 20:41:58 -070064import android.text.style.ForegroundColorSpan;
Selim Cinek60a54252016-02-26 17:03:25 -080065import android.text.style.RelativeSizeSpan;
66import android.text.style.TextAppearanceSpan;
Svet Ganovddb94882016-06-23 19:55:24 -070067import android.util.ArraySet;
Daniel Sandlerdcbaf662013-04-26 16:23:09 -040068import android.util.Log;
Dan Sandler50128532015-12-08 15:42:41 -050069import android.util.SparseArray;
Griff Hazen61a9e862014-05-22 16:05:19 -070070import android.view.Gravity;
Selim Cinekea4bef72015-12-02 15:51:10 -080071import android.view.NotificationHeaderView;
Joe Onorato8595a3d2010-11-19 18:12:07 -080072import android.view.View;
Selim Cinek954cc232016-05-20 13:29:23 -070073import android.view.ViewGroup;
Jeff Sharkey1c400132011-08-05 14:50:13 -070074import android.widget.ProgressBar;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080075import android.widget.RemoteViews;
76
Griff Hazen959591e2014-05-15 22:26:18 -070077import com.android.internal.R;
Selim Cinek389edcd2017-05-11 19:16:44 -070078import com.android.internal.annotations.VisibleForTesting;
Svet Ganovddb94882016-06-23 19:55:24 -070079import com.android.internal.util.ArrayUtils;
Griff Hazenc091ba82014-05-16 10:13:26 -070080import com.android.internal.util.NotificationColorUtil;
Adrian Roos0bc3f6a2017-03-06 11:54:05 -080081import com.android.internal.util.Preconditions;
Griff Hazen959591e2014-05-15 22:26:18 -070082
Tor Norbyed9273d62013-05-30 15:59:53 -070083import java.lang.annotation.Retention;
84import java.lang.annotation.RetentionPolicy;
Christoph Studer4600f9b2014-07-22 22:44:43 +020085import java.lang.reflect.Constructor;
Daniel Sandler2561b0b2012-02-13 21:04:12 -050086import java.util.ArrayList;
Griff Hazen61a9e862014-05-22 16:05:19 -070087import java.util.Arrays;
Griff Hazen5cadc3b2014-05-20 09:55:39 -070088import java.util.Collections;
Griff Hazen61a9e862014-05-22 16:05:19 -070089import java.util.List;
Dan Sandler50128532015-12-08 15:42:41 -050090import java.util.Set;
Joe Onorato561d3852010-11-20 18:09:34 -080091
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080092/**
93 * A class that represents how a persistent notification is to be presented to
94 * the user using the {@link android.app.NotificationManager}.
95 *
Joe Onoratocb109a02011-01-18 17:57:41 -080096 * <p>The {@link Notification.Builder Notification.Builder} has been added to make it
97 * easier to construct Notifications.</p>
98 *
Joe Fernandez558459f2011-10-13 16:47:36 -070099 * <div class="special reference">
100 * <h3>Developer Guides</h3>
101 * <p>For a guide to creating notifications, read the
102 * <a href="{@docRoot}guide/topics/ui/notifiers/notifications.html">Status Bar Notifications</a>
103 * developer guide.</p>
104 * </div>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800105 */
106public class Notification implements Parcelable
107{
Daniel Sandlerdcbaf662013-04-26 16:23:09 -0400108 private static final String TAG = "Notification";
109
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800110 /**
Daniel Sandler01df1c62014-06-09 10:54:01 -0400111 * An activity that provides a user interface for adjusting notification preferences for its
Julia Reynolds3aedded2017-03-31 14:42:09 -0400112 * containing application.
Daniel Sandler01df1c62014-06-09 10:54:01 -0400113 */
114 @SdkConstant(SdkConstantType.INTENT_CATEGORY)
115 public static final String INTENT_CATEGORY_NOTIFICATION_PREFERENCES
116 = "android.intent.category.NOTIFICATION_PREFERENCES";
117
118 /**
Julia Reynolds2619b5e2017-02-09 09:58:15 -0500119 * Optional extra for {@link #INTENT_CATEGORY_NOTIFICATION_PREFERENCES}. If provided, will
120 * contain a {@link NotificationChannel#getId() channel id} that can be used to narrow down
Julia Reynolds3aedded2017-03-31 14:42:09 -0400121 * what settings should be shown in the target app.
Julia Reynolds2619b5e2017-02-09 09:58:15 -0500122 */
123 public static final String EXTRA_CHANNEL_ID = "android.intent.extra.CHANNEL_ID";
124
125 /**
Julia Reynolds3aedded2017-03-31 14:42:09 -0400126 * Optional extra for {@link #INTENT_CATEGORY_NOTIFICATION_PREFERENCES}. If provided, will
127 * contain the tag provided to {@link NotificationManager#notify(String, int, Notification)}
128 * that can be used to narrow down what settings should be shown in the target app.
129 */
130 public static final String EXTRA_NOTIFICATION_TAG = "android.intent.extra.NOTIFICATION_TAG";
131
132 /**
133 * Optional extra for {@link #INTENT_CATEGORY_NOTIFICATION_PREFERENCES}. If provided, will
134 * contain the id provided to {@link NotificationManager#notify(String, int, Notification)}
135 * that can be used to narrow down what settings should be shown in the target app.
136 */
137 public static final String EXTRA_NOTIFICATION_ID = "android.intent.extra.NOTIFICATION_ID";
138
139 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800140 * Use all default values (where applicable).
141 */
142 public static final int DEFAULT_ALL = ~0;
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500143
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800144 /**
145 * Use the default notification sound. This will ignore any given
146 * {@link #sound}.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500147 *
Chris Wren47c20a12014-06-18 17:27:29 -0400148 * <p>
149 * A notification that is noisy is more likely to be presented as a heads-up notification.
150 * </p>
151 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800152 * @see #defaults
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500153 */
154
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800155 public static final int DEFAULT_SOUND = 1;
156
157 /**
158 * Use the default notification vibrate. This will ignore any given
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500159 * {@link #vibrate}. Using phone vibration requires the
Scott Mainb8b36452009-04-26 15:50:49 -0700160 * {@link android.Manifest.permission#VIBRATE VIBRATE} permission.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500161 *
Chris Wren47c20a12014-06-18 17:27:29 -0400162 * <p>
163 * A notification that vibrates is more likely to be presented as a heads-up notification.
164 * </p>
165 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800166 * @see #defaults
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500167 */
168
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800169 public static final int DEFAULT_VIBRATE = 2;
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500170
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800171 /**
172 * Use the default notification lights. This will ignore the
173 * {@link #FLAG_SHOW_LIGHTS} bit, and {@link #ledARGB}, {@link #ledOffMS}, or
174 * {@link #ledOnMS}.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500175 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800176 * @see #defaults
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500177 */
178
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800179 public static final int DEFAULT_LIGHTS = 4;
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500180
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800181 /**
Christoph Studer535ec612014-09-03 15:47:47 +0200182 * Maximum length of CharSequences accepted by Builder and friends.
183 *
184 * <p>
185 * Avoids spamming the system with overly large strings such as full e-mails.
186 */
187 private static final int MAX_CHARSEQUENCE_LENGTH = 5 * 1024;
188
189 /**
Adrian Roose458aa82015-12-08 16:17:19 -0800190 * Maximum entries of reply text that are accepted by Builder and friends.
191 */
192 private static final int MAX_REPLY_HISTORY = 5;
193
194 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500195 * A timestamp related to this notification, in milliseconds since the epoch.
Joe Malin8d40d042012-11-05 11:36:40 -0800196 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500197 * Default value: {@link System#currentTimeMillis() Now}.
198 *
199 * Choose a timestamp that will be most relevant to the user. For most finite events, this
200 * corresponds to the time the event happened (or will happen, in the case of events that have
201 * yet to occur but about which the user is being informed). Indefinite events should be
Joe Malin8d40d042012-11-05 11:36:40 -0800202 * timestamped according to when the activity began.
203 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500204 * Some examples:
Joe Malin8d40d042012-11-05 11:36:40 -0800205 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500206 * <ul>
207 * <li>Notification of a new chat message should be stamped when the message was received.</li>
208 * <li>Notification of an ongoing file download (with a progress bar, for example) should be stamped when the download started.</li>
209 * <li>Notification of a completed file download should be stamped when the download finished.</li>
210 * <li>Notification of an upcoming meeting should be stamped with the time the meeting will begin (that is, in the future).</li>
211 * <li>Notification of an ongoing stopwatch (increasing timer) should be stamped with the watch's start time.
212 * <li>Notification of an ongoing countdown timer should be stamped with the timer's end time.
Joe Malin8d40d042012-11-05 11:36:40 -0800213 * </ul>
214 *
Selim Cinek0ff1ce602016-04-05 18:27:16 -0700215 * For apps targeting {@link android.os.Build.VERSION_CODES#N} and above, this time is not shown
216 * anymore by default and must be opted into by using
217 * {@link android.app.Notification.Builder#setShowWhen(boolean)}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800218 */
219 public long when;
220
221 /**
Selim Cinekb85f36fd2016-04-20 18:46:36 -0700222 * The creation time of the notification
223 */
224 private long creationTime;
225
226 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800227 * The resource id of a drawable to use as the icon in the status bar.
Dan Sandler86647982015-05-13 23:41:13 -0400228 *
229 * @deprecated Use {@link Builder#setSmallIcon(Icon)} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800230 */
Dan Sandler86647982015-05-13 23:41:13 -0400231 @Deprecated
Tor Norbye7b9c9122013-05-30 16:48:33 -0700232 @DrawableRes
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800233 public int icon;
234
235 /**
Joe Onorato46439ce2010-11-19 13:56:21 -0800236 * If the icon in the status bar is to have more than one level, you can set this. Otherwise,
237 * leave it at its default value of 0.
238 *
239 * @see android.widget.ImageView#setImageLevel
Griff Hazen959591e2014-05-15 22:26:18 -0700240 * @see android.graphics.drawable.Drawable#setLevel
Joe Onorato46439ce2010-11-19 13:56:21 -0800241 */
242 public int iconLevel;
243
244 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500245 * The number of events that this notification represents. For example, in a new mail
246 * notification, this could be the number of unread messages.
Joe Malin8d40d042012-11-05 11:36:40 -0800247 *
Julia Reynolds30331982017-04-27 10:12:50 -0400248 * The system may or may not use this field to modify the appearance of the notification.
Julia Reynolds13d898c2017-02-02 12:22:05 -0500249 * Starting with {@link android.os.Build.VERSION_CODES#O}, the number may be displayed as a
250 * badge icon in Launchers that support badging.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800251 */
Julia Reynolds30331982017-04-27 10:12:50 -0400252 public int number = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800253
254 /**
255 * The intent to execute when the expanded status entry is clicked. If
256 * this is an activity, it must include the
257 * {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} flag, which requires
Scott Main7aee61f2011-02-08 11:25:01 -0800258 * that you take care of task management as described in the
259 * <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back
Dianne Hackborn6ceca582012-01-10 15:24:26 -0800260 * Stack</a> document. In particular, make sure to read the notification section
261 * <a href="{@docRoot}guide/topics/ui/notifiers/notifications.html#HandlingNotifications">Handling
262 * Notifications</a> for the correct ways to launch an application from a
263 * notification.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800264 */
265 public PendingIntent contentIntent;
266
267 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500268 * The intent to execute when the notification is explicitly dismissed by the user, either with
269 * the "Clear All" button or by swiping it away individually.
270 *
271 * This probably shouldn't be launching an activity since several of those will be sent
272 * at the same time.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800273 */
274 public PendingIntent deleteIntent;
275
276 /**
Dianne Hackborn170bae72010-09-03 15:14:28 -0700277 * An intent to launch instead of posting the notification to the status bar.
Joe Onoratocb109a02011-01-18 17:57:41 -0800278 *
Chris Wren47c20a12014-06-18 17:27:29 -0400279 * <p>
280 * The system UI may choose to display a heads-up notification, instead of
281 * launching this intent, while the user is using the device.
282 * </p>
283 *
Joe Onoratocb109a02011-01-18 17:57:41 -0800284 * @see Notification.Builder#setFullScreenIntent
Daniel Sandlere46cbd32010-06-17 10:35:26 -0400285 */
286 public PendingIntent fullScreenIntent;
287
288 /**
Dan Sandler5fcdf6e2014-07-18 11:31:15 -0400289 * Text that summarizes this notification for accessibility services.
290 *
291 * As of the L release, this text is no longer shown on screen, but it is still useful to
292 * accessibility services (where it serves as an audible announcement of the notification's
293 * appearance).
Joe Onoratoef1e7762010-09-17 18:38:38 -0400294 *
Joe Onorato46439ce2010-11-19 13:56:21 -0800295 * @see #tickerView
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800296 */
297 public CharSequence tickerText;
298
299 /**
Dan Sandler5fcdf6e2014-07-18 11:31:15 -0400300 * Formerly, a view showing the {@link #tickerText}.
301 *
302 * No longer displayed in the status bar as of API 21.
Joe Onoratoef1e7762010-09-17 18:38:38 -0400303 */
Dan Sandler5fcdf6e2014-07-18 11:31:15 -0400304 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -0800305 public RemoteViews tickerView;
Joe Onoratoef1e7762010-09-17 18:38:38 -0400306
307 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400308 * The view that will represent this notification in the notification list (which is pulled
309 * down from the status bar).
310 *
Julia Reynoldsd4ea7412016-02-17 14:00:56 -0500311 * As of N, this field may be null. The notification view is determined by the inputs
312 * to {@link Notification.Builder}; a custom RemoteViews can optionally be
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400313 * supplied with {@link Notification.Builder#setCustomContentView(RemoteViews)}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800314 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400315 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800316 public RemoteViews contentView;
317
Daniel Sandlera0a938c2012-03-15 08:42:37 -0400318 /**
Daniel Sandler4dfbe832012-04-11 14:51:46 -0400319 * A large-format version of {@link #contentView}, giving the Notification an
Daniel Sandlerf3b73432012-03-27 15:01:25 -0400320 * opportunity to show more detail. The system UI may choose to show this
321 * instead of the normal content view at its discretion.
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400322 *
Julia Reynoldsd4ea7412016-02-17 14:00:56 -0500323 * As of N, this field may be null. The expanded notification view is determined by the
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400324 * inputs to {@link Notification.Builder}; a custom RemoteViews can optionally be
325 * supplied with {@link Notification.Builder#setCustomBigContentView(RemoteViews)}.
Daniel Sandlerf3b73432012-03-27 15:01:25 -0400326 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400327 @Deprecated
Daniel Sandlerf3b73432012-03-27 15:01:25 -0400328 public RemoteViews bigContentView;
329
Chris Wren8fd39ec2014-02-27 17:43:26 -0500330
331 /**
Chris Wren47c20a12014-06-18 17:27:29 -0400332 * A medium-format version of {@link #contentView}, providing the Notification an
333 * opportunity to add action buttons to contentView. At its discretion, the system UI may
334 * choose to show this as a heads-up notification, which will pop up so the user can see
335 * it without leaving their current activity.
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400336 *
Julia Reynoldsd4ea7412016-02-17 14:00:56 -0500337 * As of N, this field may be null. The heads-up notification view is determined by the
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400338 * inputs to {@link Notification.Builder}; a custom RemoteViews can optionally be
339 * supplied with {@link Notification.Builder#setCustomHeadsUpContentView(RemoteViews)}.
Chris Wren8fd39ec2014-02-27 17:43:26 -0500340 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400341 @Deprecated
Chris Wren8fd39ec2014-02-27 17:43:26 -0500342 public RemoteViews headsUpContentView;
343
Daniel Sandlerf3b73432012-03-27 15:01:25 -0400344 /**
Dan Sandler86647982015-05-13 23:41:13 -0400345 * A large bitmap to be shown in the notification content area.
346 *
347 * @deprecated Use {@link Builder#setLargeIcon(Icon)} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800348 */
Dan Sandler86647982015-05-13 23:41:13 -0400349 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -0800350 public Bitmap largeIcon;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800351
352 /**
353 * The sound to play.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500354 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800355 * <p>
Chris Wren47c20a12014-06-18 17:27:29 -0400356 * A notification that is noisy is more likely to be presented as a heads-up notification.
357 * </p>
358 *
359 * <p>
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500360 * To play the default notification sound, see {@link #defaults}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800361 * </p>
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500362 * @deprecated use {@link NotificationChannel#getSound()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800363 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500364 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800365 public Uri sound;
366
367 /**
368 * Use this constant as the value for audioStreamType to request that
369 * the default stream type for notifications be used. Currently the
Jeff Sharkey098d5802012-04-26 17:30:34 -0700370 * default stream type is {@link AudioManager#STREAM_NOTIFICATION}.
John Spurlockc0650f022014-07-19 13:22:39 -0400371 *
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500372 * @deprecated Use {@link NotificationChannel#getAudioAttributes()} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800373 */
Jean-Michel Trivi81f871e2014-08-06 16:32:38 -0700374 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800375 public static final int STREAM_DEFAULT = -1;
376
377 /**
378 * The audio stream type to use when playing the sound.
379 * Should be one of the STREAM_ constants from
380 * {@link android.media.AudioManager}.
John Spurlockc0650f022014-07-19 13:22:39 -0400381 *
382 * @deprecated Use {@link #audioAttributes} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800383 */
Jean-Michel Trivi81f871e2014-08-06 16:32:38 -0700384 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800385 public int audioStreamType = STREAM_DEFAULT;
386
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800387 /**
John Spurlockc0650f022014-07-19 13:22:39 -0400388 * The default value of {@link #audioAttributes}.
389 */
390 public static final AudioAttributes AUDIO_ATTRIBUTES_DEFAULT = new AudioAttributes.Builder()
391 .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
392 .setUsage(AudioAttributes.USAGE_NOTIFICATION)
393 .build();
394
395 /**
396 * The {@link AudioAttributes audio attributes} to use when playing the sound.
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500397 *
398 * @deprecated use {@link NotificationChannel#getAudioAttributes()} instead.
John Spurlockc0650f022014-07-19 13:22:39 -0400399 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500400 @Deprecated
John Spurlockc0650f022014-07-19 13:22:39 -0400401 public AudioAttributes audioAttributes = AUDIO_ATTRIBUTES_DEFAULT;
402
403 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500404 * The pattern with which to vibrate.
405 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800406 * <p>
407 * To vibrate the default pattern, see {@link #defaults}.
408 * </p>
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500409 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800410 * @see android.os.Vibrator#vibrate(long[],int)
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500411 * @deprecated use {@link NotificationChannel#getVibrationPattern()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800412 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500413 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800414 public long[] vibrate;
415
416 /**
417 * The color of the led. The hardware will do its best approximation.
418 *
419 * @see #FLAG_SHOW_LIGHTS
420 * @see #flags
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500421 * @deprecated use {@link NotificationChannel#shouldShowLights()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800422 */
Tor Norbye80756e32015-03-02 09:39:27 -0800423 @ColorInt
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500424 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800425 public int ledARGB;
426
427 /**
428 * The number of milliseconds for the LED to be on while it's flashing.
429 * The hardware will do its best approximation.
430 *
431 * @see #FLAG_SHOW_LIGHTS
432 * @see #flags
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500433 * @deprecated use {@link NotificationChannel#shouldShowLights()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800434 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500435 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800436 public int ledOnMS;
437
438 /**
439 * The number of milliseconds for the LED to be off while it's flashing.
440 * The hardware will do its best approximation.
441 *
442 * @see #FLAG_SHOW_LIGHTS
443 * @see #flags
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500444 *
445 * @deprecated use {@link NotificationChannel#shouldShowLights()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800446 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500447 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800448 public int ledOffMS;
449
450 /**
451 * Specifies which values should be taken from the defaults.
452 * <p>
453 * To set, OR the desired from {@link #DEFAULT_SOUND},
454 * {@link #DEFAULT_VIBRATE}, {@link #DEFAULT_LIGHTS}. For all default
455 * values, use {@link #DEFAULT_ALL}.
456 * </p>
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500457 *
458 * @deprecated use {@link NotificationChannel#getSound()} and
459 * {@link NotificationChannel#shouldShowLights()} and
460 * {@link NotificationChannel#shouldVibrate()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800461 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500462 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800463 public int defaults;
464
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800465 /**
466 * Bit to be bitwise-ored into the {@link #flags} field that should be
467 * set if you want the LED on for this notification.
468 * <ul>
469 * <li>To turn the LED off, pass 0 in the alpha channel for colorARGB
470 * or 0 for both ledOnMS and ledOffMS.</li>
471 * <li>To turn the LED on, pass 1 for ledOnMS and 0 for ledOffMS.</li>
472 * <li>To flash the LED, pass the number of milliseconds that it should
473 * be on and off to ledOnMS and ledOffMS.</li>
474 * </ul>
475 * <p>
476 * Since hardware varies, you are not guaranteed that any of the values
Ricardo Loo Forondaf8f6d0a2018-01-25 08:42:43 -0800477 * you pass are honored exactly. Use the system defaults if possible
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800478 * because they will be set to values that work on any given hardware.
479 * <p>
480 * The alpha channel must be set for forward compatibility.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500481 *
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500482 * @deprecated use {@link NotificationChannel#shouldShowLights()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800483 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500484 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800485 public static final int FLAG_SHOW_LIGHTS = 0x00000001;
486
487 /**
488 * Bit to be bitwise-ored into the {@link #flags} field that should be
489 * set if this notification is in reference to something that is ongoing,
490 * like a phone call. It should not be set if this notification is in
491 * reference to something that happened at a particular point in time,
492 * like a missed phone call.
493 */
494 public static final int FLAG_ONGOING_EVENT = 0x00000002;
495
496 /**
497 * Bit to be bitwise-ored into the {@link #flags} field that if set,
Scott Mainb8b36452009-04-26 15:50:49 -0700498 * the audio will be repeated until the notification is
499 * cancelled or the notification window is opened.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800500 */
501 public static final int FLAG_INSISTENT = 0x00000004;
502
503 /**
504 * Bit to be bitwise-ored into the {@link #flags} field that should be
Griff Hazen293977b2014-04-28 08:37:20 -0700505 * set if you would only like the sound, vibrate and ticker to be played
506 * if the notification was not already showing.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800507 */
508 public static final int FLAG_ONLY_ALERT_ONCE = 0x00000008;
509
510 /**
511 * Bit to be bitwise-ored into the {@link #flags} field that should be
512 * set if the notification should be canceled when it is clicked by the
Daniel Sandler8aa9ae62012-12-04 23:31:47 -0500513 * user.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800514 */
515 public static final int FLAG_AUTO_CANCEL = 0x00000010;
516
517 /**
518 * Bit to be bitwise-ored into the {@link #flags} field that should be
519 * set if the notification should not be canceled when the user clicks
520 * the Clear all button.
521 */
522 public static final int FLAG_NO_CLEAR = 0x00000020;
523
Dianne Hackbornd8a43f62009-08-17 23:33:56 -0700524 /**
525 * Bit to be bitwise-ored into the {@link #flags} field that should be
526 * set if this notification represents a currently running service. This
527 * will normally be set for you by {@link Service#startForeground}.
528 */
529 public static final int FLAG_FOREGROUND_SERVICE = 0x00000040;
530
Daniel Sandlere46cbd32010-06-17 10:35:26 -0400531 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500532 * Obsolete flag indicating high-priority notifications; use the priority field instead.
Joe Malin8d40d042012-11-05 11:36:40 -0800533 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500534 * @deprecated Use {@link #priority} with a positive value.
Daniel Sandlere46cbd32010-06-17 10:35:26 -0400535 */
Aurimas Liutikas514c5ef2016-05-24 15:22:55 -0700536 @Deprecated
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500537 public static final int FLAG_HIGH_PRIORITY = 0x00000080;
Daniel Sandlere46cbd32010-06-17 10:35:26 -0400538
Griff Hazendfcb0802014-02-11 12:00:00 -0800539 /**
540 * Bit to be bitswise-ored into the {@link #flags} field that should be
541 * set if this notification is relevant to the current device only
542 * and it is not recommended that it bridge to other devices.
543 */
544 public static final int FLAG_LOCAL_ONLY = 0x00000100;
545
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700546 /**
547 * Bit to be bitswise-ored into the {@link #flags} field that should be
548 * set if this notification is the group summary for a group of notifications.
549 * Grouped notifications may display in a cluster or stack on devices which
550 * support such rendering. Requires a group key also be set using {@link Builder#setGroup}.
551 */
552 public static final int FLAG_GROUP_SUMMARY = 0x00000200;
553
Julia Reynoldse46bb372016-03-17 11:05:58 -0400554 /**
555 * Bit to be bitswise-ored into the {@link #flags} field that should be
556 * set if this notification is the group summary for an auto-group of notifications.
557 *
558 * @hide
559 */
560 @SystemApi
561 public static final int FLAG_AUTOGROUP_SUMMARY = 0x00000400;
562
Julia Reynolds4db59552017-06-30 13:34:01 -0400563 /**
564 * @hide
565 */
566 public static final int FLAG_CAN_COLORIZE = 0x00000800;
567
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800568 public int flags;
569
Tor Norbyed9273d62013-05-30 15:59:53 -0700570 /** @hide */
571 @IntDef({PRIORITY_DEFAULT,PRIORITY_LOW,PRIORITY_MIN,PRIORITY_HIGH,PRIORITY_MAX})
572 @Retention(RetentionPolicy.SOURCE)
573 public @interface Priority {}
574
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800575 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500576 * Default notification {@link #priority}. If your application does not prioritize its own
577 * notifications, use this value for all notifications.
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500578 *
579 * @deprecated use {@link NotificationManager#IMPORTANCE_DEFAULT} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500580 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500581 @Deprecated
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500582 public static final int PRIORITY_DEFAULT = 0;
583
584 /**
585 * Lower {@link #priority}, for items that are less important. The UI may choose to show these
586 * items smaller, or at a different position in the list, compared with your app's
587 * {@link #PRIORITY_DEFAULT} items.
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500588 *
589 * @deprecated use {@link NotificationManager#IMPORTANCE_LOW} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500590 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500591 @Deprecated
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500592 public static final int PRIORITY_LOW = -1;
593
594 /**
595 * Lowest {@link #priority}; these items might not be shown to the user except under special
596 * circumstances, such as detailed notification logs.
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500597 *
598 * @deprecated use {@link NotificationManager#IMPORTANCE_MIN} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500599 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500600 @Deprecated
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500601 public static final int PRIORITY_MIN = -2;
602
603 /**
604 * Higher {@link #priority}, for more important notifications or alerts. The UI may choose to
605 * show these items larger, or at a different position in notification lists, compared with
606 * your app's {@link #PRIORITY_DEFAULT} items.
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500607 *
608 * @deprecated use {@link NotificationManager#IMPORTANCE_HIGH} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500609 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500610 @Deprecated
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500611 public static final int PRIORITY_HIGH = 1;
612
613 /**
614 * Highest {@link #priority}, for your application's most important items that require the
615 * user's prompt attention or input.
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500616 *
617 * @deprecated use {@link NotificationManager#IMPORTANCE_HIGH} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500618 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500619 @Deprecated
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500620 public static final int PRIORITY_MAX = 2;
621
622 /**
623 * Relative priority for this notification.
Joe Malin8d40d042012-11-05 11:36:40 -0800624 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500625 * Priority is an indication of how much of the user's valuable attention should be consumed by
626 * this notification. Low-priority notifications may be hidden from the user in certain
627 * situations, while the user might be interrupted for a higher-priority notification. The
Daniel Sandler6738eee2012-11-16 12:03:32 -0500628 * system will make a determination about how to interpret this priority when presenting
629 * the notification.
Chris Wren47c20a12014-06-18 17:27:29 -0400630 *
631 * <p>
632 * A notification that is at least {@link #PRIORITY_HIGH} is more likely to be presented
633 * as a heads-up notification.
634 * </p>
635 *
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500636 * @deprecated use {@link NotificationChannel#getImportance()} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500637 */
Tor Norbyed9273d62013-05-30 15:59:53 -0700638 @Priority
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500639 @Deprecated
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500640 public int priority;
Joe Malin8d40d042012-11-05 11:36:40 -0800641
Dan Sandler26e81cf2014-05-06 10:01:27 -0400642 /**
643 * Accent color (an ARGB integer like the constants in {@link android.graphics.Color})
644 * to be applied by the standard Style templates when presenting this notification.
645 *
646 * The current template design constructs a colorful header image by overlaying the
647 * {@link #icon} image (stenciled in white) atop a field of this color. Alpha components are
648 * ignored.
649 */
Tor Norbye80756e32015-03-02 09:39:27 -0800650 @ColorInt
Dan Sandler26e81cf2014-05-06 10:01:27 -0400651 public int color = COLOR_DEFAULT;
652
653 /**
654 * Special value of {@link #color} telling the system not to decorate this notification with
655 * any special color but instead use default colors when presenting this notification.
656 */
Tor Norbye80756e32015-03-02 09:39:27 -0800657 @ColorInt
Dan Sandler26e81cf2014-05-06 10:01:27 -0400658 public static final int COLOR_DEFAULT = 0; // AKA Color.TRANSPARENT
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600659
660 /**
Adrian Roos4ff3b122016-02-01 12:26:13 -0800661 * Special value of {@link #color} used as a place holder for an invalid color.
Selim Cinek99104832017-01-25 14:47:33 -0800662 * @hide
Adrian Roos4ff3b122016-02-01 12:26:13 -0800663 */
664 @ColorInt
Selim Cinek99104832017-01-25 14:47:33 -0800665 public static final int COLOR_INVALID = 1;
Adrian Roos4ff3b122016-02-01 12:26:13 -0800666
667 /**
Adrian Roosc1a80b02016-04-05 14:54:55 -0700668 * Sphere of visibility of this notification, which affects how and when the SystemUI reveals
669 * the notification's presence and contents in untrusted situations (namely, on the secure
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600670 * lockscreen).
671 *
672 * The default level, {@link #VISIBILITY_PRIVATE}, behaves exactly as notifications have always
673 * done on Android: The notification's {@link #icon} and {@link #tickerText} (if available) are
674 * shown in all situations, but the contents are only available if the device is unlocked for
675 * the appropriate user.
676 *
677 * A more permissive policy can be expressed by {@link #VISIBILITY_PUBLIC}; such a notification
678 * can be read even in an "insecure" context (that is, above a secure lockscreen).
679 * To modify the public version of this notification—for example, to redact some portions—see
680 * {@link Builder#setPublicVersion(Notification)}.
681 *
682 * Finally, a notification can be made {@link #VISIBILITY_SECRET}, which will suppress its icon
683 * and ticker until the user has bypassed the lockscreen.
684 */
Jeff Sharkey30e06bb2017-04-24 11:18:03 -0600685 public @Visibility int visibility;
686
687 /** @hide */
688 @IntDef(prefix = { "VISIBILITY_" }, value = {
689 VISIBILITY_PUBLIC,
690 VISIBILITY_PRIVATE,
691 VISIBILITY_SECRET,
692 })
693 @Retention(RetentionPolicy.SOURCE)
694 public @interface Visibility {}
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600695
Griff Hazenfc3922d2014-08-20 11:56:44 -0700696 /**
697 * Notification visibility: Show this notification in its entirety on all lockscreens.
698 *
699 * {@see #visibility}
700 */
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600701 public static final int VISIBILITY_PUBLIC = 1;
Griff Hazenfc3922d2014-08-20 11:56:44 -0700702
703 /**
704 * Notification visibility: Show this notification on all lockscreens, but conceal sensitive or
705 * private information on secure lockscreens.
706 *
707 * {@see #visibility}
708 */
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600709 public static final int VISIBILITY_PRIVATE = 0;
Griff Hazenfc3922d2014-08-20 11:56:44 -0700710
711 /**
712 * Notification visibility: Do not reveal any part of this notification on a secure lockscreen.
713 *
714 * {@see #visibility}
715 */
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600716 public static final int VISIBILITY_SECRET = -1;
717
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500718 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400719 * Notification category: incoming call (voice or video) or similar synchronous communication request.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500720 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400721 public static final String CATEGORY_CALL = "call";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500722
723 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400724 * Notification category: incoming direct message (SMS, instant message, etc.).
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500725 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400726 public static final String CATEGORY_MESSAGE = "msg";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500727
728 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400729 * Notification category: asynchronous bulk message (email).
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500730 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400731 public static final String CATEGORY_EMAIL = "email";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500732
733 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400734 * Notification category: calendar event.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500735 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400736 public static final String CATEGORY_EVENT = "event";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500737
738 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400739 * Notification category: promotion or advertisement.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500740 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400741 public static final String CATEGORY_PROMO = "promo";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500742
743 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400744 * Notification category: alarm or timer.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500745 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400746 public static final String CATEGORY_ALARM = "alarm";
747
748 /**
749 * Notification category: progress of a long-running background operation.
750 */
751 public static final String CATEGORY_PROGRESS = "progress";
752
753 /**
754 * Notification category: social network or sharing update.
755 */
756 public static final String CATEGORY_SOCIAL = "social";
757
758 /**
759 * Notification category: error in background operation or authentication status.
760 */
761 public static final String CATEGORY_ERROR = "err";
762
763 /**
764 * Notification category: media transport control for playback.
765 */
766 public static final String CATEGORY_TRANSPORT = "transport";
767
768 /**
769 * Notification category: system or device status update. Reserved for system use.
770 */
771 public static final String CATEGORY_SYSTEM = "sys";
772
773 /**
774 * Notification category: indication of running background service.
775 */
776 public static final String CATEGORY_SERVICE = "service";
777
778 /**
John Spurlock0a69c8c2014-03-21 13:30:57 -0400779 * Notification category: a specific, timely recommendation for a single thing.
780 * For example, a news app might want to recommend a news story it believes the user will
781 * want to read next.
782 */
783 public static final String CATEGORY_RECOMMENDATION = "recommendation";
784
785 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400786 * Notification category: ongoing information about device or contextual status.
787 */
788 public static final String CATEGORY_STATUS = "status";
789
790 /**
John Spurlock24d3dad2015-04-02 12:24:02 -0400791 * Notification category: user-scheduled reminder.
792 */
793 public static final String CATEGORY_REMINDER = "reminder";
794
795 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400796 * One of the predefined notification categories (see the <code>CATEGORY_*</code> constants)
797 * that best describes this Notification. May be used by the system for ranking and filtering.
798 */
799 public String category;
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500800
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700801 private String mGroupKey;
802
803 /**
804 * Get the key used to group this notification into a cluster or stack
805 * with other notifications on devices which support such rendering.
806 */
807 public String getGroup() {
808 return mGroupKey;
809 }
810
811 private String mSortKey;
812
813 /**
814 * Get a sort key that orders this notification among other notifications from the
815 * same package. This can be useful if an external sort was already applied and an app
816 * would like to preserve this. Notifications will be sorted lexicographically using this
817 * value, although providing different priorities in addition to providing sort key may
818 * cause this value to be ignored.
819 *
820 * <p>This sort key can also be used to order members of a notification group. See
821 * {@link Builder#setGroup}.
822 *
823 * @see String#compareTo(String)
824 */
825 public String getSortKey() {
826 return mSortKey;
827 }
828
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500829 /**
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400830 * Additional semantic data to be carried around with this Notification.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400831 * <p>
832 * The extras keys defined here are intended to capture the original inputs to {@link Builder}
833 * APIs, and are intended to be used by
834 * {@link android.service.notification.NotificationListenerService} implementations to extract
835 * detailed information from notification objects.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500836 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400837 public Bundle extras = new Bundle();
838
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400839 /**
Felipe Lemedd85da62016-06-28 11:29:54 -0700840 * All pending intents in the notification as the system needs to be able to access them but
841 * touching the extras bundle in the system process is not safe because the bundle may contain
Svet Ganovddb94882016-06-23 19:55:24 -0700842 * custom parcelable objects.
843 *
844 * @hide
845 */
Felipe Lemedd85da62016-06-28 11:29:54 -0700846 public ArraySet<PendingIntent> allPendingIntents;
Svet Ganovddb94882016-06-23 19:55:24 -0700847
848 /**
Dianne Hackborn98305522017-05-05 17:53:53 -0700849 * Token identifying the notification that is applying doze/bgcheck whitelisting to the
850 * pending intents inside of it, so only those will get the behavior.
851 *
852 * @hide
853 */
854 static public IBinder whitelistToken;
855
856 /**
857 * Must be set by a process to start associating tokens with Notification objects
858 * coming in to it. This is set by NotificationManagerService.
859 *
860 * @hide
861 */
862 static public IBinder processWhitelistToken;
863
864 /**
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400865 * {@link #extras} key: this is the title of the notification,
866 * as supplied to {@link Builder#setContentTitle(CharSequence)}.
867 */
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -0500868 public static final String EXTRA_TITLE = "android.title";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400869
870 /**
871 * {@link #extras} key: this is the title of the notification when shown in expanded form,
872 * e.g. as supplied to {@link BigTextStyle#setBigContentTitle(CharSequence)}.
873 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400874 public static final String EXTRA_TITLE_BIG = EXTRA_TITLE + ".big";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400875
876 /**
877 * {@link #extras} key: this is the main text payload, as supplied to
878 * {@link Builder#setContentText(CharSequence)}.
879 */
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -0500880 public static final String EXTRA_TEXT = "android.text";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400881
882 /**
883 * {@link #extras} key: this is a third line of text, as supplied to
884 * {@link Builder#setSubText(CharSequence)}.
885 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400886 public static final String EXTRA_SUB_TEXT = "android.subText";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400887
888 /**
Adrian Roose458aa82015-12-08 16:17:19 -0800889 * {@link #extras} key: this is the remote input history, as supplied to
890 * {@link Builder#setRemoteInputHistory(CharSequence[])}.
Adrian Roos005e7742016-04-13 15:02:20 -0700891 *
892 * Apps can fill this through {@link Builder#setRemoteInputHistory(CharSequence[])}
893 * with the most recent inputs that have been sent through a {@link RemoteInput} of this
894 * Notification and are expected to clear it once the it is no longer relevant (e.g. for chat
895 * notifications once the other party has responded).
896 *
897 * The extra with this key is of type CharSequence[] and contains the most recent entry at
898 * the 0 index, the second most recent at the 1 index, etc.
899 *
900 * @see Builder#setRemoteInputHistory(CharSequence[])
Adrian Roose458aa82015-12-08 16:17:19 -0800901 */
902 public static final String EXTRA_REMOTE_INPUT_HISTORY = "android.remoteInputHistory";
903
904 /**
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400905 * {@link #extras} key: this is a small piece of additional text as supplied to
906 * {@link Builder#setContentInfo(CharSequence)}.
907 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400908 public static final String EXTRA_INFO_TEXT = "android.infoText";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400909
910 /**
911 * {@link #extras} key: this is a line of summary information intended to be shown
912 * alongside expanded notifications, as supplied to (e.g.)
913 * {@link BigTextStyle#setSummaryText(CharSequence)}.
914 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400915 public static final String EXTRA_SUMMARY_TEXT = "android.summaryText";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400916
917 /**
Christoph Studer4600f9b2014-07-22 22:44:43 +0200918 * {@link #extras} key: this is the longer text shown in the big form of a
919 * {@link BigTextStyle} notification, as supplied to
920 * {@link BigTextStyle#bigText(CharSequence)}.
921 */
922 public static final String EXTRA_BIG_TEXT = "android.bigText";
923
924 /**
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400925 * {@link #extras} key: this is the resource ID of the notification's main small icon, as
926 * supplied to {@link Builder#setSmallIcon(int)}.
Julia Reynoldse071abd2017-03-22 10:52:11 -0400927 *
928 * @deprecated Use {@link #getSmallIcon()}, which supports a wider variety of icon sources.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400929 */
Julia Reynoldse071abd2017-03-22 10:52:11 -0400930 @Deprecated
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -0500931 public static final String EXTRA_SMALL_ICON = "android.icon";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400932
933 /**
934 * {@link #extras} key: this is a bitmap to be used instead of the small icon when showing the
935 * notification payload, as
936 * supplied to {@link Builder#setLargeIcon(android.graphics.Bitmap)}.
Julia Reynoldse071abd2017-03-22 10:52:11 -0400937 *
938 * @deprecated Use {@link #getLargeIcon()}, which supports a wider variety of icon sources.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400939 */
Julia Reynoldse071abd2017-03-22 10:52:11 -0400940 @Deprecated
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400941 public static final String EXTRA_LARGE_ICON = "android.largeIcon";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400942
943 /**
944 * {@link #extras} key: this is a bitmap to be used instead of the one from
945 * {@link Builder#setLargeIcon(android.graphics.Bitmap)} when the notification is
946 * shown in its expanded form, as supplied to
947 * {@link BigPictureStyle#bigLargeIcon(android.graphics.Bitmap)}.
948 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400949 public static final String EXTRA_LARGE_ICON_BIG = EXTRA_LARGE_ICON + ".big";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400950
951 /**
952 * {@link #extras} key: this is the progress value supplied to
953 * {@link Builder#setProgress(int, int, boolean)}.
954 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400955 public static final String EXTRA_PROGRESS = "android.progress";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400956
957 /**
958 * {@link #extras} key: this is the maximum value supplied to
959 * {@link Builder#setProgress(int, int, boolean)}.
960 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400961 public static final String EXTRA_PROGRESS_MAX = "android.progressMax";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400962
963 /**
964 * {@link #extras} key: whether the progress bar is indeterminate, supplied to
965 * {@link Builder#setProgress(int, int, boolean)}.
966 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400967 public static final String EXTRA_PROGRESS_INDETERMINATE = "android.progressIndeterminate";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400968
969 /**
970 * {@link #extras} key: whether {@link #when} should be shown as a count-up timer (specifically
971 * a {@link android.widget.Chronometer}) instead of a timestamp, as supplied to
972 * {@link Builder#setUsesChronometer(boolean)}.
973 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400974 public static final String EXTRA_SHOW_CHRONOMETER = "android.showChronometer";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400975
976 /**
Selim Cinek81c23aa2016-02-25 16:23:13 -0800977 * {@link #extras} key: whether the chronometer set on the notification should count down
978 * instead of counting up. Is only relevant if key {@link #EXTRA_SHOW_CHRONOMETER} is present.
Adrian Roos96b7e202016-05-17 13:50:38 -0700979 * This extra is a boolean. The default is false.
Selim Cinek81c23aa2016-02-25 16:23:13 -0800980 */
Adrian Roos96b7e202016-05-17 13:50:38 -0700981 public static final String EXTRA_CHRONOMETER_COUNT_DOWN = "android.chronometerCountDown";
Selim Cinek81c23aa2016-02-25 16:23:13 -0800982
983 /**
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400984 * {@link #extras} key: whether {@link #when} should be shown,
985 * as supplied to {@link Builder#setShowWhen(boolean)}.
986 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400987 public static final String EXTRA_SHOW_WHEN = "android.showWhen";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400988
989 /**
990 * {@link #extras} key: this is a bitmap to be shown in {@link BigPictureStyle} expanded
991 * notifications, supplied to {@link BigPictureStyle#bigPicture(android.graphics.Bitmap)}.
992 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400993 public static final String EXTRA_PICTURE = "android.picture";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400994
995 /**
996 * {@link #extras} key: An array of CharSequences to show in {@link InboxStyle} expanded
997 * notifications, each of which was supplied to {@link InboxStyle#addLine(CharSequence)}.
998 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400999 public static final String EXTRA_TEXT_LINES = "android.textLines";
Dan Sandler842dd772014-05-15 09:36:47 -04001000
1001 /**
1002 * {@link #extras} key: A string representing the name of the specific
1003 * {@link android.app.Notification.Style} used to create this notification.
1004 */
Chris Wren91ad5632013-06-05 15:05:57 -04001005 public static final String EXTRA_TEMPLATE = "android.template";
Daniel Sandlerf45564e2013-04-15 15:05:08 -04001006
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001007 /**
Chris Wrene6c48932014-09-29 17:19:27 -04001008 * {@link #extras} key: A String array containing the people that this notification relates to,
1009 * each of which was supplied to {@link Builder#addPerson(String)}.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001010 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -04001011 public static final String EXTRA_PEOPLE = "android.people";
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001012
1013 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -04001014 * Allow certain system-generated notifications to appear before the device is provisioned.
1015 * Only available to notifications coming from the android package.
1016 * @hide
1017 */
Maurice Lam96c10032017-03-29 15:42:38 -07001018 @SystemApi
Jeff Sharkeyd86b8fe2017-06-02 17:36:26 -06001019 @RequiresPermission(android.Manifest.permission.NOTIFICATION_DURING_SETUP)
John Spurlockfd7f1e02014-03-18 16:41:57 -04001020 public static final String EXTRA_ALLOW_DURING_SETUP = "android.allowDuringSetup";
1021
1022 /**
Jose Limae9e3b3b2014-05-18 23:44:50 -07001023 * {@link #extras} key: A
1024 * {@link android.content.ContentUris content URI} pointing to an image that can be displayed
1025 * in the background when the notification is selected. The URI must point to an image stream
1026 * suitable for passing into
1027 * {@link android.graphics.BitmapFactory#decodeStream(java.io.InputStream)
1028 * BitmapFactory.decodeStream}; all other content types will be ignored. The content provider
1029 * URI used for this purpose must require no permissions to read the image data.
1030 */
1031 public static final String EXTRA_BACKGROUND_IMAGE_URI = "android.backgroundImageUri";
1032
1033 /**
Dan Sandler842dd772014-05-15 09:36:47 -04001034 * {@link #extras} key: A
Jeff Browndba34ba2014-06-24 20:46:03 -07001035 * {@link android.media.session.MediaSession.Token} associated with a
Dan Sandler842dd772014-05-15 09:36:47 -04001036 * {@link android.app.Notification.MediaStyle} notification.
1037 */
1038 public static final String EXTRA_MEDIA_SESSION = "android.mediaSession";
1039
1040 /**
Bryan Mawhinneye191f902014-07-22 12:50:09 +01001041 * {@link #extras} key: the indices of actions to be shown in the compact view,
1042 * as supplied to (e.g.) {@link MediaStyle#setShowActionsInCompactView(int...)}.
1043 */
1044 public static final String EXTRA_COMPACT_ACTIONS = "android.compactActions";
1045
Christoph Studer943aa672014-08-03 20:31:16 +02001046 /**
Alex Hillsfc737de2016-03-23 17:33:02 -04001047 * {@link #extras} key: the username to be displayed for all messages sent by the user including
1048 * direct replies
Adrian Roos96b7e202016-05-17 13:50:38 -07001049 * {@link android.app.Notification.MessagingStyle} notification. This extra is a
1050 * {@link CharSequence}
Alex Hillsfc737de2016-03-23 17:33:02 -04001051 */
1052 public static final String EXTRA_SELF_DISPLAY_NAME = "android.selfDisplayName";
1053
1054 /**
Adrian Roos96b7e202016-05-17 13:50:38 -07001055 * {@link #extras} key: a {@link CharSequence} to be displayed as the title to a conversation
Alex Hillsd9b04d92016-04-11 16:38:16 -04001056 * represented by a {@link android.app.Notification.MessagingStyle}
Alex Hillsfc737de2016-03-23 17:33:02 -04001057 */
Alex Hillsd9b04d92016-04-11 16:38:16 -04001058 public static final String EXTRA_CONVERSATION_TITLE = "android.conversationTitle";
Alex Hillsfc737de2016-03-23 17:33:02 -04001059
1060 /**
1061 * {@link #extras} key: an array of {@link android.app.Notification.MessagingStyle.Message}
1062 * bundles provided by a
Adrian Roos96b7e202016-05-17 13:50:38 -07001063 * {@link android.app.Notification.MessagingStyle} notification. This extra is a parcelable
1064 * array of bundles.
Alex Hillsfc737de2016-03-23 17:33:02 -04001065 */
1066 public static final String EXTRA_MESSAGES = "android.messages";
1067
1068 /**
Adrian Roos437cd562017-01-18 15:47:03 -08001069 * {@link #extras} key: an array of
1070 * {@link android.app.Notification.MessagingStyle#addHistoricMessage historic}
1071 * {@link android.app.Notification.MessagingStyle.Message} bundles provided by a
1072 * {@link android.app.Notification.MessagingStyle} notification. This extra is a parcelable
1073 * array of bundles.
1074 */
1075 public static final String EXTRA_HISTORIC_MESSAGES = "android.messages.historic";
1076
1077 /**
Selim Cinek7b9605b2017-01-19 17:36:00 -08001078 * {@link #extras} key: whether the notification should be colorized as
1079 * supplied to {@link Builder#setColorized(boolean)}}.
1080 */
1081 public static final String EXTRA_COLORIZED = "android.colorized";
1082
1083 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04001084 * @hide
1085 */
1086 public static final String EXTRA_BUILDER_APPLICATION_INFO = "android.appInfo";
1087
Selim Cinek247fa012016-02-18 09:50:48 -08001088 /**
1089 * @hide
1090 */
1091 public static final String EXTRA_CONTAINS_CUSTOM_VIEW = "android.contains.customView";
1092
Shane Brennan472a3b32016-12-12 15:28:10 -08001093 /**
Selim Cinekd0426622017-07-11 13:19:59 +02001094 * @hide
1095 */
1096 public static final String EXTRA_REDUCED_IMAGES = "android.reduced.images";
1097
1098 /**
Shane Brennan472a3b32016-12-12 15:28:10 -08001099 * {@link #extras} key: the audio contents of this notification.
1100 *
1101 * This is for use when rendering the notification on an audio-focused interface;
1102 * the audio contents are a complete sound sample that contains the contents/body of the
1103 * notification. This may be used in substitute of a Text-to-Speech reading of the
1104 * notification. For example if the notification represents a voice message this should point
1105 * to the audio of that message.
1106 *
1107 * The data stored under this key should be a String representation of a Uri that contains the
1108 * audio contents in one of the following formats: WAV, PCM 16-bit, AMR-WB.
1109 *
1110 * This extra is unnecessary if you are using {@code MessagingStyle} since each {@code Message}
1111 * has a field for holding data URI. That field can be used for audio.
1112 * See {@code Message#setData}.
1113 *
1114 * Example usage:
1115 * <pre>
1116 * {@code
1117 * Notification.Builder myBuilder = (build your Notification as normal);
1118 * myBuilder.getExtras().putString(EXTRA_AUDIO_CONTENTS_URI, myAudioUri.toString());
1119 * }
1120 * </pre>
1121 */
1122 public static final String EXTRA_AUDIO_CONTENTS_URI = "android.audioContents";
1123
Dan Sandler80eaa592016-04-14 23:34:54 -04001124 /** @hide */
1125 @SystemApi
Jeff Sharkeyd86b8fe2017-06-02 17:36:26 -06001126 @RequiresPermission(android.Manifest.permission.SUBSTITUTE_NOTIFICATION_APP_NAME)
Dan Sandler732bd6c2016-04-12 14:20:32 -04001127 public static final String EXTRA_SUBSTITUTE_APP_NAME = "android.substName";
1128
Dianne Hackbornfb5d4b52017-05-16 17:03:44 -07001129 /**
1130 * This is set on the notification shown by the activity manager about all apps
1131 * running in the background. It indicates that the notification should be shown
1132 * only if any of the given apps do not already have a {@link #FLAG_FOREGROUND_SERVICE}
1133 * notification currently visible to the user. This is a string array of all
1134 * package names of the apps.
1135 * @hide
1136 */
1137 public static final String EXTRA_FOREGROUND_APPS = "android.foregroundApps";
1138
Dan Sandlerd63f9322015-05-06 15:18:49 -04001139 private Icon mSmallIcon;
1140 private Icon mLargeIcon;
1141
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04001142 private String mChannelId;
Julia Reynolds2a128742016-11-28 14:29:25 -05001143 private long mTimeout;
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04001144
Julia Reynolds13d898c2017-02-02 12:22:05 -05001145 private String mShortcutId;
Julia Reynolds3aedded2017-03-31 14:42:09 -04001146 private CharSequence mSettingsText;
Julia Reynolds13d898c2017-02-02 12:22:05 -05001147
Julia Reynoldsa79c3712017-04-21 10:29:57 -04001148 /** @hide */
1149 @IntDef(prefix = { "GROUP_ALERT_" }, value = {
1150 GROUP_ALERT_ALL, GROUP_ALERT_CHILDREN, GROUP_ALERT_SUMMARY
1151 })
1152 @Retention(RetentionPolicy.SOURCE)
1153 public @interface GroupAlertBehavior {}
1154
1155 /**
1156 * Constant for {@link Builder#setGroupAlertBehavior(int)}, meaning that all notifications in a
1157 * group with sound or vibration ought to make sound or vibrate (respectively), so this
1158 * notification will not be muted when it is in a group.
1159 */
1160 public static final int GROUP_ALERT_ALL = 0;
1161
1162 /**
1163 * Constant for {@link Builder#setGroupAlertBehavior(int)}, meaning that all children
1164 * notification in a group should be silenced (no sound or vibration) even if they are posted
1165 * to a {@link NotificationChannel} that has sound and/or vibration. Use this constant to
Julia Reynolds399d9bf2017-08-11 12:52:14 -04001166 * mute this notification if this notification is a group child. This must be applied to all
1167 * children notifications you want to mute.
Julia Reynoldsa79c3712017-04-21 10:29:57 -04001168 *
1169 * <p> For example, you might want to use this constant if you post a number of children
1170 * notifications at once (say, after a periodic sync), and only need to notify the user
1171 * audibly once.
1172 */
1173 public static final int GROUP_ALERT_SUMMARY = 1;
1174
1175 /**
1176 * Constant for {@link Builder#setGroupAlertBehavior(int)}, meaning that the summary
1177 * notification in a group should be silenced (no sound or vibration) even if they are
1178 * posted to a {@link NotificationChannel} that has sound and/or vibration. Use this constant
1179 * to mute this notification if this notification is a group summary.
1180 *
1181 * <p>For example, you might want to use this constant if only the children notifications
Julia Reynolds399d9bf2017-08-11 12:52:14 -04001182 * in your group have content and the summary is only used to visually group notifications
1183 * rather than to alert the user that new information is available.
Julia Reynoldsa79c3712017-04-21 10:29:57 -04001184 */
1185 public static final int GROUP_ALERT_CHILDREN = 2;
1186
Julia Reynolds2f431e22017-06-07 14:12:09 +00001187 private int mGroupAlertBehavior = GROUP_ALERT_ALL;
Julia Reynoldsa79c3712017-04-21 10:29:57 -04001188
Julia Reynolds13d898c2017-02-02 12:22:05 -05001189 /**
1190 * If this notification is being shown as a badge, always show as a number.
1191 */
1192 public static final int BADGE_ICON_NONE = 0;
1193
1194 /**
1195 * If this notification is being shown as a badge, use the {@link #getSmallIcon()} to
1196 * represent this notification.
1197 */
1198 public static final int BADGE_ICON_SMALL = 1;
1199
1200 /**
1201 * If this notification is being shown as a badge, use the {@link #getLargeIcon()} to
1202 * represent this notification.
1203 */
1204 public static final int BADGE_ICON_LARGE = 2;
Julia Reynolds7d5b4da2017-03-20 10:18:49 -04001205 private int mBadgeIcon = BADGE_ICON_NONE;
Julia Reynolds13d898c2017-02-02 12:22:05 -05001206
Chris Wren51c75102013-07-16 20:49:17 -04001207 /**
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001208 * Structure to encapsulate a named action that can be shown as part of this notification.
1209 * It must include an icon, a label, and a {@link PendingIntent} to be fired when the action is
1210 * selected by the user.
1211 * <p>
Griff Hazen959591e2014-05-15 22:26:18 -07001212 * Apps should use {@link Notification.Builder#addAction(int, CharSequence, PendingIntent)}
1213 * or {@link Notification.Builder#addAction(Notification.Action)}
1214 * to attach actions.
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001215 */
Daniel Sandlerea2a3172013-02-20 22:24:20 -05001216 public static class Action implements Parcelable {
Shane Brennan472a3b32016-12-12 15:28:10 -08001217 /**
1218 * {@link #extras} key: Keys to a {@link Parcelable} {@link ArrayList} of
1219 * {@link RemoteInput}s.
1220 *
1221 * This is intended for {@link RemoteInput}s that only accept data, meaning
1222 * {@link RemoteInput#getAllowFreeFormInput} is false, {@link RemoteInput#getChoices}
1223 * is null or empty, and {@link RemoteInput#getAllowedDataTypes is non-null and not
1224 * empty. These {@link RemoteInput}s will be ignored by devices that do not
1225 * support non-text-based {@link RemoteInput}s. See {@link Builder#build}.
1226 *
1227 * You can test if a RemoteInput matches these constraints using
1228 * {@link RemoteInput#isDataOnly}.
1229 */
1230 private static final String EXTRA_DATA_ONLY_INPUTS = "android.extra.DATA_ONLY_INPUTS";
1231
Griff Hazen959591e2014-05-15 22:26:18 -07001232 private final Bundle mExtras;
Dan Sandler86647982015-05-13 23:41:13 -04001233 private Icon mIcon;
Griff Hazen61a9e862014-05-22 16:05:19 -07001234 private final RemoteInput[] mRemoteInputs;
Alex Hills1f27f882017-01-12 11:24:46 -05001235 private boolean mAllowGeneratedReplies = true;
Griff Hazen959591e2014-05-15 22:26:18 -07001236
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001237 /**
1238 * Small icon representing the action.
Dan Sandler86647982015-05-13 23:41:13 -04001239 *
1240 * @deprecated Use {@link Action#getIcon()} instead.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001241 */
Dan Sandler86647982015-05-13 23:41:13 -04001242 @Deprecated
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001243 public int icon;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001244
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001245 /**
1246 * Title of the action.
1247 */
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001248 public CharSequence title;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001249
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001250 /**
1251 * Intent to send when the user invokes this action. May be null, in which case the action
1252 * may be rendered in a disabled presentation by the system UI.
1253 */
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001254 public PendingIntent actionIntent;
Griff Hazen959591e2014-05-15 22:26:18 -07001255
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001256 private Action(Parcel in) {
Dan Sandler86647982015-05-13 23:41:13 -04001257 if (in.readInt() != 0) {
1258 mIcon = Icon.CREATOR.createFromParcel(in);
Dan Sandler68079d52015-07-22 10:45:30 -04001259 if (mIcon.getType() == Icon.TYPE_RESOURCE) {
1260 icon = mIcon.getResId();
1261 }
Dan Sandler86647982015-05-13 23:41:13 -04001262 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001263 title = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
1264 if (in.readInt() == 1) {
1265 actionIntent = PendingIntent.CREATOR.createFromParcel(in);
1266 }
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001267 mExtras = Bundle.setDefusable(in.readBundle(), true);
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001268 mRemoteInputs = in.createTypedArray(RemoteInput.CREATOR);
Alex Hills42b0c4d2016-04-26 13:35:36 -04001269 mAllowGeneratedReplies = in.readInt() == 1;
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001270 }
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001271
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001272 /**
Dan Sandler86647982015-05-13 23:41:13 -04001273 * @deprecated Use {@link android.app.Notification.Action.Builder}.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001274 */
Dan Sandler86647982015-05-13 23:41:13 -04001275 @Deprecated
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001276 public Action(int icon, CharSequence title, PendingIntent intent) {
Alex Hills1f27f882017-01-12 11:24:46 -05001277 this(Icon.createWithResource("", icon), title, intent, new Bundle(), null, true);
Griff Hazen959591e2014-05-15 22:26:18 -07001278 }
1279
Adrian Roos7af53622016-10-12 13:44:05 -07001280 /** Keep in sync with {@link Notification.Action.Builder#Builder(Action)}! */
Dan Sandler86647982015-05-13 23:41:13 -04001281 private Action(Icon icon, CharSequence title, PendingIntent intent, Bundle extras,
Alex Hills42b0c4d2016-04-26 13:35:36 -04001282 RemoteInput[] remoteInputs, boolean allowGeneratedReplies) {
Dan Sandler86647982015-05-13 23:41:13 -04001283 this.mIcon = icon;
Gus Prevasf5bff46f2015-08-24 10:34:28 -04001284 if (icon != null && icon.getType() == Icon.TYPE_RESOURCE) {
1285 this.icon = icon.getResId();
1286 }
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001287 this.title = title;
1288 this.actionIntent = intent;
Griff Hazen959591e2014-05-15 22:26:18 -07001289 this.mExtras = extras != null ? extras : new Bundle();
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001290 this.mRemoteInputs = remoteInputs;
Alex Hills42b0c4d2016-04-26 13:35:36 -04001291 this.mAllowGeneratedReplies = allowGeneratedReplies;
Griff Hazen959591e2014-05-15 22:26:18 -07001292 }
1293
1294 /**
Dan Sandler86647982015-05-13 23:41:13 -04001295 * Return an icon representing the action.
1296 */
1297 public Icon getIcon() {
1298 if (mIcon == null && icon != 0) {
1299 // you snuck an icon in here without using the builder; let's try to keep it
1300 mIcon = Icon.createWithResource("", icon);
1301 }
1302 return mIcon;
1303 }
1304
1305 /**
Griff Hazen959591e2014-05-15 22:26:18 -07001306 * Get additional metadata carried around with this Action.
1307 */
1308 public Bundle getExtras() {
1309 return mExtras;
1310 }
1311
1312 /**
Alex Hills42b0c4d2016-04-26 13:35:36 -04001313 * Return whether the platform should automatically generate possible replies for this
1314 * {@link Action}
1315 */
1316 public boolean getAllowGeneratedReplies() {
1317 return mAllowGeneratedReplies;
1318 }
1319
1320 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001321 * Get the list of inputs to be collected from the user when this action is sent.
Shane Brennan472a3b32016-12-12 15:28:10 -08001322 * May return null if no remote inputs were added. Only returns inputs which accept
1323 * a text input. For inputs which only accept data use {@link #getDataOnlyRemoteInputs}.
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001324 */
1325 public RemoteInput[] getRemoteInputs() {
1326 return mRemoteInputs;
1327 }
1328
1329 /**
Shane Brennan472a3b32016-12-12 15:28:10 -08001330 * Get the list of inputs to be collected from the user that ONLY accept data when this
1331 * action is sent. These remote inputs are guaranteed to return true on a call to
1332 * {@link RemoteInput#isDataOnly}.
1333 *
Shane Brennanf670d6c2017-04-25 13:05:45 -07001334 * Returns null if there are no data-only remote inputs.
Shane Brennan472a3b32016-12-12 15:28:10 -08001335 *
1336 * This method exists so that legacy RemoteInput collectors that pre-date the addition
1337 * of non-textual RemoteInputs do not access these remote inputs.
1338 */
1339 public RemoteInput[] getDataOnlyRemoteInputs() {
1340 return (RemoteInput[]) mExtras.getParcelableArray(EXTRA_DATA_ONLY_INPUTS);
1341 }
1342
1343 /**
Griff Hazen959591e2014-05-15 22:26:18 -07001344 * Builder class for {@link Action} objects.
1345 */
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001346 public static final class Builder {
Dan Sandler86647982015-05-13 23:41:13 -04001347 private final Icon mIcon;
Griff Hazen959591e2014-05-15 22:26:18 -07001348 private final CharSequence mTitle;
1349 private final PendingIntent mIntent;
Alex Hills1f27f882017-01-12 11:24:46 -05001350 private boolean mAllowGeneratedReplies = true;
Griff Hazen959591e2014-05-15 22:26:18 -07001351 private final Bundle mExtras;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001352 private ArrayList<RemoteInput> mRemoteInputs;
Griff Hazen959591e2014-05-15 22:26:18 -07001353
1354 /**
1355 * Construct a new builder for {@link Action} object.
1356 * @param icon icon to show for this action
1357 * @param title the title of the action
1358 * @param intent the {@link PendingIntent} to fire when users trigger this action
1359 */
Dan Sandler86647982015-05-13 23:41:13 -04001360 @Deprecated
Griff Hazen959591e2014-05-15 22:26:18 -07001361 public Builder(int icon, CharSequence title, PendingIntent intent) {
Adrian Roos7af53622016-10-12 13:44:05 -07001362 this(Icon.createWithResource("", icon), title, intent);
Dan Sandler86647982015-05-13 23:41:13 -04001363 }
1364
1365 /**
1366 * Construct a new builder for {@link Action} object.
1367 * @param icon icon to show for this action
1368 * @param title the title of the action
1369 * @param intent the {@link PendingIntent} to fire when users trigger this action
1370 */
1371 public Builder(Icon icon, CharSequence title, PendingIntent intent) {
Alex Hills1f27f882017-01-12 11:24:46 -05001372 this(icon, title, intent, new Bundle(), null, true);
Griff Hazen959591e2014-05-15 22:26:18 -07001373 }
1374
1375 /**
1376 * Construct a new builder for {@link Action} object using the fields from an
1377 * {@link Action}.
1378 * @param action the action to read fields from.
1379 */
1380 public Builder(Action action) {
Adrian Roos7af53622016-10-12 13:44:05 -07001381 this(action.getIcon(), action.title, action.actionIntent,
1382 new Bundle(action.mExtras), action.getRemoteInputs(),
1383 action.getAllowGeneratedReplies());
Griff Hazen959591e2014-05-15 22:26:18 -07001384 }
1385
Dan Sandler86647982015-05-13 23:41:13 -04001386 private Builder(Icon icon, CharSequence title, PendingIntent intent, Bundle extras,
Adrian Roos7af53622016-10-12 13:44:05 -07001387 RemoteInput[] remoteInputs, boolean allowGeneratedReplies) {
Griff Hazen959591e2014-05-15 22:26:18 -07001388 mIcon = icon;
1389 mTitle = title;
1390 mIntent = intent;
1391 mExtras = extras;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001392 if (remoteInputs != null) {
1393 mRemoteInputs = new ArrayList<RemoteInput>(remoteInputs.length);
1394 Collections.addAll(mRemoteInputs, remoteInputs);
1395 }
Adrian Roos7af53622016-10-12 13:44:05 -07001396 mAllowGeneratedReplies = allowGeneratedReplies;
Griff Hazen959591e2014-05-15 22:26:18 -07001397 }
1398
1399 /**
1400 * Merge additional metadata into this builder.
1401 *
1402 * <p>Values within the Bundle will replace existing extras values in this Builder.
1403 *
1404 * @see Notification.Action#extras
1405 */
1406 public Builder addExtras(Bundle extras) {
1407 if (extras != null) {
1408 mExtras.putAll(extras);
1409 }
1410 return this;
1411 }
1412
1413 /**
1414 * Get the metadata Bundle used by this Builder.
1415 *
1416 * <p>The returned Bundle is shared with this Builder.
1417 */
1418 public Bundle getExtras() {
1419 return mExtras;
1420 }
1421
1422 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001423 * Add an input to be collected from the user when this action is sent.
1424 * Response values can be retrieved from the fired intent by using the
1425 * {@link RemoteInput#getResultsFromIntent} function.
1426 * @param remoteInput a {@link RemoteInput} to add to the action
1427 * @return this object for method chaining
1428 */
1429 public Builder addRemoteInput(RemoteInput remoteInput) {
1430 if (mRemoteInputs == null) {
1431 mRemoteInputs = new ArrayList<RemoteInput>();
1432 }
1433 mRemoteInputs.add(remoteInput);
1434 return this;
1435 }
1436
1437 /**
Alex Hills42b0c4d2016-04-26 13:35:36 -04001438 * Set whether the platform should automatically generate possible replies to add to
1439 * {@link RemoteInput#getChoices()}. If the {@link Action} doesn't have a
1440 * {@link RemoteInput}, this has no effect.
1441 * @param allowGeneratedReplies {@code true} to allow generated replies, {@code false}
1442 * otherwise
1443 * @return this object for method chaining
Alex Hills1f27f882017-01-12 11:24:46 -05001444 * The default value is {@code true}
Alex Hills42b0c4d2016-04-26 13:35:36 -04001445 */
1446 public Builder setAllowGeneratedReplies(boolean allowGeneratedReplies) {
1447 mAllowGeneratedReplies = allowGeneratedReplies;
1448 return this;
1449 }
1450
1451 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001452 * Apply an extender to this action builder. Extenders may be used to add
1453 * metadata or change options on this builder.
1454 */
Griff Hazen61a9e862014-05-22 16:05:19 -07001455 public Builder extend(Extender extender) {
1456 extender.extend(this);
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001457 return this;
1458 }
1459
1460 /**
Griff Hazen959591e2014-05-15 22:26:18 -07001461 * Combine all of the options that have been set and return a new {@link Action}
1462 * object.
1463 * @return the built action
1464 */
1465 public Action build() {
Shane Brennan472a3b32016-12-12 15:28:10 -08001466 ArrayList<RemoteInput> dataOnlyInputs = new ArrayList<>();
1467 RemoteInput[] previousDataInputs =
1468 (RemoteInput[]) mExtras.getParcelableArray(EXTRA_DATA_ONLY_INPUTS);
Felipe Lemedfd11962017-01-18 18:38:46 -08001469 if (previousDataInputs != null) {
Shane Brennan472a3b32016-12-12 15:28:10 -08001470 for (RemoteInput input : previousDataInputs) {
1471 dataOnlyInputs.add(input);
1472 }
1473 }
1474 List<RemoteInput> textInputs = new ArrayList<>();
1475 if (mRemoteInputs != null) {
1476 for (RemoteInput input : mRemoteInputs) {
1477 if (input.isDataOnly()) {
1478 dataOnlyInputs.add(input);
1479 } else {
1480 textInputs.add(input);
1481 }
1482 }
1483 }
1484 if (!dataOnlyInputs.isEmpty()) {
1485 RemoteInput[] dataInputsArr =
1486 dataOnlyInputs.toArray(new RemoteInput[dataOnlyInputs.size()]);
1487 mExtras.putParcelableArray(EXTRA_DATA_ONLY_INPUTS, dataInputsArr);
1488 }
1489 RemoteInput[] textInputsArr = textInputs.isEmpty()
1490 ? null : textInputs.toArray(new RemoteInput[textInputs.size()]);
1491 return new Action(mIcon, mTitle, mIntent, mExtras, textInputsArr,
Alex Hills42b0c4d2016-04-26 13:35:36 -04001492 mAllowGeneratedReplies);
Griff Hazen959591e2014-05-15 22:26:18 -07001493 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001494 }
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001495
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001496 @Override
1497 public Action clone() {
1498 return new Action(
Dan Sandler86647982015-05-13 23:41:13 -04001499 getIcon(),
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001500 title,
1501 actionIntent, // safe to alias
Julia Reynolds53c934c2016-09-16 14:03:43 -04001502 mExtras == null ? new Bundle() : new Bundle(mExtras),
Alex Hills42b0c4d2016-04-26 13:35:36 -04001503 getRemoteInputs(),
1504 getAllowGeneratedReplies());
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001505 }
1506 @Override
1507 public int describeContents() {
1508 return 0;
1509 }
1510 @Override
1511 public void writeToParcel(Parcel out, int flags) {
Dan Sandler86647982015-05-13 23:41:13 -04001512 final Icon ic = getIcon();
1513 if (ic != null) {
1514 out.writeInt(1);
1515 ic.writeToParcel(out, 0);
1516 } else {
1517 out.writeInt(0);
1518 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001519 TextUtils.writeToParcel(title, out, flags);
1520 if (actionIntent != null) {
1521 out.writeInt(1);
1522 actionIntent.writeToParcel(out, flags);
1523 } else {
1524 out.writeInt(0);
1525 }
Griff Hazen959591e2014-05-15 22:26:18 -07001526 out.writeBundle(mExtras);
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001527 out.writeTypedArray(mRemoteInputs, flags);
Alex Hills42b0c4d2016-04-26 13:35:36 -04001528 out.writeInt(mAllowGeneratedReplies ? 1 : 0);
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001529 }
Griff Hazen959591e2014-05-15 22:26:18 -07001530 public static final Parcelable.Creator<Action> CREATOR =
1531 new Parcelable.Creator<Action>() {
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001532 public Action createFromParcel(Parcel in) {
1533 return new Action(in);
1534 }
1535 public Action[] newArray(int size) {
1536 return new Action[size];
1537 }
1538 };
Griff Hazen61a9e862014-05-22 16:05:19 -07001539
1540 /**
1541 * Extender interface for use with {@link Builder#extend}. Extenders may be used to add
1542 * metadata or change options on an action builder.
1543 */
1544 public interface Extender {
1545 /**
1546 * Apply this extender to a notification action builder.
1547 * @param builder the builder to be modified.
1548 * @return the build object for chaining.
1549 */
1550 public Builder extend(Builder builder);
1551 }
1552
1553 /**
1554 * Wearable extender for notification actions. To add extensions to an action,
1555 * create a new {@link android.app.Notification.Action.WearableExtender} object using
1556 * the {@code WearableExtender()} constructor and apply it to a
1557 * {@link android.app.Notification.Action.Builder} using
1558 * {@link android.app.Notification.Action.Builder#extend}.
1559 *
1560 * <pre class="prettyprint">
1561 * Notification.Action action = new Notification.Action.Builder(
1562 * R.drawable.archive_all, "Archive all", actionIntent)
Griff Hazen14f57992014-05-26 09:07:14 -07001563 * .extend(new Notification.Action.WearableExtender()
Griff Hazen61a9e862014-05-22 16:05:19 -07001564 * .setAvailableOffline(false))
Griff Hazen14f57992014-05-26 09:07:14 -07001565 * .build();</pre>
Griff Hazen61a9e862014-05-22 16:05:19 -07001566 */
1567 public static final class WearableExtender implements Extender {
1568 /** Notification action extra which contains wearable extensions */
1569 private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS";
1570
Pete Gastaf6781d2014-10-07 15:17:05 -04001571 // Keys within EXTRA_WEARABLE_EXTENSIONS for wearable options.
Griff Hazen61a9e862014-05-22 16:05:19 -07001572 private static final String KEY_FLAGS = "flags";
Pete Gastaf6781d2014-10-07 15:17:05 -04001573 private static final String KEY_IN_PROGRESS_LABEL = "inProgressLabel";
1574 private static final String KEY_CONFIRM_LABEL = "confirmLabel";
1575 private static final String KEY_CANCEL_LABEL = "cancelLabel";
Griff Hazen61a9e862014-05-22 16:05:19 -07001576
1577 // Flags bitwise-ored to mFlags
1578 private static final int FLAG_AVAILABLE_OFFLINE = 0x1;
Alex Hills9ab3a232016-04-05 14:54:56 -04001579 private static final int FLAG_HINT_LAUNCHES_ACTIVITY = 1 << 1;
Alex Hills9f087612016-06-07 09:08:59 -04001580 private static final int FLAG_HINT_DISPLAY_INLINE = 1 << 2;
Griff Hazen61a9e862014-05-22 16:05:19 -07001581
1582 // Default value for flags integer
1583 private static final int DEFAULT_FLAGS = FLAG_AVAILABLE_OFFLINE;
1584
1585 private int mFlags = DEFAULT_FLAGS;
1586
Pete Gastaf6781d2014-10-07 15:17:05 -04001587 private CharSequence mInProgressLabel;
1588 private CharSequence mConfirmLabel;
1589 private CharSequence mCancelLabel;
1590
Griff Hazen61a9e862014-05-22 16:05:19 -07001591 /**
1592 * Create a {@link android.app.Notification.Action.WearableExtender} with default
1593 * options.
1594 */
1595 public WearableExtender() {
1596 }
1597
1598 /**
1599 * Create a {@link android.app.Notification.Action.WearableExtender} by reading
1600 * wearable options present in an existing notification action.
1601 * @param action the notification action to inspect.
1602 */
1603 public WearableExtender(Action action) {
1604 Bundle wearableBundle = action.getExtras().getBundle(EXTRA_WEARABLE_EXTENSIONS);
1605 if (wearableBundle != null) {
1606 mFlags = wearableBundle.getInt(KEY_FLAGS, DEFAULT_FLAGS);
Pete Gastaf6781d2014-10-07 15:17:05 -04001607 mInProgressLabel = wearableBundle.getCharSequence(KEY_IN_PROGRESS_LABEL);
1608 mConfirmLabel = wearableBundle.getCharSequence(KEY_CONFIRM_LABEL);
1609 mCancelLabel = wearableBundle.getCharSequence(KEY_CANCEL_LABEL);
Griff Hazen61a9e862014-05-22 16:05:19 -07001610 }
1611 }
1612
1613 /**
1614 * Apply wearable extensions to a notification action that is being built. This is
1615 * typically called by the {@link android.app.Notification.Action.Builder#extend}
1616 * method of {@link android.app.Notification.Action.Builder}.
1617 */
1618 @Override
1619 public Action.Builder extend(Action.Builder builder) {
1620 Bundle wearableBundle = new Bundle();
1621
1622 if (mFlags != DEFAULT_FLAGS) {
1623 wearableBundle.putInt(KEY_FLAGS, mFlags);
1624 }
Pete Gastaf6781d2014-10-07 15:17:05 -04001625 if (mInProgressLabel != null) {
1626 wearableBundle.putCharSequence(KEY_IN_PROGRESS_LABEL, mInProgressLabel);
1627 }
1628 if (mConfirmLabel != null) {
1629 wearableBundle.putCharSequence(KEY_CONFIRM_LABEL, mConfirmLabel);
1630 }
1631 if (mCancelLabel != null) {
1632 wearableBundle.putCharSequence(KEY_CANCEL_LABEL, mCancelLabel);
1633 }
Griff Hazen61a9e862014-05-22 16:05:19 -07001634
1635 builder.getExtras().putBundle(EXTRA_WEARABLE_EXTENSIONS, wearableBundle);
1636 return builder;
1637 }
1638
1639 @Override
1640 public WearableExtender clone() {
1641 WearableExtender that = new WearableExtender();
1642 that.mFlags = this.mFlags;
Pete Gastaf6781d2014-10-07 15:17:05 -04001643 that.mInProgressLabel = this.mInProgressLabel;
1644 that.mConfirmLabel = this.mConfirmLabel;
1645 that.mCancelLabel = this.mCancelLabel;
Griff Hazen61a9e862014-05-22 16:05:19 -07001646 return that;
1647 }
1648
1649 /**
1650 * Set whether this action is available when the wearable device is not connected to
1651 * a companion device. The user can still trigger this action when the wearable device is
1652 * offline, but a visual hint will indicate that the action may not be available.
1653 * Defaults to true.
1654 */
1655 public WearableExtender setAvailableOffline(boolean availableOffline) {
1656 setFlag(FLAG_AVAILABLE_OFFLINE, availableOffline);
1657 return this;
1658 }
1659
1660 /**
1661 * Get whether this action is available when the wearable device is not connected to
1662 * a companion device. The user can still trigger this action when the wearable device is
1663 * offline, but a visual hint will indicate that the action may not be available.
1664 * Defaults to true.
1665 */
1666 public boolean isAvailableOffline() {
1667 return (mFlags & FLAG_AVAILABLE_OFFLINE) != 0;
1668 }
1669
1670 private void setFlag(int mask, boolean value) {
1671 if (value) {
1672 mFlags |= mask;
1673 } else {
1674 mFlags &= ~mask;
1675 }
1676 }
Pete Gastaf6781d2014-10-07 15:17:05 -04001677
1678 /**
1679 * Set a label to display while the wearable is preparing to automatically execute the
1680 * action. This is usually a 'ing' verb ending in ellipsis like "Sending..."
1681 *
1682 * @param label the label to display while the action is being prepared to execute
1683 * @return this object for method chaining
1684 */
1685 public WearableExtender setInProgressLabel(CharSequence label) {
1686 mInProgressLabel = label;
1687 return this;
1688 }
1689
1690 /**
1691 * Get the label to display while the wearable is preparing to automatically execute
1692 * the action. This is usually a 'ing' verb ending in ellipsis like "Sending..."
1693 *
1694 * @return the label to display while the action is being prepared to execute
1695 */
1696 public CharSequence getInProgressLabel() {
1697 return mInProgressLabel;
1698 }
1699
1700 /**
1701 * Set a label to display to confirm that the action should be executed.
1702 * This is usually an imperative verb like "Send".
1703 *
1704 * @param label the label to confirm the action should be executed
1705 * @return this object for method chaining
1706 */
1707 public WearableExtender setConfirmLabel(CharSequence label) {
1708 mConfirmLabel = label;
1709 return this;
1710 }
1711
1712 /**
1713 * Get the label to display to confirm that the action should be executed.
1714 * This is usually an imperative verb like "Send".
1715 *
1716 * @return the label to confirm the action should be executed
1717 */
1718 public CharSequence getConfirmLabel() {
1719 return mConfirmLabel;
1720 }
1721
1722 /**
1723 * Set a label to display to cancel the action.
1724 * This is usually an imperative verb, like "Cancel".
1725 *
1726 * @param label the label to display to cancel the action
1727 * @return this object for method chaining
1728 */
1729 public WearableExtender setCancelLabel(CharSequence label) {
1730 mCancelLabel = label;
1731 return this;
1732 }
1733
1734 /**
1735 * Get the label to display to cancel the action.
1736 * This is usually an imperative verb like "Cancel".
1737 *
1738 * @return the label to display to cancel the action
1739 */
1740 public CharSequence getCancelLabel() {
1741 return mCancelLabel;
1742 }
Alex Hills9ab3a232016-04-05 14:54:56 -04001743
1744 /**
1745 * Set a hint that this Action will launch an {@link Activity} directly, telling the
1746 * platform that it can generate the appropriate transitions.
1747 * @param hintLaunchesActivity {@code true} if the content intent will launch
1748 * an activity and transitions should be generated, false otherwise.
1749 * @return this object for method chaining
1750 */
Alex Hills4ec3ff42016-04-12 11:36:18 -04001751 public WearableExtender setHintLaunchesActivity(
Alex Hills9ab3a232016-04-05 14:54:56 -04001752 boolean hintLaunchesActivity) {
1753 setFlag(FLAG_HINT_LAUNCHES_ACTIVITY, hintLaunchesActivity);
1754 return this;
1755 }
1756
1757 /**
1758 * Get a hint that this Action will launch an {@link Activity} directly, telling the
1759 * platform that it can generate the appropriate transitions
1760 * @return {@code true} if the content intent will launch an activity and transitions
1761 * should be generated, false otherwise. The default value is {@code false} if this was
1762 * never set.
1763 */
Alex Hills4ec3ff42016-04-12 11:36:18 -04001764 public boolean getHintLaunchesActivity() {
Alex Hills9ab3a232016-04-05 14:54:56 -04001765 return (mFlags & FLAG_HINT_LAUNCHES_ACTIVITY) != 0;
1766 }
Alex Hills9f087612016-06-07 09:08:59 -04001767
1768 /**
1769 * Set a hint that this Action should be displayed inline.
1770 *
1771 * @param hintDisplayInline {@code true} if action should be displayed inline, false
1772 * otherwise
1773 * @return this object for method chaining
1774 */
1775 public WearableExtender setHintDisplayActionInline(
1776 boolean hintDisplayInline) {
1777 setFlag(FLAG_HINT_DISPLAY_INLINE, hintDisplayInline);
1778 return this;
1779 }
1780
1781 /**
1782 * Get a hint that this Action should be displayed inline.
1783 *
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08001784 * @return {@code true} if the Action should be displayed inline, {@code false}
Alex Hills9f087612016-06-07 09:08:59 -04001785 * otherwise. The default value is {@code false} if this was never set.
1786 */
1787 public boolean getHintDisplayActionInline() {
1788 return (mFlags & FLAG_HINT_DISPLAY_INLINE) != 0;
1789 }
Griff Hazen61a9e862014-05-22 16:05:19 -07001790 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001791 }
1792
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001793 /**
1794 * Array of all {@link Action} structures attached to this notification by
1795 * {@link Builder#addAction(int, CharSequence, PendingIntent)}. Mostly useful for instances of
1796 * {@link android.service.notification.NotificationListenerService} that provide an alternative
1797 * interface for invoking actions.
1798 */
Daniel Sandlerea2a3172013-02-20 22:24:20 -05001799 public Action[] actions;
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001800
1801 /**
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001802 * Replacement version of this notification whose content will be shown
1803 * in an insecure context such as atop a secure keyguard. See {@link #visibility}
1804 * and {@link #VISIBILITY_PUBLIC}.
1805 */
1806 public Notification publicVersion;
1807
1808 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001809 * Constructs a Notification object with default values.
Joe Onorato46439ce2010-11-19 13:56:21 -08001810 * You might want to consider using {@link Builder} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001811 */
1812 public Notification()
1813 {
1814 this.when = System.currentTimeMillis();
Selim Cinekb85f36fd2016-04-20 18:46:36 -07001815 this.creationTime = System.currentTimeMillis();
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001816 this.priority = PRIORITY_DEFAULT;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001817 }
1818
1819 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001820 * @hide
1821 */
1822 public Notification(Context context, int icon, CharSequence tickerText, long when,
1823 CharSequence contentTitle, CharSequence contentText, Intent contentIntent)
1824 {
Chris Wren1ce4b6d2015-06-11 10:19:43 -04001825 new Builder(context)
1826 .setWhen(when)
1827 .setSmallIcon(icon)
1828 .setTicker(tickerText)
1829 .setContentTitle(contentTitle)
1830 .setContentText(contentText)
1831 .setContentIntent(PendingIntent.getActivity(context, 0, contentIntent, 0))
1832 .buildInto(this);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001833 }
1834
1835 /**
1836 * Constructs a Notification object with the information needed to
1837 * have a status bar icon without the standard expanded view.
1838 *
1839 * @param icon The resource id of the icon to put in the status bar.
1840 * @param tickerText The text that flows by in the status bar when the notification first
1841 * activates.
1842 * @param when The time to show in the time field. In the System.currentTimeMillis
1843 * timebase.
Joe Onorato46439ce2010-11-19 13:56:21 -08001844 *
1845 * @deprecated Use {@link Builder} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001846 */
Joe Onorato46439ce2010-11-19 13:56:21 -08001847 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001848 public Notification(int icon, CharSequence tickerText, long when)
1849 {
1850 this.icon = icon;
1851 this.tickerText = tickerText;
1852 this.when = when;
Selim Cinekb85f36fd2016-04-20 18:46:36 -07001853 this.creationTime = System.currentTimeMillis();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001854 }
1855
1856 /**
1857 * Unflatten the notification from a parcel.
1858 */
Svet Ganovddb94882016-06-23 19:55:24 -07001859 @SuppressWarnings("unchecked")
1860 public Notification(Parcel parcel) {
1861 // IMPORTANT: Add unmarshaling code in readFromParcel as the pending
1862 // intents in extras are always written as the last entry.
1863 readFromParcelImpl(parcel);
1864 // Must be read last!
Felipe Lemedd85da62016-06-28 11:29:54 -07001865 allPendingIntents = (ArraySet<PendingIntent>) parcel.readArraySet(null);
Svet Ganovddb94882016-06-23 19:55:24 -07001866 }
1867
1868 private void readFromParcelImpl(Parcel parcel)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001869 {
1870 int version = parcel.readInt();
1871
Dianne Hackborn98305522017-05-05 17:53:53 -07001872 whitelistToken = parcel.readStrongBinder();
1873 if (whitelistToken == null) {
1874 whitelistToken = processWhitelistToken;
1875 }
1876 // Propagate this token to all pending intents that are unmarshalled from the parcel.
1877 parcel.setClassCookie(PendingIntent.class, whitelistToken);
1878
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001879 when = parcel.readLong();
Selim Cinekb85f36fd2016-04-20 18:46:36 -07001880 creationTime = parcel.readLong();
Dan Sandler3936e7a2015-05-19 20:59:12 -04001881 if (parcel.readInt() != 0) {
1882 mSmallIcon = Icon.CREATOR.createFromParcel(parcel);
Dan Sandler4e787062015-06-17 15:09:48 -04001883 if (mSmallIcon.getType() == Icon.TYPE_RESOURCE) {
1884 icon = mSmallIcon.getResId();
1885 }
Dan Sandler3936e7a2015-05-19 20:59:12 -04001886 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001887 number = parcel.readInt();
1888 if (parcel.readInt() != 0) {
1889 contentIntent = PendingIntent.CREATOR.createFromParcel(parcel);
1890 }
1891 if (parcel.readInt() != 0) {
1892 deleteIntent = PendingIntent.CREATOR.createFromParcel(parcel);
1893 }
1894 if (parcel.readInt() != 0) {
1895 tickerText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
1896 }
1897 if (parcel.readInt() != 0) {
Joe Onorato46439ce2010-11-19 13:56:21 -08001898 tickerView = RemoteViews.CREATOR.createFromParcel(parcel);
Joe Onoratoef1e7762010-09-17 18:38:38 -04001899 }
1900 if (parcel.readInt() != 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001901 contentView = RemoteViews.CREATOR.createFromParcel(parcel);
1902 }
Joe Onorato561d3852010-11-20 18:09:34 -08001903 if (parcel.readInt() != 0) {
Dan Sandlerd63f9322015-05-06 15:18:49 -04001904 mLargeIcon = Icon.CREATOR.createFromParcel(parcel);
Joe Onorato561d3852010-11-20 18:09:34 -08001905 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001906 defaults = parcel.readInt();
1907 flags = parcel.readInt();
1908 if (parcel.readInt() != 0) {
1909 sound = Uri.CREATOR.createFromParcel(parcel);
1910 }
1911
1912 audioStreamType = parcel.readInt();
John Spurlockc0650f022014-07-19 13:22:39 -04001913 if (parcel.readInt() != 0) {
1914 audioAttributes = AudioAttributes.CREATOR.createFromParcel(parcel);
1915 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001916 vibrate = parcel.createLongArray();
1917 ledARGB = parcel.readInt();
1918 ledOnMS = parcel.readInt();
1919 ledOffMS = parcel.readInt();
1920 iconLevel = parcel.readInt();
Daniel Sandlere46cbd32010-06-17 10:35:26 -04001921
1922 if (parcel.readInt() != 0) {
1923 fullScreenIntent = PendingIntent.CREATOR.createFromParcel(parcel);
1924 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001925
1926 priority = parcel.readInt();
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001927
John Spurlockfd7f1e02014-03-18 16:41:57 -04001928 category = parcel.readString();
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001929
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001930 mGroupKey = parcel.readString();
1931
1932 mSortKey = parcel.readString();
1933
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001934 extras = Bundle.setDefusable(parcel.readBundle(), true); // may be null
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001935
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001936 actions = parcel.createTypedArray(Action.CREATOR); // may be null
1937
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001938 if (parcel.readInt() != 0) {
Daniel Sandlerf3b73432012-03-27 15:01:25 -04001939 bigContentView = RemoteViews.CREATOR.createFromParcel(parcel);
1940 }
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001941
Chris Wren8fd39ec2014-02-27 17:43:26 -05001942 if (parcel.readInt() != 0) {
1943 headsUpContentView = RemoteViews.CREATOR.createFromParcel(parcel);
1944 }
1945
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001946 visibility = parcel.readInt();
1947
1948 if (parcel.readInt() != 0) {
1949 publicVersion = Notification.CREATOR.createFromParcel(parcel);
1950 }
Dan Sandler26e81cf2014-05-06 10:01:27 -04001951
1952 color = parcel.readInt();
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04001953
1954 if (parcel.readInt() != 0) {
1955 mChannelId = parcel.readString();
1956 }
Julia Reynolds2a128742016-11-28 14:29:25 -05001957 mTimeout = parcel.readLong();
Julia Reynolds13d898c2017-02-02 12:22:05 -05001958
1959 if (parcel.readInt() != 0) {
1960 mShortcutId = parcel.readString();
1961 }
1962
1963 mBadgeIcon = parcel.readInt();
Julia Reynolds3aedded2017-03-31 14:42:09 -04001964
1965 if (parcel.readInt() != 0) {
1966 mSettingsText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
1967 }
Julia Reynoldsa79c3712017-04-21 10:29:57 -04001968
1969 mGroupAlertBehavior = parcel.readInt();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001970 }
1971
Andy Stadler110988c2010-12-03 14:29:16 -08001972 @Override
Joe Onorato18e69df2010-05-17 22:26:12 -07001973 public Notification clone() {
1974 Notification that = new Notification();
Daniel Sandler1a497d32013-04-18 14:52:45 -04001975 cloneInto(that, true);
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001976 return that;
1977 }
Joe Onorato18e69df2010-05-17 22:26:12 -07001978
Daniel Sandler1a497d32013-04-18 14:52:45 -04001979 /**
1980 * Copy all (or if heavy is false, all except Bitmaps and RemoteViews) members
1981 * of this into that.
1982 * @hide
1983 */
1984 public void cloneInto(Notification that, boolean heavy) {
Dianne Hackborn98305522017-05-05 17:53:53 -07001985 that.whitelistToken = this.whitelistToken;
Joe Onorato18e69df2010-05-17 22:26:12 -07001986 that.when = this.when;
Selim Cinekb85f36fd2016-04-20 18:46:36 -07001987 that.creationTime = this.creationTime;
Dan Sandlerd63f9322015-05-06 15:18:49 -04001988 that.mSmallIcon = this.mSmallIcon;
Joe Onorato18e69df2010-05-17 22:26:12 -07001989 that.number = this.number;
1990
1991 // PendingIntents are global, so there's no reason (or way) to clone them.
1992 that.contentIntent = this.contentIntent;
1993 that.deleteIntent = this.deleteIntent;
Daniel Sandlere46cbd32010-06-17 10:35:26 -04001994 that.fullScreenIntent = this.fullScreenIntent;
Joe Onorato18e69df2010-05-17 22:26:12 -07001995
1996 if (this.tickerText != null) {
1997 that.tickerText = this.tickerText.toString();
1998 }
Daniel Sandler1a497d32013-04-18 14:52:45 -04001999 if (heavy && this.tickerView != null) {
Joe Onorato46439ce2010-11-19 13:56:21 -08002000 that.tickerView = this.tickerView.clone();
Joe Onoratoef1e7762010-09-17 18:38:38 -04002001 }
Daniel Sandler1a497d32013-04-18 14:52:45 -04002002 if (heavy && this.contentView != null) {
Joe Onorato18e69df2010-05-17 22:26:12 -07002003 that.contentView = this.contentView.clone();
2004 }
Dan Sandlerd63f9322015-05-06 15:18:49 -04002005 if (heavy && this.mLargeIcon != null) {
2006 that.mLargeIcon = this.mLargeIcon;
Joe Onorato561d3852010-11-20 18:09:34 -08002007 }
Jozef BABJAKa8b91832011-02-22 08:05:08 +01002008 that.iconLevel = this.iconLevel;
Joe Onorato18e69df2010-05-17 22:26:12 -07002009 that.sound = this.sound; // android.net.Uri is immutable
2010 that.audioStreamType = this.audioStreamType;
John Spurlockc0650f022014-07-19 13:22:39 -04002011 if (this.audioAttributes != null) {
2012 that.audioAttributes = new AudioAttributes.Builder(this.audioAttributes).build();
2013 }
Joe Onorato18e69df2010-05-17 22:26:12 -07002014
2015 final long[] vibrate = this.vibrate;
2016 if (vibrate != null) {
2017 final int N = vibrate.length;
2018 final long[] vib = that.vibrate = new long[N];
2019 System.arraycopy(vibrate, 0, vib, 0, N);
2020 }
2021
2022 that.ledARGB = this.ledARGB;
2023 that.ledOnMS = this.ledOnMS;
2024 that.ledOffMS = this.ledOffMS;
2025 that.defaults = this.defaults;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002026
Joe Onorato18e69df2010-05-17 22:26:12 -07002027 that.flags = this.flags;
2028
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002029 that.priority = this.priority;
Joe Malin8d40d042012-11-05 11:36:40 -08002030
John Spurlockfd7f1e02014-03-18 16:41:57 -04002031 that.category = this.category;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002032
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002033 that.mGroupKey = this.mGroupKey;
2034
2035 that.mSortKey = this.mSortKey;
2036
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002037 if (this.extras != null) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04002038 try {
2039 that.extras = new Bundle(this.extras);
2040 // will unparcel
2041 that.extras.size();
2042 } catch (BadParcelableException e) {
2043 Log.e(TAG, "could not unparcel extras from notification: " + this, e);
2044 that.extras = null;
2045 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002046 }
2047
Felipe Lemedd85da62016-06-28 11:29:54 -07002048 if (!ArrayUtils.isEmpty(allPendingIntents)) {
2049 that.allPendingIntents = new ArraySet<>(allPendingIntents);
Svet Ganovddb94882016-06-23 19:55:24 -07002050 }
2051
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002052 if (this.actions != null) {
2053 that.actions = new Action[this.actions.length];
2054 for(int i=0; i<this.actions.length; i++) {
liangweikang63b03b52017-03-16 19:22:15 +08002055 if ( this.actions[i] != null) {
2056 that.actions[i] = this.actions[i].clone();
2057 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002058 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002059 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002060
Daniel Sandler1a497d32013-04-18 14:52:45 -04002061 if (heavy && this.bigContentView != null) {
Daniel Sandlerf3b73432012-03-27 15:01:25 -04002062 that.bigContentView = this.bigContentView.clone();
2063 }
Daniel Sandler1a497d32013-04-18 14:52:45 -04002064
Chris Wren8fd39ec2014-02-27 17:43:26 -05002065 if (heavy && this.headsUpContentView != null) {
2066 that.headsUpContentView = this.headsUpContentView.clone();
2067 }
2068
Dan Sandler0bf2ed82013-12-21 23:33:41 -06002069 that.visibility = this.visibility;
2070
2071 if (this.publicVersion != null) {
2072 that.publicVersion = new Notification();
2073 this.publicVersion.cloneInto(that.publicVersion, heavy);
2074 }
2075
Dan Sandler26e81cf2014-05-06 10:01:27 -04002076 that.color = this.color;
2077
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04002078 that.mChannelId = this.mChannelId;
Julia Reynolds2a128742016-11-28 14:29:25 -05002079 that.mTimeout = this.mTimeout;
Julia Reynolds3aedded2017-03-31 14:42:09 -04002080 that.mShortcutId = this.mShortcutId;
2081 that.mBadgeIcon = this.mBadgeIcon;
2082 that.mSettingsText = this.mSettingsText;
Julia Reynoldsa79c3712017-04-21 10:29:57 -04002083 that.mGroupAlertBehavior = this.mGroupAlertBehavior;
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04002084
Daniel Sandler1a497d32013-04-18 14:52:45 -04002085 if (!heavy) {
2086 that.lightenPayload(); // will clean out extras
2087 }
2088 }
2089
2090 /**
2091 * Removes heavyweight parts of the Notification object for archival or for sending to
2092 * listeners when the full contents are not necessary.
2093 * @hide
2094 */
2095 public final void lightenPayload() {
2096 tickerView = null;
2097 contentView = null;
2098 bigContentView = null;
Chris Wren8fd39ec2014-02-27 17:43:26 -05002099 headsUpContentView = null;
Dan Sandlerd63f9322015-05-06 15:18:49 -04002100 mLargeIcon = null;
Dan Sandler50128532015-12-08 15:42:41 -05002101 if (extras != null && !extras.isEmpty()) {
2102 final Set<String> keyset = extras.keySet();
2103 final int N = keyset.size();
2104 final String[] keys = keyset.toArray(new String[N]);
2105 for (int i=0; i<N; i++) {
2106 final String key = keys[i];
Dmitri Plotnikov22281362017-01-30 11:16:21 -08002107 if (TvExtender.EXTRA_TV_EXTENDER.equals(key)) {
2108 continue;
2109 }
Dan Sandler50128532015-12-08 15:42:41 -05002110 final Object obj = extras.get(key);
2111 if (obj != null &&
2112 ( obj instanceof Parcelable
2113 || obj instanceof Parcelable[]
2114 || obj instanceof SparseArray
2115 || obj instanceof ArrayList)) {
2116 extras.remove(key);
2117 }
2118 }
Daniel Sandler1a497d32013-04-18 14:52:45 -04002119 }
Joe Onorato18e69df2010-05-17 22:26:12 -07002120 }
2121
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04002122 /**
2123 * Make sure this CharSequence is safe to put into a bundle, which basically
2124 * means it had better not be some custom Parcelable implementation.
2125 * @hide
2126 */
2127 public static CharSequence safeCharSequence(CharSequence cs) {
Christoph Studer535ec612014-09-03 15:47:47 +02002128 if (cs == null) return cs;
2129 if (cs.length() > MAX_CHARSEQUENCE_LENGTH) {
2130 cs = cs.subSequence(0, MAX_CHARSEQUENCE_LENGTH);
2131 }
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04002132 if (cs instanceof Parcelable) {
2133 Log.e(TAG, "warning: " + cs.getClass().getCanonicalName()
2134 + " instance is a custom Parcelable and not allowed in Notification");
2135 return cs.toString();
2136 }
Selim Cinek60a54252016-02-26 17:03:25 -08002137 return removeTextSizeSpans(cs);
2138 }
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04002139
Selim Cinek60a54252016-02-26 17:03:25 -08002140 private static CharSequence removeTextSizeSpans(CharSequence charSequence) {
2141 if (charSequence instanceof Spanned) {
2142 Spanned ss = (Spanned) charSequence;
2143 Object[] spans = ss.getSpans(0, ss.length(), Object.class);
2144 SpannableStringBuilder builder = new SpannableStringBuilder(ss.toString());
2145 for (Object span : spans) {
2146 Object resultSpan = span;
Selim Cinek89991a22016-03-07 19:51:54 -08002147 if (resultSpan instanceof CharacterStyle) {
2148 resultSpan = ((CharacterStyle) span).getUnderlying();
2149 }
2150 if (resultSpan instanceof TextAppearanceSpan) {
2151 TextAppearanceSpan originalSpan = (TextAppearanceSpan) resultSpan;
Selim Cinek60a54252016-02-26 17:03:25 -08002152 resultSpan = new TextAppearanceSpan(
2153 originalSpan.getFamily(),
2154 originalSpan.getTextStyle(),
2155 -1,
2156 originalSpan.getTextColor(),
2157 originalSpan.getLinkTextColor());
Selim Cinek89991a22016-03-07 19:51:54 -08002158 } else if (resultSpan instanceof RelativeSizeSpan
2159 || resultSpan instanceof AbsoluteSizeSpan) {
Selim Cinek60a54252016-02-26 17:03:25 -08002160 continue;
Selim Cinek89991a22016-03-07 19:51:54 -08002161 } else {
2162 resultSpan = span;
Selim Cinek60a54252016-02-26 17:03:25 -08002163 }
2164 builder.setSpan(resultSpan, ss.getSpanStart(span), ss.getSpanEnd(span),
2165 ss.getSpanFlags(span));
2166 }
2167 return builder;
2168 }
2169 return charSequence;
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04002170 }
2171
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002172 public int describeContents() {
2173 return 0;
2174 }
2175
2176 /**
Dan Sandler4e787062015-06-17 15:09:48 -04002177 * Flatten this notification into a parcel.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002178 */
Svet Ganovddb94882016-06-23 19:55:24 -07002179 public void writeToParcel(Parcel parcel, int flags) {
2180 // We need to mark all pending intents getting into the notification
2181 // system as being put there to later allow the notification ranker
2182 // to launch them and by doing so add the app to the battery saver white
2183 // list for a short period of time. The problem is that the system
2184 // cannot look into the extras as there may be parcelables there that
2185 // the platform does not know how to handle. To go around that we have
2186 // an explicit list of the pending intents in the extras bundle.
Felipe Lemedd85da62016-06-28 11:29:54 -07002187 final boolean collectPendingIntents = (allPendingIntents == null);
Svet Ganovddb94882016-06-23 19:55:24 -07002188 if (collectPendingIntents) {
2189 PendingIntent.setOnMarshaledListener(
2190 (PendingIntent intent, Parcel out, int outFlags) -> {
2191 if (parcel == out) {
Felipe Lemedd85da62016-06-28 11:29:54 -07002192 if (allPendingIntents == null) {
2193 allPendingIntents = new ArraySet<>();
Svet Ganovddb94882016-06-23 19:55:24 -07002194 }
Felipe Lemedd85da62016-06-28 11:29:54 -07002195 allPendingIntents.add(intent);
Svet Ganovddb94882016-06-23 19:55:24 -07002196 }
2197 });
2198 }
2199 try {
2200 // IMPORTANT: Add marshaling code in writeToParcelImpl as we
Julia Reynolds13d898c2017-02-02 12:22:05 -05002201 // want to intercept all pending events written to the parcel.
Svet Ganovddb94882016-06-23 19:55:24 -07002202 writeToParcelImpl(parcel, flags);
2203 // Must be written last!
Felipe Lemedd85da62016-06-28 11:29:54 -07002204 parcel.writeArraySet(allPendingIntents);
Svet Ganovddb94882016-06-23 19:55:24 -07002205 } finally {
2206 if (collectPendingIntents) {
2207 PendingIntent.setOnMarshaledListener(null);
2208 }
2209 }
2210 }
2211
2212 private void writeToParcelImpl(Parcel parcel, int flags) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002213 parcel.writeInt(1);
2214
Dianne Hackborn98305522017-05-05 17:53:53 -07002215 parcel.writeStrongBinder(whitelistToken);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002216 parcel.writeLong(when);
Selim Cinekb85f36fd2016-04-20 18:46:36 -07002217 parcel.writeLong(creationTime);
Dan Sandler4e787062015-06-17 15:09:48 -04002218 if (mSmallIcon == null && icon != 0) {
2219 // you snuck an icon in here without using the builder; let's try to keep it
2220 mSmallIcon = Icon.createWithResource("", icon);
2221 }
Dan Sandler3936e7a2015-05-19 20:59:12 -04002222 if (mSmallIcon != null) {
2223 parcel.writeInt(1);
2224 mSmallIcon.writeToParcel(parcel, 0);
2225 } else {
2226 parcel.writeInt(0);
2227 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002228 parcel.writeInt(number);
2229 if (contentIntent != null) {
2230 parcel.writeInt(1);
2231 contentIntent.writeToParcel(parcel, 0);
2232 } else {
2233 parcel.writeInt(0);
2234 }
2235 if (deleteIntent != null) {
2236 parcel.writeInt(1);
2237 deleteIntent.writeToParcel(parcel, 0);
2238 } else {
2239 parcel.writeInt(0);
2240 }
2241 if (tickerText != null) {
2242 parcel.writeInt(1);
2243 TextUtils.writeToParcel(tickerText, parcel, flags);
2244 } else {
2245 parcel.writeInt(0);
2246 }
Joe Onorato46439ce2010-11-19 13:56:21 -08002247 if (tickerView != null) {
Joe Onoratoef1e7762010-09-17 18:38:38 -04002248 parcel.writeInt(1);
Joe Onorato46439ce2010-11-19 13:56:21 -08002249 tickerView.writeToParcel(parcel, 0);
Joe Onoratoef1e7762010-09-17 18:38:38 -04002250 } else {
2251 parcel.writeInt(0);
2252 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002253 if (contentView != null) {
2254 parcel.writeInt(1);
2255 contentView.writeToParcel(parcel, 0);
2256 } else {
2257 parcel.writeInt(0);
2258 }
Selim Cinek279fa862016-06-14 10:57:25 -07002259 if (mLargeIcon == null && largeIcon != null) {
2260 // you snuck an icon in here without using the builder; let's try to keep it
2261 mLargeIcon = Icon.createWithBitmap(largeIcon);
2262 }
Dan Sandlerd63f9322015-05-06 15:18:49 -04002263 if (mLargeIcon != null) {
Joe Onorato561d3852010-11-20 18:09:34 -08002264 parcel.writeInt(1);
Dan Sandlerd63f9322015-05-06 15:18:49 -04002265 mLargeIcon.writeToParcel(parcel, 0);
Joe Onorato561d3852010-11-20 18:09:34 -08002266 } else {
2267 parcel.writeInt(0);
2268 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002269
2270 parcel.writeInt(defaults);
2271 parcel.writeInt(this.flags);
2272
2273 if (sound != null) {
2274 parcel.writeInt(1);
2275 sound.writeToParcel(parcel, 0);
2276 } else {
2277 parcel.writeInt(0);
2278 }
2279 parcel.writeInt(audioStreamType);
John Spurlockc0650f022014-07-19 13:22:39 -04002280
2281 if (audioAttributes != null) {
2282 parcel.writeInt(1);
2283 audioAttributes.writeToParcel(parcel, 0);
2284 } else {
2285 parcel.writeInt(0);
2286 }
2287
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002288 parcel.writeLongArray(vibrate);
2289 parcel.writeInt(ledARGB);
2290 parcel.writeInt(ledOnMS);
2291 parcel.writeInt(ledOffMS);
2292 parcel.writeInt(iconLevel);
Daniel Sandlere46cbd32010-06-17 10:35:26 -04002293
2294 if (fullScreenIntent != null) {
2295 parcel.writeInt(1);
2296 fullScreenIntent.writeToParcel(parcel, 0);
2297 } else {
2298 parcel.writeInt(0);
2299 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002300
2301 parcel.writeInt(priority);
Joe Malin8d40d042012-11-05 11:36:40 -08002302
John Spurlockfd7f1e02014-03-18 16:41:57 -04002303 parcel.writeString(category);
Joe Malin8d40d042012-11-05 11:36:40 -08002304
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002305 parcel.writeString(mGroupKey);
2306
2307 parcel.writeString(mSortKey);
2308
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002309 parcel.writeBundle(extras); // null ok
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002310
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002311 parcel.writeTypedArray(actions, 0); // null ok
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002312
Daniel Sandlerf3b73432012-03-27 15:01:25 -04002313 if (bigContentView != null) {
2314 parcel.writeInt(1);
2315 bigContentView.writeToParcel(parcel, 0);
2316 } else {
2317 parcel.writeInt(0);
2318 }
Dan Sandler0bf2ed82013-12-21 23:33:41 -06002319
Chris Wren8fd39ec2014-02-27 17:43:26 -05002320 if (headsUpContentView != null) {
2321 parcel.writeInt(1);
2322 headsUpContentView.writeToParcel(parcel, 0);
2323 } else {
2324 parcel.writeInt(0);
2325 }
2326
Dan Sandler0bf2ed82013-12-21 23:33:41 -06002327 parcel.writeInt(visibility);
2328
2329 if (publicVersion != null) {
2330 parcel.writeInt(1);
2331 publicVersion.writeToParcel(parcel, 0);
2332 } else {
2333 parcel.writeInt(0);
2334 }
Dan Sandler26e81cf2014-05-06 10:01:27 -04002335
2336 parcel.writeInt(color);
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04002337
2338 if (mChannelId != null) {
2339 parcel.writeInt(1);
2340 parcel.writeString(mChannelId);
2341 } else {
2342 parcel.writeInt(0);
2343 }
Julia Reynolds2a128742016-11-28 14:29:25 -05002344 parcel.writeLong(mTimeout);
Julia Reynolds13d898c2017-02-02 12:22:05 -05002345
2346 if (mShortcutId != null) {
2347 parcel.writeInt(1);
2348 parcel.writeString(mShortcutId);
2349 } else {
2350 parcel.writeInt(0);
2351 }
2352
2353 parcel.writeInt(mBadgeIcon);
Julia Reynolds3aedded2017-03-31 14:42:09 -04002354
2355 if (mSettingsText != null) {
2356 parcel.writeInt(1);
2357 TextUtils.writeToParcel(mSettingsText, parcel, flags);
2358 } else {
2359 parcel.writeInt(0);
2360 }
Julia Reynoldsa79c3712017-04-21 10:29:57 -04002361
2362 parcel.writeInt(mGroupAlertBehavior);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002363 }
2364
2365 /**
2366 * Parcelable.Creator that instantiates Notification objects
2367 */
2368 public static final Parcelable.Creator<Notification> CREATOR
2369 = new Parcelable.Creator<Notification>()
2370 {
2371 public Notification createFromParcel(Parcel parcel)
2372 {
2373 return new Notification(parcel);
2374 }
2375
2376 public Notification[] newArray(int size)
2377 {
2378 return new Notification[size];
2379 }
2380 };
2381
2382 /**
2383 * Sets the {@link #contentView} field to be a view with the standard "Latest Event"
2384 * layout.
2385 *
2386 * <p>Uses the {@link #icon} and {@link #when} fields to set the icon and time fields
2387 * in the view.</p>
2388 * @param context The context for your application / activity.
2389 * @param contentTitle The title that goes in the expanded entry.
2390 * @param contentText The text that goes in the expanded entry.
2391 * @param contentIntent The intent to launch when the user clicks the expanded notification.
2392 * If this is an activity, it must include the
2393 * {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} flag, which requires
Scott Main7aee61f2011-02-08 11:25:01 -08002394 * that you take care of task management as described in the
2395 * <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back
2396 * Stack</a> document.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002397 *
Joe Onorato46439ce2010-11-19 13:56:21 -08002398 * @deprecated Use {@link Builder} instead.
Chris Wrena05db382015-06-24 15:18:34 -04002399 * @removed
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002400 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002401 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002402 public void setLatestEventInfo(Context context,
2403 CharSequence contentTitle, CharSequence contentText, PendingIntent contentIntent) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002404 if (context.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1){
2405 Log.e(TAG, "setLatestEventInfo() is deprecated and you should feel deprecated.",
2406 new Throwable());
2407 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002408
Selim Cinek4ac6f602016-06-13 15:47:03 -07002409 if (context.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N) {
2410 extras.putBoolean(EXTRA_SHOW_WHEN, true);
2411 }
2412
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002413 // ensure that any information already set directly is preserved
2414 final Notification.Builder builder = new Notification.Builder(context, this);
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002415
2416 // now apply the latestEventInfo fields
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002417 if (contentTitle != null) {
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002418 builder.setContentTitle(contentTitle);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002419 }
2420 if (contentText != null) {
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002421 builder.setContentText(contentText);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002422 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002423 builder.setContentIntent(contentIntent);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002424
2425 builder.build(); // callers expect this notification to be ready to use
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002426 }
2427
Julia Reynoldsda303542015-11-23 14:00:20 -05002428 /**
2429 * @hide
2430 */
2431 public static void addFieldsFromContext(Context context, Notification notification) {
Julia Reynoldse071abd2017-03-22 10:52:11 -04002432 addFieldsFromContext(context.getApplicationInfo(), notification);
Jeff Sharkey012bc7b2016-04-11 16:30:27 -06002433 }
2434
2435 /**
2436 * @hide
2437 */
Julia Reynoldse071abd2017-03-22 10:52:11 -04002438 public static void addFieldsFromContext(ApplicationInfo ai, Notification notification) {
Jeff Sharkey012bc7b2016-04-11 16:30:27 -06002439 notification.extras.putParcelable(EXTRA_BUILDER_APPLICATION_INFO, ai);
Julia Reynoldsda303542015-11-23 14:00:20 -05002440 }
2441
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002442 @Override
2443 public String toString() {
2444 StringBuilder sb = new StringBuilder();
Julia Reynoldsb9e712e2017-04-17 10:31:03 -04002445 sb.append("Notification(channel=");
Julia Reynoldsbad42972017-04-25 13:52:49 -04002446 sb.append(getChannelId());
Julia Reynoldsb9e712e2017-04-17 10:31:03 -04002447 sb.append(" pri=");
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002448 sb.append(priority);
2449 sb.append(" contentView=");
Joe Onoratoc9596d62011-01-12 17:03:11 -08002450 if (contentView != null) {
2451 sb.append(contentView.getPackage());
2452 sb.append("/0x");
2453 sb.append(Integer.toHexString(contentView.getLayoutId()));
2454 } else {
2455 sb.append("null");
2456 }
2457 sb.append(" vibrate=");
Daniel Sandler6738eee2012-11-16 12:03:32 -05002458 if ((this.defaults & DEFAULT_VIBRATE) != 0) {
2459 sb.append("default");
2460 } else if (this.vibrate != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002461 int N = this.vibrate.length-1;
2462 sb.append("[");
2463 for (int i=0; i<N; i++) {
2464 sb.append(this.vibrate[i]);
2465 sb.append(',');
2466 }
Simon Schoar8cf97d92009-06-10 22:08:37 +02002467 if (N != -1) {
2468 sb.append(this.vibrate[N]);
2469 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002470 sb.append("]");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002471 } else {
2472 sb.append("null");
2473 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002474 sb.append(" sound=");
Daniel Sandler6738eee2012-11-16 12:03:32 -05002475 if ((this.defaults & DEFAULT_SOUND) != 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002476 sb.append("default");
Daniel Sandler6738eee2012-11-16 12:03:32 -05002477 } else if (this.sound != null) {
2478 sb.append(this.sound.toString());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002479 } else {
2480 sb.append("null");
2481 }
Chris Wren365b6d32015-07-16 10:39:26 -04002482 if (this.tickerText != null) {
2483 sb.append(" tick");
2484 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002485 sb.append(" defaults=0x");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002486 sb.append(Integer.toHexString(this.defaults));
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002487 sb.append(" flags=0x");
Daniel Sandlere46cbd32010-06-17 10:35:26 -04002488 sb.append(Integer.toHexString(this.flags));
Dan Sandler26e81cf2014-05-06 10:01:27 -04002489 sb.append(String.format(" color=0x%08x", this.color));
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002490 if (this.category != null) {
2491 sb.append(" category=");
2492 sb.append(this.category);
2493 }
2494 if (this.mGroupKey != null) {
2495 sb.append(" groupKey=");
2496 sb.append(this.mGroupKey);
2497 }
2498 if (this.mSortKey != null) {
2499 sb.append(" sortKey=");
2500 sb.append(this.mSortKey);
2501 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002502 if (actions != null) {
Dan Sandler1b718782014-07-18 12:43:45 -04002503 sb.append(" actions=");
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002504 sb.append(actions.length);
Dan Sandler1b718782014-07-18 12:43:45 -04002505 }
2506 sb.append(" vis=");
2507 sb.append(visibilityToString(this.visibility));
2508 if (this.publicVersion != null) {
2509 sb.append(" publicVersion=");
2510 sb.append(publicVersion.toString());
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002511 }
2512 sb.append(")");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002513 return sb.toString();
2514 }
Joe Onorato46439ce2010-11-19 13:56:21 -08002515
Dan Sandler1b718782014-07-18 12:43:45 -04002516 /**
2517 * {@hide}
2518 */
2519 public static String visibilityToString(int vis) {
2520 switch (vis) {
2521 case VISIBILITY_PRIVATE:
2522 return "PRIVATE";
2523 case VISIBILITY_PUBLIC:
2524 return "PUBLIC";
2525 case VISIBILITY_SECRET:
2526 return "SECRET";
2527 default:
2528 return "UNKNOWN(" + String.valueOf(vis) + ")";
2529 }
2530 }
2531
Joe Onoratocb109a02011-01-18 17:57:41 -08002532 /**
John Spurlock1d881a12015-03-18 19:21:54 -04002533 * {@hide}
2534 */
2535 public static String priorityToString(@Priority int pri) {
2536 switch (pri) {
2537 case PRIORITY_MIN:
2538 return "MIN";
2539 case PRIORITY_LOW:
2540 return "LOW";
2541 case PRIORITY_DEFAULT:
2542 return "DEFAULT";
2543 case PRIORITY_HIGH:
2544 return "HIGH";
2545 case PRIORITY_MAX:
2546 return "MAX";
2547 default:
2548 return "UNKNOWN(" + String.valueOf(pri) + ")";
2549 }
2550 }
2551
Julia Reynolds6ad0aec2017-07-05 08:47:03 -04002552 /**
2553 * @hide
2554 */
2555 public boolean hasCompletedProgress() {
2556 // not a progress notification; can't be complete
2557 if (!extras.containsKey(EXTRA_PROGRESS)
2558 || !extras.containsKey(EXTRA_PROGRESS_MAX)) {
2559 return false;
2560 }
2561 // many apps use max 0 for 'indeterminate'; not complete
2562 if (extras.getInt(EXTRA_PROGRESS_MAX) == 0) {
2563 return false;
2564 }
2565 return extras.getInt(EXTRA_PROGRESS) == extras.getInt(EXTRA_PROGRESS_MAX);
2566 }
2567
Jeff Sharkey000ce802017-04-29 13:13:27 -06002568 /** @removed */
2569 @Deprecated
Julia Reynolds37856052016-11-11 09:20:07 -05002570 public String getChannel() {
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04002571 return mChannelId;
2572 }
2573
2574 /**
Julia Reynoldsbad42972017-04-25 13:52:49 -04002575 * Returns the id of the channel this notification posts to.
2576 */
2577 public String getChannelId() {
2578 return mChannelId;
2579 }
2580
Jeff Sharkey000ce802017-04-29 13:13:27 -06002581 /** @removed */
2582 @Deprecated
Julia Reynolds2a128742016-11-28 14:29:25 -05002583 public long getTimeout() {
2584 return mTimeout;
2585 }
2586
2587 /**
Julia Reynoldsbad42972017-04-25 13:52:49 -04002588 * Returns the duration from posting after which this notification should be canceled by the
2589 * system, if it's not canceled already.
2590 */
2591 public long getTimeoutAfter() {
2592 return mTimeout;
2593 }
2594
2595 /**
Julia Reynoldse071abd2017-03-22 10:52:11 -04002596 * Returns what icon should be shown for this notification if it is being displayed in a
2597 * Launcher that supports badging. Will be one of {@link #BADGE_ICON_NONE},
2598 * {@link #BADGE_ICON_SMALL}, or {@link #BADGE_ICON_LARGE}.
2599 */
2600 public int getBadgeIconType() {
2601 return mBadgeIcon;
2602 }
2603
2604 /**
Julia Reynolds13d898c2017-02-02 12:22:05 -05002605 * Returns the {@link ShortcutInfo#getId() id} that this notification supersedes, if any.
Julia Reynoldsbad42972017-04-25 13:52:49 -04002606 *
2607 * <p>Used by some Launchers that display notification content to hide shortcuts that duplicate
2608 * notifications.
Julia Reynolds13d898c2017-02-02 12:22:05 -05002609 */
2610 public String getShortcutId() {
2611 return mShortcutId;
2612 }
2613
Julia Reynolds3aedded2017-03-31 14:42:09 -04002614
2615 /**
2616 * Returns the settings text provided to {@link Builder#setSettingsText(CharSequence)}.
2617 */
2618 public CharSequence getSettingsText() {
2619 return mSettingsText;
2620 }
2621
Julia Reynolds13d898c2017-02-02 12:22:05 -05002622 /**
Julia Reynoldsa79c3712017-04-21 10:29:57 -04002623 * Returns which type of notifications in a group are responsible for audibly alerting the
2624 * user. See {@link #GROUP_ALERT_ALL}, {@link #GROUP_ALERT_CHILDREN},
2625 * {@link #GROUP_ALERT_SUMMARY}.
2626 */
2627 public @GroupAlertBehavior int getGroupAlertBehavior() {
2628 return mGroupAlertBehavior;
2629 }
2630
2631 /**
Dan Sandlerd63f9322015-05-06 15:18:49 -04002632 * The small icon representing this notification in the status bar and content view.
2633 *
2634 * @return the small icon representing this notification.
2635 *
2636 * @see Builder#getSmallIcon()
2637 * @see Builder#setSmallIcon(Icon)
2638 */
2639 public Icon getSmallIcon() {
2640 return mSmallIcon;
2641 }
2642
2643 /**
2644 * Used when notifying to clean up legacy small icons.
2645 * @hide
2646 */
2647 public void setSmallIcon(Icon icon) {
2648 mSmallIcon = icon;
2649 }
2650
2651 /**
2652 * The large icon shown in this notification's content view.
2653 * @see Builder#getLargeIcon()
2654 * @see Builder#setLargeIcon(Icon)
2655 */
2656 public Icon getLargeIcon() {
2657 return mLargeIcon;
2658 }
2659
2660 /**
Christoph Studer4600f9b2014-07-22 22:44:43 +02002661 * @hide
2662 */
Christoph Studerc8db24b2014-07-25 17:50:30 +02002663 public boolean isGroupSummary() {
2664 return mGroupKey != null && (flags & FLAG_GROUP_SUMMARY) != 0;
2665 }
2666
2667 /**
2668 * @hide
2669 */
2670 public boolean isGroupChild() {
2671 return mGroupKey != null && (flags & FLAG_GROUP_SUMMARY) == 0;
2672 }
2673
2674 /**
Julia Reynolds30203152017-05-26 13:36:31 -04002675 * @hide
2676 */
2677 public boolean suppressAlertingDueToGrouping() {
2678 if (isGroupSummary()
2679 && getGroupAlertBehavior() == Notification.GROUP_ALERT_CHILDREN) {
2680 return true;
2681 } else if (isGroupChild()
2682 && getGroupAlertBehavior() == Notification.GROUP_ALERT_SUMMARY) {
2683 return true;
2684 }
2685 return false;
2686 }
2687
2688 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002689 * Builder class for {@link Notification} objects.
Joe Malin8d40d042012-11-05 11:36:40 -08002690 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002691 * Provides a convenient way to set the various fields of a {@link Notification} and generate
Scott Main183bf112012-08-13 19:12:13 -07002692 * content views using the platform's notification layout template. If your app supports
2693 * versions of Android as old as API level 4, you can instead use
2694 * {@link android.support.v4.app.NotificationCompat.Builder NotificationCompat.Builder},
2695 * available in the <a href="{@docRoot}tools/extras/support-library.html">Android Support
2696 * library</a>.
Joe Malin8d40d042012-11-05 11:36:40 -08002697 *
Scott Main183bf112012-08-13 19:12:13 -07002698 * <p>Example:
Joe Malin8d40d042012-11-05 11:36:40 -08002699 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002700 * <pre class="prettyprint">
Scott Main183bf112012-08-13 19:12:13 -07002701 * Notification noti = new Notification.Builder(mContext)
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002702 * .setContentTitle(&quot;New mail from &quot; + sender.toString())
2703 * .setContentText(subject)
2704 * .setSmallIcon(R.drawable.new_mail)
2705 * .setLargeIcon(aBitmap)
Chris Wrenfbd96ba2012-05-01 12:03:58 -04002706 * .build();
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002707 * </pre>
Joe Onoratocb109a02011-01-18 17:57:41 -08002708 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002709 public static class Builder {
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05002710 /**
2711 * @hide
2712 */
2713 public static final String EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT =
2714 "android.rebuild.contentViewActionCount";
2715 /**
2716 * @hide
2717 */
2718 public static final String EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT
2719 = "android.rebuild.bigViewActionCount";
2720 /**
2721 * @hide
2722 */
2723 public static final String EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT
2724 = "android.rebuild.hudViewActionCount";
2725
Daniel Sandler602ad1c2012-06-12 16:06:27 -04002726 private static final int MAX_ACTION_BUTTONS = 3;
Daniel Sandler8680bf82012-05-15 16:52:52 -04002727
Selim Cinek6743c0b2017-01-18 18:24:01 -08002728 private static final boolean USE_ONLY_TITLE_IN_LOW_PRIORITY_SUMMARY =
2729 SystemProperties.getBoolean("notifications.only_title", true);
2730
Selim Cinek389edcd2017-05-11 19:16:44 -07002731 /**
2732 * The lightness difference that has to be added to the primary text color to obtain the
2733 * secondary text color when the background is light.
2734 */
2735 private static final int LIGHTNESS_TEXT_DIFFERENCE_LIGHT = 20;
2736
2737 /**
2738 * The lightness difference that has to be added to the primary text color to obtain the
2739 * secondary text color when the background is dark.
2740 * A bit less then the above value, since it looks better on dark backgrounds.
2741 */
2742 private static final int LIGHTNESS_TEXT_DIFFERENCE_DARK = -10;
2743
Joe Onorato46439ce2010-11-19 13:56:21 -08002744 private Context mContext;
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002745 private Notification mN;
2746 private Bundle mUserExtras = new Bundle();
Chris Wrenfbd96ba2012-05-01 12:03:58 -04002747 private Style mStyle;
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002748 private ArrayList<Action> mActions = new ArrayList<Action>(MAX_ACTION_BUTTONS);
2749 private ArrayList<String> mPersonList = new ArrayList<String>();
2750 private NotificationColorUtil mColorUtil;
Selim Cinek7b9605b2017-01-19 17:36:00 -08002751 private boolean mIsLegacy;
2752 private boolean mIsLegacyInitialized;
Christoph Studer7ac80e62014-08-04 16:01:57 +02002753
2754 /**
Adrian Roos4ff3b122016-02-01 12:26:13 -08002755 * Caches a contrast-enhanced version of {@link #mCachedContrastColorIsFor}.
2756 */
2757 private int mCachedContrastColor = COLOR_INVALID;
2758 private int mCachedContrastColorIsFor = COLOR_INVALID;
Adrian Roos487374f2017-01-11 15:48:14 -08002759 /**
2760 * Caches a ambient version of {@link #mCachedContrastColorIsFor}.
2761 */
2762 private int mCachedAmbientColor = COLOR_INVALID;
2763 private int mCachedAmbientColorIsFor = COLOR_INVALID;
Adrian Roos4ff3b122016-02-01 12:26:13 -08002764
2765 /**
Adrian Roos70d7aa32017-01-11 15:39:06 -08002766 * Caches an instance of StandardTemplateParams. Note that this may have been used before,
2767 * so make sure to call {@link StandardTemplateParams#reset()} before using it.
2768 */
2769 StandardTemplateParams mParams = new StandardTemplateParams();
Selim Cinek7b9605b2017-01-19 17:36:00 -08002770 private int mTextColorsAreForBackground = COLOR_INVALID;
2771 private int mPrimaryTextColor = COLOR_INVALID;
2772 private int mSecondaryTextColor = COLOR_INVALID;
2773 private int mActionBarColor = COLOR_INVALID;
Selim Cinek5fb73f82017-04-20 16:55:38 -07002774 private int mBackgroundColor = COLOR_INVALID;
2775 private int mForegroundColor = COLOR_INVALID;
Selim Cinekac5f0272017-05-02 16:05:41 -07002776 private int mBackgroundColorHint = COLOR_INVALID;
Selim Cinek5d6ef8d2017-05-18 22:16:00 -07002777 /**
2778 * A temporary location where actions are stored. If != null the view originally has action
2779 * but doesn't have any for this inflation.
2780 */
2781 private ArrayList<Action> mOriginalActions;
Selim Cineka7679b62017-05-10 16:33:25 -07002782 private boolean mRebuildStyledRemoteViews;
Adrian Roos70d7aa32017-01-11 15:39:06 -08002783
Anthony Chenad4d1582017-04-10 16:07:58 -07002784 private boolean mTintActionButtons;
2785 private boolean mInNightMode;
2786
Adrian Roos70d7aa32017-01-11 15:39:06 -08002787 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002788 * Constructs a new Builder with the defaults:
Joe Onoratocb109a02011-01-18 17:57:41 -08002789 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002790 * @param context
2791 * A {@link Context} that will be used by the Builder to construct the
2792 * RemoteViews. The Context will not be held past the lifetime of this Builder
2793 * object.
Geoffrey Pitsch5caa2762017-01-12 09:35:54 -05002794 * @param channelId
2795 * The constructed Notification will be posted on this
2796 * {@link NotificationChannel}. To use a NotificationChannel, it must first be
2797 * created using {@link NotificationManager#createNotificationChannel}.
Joe Onoratocb109a02011-01-18 17:57:41 -08002798 */
Geoffrey Pitsch5caa2762017-01-12 09:35:54 -05002799 public Builder(Context context, String channelId) {
2800 this(context, (Notification) null);
2801 mN.mChannelId = channelId;
2802 }
2803
2804 /**
2805 * @deprecated use {@link Notification.Builder#Notification.Builder(Context, String)}
2806 * instead. All posted Notifications must specify a NotificationChannel Id.
2807 */
2808 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08002809 public Builder(Context context) {
Geoffrey Pitsch5caa2762017-01-12 09:35:54 -05002810 this(context, (Notification) null);
Joe Onorato46439ce2010-11-19 13:56:21 -08002811 }
2812
Joe Onoratocb109a02011-01-18 17:57:41 -08002813 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002814 * @hide
Christoph Studer4600f9b2014-07-22 22:44:43 +02002815 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002816 public Builder(Context context, Notification toAdopt) {
2817 mContext = context;
Anthony Chenad4d1582017-04-10 16:07:58 -07002818 Resources res = mContext.getResources();
2819 mTintActionButtons = res.getBoolean(R.bool.config_tintNotificationActionButtons);
2820
2821 if (res.getBoolean(R.bool.config_enableNightMode)) {
2822 Configuration currentConfig = res.getConfiguration();
2823 mInNightMode = (currentConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK)
2824 == Configuration.UI_MODE_NIGHT_YES;
2825 }
Christoph Studer4600f9b2014-07-22 22:44:43 +02002826
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002827 if (toAdopt == null) {
2828 mN = new Notification();
Selim Cinek0ff1ce602016-04-05 18:27:16 -07002829 if (context.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N) {
2830 mN.extras.putBoolean(EXTRA_SHOW_WHEN, true);
2831 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002832 mN.priority = PRIORITY_DEFAULT;
2833 mN.visibility = VISIBILITY_PRIVATE;
2834 } else {
2835 mN = toAdopt;
2836 if (mN.actions != null) {
2837 Collections.addAll(mActions, mN.actions);
Christoph Studer4600f9b2014-07-22 22:44:43 +02002838 }
2839
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002840 if (mN.extras.containsKey(EXTRA_PEOPLE)) {
2841 Collections.addAll(mPersonList, mN.extras.getStringArray(EXTRA_PEOPLE));
2842 }
2843
Selim Cinek4ac6f602016-06-13 15:47:03 -07002844 if (mN.getSmallIcon() == null && mN.icon != 0) {
2845 setSmallIcon(mN.icon);
2846 }
2847
2848 if (mN.getLargeIcon() == null && mN.largeIcon != null) {
2849 setLargeIcon(mN.largeIcon);
2850 }
2851
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002852 String templateClass = mN.extras.getString(EXTRA_TEMPLATE);
2853 if (!TextUtils.isEmpty(templateClass)) {
2854 final Class<? extends Style> styleClass
2855 = getNotificationStyleClass(templateClass);
2856 if (styleClass == null) {
2857 Log.d(TAG, "Unknown style class: " + templateClass);
2858 } else {
2859 try {
Adrian Roosc1a80b02016-04-05 14:54:55 -07002860 final Constructor<? extends Style> ctor =
2861 styleClass.getDeclaredConstructor();
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002862 ctor.setAccessible(true);
2863 final Style style = ctor.newInstance();
2864 style.restoreFromExtras(mN.extras);
2865
2866 if (style != null) {
2867 setStyle(style);
2868 }
2869 } catch (Throwable t) {
2870 Log.e(TAG, "Could not create Style", t);
2871 }
2872 }
2873 }
2874
2875 }
2876 }
2877
2878 private NotificationColorUtil getColorUtil() {
Selim Cinek99104832017-01-25 14:47:33 -08002879 if (mColorUtil == null) {
2880 mColorUtil = NotificationColorUtil.getInstance(mContext);
Christoph Studer4600f9b2014-07-22 22:44:43 +02002881 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002882 return mColorUtil;
Christoph Studer4600f9b2014-07-22 22:44:43 +02002883 }
2884
2885 /**
Julia Reynolds13d898c2017-02-02 12:22:05 -05002886 * If this notification is duplicative of a Launcher shortcut, sets the
2887 * {@link ShortcutInfo#getId() id} of the shortcut, in case the Launcher wants to hide
2888 * the shortcut.
2889 *
Julia Reynoldsbad42972017-04-25 13:52:49 -04002890 * This field will be ignored by Launchers that don't support badging, don't show
2891 * notification content, or don't show {@link android.content.pm.ShortcutManager shortcuts}.
Julia Reynolds13d898c2017-02-02 12:22:05 -05002892 *
2893 * @param shortcutId the {@link ShortcutInfo#getId() id} of the shortcut this notification
2894 * supersedes
2895 */
2896 public Builder setShortcutId(String shortcutId) {
2897 mN.mShortcutId = shortcutId;
2898 return this;
2899 }
2900
2901 /**
Julia Reynoldse071abd2017-03-22 10:52:11 -04002902 * Sets which icon to display as a badge for this notification.
2903 *
2904 * Must be one of {@link #BADGE_ICON_NONE}, {@link #BADGE_ICON_SMALL},
2905 * {@link #BADGE_ICON_LARGE}.
2906 *
2907 * Note: This value might be ignored, for launchers that don't support badge icons.
2908 */
Julia Reynolds612beb22017-03-30 10:48:30 -04002909 public Builder setBadgeIconType(int icon) {
Julia Reynoldse071abd2017-03-22 10:52:11 -04002910 mN.mBadgeIcon = icon;
2911 return this;
2912 }
2913
2914 /**
Julia Reynoldsa79c3712017-04-21 10:29:57 -04002915 * Sets the group alert behavior for this notification. Use this method to mute this
2916 * notification if alerts for this notification's group should be handled by a different
2917 * notification. This is only applicable for notifications that belong to a
Julia Reynolds399d9bf2017-08-11 12:52:14 -04002918 * {@link #setGroup(String) group}. This must be called on all notifications you want to
2919 * mute. For example, if you want only the summary of your group to make noise, all
2920 * children in the group should have the group alert behavior {@link #GROUP_ALERT_SUMMARY}.
Julia Reynoldsa79c3712017-04-21 10:29:57 -04002921 *
2922 * <p> The default value is {@link #GROUP_ALERT_ALL}.</p>
2923 */
2924 public Builder setGroupAlertBehavior(@GroupAlertBehavior int groupAlertBehavior) {
2925 mN.mGroupAlertBehavior = groupAlertBehavior;
2926 return this;
2927 }
2928
Jeff Sharkey000ce802017-04-29 13:13:27 -06002929 /** @removed */
2930 @Deprecated
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04002931 public Builder setChannel(String channelId) {
2932 mN.mChannelId = channelId;
2933 return this;
2934 }
2935
2936 /**
Julia Reynoldsbad42972017-04-25 13:52:49 -04002937 * Specifies the channel the notification should be delivered on.
2938 */
2939 public Builder setChannelId(String channelId) {
2940 mN.mChannelId = channelId;
2941 return this;
2942 }
2943
Jeff Sharkey000ce802017-04-29 13:13:27 -06002944 /** @removed */
2945 @Deprecated
Julia Reynolds50989772017-02-23 14:32:16 -05002946 public Builder setTimeout(long durationMs) {
2947 mN.mTimeout = durationMs;
Julia Reynolds2a128742016-11-28 14:29:25 -05002948 return this;
2949 }
2950
2951 /**
Julia Reynoldsbad42972017-04-25 13:52:49 -04002952 * Specifies a duration in milliseconds after which this notification should be canceled,
2953 * if it is not already canceled.
2954 */
2955 public Builder setTimeoutAfter(long durationMs) {
2956 mN.mTimeout = durationMs;
2957 return this;
2958 }
2959
2960 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002961 * Add a timestamp pertaining to the notification (usually the time the event occurred).
Selim Cinek0ff1ce602016-04-05 18:27:16 -07002962 *
2963 * For apps targeting {@link android.os.Build.VERSION_CODES#N} and above, this time is not
2964 * shown anymore by default and must be opted into by using
2965 * {@link android.app.Notification.Builder#setShowWhen(boolean)}
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002966 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002967 * @see Notification#when
Joe Onoratocb109a02011-01-18 17:57:41 -08002968 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002969 public Builder setWhen(long when) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002970 mN.when = when;
Joe Onorato46439ce2010-11-19 13:56:21 -08002971 return this;
2972 }
2973
Joe Onoratocb109a02011-01-18 17:57:41 -08002974 /**
Griff Hazen50c11652014-05-16 09:46:31 -07002975 * Control whether the timestamp set with {@link #setWhen(long) setWhen} is shown
Daniel Sandler0c890492012-09-12 17:23:10 -07002976 * in the content view.
Selim Cinek0ff1ce602016-04-05 18:27:16 -07002977 * For apps targeting {@link android.os.Build.VERSION_CODES#N} and above, this defaults to
2978 * {@code false}. For earlier apps, the default is {@code true}.
Daniel Sandler0c890492012-09-12 17:23:10 -07002979 */
2980 public Builder setShowWhen(boolean show) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002981 mN.extras.putBoolean(EXTRA_SHOW_WHEN, show);
Daniel Sandler0c890492012-09-12 17:23:10 -07002982 return this;
2983 }
2984
2985 /**
Daniel Sandlerd33b8032012-05-10 11:41:48 -04002986 * Show the {@link Notification#when} field as a stopwatch.
Joe Malin8d40d042012-11-05 11:36:40 -08002987 *
2988 * Instead of presenting <code>when</code> as a timestamp, the notification will show an
Daniel Sandlerd33b8032012-05-10 11:41:48 -04002989 * automatically updating display of the minutes and seconds since <code>when</code>.
Daniel Sandlera2985ed2012-04-03 16:42:00 -04002990 *
Daniel Sandlerd33b8032012-05-10 11:41:48 -04002991 * Useful when showing an elapsed time (like an ongoing phone call).
2992 *
Selim Cinek81c23aa2016-02-25 16:23:13 -08002993 * The counter can also be set to count down to <code>when</code> when using
Adrian Roos96b7e202016-05-17 13:50:38 -07002994 * {@link #setChronometerCountDown(boolean)}.
Selim Cinek81c23aa2016-02-25 16:23:13 -08002995 *
Daniel Sandlerd33b8032012-05-10 11:41:48 -04002996 * @see android.widget.Chronometer
Daniel Sandlera2985ed2012-04-03 16:42:00 -04002997 * @see Notification#when
Adrian Roos96b7e202016-05-17 13:50:38 -07002998 * @see #setChronometerCountDown(boolean)
Daniel Sandlera2985ed2012-04-03 16:42:00 -04002999 */
3000 public Builder setUsesChronometer(boolean b) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003001 mN.extras.putBoolean(EXTRA_SHOW_CHRONOMETER, b);
Daniel Sandlera2985ed2012-04-03 16:42:00 -04003002 return this;
3003 }
3004
3005 /**
Selim Cinek81c23aa2016-02-25 16:23:13 -08003006 * Sets the Chronometer to count down instead of counting up.
3007 *
3008 * <p>This is only relevant if {@link #setUsesChronometer(boolean)} has been set to true.
3009 * If it isn't set the chronometer will count up.
3010 *
3011 * @see #setUsesChronometer(boolean)
3012 */
Adrian Roos96b7e202016-05-17 13:50:38 -07003013 public Builder setChronometerCountDown(boolean countDown) {
3014 mN.extras.putBoolean(EXTRA_CHRONOMETER_COUNT_DOWN, countDown);
Selim Cinek81c23aa2016-02-25 16:23:13 -08003015 return this;
3016 }
3017
3018 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003019 * Set the small icon resource, which will be used to represent the notification in the
3020 * status bar.
Joe Onoratocb109a02011-01-18 17:57:41 -08003021 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003022
3023 * The platform template for the expanded view will draw this icon in the left, unless a
3024 * {@link #setLargeIcon(Bitmap) large icon} has also been specified, in which case the small
3025 * icon will be moved to the right-hand side.
3026 *
3027
3028 * @param icon
3029 * A resource ID in the application's package of the drawable to use.
3030 * @see Notification#icon
Joe Onoratocb109a02011-01-18 17:57:41 -08003031 */
Tor Norbye7b9c9122013-05-30 16:48:33 -07003032 public Builder setSmallIcon(@DrawableRes int icon) {
Dan Sandlerd63f9322015-05-06 15:18:49 -04003033 return setSmallIcon(icon != 0
3034 ? Icon.createWithResource(mContext, icon)
3035 : null);
Joe Onorato46439ce2010-11-19 13:56:21 -08003036 }
3037
Joe Onoratocb109a02011-01-18 17:57:41 -08003038 /**
3039 * A variant of {@link #setSmallIcon(int) setSmallIcon(int)} that takes an additional
3040 * level parameter for when the icon is a {@link android.graphics.drawable.LevelListDrawable
3041 * LevelListDrawable}.
3042 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003043 * @param icon A resource ID in the application's package of the drawable to use.
Joe Onoratocb109a02011-01-18 17:57:41 -08003044 * @param level The level to use for the icon.
3045 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003046 * @see Notification#icon
3047 * @see Notification#iconLevel
Joe Onoratocb109a02011-01-18 17:57:41 -08003048 */
Tor Norbye7b9c9122013-05-30 16:48:33 -07003049 public Builder setSmallIcon(@DrawableRes int icon, int level) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003050 mN.iconLevel = level;
Dan Sandlerd63f9322015-05-06 15:18:49 -04003051 return setSmallIcon(icon);
3052 }
3053
3054 /**
3055 * Set the small icon, which will be used to represent the notification in the
3056 * status bar and content view (unless overriden there by a
3057 * {@link #setLargeIcon(Bitmap) large icon}).
3058 *
3059 * @param icon An Icon object to use.
3060 * @see Notification#icon
3061 */
3062 public Builder setSmallIcon(Icon icon) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003063 mN.setSmallIcon(icon);
3064 if (icon != null && icon.getType() == Icon.TYPE_RESOURCE) {
3065 mN.icon = icon.getResId();
3066 }
Joe Onorato46439ce2010-11-19 13:56:21 -08003067 return this;
3068 }
3069
Joe Onoratocb109a02011-01-18 17:57:41 -08003070 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003071 * Set the first line of text in the platform notification template.
Joe Onoratocb109a02011-01-18 17:57:41 -08003072 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003073 public Builder setContentTitle(CharSequence title) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003074 mN.extras.putCharSequence(EXTRA_TITLE, safeCharSequence(title));
Joe Onorato46439ce2010-11-19 13:56:21 -08003075 return this;
3076 }
3077
Joe Onoratocb109a02011-01-18 17:57:41 -08003078 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003079 * Set the second line of text in the platform notification template.
Joe Onoratocb109a02011-01-18 17:57:41 -08003080 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003081 public Builder setContentText(CharSequence text) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003082 mN.extras.putCharSequence(EXTRA_TEXT, safeCharSequence(text));
Joe Onorato46439ce2010-11-19 13:56:21 -08003083 return this;
3084 }
3085
Joe Onoratocb109a02011-01-18 17:57:41 -08003086 /**
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07003087 * This provides some additional information that is displayed in the notification. No
3088 * guarantees are given where exactly it is displayed.
3089 *
3090 * <p>This information should only be provided if it provides an essential
3091 * benefit to the understanding of the notification. The more text you provide the
3092 * less readable it becomes. For example, an email client should only provide the account
3093 * name here if more than one email account has been added.</p>
3094 *
3095 * <p>As of {@link android.os.Build.VERSION_CODES#N} this information is displayed in the
3096 * notification header area.
3097 *
3098 * On Android versions before {@link android.os.Build.VERSION_CODES#N}
3099 * this will be shown in the third line of text in the platform notification template.
3100 * You should not be using {@link #setProgress(int, int, boolean)} at the
3101 * same time on those versions; they occupy the same place.
3102 * </p>
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003103 */
3104 public Builder setSubText(CharSequence text) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003105 mN.extras.putCharSequence(EXTRA_SUB_TEXT, safeCharSequence(text));
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003106 return this;
3107 }
3108
3109 /**
Julia Reynolds3aedded2017-03-31 14:42:09 -04003110 * Provides text that will appear as a link to your application's settings.
3111 *
3112 * <p>This text does not appear within notification {@link Style templates} but may
3113 * appear when the user uses an affordance to learn more about the notification.
3114 * Additionally, this text will not appear unless you provide a valid link target by
3115 * handling {@link #INTENT_CATEGORY_NOTIFICATION_PREFERENCES}.
3116 *
3117 * <p>This text is meant to be concise description about what the user can customize
3118 * when they click on this link. The recommended maximum length is 40 characters.
3119 * @param text
3120 * @return
3121 */
3122 public Builder setSettingsText(CharSequence text) {
3123 mN.mSettingsText = safeCharSequence(text);
3124 return this;
3125 }
3126
3127 /**
Adrian Roose458aa82015-12-08 16:17:19 -08003128 * Set the remote input history.
3129 *
3130 * This should be set to the most recent inputs that have been sent
3131 * through a {@link RemoteInput} of this Notification and cleared once the it is no
3132 * longer relevant (e.g. for chat notifications once the other party has responded).
3133 *
3134 * The most recent input must be stored at the 0 index, the second most recent at the
3135 * 1 index, etc. Note that the system will limit both how far back the inputs will be shown
3136 * and how much of each individual input is shown.
3137 *
3138 * <p>Note: The reply text will only be shown on notifications that have least one action
3139 * with a {@code RemoteInput}.</p>
3140 */
3141 public Builder setRemoteInputHistory(CharSequence[] text) {
3142 if (text == null) {
3143 mN.extras.putCharSequenceArray(EXTRA_REMOTE_INPUT_HISTORY, null);
3144 } else {
3145 final int N = Math.min(MAX_REPLY_HISTORY, text.length);
3146 CharSequence[] safe = new CharSequence[N];
3147 for (int i = 0; i < N; i++) {
3148 safe[i] = safeCharSequence(text[i]);
3149 }
3150 mN.extras.putCharSequenceArray(EXTRA_REMOTE_INPUT_HISTORY, safe);
3151 }
3152 return this;
3153 }
3154
3155 /**
Julia Reynolds13d898c2017-02-02 12:22:05 -05003156 * Sets the number of items this notification represents. May be displayed as a badge count
3157 * for Launchers that support badging.
Joe Onoratocb109a02011-01-18 17:57:41 -08003158 */
Joe Onorato8595a3d2010-11-19 18:12:07 -08003159 public Builder setNumber(int number) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003160 mN.number = number;
Joe Onorato8595a3d2010-11-19 18:12:07 -08003161 return this;
3162 }
3163
Joe Onoratocb109a02011-01-18 17:57:41 -08003164 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003165 * A small piece of additional information pertaining to this notification.
3166 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003167 * The platform template will draw this on the last line of the notification, at the far
3168 * right (to the right of a smallIcon if it has been placed there).
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07003169 *
3170 * @deprecated use {@link #setSubText(CharSequence)} instead to set a text in the header.
3171 * For legacy apps targeting a version below {@link android.os.Build.VERSION_CODES#N} this
3172 * field will still show up, but the subtext will take precedence.
Joe Onoratocb109a02011-01-18 17:57:41 -08003173 */
Aurimas Liutikas514c5ef2016-05-24 15:22:55 -07003174 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08003175 public Builder setContentInfo(CharSequence info) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003176 mN.extras.putCharSequence(EXTRA_INFO_TEXT, safeCharSequence(info));
Joe Onorato46439ce2010-11-19 13:56:21 -08003177 return this;
3178 }
3179
Joe Onoratocb109a02011-01-18 17:57:41 -08003180 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003181 * Set the progress this notification represents.
3182 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003183 * The platform template will represent this using a {@link ProgressBar}.
Jeff Sharkey1c400132011-08-05 14:50:13 -07003184 */
3185 public Builder setProgress(int max, int progress, boolean indeterminate) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003186 mN.extras.putInt(EXTRA_PROGRESS, progress);
3187 mN.extras.putInt(EXTRA_PROGRESS_MAX, max);
3188 mN.extras.putBoolean(EXTRA_PROGRESS_INDETERMINATE, indeterminate);
Jeff Sharkey1c400132011-08-05 14:50:13 -07003189 return this;
3190 }
3191
3192 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003193 * Supply a custom RemoteViews to use instead of the platform template.
3194 *
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003195 * Use {@link #setCustomContentView(RemoteViews)} instead.
Joe Onoratocb109a02011-01-18 17:57:41 -08003196 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003197 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08003198 public Builder setContent(RemoteViews views) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003199 return setCustomContentView(views);
3200 }
3201
3202 /**
3203 * Supply custom RemoteViews to use instead of the platform template.
3204 *
3205 * This will override the layout that would otherwise be constructed by this Builder
3206 * object.
3207 */
3208 public Builder setCustomContentView(RemoteViews contentView) {
3209 mN.contentView = contentView;
3210 return this;
3211 }
3212
3213 /**
3214 * Supply custom RemoteViews to use instead of the platform template in the expanded form.
3215 *
3216 * This will override the expanded layout that would otherwise be constructed by this
3217 * Builder object.
3218 */
3219 public Builder setCustomBigContentView(RemoteViews contentView) {
3220 mN.bigContentView = contentView;
3221 return this;
3222 }
3223
3224 /**
3225 * Supply custom RemoteViews to use instead of the platform template in the heads up dialog.
3226 *
3227 * This will override the heads-up layout that would otherwise be constructed by this
3228 * Builder object.
3229 */
3230 public Builder setCustomHeadsUpContentView(RemoteViews contentView) {
3231 mN.headsUpContentView = contentView;
Joe Onorato46439ce2010-11-19 13:56:21 -08003232 return this;
3233 }
3234
Joe Onoratocb109a02011-01-18 17:57:41 -08003235 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003236 * Supply a {@link PendingIntent} to be sent when the notification is clicked.
3237 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003238 * As of {@link android.os.Build.VERSION_CODES#HONEYCOMB}, if this field is unset and you
3239 * have specified a custom RemoteViews with {@link #setContent(RemoteViews)}, you can use
3240 * {@link RemoteViews#setOnClickPendingIntent RemoteViews.setOnClickPendingIntent(int,PendingIntent)}
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003241 * to assign PendingIntents to individual views in that custom layout (i.e., to create
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003242 * clickable buttons inside the notification view).
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003243 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003244 * @see Notification#contentIntent Notification.contentIntent
Joe Onoratocb109a02011-01-18 17:57:41 -08003245 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003246 public Builder setContentIntent(PendingIntent intent) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003247 mN.contentIntent = intent;
Joe Onorato46439ce2010-11-19 13:56:21 -08003248 return this;
3249 }
3250
Joe Onoratocb109a02011-01-18 17:57:41 -08003251 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003252 * Supply a {@link PendingIntent} to send when the notification is cleared explicitly by the user.
3253 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003254 * @see Notification#deleteIntent
Joe Onoratocb109a02011-01-18 17:57:41 -08003255 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003256 public Builder setDeleteIntent(PendingIntent intent) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003257 mN.deleteIntent = intent;
Joe Onorato46439ce2010-11-19 13:56:21 -08003258 return this;
3259 }
3260
Joe Onoratocb109a02011-01-18 17:57:41 -08003261 /**
3262 * An intent to launch instead of posting the notification to the status bar.
3263 * Only for use with extremely high-priority notifications demanding the user's
3264 * <strong>immediate</strong> attention, such as an incoming phone call or
3265 * alarm clock that the user has explicitly set to a particular time.
3266 * If this facility is used for something else, please give the user an option
3267 * to turn it off and use a normal notification, as this can be extremely
3268 * disruptive.
3269 *
Chris Wren47c20a12014-06-18 17:27:29 -04003270 * <p>
3271 * The system UI may choose to display a heads-up notification, instead of
3272 * launching this intent, while the user is using the device.
3273 * </p>
3274 *
Joe Onoratocb109a02011-01-18 17:57:41 -08003275 * @param intent The pending intent to launch.
3276 * @param highPriority Passing true will cause this notification to be sent
3277 * even if other notifications are suppressed.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003278 *
3279 * @see Notification#fullScreenIntent
Joe Onoratocb109a02011-01-18 17:57:41 -08003280 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003281 public Builder setFullScreenIntent(PendingIntent intent, boolean highPriority) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003282 mN.fullScreenIntent = intent;
Joe Onorato46439ce2010-11-19 13:56:21 -08003283 setFlag(FLAG_HIGH_PRIORITY, highPriority);
3284 return this;
3285 }
3286
Joe Onoratocb109a02011-01-18 17:57:41 -08003287 /**
Dan Sandler5fcdf6e2014-07-18 11:31:15 -04003288 * Set the "ticker" text which is sent to accessibility services.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003289 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003290 * @see Notification#tickerText
Joe Onoratocb109a02011-01-18 17:57:41 -08003291 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003292 public Builder setTicker(CharSequence tickerText) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003293 mN.tickerText = safeCharSequence(tickerText);
Joe Onorato46439ce2010-11-19 13:56:21 -08003294 return this;
3295 }
3296
Joe Onoratocb109a02011-01-18 17:57:41 -08003297 /**
Dan Sandler5fcdf6e2014-07-18 11:31:15 -04003298 * Obsolete version of {@link #setTicker(CharSequence)}.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003299 *
Joe Onoratocb109a02011-01-18 17:57:41 -08003300 */
Dan Sandler5fcdf6e2014-07-18 11:31:15 -04003301 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08003302 public Builder setTicker(CharSequence tickerText, RemoteViews views) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003303 setTicker(tickerText);
3304 // views is ignored
Joe Onorato46439ce2010-11-19 13:56:21 -08003305 return this;
3306 }
3307
Joe Onoratocb109a02011-01-18 17:57:41 -08003308 /**
Dan Sandlerd63f9322015-05-06 15:18:49 -04003309 * Add a large icon to the notification content view.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003310 *
3311 * In the platform template, this image will be shown on the left of the notification view
Dan Sandlerd63f9322015-05-06 15:18:49 -04003312 * in place of the {@link #setSmallIcon(Icon) small icon} (which will be placed in a small
3313 * badge atop the large icon).
Dan Sandler08a04c12015-05-06 15:18:49 -04003314 */
Dan Sandlerd63f9322015-05-06 15:18:49 -04003315 public Builder setLargeIcon(Bitmap b) {
3316 return setLargeIcon(b != null ? Icon.createWithBitmap(b) : null);
3317 }
3318
3319 /**
3320 * Add a large icon to the notification content view.
3321 *
3322 * In the platform template, this image will be shown on the left of the notification view
3323 * in place of the {@link #setSmallIcon(Icon) small icon} (which will be placed in a small
3324 * badge atop the large icon).
3325 */
3326 public Builder setLargeIcon(Icon icon) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003327 mN.mLargeIcon = icon;
3328 mN.extras.putParcelable(EXTRA_LARGE_ICON, icon);
Joe Onorato46439ce2010-11-19 13:56:21 -08003329 return this;
3330 }
3331
Joe Onoratocb109a02011-01-18 17:57:41 -08003332 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003333 * Set the sound to play.
3334 *
John Spurlockc0650f022014-07-19 13:22:39 -04003335 * It will be played using the {@link #AUDIO_ATTRIBUTES_DEFAULT default audio attributes}
3336 * for notifications.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003337 *
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003338 * @deprecated use {@link NotificationChannel#setSound(Uri, AudioAttributes)} instead.
Joe Onoratocb109a02011-01-18 17:57:41 -08003339 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003340 @Deprecated
Joe Onorato52f80cd2010-11-21 15:34:48 -08003341 public Builder setSound(Uri sound) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003342 mN.sound = sound;
3343 mN.audioAttributes = AUDIO_ATTRIBUTES_DEFAULT;
Joe Onorato52f80cd2010-11-21 15:34:48 -08003344 return this;
3345 }
3346
Joe Onoratocb109a02011-01-18 17:57:41 -08003347 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003348 * Set the sound to play, along with a specific stream on which to play it.
Joe Onoratocb109a02011-01-18 17:57:41 -08003349 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003350 * See {@link android.media.AudioManager} for the <code>STREAM_</code> constants.
3351 *
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003352 * @deprecated use {@link NotificationChannel#setSound(Uri, AudioAttributes)}.
Joe Onoratocb109a02011-01-18 17:57:41 -08003353 */
Jean-Michel Trivi81f871e2014-08-06 16:32:38 -07003354 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08003355 public Builder setSound(Uri sound, int streamType) {
Jean-Michel Trivi2f7511f2016-11-28 15:40:27 -08003356 PlayerBase.deprecateStreamTypeForPlayback(streamType, "Notification", "setSound()");
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003357 mN.sound = sound;
3358 mN.audioStreamType = streamType;
Joe Onorato46439ce2010-11-19 13:56:21 -08003359 return this;
3360 }
3361
Joe Onoratocb109a02011-01-18 17:57:41 -08003362 /**
John Spurlockc0650f022014-07-19 13:22:39 -04003363 * Set the sound to play, along with specific {@link AudioAttributes audio attributes} to
3364 * use during playback.
3365 *
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003366 * @deprecated use {@link NotificationChannel#setSound(Uri, AudioAttributes)} instead.
John Spurlockc0650f022014-07-19 13:22:39 -04003367 * @see Notification#sound
3368 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003369 @Deprecated
John Spurlockc0650f022014-07-19 13:22:39 -04003370 public Builder setSound(Uri sound, AudioAttributes audioAttributes) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003371 mN.sound = sound;
3372 mN.audioAttributes = audioAttributes;
John Spurlockc0650f022014-07-19 13:22:39 -04003373 return this;
3374 }
3375
3376 /**
Joe Onoratocb109a02011-01-18 17:57:41 -08003377 * Set the vibration pattern to use.
3378 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003379 * See {@link android.os.Vibrator#vibrate(long[], int)} for a discussion of the
3380 * <code>pattern</code> parameter.
3381 *
Chris Wren47c20a12014-06-18 17:27:29 -04003382 * <p>
3383 * A notification that vibrates is more likely to be presented as a heads-up notification.
3384 * </p>
3385 *
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003386 * @deprecated use {@link NotificationChannel#setVibrationPattern(long[])} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003387 * @see Notification#vibrate
Joe Onoratocb109a02011-01-18 17:57:41 -08003388 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003389 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08003390 public Builder setVibrate(long[] pattern) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003391 mN.vibrate = pattern;
Joe Onorato46439ce2010-11-19 13:56:21 -08003392 return this;
3393 }
3394
Joe Onoratocb109a02011-01-18 17:57:41 -08003395 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003396 * Set the desired color for the indicator LED on the device, as well as the
3397 * blink duty cycle (specified in milliseconds).
3398 *
3399
3400 * Not all devices will honor all (or even any) of these values.
3401 *
Julia Reynolds529e3322017-02-06 08:33:01 -05003402 * @deprecated use {@link NotificationChannel#enableLights(boolean)} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003403 * @see Notification#ledARGB
3404 * @see Notification#ledOnMS
3405 * @see Notification#ledOffMS
Joe Onoratocb109a02011-01-18 17:57:41 -08003406 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003407 @Deprecated
Tor Norbye80756e32015-03-02 09:39:27 -08003408 public Builder setLights(@ColorInt int argb, int onMs, int offMs) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003409 mN.ledARGB = argb;
3410 mN.ledOnMS = onMs;
3411 mN.ledOffMS = offMs;
Julia Reynolds10ee1fc2015-11-09 11:04:55 -05003412 if (onMs != 0 || offMs != 0) {
3413 mN.flags |= FLAG_SHOW_LIGHTS;
3414 }
Joe Onorato46439ce2010-11-19 13:56:21 -08003415 return this;
3416 }
3417
Joe Onoratocb109a02011-01-18 17:57:41 -08003418 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003419 * Set whether this is an "ongoing" notification.
Joe Onoratocb109a02011-01-18 17:57:41 -08003420 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003421
3422 * Ongoing notifications cannot be dismissed by the user, so your application or service
3423 * must take care of canceling them.
3424 *
3425
3426 * They are typically used to indicate a background task that the user is actively engaged
3427 * with (e.g., playing music) or is pending in some way and therefore occupying the device
3428 * (e.g., a file download, sync operation, active network connection).
3429 *
3430
3431 * @see Notification#FLAG_ONGOING_EVENT
3432 * @see Service#setForeground(boolean)
Joe Onoratocb109a02011-01-18 17:57:41 -08003433 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003434 public Builder setOngoing(boolean ongoing) {
3435 setFlag(FLAG_ONGOING_EVENT, ongoing);
3436 return this;
3437 }
3438
Joe Onoratocb109a02011-01-18 17:57:41 -08003439 /**
Selim Cinek7b9605b2017-01-19 17:36:00 -08003440 * Set whether this notification should be colorized. When set, the color set with
3441 * {@link #setColor(int)} will be used as the background color of this notification.
3442 * <p>
Selim Cinek7b9605b2017-01-19 17:36:00 -08003443 * This should only be used for high priority ongoing tasks like navigation, an ongoing
3444 * call, or other similarly high-priority events for the user.
Selim Cinek99104832017-01-25 14:47:33 -08003445 * <p>
Selim Cinek22714f12017-04-13 16:23:53 -07003446 * For most styles, the coloring will only be applied if the notification is for a
3447 * foreground service notification.
Selim Cinek99104832017-01-25 14:47:33 -08003448 * However, for {@link MediaStyle} and {@link DecoratedMediaCustomViewStyle} notifications
Selim Cinek22714f12017-04-13 16:23:53 -07003449 * that have a media session attached there is no such requirement.
Selim Cinek7b9605b2017-01-19 17:36:00 -08003450 *
Selim Cinek7b9605b2017-01-19 17:36:00 -08003451 * @see Builder#setColor(int)
Selim Cinek99104832017-01-25 14:47:33 -08003452 * @see MediaStyle#setMediaSession(MediaSession.Token)
Selim Cinek7b9605b2017-01-19 17:36:00 -08003453 */
3454 public Builder setColorized(boolean colorize) {
3455 mN.extras.putBoolean(EXTRA_COLORIZED, colorize);
3456 return this;
3457 }
3458
3459 /**
Joe Onoratocb109a02011-01-18 17:57:41 -08003460 * Set this flag if you would only like the sound, vibrate
3461 * and ticker to be played if the notification is not already showing.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003462 *
3463 * @see Notification#FLAG_ONLY_ALERT_ONCE
Joe Onoratocb109a02011-01-18 17:57:41 -08003464 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003465 public Builder setOnlyAlertOnce(boolean onlyAlertOnce) {
3466 setFlag(FLAG_ONLY_ALERT_ONCE, onlyAlertOnce);
3467 return this;
3468 }
3469
Joe Onoratocb109a02011-01-18 17:57:41 -08003470 /**
Julia Reynolds04499532016-09-13 14:04:53 -04003471 * Make this notification automatically dismissed when the user touches it.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003472 *
3473 * @see Notification#FLAG_AUTO_CANCEL
Joe Onoratocb109a02011-01-18 17:57:41 -08003474 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003475 public Builder setAutoCancel(boolean autoCancel) {
Joe Onorato281d83f2011-01-04 17:13:10 -08003476 setFlag(FLAG_AUTO_CANCEL, autoCancel);
Joe Onorato46439ce2010-11-19 13:56:21 -08003477 return this;
3478 }
3479
Joe Onoratocb109a02011-01-18 17:57:41 -08003480 /**
Griff Hazendfcb0802014-02-11 12:00:00 -08003481 * Set whether or not this notification should not bridge to other devices.
3482 *
3483 * <p>Some notifications can be bridged to other devices for remote display.
3484 * This hint can be set to recommend this notification not be bridged.
3485 */
3486 public Builder setLocalOnly(boolean localOnly) {
3487 setFlag(FLAG_LOCAL_ONLY, localOnly);
3488 return this;
3489 }
3490
3491 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003492 * Set which notification properties will be inherited from system defaults.
Joe Onoratocb109a02011-01-18 17:57:41 -08003493 * <p>
3494 * The value should be one or more of the following fields combined with
3495 * bitwise-or:
3496 * {@link #DEFAULT_SOUND}, {@link #DEFAULT_VIBRATE}, {@link #DEFAULT_LIGHTS}.
3497 * <p>
3498 * For all default values, use {@link #DEFAULT_ALL}.
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003499 *
3500 * @deprecated use {@link NotificationChannel#enableVibration(boolean)} and
Julia Reynolds529e3322017-02-06 08:33:01 -05003501 * {@link NotificationChannel#enableLights(boolean)} and
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003502 * {@link NotificationChannel#setSound(Uri, AudioAttributes)} instead.
Joe Onoratocb109a02011-01-18 17:57:41 -08003503 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003504 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08003505 public Builder setDefaults(int defaults) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003506 mN.defaults = defaults;
Joe Onorato46439ce2010-11-19 13:56:21 -08003507 return this;
3508 }
3509
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003510 /**
3511 * Set the priority of this notification.
3512 *
3513 * @see Notification#priority
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003514 * @deprecated use {@link NotificationChannel#setImportance(int)} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003515 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003516 @Deprecated
Tor Norbyed9273d62013-05-30 15:59:53 -07003517 public Builder setPriority(@Priority int pri) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003518 mN.priority = pri;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003519 return this;
3520 }
Joe Malin8d40d042012-11-05 11:36:40 -08003521
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003522 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -04003523 * Set the notification category.
Joe Malin8d40d042012-11-05 11:36:40 -08003524 *
John Spurlockfd7f1e02014-03-18 16:41:57 -04003525 * @see Notification#category
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003526 */
John Spurlockfd7f1e02014-03-18 16:41:57 -04003527 public Builder setCategory(String category) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003528 mN.category = category;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003529 return this;
3530 }
3531
3532 /**
Chris Wrendde75302014-03-26 17:24:15 -04003533 * Add a person that is relevant to this notification.
3534 *
Chris Wrene6c48932014-09-29 17:19:27 -04003535 * <P>
3536 * Depending on user preferences, this annotation may allow the notification to pass
Julia Reynoldse071abd2017-03-22 10:52:11 -04003537 * through interruption filters, if this notification is of category {@link #CATEGORY_CALL}
3538 * or {@link #CATEGORY_MESSAGE}. The addition of people may also cause this notification to
3539 * appear more prominently in the user interface.
Chris Wrene6c48932014-09-29 17:19:27 -04003540 * </P>
3541 *
3542 * <P>
3543 * The person should be specified by the {@code String} representation of a
3544 * {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI}.
3545 * </P>
3546 *
3547 * <P>The system will also attempt to resolve {@code mailto:} and {@code tel:} schema
3548 * URIs. The path part of these URIs must exist in the contacts database, in the
3549 * appropriate column, or the reference will be discarded as invalid. Telephone schema
3550 * URIs will be resolved by {@link android.provider.ContactsContract.PhoneLookup}.
3551 * </P>
3552 *
3553 * @param uri A URI for the person.
Chris Wrendde75302014-03-26 17:24:15 -04003554 * @see Notification#EXTRA_PEOPLE
3555 */
Chris Wrene6c48932014-09-29 17:19:27 -04003556 public Builder addPerson(String uri) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003557 mPersonList.add(uri);
Chris Wrendde75302014-03-26 17:24:15 -04003558 return this;
3559 }
3560
3561 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003562 * Set this notification to be part of a group of notifications sharing the same key.
3563 * Grouped notifications may display in a cluster or stack on devices which
3564 * support such rendering.
3565 *
3566 * <p>To make this notification the summary for its group, also call
3567 * {@link #setGroupSummary}. A sort order can be specified for group members by using
3568 * {@link #setSortKey}.
3569 * @param groupKey The group key of the group.
3570 * @return this object for method chaining
3571 */
3572 public Builder setGroup(String groupKey) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003573 mN.mGroupKey = groupKey;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003574 return this;
3575 }
3576
3577 /**
3578 * Set this notification to be the group summary for a group of notifications.
3579 * Grouped notifications may display in a cluster or stack on devices which
Julia Reynolds04499532016-09-13 14:04:53 -04003580 * support such rendering. If thereRequires a group key also be set using {@link #setGroup}.
3581 * The group summary may be suppressed if too few notifications are included in the group.
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003582 * @param isGroupSummary Whether this notification should be a group summary.
3583 * @return this object for method chaining
3584 */
3585 public Builder setGroupSummary(boolean isGroupSummary) {
3586 setFlag(FLAG_GROUP_SUMMARY, isGroupSummary);
3587 return this;
3588 }
3589
3590 /**
3591 * Set a sort key that orders this notification among other notifications from the
3592 * same package. This can be useful if an external sort was already applied and an app
3593 * would like to preserve this. Notifications will be sorted lexicographically using this
3594 * value, although providing different priorities in addition to providing sort key may
3595 * cause this value to be ignored.
3596 *
3597 * <p>This sort key can also be used to order members of a notification group. See
Griff Hazen9e1379f2014-05-20 12:50:51 -07003598 * {@link #setGroup}.
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003599 *
3600 * @see String#compareTo(String)
3601 */
3602 public Builder setSortKey(String sortKey) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003603 mN.mSortKey = sortKey;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003604 return this;
3605 }
3606
3607 /**
Griff Hazen720042b2014-02-24 15:46:56 -08003608 * Merge additional metadata into this notification.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003609 *
Griff Hazen720042b2014-02-24 15:46:56 -08003610 * <p>Values within the Bundle will replace existing extras values in this Builder.
3611 *
3612 * @see Notification#extras
3613 */
Griff Hazen959591e2014-05-15 22:26:18 -07003614 public Builder addExtras(Bundle extras) {
3615 if (extras != null) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003616 mUserExtras.putAll(extras);
Griff Hazen720042b2014-02-24 15:46:56 -08003617 }
3618 return this;
3619 }
3620
3621 /**
3622 * Set metadata for this notification.
3623 *
3624 * <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 -04003625 * current contents are copied into the Notification each time {@link #build()} is
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003626 * called.
3627 *
Griff Hazen720042b2014-02-24 15:46:56 -08003628 * <p>Replaces any existing extras values with those from the provided Bundle.
3629 * Use {@link #addExtras} to merge in metadata instead.
3630 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003631 * @see Notification#extras
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003632 */
Griff Hazen959591e2014-05-15 22:26:18 -07003633 public Builder setExtras(Bundle extras) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003634 if (extras != null) {
3635 mUserExtras = extras;
3636 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003637 return this;
3638 }
3639
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003640 /**
Griff Hazen720042b2014-02-24 15:46:56 -08003641 * Get the current metadata Bundle used by this notification Builder.
3642 *
3643 * <p>The returned Bundle is shared with this Builder.
3644 *
3645 * <p>The current contents of this Bundle are copied into the Notification each time
3646 * {@link #build()} is called.
3647 *
3648 * @see Notification#extras
3649 */
3650 public Bundle getExtras() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003651 return mUserExtras;
3652 }
3653
3654 private Bundle getAllExtras() {
3655 final Bundle saveExtras = (Bundle) mUserExtras.clone();
3656 saveExtras.putAll(mN.extras);
3657 return saveExtras;
Griff Hazen720042b2014-02-24 15:46:56 -08003658 }
3659
3660 /**
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003661 * Add an action to this notification. Actions are typically displayed by
3662 * the system as a button adjacent to the notification content.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04003663 * <p>
3664 * Every action must have an icon (32dp square and matching the
3665 * <a href="{@docRoot}design/style/iconography.html#action-bar">Holo
3666 * Dark action bar</a> visual style), a textual label, and a {@link PendingIntent}.
3667 * <p>
3668 * A notification in its expanded form can display up to 3 actions, from left to right in
3669 * the order they were added. Actions will not be displayed when the notification is
3670 * collapsed, however, so be sure that any essential functions may be accessed by the user
3671 * in some other way (for example, in the Activity pointed to by {@link #contentIntent}).
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003672 *
3673 * @param icon Resource ID of a drawable that represents the action.
3674 * @param title Text describing the action.
3675 * @param intent PendingIntent to be fired when the action is invoked.
Dan Sandler86647982015-05-13 23:41:13 -04003676 *
3677 * @deprecated Use {@link #addAction(Action)} instead.
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003678 */
Dan Sandler86647982015-05-13 23:41:13 -04003679 @Deprecated
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003680 public Builder addAction(int icon, CharSequence title, PendingIntent intent) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04003681 mActions.add(new Action(icon, safeCharSequence(title), intent));
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003682 return this;
3683 }
3684
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003685 /**
Griff Hazen959591e2014-05-15 22:26:18 -07003686 * Add an action to this notification. Actions are typically displayed by
3687 * the system as a button adjacent to the notification content.
3688 * <p>
3689 * Every action must have an icon (32dp square and matching the
3690 * <a href="{@docRoot}design/style/iconography.html#action-bar">Holo
3691 * Dark action bar</a> visual style), a textual label, and a {@link PendingIntent}.
3692 * <p>
3693 * A notification in its expanded form can display up to 3 actions, from left to right in
3694 * the order they were added. Actions will not be displayed when the notification is
3695 * collapsed, however, so be sure that any essential functions may be accessed by the user
3696 * in some other way (for example, in the Activity pointed to by {@link #contentIntent}).
3697 *
3698 * @param action The action to add.
3699 */
3700 public Builder addAction(Action action) {
liangweikang63b03b52017-03-16 19:22:15 +08003701 if (action != null) {
3702 mActions.add(action);
3703 }
Griff Hazen959591e2014-05-15 22:26:18 -07003704 return this;
3705 }
3706
3707 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003708 * Alter the complete list of actions attached to this notification.
3709 * @see #addAction(Action).
3710 *
3711 * @param actions
3712 * @return
3713 */
3714 public Builder setActions(Action... actions) {
3715 mActions.clear();
3716 for (int i = 0; i < actions.length; i++) {
liangweikang63b03b52017-03-16 19:22:15 +08003717 if (actions[i] != null) {
3718 mActions.add(actions[i]);
3719 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003720 }
3721 return this;
3722 }
3723
3724 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003725 * Add a rich notification style to be applied at build time.
3726 *
3727 * @param style Object responsible for modifying the notification style.
3728 */
3729 public Builder setStyle(Style style) {
3730 if (mStyle != style) {
3731 mStyle = style;
Daniel Sandlerc08dea22012-06-28 08:35:24 -07003732 if (mStyle != null) {
3733 mStyle.setBuilder(this);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003734 mN.extras.putString(EXTRA_TEMPLATE, style.getClass().getName());
3735 } else {
3736 mN.extras.remove(EXTRA_TEMPLATE);
Daniel Sandlerc08dea22012-06-28 08:35:24 -07003737 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003738 }
3739 return this;
3740 }
3741
Dan Sandler0bf2ed82013-12-21 23:33:41 -06003742 /**
3743 * Specify the value of {@link #visibility}.
Griff Hazenb720abe2014-05-20 13:15:30 -07003744 *
Dan Sandler0bf2ed82013-12-21 23:33:41 -06003745 * @return The same Builder.
3746 */
Jeff Sharkey30e06bb2017-04-24 11:18:03 -06003747 public Builder setVisibility(@Visibility int visibility) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003748 mN.visibility = visibility;
Dan Sandler0bf2ed82013-12-21 23:33:41 -06003749 return this;
3750 }
3751
3752 /**
3753 * Supply a replacement Notification whose contents should be shown in insecure contexts
3754 * (i.e. atop the secure lockscreen). See {@link #visibility} and {@link #VISIBILITY_PUBLIC}.
3755 * @param n A replacement notification, presumably with some or all info redacted.
3756 * @return The same Builder.
3757 */
3758 public Builder setPublicVersion(Notification n) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003759 if (n != null) {
3760 mN.publicVersion = new Notification();
3761 n.cloneInto(mN.publicVersion, /*heavy=*/ true);
3762 } else {
3763 mN.publicVersion = null;
3764 }
Dan Sandler0bf2ed82013-12-21 23:33:41 -06003765 return this;
3766 }
3767
Griff Hazenb720abe2014-05-20 13:15:30 -07003768 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003769 * Apply an extender to this notification builder. Extenders may be used to add
3770 * metadata or change options on this builder.
3771 */
Griff Hazen61a9e862014-05-22 16:05:19 -07003772 public Builder extend(Extender extender) {
3773 extender.extend(this);
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003774 return this;
3775 }
3776
Dan Sandler4e787062015-06-17 15:09:48 -04003777 /**
3778 * @hide
3779 */
Julia Reynoldse46bb372016-03-17 11:05:58 -04003780 public Builder setFlag(int mask, boolean value) {
Joe Onorato46439ce2010-11-19 13:56:21 -08003781 if (value) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003782 mN.flags |= mask;
Joe Onorato46439ce2010-11-19 13:56:21 -08003783 } else {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003784 mN.flags &= ~mask;
Joe Onorato46439ce2010-11-19 13:56:21 -08003785 }
Julia Reynoldse46bb372016-03-17 11:05:58 -04003786 return this;
Joe Onorato46439ce2010-11-19 13:56:21 -08003787 }
3788
Dan Sandler26e81cf2014-05-06 10:01:27 -04003789 /**
3790 * Sets {@link Notification#color}.
3791 *
3792 * @param argb The accent color to use
3793 *
3794 * @return The same Builder.
3795 */
Tor Norbye80756e32015-03-02 09:39:27 -08003796 public Builder setColor(@ColorInt int argb) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003797 mN.color = argb;
Julia Reynolds10ee1fc2015-11-09 11:04:55 -05003798 sanitizeColor();
Dan Sandler26e81cf2014-05-06 10:01:27 -04003799 return this;
3800 }
3801
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003802 private Drawable getProfileBadgeDrawable() {
Chris Wren66619a22016-05-12 16:42:37 -04003803 if (mContext.getUserId() == UserHandle.USER_SYSTEM) {
3804 // This user can never be a badged profile,
3805 // and also includes USER_ALL system notifications.
3806 return null;
3807 }
Christoph Studer7ac80e62014-08-04 16:01:57 +02003808 // Note: This assumes that the current user can read the profile badge of the
3809 // originating user.
Selim Cineke6ff9462016-01-15 15:07:06 -08003810 return mContext.getPackageManager().getUserBadgeForDensityNoBackground(
Julia Reynoldsda303542015-11-23 14:00:20 -05003811 new UserHandle(mContext.getUserId()), 0);
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003812 }
3813
3814 private Bitmap getProfileBadge() {
3815 Drawable badge = getProfileBadgeDrawable();
Kenny Guy8a0101b2014-05-08 23:34:12 +01003816 if (badge == null) {
3817 return null;
3818 }
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003819 final int size = mContext.getResources().getDimensionPixelSize(
3820 R.dimen.notification_badge_size);
3821 Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
Kenny Guy8a0101b2014-05-08 23:34:12 +01003822 Canvas canvas = new Canvas(bitmap);
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003823 badge.setBounds(0, 0, size, size);
Kenny Guy8a0101b2014-05-08 23:34:12 +01003824 badge.draw(canvas);
3825 return bitmap;
3826 }
3827
Selim Cinekc848c3a2016-01-13 15:27:30 -08003828 private void bindProfileBadge(RemoteViews contentView) {
Kenny Guy98193ea2014-07-24 19:54:37 +01003829 Bitmap profileBadge = getProfileBadge();
3830
Kenny Guy98193ea2014-07-24 19:54:37 +01003831 if (profileBadge != null) {
Selim Cinekc848c3a2016-01-13 15:27:30 -08003832 contentView.setImageViewBitmap(R.id.profile_badge, profileBadge);
3833 contentView.setViewVisibility(R.id.profile_badge, View.VISIBLE);
Selim Cinek7b9605b2017-01-19 17:36:00 -08003834 if (isColorized()) {
3835 contentView.setDrawableParameters(R.id.profile_badge, false, -1,
3836 getPrimaryTextColor(), PorterDuff.Mode.SRC_ATOP, -1);
3837 }
Kenny Guy98193ea2014-07-24 19:54:37 +01003838 }
Kenny Guy98193ea2014-07-24 19:54:37 +01003839 }
3840
Christoph Studerfe718432014-09-01 18:21:18 +02003841 private void resetStandardTemplate(RemoteViews contentView) {
Selim Cinekeaa29ca2015-11-23 13:51:13 -08003842 resetNotificationHeader(contentView);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003843 resetContentMargins(contentView);
Christoph Studerfe718432014-09-01 18:21:18 +02003844 contentView.setViewVisibility(R.id.right_icon, View.GONE);
Selim Cinek860b6da2015-12-16 19:02:19 -08003845 contentView.setViewVisibility(R.id.title, View.GONE);
Christoph Studerfe718432014-09-01 18:21:18 +02003846 contentView.setTextViewText(R.id.title, null);
Selim Cinek41598732016-01-11 16:58:37 -08003847 contentView.setViewVisibility(R.id.text, View.GONE);
Christoph Studerfe718432014-09-01 18:21:18 +02003848 contentView.setTextViewText(R.id.text, null);
Selim Cinek29603462015-11-17 19:04:39 -08003849 contentView.setViewVisibility(R.id.text_line_1, View.GONE);
Selim Cinek41598732016-01-11 16:58:37 -08003850 contentView.setTextViewText(R.id.text_line_1, null);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003851 }
3852
Selim Cinekeaa29ca2015-11-23 13:51:13 -08003853 /**
3854 * Resets the notification header to its original state
3855 */
3856 private void resetNotificationHeader(RemoteViews contentView) {
Adrian Roosc4337a32016-08-02 18:30:34 -07003857 // Small icon doesn't need to be reset, as it's always set. Resetting would prevent
3858 // re-using the drawable when the notification is updated.
Selim Cinek7b836392015-12-04 20:02:59 -08003859 contentView.setBoolean(R.id.notification_header, "setExpanded", false);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003860 contentView.setTextViewText(R.id.app_name_text, null);
Christoph Studerca1db712014-09-10 17:31:33 +02003861 contentView.setViewVisibility(R.id.chronometer, View.GONE);
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07003862 contentView.setViewVisibility(R.id.header_text, View.GONE);
Adrian Roos9dfb78f2016-06-30 15:43:44 -07003863 contentView.setTextViewText(R.id.header_text, null);
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07003864 contentView.setViewVisibility(R.id.header_text_divider, View.GONE);
Selim Cinek29603462015-11-17 19:04:39 -08003865 contentView.setViewVisibility(R.id.time_divider, View.GONE);
Selim Cinekb85f36fd2016-04-20 18:46:36 -07003866 contentView.setViewVisibility(R.id.time, View.GONE);
Selim Cinekc848c3a2016-01-13 15:27:30 -08003867 contentView.setImageViewIcon(R.id.profile_badge, null);
3868 contentView.setViewVisibility(R.id.profile_badge, View.GONE);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003869 }
3870
3871 private void resetContentMargins(RemoteViews contentView) {
Adrian Roos2d5dbba2016-06-08 17:11:53 -07003872 contentView.setViewLayoutMarginEndDimen(R.id.line1, 0);
3873 contentView.setViewLayoutMarginEndDimen(R.id.text, 0);
Christoph Studerfe718432014-09-01 18:21:18 +02003874 }
3875
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003876 private RemoteViews applyStandardTemplate(int resId) {
Adrian Roos70d7aa32017-01-11 15:39:06 -08003877 return applyStandardTemplate(resId, mParams.reset().fillTextsFrom(this));
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02003878 }
3879
3880 /**
3881 * @param hasProgress whether the progress bar should be shown and set
3882 */
3883 private RemoteViews applyStandardTemplate(int resId, boolean hasProgress) {
Adrian Roos70d7aa32017-01-11 15:39:06 -08003884 return applyStandardTemplate(resId, mParams.reset().hasProgress(hasProgress)
3885 .fillTextsFrom(this));
Adrian Roosc1a80b02016-04-05 14:54:55 -07003886 }
3887
Adrian Roos70d7aa32017-01-11 15:39:06 -08003888 private RemoteViews applyStandardTemplate(int resId, StandardTemplateParams p) {
Kenny Guy77320062014-08-27 21:37:15 +01003889 RemoteViews contentView = new BuilderRemoteViews(mContext.getApplicationInfo(), resId);
Dan Sandler539aad42014-08-04 00:43:39 -04003890
Christoph Studerfe718432014-09-01 18:21:18 +02003891 resetStandardTemplate(contentView);
3892
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003893 final Bundle ex = mN.extras;
Selim Cinek7b9605b2017-01-19 17:36:00 -08003894 updateBackgroundColor(contentView);
Adrian Roos487374f2017-01-11 15:48:14 -08003895 bindNotificationHeader(contentView, p.ambient);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003896 bindLargeIcon(contentView);
Adrian Roos70d7aa32017-01-11 15:39:06 -08003897 boolean showProgress = handleProgressBar(p.hasProgress, contentView, ex);
3898 if (p.title != null) {
Selim Cinek860b6da2015-12-16 19:02:19 -08003899 contentView.setViewVisibility(R.id.title, View.VISIBLE);
Selim Cinek48f66b72017-08-18 16:17:51 -07003900 contentView.setTextViewText(R.id.title, processTextSpans(p.title));
Adrian Roos72171622017-01-27 10:32:06 -08003901 if (!p.ambient) {
3902 setTextViewColorPrimary(contentView, R.id.title);
3903 }
Selim Cinek954cc232016-05-20 13:29:23 -07003904 contentView.setViewLayoutWidth(R.id.title, showProgress
3905 ? ViewGroup.LayoutParams.WRAP_CONTENT
3906 : ViewGroup.LayoutParams.MATCH_PARENT);
Joe Onorato561d3852010-11-20 18:09:34 -08003907 }
Adrian Roos70d7aa32017-01-11 15:39:06 -08003908 if (p.text != null) {
Selim Cinek41598732016-01-11 16:58:37 -08003909 int textId = showProgress ? com.android.internal.R.id.text_line_1
3910 : com.android.internal.R.id.text;
Selim Cinek48f66b72017-08-18 16:17:51 -07003911 contentView.setTextViewText(textId, processTextSpans(p.text));
Adrian Roos72171622017-01-27 10:32:06 -08003912 if (!p.ambient) {
3913 setTextViewColorSecondary(contentView, textId);
3914 }
Selim Cinek41598732016-01-11 16:58:37 -08003915 contentView.setViewVisibility(textId, View.VISIBLE);
Joe Onorato561d3852010-11-20 18:09:34 -08003916 }
Selim Cinekc848c3a2016-01-13 15:27:30 -08003917
Selim Cinek279fa862016-06-14 10:57:25 -07003918 setContentMinHeight(contentView, showProgress || mN.hasLargeIcon());
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003919
Selim Cinek29603462015-11-17 19:04:39 -08003920 return contentView;
3921 }
3922
Selim Cinek48f66b72017-08-18 16:17:51 -07003923 private CharSequence processTextSpans(CharSequence text) {
3924 if (hasForegroundColor()) {
Selim Cinek87c31532017-08-18 18:53:44 -07003925 return NotificationColorUtil.clearColorSpans(text);
Selim Cinek48f66b72017-08-18 16:17:51 -07003926 }
3927 return text;
3928 }
3929
Selim Cinek7b9605b2017-01-19 17:36:00 -08003930 private void setTextViewColorPrimary(RemoteViews contentView, int id) {
3931 ensureColors();
3932 contentView.setTextColor(id, mPrimaryTextColor);
3933 }
3934
Selim Cinek48f66b72017-08-18 16:17:51 -07003935 private boolean hasForegroundColor() {
3936 return mForegroundColor != COLOR_INVALID;
3937 }
3938
Selim Cinek389edcd2017-05-11 19:16:44 -07003939 /**
3940 * @return the primary text color
3941 * @hide
3942 */
3943 @VisibleForTesting
3944 public int getPrimaryTextColor() {
Selim Cinek7b9605b2017-01-19 17:36:00 -08003945 ensureColors();
3946 return mPrimaryTextColor;
3947 }
3948
Selim Cinek389edcd2017-05-11 19:16:44 -07003949 /**
3950 * @return the secondary text color
3951 * @hide
3952 */
3953 @VisibleForTesting
3954 public int getSecondaryTextColor() {
3955 ensureColors();
3956 return mSecondaryTextColor;
3957 }
3958
Selim Cinek7b9605b2017-01-19 17:36:00 -08003959 private int getActionBarColor() {
3960 ensureColors();
3961 return mActionBarColor;
3962 }
3963
Selim Cinek622c64a2017-04-17 17:10:05 -07003964 private int getActionBarColorDeEmphasized() {
3965 int backgroundColor = getBackgroundColor();
3966 return NotificationColorUtil.getShiftedColor(backgroundColor, 12);
3967 }
3968
Selim Cinek7b9605b2017-01-19 17:36:00 -08003969 private void setTextViewColorSecondary(RemoteViews contentView, int id) {
3970 ensureColors();
3971 contentView.setTextColor(id, mSecondaryTextColor);
3972 }
3973
3974 private void ensureColors() {
3975 int backgroundColor = getBackgroundColor();
3976 if (mPrimaryTextColor == COLOR_INVALID
3977 || mSecondaryTextColor == COLOR_INVALID
3978 || mActionBarColor == COLOR_INVALID
3979 || mTextColorsAreForBackground != backgroundColor) {
3980 mTextColorsAreForBackground = backgroundColor;
Selim Cinek48f66b72017-08-18 16:17:51 -07003981 if (!hasForegroundColor() || !isColorized()) {
Selim Cinek5fb73f82017-04-20 16:55:38 -07003982 mPrimaryTextColor = NotificationColorUtil.resolvePrimaryColor(mContext,
3983 backgroundColor);
3984 mSecondaryTextColor = NotificationColorUtil.resolveSecondaryColor(mContext,
3985 backgroundColor);
Selim Cinekac5f0272017-05-02 16:05:41 -07003986 if (backgroundColor != COLOR_DEFAULT
3987 && (mBackgroundColorHint != COLOR_INVALID || isColorized())) {
3988 mPrimaryTextColor = NotificationColorUtil.findAlphaToMeetContrast(
3989 mPrimaryTextColor, backgroundColor, 4.5);
3990 mSecondaryTextColor = NotificationColorUtil.findAlphaToMeetContrast(
3991 mSecondaryTextColor, backgroundColor, 4.5);
3992 }
Selim Cinek5fb73f82017-04-20 16:55:38 -07003993 } else {
3994 double backLum = NotificationColorUtil.calculateLuminance(backgroundColor);
3995 double textLum = NotificationColorUtil.calculateLuminance(mForegroundColor);
3996 double contrast = NotificationColorUtil.calculateContrast(mForegroundColor,
3997 backgroundColor);
Selim Cinek389edcd2017-05-11 19:16:44 -07003998 // We only respect the given colors if worst case Black or White still has
3999 // contrast
4000 boolean backgroundLight = backLum > textLum
4001 && satisfiesTextContrast(backgroundColor, Color.BLACK)
4002 || backLum <= textLum
4003 && !satisfiesTextContrast(backgroundColor, Color.WHITE);
Selim Cinek5fb73f82017-04-20 16:55:38 -07004004 if (contrast < 4.5f) {
Selim Cinek389edcd2017-05-11 19:16:44 -07004005 if (backgroundLight) {
Selim Cinek5fb73f82017-04-20 16:55:38 -07004006 mSecondaryTextColor = NotificationColorUtil.findContrastColor(
4007 mForegroundColor,
4008 backgroundColor,
4009 true /* findFG */,
4010 4.5f);
4011 mPrimaryTextColor = NotificationColorUtil.changeColorLightness(
Selim Cinek389edcd2017-05-11 19:16:44 -07004012 mSecondaryTextColor, -LIGHTNESS_TEXT_DIFFERENCE_LIGHT);
Selim Cinek5fb73f82017-04-20 16:55:38 -07004013 } else {
4014 mSecondaryTextColor =
4015 NotificationColorUtil.findContrastColorAgainstDark(
4016 mForegroundColor,
4017 backgroundColor,
4018 true /* findFG */,
4019 4.5f);
4020 mPrimaryTextColor = NotificationColorUtil.changeColorLightness(
Selim Cinek389edcd2017-05-11 19:16:44 -07004021 mSecondaryTextColor, -LIGHTNESS_TEXT_DIFFERENCE_DARK);
Selim Cinek5fb73f82017-04-20 16:55:38 -07004022 }
4023 } else {
4024 mPrimaryTextColor = mForegroundColor;
4025 mSecondaryTextColor = NotificationColorUtil.changeColorLightness(
Selim Cinek389edcd2017-05-11 19:16:44 -07004026 mPrimaryTextColor, backgroundLight ? LIGHTNESS_TEXT_DIFFERENCE_LIGHT
4027 : LIGHTNESS_TEXT_DIFFERENCE_DARK);
Selim Cinek5fb73f82017-04-20 16:55:38 -07004028 if (NotificationColorUtil.calculateContrast(mSecondaryTextColor,
4029 backgroundColor) < 4.5f) {
4030 // oh well the secondary is not good enough
Selim Cinek389edcd2017-05-11 19:16:44 -07004031 if (backgroundLight) {
Selim Cinek5fb73f82017-04-20 16:55:38 -07004032 mSecondaryTextColor = NotificationColorUtil.findContrastColor(
4033 mSecondaryTextColor,
4034 backgroundColor,
4035 true /* findFG */,
4036 4.5f);
4037 } else {
4038 mSecondaryTextColor
4039 = NotificationColorUtil.findContrastColorAgainstDark(
4040 mSecondaryTextColor,
4041 backgroundColor,
4042 true /* findFG */,
4043 4.5f);
4044 }
4045 mPrimaryTextColor = NotificationColorUtil.changeColorLightness(
Selim Cinek389edcd2017-05-11 19:16:44 -07004046 mSecondaryTextColor, backgroundLight
4047 ? -LIGHTNESS_TEXT_DIFFERENCE_LIGHT
4048 : -LIGHTNESS_TEXT_DIFFERENCE_DARK);
Selim Cinek5fb73f82017-04-20 16:55:38 -07004049 }
4050 }
4051 }
Selim Cinek875ba9b2017-02-13 16:20:17 -08004052 mActionBarColor = NotificationColorUtil.resolveActionBarColor(mContext,
4053 backgroundColor);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004054 }
4055 }
4056
4057 private void updateBackgroundColor(RemoteViews contentView) {
4058 if (isColorized()) {
4059 contentView.setInt(R.id.status_bar_latest_event_content, "setBackgroundColor",
4060 getBackgroundColor());
4061 } else {
4062 // Clear it!
4063 contentView.setInt(R.id.status_bar_latest_event_content, "setBackgroundResource",
4064 0);
4065 }
4066 }
4067
Selim Cinek860b6da2015-12-16 19:02:19 -08004068 /**
4069 * @param remoteView the remote view to update the minheight in
4070 * @param hasMinHeight does it have a mimHeight
4071 * @hide
4072 */
4073 void setContentMinHeight(RemoteViews remoteView, boolean hasMinHeight) {
4074 int minHeight = 0;
4075 if (hasMinHeight) {
4076 // we need to set the minHeight of the notification
4077 minHeight = mContext.getResources().getDimensionPixelSize(
4078 com.android.internal.R.dimen.notification_min_content_height);
4079 }
4080 remoteView.setInt(R.id.notification_main_column, "setMinimumHeight", minHeight);
4081 }
4082
Selim Cinek29603462015-11-17 19:04:39 -08004083 private boolean handleProgressBar(boolean hasProgress, RemoteViews contentView, Bundle ex) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004084 final int max = ex.getInt(EXTRA_PROGRESS_MAX, 0);
4085 final int progress = ex.getInt(EXTRA_PROGRESS, 0);
4086 final boolean ind = ex.getBoolean(EXTRA_PROGRESS_INDETERMINATE);
4087 if (hasProgress && (max != 0 || ind)) {
Selim Cinek29603462015-11-17 19:04:39 -08004088 contentView.setViewVisibility(com.android.internal.R.id.progress, View.VISIBLE);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004089 contentView.setProgressBar(
Selim Cinek29603462015-11-17 19:04:39 -08004090 R.id.progress, max, progress, ind);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004091 contentView.setProgressBackgroundTintList(
4092 R.id.progress, ColorStateList.valueOf(mContext.getColor(
4093 R.color.notification_progress_background_color)));
Selim Cinek29603462015-11-17 19:04:39 -08004094 if (mN.color != COLOR_DEFAULT) {
Adrian Roos4ff3b122016-02-01 12:26:13 -08004095 ColorStateList colorStateList = ColorStateList.valueOf(resolveContrastColor());
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004096 contentView.setProgressTintList(R.id.progress, colorStateList);
4097 contentView.setProgressIndeterminateTintList(R.id.progress, colorStateList);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004098 }
Selim Cinek29603462015-11-17 19:04:39 -08004099 return true;
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004100 } else {
4101 contentView.setViewVisibility(R.id.progress, View.GONE);
Selim Cinek29603462015-11-17 19:04:39 -08004102 return false;
Jeff Sharkey1c400132011-08-05 14:50:13 -07004103 }
Joe Onorato561d3852010-11-20 18:09:34 -08004104 }
4105
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004106 private void bindLargeIcon(RemoteViews contentView) {
Selim Cinek279fa862016-06-14 10:57:25 -07004107 if (mN.mLargeIcon == null && mN.largeIcon != null) {
4108 mN.mLargeIcon = Icon.createWithBitmap(mN.largeIcon);
4109 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004110 if (mN.mLargeIcon != null) {
4111 contentView.setViewVisibility(R.id.right_icon, View.VISIBLE);
4112 contentView.setImageViewIcon(R.id.right_icon, mN.mLargeIcon);
4113 processLargeLegacyIcon(mN.mLargeIcon, contentView);
Adrian Roos2d5dbba2016-06-08 17:11:53 -07004114 int endMargin = R.dimen.notification_content_picture_margin;
4115 contentView.setViewLayoutMarginEndDimen(R.id.line1, endMargin);
4116 contentView.setViewLayoutMarginEndDimen(R.id.text, endMargin);
4117 contentView.setViewLayoutMarginEndDimen(R.id.progress, endMargin);
Selim Cinek5d6ef8d2017-05-18 22:16:00 -07004118 // Bind the reply action
4119 Action action = findReplyAction();
4120 contentView.setViewVisibility(R.id.reply_icon_action, action != null
4121 ? View.VISIBLE
4122 : View.GONE);
4123
4124 if (action != null) {
4125 int contrastColor = resolveContrastColor();
4126 contentView.setDrawableParameters(R.id.reply_icon_action,
4127 true /* targetBackground */,
4128 -1,
4129 contrastColor,
4130 PorterDuff.Mode.SRC_ATOP, -1);
4131 int iconColor = NotificationColorUtil.isColorLight(contrastColor)
4132 ? Color.BLACK : Color.WHITE;
4133 contentView.setDrawableParameters(R.id.reply_icon_action,
4134 false /* targetBackground */,
4135 -1,
4136 iconColor,
4137 PorterDuff.Mode.SRC_ATOP, -1);
4138 contentView.setOnClickPendingIntent(R.id.right_icon,
4139 action.actionIntent);
4140 contentView.setOnClickPendingIntent(R.id.reply_icon_action,
4141 action.actionIntent);
4142 contentView.setRemoteInputs(R.id.right_icon, action.mRemoteInputs);
4143 contentView.setRemoteInputs(R.id.reply_icon_action, action.mRemoteInputs);
4144
4145 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004146 }
Selim Cinek5d6ef8d2017-05-18 22:16:00 -07004147 contentView.setViewVisibility(R.id.right_icon_container, mN.mLargeIcon != null
4148 ? View.VISIBLE
4149 : View.GONE);
4150 }
4151
4152 private Action findReplyAction() {
4153 ArrayList<Action> actions = mActions;
4154 if (mOriginalActions != null) {
4155 actions = mOriginalActions;
4156 }
4157 int numActions = actions.size();
4158 for (int i = 0; i < numActions; i++) {
4159 Action action = actions.get(i);
4160 if (hasValidRemoteInput(action)) {
4161 return action;
4162 }
4163 }
4164 return null;
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004165 }
4166
Adrian Roos487374f2017-01-11 15:48:14 -08004167 private void bindNotificationHeader(RemoteViews contentView, boolean ambient) {
4168 bindSmallIcon(contentView, ambient);
4169 bindHeaderAppName(contentView, ambient);
4170 if (!ambient) {
4171 // Ambient view does not have these
4172 bindHeaderText(contentView);
4173 bindHeaderChronometerAndTime(contentView);
Adrian Roos487374f2017-01-11 15:48:14 -08004174 bindProfileBadge(contentView);
4175 }
Adrian Roosd83e9992017-03-16 15:17:57 -07004176 bindExpandButton(contentView);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004177 }
4178
4179 private void bindExpandButton(RemoteViews contentView) {
Selim Cinek99104832017-01-25 14:47:33 -08004180 int color = getPrimaryHighlightColor();
Selim Cinek7b9605b2017-01-19 17:36:00 -08004181 contentView.setDrawableParameters(R.id.expand_button, false, -1, color,
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004182 PorterDuff.Mode.SRC_ATOP, -1);
Selim Cinekea4bef72015-12-02 15:51:10 -08004183 contentView.setInt(R.id.notification_header, "setOriginalNotificationColor",
Selim Cinek7b9605b2017-01-19 17:36:00 -08004184 color);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004185 }
4186
Selim Cinek99104832017-01-25 14:47:33 -08004187 /**
4188 * @return the color that is used as the first primary highlight color. This is applied
4189 * in several places like the action buttons or the app name in the header.
4190 */
4191 private int getPrimaryHighlightColor() {
4192 return isColorized() ? getPrimaryTextColor() : resolveContrastColor();
4193 }
4194
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004195 private void bindHeaderChronometerAndTime(RemoteViews contentView) {
4196 if (showsTimeOrChronometer()) {
Selim Cinek29603462015-11-17 19:04:39 -08004197 contentView.setViewVisibility(R.id.time_divider, View.VISIBLE);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004198 setTextViewColorSecondary(contentView, R.id.time_divider);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004199 if (mN.extras.getBoolean(EXTRA_SHOW_CHRONOMETER)) {
4200 contentView.setViewVisibility(R.id.chronometer, View.VISIBLE);
4201 contentView.setLong(R.id.chronometer, "setBase",
4202 mN.when + (SystemClock.elapsedRealtime() - System.currentTimeMillis()));
4203 contentView.setBoolean(R.id.chronometer, "setStarted", true);
Adrian Roos96b7e202016-05-17 13:50:38 -07004204 boolean countsDown = mN.extras.getBoolean(EXTRA_CHRONOMETER_COUNT_DOWN);
Selim Cinekc3b752e2016-04-20 16:13:59 -07004205 contentView.setChronometerCountDown(R.id.chronometer, countsDown);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004206 setTextViewColorSecondary(contentView, R.id.chronometer);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004207 } else {
4208 contentView.setViewVisibility(R.id.time, View.VISIBLE);
4209 contentView.setLong(R.id.time, "setTime", mN.when);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004210 setTextViewColorSecondary(contentView, R.id.time);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004211 }
Selim Cinekb85f36fd2016-04-20 18:46:36 -07004212 } else {
4213 // We still want a time to be set but gone, such that we can show and hide it
4214 // on demand in case it's a child notification without anything in the header
4215 contentView.setLong(R.id.time, "setTime", mN.when != 0 ? mN.when : mN.creationTime);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004216 }
4217 }
4218
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07004219 private void bindHeaderText(RemoteViews contentView) {
4220 CharSequence headerText = mN.extras.getCharSequence(EXTRA_SUB_TEXT);
4221 if (headerText == null && mStyle != null && mStyle.mSummaryTextSet
Selim Cinek03d0d652015-11-13 13:18:09 -05004222 && mStyle.hasSummaryInHeader()) {
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07004223 headerText = mStyle.mSummaryText;
Selim Cinek03d0d652015-11-13 13:18:09 -05004224 }
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07004225 if (headerText == null
4226 && mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N
4227 && mN.extras.getCharSequence(EXTRA_INFO_TEXT) != null) {
4228 headerText = mN.extras.getCharSequence(EXTRA_INFO_TEXT);
4229 }
4230 if (headerText != null) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004231 // TODO: Remove the span entirely to only have the string with propper formating.
Selim Cinek48f66b72017-08-18 16:17:51 -07004232 contentView.setTextViewText(R.id.header_text, processTextSpans(
4233 processLegacyText(headerText)));
Selim Cinek7b9605b2017-01-19 17:36:00 -08004234 setTextViewColorSecondary(contentView, R.id.header_text);
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07004235 contentView.setViewVisibility(R.id.header_text, View.VISIBLE);
4236 contentView.setViewVisibility(R.id.header_text_divider, View.VISIBLE);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004237 setTextViewColorSecondary(contentView, R.id.header_text_divider);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004238 }
4239 }
4240
Adrian Rooseba05822016-04-22 17:09:27 -07004241 /**
4242 * @hide
4243 */
4244 public String loadHeaderAppName() {
Dan Sandler732bd6c2016-04-12 14:20:32 -04004245 CharSequence name = null;
4246 final PackageManager pm = mContext.getPackageManager();
4247 if (mN.extras.containsKey(EXTRA_SUBSTITUTE_APP_NAME)) {
4248 // only system packages which lump together a bunch of unrelated stuff
4249 // may substitute a different name to make the purpose of the
4250 // notification more clear. the correct package label should always
4251 // be accessible via SystemUI.
4252 final String pkg = mContext.getPackageName();
4253 final String subName = mN.extras.getString(EXTRA_SUBSTITUTE_APP_NAME);
4254 if (PackageManager.PERMISSION_GRANTED == pm.checkPermission(
4255 android.Manifest.permission.SUBSTITUTE_NOTIFICATION_APP_NAME, pkg)) {
4256 name = subName;
4257 } else {
4258 Log.w(TAG, "warning: pkg "
4259 + pkg + " attempting to substitute app name '" + subName
4260 + "' without holding perm "
4261 + android.Manifest.permission.SUBSTITUTE_NOTIFICATION_APP_NAME);
4262 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004263 }
Dan Sandler732bd6c2016-04-12 14:20:32 -04004264 if (TextUtils.isEmpty(name)) {
4265 name = pm.getApplicationLabel(mContext.getApplicationInfo());
4266 }
4267 if (TextUtils.isEmpty(name)) {
4268 // still nothing?
4269 return null;
4270 }
4271
4272 return String.valueOf(name);
4273 }
Adrian Roos487374f2017-01-11 15:48:14 -08004274 private void bindHeaderAppName(RemoteViews contentView, boolean ambient) {
Dan Sandler732bd6c2016-04-12 14:20:32 -04004275 contentView.setTextViewText(R.id.app_name_text, loadHeaderAppName());
Adrian Roos72171622017-01-27 10:32:06 -08004276 if (isColorized() && !ambient) {
Selim Cinek7b9605b2017-01-19 17:36:00 -08004277 setTextViewColorPrimary(contentView, R.id.app_name_text);
4278 } else {
4279 contentView.setTextColor(R.id.app_name_text,
4280 ambient ? resolveAmbientColor() : resolveContrastColor());
4281 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004282 }
4283
Adrian Roos487374f2017-01-11 15:48:14 -08004284 private void bindSmallIcon(RemoteViews contentView, boolean ambient) {
Selim Cinek279fa862016-06-14 10:57:25 -07004285 if (mN.mSmallIcon == null && mN.icon != 0) {
4286 mN.mSmallIcon = Icon.createWithResource(mContext, mN.icon);
4287 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004288 contentView.setImageViewIcon(R.id.icon, mN.mSmallIcon);
Adrian Roos9b45a152016-06-28 13:32:29 -07004289 contentView.setDrawableParameters(R.id.icon, false /* targetBackground */,
4290 -1 /* alpha */, -1 /* colorFilter */, null /* mode */, mN.iconLevel);
Adrian Roos487374f2017-01-11 15:48:14 -08004291 processSmallIconColor(mN.mSmallIcon, contentView, ambient);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004292 }
4293
Jorim Jaggi445d3c02014-08-19 22:33:42 +02004294 /**
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004295 * @return true if the built notification will show the time or the chronometer; false
4296 * otherwise
4297 */
4298 private boolean showsTimeOrChronometer() {
Selim Cinekc2c0b042016-05-18 17:13:46 -07004299 return mN.showsTime() || mN.showsChronometer();
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004300 }
4301
Christoph Studerfe718432014-09-01 18:21:18 +02004302 private void resetStandardTemplateWithActions(RemoteViews big) {
Adrian Roos4c1fcc82016-03-31 14:39:39 -07004303 // actions_container is only reset when there are no actions to avoid focus issues with
4304 // remote inputs.
Christoph Studerfe718432014-09-01 18:21:18 +02004305 big.setViewVisibility(R.id.actions, View.GONE);
Christoph Studerfe718432014-09-01 18:21:18 +02004306 big.removeAllViews(R.id.actions);
Adrian Roose458aa82015-12-08 16:17:19 -08004307
4308 big.setViewVisibility(R.id.notification_material_reply_container, View.GONE);
4309 big.setTextViewText(R.id.notification_material_reply_text_1, null);
4310
4311 big.setViewVisibility(R.id.notification_material_reply_text_2, View.GONE);
4312 big.setTextViewText(R.id.notification_material_reply_text_2, null);
4313 big.setViewVisibility(R.id.notification_material_reply_text_3, View.GONE);
4314 big.setTextViewText(R.id.notification_material_reply_text_3, null);
Adrian Roosf852a422016-06-03 13:33:43 -07004315
4316 big.setViewLayoutMarginBottomDimen(R.id.notification_action_list_margin_target, 0);
Christoph Studerfe718432014-09-01 18:21:18 +02004317 }
4318
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004319 private RemoteViews applyStandardTemplateWithActions(int layoutId) {
Adrian Roos70d7aa32017-01-11 15:39:06 -08004320 return applyStandardTemplateWithActions(layoutId, mParams.reset().fillTextsFrom(this));
Adrian Roos48d746a2016-04-12 14:57:28 -07004321 }
4322
Adrian Roos70d7aa32017-01-11 15:39:06 -08004323 private RemoteViews applyStandardTemplateWithActions(int layoutId,
4324 StandardTemplateParams p) {
4325 RemoteViews big = applyStandardTemplate(layoutId, p);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004326
Christoph Studerfe718432014-09-01 18:21:18 +02004327 resetStandardTemplateWithActions(big);
4328
Adrian Roose458aa82015-12-08 16:17:19 -08004329 boolean validRemoteInput = false;
4330
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004331 int N = mActions.size();
Adrian Roos487374f2017-01-11 15:48:14 -08004332 boolean emphazisedMode = mN.fullScreenIntent != null && !p.ambient;
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004333 big.setBoolean(R.id.actions, "setEmphasizedMode", emphazisedMode);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004334 if (N > 0) {
Adrian Roos7052de52016-03-03 15:53:34 -08004335 big.setViewVisibility(R.id.actions_container, View.VISIBLE);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004336 big.setViewVisibility(R.id.actions, View.VISIBLE);
Adrian Roos487374f2017-01-11 15:48:14 -08004337 if (p.ambient) {
4338 big.setInt(R.id.actions, "setBackgroundColor", Color.TRANSPARENT);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004339 } else if (isColorized()) {
4340 big.setInt(R.id.actions, "setBackgroundColor", getActionBarColor());
4341 } else {
4342 big.setInt(R.id.actions, "setBackgroundColor", mContext.getColor(
4343 R.color.notification_action_list));
Adrian Roos487374f2017-01-11 15:48:14 -08004344 }
Adrian Roosf852a422016-06-03 13:33:43 -07004345 big.setViewLayoutMarginBottomDimen(R.id.notification_action_list_margin_target,
4346 R.dimen.notification_action_list_height);
Daniel Sandler8680bf82012-05-15 16:52:52 -04004347 if (N>MAX_ACTION_BUTTONS) N=MAX_ACTION_BUTTONS;
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004348 for (int i=0; i<N; i++) {
Adrian Roose458aa82015-12-08 16:17:19 -08004349 Action action = mActions.get(i);
4350 validRemoteInput |= hasValidRemoteInput(action);
4351
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004352 final RemoteViews button = generateActionButton(action, emphazisedMode,
Adrian Roos487374f2017-01-11 15:48:14 -08004353 i % 2 != 0, p.ambient);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004354 big.addView(R.id.actions, button);
4355 }
Adrian Roos4c1fcc82016-03-31 14:39:39 -07004356 } else {
4357 big.setViewVisibility(R.id.actions_container, View.GONE);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004358 }
Adrian Roose458aa82015-12-08 16:17:19 -08004359
4360 CharSequence[] replyText = mN.extras.getCharSequenceArray(EXTRA_REMOTE_INPUT_HISTORY);
Adrian Roos487374f2017-01-11 15:48:14 -08004361 if (!p.ambient && validRemoteInput && replyText != null
Adrian Roose458aa82015-12-08 16:17:19 -08004362 && replyText.length > 0 && !TextUtils.isEmpty(replyText[0])) {
4363 big.setViewVisibility(R.id.notification_material_reply_container, View.VISIBLE);
Selim Cinek48f66b72017-08-18 16:17:51 -07004364 big.setTextViewText(R.id.notification_material_reply_text_1,
4365 processTextSpans(replyText[0]));
Selim Cinek7b9605b2017-01-19 17:36:00 -08004366 setTextViewColorSecondary(big, R.id.notification_material_reply_text_1);
Adrian Roose458aa82015-12-08 16:17:19 -08004367
4368 if (replyText.length > 1 && !TextUtils.isEmpty(replyText[1])) {
4369 big.setViewVisibility(R.id.notification_material_reply_text_2, View.VISIBLE);
Selim Cinek48f66b72017-08-18 16:17:51 -07004370 big.setTextViewText(R.id.notification_material_reply_text_2,
4371 processTextSpans(replyText[1]));
Selim Cinek7b9605b2017-01-19 17:36:00 -08004372 setTextViewColorSecondary(big, R.id.notification_material_reply_text_2);
Adrian Roose458aa82015-12-08 16:17:19 -08004373
4374 if (replyText.length > 2 && !TextUtils.isEmpty(replyText[2])) {
4375 big.setViewVisibility(
4376 R.id.notification_material_reply_text_3, View.VISIBLE);
Selim Cinek48f66b72017-08-18 16:17:51 -07004377 big.setTextViewText(R.id.notification_material_reply_text_3,
4378 processTextSpans(replyText[2]));
Selim Cinek7b9605b2017-01-19 17:36:00 -08004379 setTextViewColorSecondary(big, R.id.notification_material_reply_text_3);
Adrian Roose458aa82015-12-08 16:17:19 -08004380 }
4381 }
4382 }
4383
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004384 return big;
4385 }
4386
Adrian Roose458aa82015-12-08 16:17:19 -08004387 private boolean hasValidRemoteInput(Action action) {
4388 if (TextUtils.isEmpty(action.title) || action.actionIntent == null) {
4389 // Weird actions
4390 return false;
4391 }
4392
4393 RemoteInput[] remoteInputs = action.getRemoteInputs();
4394 if (remoteInputs == null) {
4395 return false;
4396 }
4397
4398 for (RemoteInput r : remoteInputs) {
4399 CharSequence[] choices = r.getChoices();
4400 if (r.getAllowFreeFormInput() || (choices != null && choices.length != 0)) {
4401 return true;
4402 }
4403 }
4404 return false;
4405 }
4406
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004407 /**
4408 * Construct a RemoteViews for the final 1U notification layout. In order:
4409 * 1. Custom contentView from the caller
4410 * 2. Style's proposed content view
4411 * 3. Standard template view
4412 */
Julia Reynolds3b848122016-02-26 10:45:32 -05004413 public RemoteViews createContentView() {
Selim Cinek7d1009b2017-01-25 15:28:28 -08004414 return createContentView(false /* increasedheight */ );
4415 }
4416
4417 /**
4418 * Construct a RemoteViews for the smaller content view.
4419 *
4420 * @param increasedHeight true if this layout be created with an increased height. Some
4421 * styles may support showing more then just that basic 1U size
4422 * and the system may decide to render important notifications
4423 * slightly bigger even when collapsed.
4424 *
4425 * @hide
4426 */
4427 public RemoteViews createContentView(boolean increasedHeight) {
Selim Cineka7679b62017-05-10 16:33:25 -07004428 if (mN.contentView != null && useExistingRemoteView()) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004429 return mN.contentView;
4430 } else if (mStyle != null) {
Selim Cinek7d1009b2017-01-25 15:28:28 -08004431 final RemoteViews styleView = mStyle.makeContentView(increasedHeight);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004432 if (styleView != null) {
4433 return styleView;
4434 }
Joe Onorato46439ce2010-11-19 13:56:21 -08004435 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004436 return applyStandardTemplate(getBaseLayoutResource());
Joe Onorato46439ce2010-11-19 13:56:21 -08004437 }
4438
Selim Cineka7679b62017-05-10 16:33:25 -07004439 private boolean useExistingRemoteView() {
4440 return mStyle == null || (!mStyle.displayCustomViewInline()
4441 && !mRebuildStyledRemoteViews);
4442 }
4443
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004444 /**
4445 * Construct a RemoteViews for the final big notification layout.
4446 */
Julia Reynolds3b848122016-02-26 10:45:32 -05004447 public RemoteViews createBigContentView() {
Selim Cinek850a8542015-11-11 11:48:36 -05004448 RemoteViews result = null;
Selim Cineka7679b62017-05-10 16:33:25 -07004449 if (mN.bigContentView != null && useExistingRemoteView()) {
Julia Reynolds089e3e42015-11-18 09:59:57 -05004450 return mN.bigContentView;
4451 } else if (mStyle != null) {
Selim Cinek850a8542015-11-11 11:48:36 -05004452 result = mStyle.makeBigContentView();
Selim Cinek90dcf6d2015-11-18 20:24:13 -08004453 hideLine1Text(result);
Selim Cinekcc10bfb2016-02-10 16:24:21 -08004454 } else if (mActions.size() != 0) {
4455 result = applyStandardTemplateWithActions(getBigBaseLayoutResource());
Selim Cinek850a8542015-11-11 11:48:36 -05004456 }
Selim Cinek6743c0b2017-01-18 18:24:01 -08004457 makeHeaderExpanded(result);
Selim Cinek850a8542015-11-11 11:48:36 -05004458 return result;
4459 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004460
Selim Cinekeaa29ca2015-11-23 13:51:13 -08004461 /**
Selim Cinek414ad332017-02-24 19:06:12 -08004462 * Construct a RemoteViews for the final notification header only. This will not be
4463 * colorized.
Selim Cinekeaa29ca2015-11-23 13:51:13 -08004464 *
Adrian Roos6f6e1592017-05-02 16:22:53 -07004465 * @param ambient if true, generate the header for the ambient display layout.
Selim Cinekeaa29ca2015-11-23 13:51:13 -08004466 * @hide
4467 */
Adrian Roos6f6e1592017-05-02 16:22:53 -07004468 public RemoteViews makeNotificationHeader(boolean ambient) {
Selim Cinek414ad332017-02-24 19:06:12 -08004469 Boolean colorized = (Boolean) mN.extras.get(EXTRA_COLORIZED);
4470 mN.extras.putBoolean(EXTRA_COLORIZED, false);
Selim Cinekeaa29ca2015-11-23 13:51:13 -08004471 RemoteViews header = new BuilderRemoteViews(mContext.getApplicationInfo(),
Adrian Roos6f6e1592017-05-02 16:22:53 -07004472 ambient ? R.layout.notification_template_ambient_header
4473 : R.layout.notification_template_header);
Selim Cinekeaa29ca2015-11-23 13:51:13 -08004474 resetNotificationHeader(header);
Adrian Roos6f6e1592017-05-02 16:22:53 -07004475 bindNotificationHeader(header, ambient);
Selim Cinek414ad332017-02-24 19:06:12 -08004476 if (colorized != null) {
4477 mN.extras.putBoolean(EXTRA_COLORIZED, colorized);
4478 } else {
4479 mN.extras.remove(EXTRA_COLORIZED);
4480 }
Selim Cinekeaa29ca2015-11-23 13:51:13 -08004481 return header;
4482 }
4483
Adrian Roos487374f2017-01-11 15:48:14 -08004484 /**
4485 * Construct a RemoteViews for the ambient version of the notification.
4486 *
4487 * @hide
4488 */
4489 public RemoteViews makeAmbientNotification() {
4490 RemoteViews ambient = applyStandardTemplateWithActions(
4491 R.layout.notification_template_material_ambient,
Adrian Roos0bc3f6a2017-03-06 11:54:05 -08004492 mParams.reset().ambient(true).fillTextsFrom(this).hasProgress(false));
Adrian Roos487374f2017-01-11 15:48:14 -08004493 return ambient;
4494 }
4495
Selim Cinek29603462015-11-17 19:04:39 -08004496 private void hideLine1Text(RemoteViews result) {
Selim Cinekcc10bfb2016-02-10 16:24:21 -08004497 if (result != null) {
4498 result.setViewVisibility(R.id.text_line_1, View.GONE);
4499 }
Selim Cinek29603462015-11-17 19:04:39 -08004500 }
4501
Selim Cinek6743c0b2017-01-18 18:24:01 -08004502 /**
4503 * Adapt the Notification header if this view is used as an expanded view.
4504 *
4505 * @hide
4506 */
4507 public static void makeHeaderExpanded(RemoteViews result) {
Selim Cinekcc10bfb2016-02-10 16:24:21 -08004508 if (result != null) {
4509 result.setBoolean(R.id.notification_header, "setExpanded", true);
4510 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004511 }
4512
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004513 /**
4514 * Construct a RemoteViews for the final heads-up notification layout.
Selim Cinek87ed69b2017-02-09 15:59:43 -08004515 *
4516 * @param increasedHeight true if this layout be created with an increased height. Some
4517 * styles may support showing more then just that basic 1U size
4518 * and the system may decide to render important notifications
4519 * slightly bigger even when collapsed.
4520 *
4521 * @hide
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004522 */
Selim Cinek87ed69b2017-02-09 15:59:43 -08004523 public RemoteViews createHeadsUpContentView(boolean increasedHeight) {
Selim Cineka7679b62017-05-10 16:33:25 -07004524 if (mN.headsUpContentView != null && useExistingRemoteView()) {
Julia Reynolds089e3e42015-11-18 09:59:57 -05004525 return mN.headsUpContentView;
4526 } else if (mStyle != null) {
Selim Cinek87ed69b2017-02-09 15:59:43 -08004527 final RemoteViews styleView = mStyle.makeHeadsUpContentView(increasedHeight);
4528 if (styleView != null) {
4529 return styleView;
4530 }
Julia Reynolds089e3e42015-11-18 09:59:57 -05004531 } else if (mActions.size() == 0) {
4532 return null;
4533 }
4534
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004535 return applyStandardTemplateWithActions(getBigBaseLayoutResource());
Chris Wren8fd39ec2014-02-27 17:43:26 -05004536 }
4537
Selim Cinek624c02db2015-12-14 21:00:02 -08004538 /**
Selim Cinek87ed69b2017-02-09 15:59:43 -08004539 * Construct a RemoteViews for the final heads-up notification layout.
4540 */
4541 public RemoteViews createHeadsUpContentView() {
4542 return createHeadsUpContentView(false /* useIncreasedHeight */);
4543 }
4544
4545 /**
Selim Cinek624c02db2015-12-14 21:00:02 -08004546 * Construct a RemoteViews for the display in public contexts like on the lockscreen.
4547 *
4548 * @hide
4549 */
4550 public RemoteViews makePublicContentView() {
Adrian Roos1a1ecfc2017-04-17 11:17:59 -07004551 return makePublicView(false /* ambient */);
4552 }
4553
4554 /**
4555 * Construct a RemoteViews for the display in public contexts like on the lockscreen.
4556 *
4557 * @hide
4558 */
4559 public RemoteViews makePublicAmbientNotification() {
4560 return makePublicView(true /* ambient */);
4561 }
4562
4563 private RemoteViews makePublicView(boolean ambient) {
Selim Cinek624c02db2015-12-14 21:00:02 -08004564 if (mN.publicVersion != null) {
4565 final Builder builder = recoverBuilder(mContext, mN.publicVersion);
Adrian Roos1a1ecfc2017-04-17 11:17:59 -07004566 return ambient ? builder.makeAmbientNotification() : builder.createContentView();
Selim Cinek624c02db2015-12-14 21:00:02 -08004567 }
4568 Bundle savedBundle = mN.extras;
4569 Style style = mStyle;
4570 mStyle = null;
4571 Icon largeIcon = mN.mLargeIcon;
4572 mN.mLargeIcon = null;
Selim Cinek279fa862016-06-14 10:57:25 -07004573 Bitmap largeIconLegacy = mN.largeIcon;
4574 mN.largeIcon = null;
Adrian Roosb19b06b2017-05-02 18:54:40 -07004575 ArrayList<Action> actions = mActions;
4576 mActions = new ArrayList<>();
Selim Cinek624c02db2015-12-14 21:00:02 -08004577 Bundle publicExtras = new Bundle();
4578 publicExtras.putBoolean(EXTRA_SHOW_WHEN,
4579 savedBundle.getBoolean(EXTRA_SHOW_WHEN));
4580 publicExtras.putBoolean(EXTRA_SHOW_CHRONOMETER,
4581 savedBundle.getBoolean(EXTRA_SHOW_CHRONOMETER));
Adrian Roos96b7e202016-05-17 13:50:38 -07004582 publicExtras.putBoolean(EXTRA_CHRONOMETER_COUNT_DOWN,
4583 savedBundle.getBoolean(EXTRA_CHRONOMETER_COUNT_DOWN));
Selim Cinek624c02db2015-12-14 21:00:02 -08004584 mN.extras = publicExtras;
Selim Cinek499c20f2017-07-20 14:06:09 -07004585 RemoteViews view;
4586 if (ambient) {
4587 publicExtras.putCharSequence(EXTRA_TITLE,
4588 mContext.getString(com.android.internal.R.string.notification_hidden_text));
4589 view = makeAmbientNotification();
4590 } else{
4591 view = makeNotificationHeader(false /* ambient */);
4592 view.setBoolean(R.id.notification_header, "setExpandOnlyOnButton", true);
4593 }
Selim Cinek624c02db2015-12-14 21:00:02 -08004594 mN.extras = savedBundle;
4595 mN.mLargeIcon = largeIcon;
Selim Cinek279fa862016-06-14 10:57:25 -07004596 mN.largeIcon = largeIconLegacy;
Adrian Roosb19b06b2017-05-02 18:54:40 -07004597 mActions = actions;
Selim Cinek624c02db2015-12-14 21:00:02 -08004598 mStyle = style;
Adrian Roos1a1ecfc2017-04-17 11:17:59 -07004599 return view;
Selim Cinek624c02db2015-12-14 21:00:02 -08004600 }
4601
Selim Cinek6743c0b2017-01-18 18:24:01 -08004602 /**
4603 * Construct a content view for the display when low - priority
4604 *
4605 * @param useRegularSubtext uses the normal subtext set if there is one available. Otherwise
4606 * a new subtext is created consisting of the content of the
4607 * notification.
4608 * @hide
4609 */
4610 public RemoteViews makeLowPriorityContentView(boolean useRegularSubtext) {
4611 int color = mN.color;
4612 mN.color = COLOR_DEFAULT;
4613 CharSequence summary = mN.extras.getCharSequence(EXTRA_SUB_TEXT);
4614 if (!useRegularSubtext || TextUtils.isEmpty(summary)) {
4615 CharSequence newSummary = createSummaryText();
4616 if (!TextUtils.isEmpty(newSummary)) {
4617 mN.extras.putCharSequence(EXTRA_SUB_TEXT, newSummary);
4618 }
4619 }
Selim Cinek875ba9b2017-02-13 16:20:17 -08004620
Adrian Roos6f6e1592017-05-02 16:22:53 -07004621 RemoteViews header = makeNotificationHeader(false /* ambient */);
Selim Cinek1b554392017-02-28 17:22:49 -08004622 header.setBoolean(R.id.notification_header, "setAcceptAllTouches", true);
Selim Cinek6743c0b2017-01-18 18:24:01 -08004623 if (summary != null) {
4624 mN.extras.putCharSequence(EXTRA_SUB_TEXT, summary);
4625 } else {
4626 mN.extras.remove(EXTRA_SUB_TEXT);
4627 }
4628 mN.color = color;
4629 return header;
4630 }
Selim Cinek624c02db2015-12-14 21:00:02 -08004631
Selim Cinek6743c0b2017-01-18 18:24:01 -08004632 private CharSequence createSummaryText() {
4633 CharSequence titleText = mN.extras.getCharSequence(Notification.EXTRA_TITLE);
4634 if (USE_ONLY_TITLE_IN_LOW_PRIORITY_SUMMARY) {
4635 return titleText;
4636 }
4637 SpannableStringBuilder summary = new SpannableStringBuilder();
4638 if (titleText == null) {
4639 titleText = mN.extras.getCharSequence(Notification.EXTRA_TITLE_BIG);
4640 }
4641 BidiFormatter bidi = BidiFormatter.getInstance();
4642 if (titleText != null) {
4643 summary.append(bidi.unicodeWrap(titleText));
4644 }
4645 CharSequence contentText = mN.extras.getCharSequence(Notification.EXTRA_TEXT);
4646 if (titleText != null && contentText != null) {
4647 summary.append(bidi.unicodeWrap(mContext.getText(
4648 R.string.notification_header_divider_symbol_with_spaces)));
4649 }
4650 if (contentText != null) {
4651 summary.append(bidi.unicodeWrap(contentText));
4652 }
4653 return summary;
4654 }
Chris Wren8fd39ec2014-02-27 17:43:26 -05004655
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004656 private RemoteViews generateActionButton(Action action, boolean emphazisedMode,
Adrian Roos487374f2017-01-11 15:48:14 -08004657 boolean oddAction, boolean ambient) {
Daniel Sandler8680bf82012-05-15 16:52:52 -04004658 final boolean tombstone = (action.actionIntent == null);
Selim Cinekf33b1112015-07-15 17:45:11 -07004659 RemoteViews button = new BuilderRemoteViews(mContext.getApplicationInfo(),
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004660 emphazisedMode ? getEmphasizedActionLayoutResource()
4661 : tombstone ? getActionTombstoneLayoutResource()
4662 : getActionLayoutResource());
Daniel Sandler8680bf82012-05-15 16:52:52 -04004663 if (!tombstone) {
Daniel Sandlere5518842012-05-10 16:20:40 -04004664 button.setOnClickPendingIntent(R.id.action0, action.actionIntent);
Daniel Sandlere5518842012-05-10 16:20:40 -04004665 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004666 button.setContentDescription(R.id.action0, action.title);
Adrian Roosfe84e1f2015-11-04 15:55:39 -08004667 if (action.mRemoteInputs != null) {
4668 button.setRemoteInputs(R.id.action0, action.mRemoteInputs);
4669 }
Selim Cinek7b9605b2017-01-19 17:36:00 -08004670 // TODO: handle emphasized mode / actions right
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004671 if (emphazisedMode) {
Selim Cinek981962e2016-07-20 20:41:58 -07004672 // change the background bgColor
Selim Cinek622c64a2017-04-17 17:10:05 -07004673 int bgColor;
4674 if (isColorized()) {
4675 bgColor = oddAction ? getActionBarColor() : getActionBarColorDeEmphasized();
4676 } else {
4677 bgColor = mContext.getColor(oddAction ? R.color.notification_action_list
4678 : R.color.notification_action_list_dark);
4679 }
Selim Cinek981962e2016-07-20 20:41:58 -07004680 button.setDrawableParameters(R.id.button_holder, true, -1, bgColor,
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004681 PorterDuff.Mode.SRC_ATOP, -1);
Selim Cinek981962e2016-07-20 20:41:58 -07004682 CharSequence title = action.title;
4683 ColorStateList[] outResultColor = null;
4684 if (isLegacy()) {
Selim Cinek87c31532017-08-18 18:53:44 -07004685 title = NotificationColorUtil.clearColorSpans(title);
Selim Cinek981962e2016-07-20 20:41:58 -07004686 } else {
4687 outResultColor = new ColorStateList[1];
4688 title = ensureColorSpanContrast(title, bgColor, outResultColor);
4689 }
Selim Cinek48f66b72017-08-18 16:17:51 -07004690 button.setTextViewText(R.id.action0, processTextSpans(title));
Selim Cinek7b9605b2017-01-19 17:36:00 -08004691 setTextViewColorPrimary(button, R.id.action0);
Selim Cinek981962e2016-07-20 20:41:58 -07004692 if (outResultColor != null && outResultColor[0] != null) {
4693 // We need to set the text color as well since changing a text to uppercase
4694 // clears its spans.
4695 button.setTextColor(R.id.action0, outResultColor[0]);
Anthony Chenad4d1582017-04-10 16:07:58 -07004696 } else if (mN.color != COLOR_DEFAULT && !isColorized() && mTintActionButtons) {
Selim Cinek981962e2016-07-20 20:41:58 -07004697 button.setTextColor(R.id.action0,resolveContrastColor());
4698 }
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004699 } else {
Selim Cinek48f66b72017-08-18 16:17:51 -07004700 button.setTextViewText(R.id.action0, processTextSpans(
4701 processLegacyText(action.title)));
Adrian Roos72171622017-01-27 10:32:06 -08004702 if (isColorized() && !ambient) {
Selim Cinek7b9605b2017-01-19 17:36:00 -08004703 setTextViewColorPrimary(button, R.id.action0);
Anthony Chenad4d1582017-04-10 16:07:58 -07004704 } else if (mN.color != COLOR_DEFAULT && mTintActionButtons) {
Adrian Roos487374f2017-01-11 15:48:14 -08004705 button.setTextColor(R.id.action0,
4706 ambient ? resolveAmbientColor() : resolveContrastColor());
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004707 }
Adrian Roosfe84e1f2015-11-04 15:55:39 -08004708 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004709 return button;
4710 }
4711
Joe Onoratocb109a02011-01-18 17:57:41 -08004712 /**
Selim Cinek981962e2016-07-20 20:41:58 -07004713 * Ensures contrast on color spans against a background color. also returns the color of the
4714 * text if a span was found that spans over the whole text.
4715 *
4716 * @param charSequence the charSequence on which the spans are
4717 * @param background the background color to ensure the contrast against
4718 * @param outResultColor an array in which a color will be returned as the first element if
4719 * there exists a full length color span.
4720 * @return the contrasted charSequence
4721 */
4722 private CharSequence ensureColorSpanContrast(CharSequence charSequence, int background,
4723 ColorStateList[] outResultColor) {
4724 if (charSequence instanceof Spanned) {
4725 Spanned ss = (Spanned) charSequence;
4726 Object[] spans = ss.getSpans(0, ss.length(), Object.class);
4727 SpannableStringBuilder builder = new SpannableStringBuilder(ss.toString());
4728 for (Object span : spans) {
4729 Object resultSpan = span;
4730 int spanStart = ss.getSpanStart(span);
4731 int spanEnd = ss.getSpanEnd(span);
4732 boolean fullLength = (spanEnd - spanStart) == charSequence.length();
4733 if (resultSpan instanceof CharacterStyle) {
4734 resultSpan = ((CharacterStyle) span).getUnderlying();
4735 }
4736 if (resultSpan instanceof TextAppearanceSpan) {
4737 TextAppearanceSpan originalSpan = (TextAppearanceSpan) resultSpan;
4738 ColorStateList textColor = originalSpan.getTextColor();
4739 if (textColor != null) {
4740 int[] colors = textColor.getColors();
4741 int[] newColors = new int[colors.length];
4742 for (int i = 0; i < newColors.length; i++) {
4743 newColors[i] = NotificationColorUtil.ensureLargeTextContrast(
Anthony Chenad4d1582017-04-10 16:07:58 -07004744 colors[i], background, mInNightMode);
Selim Cinek981962e2016-07-20 20:41:58 -07004745 }
4746 textColor = new ColorStateList(textColor.getStates().clone(),
4747 newColors);
4748 resultSpan = new TextAppearanceSpan(
4749 originalSpan.getFamily(),
4750 originalSpan.getTextStyle(),
4751 originalSpan.getTextSize(),
4752 textColor,
4753 originalSpan.getLinkTextColor());
4754 if (fullLength) {
4755 outResultColor[0] = new ColorStateList(
4756 textColor.getStates().clone(), newColors);
4757 }
4758 }
4759 } else if (resultSpan instanceof ForegroundColorSpan) {
4760 ForegroundColorSpan originalSpan = (ForegroundColorSpan) resultSpan;
4761 int foregroundColor = originalSpan.getForegroundColor();
4762 foregroundColor = NotificationColorUtil.ensureLargeTextContrast(
Anthony Chenad4d1582017-04-10 16:07:58 -07004763 foregroundColor, background, mInNightMode);
Selim Cinek981962e2016-07-20 20:41:58 -07004764 resultSpan = new ForegroundColorSpan(foregroundColor);
4765 if (fullLength) {
4766 outResultColor[0] = ColorStateList.valueOf(foregroundColor);
4767 }
4768 } else {
4769 resultSpan = span;
4770 }
4771
4772 builder.setSpan(resultSpan, spanStart, spanEnd, ss.getSpanFlags(span));
4773 }
4774 return builder;
4775 }
4776 return charSequence;
4777 }
4778
4779 /**
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004780 * @return Whether we are currently building a notification from a legacy (an app that
Alan Viverette3cb07a462014-06-06 14:19:53 -07004781 * doesn't create material notifications by itself) app.
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004782 */
4783 private boolean isLegacy() {
Selim Cinek7b9605b2017-01-19 17:36:00 -08004784 if (!mIsLegacyInitialized) {
4785 mIsLegacy = mContext.getApplicationInfo().targetSdkVersion
4786 < Build.VERSION_CODES.LOLLIPOP;
4787 mIsLegacyInitialized = true;
4788 }
4789 return mIsLegacy;
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004790 }
4791
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004792 private CharSequence processLegacyText(CharSequence charSequence) {
Adrian Roos0bc3f6a2017-03-06 11:54:05 -08004793 return processLegacyText(charSequence, false /* ambient */);
4794 }
4795
4796 private CharSequence processLegacyText(CharSequence charSequence, boolean ambient) {
4797 boolean isAlreadyLightText = isLegacy() || textColorsNeedInversion();
4798 boolean wantLightText = ambient;
4799 if (isAlreadyLightText != wantLightText) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004800 return getColorUtil().invertCharSequenceColors(charSequence);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004801 } else {
4802 return charSequence;
4803 }
4804 }
4805
Dan Sandler26e81cf2014-05-06 10:01:27 -04004806 /**
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004807 * Apply any necessariy colors to the small icon
Dan Sandler26e81cf2014-05-06 10:01:27 -04004808 */
Adrian Roos487374f2017-01-11 15:48:14 -08004809 private void processSmallIconColor(Icon smallIcon, RemoteViews contentView,
4810 boolean ambient) {
Selim Cinekea4bef72015-12-02 15:51:10 -08004811 boolean colorable = !isLegacy() || getColorUtil().isGrayscaleIcon(mContext, smallIcon);
Selim Cinek99104832017-01-25 14:47:33 -08004812 int color = ambient ? resolveAmbientColor() : getPrimaryHighlightColor();
Selim Cinekea4bef72015-12-02 15:51:10 -08004813 if (colorable) {
Adrian Roos487374f2017-01-11 15:48:14 -08004814 contentView.setDrawableParameters(R.id.icon, false, -1, color,
Jorim Jaggi92df1f22014-12-16 19:44:41 +01004815 PorterDuff.Mode.SRC_ATOP, -1);
Selim Cinekea4bef72015-12-02 15:51:10 -08004816
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004817 }
Selim Cinekea4bef72015-12-02 15:51:10 -08004818 contentView.setInt(R.id.notification_header, "setOriginalIconColor",
Adrian Roos487374f2017-01-11 15:48:14 -08004819 colorable ? color : NotificationHeaderView.NO_COLOR);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004820 }
4821
Dan Sandler26e81cf2014-05-06 10:01:27 -04004822 /**
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004823 * Make the largeIcon dark if it's a fake smallIcon (that is,
Dan Sandler26e81cf2014-05-06 10:01:27 -04004824 * if it's grayscale).
4825 */
4826 // TODO: also check bounds, transparency, that sort of thing.
Dan Sandlerd63f9322015-05-06 15:18:49 -04004827 private void processLargeLegacyIcon(Icon largeIcon, RemoteViews contentView) {
4828 if (largeIcon != null && isLegacy()
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004829 && getColorUtil().isGrayscaleIcon(mContext, largeIcon)) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004830 // resolve color will fall back to the default when legacy
Adrian Roos4ff3b122016-02-01 12:26:13 -08004831 contentView.setDrawableParameters(R.id.icon, false, -1, resolveContrastColor(),
Dan Sandler190d58d2014-05-15 09:33:39 -04004832 PorterDuff.Mode.SRC_ATOP, -1);
Jorim Jaggi92df1f22014-12-16 19:44:41 +01004833 }
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004834 }
4835
Julia Reynolds10ee1fc2015-11-09 11:04:55 -05004836 private void sanitizeColor() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004837 if (mN.color != COLOR_DEFAULT) {
4838 mN.color |= 0xFF000000; // no alpha for custom colors
Jorim Jaggi74419312014-06-10 20:57:21 +02004839 }
Jorim Jaggi74419312014-06-10 20:57:21 +02004840 }
4841
Adrian Roos4ff3b122016-02-01 12:26:13 -08004842 int resolveContrastColor() {
4843 if (mCachedContrastColorIsFor == mN.color && mCachedContrastColor != COLOR_INVALID) {
4844 return mCachedContrastColor;
Dan Sandler26e81cf2014-05-06 10:01:27 -04004845 }
Adrian Roos4ff3b122016-02-01 12:26:13 -08004846
Selim Cinekac5f0272017-05-02 16:05:41 -07004847 int color;
4848 int background = mBackgroundColorHint;
4849 if (mBackgroundColorHint == COLOR_INVALID) {
4850 background = mContext.getColor(
4851 com.android.internal.R.color.notification_material_background_color);
4852 }
4853 if (mN.color == COLOR_DEFAULT) {
4854 ensureColors();
4855 color = mSecondaryTextColor;
4856 } else {
4857 color = NotificationColorUtil.resolveContrastColor(mContext, mN.color,
Anthony Chenad4d1582017-04-10 16:07:58 -07004858 background, mInNightMode);
Selim Cinekac5f0272017-05-02 16:05:41 -07004859 }
4860 if (Color.alpha(color) < 255) {
4861 // alpha doesn't go well for color filters, so let's blend it manually
4862 color = NotificationColorUtil.compositeColors(color, background);
4863 }
Adrian Roos4ff3b122016-02-01 12:26:13 -08004864 mCachedContrastColorIsFor = mN.color;
Selim Cinekac5f0272017-05-02 16:05:41 -07004865 return mCachedContrastColor = color;
Dan Sandler26e81cf2014-05-06 10:01:27 -04004866 }
4867
Adrian Roos487374f2017-01-11 15:48:14 -08004868 int resolveAmbientColor() {
4869 if (mCachedAmbientColorIsFor == mN.color && mCachedAmbientColorIsFor != COLOR_INVALID) {
4870 return mCachedAmbientColor;
4871 }
4872 final int contrasted = NotificationColorUtil.resolveAmbientColor(mContext, mN.color);
4873
4874 mCachedAmbientColorIsFor = mN.color;
4875 return mCachedAmbientColor = contrasted;
4876 }
4877
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004878 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004879 * Apply the unstyled operations and return a new {@link Notification} object.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04004880 * @hide
Joe Onoratocb109a02011-01-18 17:57:41 -08004881 */
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04004882 public Notification buildUnstyled() {
Daniel Sandlera0a938c2012-03-15 08:42:37 -04004883 if (mActions.size() > 0) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004884 mN.actions = new Action[mActions.size()];
4885 mActions.toArray(mN.actions);
Daniel Sandlera0a938c2012-03-15 08:42:37 -04004886 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004887 if (!mPersonList.isEmpty()) {
4888 mN.extras.putStringArray(EXTRA_PEOPLE,
4889 mPersonList.toArray(new String[mPersonList.size()]));
Dan Sandler0bf2ed82013-12-21 23:33:41 -06004890 }
Selim Cinek247fa012016-02-18 09:50:48 -08004891 if (mN.bigContentView != null || mN.contentView != null
4892 || mN.headsUpContentView != null) {
4893 mN.extras.putBoolean(EXTRA_CONTAINS_CUSTOM_VIEW, true);
4894 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004895 return mN;
Joe Onorato46439ce2010-11-19 13:56:21 -08004896 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004897
Julia Reynolds3b848122016-02-26 10:45:32 -05004898 /**
4899 * Creates a Builder from an existing notification so further changes can be made.
4900 * @param context The context for your application / activity.
4901 * @param n The notification to create a Builder from.
4902 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004903 public static Notification.Builder recoverBuilder(Context context, Notification n) {
Christoph Studer4600f9b2014-07-22 22:44:43 +02004904 // Re-create notification context so we can access app resources.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004905 ApplicationInfo applicationInfo = n.extras.getParcelable(
4906 EXTRA_BUILDER_APPLICATION_INFO);
Christoph Studer4600f9b2014-07-22 22:44:43 +02004907 Context builderContext;
Julia Reynoldsda303542015-11-23 14:00:20 -05004908 if (applicationInfo != null) {
4909 try {
4910 builderContext = context.createApplicationContext(applicationInfo,
4911 Context.CONTEXT_RESTRICTED);
4912 } catch (NameNotFoundException e) {
4913 Log.e(TAG, "ApplicationInfo " + applicationInfo + " not found");
4914 builderContext = context; // try with our context
4915 }
4916 } else {
4917 builderContext = context; // try with given context
Christoph Studer4600f9b2014-07-22 22:44:43 +02004918 }
4919
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004920 return new Builder(builderContext, n);
Christoph Studer4600f9b2014-07-22 22:44:43 +02004921 }
4922
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004923 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004924 * @deprecated Use {@link #build()} instead.
4925 */
4926 @Deprecated
4927 public Notification getNotification() {
4928 return build();
4929 }
4930
4931 /**
4932 * Combine all of the options that have been set and return a new {@link Notification}
4933 * object.
4934 */
4935 public Notification build() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004936 // first, add any extras from the calling code
4937 if (mUserExtras != null) {
4938 mN.extras = getAllExtras();
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07004939 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004940
Selim Cinekb85f36fd2016-04-20 18:46:36 -07004941 mN.creationTime = System.currentTimeMillis();
4942
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004943 // lazy stuff from mContext; see comment in Builder(Context, Notification)
Julia Reynoldsda303542015-11-23 14:00:20 -05004944 Notification.addFieldsFromContext(mContext, mN);
Christoph Studer943aa672014-08-03 20:31:16 +02004945
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004946 buildUnstyled();
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004947
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004948 if (mStyle != null) {
Selim Cinekd0426622017-07-11 13:19:59 +02004949 mStyle.reduceImageSizes(mContext);
4950 mStyle.purgeResources();
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004951 mStyle.buildStyled(mN);
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004952 }
Selim Cinekd0426622017-07-11 13:19:59 +02004953 mN.reduceImageSizes(mContext);
4954
Adrian Roos5081c0d2016-02-26 16:04:19 -08004955 if (mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N
Selim Cineka7679b62017-05-10 16:33:25 -07004956 && (useExistingRemoteView())) {
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004957 if (mN.contentView == null) {
Julia Reynolds3b848122016-02-26 10:45:32 -05004958 mN.contentView = createContentView();
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004959 mN.extras.putInt(EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT,
4960 mN.contentView.getSequenceNumber());
4961 }
4962 if (mN.bigContentView == null) {
Julia Reynolds3b848122016-02-26 10:45:32 -05004963 mN.bigContentView = createBigContentView();
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004964 if (mN.bigContentView != null) {
4965 mN.extras.putInt(EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT,
4966 mN.bigContentView.getSequenceNumber());
4967 }
4968 }
4969 if (mN.headsUpContentView == null) {
Julia Reynolds3b848122016-02-26 10:45:32 -05004970 mN.headsUpContentView = createHeadsUpContentView();
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004971 if (mN.headsUpContentView != null) {
4972 mN.extras.putInt(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT,
4973 mN.headsUpContentView.getSequenceNumber());
4974 }
4975 }
4976 }
4977
Julia Reynolds4c0c2022016-02-02 15:11:59 -05004978 if ((mN.defaults & DEFAULT_LIGHTS) != 0) {
4979 mN.flags |= FLAG_SHOW_LIGHTS;
4980 }
4981
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004982 return mN;
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004983 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05004984
4985 /**
4986 * Apply this Builder to an existing {@link Notification} object.
4987 *
4988 * @hide
4989 */
4990 public Notification buildInto(Notification n) {
Daniel Sandler1a497d32013-04-18 14:52:45 -04004991 build().cloneInto(n, true);
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05004992 return n;
4993 }
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004994
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004995 /**
Adrian Roos184bfe022016-03-03 13:41:44 -08004996 * Removes RemoteViews that were created for compatibility from {@param n}, if they did not
Julia Reynolds8a3b4592017-06-26 17:15:14 -04004997 * change. Also removes extenders on low ram devices, as
4998 * {@link android.service.notification.NotificationListenerService} services are disabled.
Adrian Roos184bfe022016-03-03 13:41:44 -08004999 *
5000 * @return {@param n}, if no stripping is needed, otherwise a stripped clone of {@param n}.
5001 *
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05005002 * @hide
5003 */
Julia Reynolds8a3b4592017-06-26 17:15:14 -04005004 public static Notification maybeCloneStrippedForDelivery(Notification n, boolean isLowRam) {
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05005005 String templateClass = n.extras.getString(EXTRA_TEMPLATE);
Adrian Roos184bfe022016-03-03 13:41:44 -08005006
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05005007 // Only strip views for known Styles because we won't know how to
5008 // re-create them otherwise.
Julia Reynolds8a3b4592017-06-26 17:15:14 -04005009 if (!isLowRam
5010 && !TextUtils.isEmpty(templateClass)
Adrian Roos184bfe022016-03-03 13:41:44 -08005011 && getNotificationStyleClass(templateClass) == null) {
5012 return n;
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05005013 }
Adrian Roos184bfe022016-03-03 13:41:44 -08005014
5015 // Only strip unmodified BuilderRemoteViews.
5016 boolean stripContentView = n.contentView instanceof BuilderRemoteViews &&
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05005017 n.extras.getInt(EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT, -1) ==
Adrian Roos184bfe022016-03-03 13:41:44 -08005018 n.contentView.getSequenceNumber();
5019 boolean stripBigContentView = n.bigContentView instanceof BuilderRemoteViews &&
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05005020 n.extras.getInt(EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT, -1) ==
Adrian Roos184bfe022016-03-03 13:41:44 -08005021 n.bigContentView.getSequenceNumber();
5022 boolean stripHeadsUpContentView = n.headsUpContentView instanceof BuilderRemoteViews &&
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05005023 n.extras.getInt(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT, -1) ==
Adrian Roos184bfe022016-03-03 13:41:44 -08005024 n.headsUpContentView.getSequenceNumber();
5025
5026 // Nothing to do here, no need to clone.
Julia Reynolds8a3b4592017-06-26 17:15:14 -04005027 if (!isLowRam
5028 && !stripContentView && !stripBigContentView && !stripHeadsUpContentView) {
Adrian Roos184bfe022016-03-03 13:41:44 -08005029 return n;
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05005030 }
Adrian Roos184bfe022016-03-03 13:41:44 -08005031
5032 Notification clone = n.clone();
5033 if (stripContentView) {
5034 clone.contentView = null;
5035 clone.extras.remove(EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT);
5036 }
5037 if (stripBigContentView) {
5038 clone.bigContentView = null;
5039 clone.extras.remove(EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT);
5040 }
5041 if (stripHeadsUpContentView) {
5042 clone.headsUpContentView = null;
5043 clone.extras.remove(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT);
5044 }
Julia Reynolds8a3b4592017-06-26 17:15:14 -04005045 if (isLowRam) {
5046 clone.extras.remove(Notification.TvExtender.EXTRA_TV_EXTENDER);
5047 clone.extras.remove(WearableExtender.EXTRA_WEARABLE_EXTENSIONS);
5048 clone.extras.remove(CarExtender.EXTRA_CAR_EXTENDER);
5049 }
Adrian Roos184bfe022016-03-03 13:41:44 -08005050 return clone;
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05005051 }
5052
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01005053 private int getBaseLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07005054 return R.layout.notification_template_material_base;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01005055 }
5056
5057 private int getBigBaseLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07005058 return R.layout.notification_template_material_big_base;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01005059 }
5060
5061 private int getBigPictureLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07005062 return R.layout.notification_template_material_big_picture;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01005063 }
5064
5065 private int getBigTextLayoutResource() {
Jorim Jaggi445d3c02014-08-19 22:33:42 +02005066 return R.layout.notification_template_material_big_text;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01005067 }
5068
5069 private int getInboxLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07005070 return R.layout.notification_template_material_inbox;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01005071 }
5072
Adrian Roosc1a80b02016-04-05 14:54:55 -07005073 private int getMessagingLayoutResource() {
5074 return R.layout.notification_template_material_messaging;
5075 }
5076
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01005077 private int getActionLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07005078 return R.layout.notification_material_action;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01005079 }
5080
Selim Cinek06e9e1f2016-07-08 17:14:16 -07005081 private int getEmphasizedActionLayoutResource() {
5082 return R.layout.notification_material_action_emphasized;
5083 }
5084
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01005085 private int getActionTombstoneLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07005086 return R.layout.notification_material_action_tombstone;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01005087 }
Selim Cinek7b9605b2017-01-19 17:36:00 -08005088
5089 private int getBackgroundColor() {
5090 if (isColorized()) {
Selim Cinek5fb73f82017-04-20 16:55:38 -07005091 return mBackgroundColor != COLOR_INVALID ? mBackgroundColor : mN.color;
Selim Cinek7b9605b2017-01-19 17:36:00 -08005092 } else {
Selim Cinekac5f0272017-05-02 16:05:41 -07005093 return mBackgroundColorHint != COLOR_INVALID ? mBackgroundColorHint
5094 : COLOR_DEFAULT;
Selim Cinek7b9605b2017-01-19 17:36:00 -08005095 }
5096 }
5097
5098 private boolean isColorized() {
5099 return mN.isColorized();
5100 }
Selim Cinek99104832017-01-25 14:47:33 -08005101
Anthony Chenad4d1582017-04-10 16:07:58 -07005102 private boolean shouldTintActionButtons() {
5103 return mTintActionButtons;
5104 }
5105
Selim Cinek99104832017-01-25 14:47:33 -08005106 private boolean textColorsNeedInversion() {
5107 if (mStyle == null || !MediaStyle.class.equals(mStyle.getClass())) {
5108 return false;
5109 }
5110 int targetSdkVersion = mContext.getApplicationInfo().targetSdkVersion;
5111 return targetSdkVersion > Build.VERSION_CODES.M
5112 && targetSdkVersion < Build.VERSION_CODES.O;
5113 }
Selim Cinek5fb73f82017-04-20 16:55:38 -07005114
5115 /**
5116 * Set a color palette to be used as the background and textColors
5117 *
5118 * @param backgroundColor the color to be used as the background
5119 * @param foregroundColor the color to be used as the foreground
5120 *
5121 * @hide
5122 */
5123 public void setColorPalette(int backgroundColor, int foregroundColor) {
5124 mBackgroundColor = backgroundColor;
5125 mForegroundColor = foregroundColor;
5126 mTextColorsAreForBackground = COLOR_INVALID;
5127 ensureColors();
5128 }
Selim Cinekac5f0272017-05-02 16:05:41 -07005129
5130 /**
5131 * Sets the background color for this notification to be a different one then the default.
5132 * This is mainly used to calculate contrast and won't necessarily be applied to the
5133 * background.
5134 *
5135 * @hide
5136 */
5137 public void setBackgroundColorHint(int backgroundColor) {
5138 mBackgroundColorHint = backgroundColor;
5139 }
Selim Cineka7679b62017-05-10 16:33:25 -07005140
5141
5142 /**
5143 * Forces all styled remoteViews to be built from scratch and not use any cached
5144 * RemoteViews.
5145 * This is needed for legacy apps that are baking in their remoteviews into the
5146 * notification.
5147 *
5148 * @hide
5149 */
5150 public void setRebuildStyledRemoteViews(boolean rebuild) {
5151 mRebuildStyledRemoteViews = rebuild;
5152 }
Selim Cinek7b9605b2017-01-19 17:36:00 -08005153 }
5154
5155 /**
Selim Cinekd0426622017-07-11 13:19:59 +02005156 * Reduces the image sizes to conform to a maximum allowed size. This also processes all custom
5157 * remote views.
5158 *
5159 * @hide
5160 */
5161 void reduceImageSizes(Context context) {
5162 if (extras.getBoolean(EXTRA_REDUCED_IMAGES)) {
5163 return;
5164 }
Selim Cineka8cb1262017-08-15 16:53:44 -07005165 boolean isLowRam = ActivityManager.isLowRamDeviceStatic();
Selim Cinekd0426622017-07-11 13:19:59 +02005166 if (mLargeIcon != null || largeIcon != null) {
5167 Resources resources = context.getResources();
5168 Class<? extends Style> style = getNotificationStyle();
Selim Cineka8cb1262017-08-15 16:53:44 -07005169 int maxWidth = resources.getDimensionPixelSize(isLowRam
5170 ? R.dimen.notification_right_icon_size_low_ram
5171 : R.dimen.notification_right_icon_size);
Selim Cinekd0426622017-07-11 13:19:59 +02005172 int maxHeight = maxWidth;
5173 if (MediaStyle.class.equals(style)
5174 || DecoratedMediaCustomViewStyle.class.equals(style)) {
Selim Cineka8cb1262017-08-15 16:53:44 -07005175 maxHeight = resources.getDimensionPixelSize(isLowRam
5176 ? R.dimen.notification_media_image_max_height_low_ram
5177 : R.dimen.notification_media_image_max_height);
5178 maxWidth = resources.getDimensionPixelSize(isLowRam
5179 ? R.dimen.notification_media_image_max_width_low_ram
5180 : R.dimen.notification_media_image_max_width);
Selim Cinekd0426622017-07-11 13:19:59 +02005181 }
5182 if (mLargeIcon != null) {
5183 mLargeIcon.scaleDownIfNecessary(maxWidth, maxHeight);
5184 }
5185 if (largeIcon != null) {
5186 largeIcon = Icon.scaleDownIfNecessary(largeIcon, maxWidth, maxHeight);
5187 }
5188 }
Selim Cineka8cb1262017-08-15 16:53:44 -07005189 reduceImageSizesForRemoteView(contentView, context, isLowRam);
5190 reduceImageSizesForRemoteView(headsUpContentView, context, isLowRam);
5191 reduceImageSizesForRemoteView(bigContentView, context, isLowRam);
Selim Cinekd0426622017-07-11 13:19:59 +02005192 extras.putBoolean(EXTRA_REDUCED_IMAGES, true);
5193 }
5194
Selim Cineka8cb1262017-08-15 16:53:44 -07005195 private void reduceImageSizesForRemoteView(RemoteViews remoteView, Context context,
5196 boolean isLowRam) {
Selim Cinekd0426622017-07-11 13:19:59 +02005197 if (remoteView != null) {
5198 Resources resources = context.getResources();
Selim Cineka8cb1262017-08-15 16:53:44 -07005199 int maxWidth = resources.getDimensionPixelSize(isLowRam
5200 ? R.dimen.notification_custom_view_max_image_width_low_ram
5201 : R.dimen.notification_custom_view_max_image_width);
5202 int maxHeight = resources.getDimensionPixelSize(isLowRam
5203 ? R.dimen.notification_custom_view_max_image_height_low_ram
5204 : R.dimen.notification_custom_view_max_image_height);
Selim Cinekd0426622017-07-11 13:19:59 +02005205 remoteView.reduceImageSizes(maxWidth, maxHeight);
5206 }
5207 }
5208
5209 /**
Selim Cinek22714f12017-04-13 16:23:53 -07005210 * @return whether this notification is a foreground service notification
Selim Cinek7b9605b2017-01-19 17:36:00 -08005211 */
Selim Cinek22714f12017-04-13 16:23:53 -07005212 private boolean isForegroundService() {
5213 return (flags & Notification.FLAG_FOREGROUND_SERVICE) != 0;
Selim Cinek7b9605b2017-01-19 17:36:00 -08005214 }
5215
5216 /**
Selim Cinek99104832017-01-25 14:47:33 -08005217 * @return whether this notification has a media session attached
5218 * @hide
5219 */
5220 public boolean hasMediaSession() {
5221 return extras.getParcelable(Notification.EXTRA_MEDIA_SESSION) != null;
5222 }
5223
5224 /**
Selim Cinek7d1009b2017-01-25 15:28:28 -08005225 * @return the style class of this notification
5226 * @hide
5227 */
5228 public Class<? extends Notification.Style> getNotificationStyle() {
5229 String templateClass = extras.getString(Notification.EXTRA_TEMPLATE);
5230
5231 if (!TextUtils.isEmpty(templateClass)) {
5232 return Notification.getNotificationStyleClass(templateClass);
5233 }
5234 return null;
5235 }
5236
5237 /**
Selim Cinek22714f12017-04-13 16:23:53 -07005238 * @return true if this notification is colorized.
Selim Cinek7b9605b2017-01-19 17:36:00 -08005239 *
5240 * @hide
5241 */
5242 public boolean isColorized() {
Selim Cinek5fb73f82017-04-20 16:55:38 -07005243 if (isColorizedMedia()) {
5244 return true;
5245 }
Julia Reynolds4db59552017-06-30 13:34:01 -04005246 return extras.getBoolean(EXTRA_COLORIZED)
5247 && (hasColorizedPermission() || isForegroundService());
5248 }
5249
5250 /**
5251 * Returns whether an app can colorize due to the android.permission.USE_COLORIZED_NOTIFICATIONS
5252 * permission. The permission is checked when a notification is enqueued.
5253 */
5254 private boolean hasColorizedPermission() {
5255 return (flags & Notification.FLAG_CAN_COLORIZE) != 0;
Selim Cinek5fb73f82017-04-20 16:55:38 -07005256 }
5257
5258 /**
5259 * @return true if this notification is colorized and it is a media notification
5260 *
5261 * @hide
5262 */
5263 public boolean isColorizedMedia() {
Selim Cinek99104832017-01-25 14:47:33 -08005264 Class<? extends Style> style = getNotificationStyle();
5265 if (MediaStyle.class.equals(style)) {
5266 Boolean colorized = (Boolean) extras.get(EXTRA_COLORIZED);
5267 if ((colorized == null || colorized) && hasMediaSession()) {
5268 return true;
5269 }
5270 } else if (DecoratedMediaCustomViewStyle.class.equals(style)) {
5271 if (extras.getBoolean(EXTRA_COLORIZED) && hasMediaSession()) {
5272 return true;
5273 }
5274 }
Selim Cinek5fb73f82017-04-20 16:55:38 -07005275 return false;
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005276 }
5277
Selim Cinek0847acd2017-04-24 19:48:29 -07005278
5279 /**
5280 * @return true if this is a media notification
5281 *
5282 * @hide
5283 */
5284 public boolean isMediaNotification() {
5285 Class<? extends Style> style = getNotificationStyle();
5286 if (MediaStyle.class.equals(style)) {
5287 return true;
5288 } else if (DecoratedMediaCustomViewStyle.class.equals(style)) {
5289 return true;
5290 }
5291 return false;
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005292 }
5293
Selim Cinek279fa862016-06-14 10:57:25 -07005294 private boolean hasLargeIcon() {
5295 return mLargeIcon != null || largeIcon != null;
5296 }
5297
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005298 /**
Selim Cinekc2c0b042016-05-18 17:13:46 -07005299 * @return true if the notification will show the time; false otherwise
Selim Cinekb85f36fd2016-04-20 18:46:36 -07005300 * @hide
5301 */
Selim Cinekc2c0b042016-05-18 17:13:46 -07005302 public boolean showsTime() {
Selim Cinekb85f36fd2016-04-20 18:46:36 -07005303 return when != 0 && extras.getBoolean(EXTRA_SHOW_WHEN);
5304 }
5305
5306 /**
Selim Cinekc2c0b042016-05-18 17:13:46 -07005307 * @return true if the notification will show a chronometer; false otherwise
5308 * @hide
5309 */
5310 public boolean showsChronometer() {
5311 return when != 0 && extras.getBoolean(EXTRA_SHOW_CHRONOMETER);
5312 }
5313
5314 /**
Julia Reynolds7ca33072017-06-29 13:58:24 -04005315 * @removed
Julia Reynolds4a02afb2016-12-13 13:39:52 -05005316 */
5317 @SystemApi
5318 public static Class<? extends Style> getNotificationStyleClass(String templateClass) {
5319 Class<? extends Style>[] classes = new Class[] {
5320 BigTextStyle.class, BigPictureStyle.class, InboxStyle.class, MediaStyle.class,
5321 DecoratedCustomViewStyle.class, DecoratedMediaCustomViewStyle.class,
5322 MessagingStyle.class };
5323 for (Class<? extends Style> innerClass : classes) {
5324 if (templateClass.equals(innerClass.getName())) {
5325 return innerClass;
5326 }
5327 }
5328 return null;
5329 }
5330
5331 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005332 * An object that can apply a rich notification style to a {@link Notification.Builder}
5333 * object.
5334 */
Griff Hazendfcb0802014-02-11 12:00:00 -08005335 public static abstract class Style {
Chris Wrend6297db2012-05-03 16:20:13 -04005336 private CharSequence mBigContentTitle;
Jorim Jaggi457a10d2014-09-08 16:18:23 +02005337
5338 /**
5339 * @hide
5340 */
5341 protected CharSequence mSummaryText = null;
5342
5343 /**
5344 * @hide
5345 */
5346 protected boolean mSummaryTextSet = false;
Chris Wrend6297db2012-05-03 16:20:13 -04005347
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005348 protected Builder mBuilder;
5349
Chris Wrend6297db2012-05-03 16:20:13 -04005350 /**
5351 * Overrides ContentTitle in the big form of the template.
5352 * This defaults to the value passed to setContentTitle().
5353 */
5354 protected void internalSetBigContentTitle(CharSequence title) {
5355 mBigContentTitle = title;
5356 }
5357
5358 /**
5359 * Set the first line of text after the detail section in the big form of the template.
5360 */
5361 protected void internalSetSummaryText(CharSequence cs) {
5362 mSummaryText = cs;
Daniel Sandler619738c2012-06-07 16:33:08 -04005363 mSummaryTextSet = true;
Chris Wrend6297db2012-05-03 16:20:13 -04005364 }
5365
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005366 public void setBuilder(Builder builder) {
5367 if (mBuilder != builder) {
5368 mBuilder = builder;
Daniel Sandlerc08dea22012-06-28 08:35:24 -07005369 if (mBuilder != null) {
5370 mBuilder.setStyle(this);
5371 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005372 }
5373 }
5374
Chris Wrend6297db2012-05-03 16:20:13 -04005375 protected void checkBuilder() {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005376 if (mBuilder == null) {
5377 throw new IllegalArgumentException("Style requires a valid Builder object");
5378 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005379 }
Chris Wrend6297db2012-05-03 16:20:13 -04005380
5381 protected RemoteViews getStandardView(int layoutId) {
5382 checkBuilder();
5383
Christoph Studer4600f9b2014-07-22 22:44:43 +02005384 // Nasty.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005385 CharSequence oldBuilderContentTitle =
5386 mBuilder.getAllExtras().getCharSequence(EXTRA_TITLE);
Chris Wrend6297db2012-05-03 16:20:13 -04005387 if (mBigContentTitle != null) {
5388 mBuilder.setContentTitle(mBigContentTitle);
5389 }
5390
Chris Wrend6297db2012-05-03 16:20:13 -04005391 RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(layoutId);
5392
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005393 mBuilder.getAllExtras().putCharSequence(EXTRA_TITLE, oldBuilderContentTitle);
Christoph Studer4600f9b2014-07-22 22:44:43 +02005394
Chris Wrend6297db2012-05-03 16:20:13 -04005395 if (mBigContentTitle != null && mBigContentTitle.equals("")) {
5396 contentView.setViewVisibility(R.id.line1, View.GONE);
Chris Wren67dc9a02012-05-16 01:03:20 -04005397 } else {
5398 contentView.setViewVisibility(R.id.line1, View.VISIBLE);
Chris Wrend6297db2012-05-03 16:20:13 -04005399 }
5400
Chris Wrend6297db2012-05-03 16:20:13 -04005401 return contentView;
5402 }
5403
Daniel Sandlerf45564e2013-04-15 15:05:08 -04005404 /**
Selim Cinek7d1009b2017-01-25 15:28:28 -08005405 * Construct a Style-specific RemoteViews for the collapsed notification layout.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005406 * The default implementation has nothing additional to add.
Selim Cinek7d1009b2017-01-25 15:28:28 -08005407 *
5408 * @param increasedHeight true if this layout be created with an increased height.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005409 * @hide
5410 */
Selim Cinek7d1009b2017-01-25 15:28:28 -08005411 public RemoteViews makeContentView(boolean increasedHeight) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005412 return null;
5413 }
5414
5415 /**
5416 * Construct a Style-specific RemoteViews for the final big notification layout.
5417 * @hide
5418 */
5419 public RemoteViews makeBigContentView() {
5420 return null;
5421 }
5422
5423 /**
5424 * Construct a Style-specific RemoteViews for the final HUN layout.
Selim Cinek87ed69b2017-02-09 15:59:43 -08005425 *
5426 * @param increasedHeight true if this layout be created with an increased height.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005427 * @hide
5428 */
Selim Cinek87ed69b2017-02-09 15:59:43 -08005429 public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005430 return null;
5431 }
5432
5433 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005434 * Apply any style-specific extras to this notification before shipping it out.
Daniel Sandlerf45564e2013-04-15 15:05:08 -04005435 * @hide
5436 */
5437 public void addExtras(Bundle extras) {
5438 if (mSummaryTextSet) {
5439 extras.putCharSequence(EXTRA_SUMMARY_TEXT, mSummaryText);
5440 }
5441 if (mBigContentTitle != null) {
5442 extras.putCharSequence(EXTRA_TITLE_BIG, mBigContentTitle);
5443 }
Chris Wren91ad5632013-06-05 15:05:57 -04005444 extras.putString(EXTRA_TEMPLATE, this.getClass().getName());
Daniel Sandlerf45564e2013-04-15 15:05:08 -04005445 }
5446
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04005447 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005448 * Reconstruct the internal state of this Style object from extras.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04005449 * @hide
5450 */
Christoph Studer4600f9b2014-07-22 22:44:43 +02005451 protected void restoreFromExtras(Bundle extras) {
5452 if (extras.containsKey(EXTRA_SUMMARY_TEXT)) {
5453 mSummaryText = extras.getCharSequence(EXTRA_SUMMARY_TEXT);
5454 mSummaryTextSet = true;
5455 }
5456 if (extras.containsKey(EXTRA_TITLE_BIG)) {
5457 mBigContentTitle = extras.getCharSequence(EXTRA_TITLE_BIG);
5458 }
5459 }
5460
5461
5462 /**
5463 * @hide
5464 */
5465 public Notification buildStyled(Notification wip) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005466 addExtras(wip.extras);
Christoph Studer4600f9b2014-07-22 22:44:43 +02005467 return wip;
5468 }
5469
Daniel Sandler0ec46202015-06-24 01:27:05 -04005470 /**
5471 * @hide
5472 */
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07005473 public void purgeResources() {}
5474
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04005475 /**
5476 * Calls {@link android.app.Notification.Builder#build()} on the Builder this Style is
5477 * attached to.
5478 *
5479 * @return the fully constructed Notification.
5480 */
5481 public Notification build() {
5482 checkBuilder();
5483 return mBuilder.build();
5484 }
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02005485
5486 /**
5487 * @hide
5488 * @return true if the style positions the progress bar on the second line; false if the
5489 * style hides the progress bar
5490 */
5491 protected boolean hasProgress() {
5492 return true;
5493 }
Selim Cinek03d0d652015-11-13 13:18:09 -05005494
5495 /**
5496 * @hide
5497 * @return Whether we should put the summary be put into the notification header
5498 */
5499 public boolean hasSummaryInHeader() {
5500 return true;
5501 }
Selim Cinek593610c2016-02-16 18:42:57 -08005502
5503 /**
5504 * @hide
5505 * @return Whether custom content views are displayed inline in the style
5506 */
5507 public boolean displayCustomViewInline() {
5508 return false;
5509 }
Selim Cinekd0426622017-07-11 13:19:59 +02005510
5511 /**
5512 * Reduces the image sizes contained in this style.
5513 *
5514 * @hide
5515 */
5516 public void reduceImageSizes(Context context) {
5517 }
Joe Onorato46439ce2010-11-19 13:56:21 -08005518 }
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005519
5520 /**
Daniel Sandler4dfbe832012-04-11 14:51:46 -04005521 * Helper class for generating large-format notifications that include a large image attachment.
Joe Malin8d40d042012-11-05 11:36:40 -08005522 *
Robert Ly91c5ce32014-06-08 15:37:00 -07005523 * Here's how you'd set the <code>BigPictureStyle</code> on a notification:
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005524 * <pre class="prettyprint">
Robert Ly91c5ce32014-06-08 15:37:00 -07005525 * Notification notif = new Notification.Builder(mContext)
5526 * .setContentTitle(&quot;New photo from &quot; + sender.toString())
5527 * .setContentText(subject)
5528 * .setSmallIcon(R.drawable.new_post)
5529 * .setLargeIcon(aBitmap)
5530 * .setStyle(new Notification.BigPictureStyle()
5531 * .bigPicture(aBigBitmap))
5532 * .build();
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005533 * </pre>
Joe Malin8d40d042012-11-05 11:36:40 -08005534 *
Daniel Sandler4dfbe832012-04-11 14:51:46 -04005535 * @see Notification#bigContentView
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005536 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005537 public static class BigPictureStyle extends Style {
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005538 private Bitmap mPicture;
Dan Sandlerd63f9322015-05-06 15:18:49 -04005539 private Icon mBigLargeIcon;
Chris Wren3745a3d2012-05-22 15:11:52 -04005540 private boolean mBigLargeIconSet = false;
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005541
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005542 public BigPictureStyle() {
5543 }
5544
Adrian Roosf5faf9d2016-05-23 13:56:15 -07005545 /**
5546 * @deprecated use {@code BigPictureStyle()}.
5547 */
5548 @Deprecated
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005549 public BigPictureStyle(Builder builder) {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005550 setBuilder(builder);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005551 }
5552
Chris Wrend6297db2012-05-03 16:20:13 -04005553 /**
5554 * Overrides ContentTitle in the big form of the template.
5555 * This defaults to the value passed to setContentTitle().
5556 */
5557 public BigPictureStyle setBigContentTitle(CharSequence title) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04005558 internalSetBigContentTitle(safeCharSequence(title));
Chris Wrend6297db2012-05-03 16:20:13 -04005559 return this;
5560 }
5561
5562 /**
5563 * Set the first line of text after the detail section in the big form of the template.
5564 */
5565 public BigPictureStyle setSummaryText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04005566 internalSetSummaryText(safeCharSequence(cs));
Chris Wrend6297db2012-05-03 16:20:13 -04005567 return this;
5568 }
5569
Chris Wren0bd664d2012-08-01 13:56:56 -04005570 /**
5571 * Provide the bitmap to be used as the payload for the BigPicture notification.
5572 */
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005573 public BigPictureStyle bigPicture(Bitmap b) {
5574 mPicture = b;
5575 return this;
5576 }
5577
Chris Wren3745a3d2012-05-22 15:11:52 -04005578 /**
Chris Wren3745a3d2012-05-22 15:11:52 -04005579 * Override the large icon when the big notification is shown.
5580 */
5581 public BigPictureStyle bigLargeIcon(Bitmap b) {
Dan Sandlerd63f9322015-05-06 15:18:49 -04005582 return bigLargeIcon(b != null ? Icon.createWithBitmap(b) : null);
5583 }
5584
5585 /**
5586 * Override the large icon when the big notification is shown.
5587 */
5588 public BigPictureStyle bigLargeIcon(Icon icon) {
Chris Wren3745a3d2012-05-22 15:11:52 -04005589 mBigLargeIconSet = true;
Dan Sandlerd63f9322015-05-06 15:18:49 -04005590 mBigLargeIcon = icon;
Chris Wren3745a3d2012-05-22 15:11:52 -04005591 return this;
5592 }
5593
Riley Andrews0394a0c2015-11-03 23:36:52 -08005594 /** @hide */
5595 public static final int MIN_ASHMEM_BITMAP_SIZE = 128 * (1 << 10);
5596
Daniel Sandler0ec46202015-06-24 01:27:05 -04005597 /**
5598 * @hide
5599 */
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07005600 @Override
5601 public void purgeResources() {
5602 super.purgeResources();
Riley Andrews8cee7c12015-11-01 23:36:04 -08005603 if (mPicture != null &&
5604 mPicture.isMutable() &&
Riley Andrews0394a0c2015-11-03 23:36:52 -08005605 mPicture.getAllocationByteCount() >= MIN_ASHMEM_BITMAP_SIZE) {
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07005606 mPicture = mPicture.createAshmemBitmap();
5607 }
5608 if (mBigLargeIcon != null) {
5609 mBigLargeIcon.convertToAshmem();
5610 }
5611 }
Christoph Studer5c510ee2014-12-15 16:32:27 +01005612
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005613 /**
5614 * @hide
5615 */
Selim Cinekd0426622017-07-11 13:19:59 +02005616 @Override
5617 public void reduceImageSizes(Context context) {
5618 super.reduceImageSizes(context);
5619 Resources resources = context.getResources();
Selim Cineka8cb1262017-08-15 16:53:44 -07005620 boolean isLowRam = ActivityManager.isLowRamDeviceStatic();
Selim Cinekd0426622017-07-11 13:19:59 +02005621 if (mPicture != null) {
Selim Cineka8cb1262017-08-15 16:53:44 -07005622 int maxPictureWidth = resources.getDimensionPixelSize(isLowRam
5623 ? R.dimen.notification_big_picture_max_height_low_ram
5624 : R.dimen.notification_big_picture_max_height);
5625 int maxPictureHeight = resources.getDimensionPixelSize(isLowRam
5626 ? R.dimen.notification_big_picture_max_width_low_ram
5627 : R.dimen.notification_big_picture_max_width);
Selim Cinekd0426622017-07-11 13:19:59 +02005628 mPicture = Icon.scaleDownIfNecessary(mPicture, maxPictureWidth, maxPictureHeight);
5629 }
5630 if (mBigLargeIcon != null) {
Selim Cineka8cb1262017-08-15 16:53:44 -07005631 int rightIconSize = resources.getDimensionPixelSize(isLowRam
5632 ? R.dimen.notification_right_icon_size_low_ram
5633 : R.dimen.notification_right_icon_size);
Selim Cinekd0426622017-07-11 13:19:59 +02005634 mBigLargeIcon.scaleDownIfNecessary(rightIconSize, rightIconSize);
5635 }
5636 }
5637
5638 /**
5639 * @hide
5640 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005641 public RemoteViews makeBigContentView() {
5642 // Replace mN.mLargeIcon with mBigLargeIcon if mBigLargeIconSet
Christoph Studer5c510ee2014-12-15 16:32:27 +01005643 // This covers the following cases:
5644 // 1. mBigLargeIconSet -> mBigLargeIcon (null or non-null) applies, overrides
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005645 // mN.mLargeIcon
5646 // 2. !mBigLargeIconSet -> mN.mLargeIcon applies
Dan Sandlerd63f9322015-05-06 15:18:49 -04005647 Icon oldLargeIcon = null;
Selim Cineke99acb22016-08-04 12:55:48 -07005648 Bitmap largeIconLegacy = null;
Christoph Studer5c510ee2014-12-15 16:32:27 +01005649 if (mBigLargeIconSet) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005650 oldLargeIcon = mBuilder.mN.mLargeIcon;
5651 mBuilder.mN.mLargeIcon = mBigLargeIcon;
Selim Cineke99acb22016-08-04 12:55:48 -07005652 // The legacy largeIcon might not allow us to clear the image, as it's taken in
5653 // replacement if the other one is null. Because we're restoring these legacy icons
5654 // for old listeners, this is in general non-null.
5655 largeIconLegacy = mBuilder.mN.largeIcon;
5656 mBuilder.mN.largeIcon = null;
Christoph Studer5c510ee2014-12-15 16:32:27 +01005657 }
5658
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01005659 RemoteViews contentView = getStandardView(mBuilder.getBigPictureLayoutResource());
Selim Cinek03d0d652015-11-13 13:18:09 -05005660 if (mSummaryTextSet) {
Selim Cinek48f66b72017-08-18 16:17:51 -07005661 contentView.setTextViewText(R.id.text, mBuilder.processTextSpans(
5662 mBuilder.processLegacyText(mSummaryText)));
Selim Cinek7b9605b2017-01-19 17:36:00 -08005663 mBuilder.setTextViewColorSecondary(contentView, R.id.text);
Selim Cinekc848c3a2016-01-13 15:27:30 -08005664 contentView.setViewVisibility(R.id.text, View.VISIBLE);
Selim Cinek03d0d652015-11-13 13:18:09 -05005665 }
Selim Cinek279fa862016-06-14 10:57:25 -07005666 mBuilder.setContentMinHeight(contentView, mBuilder.mN.hasLargeIcon());
Selim Cinek53e64a42015-11-16 10:40:56 -08005667
Christoph Studer5c510ee2014-12-15 16:32:27 +01005668 if (mBigLargeIconSet) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005669 mBuilder.mN.mLargeIcon = oldLargeIcon;
Selim Cineke99acb22016-08-04 12:55:48 -07005670 mBuilder.mN.largeIcon = largeIconLegacy;
Christoph Studer5c510ee2014-12-15 16:32:27 +01005671 }
5672
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005673 contentView.setImageViewBitmap(R.id.big_picture, mPicture);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005674 return contentView;
5675 }
5676
Daniel Sandlerf45564e2013-04-15 15:05:08 -04005677 /**
5678 * @hide
5679 */
5680 public void addExtras(Bundle extras) {
5681 super.addExtras(extras);
5682
5683 if (mBigLargeIconSet) {
5684 extras.putParcelable(EXTRA_LARGE_ICON_BIG, mBigLargeIcon);
5685 }
5686 extras.putParcelable(EXTRA_PICTURE, mPicture);
5687 }
5688
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04005689 /**
5690 * @hide
5691 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005692 @Override
Christoph Studer4600f9b2014-07-22 22:44:43 +02005693 protected void restoreFromExtras(Bundle extras) {
5694 super.restoreFromExtras(extras);
5695
5696 if (extras.containsKey(EXTRA_LARGE_ICON_BIG)) {
Christoph Studer5c510ee2014-12-15 16:32:27 +01005697 mBigLargeIconSet = true;
Christoph Studer4600f9b2014-07-22 22:44:43 +02005698 mBigLargeIcon = extras.getParcelable(EXTRA_LARGE_ICON_BIG);
Chris Wren3745a3d2012-05-22 15:11:52 -04005699 }
Christoph Studer4600f9b2014-07-22 22:44:43 +02005700 mPicture = extras.getParcelable(EXTRA_PICTURE);
5701 }
Selim Cinek03d0d652015-11-13 13:18:09 -05005702
5703 /**
5704 * @hide
5705 */
5706 @Override
5707 public boolean hasSummaryInHeader() {
5708 return false;
5709 }
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005710 }
5711
5712 /**
Daniel Sandler4dfbe832012-04-11 14:51:46 -04005713 * Helper class for generating large-format notifications that include a lot of text.
Joe Malin8d40d042012-11-05 11:36:40 -08005714 *
Robert Ly91c5ce32014-06-08 15:37:00 -07005715 * Here's how you'd set the <code>BigTextStyle</code> on a notification:
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005716 * <pre class="prettyprint">
Robert Ly91c5ce32014-06-08 15:37:00 -07005717 * Notification notif = new Notification.Builder(mContext)
5718 * .setContentTitle(&quot;New mail from &quot; + sender.toString())
5719 * .setContentText(subject)
5720 * .setSmallIcon(R.drawable.new_mail)
5721 * .setLargeIcon(aBitmap)
5722 * .setStyle(new Notification.BigTextStyle()
5723 * .bigText(aVeryLongString))
5724 * .build();
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005725 * </pre>
Joe Malin8d40d042012-11-05 11:36:40 -08005726 *
Daniel Sandler4dfbe832012-04-11 14:51:46 -04005727 * @see Notification#bigContentView
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005728 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005729 public static class BigTextStyle extends Style {
Jorim Jaggi457a10d2014-09-08 16:18:23 +02005730
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005731 private CharSequence mBigText;
5732
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005733 public BigTextStyle() {
5734 }
5735
Adrian Roosf5faf9d2016-05-23 13:56:15 -07005736 /**
5737 * @deprecated use {@code BigTextStyle()}.
5738 */
5739 @Deprecated
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005740 public BigTextStyle(Builder builder) {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005741 setBuilder(builder);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005742 }
5743
Chris Wrend6297db2012-05-03 16:20:13 -04005744 /**
5745 * Overrides ContentTitle in the big form of the template.
5746 * This defaults to the value passed to setContentTitle().
5747 */
5748 public BigTextStyle setBigContentTitle(CharSequence title) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04005749 internalSetBigContentTitle(safeCharSequence(title));
Chris Wrend6297db2012-05-03 16:20:13 -04005750 return this;
5751 }
5752
5753 /**
5754 * Set the first line of text after the detail section in the big form of the template.
5755 */
5756 public BigTextStyle setSummaryText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04005757 internalSetSummaryText(safeCharSequence(cs));
Chris Wrend6297db2012-05-03 16:20:13 -04005758 return this;
5759 }
5760
Chris Wren0bd664d2012-08-01 13:56:56 -04005761 /**
5762 * Provide the longer text to be displayed in the big form of the
5763 * template in place of the content text.
5764 */
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005765 public BigTextStyle bigText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04005766 mBigText = safeCharSequence(cs);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005767 return this;
5768 }
5769
Daniel Sandlerf45564e2013-04-15 15:05:08 -04005770 /**
5771 * @hide
5772 */
5773 public void addExtras(Bundle extras) {
5774 super.addExtras(extras);
5775
Christoph Studer4600f9b2014-07-22 22:44:43 +02005776 extras.putCharSequence(EXTRA_BIG_TEXT, mBigText);
5777 }
5778
5779 /**
5780 * @hide
5781 */
5782 @Override
5783 protected void restoreFromExtras(Bundle extras) {
5784 super.restoreFromExtras(extras);
5785
5786 mBigText = extras.getCharSequence(EXTRA_BIG_TEXT);
Daniel Sandlerf45564e2013-04-15 15:05:08 -04005787 }
5788
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005789 /**
Selim Cinek7d1009b2017-01-25 15:28:28 -08005790 * @param increasedHeight true if this layout be created with an increased height.
5791 *
5792 * @hide
5793 */
5794 @Override
5795 public RemoteViews makeContentView(boolean increasedHeight) {
5796 if (increasedHeight) {
Selim Cinek5d6ef8d2017-05-18 22:16:00 -07005797 mBuilder.mOriginalActions = mBuilder.mActions;
Selim Cinek7d1009b2017-01-25 15:28:28 -08005798 mBuilder.mActions = new ArrayList<>();
5799 RemoteViews remoteViews = makeBigContentView();
Selim Cinek5d6ef8d2017-05-18 22:16:00 -07005800 mBuilder.mActions = mBuilder.mOriginalActions;
5801 mBuilder.mOriginalActions = null;
Selim Cinek7d1009b2017-01-25 15:28:28 -08005802 return remoteViews;
5803 }
5804 return super.makeContentView(increasedHeight);
5805 }
5806
5807 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005808 * @hide
5809 */
Selim Cinek87ed69b2017-02-09 15:59:43 -08005810 @Override
5811 public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
5812 if (increasedHeight && mBuilder.mActions.size() > 0) {
5813 return makeBigContentView();
5814 }
5815 return super.makeHeadsUpContentView(increasedHeight);
5816 }
5817
5818 /**
5819 * @hide
5820 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005821 public RemoteViews makeBigContentView() {
Christoph Studer4600f9b2014-07-22 22:44:43 +02005822
5823 // Nasty
Selim Cinek75998782016-04-26 10:39:17 -07005824 CharSequence text = mBuilder.getAllExtras().getCharSequence(EXTRA_TEXT);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005825 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, null);
Daniel Sandler916ad912012-06-13 12:17:07 -04005826
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01005827 RemoteViews contentView = getStandardView(mBuilder.getBigTextLayoutResource());
Joe Malin8d40d042012-11-05 11:36:40 -08005828
Selim Cinek75998782016-04-26 10:39:17 -07005829 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, text);
Christoph Studer4600f9b2014-07-22 22:44:43 +02005830
Selim Cinek3a2c4b92015-12-17 17:01:17 -08005831 CharSequence bigTextText = mBuilder.processLegacyText(mBigText);
Selim Cinek75998782016-04-26 10:39:17 -07005832 if (TextUtils.isEmpty(bigTextText)) {
5833 // In case the bigtext is null / empty fall back to the normal text to avoid a weird
5834 // experience
5835 bigTextText = mBuilder.processLegacyText(text);
5836 }
Adrian Roosb1f427c2016-05-26 12:27:15 -07005837 applyBigTextContentView(mBuilder, contentView, bigTextText);
Selim Cinek4fb12d32015-11-19 18:10:48 -08005838
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005839 return contentView;
5840 }
5841
Adrian Roosb1f427c2016-05-26 12:27:15 -07005842 static void applyBigTextContentView(Builder builder,
5843 RemoteViews contentView, CharSequence bigTextText) {
Selim Cinek48f66b72017-08-18 16:17:51 -07005844 contentView.setTextViewText(R.id.big_text, builder.processTextSpans(bigTextText));
Selim Cinek7b9605b2017-01-19 17:36:00 -08005845 builder.setTextViewColorSecondary(contentView, R.id.big_text);
Adrian Roosb1f427c2016-05-26 12:27:15 -07005846 contentView.setViewVisibility(R.id.big_text,
5847 TextUtils.isEmpty(bigTextText) ? View.GONE : View.VISIBLE);
Selim Cinek279fa862016-06-14 10:57:25 -07005848 contentView.setBoolean(R.id.big_text, "setHasImage", builder.mN.hasLargeIcon());
Adrian Roosb1f427c2016-05-26 12:27:15 -07005849 }
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005850 }
Daniel Sandler879c5e02012-04-17 16:46:51 -04005851
5852 /**
Alex Hillsfc737de2016-03-23 17:33:02 -04005853 * Helper class for generating large-format notifications that include multiple back-and-forth
5854 * messages of varying types between any number of people.
5855 *
5856 * <br>
5857 * If the platform does not provide large-format notifications, this method has no effect. The
5858 * user will always see the normal notification view.
5859 * <br>
5860 * This class is a "rebuilder": It attaches to a Builder object and modifies its behavior, like
5861 * so:
5862 * <pre class="prettyprint">
5863 *
5864 * Notification noti = new Notification.Builder()
5865 * .setContentTitle(&quot;2 new messages wtih &quot; + sender.toString())
5866 * .setContentText(subject)
5867 * .setSmallIcon(R.drawable.new_message)
5868 * .setLargeIcon(aBitmap)
5869 * .setStyle(new Notification.MessagingStyle(resources.getString(R.string.reply_name))
5870 * .addMessage(messages[0].getText(), messages[0].getTime(), messages[0].getSender())
5871 * .addMessage(messages[1].getText(), messages[1].getTime(), messages[1].getSender()))
5872 * .build();
5873 * </pre>
5874 */
5875 public static class MessagingStyle extends Style {
5876
5877 /**
5878 * The maximum number of messages that will be retained in the Notification itself (the
5879 * number displayed is up to the platform).
5880 */
5881 public static final int MAXIMUM_RETAINED_MESSAGES = 25;
5882
5883 CharSequence mUserDisplayName;
5884 CharSequence mConversationTitle;
Alex Hillsd9b04d92016-04-11 16:38:16 -04005885 List<Message> mMessages = new ArrayList<>();
Adrian Roos437cd562017-01-18 15:47:03 -08005886 List<Message> mHistoricMessages = new ArrayList<>();
Alex Hillsfc737de2016-03-23 17:33:02 -04005887
5888 MessagingStyle() {
5889 }
5890
5891 /**
Alex Hillsfd590442016-10-07 09:52:44 -04005892 * @param userDisplayName Required - the name to be displayed for any replies sent by the
5893 * user before the posting app reposts the notification with those messages after they've
5894 * been actually sent and in previous messages sent by the user added in
Alex Hillsfc737de2016-03-23 17:33:02 -04005895 * {@link #addMessage(Notification.MessagingStyle.Message)}
5896 */
Alex Hillsfd590442016-10-07 09:52:44 -04005897 public MessagingStyle(@NonNull CharSequence userDisplayName) {
Alex Hillsfc737de2016-03-23 17:33:02 -04005898 mUserDisplayName = userDisplayName;
5899 }
5900
5901 /**
5902 * Returns the name to be displayed for any replies sent by the user
5903 */
5904 public CharSequence getUserDisplayName() {
5905 return mUserDisplayName;
5906 }
5907
5908 /**
Alex Hillsfc737de2016-03-23 17:33:02 -04005909 * Sets the title to be displayed on this conversation. This should only be used for
5910 * group messaging and left unset for one-on-one conversations.
5911 * @param conversationTitle
5912 * @return this object for method chaining.
5913 */
5914 public MessagingStyle setConversationTitle(CharSequence conversationTitle) {
5915 mConversationTitle = conversationTitle;
5916 return this;
5917 }
5918
5919 /**
5920 * Return the title to be displayed on this conversation. Can be <code>null</code> and
5921 * should be for one-on-one conversations
5922 */
5923 public CharSequence getConversationTitle() {
5924 return mConversationTitle;
5925 }
5926
5927 /**
5928 * Adds a message for display by this notification. Convenience call for a simple
5929 * {@link Message} in {@link #addMessage(Notification.MessagingStyle.Message)}.
5930 * @param text A {@link CharSequence} to be displayed as the message content
5931 * @param timestamp Time at which the message arrived
5932 * @param sender A {@link CharSequence} to be used for displaying the name of the
5933 * sender. Should be <code>null</code> for messages by the current user, in which case
5934 * the platform will insert {@link #getUserDisplayName()}.
5935 * Should be unique amongst all individuals in the conversation, and should be
5936 * consistent during re-posts of the notification.
5937 *
5938 * @see Message#Message(CharSequence, long, CharSequence)
5939 *
5940 * @return this object for method chaining
5941 */
5942 public MessagingStyle addMessage(CharSequence text, long timestamp, CharSequence sender) {
Adrian Roos437cd562017-01-18 15:47:03 -08005943 return addMessage(new Message(text, timestamp, sender));
Alex Hillsfc737de2016-03-23 17:33:02 -04005944 }
5945
5946 /**
5947 * Adds a {@link Message} for display in this notification.
Adrian Roos437cd562017-01-18 15:47:03 -08005948 *
5949 * <p>The messages should be added in chronologic order, i.e. the oldest first,
5950 * the newest last.
5951 *
Alex Hillsfc737de2016-03-23 17:33:02 -04005952 * @param message The {@link Message} to be displayed
5953 * @return this object for method chaining
5954 */
5955 public MessagingStyle addMessage(Message message) {
5956 mMessages.add(message);
5957 if (mMessages.size() > MAXIMUM_RETAINED_MESSAGES) {
5958 mMessages.remove(0);
5959 }
5960 return this;
5961 }
5962
5963 /**
Adrian Roos437cd562017-01-18 15:47:03 -08005964 * Adds a {@link Message} for historic context in this notification.
5965 *
5966 * <p>Messages should be added as historic if they are not the main subject of the
5967 * notification but may give context to a conversation. The system may choose to present
5968 * them only when relevant, e.g. when replying to a message through a {@link RemoteInput}.
5969 *
5970 * <p>The messages should be added in chronologic order, i.e. the oldest first,
5971 * the newest last.
5972 *
5973 * @param message The historic {@link Message} to be added
5974 * @return this object for method chaining
5975 */
5976 public MessagingStyle addHistoricMessage(Message message) {
5977 mHistoricMessages.add(message);
5978 if (mHistoricMessages.size() > MAXIMUM_RETAINED_MESSAGES) {
5979 mHistoricMessages.remove(0);
5980 }
5981 return this;
5982 }
5983
5984 /**
Alex Hillsfc737de2016-03-23 17:33:02 -04005985 * Gets the list of {@code Message} objects that represent the notification
5986 */
5987 public List<Message> getMessages() {
5988 return mMessages;
5989 }
5990
5991 /**
Adrian Roos437cd562017-01-18 15:47:03 -08005992 * Gets the list of historic {@code Message}s in the notification.
5993 */
5994 public List<Message> getHistoricMessages() {
5995 return mHistoricMessages;
5996 }
5997
5998 /**
Alex Hillsfc737de2016-03-23 17:33:02 -04005999 * @hide
6000 */
6001 @Override
6002 public void addExtras(Bundle extras) {
6003 super.addExtras(extras);
6004 if (mUserDisplayName != null) {
6005 extras.putCharSequence(EXTRA_SELF_DISPLAY_NAME, mUserDisplayName);
6006 }
6007 if (mConversationTitle != null) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04006008 extras.putCharSequence(EXTRA_CONVERSATION_TITLE, mConversationTitle);
Alex Hillsfc737de2016-03-23 17:33:02 -04006009 }
Alex Hillsd9b04d92016-04-11 16:38:16 -04006010 if (!mMessages.isEmpty()) { extras.putParcelableArray(EXTRA_MESSAGES,
6011 Message.getBundleArrayForMessages(mMessages));
Alex Hillsfc737de2016-03-23 17:33:02 -04006012 }
Adrian Roos437cd562017-01-18 15:47:03 -08006013 if (!mHistoricMessages.isEmpty()) { extras.putParcelableArray(EXTRA_HISTORIC_MESSAGES,
6014 Message.getBundleArrayForMessages(mHistoricMessages));
6015 }
Adrian Roos33fbd2c2016-05-27 15:35:28 -07006016
6017 fixTitleAndTextExtras(extras);
6018 }
6019
6020 private void fixTitleAndTextExtras(Bundle extras) {
6021 Message m = findLatestIncomingMessage();
6022 CharSequence text = (m == null) ? null : m.mText;
6023 CharSequence sender = m == null ? null
6024 : TextUtils.isEmpty(m.mSender) ? mUserDisplayName : m.mSender;
6025 CharSequence title;
6026 if (!TextUtils.isEmpty(mConversationTitle)) {
6027 if (!TextUtils.isEmpty(sender)) {
6028 BidiFormatter bidi = BidiFormatter.getInstance();
6029 title = mBuilder.mContext.getString(
6030 com.android.internal.R.string.notification_messaging_title_template,
6031 bidi.unicodeWrap(mConversationTitle), bidi.unicodeWrap(m.mSender));
6032 } else {
6033 title = mConversationTitle;
6034 }
6035 } else {
6036 title = sender;
6037 }
6038
6039 if (title != null) {
6040 extras.putCharSequence(EXTRA_TITLE, title);
6041 }
6042 if (text != null) {
6043 extras.putCharSequence(EXTRA_TEXT, text);
6044 }
Alex Hillsfc737de2016-03-23 17:33:02 -04006045 }
6046
6047 /**
6048 * @hide
6049 */
6050 @Override
6051 protected void restoreFromExtras(Bundle extras) {
6052 super.restoreFromExtras(extras);
6053
6054 mMessages.clear();
Adrian Roos437cd562017-01-18 15:47:03 -08006055 mHistoricMessages.clear();
Adrian Roos96b7e202016-05-17 13:50:38 -07006056 mUserDisplayName = extras.getCharSequence(EXTRA_SELF_DISPLAY_NAME);
6057 mConversationTitle = extras.getCharSequence(EXTRA_CONVERSATION_TITLE);
Adrian Roos437cd562017-01-18 15:47:03 -08006058 Parcelable[] messages = extras.getParcelableArray(EXTRA_MESSAGES);
6059 if (messages != null && messages instanceof Parcelable[]) {
6060 mMessages = Message.getMessagesFromBundleArray(messages);
6061 }
6062 Parcelable[] histMessages = extras.getParcelableArray(EXTRA_HISTORIC_MESSAGES);
6063 if (histMessages != null && histMessages instanceof Parcelable[]) {
6064 mHistoricMessages = Message.getMessagesFromBundleArray(histMessages);
Alex Hillsfc737de2016-03-23 17:33:02 -04006065 }
6066 }
6067
6068 /**
6069 * @hide
6070 */
Adrian Roosc1a80b02016-04-05 14:54:55 -07006071 @Override
Selim Cinek7d1009b2017-01-25 15:28:28 -08006072 public RemoteViews makeContentView(boolean increasedHeight) {
6073 if (!increasedHeight) {
6074 Message m = findLatestIncomingMessage();
6075 CharSequence title = mConversationTitle != null
6076 ? mConversationTitle
6077 : (m == null) ? null : m.mSender;
6078 CharSequence text = (m == null)
6079 ? null
6080 : mConversationTitle != null ? makeMessageLine(m, mBuilder) : m.mText;
Alex Hillsfc737de2016-03-23 17:33:02 -04006081
Selim Cinek7d1009b2017-01-25 15:28:28 -08006082 return mBuilder.applyStandardTemplate(mBuilder.getBaseLayoutResource(),
6083 mBuilder.mParams.reset().hasProgress(false).title(title).text(text));
6084 } else {
Selim Cinek5d6ef8d2017-05-18 22:16:00 -07006085 mBuilder.mOriginalActions = mBuilder.mActions;
Selim Cinek7d1009b2017-01-25 15:28:28 -08006086 mBuilder.mActions = new ArrayList<>();
6087 RemoteViews remoteViews = makeBigContentView();
Selim Cinek5d6ef8d2017-05-18 22:16:00 -07006088 mBuilder.mActions = mBuilder.mOriginalActions;
6089 mBuilder.mOriginalActions = null;
Selim Cinek7d1009b2017-01-25 15:28:28 -08006090 return remoteViews;
6091 }
Adrian Roosc1a80b02016-04-05 14:54:55 -07006092 }
6093
6094 private Message findLatestIncomingMessage() {
6095 for (int i = mMessages.size() - 1; i >= 0; i--) {
6096 Message m = mMessages.get(i);
6097 // Incoming messages have a non-empty sender.
6098 if (!TextUtils.isEmpty(m.mSender)) {
6099 return m;
6100 }
6101 }
Adrian Roos33fbd2c2016-05-27 15:35:28 -07006102 if (!mMessages.isEmpty()) {
6103 // No incoming messages, fall back to outgoing message
6104 return mMessages.get(mMessages.size() - 1);
6105 }
Adrian Roosc1a80b02016-04-05 14:54:55 -07006106 return null;
6107 }
6108
6109 /**
6110 * @hide
6111 */
6112 @Override
6113 public RemoteViews makeBigContentView() {
6114 CharSequence title = !TextUtils.isEmpty(super.mBigContentTitle)
6115 ? super.mBigContentTitle
6116 : mConversationTitle;
6117 boolean hasTitle = !TextUtils.isEmpty(title);
6118
Adrian Roosfeafa052016-06-01 17:09:45 -07006119 if (mMessages.size() == 1) {
6120 // Special case for a single message: Use the big text style
6121 // so the collapsed and expanded versions match nicely.
6122 CharSequence bigTitle;
6123 CharSequence text;
6124 if (hasTitle) {
6125 bigTitle = title;
Selim Cinek7b9605b2017-01-19 17:36:00 -08006126 text = makeMessageLine(mMessages.get(0), mBuilder);
Adrian Roosfeafa052016-06-01 17:09:45 -07006127 } else {
6128 bigTitle = mMessages.get(0).mSender;
6129 text = mMessages.get(0).mText;
6130 }
Adrian Roosb1f427c2016-05-26 12:27:15 -07006131 RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(
6132 mBuilder.getBigTextLayoutResource(),
Adrian Roos70d7aa32017-01-11 15:39:06 -08006133 mBuilder.mParams.reset().hasProgress(false).title(bigTitle).text(null));
Adrian Roosb1f427c2016-05-26 12:27:15 -07006134 BigTextStyle.applyBigTextContentView(mBuilder, contentView, text);
6135 return contentView;
6136 }
6137
Adrian Roos48d746a2016-04-12 14:57:28 -07006138 RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(
Adrian Roosc1a80b02016-04-05 14:54:55 -07006139 mBuilder.getMessagingLayoutResource(),
Adrian Roos70d7aa32017-01-11 15:39:06 -08006140 mBuilder.mParams.reset().hasProgress(false).title(title).text(null));
Adrian Roosc1a80b02016-04-05 14:54:55 -07006141
6142 int[] rowIds = {R.id.inbox_text0, R.id.inbox_text1, R.id.inbox_text2, R.id.inbox_text3,
6143 R.id.inbox_text4, R.id.inbox_text5, R.id.inbox_text6};
6144
6145 // Make sure all rows are gone in case we reuse a view.
6146 for (int rowId : rowIds) {
6147 contentView.setViewVisibility(rowId, View.GONE);
6148 }
6149
6150 int i=0;
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006151 contentView.setViewLayoutMarginBottomDimen(R.id.line1,
6152 hasTitle ? R.dimen.notification_messaging_spacing : 0);
Adrian Roosc1a80b02016-04-05 14:54:55 -07006153 contentView.setInt(R.id.notification_messaging, "setNumIndentLines",
Selim Cinek279fa862016-06-14 10:57:25 -07006154 !mBuilder.mN.hasLargeIcon() ? 0 : (hasTitle ? 1 : 2));
Adrian Roosc1a80b02016-04-05 14:54:55 -07006155
Adrian Roosfeafa052016-06-01 17:09:45 -07006156 int contractedChildId = View.NO_ID;
6157 Message contractedMessage = findLatestIncomingMessage();
Adrian Roos437cd562017-01-18 15:47:03 -08006158 int firstHistoricMessage = Math.max(0, mHistoricMessages.size()
6159 - (rowIds.length - mMessages.size()));
6160 while (firstHistoricMessage + i < mHistoricMessages.size() && i < rowIds.length) {
6161 Message m = mHistoricMessages.get(firstHistoricMessage + i);
6162 int rowId = rowIds[i];
6163
Selim Cinek7b9605b2017-01-19 17:36:00 -08006164 contentView.setTextViewText(rowId, makeMessageLine(m, mBuilder));
Adrian Roos437cd562017-01-18 15:47:03 -08006165
6166 if (contractedMessage == m) {
6167 contractedChildId = rowId;
6168 }
6169
6170 i++;
6171 }
6172
Adrian Roosc1a80b02016-04-05 14:54:55 -07006173 int firstMessage = Math.max(0, mMessages.size() - rowIds.length);
6174 while (firstMessage + i < mMessages.size() && i < rowIds.length) {
6175 Message m = mMessages.get(firstMessage + i);
6176 int rowId = rowIds[i];
6177
6178 contentView.setViewVisibility(rowId, View.VISIBLE);
Selim Cinek48f66b72017-08-18 16:17:51 -07006179 contentView.setTextViewText(rowId, mBuilder.processTextSpans(
6180 makeMessageLine(m, mBuilder)));
Selim Cinek7b9605b2017-01-19 17:36:00 -08006181 mBuilder.setTextViewColorSecondary(contentView, rowId);
Adrian Roosc1a80b02016-04-05 14:54:55 -07006182
Adrian Roosfeafa052016-06-01 17:09:45 -07006183 if (contractedMessage == m) {
6184 contractedChildId = rowId;
6185 }
6186
Adrian Roosc1a80b02016-04-05 14:54:55 -07006187 i++;
6188 }
Adrian Roos437cd562017-01-18 15:47:03 -08006189 // Clear the remaining views for reapply. Ensures that historic message views can
6190 // reliably be identified as being GONE and having non-null text.
6191 while (i < rowIds.length) {
6192 int rowId = rowIds[i];
6193 contentView.setTextViewText(rowId, null);
6194 i++;
6195 }
6196
Adrian Roosfeafa052016-06-01 17:09:45 -07006197 // Record this here to allow transformation between the contracted and expanded views.
6198 contentView.setInt(R.id.notification_messaging, "setContractedChildId",
6199 contractedChildId);
Alex Hillsfc737de2016-03-23 17:33:02 -04006200 return contentView;
6201 }
6202
Selim Cinek7b9605b2017-01-19 17:36:00 -08006203 private CharSequence makeMessageLine(Message m, Builder builder) {
Adrian Roosc1a80b02016-04-05 14:54:55 -07006204 BidiFormatter bidi = BidiFormatter.getInstance();
6205 SpannableStringBuilder sb = new SpannableStringBuilder();
Selim Cinek7b9605b2017-01-19 17:36:00 -08006206 boolean colorize = builder.isColorized();
Anthony Chenad4d1582017-04-10 16:07:58 -07006207 TextAppearanceSpan colorSpan;
6208 CharSequence messageName;
Adrian Roosc1a80b02016-04-05 14:54:55 -07006209 if (TextUtils.isEmpty(m.mSender)) {
6210 CharSequence replyName = mUserDisplayName == null ? "" : mUserDisplayName;
6211 sb.append(bidi.unicodeWrap(replyName),
Selim Cinek7b9605b2017-01-19 17:36:00 -08006212 makeFontColorSpan(colorize
6213 ? builder.getPrimaryTextColor()
6214 : mBuilder.resolveContrastColor()),
Adrian Roosc1a80b02016-04-05 14:54:55 -07006215 0 /* flags */);
6216 } else {
6217 sb.append(bidi.unicodeWrap(m.mSender),
Selim Cinek7b9605b2017-01-19 17:36:00 -08006218 makeFontColorSpan(colorize
6219 ? builder.getPrimaryTextColor()
6220 : Color.BLACK),
Adrian Roosc1a80b02016-04-05 14:54:55 -07006221 0 /* flags */);
6222 }
6223 CharSequence text = m.mText == null ? "" : m.mText;
6224 sb.append(" ").append(bidi.unicodeWrap(text));
6225 return sb;
6226 }
6227
Adrian Roosdedd1df2016-04-26 16:38:47 -07006228 /**
6229 * @hide
6230 */
6231 @Override
Selim Cinek87ed69b2017-02-09 15:59:43 -08006232 public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
6233 if (increasedHeight) {
6234 return makeBigContentView();
6235 }
Adrian Roosdedd1df2016-04-26 16:38:47 -07006236 Message m = findLatestIncomingMessage();
6237 CharSequence title = mConversationTitle != null
6238 ? mConversationTitle
6239 : (m == null) ? null : m.mSender;
6240 CharSequence text = (m == null)
6241 ? null
Selim Cinek7b9605b2017-01-19 17:36:00 -08006242 : mConversationTitle != null ? makeMessageLine(m, mBuilder) : m.mText;
Adrian Roosdedd1df2016-04-26 16:38:47 -07006243
6244 return mBuilder.applyStandardTemplateWithActions(mBuilder.getBigBaseLayoutResource(),
Adrian Roos70d7aa32017-01-11 15:39:06 -08006245 mBuilder.mParams.reset().hasProgress(false).title(title).text(text));
Adrian Roosdedd1df2016-04-26 16:38:47 -07006246 }
6247
Adrian Roosc1a80b02016-04-05 14:54:55 -07006248 private static TextAppearanceSpan makeFontColorSpan(int color) {
6249 return new TextAppearanceSpan(null, 0, 0,
6250 ColorStateList.valueOf(color), null);
6251 }
6252
Alex Hillsd9b04d92016-04-11 16:38:16 -04006253 public static final class Message {
6254
6255 static final String KEY_TEXT = "text";
6256 static final String KEY_TIMESTAMP = "time";
6257 static final String KEY_SENDER = "sender";
6258 static final String KEY_DATA_MIME_TYPE = "type";
6259 static final String KEY_DATA_URI= "uri";
Shane Brennan5a871862017-03-11 13:14:17 -08006260 static final String KEY_EXTRAS_BUNDLE = "extras";
Alex Hillsfc737de2016-03-23 17:33:02 -04006261
6262 private final CharSequence mText;
6263 private final long mTimestamp;
6264 private final CharSequence mSender;
6265
Shane Brennan5a871862017-03-11 13:14:17 -08006266 private Bundle mExtras = new Bundle();
Alex Hillsfc737de2016-03-23 17:33:02 -04006267 private String mDataMimeType;
6268 private Uri mDataUri;
6269
6270 /**
6271 * Constructor
6272 * @param text A {@link CharSequence} to be displayed as the message content
6273 * @param timestamp Time at which the message arrived
6274 * @param sender A {@link CharSequence} to be used for displaying the name of the
6275 * sender. Should be <code>null</code> for messages by the current user, in which case
6276 * the platform will insert {@link MessagingStyle#getUserDisplayName()}.
6277 * Should be unique amongst all individuals in the conversation, and should be
6278 * consistent during re-posts of the notification.
6279 */
6280 public Message(CharSequence text, long timestamp, CharSequence sender){
6281 mText = text;
6282 mTimestamp = timestamp;
6283 mSender = sender;
6284 }
6285
6286 /**
6287 * Sets a binary blob of data and an associated MIME type for a message. In the case
6288 * where the platform doesn't support the MIME type, the original text provided in the
6289 * constructor will be used.
6290 * @param dataMimeType The MIME type of the content. See
6291 * <a href="{@docRoot}notifications/messaging.html"> for the list of supported MIME
6292 * types on Android and Android Wear.
6293 * @param dataUri The uri containing the content whose type is given by the MIME type.
6294 * <p class="note">
6295 * <ol>
6296 * <li>Notification Listeners including the System UI need permission to access the
6297 * data the Uri points to. The recommended ways to do this are:</li>
6298 * <li>Store the data in your own ContentProvider, making sure that other apps have
6299 * the correct permission to access your provider. The preferred mechanism for
6300 * providing access is to use per-URI permissions which are temporary and only
6301 * grant access to the receiving application. An easy way to create a
6302 * ContentProvider like this is to use the FileProvider helper class.</li>
6303 * <li>Use the system MediaStore. The MediaStore is primarily aimed at video, audio
6304 * and image MIME types, however beginning with Android 3.0 (API level 11) it can
6305 * also store non-media types (see MediaStore.Files for more info). Files can be
6306 * inserted into the MediaStore using scanFile() after which a content:// style
6307 * Uri suitable for sharing is passed to the provided onScanCompleted() callback.
6308 * Note that once added to the system MediaStore the content is accessible to any
6309 * app on the device.</li>
6310 * </ol>
6311 * @return this object for method chaining
6312 */
6313 public Message setData(String dataMimeType, Uri dataUri) {
6314 mDataMimeType = dataMimeType;
6315 mDataUri = dataUri;
6316 return this;
6317 }
6318
Alex Hillsfc737de2016-03-23 17:33:02 -04006319 /**
6320 * Get the text to be used for this message, or the fallback text if a type and content
6321 * Uri have been set
6322 */
6323 public CharSequence getText() {
6324 return mText;
6325 }
6326
6327 /**
6328 * Get the time at which this message arrived
6329 */
6330 public long getTimestamp() {
6331 return mTimestamp;
6332 }
6333
6334 /**
Shane Brennan5a871862017-03-11 13:14:17 -08006335 * Get the extras Bundle for this message.
6336 */
6337 public Bundle getExtras() {
6338 return mExtras;
6339 }
6340
6341 /**
Alex Hillsfc737de2016-03-23 17:33:02 -04006342 * Get the text used to display the contact's name in the messaging experience
6343 */
6344 public CharSequence getSender() {
6345 return mSender;
6346 }
6347
6348 /**
6349 * Get the MIME type of the data pointed to by the Uri
6350 */
6351 public String getDataMimeType() {
6352 return mDataMimeType;
6353 }
6354
6355 /**
6356 * Get the the Uri pointing to the content of the message. Can be null, in which case
6357 * {@see #getText()} is used.
6358 */
6359 public Uri getDataUri() {
6360 return mDataUri;
6361 }
6362
Alex Hillsd9b04d92016-04-11 16:38:16 -04006363 private Bundle toBundle() {
6364 Bundle bundle = new Bundle();
Alex Hillsfc737de2016-03-23 17:33:02 -04006365 if (mText != null) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04006366 bundle.putCharSequence(KEY_TEXT, mText);
Alex Hillsfc737de2016-03-23 17:33:02 -04006367 }
Alex Hillsd9b04d92016-04-11 16:38:16 -04006368 bundle.putLong(KEY_TIMESTAMP, mTimestamp);
Alex Hillsfc737de2016-03-23 17:33:02 -04006369 if (mSender != null) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04006370 bundle.putCharSequence(KEY_SENDER, mSender);
Alex Hillsfc737de2016-03-23 17:33:02 -04006371 }
6372 if (mDataMimeType != null) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04006373 bundle.putString(KEY_DATA_MIME_TYPE, mDataMimeType);
Alex Hillsfc737de2016-03-23 17:33:02 -04006374 }
6375 if (mDataUri != null) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04006376 bundle.putParcelable(KEY_DATA_URI, mDataUri);
Alex Hillsfc737de2016-03-23 17:33:02 -04006377 }
Shane Brennan5a871862017-03-11 13:14:17 -08006378 if (mExtras != null) {
6379 bundle.putBundle(KEY_EXTRAS_BUNDLE, mExtras);
6380 }
Alex Hillsd9b04d92016-04-11 16:38:16 -04006381 return bundle;
Alex Hillsfc737de2016-03-23 17:33:02 -04006382 }
6383
Alex Hillsd9b04d92016-04-11 16:38:16 -04006384 static Bundle[] getBundleArrayForMessages(List<Message> messages) {
6385 Bundle[] bundles = new Bundle[messages.size()];
6386 final int N = messages.size();
6387 for (int i = 0; i < N; i++) {
6388 bundles[i] = messages.get(i).toBundle();
6389 }
6390 return bundles;
6391 }
6392
Adrian Roosdedd1df2016-04-26 16:38:47 -07006393 static List<Message> getMessagesFromBundleArray(Parcelable[] bundles) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04006394 List<Message> messages = new ArrayList<>(bundles.length);
6395 for (int i = 0; i < bundles.length; i++) {
Adrian Roosdedd1df2016-04-26 16:38:47 -07006396 if (bundles[i] instanceof Bundle) {
6397 Message message = getMessageFromBundle((Bundle)bundles[i]);
6398 if (message != null) {
6399 messages.add(message);
6400 }
Alex Hillsd9b04d92016-04-11 16:38:16 -04006401 }
6402 }
6403 return messages;
6404 }
6405
6406 static Message getMessageFromBundle(Bundle bundle) {
6407 try {
Adrian Roosfbddd2c2016-05-13 12:57:20 -07006408 if (!bundle.containsKey(KEY_TEXT) || !bundle.containsKey(KEY_TIMESTAMP)) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04006409 return null;
6410 } else {
6411 Message message = new Message(bundle.getCharSequence(KEY_TEXT),
6412 bundle.getLong(KEY_TIMESTAMP), bundle.getCharSequence(KEY_SENDER));
6413 if (bundle.containsKey(KEY_DATA_MIME_TYPE) &&
6414 bundle.containsKey(KEY_DATA_URI)) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04006415 message.setData(bundle.getString(KEY_DATA_MIME_TYPE),
6416 (Uri) bundle.getParcelable(KEY_DATA_URI));
Alex Hillsfc737de2016-03-23 17:33:02 -04006417 }
Shane Brennan5a871862017-03-11 13:14:17 -08006418 if (bundle.containsKey(KEY_EXTRAS_BUNDLE)) {
6419 message.getExtras().putAll(bundle.getBundle(KEY_EXTRAS_BUNDLE));
6420 }
Alex Hillsd9b04d92016-04-11 16:38:16 -04006421 return message;
6422 }
6423 } catch (ClassCastException e) {
6424 return null;
6425 }
6426 }
Alex Hillsfc737de2016-03-23 17:33:02 -04006427 }
6428 }
6429
6430 /**
Daniel Sandler879c5e02012-04-17 16:46:51 -04006431 * Helper class for generating large-format notifications that include a list of (up to 5) strings.
Joe Malin8d40d042012-11-05 11:36:40 -08006432 *
Robert Ly91c5ce32014-06-08 15:37:00 -07006433 * Here's how you'd set the <code>InboxStyle</code> on a notification:
Daniel Sandler879c5e02012-04-17 16:46:51 -04006434 * <pre class="prettyprint">
Robert Ly91c5ce32014-06-08 15:37:00 -07006435 * Notification notif = new Notification.Builder(mContext)
6436 * .setContentTitle(&quot;5 New mails from &quot; + sender.toString())
6437 * .setContentText(subject)
6438 * .setSmallIcon(R.drawable.new_mail)
6439 * .setLargeIcon(aBitmap)
6440 * .setStyle(new Notification.InboxStyle()
6441 * .addLine(str1)
6442 * .addLine(str2)
6443 * .setContentTitle(&quot;&quot;)
6444 * .setSummaryText(&quot;+3 more&quot;))
6445 * .build();
Daniel Sandler879c5e02012-04-17 16:46:51 -04006446 * </pre>
Joe Malin8d40d042012-11-05 11:36:40 -08006447 *
Daniel Sandler879c5e02012-04-17 16:46:51 -04006448 * @see Notification#bigContentView
6449 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04006450 public static class InboxStyle extends Style {
Daniel Sandler879c5e02012-04-17 16:46:51 -04006451 private ArrayList<CharSequence> mTexts = new ArrayList<CharSequence>(5);
6452
Chris Wrenfbd96ba2012-05-01 12:03:58 -04006453 public InboxStyle() {
6454 }
6455
Adrian Roosf5faf9d2016-05-23 13:56:15 -07006456 /**
6457 * @deprecated use {@code InboxStyle()}.
6458 */
6459 @Deprecated
Daniel Sandler879c5e02012-04-17 16:46:51 -04006460 public InboxStyle(Builder builder) {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04006461 setBuilder(builder);
Daniel Sandler879c5e02012-04-17 16:46:51 -04006462 }
6463
Chris Wrend6297db2012-05-03 16:20:13 -04006464 /**
6465 * Overrides ContentTitle in the big form of the template.
6466 * This defaults to the value passed to setContentTitle().
6467 */
6468 public InboxStyle setBigContentTitle(CharSequence title) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04006469 internalSetBigContentTitle(safeCharSequence(title));
Chris Wrend6297db2012-05-03 16:20:13 -04006470 return this;
6471 }
6472
6473 /**
6474 * Set the first line of text after the detail section in the big form of the template.
6475 */
6476 public InboxStyle setSummaryText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04006477 internalSetSummaryText(safeCharSequence(cs));
Chris Wrend6297db2012-05-03 16:20:13 -04006478 return this;
6479 }
6480
Chris Wren0bd664d2012-08-01 13:56:56 -04006481 /**
6482 * Append a line to the digest section of the Inbox notification.
6483 */
Daniel Sandler879c5e02012-04-17 16:46:51 -04006484 public InboxStyle addLine(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04006485 mTexts.add(safeCharSequence(cs));
Daniel Sandler879c5e02012-04-17 16:46:51 -04006486 return this;
6487 }
6488
Daniel Sandlerf45564e2013-04-15 15:05:08 -04006489 /**
6490 * @hide
6491 */
6492 public void addExtras(Bundle extras) {
6493 super.addExtras(extras);
Christoph Studer4600f9b2014-07-22 22:44:43 +02006494
Daniel Sandlerf45564e2013-04-15 15:05:08 -04006495 CharSequence[] a = new CharSequence[mTexts.size()];
6496 extras.putCharSequenceArray(EXTRA_TEXT_LINES, mTexts.toArray(a));
6497 }
6498
Christoph Studer4600f9b2014-07-22 22:44:43 +02006499 /**
6500 * @hide
6501 */
6502 @Override
6503 protected void restoreFromExtras(Bundle extras) {
6504 super.restoreFromExtras(extras);
6505
6506 mTexts.clear();
6507 if (extras.containsKey(EXTRA_TEXT_LINES)) {
6508 Collections.addAll(mTexts, extras.getCharSequenceArray(EXTRA_TEXT_LINES));
6509 }
6510 }
6511
Julia Reynoldsd9228f12015-10-20 10:37:27 -04006512 /**
6513 * @hide
6514 */
6515 public RemoteViews makeBigContentView() {
Selim Cinekc848c3a2016-01-13 15:27:30 -08006516 // Remove the content text so it disappears unless you have a summary
Christoph Studer4600f9b2014-07-22 22:44:43 +02006517 // Nasty
Julia Reynoldsd9228f12015-10-20 10:37:27 -04006518 CharSequence oldBuilderContentText = mBuilder.mN.extras.getCharSequence(EXTRA_TEXT);
6519 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, null);
Christoph Studer4600f9b2014-07-22 22:44:43 +02006520
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01006521 RemoteViews contentView = getStandardView(mBuilder.getInboxLayoutResource());
Daniel Sandler619738c2012-06-07 16:33:08 -04006522
Julia Reynoldsd9228f12015-10-20 10:37:27 -04006523 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, oldBuilderContentText);
Christoph Studer4600f9b2014-07-22 22:44:43 +02006524
Chris Wrend6297db2012-05-03 16:20:13 -04006525 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 -04006526 R.id.inbox_text4, R.id.inbox_text5, R.id.inbox_text6};
Chris Wrend6297db2012-05-03 16:20:13 -04006527
Chris Wren4ed80d52012-05-17 09:30:03 -04006528 // Make sure all rows are gone in case we reuse a view.
6529 for (int rowId : rowIds) {
6530 contentView.setViewVisibility(rowId, View.GONE);
6531 }
6532
Daniel Sandler879c5e02012-04-17 16:46:51 -04006533 int i=0;
Selim Cinek07c80172016-04-21 16:40:47 -07006534 int topPadding = mBuilder.mContext.getResources().getDimensionPixelSize(
6535 R.dimen.notification_inbox_item_top_padding);
Selim Cinek247fa012016-02-18 09:50:48 -08006536 boolean first = true;
Selim Cinek07c80172016-04-21 16:40:47 -07006537 int onlyViewId = 0;
6538 int maxRows = rowIds.length;
6539 if (mBuilder.mActions.size() > 0) {
6540 maxRows--;
6541 }
6542 while (i < mTexts.size() && i < maxRows) {
Daniel Sandler879c5e02012-04-17 16:46:51 -04006543 CharSequence str = mTexts.get(i);
Selim Cinek07c80172016-04-21 16:40:47 -07006544 if (!TextUtils.isEmpty(str)) {
Daniel Sandler879c5e02012-04-17 16:46:51 -04006545 contentView.setViewVisibility(rowIds[i], View.VISIBLE);
Selim Cinek48f66b72017-08-18 16:17:51 -07006546 contentView.setTextViewText(rowIds[i],
6547 mBuilder.processTextSpans(mBuilder.processLegacyText(str)));
Selim Cinek7b9605b2017-01-19 17:36:00 -08006548 mBuilder.setTextViewColorSecondary(contentView, rowIds[i]);
Selim Cinek07c80172016-04-21 16:40:47 -07006549 contentView.setViewPadding(rowIds[i], 0, topPadding, 0, 0);
Selim Cinek247fa012016-02-18 09:50:48 -08006550 handleInboxImageMargin(contentView, rowIds[i], first);
Selim Cinek07c80172016-04-21 16:40:47 -07006551 if (first) {
6552 onlyViewId = rowIds[i];
6553 } else {
6554 onlyViewId = 0;
6555 }
Selim Cinek247fa012016-02-18 09:50:48 -08006556 first = false;
Daniel Sandler879c5e02012-04-17 16:46:51 -04006557 }
6558 i++;
6559 }
Selim Cinek07c80172016-04-21 16:40:47 -07006560 if (onlyViewId != 0) {
6561 // We only have 1 entry, lets make it look like the normal Text of a Bigtext
6562 topPadding = mBuilder.mContext.getResources().getDimensionPixelSize(
6563 R.dimen.notification_text_margin_top);
6564 contentView.setViewPadding(onlyViewId, 0, topPadding, 0, 0);
6565 }
Selim Cinek1e0bf612015-11-20 15:57:26 -08006566
Daniel Sandler879c5e02012-04-17 16:46:51 -04006567 return contentView;
6568 }
Selim Cinek1e0bf612015-11-20 15:57:26 -08006569
Selim Cinek247fa012016-02-18 09:50:48 -08006570 private void handleInboxImageMargin(RemoteViews contentView, int id, boolean first) {
Selim Cinek1e0bf612015-11-20 15:57:26 -08006571 int endMargin = 0;
Selim Cinek247fa012016-02-18 09:50:48 -08006572 if (first) {
6573 final int max = mBuilder.mN.extras.getInt(EXTRA_PROGRESS_MAX, 0);
6574 final boolean ind = mBuilder.mN.extras.getBoolean(EXTRA_PROGRESS_INDETERMINATE);
6575 boolean hasProgress = max != 0 || ind;
Selim Cinek279fa862016-06-14 10:57:25 -07006576 if (mBuilder.mN.hasLargeIcon() && !hasProgress) {
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006577 endMargin = R.dimen.notification_content_picture_margin;
Selim Cinek247fa012016-02-18 09:50:48 -08006578 }
Selim Cinek1e0bf612015-11-20 15:57:26 -08006579 }
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006580 contentView.setViewLayoutMarginEndDimen(id, endMargin);
Selim Cinek1e0bf612015-11-20 15:57:26 -08006581 }
Daniel Sandler879c5e02012-04-17 16:46:51 -04006582 }
Dan Sandler842dd772014-05-15 09:36:47 -04006583
6584 /**
6585 * Notification style for media playback notifications.
6586 *
6587 * In the expanded form, {@link Notification#bigContentView}, up to 5
6588 * {@link Notification.Action}s specified with
Dan Sandler86647982015-05-13 23:41:13 -04006589 * {@link Notification.Builder#addAction(Action) addAction} will be
Dan Sandler842dd772014-05-15 09:36:47 -04006590 * shown as icon-only pushbuttons, suitable for transport controls. The Bitmap given to
6591 * {@link Notification.Builder#setLargeIcon(android.graphics.Bitmap) setLargeIcon()} will be
6592 * treated as album artwork.
Selim Cinek99104832017-01-25 14:47:33 -08006593 * <p>
Dan Sandler842dd772014-05-15 09:36:47 -04006594 * Unlike the other styles provided here, MediaStyle can also modify the standard-size
6595 * {@link Notification#contentView}; by providing action indices to
Christoph Studerfde6f4d2014-12-12 13:23:26 +01006596 * {@link #setShowActionsInCompactView(int...)} you can promote up to 3 actions to be displayed
Dan Sandler842dd772014-05-15 09:36:47 -04006597 * in the standard view alongside the usual content.
Selim Cinek99104832017-01-25 14:47:33 -08006598 * <p>
Bryan Mawhinney6be8de32014-07-18 10:35:12 +01006599 * Notifications created with MediaStyle will have their category set to
6600 * {@link Notification#CATEGORY_TRANSPORT CATEGORY_TRANSPORT} unless you set a different
6601 * category using {@link Notification.Builder#setCategory(String) setCategory()}.
Selim Cinek99104832017-01-25 14:47:33 -08006602 * <p>
Jeff Browndba34ba2014-06-24 20:46:03 -07006603 * Finally, if you attach a {@link android.media.session.MediaSession.Token} using
6604 * {@link android.app.Notification.MediaStyle#setMediaSession(MediaSession.Token)},
Dan Sandler842dd772014-05-15 09:36:47 -04006605 * the System UI can identify this as a notification representing an active media session
6606 * and respond accordingly (by showing album artwork in the lockscreen, for example).
6607 *
Selim Cinek99104832017-01-25 14:47:33 -08006608 * <p>
6609 * Starting at {@link android.os.Build.VERSION_CODES#O Android O} any notification that has a
6610 * media session attached with {@link #setMediaSession(MediaSession.Token)} will be colorized.
6611 * You can opt-out of this behavior by using {@link Notification.Builder#setColorized(boolean)}.
6612 * <p>
6613 *
Dan Sandler842dd772014-05-15 09:36:47 -04006614 * To use this style with your Notification, feed it to
6615 * {@link Notification.Builder#setStyle(android.app.Notification.Style)} like so:
6616 * <pre class="prettyprint">
6617 * Notification noti = new Notification.Builder()
6618 * .setSmallIcon(R.drawable.ic_stat_player)
Christoph Studere935fe92014-11-24 14:18:06 +01006619 * .setContentTitle(&quot;Track title&quot;)
6620 * .setContentText(&quot;Artist - Album&quot;)
6621 * .setLargeIcon(albumArtBitmap))
Bryan Mawhinney6be8de32014-07-18 10:35:12 +01006622 * .setStyle(<b>new Notification.MediaStyle()</b>
6623 * .setMediaSession(mySession))
Dan Sandler842dd772014-05-15 09:36:47 -04006624 * .build();
6625 * </pre>
6626 *
6627 * @see Notification#bigContentView
Selim Cinek99104832017-01-25 14:47:33 -08006628 * @see Notification.Builder#setColorized(boolean)
Dan Sandler842dd772014-05-15 09:36:47 -04006629 */
6630 public static class MediaStyle extends Style {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006631 static final int MAX_MEDIA_BUTTONS_IN_COMPACT = 3;
Dan Sandler842dd772014-05-15 09:36:47 -04006632 static final int MAX_MEDIA_BUTTONS = 5;
6633
6634 private int[] mActionsToShowInCompact = null;
Jeff Browndba34ba2014-06-24 20:46:03 -07006635 private MediaSession.Token mToken;
Dan Sandler842dd772014-05-15 09:36:47 -04006636
6637 public MediaStyle() {
6638 }
6639
Adrian Roosf5faf9d2016-05-23 13:56:15 -07006640 /**
6641 * @deprecated use {@code MediaStyle()}.
6642 */
6643 @Deprecated
Dan Sandler842dd772014-05-15 09:36:47 -04006644 public MediaStyle(Builder builder) {
6645 setBuilder(builder);
6646 }
6647
6648 /**
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006649 * 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 -04006650 * notification view.
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006651 *
6652 * @param actions the indices of the actions to show in the compact notification view
Dan Sandler842dd772014-05-15 09:36:47 -04006653 */
6654 public MediaStyle setShowActionsInCompactView(int...actions) {
6655 mActionsToShowInCompact = actions;
6656 return this;
6657 }
6658
6659 /**
Jeff Browndba34ba2014-06-24 20:46:03 -07006660 * Attach a {@link android.media.session.MediaSession.Token} to this Notification
6661 * to provide additional playback information and control to the SystemUI.
Dan Sandler842dd772014-05-15 09:36:47 -04006662 */
Jeff Browndba34ba2014-06-24 20:46:03 -07006663 public MediaStyle setMediaSession(MediaSession.Token token) {
Dan Sandler842dd772014-05-15 09:36:47 -04006664 mToken = token;
6665 return this;
6666 }
6667
Christoph Studer4600f9b2014-07-22 22:44:43 +02006668 /**
6669 * @hide
6670 */
Dan Sandler842dd772014-05-15 09:36:47 -04006671 @Override
6672 public Notification buildStyled(Notification wip) {
Christoph Studer4600f9b2014-07-22 22:44:43 +02006673 super.buildStyled(wip);
Bryan Mawhinney6be8de32014-07-18 10:35:12 +01006674 if (wip.category == null) {
6675 wip.category = Notification.CATEGORY_TRANSPORT;
6676 }
Dan Sandler842dd772014-05-15 09:36:47 -04006677 return wip;
6678 }
6679
Christoph Studer4600f9b2014-07-22 22:44:43 +02006680 /**
6681 * @hide
6682 */
6683 @Override
Selim Cinek7d1009b2017-01-25 15:28:28 -08006684 public RemoteViews makeContentView(boolean increasedHeight) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04006685 return makeMediaContentView();
Christoph Studer4600f9b2014-07-22 22:44:43 +02006686 }
6687
6688 /**
6689 * @hide
6690 */
6691 @Override
Julia Reynoldsd9228f12015-10-20 10:37:27 -04006692 public RemoteViews makeBigContentView() {
6693 return makeMediaBigContentView();
Christoph Studer4600f9b2014-07-22 22:44:43 +02006694 }
6695
Selim Cinekcc10bfb2016-02-10 16:24:21 -08006696 /**
6697 * @hide
6698 */
6699 @Override
Selim Cinek87ed69b2017-02-09 15:59:43 -08006700 public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
Selim Cinekcc10bfb2016-02-10 16:24:21 -08006701 RemoteViews expanded = makeMediaBigContentView();
6702 return expanded != null ? expanded : makeMediaContentView();
6703 }
6704
Dan Sandler842dd772014-05-15 09:36:47 -04006705 /** @hide */
6706 @Override
6707 public void addExtras(Bundle extras) {
6708 super.addExtras(extras);
6709
6710 if (mToken != null) {
6711 extras.putParcelable(EXTRA_MEDIA_SESSION, mToken);
6712 }
Bryan Mawhinneye191f902014-07-22 12:50:09 +01006713 if (mActionsToShowInCompact != null) {
6714 extras.putIntArray(EXTRA_COMPACT_ACTIONS, mActionsToShowInCompact);
6715 }
Dan Sandler842dd772014-05-15 09:36:47 -04006716 }
6717
Christoph Studer4600f9b2014-07-22 22:44:43 +02006718 /**
6719 * @hide
6720 */
6721 @Override
6722 protected void restoreFromExtras(Bundle extras) {
6723 super.restoreFromExtras(extras);
6724
6725 if (extras.containsKey(EXTRA_MEDIA_SESSION)) {
6726 mToken = extras.getParcelable(EXTRA_MEDIA_SESSION);
6727 }
6728 if (extras.containsKey(EXTRA_COMPACT_ACTIONS)) {
6729 mActionsToShowInCompact = extras.getIntArray(EXTRA_COMPACT_ACTIONS);
6730 }
6731 }
6732
Selim Cinek5bf069a2015-11-10 19:14:27 -05006733 private RemoteViews generateMediaActionButton(Action action, int color) {
Dan Sandler842dd772014-05-15 09:36:47 -04006734 final boolean tombstone = (action.actionIntent == null);
Selim Cinekf33b1112015-07-15 17:45:11 -07006735 RemoteViews button = new BuilderRemoteViews(mBuilder.mContext.getApplicationInfo(),
Alan Viverette3cb07a462014-06-06 14:19:53 -07006736 R.layout.notification_material_media_action);
Dan Sandler68079d52015-07-22 10:45:30 -04006737 button.setImageViewIcon(R.id.action0, action.getIcon());
Anthony Chenad4d1582017-04-10 16:07:58 -07006738
6739 // If the action buttons should not be tinted, then just use the default
6740 // notification color. Otherwise, just use the passed-in color.
6741 int tintColor = mBuilder.shouldTintActionButtons() || mBuilder.isColorized()
6742 ? color
6743 : NotificationColorUtil.resolveColor(mBuilder.mContext,
6744 Notification.COLOR_DEFAULT);
6745
6746 button.setDrawableParameters(R.id.action0, false, -1, tintColor,
6747 PorterDuff.Mode.SRC_ATOP, -1);
Dan Sandler842dd772014-05-15 09:36:47 -04006748 if (!tombstone) {
6749 button.setOnClickPendingIntent(R.id.action0, action.actionIntent);
6750 }
6751 button.setContentDescription(R.id.action0, action.title);
6752 return button;
6753 }
6754
6755 private RemoteViews makeMediaContentView() {
6756 RemoteViews view = mBuilder.applyStandardTemplate(
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006757 R.layout.notification_template_material_media, false /* hasProgress */);
Dan Sandler842dd772014-05-15 09:36:47 -04006758
6759 final int numActions = mBuilder.mActions.size();
6760 final int N = mActionsToShowInCompact == null
6761 ? 0
6762 : Math.min(mActionsToShowInCompact.length, MAX_MEDIA_BUTTONS_IN_COMPACT);
6763 if (N > 0) {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006764 view.removeAllViews(com.android.internal.R.id.media_actions);
Dan Sandler842dd772014-05-15 09:36:47 -04006765 for (int i = 0; i < N; i++) {
6766 if (i >= numActions) {
6767 throw new IllegalArgumentException(String.format(
6768 "setShowActionsInCompactView: action %d out of bounds (max %d)",
6769 i, numActions - 1));
6770 }
6771
6772 final Action action = mBuilder.mActions.get(mActionsToShowInCompact[i]);
Selim Cinek5bf069a2015-11-10 19:14:27 -05006773 final RemoteViews button = generateMediaActionButton(action,
Selim Cinek99104832017-01-25 14:47:33 -08006774 getPrimaryHighlightColor());
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006775 view.addView(com.android.internal.R.id.media_actions, button);
Dan Sandler842dd772014-05-15 09:36:47 -04006776 }
6777 }
Selim Cinekfdc738f2016-01-27 20:04:27 -08006778 handleImage(view);
6779 // handle the content margin
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006780 int endMargin = R.dimen.notification_content_margin_end;
Selim Cinek279fa862016-06-14 10:57:25 -07006781 if (mBuilder.mN.hasLargeIcon()) {
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006782 endMargin = R.dimen.notification_content_plus_picture_margin_end;
Selim Cinekfdc738f2016-01-27 20:04:27 -08006783 }
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006784 view.setViewLayoutMarginEndDimen(R.id.notification_main_column, endMargin);
Dan Sandler842dd772014-05-15 09:36:47 -04006785 return view;
6786 }
6787
Selim Cinek99104832017-01-25 14:47:33 -08006788 private int getPrimaryHighlightColor() {
6789 return mBuilder.getPrimaryHighlightColor();
6790 }
6791
Dan Sandler842dd772014-05-15 09:36:47 -04006792 private RemoteViews makeMediaBigContentView() {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006793 final int actionCount = Math.min(mBuilder.mActions.size(), MAX_MEDIA_BUTTONS);
Selim Cinekcc10bfb2016-02-10 16:24:21 -08006794 // Dont add an expanded view if there is no more content to be revealed
6795 int actionsInCompact = mActionsToShowInCompact == null
6796 ? 0
6797 : Math.min(mActionsToShowInCompact.length, MAX_MEDIA_BUTTONS_IN_COMPACT);
Selim Cinek279fa862016-06-14 10:57:25 -07006798 if (!mBuilder.mN.hasLargeIcon() && actionCount <= actionsInCompact) {
Selim Cinekcc10bfb2016-02-10 16:24:21 -08006799 return null;
6800 }
Selim Cinek5bf069a2015-11-10 19:14:27 -05006801 RemoteViews big = mBuilder.applyStandardTemplate(
6802 R.layout.notification_template_material_big_media,
6803 false);
Dan Sandler842dd772014-05-15 09:36:47 -04006804
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006805 if (actionCount > 0) {
6806 big.removeAllViews(com.android.internal.R.id.media_actions);
6807 for (int i = 0; i < actionCount; i++) {
Selim Cinek5bf069a2015-11-10 19:14:27 -05006808 final RemoteViews button = generateMediaActionButton(mBuilder.mActions.get(i),
Selim Cinek99104832017-01-25 14:47:33 -08006809 getPrimaryHighlightColor());
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006810 big.addView(com.android.internal.R.id.media_actions, button);
Dan Sandler842dd772014-05-15 09:36:47 -04006811 }
6812 }
Selim Cinek5bf069a2015-11-10 19:14:27 -05006813 handleImage(big);
Dan Sandler842dd772014-05-15 09:36:47 -04006814 return big;
6815 }
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006816
Selim Cinek5bf069a2015-11-10 19:14:27 -05006817 private void handleImage(RemoteViews contentView) {
Selim Cinek279fa862016-06-14 10:57:25 -07006818 if (mBuilder.mN.hasLargeIcon()) {
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006819 contentView.setViewLayoutMarginEndDimen(R.id.line1, 0);
6820 contentView.setViewLayoutMarginEndDimen(R.id.text, 0);
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006821 }
6822 }
6823
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006824 /**
6825 * @hide
6826 */
6827 @Override
6828 protected boolean hasProgress() {
6829 return false;
6830 }
Dan Sandler842dd772014-05-15 09:36:47 -04006831 }
Griff Hazen61a9e862014-05-22 16:05:19 -07006832
Selim Cinek593610c2016-02-16 18:42:57 -08006833 /**
6834 * Notification style for custom views that are decorated by the system
6835 *
6836 * <p>Instead of providing a notification that is completely custom, a developer can set this
6837 * style and still obtain system decorations like the notification header with the expand
6838 * affordance and actions.
6839 *
6840 * <p>Use {@link android.app.Notification.Builder#setCustomContentView(RemoteViews)},
6841 * {@link android.app.Notification.Builder#setCustomBigContentView(RemoteViews)} and
6842 * {@link android.app.Notification.Builder#setCustomHeadsUpContentView(RemoteViews)} to set the
6843 * corresponding custom views to display.
6844 *
6845 * To use this style with your Notification, feed it to
6846 * {@link Notification.Builder#setStyle(android.app.Notification.Style)} like so:
6847 * <pre class="prettyprint">
6848 * Notification noti = new Notification.Builder()
6849 * .setSmallIcon(R.drawable.ic_stat_player)
6850 * .setLargeIcon(albumArtBitmap))
6851 * .setCustomContentView(contentView);
6852 * .setStyle(<b>new Notification.DecoratedCustomViewStyle()</b>)
6853 * .build();
6854 * </pre>
6855 */
6856 public static class DecoratedCustomViewStyle extends Style {
6857
6858 public DecoratedCustomViewStyle() {
6859 }
6860
Selim Cinek593610c2016-02-16 18:42:57 -08006861 /**
6862 * @hide
6863 */
6864 public boolean displayCustomViewInline() {
6865 return true;
6866 }
6867
6868 /**
6869 * @hide
6870 */
6871 @Override
Selim Cinek7d1009b2017-01-25 15:28:28 -08006872 public RemoteViews makeContentView(boolean increasedHeight) {
Selim Cinek593610c2016-02-16 18:42:57 -08006873 return makeStandardTemplateWithCustomContent(mBuilder.mN.contentView);
6874 }
6875
6876 /**
6877 * @hide
6878 */
6879 @Override
6880 public RemoteViews makeBigContentView() {
6881 return makeDecoratedBigContentView();
6882 }
6883
6884 /**
6885 * @hide
6886 */
6887 @Override
Selim Cinek87ed69b2017-02-09 15:59:43 -08006888 public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
Selim Cinek593610c2016-02-16 18:42:57 -08006889 return makeDecoratedHeadsUpContentView();
6890 }
6891
Selim Cinek593610c2016-02-16 18:42:57 -08006892 private RemoteViews makeDecoratedHeadsUpContentView() {
6893 RemoteViews headsUpContentView = mBuilder.mN.headsUpContentView == null
6894 ? mBuilder.mN.contentView
6895 : mBuilder.mN.headsUpContentView;
6896 if (mBuilder.mActions.size() == 0) {
6897 return makeStandardTemplateWithCustomContent(headsUpContentView);
6898 }
6899 RemoteViews remoteViews = mBuilder.applyStandardTemplateWithActions(
6900 mBuilder.getBigBaseLayoutResource());
Selim Cinek247fa012016-02-18 09:50:48 -08006901 buildIntoRemoteViewContent(remoteViews, headsUpContentView);
Selim Cinek593610c2016-02-16 18:42:57 -08006902 return remoteViews;
6903 }
6904
Selim Cinek593610c2016-02-16 18:42:57 -08006905 private RemoteViews makeStandardTemplateWithCustomContent(RemoteViews customContent) {
6906 RemoteViews remoteViews = mBuilder.applyStandardTemplate(
6907 mBuilder.getBaseLayoutResource());
Selim Cinek247fa012016-02-18 09:50:48 -08006908 buildIntoRemoteViewContent(remoteViews, customContent);
Selim Cinek593610c2016-02-16 18:42:57 -08006909 return remoteViews;
6910 }
6911
Selim Cinek593610c2016-02-16 18:42:57 -08006912 private RemoteViews makeDecoratedBigContentView() {
6913 RemoteViews bigContentView = mBuilder.mN.bigContentView == null
6914 ? mBuilder.mN.contentView
6915 : mBuilder.mN.bigContentView;
6916 if (mBuilder.mActions.size() == 0) {
6917 return makeStandardTemplateWithCustomContent(bigContentView);
6918 }
6919 RemoteViews remoteViews = mBuilder.applyStandardTemplateWithActions(
6920 mBuilder.getBigBaseLayoutResource());
Selim Cinek247fa012016-02-18 09:50:48 -08006921 buildIntoRemoteViewContent(remoteViews, bigContentView);
Selim Cinek593610c2016-02-16 18:42:57 -08006922 return remoteViews;
6923 }
Selim Cinek247fa012016-02-18 09:50:48 -08006924
6925 private void buildIntoRemoteViewContent(RemoteViews remoteViews,
6926 RemoteViews customContent) {
Adrian Roos5081c0d2016-02-26 16:04:19 -08006927 if (customContent != null) {
Selim Cinekf91017e2016-03-14 12:25:09 -07006928 // Need to clone customContent before adding, because otherwise it can no longer be
6929 // parceled independently of remoteViews.
Adrian Roos5081c0d2016-02-26 16:04:19 -08006930 customContent = customContent.clone();
Anthony Chen8f5f3582017-04-11 11:18:37 -07006931 remoteViews.removeAllViewsExceptId(R.id.notification_main_column, R.id.progress);
6932 remoteViews.addView(R.id.notification_main_column, customContent, 0 /* index */);
Selim Cinekfc8073c2017-08-16 17:50:20 -07006933 remoteViews.setReapplyDisallowed();
Adrian Roos5081c0d2016-02-26 16:04:19 -08006934 }
Selim Cinek247fa012016-02-18 09:50:48 -08006935 // also update the end margin if there is an image
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006936 int endMargin = R.dimen.notification_content_margin_end;
Selim Cinek279fa862016-06-14 10:57:25 -07006937 if (mBuilder.mN.hasLargeIcon()) {
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006938 endMargin = R.dimen.notification_content_plus_picture_margin_end;
Selim Cinek247fa012016-02-18 09:50:48 -08006939 }
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006940 remoteViews.setViewLayoutMarginEndDimen(R.id.notification_main_column, endMargin);
Selim Cinek247fa012016-02-18 09:50:48 -08006941 }
Selim Cinek593610c2016-02-16 18:42:57 -08006942 }
6943
Selim Cinek03eb3b72016-02-18 10:39:45 -08006944 /**
6945 * Notification style for media custom views that are decorated by the system
6946 *
6947 * <p>Instead of providing a media notification that is completely custom, a developer can set
6948 * this style and still obtain system decorations like the notification header with the expand
6949 * affordance and actions.
6950 *
6951 * <p>Use {@link android.app.Notification.Builder#setCustomContentView(RemoteViews)},
6952 * {@link android.app.Notification.Builder#setCustomBigContentView(RemoteViews)} and
6953 * {@link android.app.Notification.Builder#setCustomHeadsUpContentView(RemoteViews)} to set the
6954 * corresponding custom views to display.
Selim Cinek99104832017-01-25 14:47:33 -08006955 * <p>
6956 * Contrary to {@link MediaStyle} a developer has to opt-in to the colorizing of the
6957 * notification by using {@link Notification.Builder#setColorized(boolean)}.
6958 * <p>
Selim Cinek03eb3b72016-02-18 10:39:45 -08006959 * To use this style with your Notification, feed it to
6960 * {@link Notification.Builder#setStyle(android.app.Notification.Style)} like so:
6961 * <pre class="prettyprint">
6962 * Notification noti = new Notification.Builder()
6963 * .setSmallIcon(R.drawable.ic_stat_player)
6964 * .setLargeIcon(albumArtBitmap))
6965 * .setCustomContentView(contentView);
6966 * .setStyle(<b>new Notification.DecoratedMediaCustomViewStyle()</b>
6967 * .setMediaSession(mySession))
6968 * .build();
6969 * </pre>
6970 *
6971 * @see android.app.Notification.DecoratedCustomViewStyle
6972 * @see android.app.Notification.MediaStyle
6973 */
6974 public static class DecoratedMediaCustomViewStyle extends MediaStyle {
6975
6976 public DecoratedMediaCustomViewStyle() {
6977 }
6978
Selim Cinek03eb3b72016-02-18 10:39:45 -08006979 /**
6980 * @hide
6981 */
6982 public boolean displayCustomViewInline() {
6983 return true;
6984 }
6985
6986 /**
6987 * @hide
6988 */
6989 @Override
Selim Cinek7d1009b2017-01-25 15:28:28 -08006990 public RemoteViews makeContentView(boolean increasedHeight) {
6991 RemoteViews remoteViews = super.makeContentView(false /* increasedHeight */);
Selim Cinek03eb3b72016-02-18 10:39:45 -08006992 return buildIntoRemoteView(remoteViews, R.id.notification_content_container,
6993 mBuilder.mN.contentView);
6994 }
6995
6996 /**
6997 * @hide
6998 */
6999 @Override
7000 public RemoteViews makeBigContentView() {
7001 RemoteViews customRemoteView = mBuilder.mN.bigContentView != null
7002 ? mBuilder.mN.bigContentView
7003 : mBuilder.mN.contentView;
7004 return makeBigContentViewWithCustomContent(customRemoteView);
7005 }
7006
7007 private RemoteViews makeBigContentViewWithCustomContent(RemoteViews customRemoteView) {
7008 RemoteViews remoteViews = super.makeBigContentView();
7009 if (remoteViews != null) {
7010 return buildIntoRemoteView(remoteViews, R.id.notification_main_column,
7011 customRemoteView);
7012 } else if (customRemoteView != mBuilder.mN.contentView){
Selim Cinek7d1009b2017-01-25 15:28:28 -08007013 remoteViews = super.makeContentView(false /* increasedHeight */);
Selim Cinek03eb3b72016-02-18 10:39:45 -08007014 return buildIntoRemoteView(remoteViews, R.id.notification_content_container,
7015 customRemoteView);
7016 } else {
7017 return null;
7018 }
7019 }
7020
7021 /**
7022 * @hide
7023 */
7024 @Override
Selim Cinek87ed69b2017-02-09 15:59:43 -08007025 public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
Selim Cinek03eb3b72016-02-18 10:39:45 -08007026 RemoteViews customRemoteView = mBuilder.mN.headsUpContentView != null
7027 ? mBuilder.mN.headsUpContentView
7028 : mBuilder.mN.contentView;
7029 return makeBigContentViewWithCustomContent(customRemoteView);
7030 }
7031
7032 private RemoteViews buildIntoRemoteView(RemoteViews remoteViews, int id,
7033 RemoteViews customContent) {
Adrian Roos5081c0d2016-02-26 16:04:19 -08007034 if (customContent != null) {
Selim Cinekf91017e2016-03-14 12:25:09 -07007035 // Need to clone customContent before adding, because otherwise it can no longer be
7036 // parceled independently of remoteViews.
Adrian Roos5081c0d2016-02-26 16:04:19 -08007037 customContent = customContent.clone();
Selim Cinek87c31532017-08-18 18:53:44 -07007038 customContent.overrideTextColors(mBuilder.getPrimaryTextColor());
Selim Cinekf91017e2016-03-14 12:25:09 -07007039 remoteViews.removeAllViews(id);
7040 remoteViews.addView(id, customContent);
Selim Cinekfc8073c2017-08-16 17:50:20 -07007041 remoteViews.setReapplyDisallowed();
Adrian Roos5081c0d2016-02-26 16:04:19 -08007042 }
Selim Cinek03eb3b72016-02-18 10:39:45 -08007043 return remoteViews;
7044 }
7045 }
7046
Christoph Studer4600f9b2014-07-22 22:44:43 +02007047 // When adding a new Style subclass here, don't forget to update
7048 // Builder.getNotificationStyleClass.
7049
Griff Hazen61a9e862014-05-22 16:05:19 -07007050 /**
7051 * Extender interface for use with {@link Builder#extend}. Extenders may be used to add
7052 * metadata or change options on a notification builder.
7053 */
7054 public interface Extender {
7055 /**
7056 * Apply this extender to a notification builder.
7057 * @param builder the builder to be modified.
7058 * @return the build object for chaining.
7059 */
7060 public Builder extend(Builder builder);
7061 }
7062
7063 /**
7064 * Helper class to add wearable extensions to notifications.
7065 * <p class="note"> See
7066 * <a href="{@docRoot}wear/notifications/creating.html">Creating Notifications
7067 * for Android Wear</a> for more information on how to use this class.
7068 * <p>
7069 * To create a notification with wearable extensions:
7070 * <ol>
7071 * <li>Create a {@link android.app.Notification.Builder}, setting any desired
7072 * properties.
7073 * <li>Create a {@link android.app.Notification.WearableExtender}.
7074 * <li>Set wearable-specific properties using the
7075 * {@code add} and {@code set} methods of {@link android.app.Notification.WearableExtender}.
7076 * <li>Call {@link android.app.Notification.Builder#extend} to apply the extensions to a
7077 * notification.
7078 * <li>Post the notification to the notification system with the
7079 * {@code NotificationManager.notify(...)} methods.
7080 * </ol>
7081 *
7082 * <pre class="prettyprint">
7083 * Notification notif = new Notification.Builder(mContext)
7084 * .setContentTitle(&quot;New mail from &quot; + sender.toString())
7085 * .setContentText(subject)
7086 * .setSmallIcon(R.drawable.new_mail)
7087 * .extend(new Notification.WearableExtender()
7088 * .setContentIcon(R.drawable.new_mail))
7089 * .build();
7090 * NotificationManager notificationManger =
7091 * (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
7092 * notificationManger.notify(0, notif);</pre>
7093 *
7094 * <p>Wearable extensions can be accessed on an existing notification by using the
7095 * {@code WearableExtender(Notification)} constructor,
7096 * and then using the {@code get} methods to access values.
7097 *
7098 * <pre class="prettyprint">
7099 * Notification.WearableExtender wearableExtender = new Notification.WearableExtender(
7100 * notification);
Griff Hazen14f57992014-05-26 09:07:14 -07007101 * List&lt;Notification&gt; pages = wearableExtender.getPages();</pre>
Griff Hazen61a9e862014-05-22 16:05:19 -07007102 */
7103 public static final class WearableExtender implements Extender {
7104 /**
7105 * Sentinel value for an action index that is unset.
7106 */
7107 public static final int UNSET_ACTION_INDEX = -1;
7108
7109 /**
7110 * Size value for use with {@link #setCustomSizePreset} to show this notification with
7111 * default sizing.
7112 * <p>For custom display notifications created using {@link #setDisplayIntent},
Paul Soulosaa4f4bf2015-08-04 11:59:45 -07007113 * the default is {@link #SIZE_MEDIUM}. All other notifications size automatically based
Griff Hazen61a9e862014-05-22 16:05:19 -07007114 * on their content.
7115 */
7116 public static final int SIZE_DEFAULT = 0;
7117
7118 /**
7119 * Size value for use with {@link #setCustomSizePreset} to show this notification
7120 * with an extra small size.
7121 * <p>This value is only applicable for custom display notifications created using
7122 * {@link #setDisplayIntent}.
7123 */
7124 public static final int SIZE_XSMALL = 1;
7125
7126 /**
7127 * Size value for use with {@link #setCustomSizePreset} to show this notification
7128 * with a small size.
7129 * <p>This value is only applicable for custom display notifications created using
7130 * {@link #setDisplayIntent}.
7131 */
7132 public static final int SIZE_SMALL = 2;
7133
7134 /**
7135 * Size value for use with {@link #setCustomSizePreset} to show this notification
7136 * with a medium size.
7137 * <p>This value is only applicable for custom display notifications created using
7138 * {@link #setDisplayIntent}.
7139 */
7140 public static final int SIZE_MEDIUM = 3;
7141
7142 /**
7143 * Size value for use with {@link #setCustomSizePreset} to show this notification
7144 * with a large size.
7145 * <p>This value is only applicable for custom display notifications created using
7146 * {@link #setDisplayIntent}.
7147 */
7148 public static final int SIZE_LARGE = 4;
7149
Griff Hazend5f11f92014-05-27 15:40:09 -07007150 /**
7151 * Size value for use with {@link #setCustomSizePreset} to show this notification
7152 * full screen.
7153 * <p>This value is only applicable for custom display notifications created using
7154 * {@link #setDisplayIntent}.
7155 */
7156 public static final int SIZE_FULL_SCREEN = 5;
7157
Griff Hazen5f2edfc2014-09-29 16:28:44 -07007158 /**
7159 * Sentinel value for use with {@link #setHintScreenTimeout} to keep the screen on for a
7160 * short amount of time when this notification is displayed on the screen. This
7161 * is the default value.
7162 */
7163 public static final int SCREEN_TIMEOUT_SHORT = 0;
7164
7165 /**
7166 * Sentinel value for use with {@link #setHintScreenTimeout} to keep the screen on
7167 * for a longer amount of time when this notification is displayed on the screen.
7168 */
7169 public static final int SCREEN_TIMEOUT_LONG = -1;
7170
Griff Hazen61a9e862014-05-22 16:05:19 -07007171 /** Notification extra which contains wearable extensions */
7172 private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS";
7173
Pete Gastaf6781d2014-10-07 15:17:05 -04007174 // Keys within EXTRA_WEARABLE_EXTENSIONS for wearable options.
Griff Hazen61a9e862014-05-22 16:05:19 -07007175 private static final String KEY_ACTIONS = "actions";
7176 private static final String KEY_FLAGS = "flags";
7177 private static final String KEY_DISPLAY_INTENT = "displayIntent";
7178 private static final String KEY_PAGES = "pages";
7179 private static final String KEY_BACKGROUND = "background";
7180 private static final String KEY_CONTENT_ICON = "contentIcon";
7181 private static final String KEY_CONTENT_ICON_GRAVITY = "contentIconGravity";
7182 private static final String KEY_CONTENT_ACTION_INDEX = "contentActionIndex";
7183 private static final String KEY_CUSTOM_SIZE_PRESET = "customSizePreset";
7184 private static final String KEY_CUSTOM_CONTENT_HEIGHT = "customContentHeight";
7185 private static final String KEY_GRAVITY = "gravity";
Griff Hazen5f2edfc2014-09-29 16:28:44 -07007186 private static final String KEY_HINT_SCREEN_TIMEOUT = "hintScreenTimeout";
Nadia Benbernou948627e2016-04-14 14:41:08 -04007187 private static final String KEY_DISMISSAL_ID = "dismissalId";
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04007188 private static final String KEY_BRIDGE_TAG = "bridgeTag";
Griff Hazen61a9e862014-05-22 16:05:19 -07007189
7190 // Flags bitwise-ored to mFlags
7191 private static final int FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE = 0x1;
7192 private static final int FLAG_HINT_HIDE_ICON = 1 << 1;
7193 private static final int FLAG_HINT_SHOW_BACKGROUND_ONLY = 1 << 2;
7194 private static final int FLAG_START_SCROLL_BOTTOM = 1 << 3;
Griff Hazen5f2edfc2014-09-29 16:28:44 -07007195 private static final int FLAG_HINT_AVOID_BACKGROUND_CLIPPING = 1 << 4;
Alex Hills4bcb06b2016-04-05 14:26:25 -04007196 private static final int FLAG_BIG_PICTURE_AMBIENT = 1 << 5;
Alex Hills9ab3a232016-04-05 14:54:56 -04007197 private static final int FLAG_HINT_CONTENT_INTENT_LAUNCHES_ACTIVITY = 1 << 6;
Griff Hazen61a9e862014-05-22 16:05:19 -07007198
7199 // Default value for flags integer
7200 private static final int DEFAULT_FLAGS = FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE;
7201
7202 private static final int DEFAULT_CONTENT_ICON_GRAVITY = Gravity.END;
7203 private static final int DEFAULT_GRAVITY = Gravity.BOTTOM;
7204
7205 private ArrayList<Action> mActions = new ArrayList<Action>();
7206 private int mFlags = DEFAULT_FLAGS;
7207 private PendingIntent mDisplayIntent;
7208 private ArrayList<Notification> mPages = new ArrayList<Notification>();
7209 private Bitmap mBackground;
7210 private int mContentIcon;
7211 private int mContentIconGravity = DEFAULT_CONTENT_ICON_GRAVITY;
7212 private int mContentActionIndex = UNSET_ACTION_INDEX;
7213 private int mCustomSizePreset = SIZE_DEFAULT;
7214 private int mCustomContentHeight;
7215 private int mGravity = DEFAULT_GRAVITY;
Griff Hazen5f2edfc2014-09-29 16:28:44 -07007216 private int mHintScreenTimeout;
Nadia Benbernou948627e2016-04-14 14:41:08 -04007217 private String mDismissalId;
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04007218 private String mBridgeTag;
Griff Hazen61a9e862014-05-22 16:05:19 -07007219
7220 /**
7221 * Create a {@link android.app.Notification.WearableExtender} with default
7222 * options.
7223 */
7224 public WearableExtender() {
7225 }
7226
7227 public WearableExtender(Notification notif) {
7228 Bundle wearableBundle = notif.extras.getBundle(EXTRA_WEARABLE_EXTENSIONS);
7229 if (wearableBundle != null) {
7230 List<Action> actions = wearableBundle.getParcelableArrayList(KEY_ACTIONS);
7231 if (actions != null) {
7232 mActions.addAll(actions);
7233 }
7234
7235 mFlags = wearableBundle.getInt(KEY_FLAGS, DEFAULT_FLAGS);
7236 mDisplayIntent = wearableBundle.getParcelable(KEY_DISPLAY_INTENT);
7237
7238 Notification[] pages = getNotificationArrayFromBundle(
7239 wearableBundle, KEY_PAGES);
7240 if (pages != null) {
7241 Collections.addAll(mPages, pages);
7242 }
7243
7244 mBackground = wearableBundle.getParcelable(KEY_BACKGROUND);
7245 mContentIcon = wearableBundle.getInt(KEY_CONTENT_ICON);
7246 mContentIconGravity = wearableBundle.getInt(KEY_CONTENT_ICON_GRAVITY,
7247 DEFAULT_CONTENT_ICON_GRAVITY);
7248 mContentActionIndex = wearableBundle.getInt(KEY_CONTENT_ACTION_INDEX,
7249 UNSET_ACTION_INDEX);
7250 mCustomSizePreset = wearableBundle.getInt(KEY_CUSTOM_SIZE_PRESET,
7251 SIZE_DEFAULT);
7252 mCustomContentHeight = wearableBundle.getInt(KEY_CUSTOM_CONTENT_HEIGHT);
7253 mGravity = wearableBundle.getInt(KEY_GRAVITY, DEFAULT_GRAVITY);
Griff Hazen5f2edfc2014-09-29 16:28:44 -07007254 mHintScreenTimeout = wearableBundle.getInt(KEY_HINT_SCREEN_TIMEOUT);
Nadia Benbernou948627e2016-04-14 14:41:08 -04007255 mDismissalId = wearableBundle.getString(KEY_DISMISSAL_ID);
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04007256 mBridgeTag = wearableBundle.getString(KEY_BRIDGE_TAG);
Griff Hazen61a9e862014-05-22 16:05:19 -07007257 }
7258 }
7259
7260 /**
7261 * Apply wearable extensions to a notification that is being built. This is typically
7262 * called by the {@link android.app.Notification.Builder#extend} method of
7263 * {@link android.app.Notification.Builder}.
7264 */
7265 @Override
7266 public Notification.Builder extend(Notification.Builder builder) {
7267 Bundle wearableBundle = new Bundle();
7268
7269 if (!mActions.isEmpty()) {
7270 wearableBundle.putParcelableArrayList(KEY_ACTIONS, mActions);
7271 }
7272 if (mFlags != DEFAULT_FLAGS) {
7273 wearableBundle.putInt(KEY_FLAGS, mFlags);
7274 }
7275 if (mDisplayIntent != null) {
7276 wearableBundle.putParcelable(KEY_DISPLAY_INTENT, mDisplayIntent);
7277 }
7278 if (!mPages.isEmpty()) {
7279 wearableBundle.putParcelableArray(KEY_PAGES, mPages.toArray(
7280 new Notification[mPages.size()]));
7281 }
7282 if (mBackground != null) {
7283 wearableBundle.putParcelable(KEY_BACKGROUND, mBackground);
7284 }
7285 if (mContentIcon != 0) {
7286 wearableBundle.putInt(KEY_CONTENT_ICON, mContentIcon);
7287 }
7288 if (mContentIconGravity != DEFAULT_CONTENT_ICON_GRAVITY) {
7289 wearableBundle.putInt(KEY_CONTENT_ICON_GRAVITY, mContentIconGravity);
7290 }
7291 if (mContentActionIndex != UNSET_ACTION_INDEX) {
7292 wearableBundle.putInt(KEY_CONTENT_ACTION_INDEX,
7293 mContentActionIndex);
7294 }
7295 if (mCustomSizePreset != SIZE_DEFAULT) {
7296 wearableBundle.putInt(KEY_CUSTOM_SIZE_PRESET, mCustomSizePreset);
7297 }
7298 if (mCustomContentHeight != 0) {
7299 wearableBundle.putInt(KEY_CUSTOM_CONTENT_HEIGHT, mCustomContentHeight);
7300 }
7301 if (mGravity != DEFAULT_GRAVITY) {
7302 wearableBundle.putInt(KEY_GRAVITY, mGravity);
7303 }
Griff Hazen5f2edfc2014-09-29 16:28:44 -07007304 if (mHintScreenTimeout != 0) {
7305 wearableBundle.putInt(KEY_HINT_SCREEN_TIMEOUT, mHintScreenTimeout);
7306 }
Nadia Benbernou948627e2016-04-14 14:41:08 -04007307 if (mDismissalId != null) {
7308 wearableBundle.putString(KEY_DISMISSAL_ID, mDismissalId);
7309 }
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04007310 if (mBridgeTag != null) {
7311 wearableBundle.putString(KEY_BRIDGE_TAG, mBridgeTag);
7312 }
Griff Hazen61a9e862014-05-22 16:05:19 -07007313
7314 builder.getExtras().putBundle(EXTRA_WEARABLE_EXTENSIONS, wearableBundle);
7315 return builder;
7316 }
7317
7318 @Override
7319 public WearableExtender clone() {
7320 WearableExtender that = new WearableExtender();
7321 that.mActions = new ArrayList<Action>(this.mActions);
7322 that.mFlags = this.mFlags;
7323 that.mDisplayIntent = this.mDisplayIntent;
7324 that.mPages = new ArrayList<Notification>(this.mPages);
7325 that.mBackground = this.mBackground;
7326 that.mContentIcon = this.mContentIcon;
7327 that.mContentIconGravity = this.mContentIconGravity;
7328 that.mContentActionIndex = this.mContentActionIndex;
7329 that.mCustomSizePreset = this.mCustomSizePreset;
7330 that.mCustomContentHeight = this.mCustomContentHeight;
7331 that.mGravity = this.mGravity;
Griff Hazen5f2edfc2014-09-29 16:28:44 -07007332 that.mHintScreenTimeout = this.mHintScreenTimeout;
Nadia Benbernou948627e2016-04-14 14:41:08 -04007333 that.mDismissalId = this.mDismissalId;
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04007334 that.mBridgeTag = this.mBridgeTag;
Griff Hazen61a9e862014-05-22 16:05:19 -07007335 return that;
7336 }
7337
7338 /**
7339 * Add a wearable action to this notification.
7340 *
7341 * <p>When wearable actions are added using this method, the set of actions that
7342 * show on a wearable device splits from devices that only show actions added
7343 * using {@link android.app.Notification.Builder#addAction}. This allows for customization
7344 * of which actions display on different devices.
7345 *
7346 * @param action the action to add to this notification
7347 * @return this object for method chaining
7348 * @see android.app.Notification.Action
7349 */
7350 public WearableExtender addAction(Action action) {
7351 mActions.add(action);
7352 return this;
7353 }
7354
7355 /**
7356 * Adds wearable actions to this notification.
7357 *
7358 * <p>When wearable actions are added using this method, the set of actions that
7359 * show on a wearable device splits from devices that only show actions added
7360 * using {@link android.app.Notification.Builder#addAction}. This allows for customization
7361 * of which actions display on different devices.
7362 *
7363 * @param actions the actions to add to this notification
7364 * @return this object for method chaining
7365 * @see android.app.Notification.Action
7366 */
7367 public WearableExtender addActions(List<Action> actions) {
7368 mActions.addAll(actions);
7369 return this;
7370 }
7371
7372 /**
7373 * Clear all wearable actions present on this builder.
7374 * @return this object for method chaining.
7375 * @see #addAction
7376 */
7377 public WearableExtender clearActions() {
7378 mActions.clear();
7379 return this;
7380 }
7381
7382 /**
7383 * Get the wearable actions present on this notification.
7384 */
7385 public List<Action> getActions() {
7386 return mActions;
7387 }
7388
7389 /**
7390 * Set an intent to launch inside of an activity view when displaying
Griff Hazen14f57992014-05-26 09:07:14 -07007391 * this notification. The {@link PendingIntent} provided should be for an activity.
7392 *
7393 * <pre class="prettyprint">
7394 * Intent displayIntent = new Intent(context, MyDisplayActivity.class);
7395 * PendingIntent displayPendingIntent = PendingIntent.getActivity(context,
7396 * 0, displayIntent, PendingIntent.FLAG_UPDATE_CURRENT);
7397 * Notification notif = new Notification.Builder(context)
7398 * .extend(new Notification.WearableExtender()
7399 * .setDisplayIntent(displayPendingIntent)
7400 * .setCustomSizePreset(Notification.WearableExtender.SIZE_MEDIUM))
7401 * .build();</pre>
7402 *
7403 * <p>The activity to launch needs to allow embedding, must be exported, and
Griff Hazen831ca9d2014-06-17 00:38:38 -07007404 * should have an empty task affinity. It is also recommended to use the device
7405 * default light theme.
Griff Hazen14f57992014-05-26 09:07:14 -07007406 *
7407 * <p>Example AndroidManifest.xml entry:
7408 * <pre class="prettyprint">
7409 * &lt;activity android:name=&quot;com.example.MyDisplayActivity&quot;
7410 * android:exported=&quot;true&quot;
7411 * android:allowEmbedded=&quot;true&quot;
Griff Hazen831ca9d2014-06-17 00:38:38 -07007412 * android:taskAffinity=&quot;&quot;
7413 * android:theme=&quot;@android:style/Theme.DeviceDefault.Light&quot; /&gt;</pre>
Griff Hazen61a9e862014-05-22 16:05:19 -07007414 *
7415 * @param intent the {@link PendingIntent} for an activity
7416 * @return this object for method chaining
7417 * @see android.app.Notification.WearableExtender#getDisplayIntent
7418 */
7419 public WearableExtender setDisplayIntent(PendingIntent intent) {
7420 mDisplayIntent = intent;
7421 return this;
7422 }
7423
7424 /**
7425 * Get the intent to launch inside of an activity view when displaying this
7426 * notification. This {@code PendingIntent} should be for an activity.
7427 */
7428 public PendingIntent getDisplayIntent() {
7429 return mDisplayIntent;
7430 }
7431
7432 /**
7433 * Add an additional page of content to display with this notification. The current
7434 * notification forms the first page, and pages added using this function form
7435 * subsequent pages. This field can be used to separate a notification into multiple
7436 * sections.
7437 *
7438 * @param page the notification to add as another page
7439 * @return this object for method chaining
7440 * @see android.app.Notification.WearableExtender#getPages
7441 */
7442 public WearableExtender addPage(Notification page) {
7443 mPages.add(page);
7444 return this;
7445 }
7446
7447 /**
7448 * Add additional pages of content to display with this notification. The current
7449 * notification forms the first page, and pages added using this function form
7450 * subsequent pages. This field can be used to separate a notification into multiple
7451 * sections.
7452 *
7453 * @param pages a list of notifications
7454 * @return this object for method chaining
7455 * @see android.app.Notification.WearableExtender#getPages
7456 */
7457 public WearableExtender addPages(List<Notification> pages) {
7458 mPages.addAll(pages);
7459 return this;
7460 }
7461
7462 /**
7463 * Clear all additional pages present on this builder.
7464 * @return this object for method chaining.
7465 * @see #addPage
7466 */
7467 public WearableExtender clearPages() {
7468 mPages.clear();
7469 return this;
7470 }
7471
7472 /**
7473 * Get the array of additional pages of content for displaying this notification. The
7474 * current notification forms the first page, and elements within this array form
7475 * subsequent pages. This field can be used to separate a notification into multiple
7476 * sections.
7477 * @return the pages for this notification
7478 */
7479 public List<Notification> getPages() {
7480 return mPages;
7481 }
7482
7483 /**
7484 * Set a background image to be displayed behind the notification content.
7485 * Contrary to the {@link android.app.Notification.BigPictureStyle}, this background
7486 * will work with any notification style.
7487 *
7488 * @param background the background bitmap
7489 * @return this object for method chaining
7490 * @see android.app.Notification.WearableExtender#getBackground
7491 */
7492 public WearableExtender setBackground(Bitmap background) {
7493 mBackground = background;
7494 return this;
7495 }
7496
7497 /**
7498 * Get a background image to be displayed behind the notification content.
7499 * Contrary to the {@link android.app.Notification.BigPictureStyle}, this background
7500 * will work with any notification style.
7501 *
7502 * @return the background image
7503 * @see android.app.Notification.WearableExtender#setBackground
7504 */
7505 public Bitmap getBackground() {
7506 return mBackground;
7507 }
7508
7509 /**
7510 * Set an icon that goes with the content of this notification.
7511 */
7512 public WearableExtender setContentIcon(int icon) {
7513 mContentIcon = icon;
7514 return this;
7515 }
7516
7517 /**
7518 * Get an icon that goes with the content of this notification.
7519 */
7520 public int getContentIcon() {
7521 return mContentIcon;
7522 }
7523
7524 /**
7525 * Set the gravity that the content icon should have within the notification display.
7526 * Supported values include {@link android.view.Gravity#START} and
7527 * {@link android.view.Gravity#END}. The default value is {@link android.view.Gravity#END}.
7528 * @see #setContentIcon
7529 */
7530 public WearableExtender setContentIconGravity(int contentIconGravity) {
7531 mContentIconGravity = contentIconGravity;
7532 return this;
7533 }
7534
7535 /**
7536 * Get the gravity that the content icon should have within the notification display.
7537 * Supported values include {@link android.view.Gravity#START} and
7538 * {@link android.view.Gravity#END}. The default value is {@link android.view.Gravity#END}.
7539 * @see #getContentIcon
7540 */
7541 public int getContentIconGravity() {
7542 return mContentIconGravity;
7543 }
7544
7545 /**
7546 * Set an action from this notification's actions to be clickable with the content of
Griff Hazen14f57992014-05-26 09:07:14 -07007547 * this notification. This action will no longer display separately from the
7548 * notification's content.
7549 *
Griff Hazenca48d352014-05-28 22:37:13 -07007550 * <p>For notifications with multiple pages, child pages can also have content actions
Griff Hazen14f57992014-05-26 09:07:14 -07007551 * set, although the list of available actions comes from the main notification and not
7552 * from the child page's notification.
7553 *
7554 * @param actionIndex The index of the action to hoist onto the current notification page.
7555 * If wearable actions were added to the main notification, this index
7556 * will apply to that list, otherwise it will apply to the regular
7557 * actions list.
Griff Hazen61a9e862014-05-22 16:05:19 -07007558 */
7559 public WearableExtender setContentAction(int actionIndex) {
7560 mContentActionIndex = actionIndex;
7561 return this;
7562 }
7563
7564 /**
Griff Hazenca48d352014-05-28 22:37:13 -07007565 * Get the index of the notification action, if any, that was specified as being clickable
7566 * with the content of this notification. This action will no longer display separately
Griff Hazen14f57992014-05-26 09:07:14 -07007567 * from the notification's content.
Griff Hazen61a9e862014-05-22 16:05:19 -07007568 *
Griff Hazenca48d352014-05-28 22:37:13 -07007569 * <p>For notifications with multiple pages, child pages can also have content actions
Griff Hazen14f57992014-05-26 09:07:14 -07007570 * set, although the list of available actions comes from the main notification and not
7571 * from the child page's notification.
7572 *
7573 * <p>If wearable specific actions were added to the main notification, this index will
7574 * apply to that list, otherwise it will apply to the regular actions list.
Griff Hazenca48d352014-05-28 22:37:13 -07007575 *
7576 * @return the action index or {@link #UNSET_ACTION_INDEX} if no action was selected.
Griff Hazen61a9e862014-05-22 16:05:19 -07007577 */
7578 public int getContentAction() {
7579 return mContentActionIndex;
7580 }
7581
7582 /**
7583 * Set the gravity that this notification should have within the available viewport space.
7584 * Supported values include {@link android.view.Gravity#TOP},
7585 * {@link android.view.Gravity#CENTER_VERTICAL} and {@link android.view.Gravity#BOTTOM}.
7586 * The default value is {@link android.view.Gravity#BOTTOM}.
7587 */
7588 public WearableExtender setGravity(int gravity) {
7589 mGravity = gravity;
7590 return this;
7591 }
7592
7593 /**
7594 * Get the gravity that this notification should have within the available viewport space.
7595 * Supported values include {@link android.view.Gravity#TOP},
7596 * {@link android.view.Gravity#CENTER_VERTICAL} and {@link android.view.Gravity#BOTTOM}.
7597 * The default value is {@link android.view.Gravity#BOTTOM}.
7598 */
7599 public int getGravity() {
7600 return mGravity;
7601 }
7602
7603 /**
7604 * Set the custom size preset for the display of this notification out of the available
7605 * presets found in {@link android.app.Notification.WearableExtender}, e.g.
7606 * {@link #SIZE_LARGE}.
7607 * <p>Some custom size presets are only applicable for custom display notifications created
7608 * using {@link android.app.Notification.WearableExtender#setDisplayIntent}. Check the
7609 * documentation for the preset in question. See also
7610 * {@link #setCustomContentHeight} and {@link #getCustomSizePreset}.
7611 */
7612 public WearableExtender setCustomSizePreset(int sizePreset) {
7613 mCustomSizePreset = sizePreset;
7614 return this;
7615 }
7616
7617 /**
7618 * Get the custom size preset for the display of this notification out of the available
7619 * presets found in {@link android.app.Notification.WearableExtender}, e.g.
7620 * {@link #SIZE_LARGE}.
7621 * <p>Some custom size presets are only applicable for custom display notifications created
7622 * using {@link #setDisplayIntent}. Check the documentation for the preset in question.
7623 * See also {@link #setCustomContentHeight} and {@link #setCustomSizePreset}.
7624 */
7625 public int getCustomSizePreset() {
7626 return mCustomSizePreset;
7627 }
7628
7629 /**
7630 * Set the custom height in pixels for the display of this notification's content.
7631 * <p>This option is only available for custom display notifications created
7632 * using {@link android.app.Notification.WearableExtender#setDisplayIntent}. See also
7633 * {@link android.app.Notification.WearableExtender#setCustomSizePreset} and
7634 * {@link #getCustomContentHeight}.
7635 */
7636 public WearableExtender setCustomContentHeight(int height) {
7637 mCustomContentHeight = height;
7638 return this;
7639 }
7640
7641 /**
7642 * Get the custom height in pixels for the display of this notification's content.
7643 * <p>This option is only available for custom display notifications created
7644 * using {@link #setDisplayIntent}. See also {@link #setCustomSizePreset} and
7645 * {@link #setCustomContentHeight}.
7646 */
7647 public int getCustomContentHeight() {
7648 return mCustomContentHeight;
7649 }
7650
7651 /**
7652 * Set whether the scrolling position for the contents of this notification should start
7653 * at the bottom of the contents instead of the top when the contents are too long to
7654 * display within the screen. Default is false (start scroll at the top).
7655 */
7656 public WearableExtender setStartScrollBottom(boolean startScrollBottom) {
7657 setFlag(FLAG_START_SCROLL_BOTTOM, startScrollBottom);
7658 return this;
7659 }
7660
7661 /**
7662 * Get whether the scrolling position for the contents of this notification should start
7663 * at the bottom of the contents instead of the top when the contents are too long to
7664 * display within the screen. Default is false (start scroll at the top).
7665 */
7666 public boolean getStartScrollBottom() {
7667 return (mFlags & FLAG_START_SCROLL_BOTTOM) != 0;
7668 }
7669
7670 /**
7671 * Set whether the content intent is available when the wearable device is not connected
7672 * to a companion device. The user can still trigger this intent when the wearable device
7673 * is offline, but a visual hint will indicate that the content intent may not be available.
7674 * Defaults to true.
7675 */
7676 public WearableExtender setContentIntentAvailableOffline(
7677 boolean contentIntentAvailableOffline) {
7678 setFlag(FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE, contentIntentAvailableOffline);
7679 return this;
7680 }
7681
7682 /**
7683 * Get whether the content intent is available when the wearable device is not connected
7684 * to a companion device. The user can still trigger this intent when the wearable device
7685 * is offline, but a visual hint will indicate that the content intent may not be available.
7686 * Defaults to true.
7687 */
7688 public boolean getContentIntentAvailableOffline() {
7689 return (mFlags & FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE) != 0;
7690 }
7691
7692 /**
7693 * Set a hint that this notification's icon should not be displayed.
7694 * @param hintHideIcon {@code true} to hide the icon, {@code false} otherwise.
7695 * @return this object for method chaining
7696 */
7697 public WearableExtender setHintHideIcon(boolean hintHideIcon) {
7698 setFlag(FLAG_HINT_HIDE_ICON, hintHideIcon);
7699 return this;
7700 }
7701
7702 /**
7703 * Get a hint that this notification's icon should not be displayed.
7704 * @return {@code true} if this icon should not be displayed, false otherwise.
7705 * The default value is {@code false} if this was never set.
7706 */
7707 public boolean getHintHideIcon() {
7708 return (mFlags & FLAG_HINT_HIDE_ICON) != 0;
7709 }
7710
7711 /**
7712 * Set a visual hint that only the background image of this notification should be
7713 * displayed, and other semantic content should be hidden. This hint is only applicable
7714 * to sub-pages added using {@link #addPage}.
7715 */
7716 public WearableExtender setHintShowBackgroundOnly(boolean hintShowBackgroundOnly) {
7717 setFlag(FLAG_HINT_SHOW_BACKGROUND_ONLY, hintShowBackgroundOnly);
7718 return this;
7719 }
7720
7721 /**
7722 * Get a visual hint that only the background image of this notification should be
7723 * displayed, and other semantic content should be hidden. This hint is only applicable
7724 * to sub-pages added using {@link android.app.Notification.WearableExtender#addPage}.
7725 */
7726 public boolean getHintShowBackgroundOnly() {
7727 return (mFlags & FLAG_HINT_SHOW_BACKGROUND_ONLY) != 0;
7728 }
7729
Griff Hazen5f2edfc2014-09-29 16:28:44 -07007730 /**
Griff Hazen9c5be4e2014-11-17 17:47:50 -08007731 * Set a hint that this notification's background should not be clipped if possible,
7732 * and should instead be resized to fully display on the screen, retaining the aspect
7733 * ratio of the image. This can be useful for images like barcodes or qr codes.
Griff Hazen5f2edfc2014-09-29 16:28:44 -07007734 * @param hintAvoidBackgroundClipping {@code true} to avoid clipping if possible.
7735 * @return this object for method chaining
7736 */
7737 public WearableExtender setHintAvoidBackgroundClipping(
7738 boolean hintAvoidBackgroundClipping) {
7739 setFlag(FLAG_HINT_AVOID_BACKGROUND_CLIPPING, hintAvoidBackgroundClipping);
7740 return this;
7741 }
7742
7743 /**
Griff Hazen9c5be4e2014-11-17 17:47:50 -08007744 * Get a hint that this notification's background should not be clipped if possible,
7745 * and should instead be resized to fully display on the screen, retaining the aspect
7746 * ratio of the image. This can be useful for images like barcodes or qr codes.
Griff Hazen5f2edfc2014-09-29 16:28:44 -07007747 * @return {@code true} if it's ok if the background is clipped on the screen, false
7748 * otherwise. The default value is {@code false} if this was never set.
7749 */
7750 public boolean getHintAvoidBackgroundClipping() {
7751 return (mFlags & FLAG_HINT_AVOID_BACKGROUND_CLIPPING) != 0;
7752 }
7753
7754 /**
7755 * Set a hint that the screen should remain on for at least this duration when
7756 * this notification is displayed on the screen.
7757 * @param timeout The requested screen timeout in milliseconds. Can also be either
7758 * {@link #SCREEN_TIMEOUT_SHORT} or {@link #SCREEN_TIMEOUT_LONG}.
7759 * @return this object for method chaining
7760 */
7761 public WearableExtender setHintScreenTimeout(int timeout) {
7762 mHintScreenTimeout = timeout;
7763 return this;
7764 }
7765
7766 /**
7767 * Get the duration, in milliseconds, that the screen should remain on for
7768 * when this notification is displayed.
7769 * @return the duration in milliseconds if > 0, or either one of the sentinel values
7770 * {@link #SCREEN_TIMEOUT_SHORT} or {@link #SCREEN_TIMEOUT_LONG}.
7771 */
7772 public int getHintScreenTimeout() {
7773 return mHintScreenTimeout;
7774 }
7775
Alex Hills9ab3a232016-04-05 14:54:56 -04007776 /**
Alex Hills4bcb06b2016-04-05 14:26:25 -04007777 * Set a hint that this notification's {@link BigPictureStyle} (if present) should be
7778 * converted to low-bit and displayed in ambient mode, especially useful for barcodes and
7779 * qr codes, as well as other simple black-and-white tickets.
7780 * @param hintAmbientBigPicture {@code true} to enable converstion and ambient.
7781 * @return this object for method chaining
7782 */
7783 public WearableExtender setHintAmbientBigPicture(boolean hintAmbientBigPicture) {
7784 setFlag(FLAG_BIG_PICTURE_AMBIENT, hintAmbientBigPicture);
7785 return this;
7786 }
7787
7788 /**
7789 * Get a hint that this notification's {@link BigPictureStyle} (if present) should be
7790 * converted to low-bit and displayed in ambient mode, especially useful for barcodes and
7791 * qr codes, as well as other simple black-and-white tickets.
7792 * @return {@code true} if it should be displayed in ambient, false otherwise
7793 * otherwise. The default value is {@code false} if this was never set.
7794 */
7795 public boolean getHintAmbientBigPicture() {
7796 return (mFlags & FLAG_BIG_PICTURE_AMBIENT) != 0;
7797 }
7798
7799 /**
Alex Hills9ab3a232016-04-05 14:54:56 -04007800 * Set a hint that this notification's content intent will launch an {@link Activity}
7801 * directly, telling the platform that it can generate the appropriate transitions.
7802 * @param hintContentIntentLaunchesActivity {@code true} if the content intent will launch
7803 * an activity and transitions should be generated, false otherwise.
7804 * @return this object for method chaining
7805 */
7806 public WearableExtender setHintContentIntentLaunchesActivity(
7807 boolean hintContentIntentLaunchesActivity) {
7808 setFlag(FLAG_HINT_CONTENT_INTENT_LAUNCHES_ACTIVITY, hintContentIntentLaunchesActivity);
7809 return this;
7810 }
7811
7812 /**
7813 * Get a hint that this notification's content intent will launch an {@link Activity}
7814 * directly, telling the platform that it can generate the appropriate transitions
7815 * @return {@code true} if the content intent will launch an activity and transitions should
7816 * be generated, false otherwise. The default value is {@code false} if this was never set.
7817 */
7818 public boolean getHintContentIntentLaunchesActivity() {
7819 return (mFlags & FLAG_HINT_CONTENT_INTENT_LAUNCHES_ACTIVITY) != 0;
7820 }
7821
Nadia Benbernou948627e2016-04-14 14:41:08 -04007822 /**
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04007823 * Sets the dismissal id for this notification. If a notification is posted with a
7824 * dismissal id, then when that notification is canceled, notifications on other wearables
7825 * and the paired Android phone having that same dismissal id will also be canceled. See
Nadia Benbernou948627e2016-04-14 14:41:08 -04007826 * <a href="{@docRoot}wear/notifications/index.html">Adding Wearable Features to
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04007827 * Notifications</a> for more information.
Nadia Benbernou948627e2016-04-14 14:41:08 -04007828 * @param dismissalId the dismissal id of the notification.
7829 * @return this object for method chaining
7830 */
7831 public WearableExtender setDismissalId(String dismissalId) {
7832 mDismissalId = dismissalId;
7833 return this;
7834 }
7835
7836 /**
7837 * Returns the dismissal id of the notification.
7838 * @return the dismissal id of the notification or null if it has not been set.
7839 */
7840 public String getDismissalId() {
7841 return mDismissalId;
7842 }
7843
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04007844 /**
7845 * Sets a bridge tag for this notification. A bridge tag can be set for notifications
7846 * posted from a phone to provide finer-grained control on what notifications are bridged
7847 * to wearables. See <a href="{@docRoot}wear/notifications/index.html">Adding Wearable
7848 * Features to Notifications</a> for more information.
7849 * @param bridgeTag the bridge tag of the notification.
7850 * @return this object for method chaining
7851 */
7852 public WearableExtender setBridgeTag(String bridgeTag) {
7853 mBridgeTag = bridgeTag;
7854 return this;
7855 }
7856
7857 /**
7858 * Returns the bridge tag of the notification.
7859 * @return the bridge tag or null if not present.
7860 */
7861 public String getBridgeTag() {
7862 return mBridgeTag;
7863 }
7864
Griff Hazen61a9e862014-05-22 16:05:19 -07007865 private void setFlag(int mask, boolean value) {
7866 if (value) {
7867 mFlags |= mask;
7868 } else {
7869 mFlags &= ~mask;
7870 }
7871 }
7872 }
7873
7874 /**
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08007875 * <p>Helper class to add Android Auto extensions to notifications. To create a notification
7876 * with car extensions:
7877 *
7878 * <ol>
7879 * <li>Create an {@link Notification.Builder}, setting any desired
7880 * properties.
7881 * <li>Create a {@link CarExtender}.
7882 * <li>Set car-specific properties using the {@code add} and {@code set} methods of
7883 * {@link CarExtender}.
7884 * <li>Call {@link Notification.Builder#extend(Notification.Extender)}
7885 * to apply the extensions to a notification.
7886 * </ol>
7887 *
7888 * <pre class="prettyprint">
7889 * Notification notification = new Notification.Builder(context)
7890 * ...
7891 * .extend(new CarExtender()
7892 * .set*(...))
7893 * .build();
7894 * </pre>
7895 *
7896 * <p>Car extensions can be accessed on an existing notification by using the
7897 * {@code CarExtender(Notification)} constructor, and then using the {@code get} methods
7898 * to access values.
7899 */
7900 public static final class CarExtender implements Extender {
7901 private static final String TAG = "CarExtender";
7902
7903 private static final String EXTRA_CAR_EXTENDER = "android.car.EXTENSIONS";
7904 private static final String EXTRA_LARGE_ICON = "large_icon";
7905 private static final String EXTRA_CONVERSATION = "car_conversation";
7906 private static final String EXTRA_COLOR = "app_color";
7907
7908 private Bitmap mLargeIcon;
7909 private UnreadConversation mUnreadConversation;
7910 private int mColor = Notification.COLOR_DEFAULT;
7911
7912 /**
7913 * Create a {@link CarExtender} with default options.
7914 */
7915 public CarExtender() {
7916 }
7917
7918 /**
7919 * Create a {@link CarExtender} from the CarExtender options of an existing Notification.
7920 *
7921 * @param notif The notification from which to copy options.
7922 */
7923 public CarExtender(Notification notif) {
7924 Bundle carBundle = notif.extras == null ?
7925 null : notif.extras.getBundle(EXTRA_CAR_EXTENDER);
7926 if (carBundle != null) {
7927 mLargeIcon = carBundle.getParcelable(EXTRA_LARGE_ICON);
7928 mColor = carBundle.getInt(EXTRA_COLOR, Notification.COLOR_DEFAULT);
7929
7930 Bundle b = carBundle.getBundle(EXTRA_CONVERSATION);
7931 mUnreadConversation = UnreadConversation.getUnreadConversationFromBundle(b);
7932 }
7933 }
7934
7935 /**
7936 * Apply car extensions to a notification that is being built. This is typically called by
7937 * the {@link Notification.Builder#extend(Notification.Extender)}
7938 * method of {@link Notification.Builder}.
7939 */
7940 @Override
7941 public Notification.Builder extend(Notification.Builder builder) {
7942 Bundle carExtensions = new Bundle();
7943
7944 if (mLargeIcon != null) {
7945 carExtensions.putParcelable(EXTRA_LARGE_ICON, mLargeIcon);
7946 }
7947 if (mColor != Notification.COLOR_DEFAULT) {
7948 carExtensions.putInt(EXTRA_COLOR, mColor);
7949 }
7950
7951 if (mUnreadConversation != null) {
7952 Bundle b = mUnreadConversation.getBundleForUnreadConversation();
7953 carExtensions.putBundle(EXTRA_CONVERSATION, b);
7954 }
7955
7956 builder.getExtras().putBundle(EXTRA_CAR_EXTENDER, carExtensions);
7957 return builder;
7958 }
7959
7960 /**
7961 * Sets the accent color to use when Android Auto presents the notification.
7962 *
7963 * Android Auto uses the color set with {@link Notification.Builder#setColor(int)}
7964 * to accent the displayed notification. However, not all colors are acceptable in an
7965 * automotive setting. This method can be used to override the color provided in the
7966 * notification in such a situation.
7967 */
Tor Norbye80756e32015-03-02 09:39:27 -08007968 public CarExtender setColor(@ColorInt int color) {
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08007969 mColor = color;
7970 return this;
7971 }
7972
7973 /**
7974 * Gets the accent color.
7975 *
Julia Reynoldsd9228f12015-10-20 10:37:27 -04007976 * @see #setColor
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08007977 */
Tor Norbye80756e32015-03-02 09:39:27 -08007978 @ColorInt
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08007979 public int getColor() {
7980 return mColor;
7981 }
7982
7983 /**
7984 * Sets the large icon of the car notification.
7985 *
7986 * If no large icon is set in the extender, Android Auto will display the icon
7987 * specified by {@link Notification.Builder#setLargeIcon(android.graphics.Bitmap)}
7988 *
7989 * @param largeIcon The large icon to use in the car notification.
7990 * @return This object for method chaining.
7991 */
7992 public CarExtender setLargeIcon(Bitmap largeIcon) {
7993 mLargeIcon = largeIcon;
7994 return this;
7995 }
7996
7997 /**
7998 * Gets the large icon used in this car notification, or null if no icon has been set.
7999 *
8000 * @return The large icon for the car notification.
8001 * @see CarExtender#setLargeIcon
8002 */
8003 public Bitmap getLargeIcon() {
8004 return mLargeIcon;
8005 }
8006
8007 /**
8008 * Sets the unread conversation in a message notification.
8009 *
8010 * @param unreadConversation The unread part of the conversation this notification conveys.
8011 * @return This object for method chaining.
8012 */
8013 public CarExtender setUnreadConversation(UnreadConversation unreadConversation) {
8014 mUnreadConversation = unreadConversation;
8015 return this;
8016 }
8017
8018 /**
8019 * Returns the unread conversation conveyed by this notification.
8020 * @see #setUnreadConversation(UnreadConversation)
8021 */
8022 public UnreadConversation getUnreadConversation() {
8023 return mUnreadConversation;
8024 }
8025
8026 /**
8027 * A class which holds the unread messages from a conversation.
8028 */
8029 public static class UnreadConversation {
8030 private static final String KEY_AUTHOR = "author";
8031 private static final String KEY_TEXT = "text";
8032 private static final String KEY_MESSAGES = "messages";
8033 private static final String KEY_REMOTE_INPUT = "remote_input";
8034 private static final String KEY_ON_REPLY = "on_reply";
8035 private static final String KEY_ON_READ = "on_read";
8036 private static final String KEY_PARTICIPANTS = "participants";
8037 private static final String KEY_TIMESTAMP = "timestamp";
8038
8039 private final String[] mMessages;
8040 private final RemoteInput mRemoteInput;
8041 private final PendingIntent mReplyPendingIntent;
8042 private final PendingIntent mReadPendingIntent;
8043 private final String[] mParticipants;
8044 private final long mLatestTimestamp;
8045
8046 UnreadConversation(String[] messages, RemoteInput remoteInput,
8047 PendingIntent replyPendingIntent, PendingIntent readPendingIntent,
8048 String[] participants, long latestTimestamp) {
8049 mMessages = messages;
8050 mRemoteInput = remoteInput;
8051 mReadPendingIntent = readPendingIntent;
8052 mReplyPendingIntent = replyPendingIntent;
8053 mParticipants = participants;
8054 mLatestTimestamp = latestTimestamp;
8055 }
8056
8057 /**
8058 * Gets the list of messages conveyed by this notification.
8059 */
8060 public String[] getMessages() {
8061 return mMessages;
8062 }
8063
8064 /**
8065 * Gets the remote input that will be used to convey the response to a message list, or
8066 * null if no such remote input exists.
8067 */
8068 public RemoteInput getRemoteInput() {
8069 return mRemoteInput;
8070 }
8071
8072 /**
8073 * Gets the pending intent that will be triggered when the user replies to this
8074 * notification.
8075 */
8076 public PendingIntent getReplyPendingIntent() {
8077 return mReplyPendingIntent;
8078 }
8079
8080 /**
8081 * Gets the pending intent that Android Auto will send after it reads aloud all messages
8082 * in this object's message list.
8083 */
8084 public PendingIntent getReadPendingIntent() {
8085 return mReadPendingIntent;
8086 }
8087
8088 /**
8089 * Gets the participants in the conversation.
8090 */
8091 public String[] getParticipants() {
8092 return mParticipants;
8093 }
8094
8095 /**
8096 * Gets the firs participant in the conversation.
8097 */
8098 public String getParticipant() {
8099 return mParticipants.length > 0 ? mParticipants[0] : null;
8100 }
8101
8102 /**
8103 * Gets the timestamp of the conversation.
8104 */
8105 public long getLatestTimestamp() {
8106 return mLatestTimestamp;
8107 }
8108
8109 Bundle getBundleForUnreadConversation() {
8110 Bundle b = new Bundle();
8111 String author = null;
8112 if (mParticipants != null && mParticipants.length > 1) {
8113 author = mParticipants[0];
8114 }
8115 Parcelable[] messages = new Parcelable[mMessages.length];
8116 for (int i = 0; i < messages.length; i++) {
8117 Bundle m = new Bundle();
8118 m.putString(KEY_TEXT, mMessages[i]);
8119 m.putString(KEY_AUTHOR, author);
8120 messages[i] = m;
8121 }
8122 b.putParcelableArray(KEY_MESSAGES, messages);
8123 if (mRemoteInput != null) {
8124 b.putParcelable(KEY_REMOTE_INPUT, mRemoteInput);
8125 }
8126 b.putParcelable(KEY_ON_REPLY, mReplyPendingIntent);
8127 b.putParcelable(KEY_ON_READ, mReadPendingIntent);
8128 b.putStringArray(KEY_PARTICIPANTS, mParticipants);
8129 b.putLong(KEY_TIMESTAMP, mLatestTimestamp);
8130 return b;
8131 }
8132
8133 static UnreadConversation getUnreadConversationFromBundle(Bundle b) {
8134 if (b == null) {
8135 return null;
8136 }
8137 Parcelable[] parcelableMessages = b.getParcelableArray(KEY_MESSAGES);
8138 String[] messages = null;
8139 if (parcelableMessages != null) {
8140 String[] tmp = new String[parcelableMessages.length];
8141 boolean success = true;
8142 for (int i = 0; i < tmp.length; i++) {
8143 if (!(parcelableMessages[i] instanceof Bundle)) {
8144 success = false;
8145 break;
8146 }
8147 tmp[i] = ((Bundle) parcelableMessages[i]).getString(KEY_TEXT);
8148 if (tmp[i] == null) {
8149 success = false;
8150 break;
8151 }
8152 }
8153 if (success) {
8154 messages = tmp;
8155 } else {
8156 return null;
8157 }
8158 }
8159
8160 PendingIntent onRead = b.getParcelable(KEY_ON_READ);
8161 PendingIntent onReply = b.getParcelable(KEY_ON_REPLY);
8162
8163 RemoteInput remoteInput = b.getParcelable(KEY_REMOTE_INPUT);
8164
8165 String[] participants = b.getStringArray(KEY_PARTICIPANTS);
8166 if (participants == null || participants.length != 1) {
8167 return null;
8168 }
8169
8170 return new UnreadConversation(messages,
8171 remoteInput,
8172 onReply,
8173 onRead,
8174 participants, b.getLong(KEY_TIMESTAMP));
8175 }
8176 };
8177
8178 /**
8179 * Builder class for {@link CarExtender.UnreadConversation} objects.
8180 */
8181 public static class Builder {
8182 private final List<String> mMessages = new ArrayList<String>();
8183 private final String mParticipant;
8184 private RemoteInput mRemoteInput;
8185 private PendingIntent mReadPendingIntent;
8186 private PendingIntent mReplyPendingIntent;
8187 private long mLatestTimestamp;
8188
8189 /**
8190 * Constructs a new builder for {@link CarExtender.UnreadConversation}.
8191 *
8192 * @param name The name of the other participant in the conversation.
8193 */
8194 public Builder(String name) {
8195 mParticipant = name;
8196 }
8197
8198 /**
8199 * Appends a new unread message to the list of messages for this conversation.
8200 *
8201 * The messages should be added from oldest to newest.
8202 *
8203 * @param message The text of the new unread message.
8204 * @return This object for method chaining.
8205 */
8206 public Builder addMessage(String message) {
8207 mMessages.add(message);
8208 return this;
8209 }
8210
8211 /**
8212 * Sets the pending intent and remote input which will convey the reply to this
8213 * notification.
8214 *
8215 * @param pendingIntent The pending intent which will be triggered on a reply.
8216 * @param remoteInput The remote input parcelable which will carry the reply.
8217 * @return This object for method chaining.
8218 *
8219 * @see CarExtender.UnreadConversation#getRemoteInput
8220 * @see CarExtender.UnreadConversation#getReplyPendingIntent
8221 */
8222 public Builder setReplyAction(
8223 PendingIntent pendingIntent, RemoteInput remoteInput) {
8224 mRemoteInput = remoteInput;
8225 mReplyPendingIntent = pendingIntent;
8226
8227 return this;
8228 }
8229
8230 /**
8231 * Sets the pending intent that will be sent once the messages in this notification
8232 * are read.
8233 *
8234 * @param pendingIntent The pending intent to use.
8235 * @return This object for method chaining.
8236 */
8237 public Builder setReadPendingIntent(PendingIntent pendingIntent) {
8238 mReadPendingIntent = pendingIntent;
8239 return this;
8240 }
8241
8242 /**
8243 * Sets the timestamp of the most recent message in an unread conversation.
8244 *
8245 * If a messaging notification has been posted by your application and has not
8246 * yet been cancelled, posting a later notification with the same id and tag
8247 * but without a newer timestamp may result in Android Auto not displaying a
8248 * heads up notification for the later notification.
8249 *
8250 * @param timestamp The timestamp of the most recent message in the conversation.
8251 * @return This object for method chaining.
8252 */
8253 public Builder setLatestTimestamp(long timestamp) {
8254 mLatestTimestamp = timestamp;
8255 return this;
8256 }
8257
8258 /**
8259 * Builds a new unread conversation object.
8260 *
8261 * @return The new unread conversation object.
8262 */
8263 public UnreadConversation build() {
8264 String[] messages = mMessages.toArray(new String[mMessages.size()]);
8265 String[] participants = { mParticipant };
8266 return new UnreadConversation(messages, mRemoteInput, mReplyPendingIntent,
8267 mReadPendingIntent, participants, mLatestTimestamp);
8268 }
8269 }
8270 }
8271
8272 /**
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08008273 * <p>Helper class to add Android TV extensions to notifications. To create a notification
8274 * with a TV extension:
8275 *
8276 * <ol>
8277 * <li>Create an {@link Notification.Builder}, setting any desired properties.
8278 * <li>Create a {@link TvExtender}.
8279 * <li>Set TV-specific properties using the {@code set} methods of
8280 * {@link TvExtender}.
8281 * <li>Call {@link Notification.Builder#extend(Notification.Extender)}
8282 * to apply the extension to a notification.
8283 * </ol>
8284 *
8285 * <pre class="prettyprint">
8286 * Notification notification = new Notification.Builder(context)
8287 * ...
8288 * .extend(new TvExtender()
8289 * .set*(...))
8290 * .build();
8291 * </pre>
8292 *
8293 * <p>TV extensions can be accessed on an existing notification by using the
8294 * {@code TvExtender(Notification)} constructor, and then using the {@code get} methods
8295 * to access values.
8296 *
8297 * @hide
8298 */
8299 @SystemApi
8300 public static final class TvExtender implements Extender {
8301 private static final String TAG = "TvExtender";
8302
8303 private static final String EXTRA_TV_EXTENDER = "android.tv.EXTENSIONS";
8304 private static final String EXTRA_FLAGS = "flags";
8305 private static final String EXTRA_CONTENT_INTENT = "content_intent";
8306 private static final String EXTRA_DELETE_INTENT = "delete_intent";
Dmitri Plotnikovb8a04ee2017-01-20 10:39:22 -08008307 private static final String EXTRA_CHANNEL_ID = "channel_id";
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08008308
8309 // Flags bitwise-ored to mFlags
8310 private static final int FLAG_AVAILABLE_ON_TV = 0x1;
8311
8312 private int mFlags;
Dmitri Plotnikovb8a04ee2017-01-20 10:39:22 -08008313 private String mChannelId;
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08008314 private PendingIntent mContentIntent;
8315 private PendingIntent mDeleteIntent;
8316
8317 /**
8318 * Create a {@link TvExtender} with default options.
8319 */
8320 public TvExtender() {
8321 mFlags = FLAG_AVAILABLE_ON_TV;
8322 }
8323
8324 /**
8325 * Create a {@link TvExtender} from the TvExtender options of an existing Notification.
8326 *
8327 * @param notif The notification from which to copy options.
8328 */
8329 public TvExtender(Notification notif) {
8330 Bundle bundle = notif.extras == null ?
8331 null : notif.extras.getBundle(EXTRA_TV_EXTENDER);
8332 if (bundle != null) {
8333 mFlags = bundle.getInt(EXTRA_FLAGS);
Dmitri Plotnikovb8a04ee2017-01-20 10:39:22 -08008334 mChannelId = bundle.getString(EXTRA_CHANNEL_ID);
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08008335 mContentIntent = bundle.getParcelable(EXTRA_CONTENT_INTENT);
8336 mDeleteIntent = bundle.getParcelable(EXTRA_DELETE_INTENT);
8337 }
8338 }
8339
8340 /**
8341 * Apply a TV extension to a notification that is being built. This is typically called by
8342 * the {@link Notification.Builder#extend(Notification.Extender)}
8343 * method of {@link Notification.Builder}.
8344 */
8345 @Override
8346 public Notification.Builder extend(Notification.Builder builder) {
8347 Bundle bundle = new Bundle();
8348
8349 bundle.putInt(EXTRA_FLAGS, mFlags);
Dmitri Plotnikovb8a04ee2017-01-20 10:39:22 -08008350 bundle.putString(EXTRA_CHANNEL_ID, mChannelId);
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08008351 if (mContentIntent != null) {
8352 bundle.putParcelable(EXTRA_CONTENT_INTENT, mContentIntent);
8353 }
8354
8355 if (mDeleteIntent != null) {
8356 bundle.putParcelable(EXTRA_DELETE_INTENT, mDeleteIntent);
8357 }
8358
8359 builder.getExtras().putBundle(EXTRA_TV_EXTENDER, bundle);
8360 return builder;
8361 }
8362
8363 /**
8364 * Returns true if this notification should be shown on TV. This method return true
8365 * if the notification was extended with a TvExtender.
8366 */
8367 public boolean isAvailableOnTv() {
8368 return (mFlags & FLAG_AVAILABLE_ON_TV) != 0;
8369 }
8370
8371 /**
Dmitri Plotnikovb8a04ee2017-01-20 10:39:22 -08008372 * Specifies the channel the notification should be delivered on when shown on TV.
8373 * It can be different from the channel that the notification is delivered to when
8374 * posting on a non-TV device.
8375 */
8376 public TvExtender setChannel(String channelId) {
8377 mChannelId = channelId;
8378 return this;
8379 }
8380
8381 /**
Julia Reynoldsbad42972017-04-25 13:52:49 -04008382 * Specifies the channel the notification should be delivered on when shown on TV.
8383 * It can be different from the channel that the notification is delivered to when
8384 * posting on a non-TV device.
8385 */
8386 public TvExtender setChannelId(String channelId) {
8387 mChannelId = channelId;
8388 return this;
8389 }
8390
Jeff Sharkey000ce802017-04-29 13:13:27 -06008391 /** @removed */
8392 @Deprecated
Dmitri Plotnikovb8a04ee2017-01-20 10:39:22 -08008393 public String getChannel() {
8394 return mChannelId;
8395 }
8396
8397 /**
Julia Reynoldsbad42972017-04-25 13:52:49 -04008398 * Returns the id of the channel this notification posts to on TV.
8399 */
8400 public String getChannelId() {
8401 return mChannelId;
8402 }
8403
8404 /**
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08008405 * Supplies a {@link PendingIntent} to be sent when the notification is selected on TV.
8406 * If provided, it is used instead of the content intent specified
8407 * at the level of Notification.
8408 */
8409 public TvExtender setContentIntent(PendingIntent intent) {
8410 mContentIntent = intent;
8411 return this;
8412 }
8413
8414 /**
8415 * Returns the TV-specific content intent. If this method returns null, the
8416 * main content intent on the notification should be used.
8417 *
8418 * @see {@link Notification#contentIntent}
8419 */
8420 public PendingIntent getContentIntent() {
8421 return mContentIntent;
8422 }
8423
8424 /**
8425 * Supplies a {@link PendingIntent} to send when the notification is cleared explicitly
8426 * by the user on TV. If provided, it is used instead of the delete intent specified
8427 * at the level of Notification.
8428 */
8429 public TvExtender setDeleteIntent(PendingIntent intent) {
8430 mDeleteIntent = intent;
8431 return this;
8432 }
8433
8434 /**
8435 * Returns the TV-specific delete intent. If this method returns null, the
8436 * main delete intent on the notification should be used.
8437 *
8438 * @see {@link Notification#deleteIntent}
8439 */
8440 public PendingIntent getDeleteIntent() {
8441 return mDeleteIntent;
8442 }
8443 }
8444
8445 /**
Griff Hazen61a9e862014-05-22 16:05:19 -07008446 * Get an array of Notification objects from a parcelable array bundle field.
8447 * Update the bundle to have a typed array so fetches in the future don't need
8448 * to do an array copy.
8449 */
8450 private static Notification[] getNotificationArrayFromBundle(Bundle bundle, String key) {
8451 Parcelable[] array = bundle.getParcelableArray(key);
8452 if (array instanceof Notification[] || array == null) {
8453 return (Notification[]) array;
8454 }
8455 Notification[] typedArray = Arrays.copyOf(array, array.length,
8456 Notification[].class);
8457 bundle.putParcelableArray(key, typedArray);
8458 return typedArray;
8459 }
Christoph Studer4600f9b2014-07-22 22:44:43 +02008460
8461 private static class BuilderRemoteViews extends RemoteViews {
8462 public BuilderRemoteViews(Parcel parcel) {
8463 super(parcel);
8464 }
8465
Kenny Guy77320062014-08-27 21:37:15 +01008466 public BuilderRemoteViews(ApplicationInfo appInfo, int layoutId) {
8467 super(appInfo, layoutId);
Christoph Studer4600f9b2014-07-22 22:44:43 +02008468 }
8469
8470 @Override
8471 public BuilderRemoteViews clone() {
8472 Parcel p = Parcel.obtain();
8473 writeToParcel(p, 0);
8474 p.setDataPosition(0);
8475 BuilderRemoteViews brv = new BuilderRemoteViews(p);
8476 p.recycle();
8477 return brv;
8478 }
8479 }
Adrian Roos70d7aa32017-01-11 15:39:06 -08008480
8481 private static class StandardTemplateParams {
8482 boolean hasProgress = true;
8483 boolean ambient = false;
8484 CharSequence title;
8485 CharSequence text;
8486
8487 final StandardTemplateParams reset() {
8488 hasProgress = true;
8489 ambient = false;
8490 title = null;
8491 text = null;
8492 return this;
8493 }
8494
8495 final StandardTemplateParams hasProgress(boolean hasProgress) {
8496 this.hasProgress = hasProgress;
8497 return this;
8498 }
8499
8500 final StandardTemplateParams title(CharSequence title) {
8501 this.title = title;
8502 return this;
8503 }
8504
8505 final StandardTemplateParams text(CharSequence text) {
8506 this.text = text;
8507 return this;
8508 }
8509
8510 final StandardTemplateParams ambient(boolean ambient) {
Adrian Roos0bc3f6a2017-03-06 11:54:05 -08008511 Preconditions.checkState(title == null && text == null, "must set ambient before text");
Adrian Roos70d7aa32017-01-11 15:39:06 -08008512 this.ambient = ambient;
8513 return this;
8514 }
8515
8516 final StandardTemplateParams fillTextsFrom(Builder b) {
8517 Bundle extras = b.mN.extras;
Lucas Dupin06c5e642017-09-13 16:34:58 -07008518 this.title = b.processLegacyText(extras.getCharSequence(EXTRA_TITLE), ambient);
8519
8520 // Big text notifications should contain their content when viewed in ambient mode.
8521 CharSequence text = extras.getCharSequence(EXTRA_BIG_TEXT);
8522 if (!ambient || TextUtils.isEmpty(text)) {
8523 text = extras.getCharSequence(EXTRA_TEXT);
8524 }
8525 this.text = b.processLegacyText(text, ambient);
8526
Adrian Roos70d7aa32017-01-11 15:39:06 -08008527 return this;
8528 }
8529 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08008530}