blob: 83d6003b51619c13df4cebd1a418851aff36d627 [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;
Selim Cinek88188f22017-09-19 16:46:56 -070025import android.annotation.Nullable;
Jeff Sharkeyd86b8fe2017-06-02 17:36:26 -060026import android.annotation.RequiresPermission;
Daniel Sandler01df1c62014-06-09 10:54:01 -040027import android.annotation.SdkConstant;
28import android.annotation.SdkConstant.SdkConstantType;
Julia Reynoldse46bb372016-03-17 11:05:58 -040029import android.annotation.SystemApi;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080030import android.content.Context;
31import android.content.Intent;
Kenny Guy77320062014-08-27 21:37:15 +010032import android.content.pm.ApplicationInfo;
Dan Sandler732bd6c2016-04-12 14:20:32 -040033import android.content.pm.PackageManager;
Christoph Studer4600f9b2014-07-22 22:44:43 +020034import android.content.pm.PackageManager.NameNotFoundException;
Julia Reynolds13d898c2017-02-02 12:22:05 -050035import android.content.pm.ShortcutInfo;
Jorim Jaggief72a192014-08-26 21:57:46 +020036import android.content.res.ColorStateList;
Anthony Chenad4d1582017-04-10 16:07:58 -070037import android.content.res.Configuration;
38import android.content.res.Resources;
Joe Onoratoef1e7762010-09-17 18:38:38 -040039import android.graphics.Bitmap;
Kenny Guy8a0101b2014-05-08 23:34:12 +010040import android.graphics.Canvas;
Adrian Roosc1a80b02016-04-05 14:54:55 -070041import android.graphics.Color;
Jorim Jaggi5c2d8462014-03-21 17:37:00 +010042import android.graphics.PorterDuff;
Kenny Guy8a0101b2014-05-08 23:34:12 +010043import android.graphics.drawable.Drawable;
Dan Sandlerd63f9322015-05-06 15:18:49 -040044import android.graphics.drawable.Icon;
John Spurlockc0650f022014-07-19 13:22:39 -040045import android.media.AudioAttributes;
Jeff Sharkey098d5802012-04-26 17:30:34 -070046import android.media.AudioManager;
Jean-Michel Trivi2f7511f2016-11-28 15:40:27 -080047import android.media.PlayerBase;
Jeff Browndba34ba2014-06-24 20:46:03 -070048import android.media.session.MediaSession;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080049import android.net.Uri;
Daniel Sandlerdcbaf662013-04-26 16:23:09 -040050import android.os.BadParcelableException;
Christoph Studer239f8352014-08-25 15:13:18 +020051import android.os.Build;
Daniel Sandler2561b0b2012-02-13 21:04:12 -050052import android.os.Bundle;
Dianne Hackborn98305522017-05-05 17:53:53 -070053import android.os.IBinder;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080054import android.os.Parcel;
55import android.os.Parcelable;
Daniel Sandlera2985ed2012-04-03 16:42:00 -040056import android.os.SystemClock;
Selim Cinek6743c0b2017-01-18 18:24:01 -080057import android.os.SystemProperties;
Jeff Sharkey6d515712012-09-20 16:06:08 -070058import android.os.UserHandle;
Adrian Roosc1a80b02016-04-05 14:54:55 -070059import android.text.BidiFormatter;
Selim Cinek60a54252016-02-26 17:03:25 -080060import android.text.SpannableStringBuilder;
61import android.text.Spanned;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080062import android.text.TextUtils;
Selim Cinek60a54252016-02-26 17:03:25 -080063import android.text.style.AbsoluteSizeSpan;
Selim Cinek89991a22016-03-07 19:51:54 -080064import android.text.style.CharacterStyle;
Selim Cinek981962e2016-07-20 20:41:58 -070065import android.text.style.ForegroundColorSpan;
Selim Cinek60a54252016-02-26 17:03:25 -080066import android.text.style.RelativeSizeSpan;
67import android.text.style.TextAppearanceSpan;
Svet Ganovddb94882016-06-23 19:55:24 -070068import android.util.ArraySet;
Daniel Sandlerdcbaf662013-04-26 16:23:09 -040069import android.util.Log;
Dan Sandler50128532015-12-08 15:42:41 -050070import android.util.SparseArray;
Yi Jin6b514142017-10-30 14:54:12 -070071import android.util.proto.ProtoOutputStream;
Griff Hazen61a9e862014-05-22 16:05:19 -070072import android.view.Gravity;
Selim Cinekea4bef72015-12-02 15:51:10 -080073import android.view.NotificationHeaderView;
Joe Onorato8595a3d2010-11-19 18:12:07 -080074import android.view.View;
Selim Cinek954cc232016-05-20 13:29:23 -070075import android.view.ViewGroup;
Jeff Sharkey1c400132011-08-05 14:50:13 -070076import android.widget.ProgressBar;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080077import android.widget.RemoteViews;
78
Griff Hazen959591e2014-05-15 22:26:18 -070079import com.android.internal.R;
Selim Cinek389edcd2017-05-11 19:16:44 -070080import com.android.internal.annotations.VisibleForTesting;
Svet Ganovddb94882016-06-23 19:55:24 -070081import com.android.internal.util.ArrayUtils;
Griff Hazenc091ba82014-05-16 10:13:26 -070082import com.android.internal.util.NotificationColorUtil;
Adrian Roos0bc3f6a2017-03-06 11:54:05 -080083import com.android.internal.util.Preconditions;
Griff Hazen959591e2014-05-15 22:26:18 -070084
Tor Norbyed9273d62013-05-30 15:59:53 -070085import java.lang.annotation.Retention;
86import java.lang.annotation.RetentionPolicy;
Christoph Studer4600f9b2014-07-22 22:44:43 +020087import java.lang.reflect.Constructor;
Daniel Sandler2561b0b2012-02-13 21:04:12 -050088import java.util.ArrayList;
Griff Hazen61a9e862014-05-22 16:05:19 -070089import java.util.Arrays;
Griff Hazen5cadc3b2014-05-20 09:55:39 -070090import java.util.Collections;
Griff Hazen61a9e862014-05-22 16:05:19 -070091import java.util.List;
Dan Sandler50128532015-12-08 15:42:41 -050092import java.util.Set;
Joe Onorato561d3852010-11-20 18:09:34 -080093
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080094/**
95 * A class that represents how a persistent notification is to be presented to
96 * the user using the {@link android.app.NotificationManager}.
97 *
Joe Onoratocb109a02011-01-18 17:57:41 -080098 * <p>The {@link Notification.Builder Notification.Builder} has been added to make it
99 * easier to construct Notifications.</p>
100 *
Joe Fernandez558459f2011-10-13 16:47:36 -0700101 * <div class="special reference">
102 * <h3>Developer Guides</h3>
103 * <p>For a guide to creating notifications, read the
104 * <a href="{@docRoot}guide/topics/ui/notifiers/notifications.html">Status Bar Notifications</a>
105 * developer guide.</p>
106 * </div>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800107 */
108public class Notification implements Parcelable
109{
Daniel Sandlerdcbaf662013-04-26 16:23:09 -0400110 private static final String TAG = "Notification";
111
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800112 /**
Daniel Sandler01df1c62014-06-09 10:54:01 -0400113 * An activity that provides a user interface for adjusting notification preferences for its
Julia Reynolds3aedded2017-03-31 14:42:09 -0400114 * containing application.
Daniel Sandler01df1c62014-06-09 10:54:01 -0400115 */
116 @SdkConstant(SdkConstantType.INTENT_CATEGORY)
117 public static final String INTENT_CATEGORY_NOTIFICATION_PREFERENCES
118 = "android.intent.category.NOTIFICATION_PREFERENCES";
119
120 /**
Julia Reynolds2619b5e2017-02-09 09:58:15 -0500121 * Optional extra for {@link #INTENT_CATEGORY_NOTIFICATION_PREFERENCES}. If provided, will
122 * contain a {@link NotificationChannel#getId() channel id} that can be used to narrow down
Julia Reynolds3aedded2017-03-31 14:42:09 -0400123 * what settings should be shown in the target app.
Julia Reynolds2619b5e2017-02-09 09:58:15 -0500124 */
125 public static final String EXTRA_CHANNEL_ID = "android.intent.extra.CHANNEL_ID";
126
127 /**
Julia Reynolds3aedded2017-03-31 14:42:09 -0400128 * Optional extra for {@link #INTENT_CATEGORY_NOTIFICATION_PREFERENCES}. If provided, will
Julia Reynolds005c8b92017-08-24 10:35:53 -0400129 * contain a {@link NotificationChannelGroup#getId() group id} that can be used to narrow down
130 * what settings should be shown in the target app.
131 */
132 public static final String EXTRA_CHANNEL_GROUP_ID = "android.intent.extra.CHANNEL_GROUP_ID";
133
134 /**
135 * Optional extra for {@link #INTENT_CATEGORY_NOTIFICATION_PREFERENCES}. If provided, will
Julia Reynolds3aedded2017-03-31 14:42:09 -0400136 * contain the tag provided to {@link NotificationManager#notify(String, int, Notification)}
137 * that can be used to narrow down what settings should be shown in the target app.
138 */
139 public static final String EXTRA_NOTIFICATION_TAG = "android.intent.extra.NOTIFICATION_TAG";
140
141 /**
142 * Optional extra for {@link #INTENT_CATEGORY_NOTIFICATION_PREFERENCES}. If provided, will
143 * contain the id provided to {@link NotificationManager#notify(String, int, Notification)}
144 * that can be used to narrow down what settings should be shown in the target app.
145 */
146 public static final String EXTRA_NOTIFICATION_ID = "android.intent.extra.NOTIFICATION_ID";
147
148 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800149 * Use all default values (where applicable).
150 */
151 public static final int DEFAULT_ALL = ~0;
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500152
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800153 /**
154 * Use the default notification sound. This will ignore any given
155 * {@link #sound}.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500156 *
Chris Wren47c20a12014-06-18 17:27:29 -0400157 * <p>
158 * A notification that is noisy is more likely to be presented as a heads-up notification.
159 * </p>
160 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800161 * @see #defaults
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500162 */
163
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800164 public static final int DEFAULT_SOUND = 1;
165
166 /**
167 * Use the default notification vibrate. This will ignore any given
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500168 * {@link #vibrate}. Using phone vibration requires the
Scott Mainb8b36452009-04-26 15:50:49 -0700169 * {@link android.Manifest.permission#VIBRATE VIBRATE} permission.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500170 *
Chris Wren47c20a12014-06-18 17:27:29 -0400171 * <p>
172 * A notification that vibrates is more likely to be presented as a heads-up notification.
173 * </p>
174 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800175 * @see #defaults
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500176 */
177
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800178 public static final int DEFAULT_VIBRATE = 2;
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500179
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800180 /**
181 * Use the default notification lights. This will ignore the
182 * {@link #FLAG_SHOW_LIGHTS} bit, and {@link #ledARGB}, {@link #ledOffMS}, or
183 * {@link #ledOnMS}.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500184 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800185 * @see #defaults
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500186 */
187
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800188 public static final int DEFAULT_LIGHTS = 4;
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500189
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800190 /**
Christoph Studer535ec612014-09-03 15:47:47 +0200191 * Maximum length of CharSequences accepted by Builder and friends.
192 *
193 * <p>
194 * Avoids spamming the system with overly large strings such as full e-mails.
195 */
196 private static final int MAX_CHARSEQUENCE_LENGTH = 5 * 1024;
197
198 /**
Adrian Roose458aa82015-12-08 16:17:19 -0800199 * Maximum entries of reply text that are accepted by Builder and friends.
200 */
201 private static final int MAX_REPLY_HISTORY = 5;
202
Selim Cinekde4de0e2018-01-24 16:21:07 -0800203
204 /**
205 * If the notification contained an unsent draft for a RemoteInput when the user clicked on it,
206 * we're adding the draft as a String extra to the {@link #contentIntent} using this key.
207 *
208 * <p>Apps may use this extra to prepopulate text fields in the app, where the user usually
209 * sends messages.</p>
210 */
211 public static final String EXTRA_REMOTE_INPUT_DRAFT = "android.remoteInputDraft";
212
Adrian Roose458aa82015-12-08 16:17:19 -0800213 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500214 * A timestamp related to this notification, in milliseconds since the epoch.
Joe Malin8d40d042012-11-05 11:36:40 -0800215 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500216 * Default value: {@link System#currentTimeMillis() Now}.
217 *
218 * Choose a timestamp that will be most relevant to the user. For most finite events, this
219 * corresponds to the time the event happened (or will happen, in the case of events that have
220 * yet to occur but about which the user is being informed). Indefinite events should be
Joe Malin8d40d042012-11-05 11:36:40 -0800221 * timestamped according to when the activity began.
222 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500223 * Some examples:
Joe Malin8d40d042012-11-05 11:36:40 -0800224 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500225 * <ul>
226 * <li>Notification of a new chat message should be stamped when the message was received.</li>
227 * <li>Notification of an ongoing file download (with a progress bar, for example) should be stamped when the download started.</li>
228 * <li>Notification of a completed file download should be stamped when the download finished.</li>
229 * <li>Notification of an upcoming meeting should be stamped with the time the meeting will begin (that is, in the future).</li>
230 * <li>Notification of an ongoing stopwatch (increasing timer) should be stamped with the watch's start time.
231 * <li>Notification of an ongoing countdown timer should be stamped with the timer's end time.
Joe Malin8d40d042012-11-05 11:36:40 -0800232 * </ul>
233 *
Selim Cinek0ff1ce602016-04-05 18:27:16 -0700234 * For apps targeting {@link android.os.Build.VERSION_CODES#N} and above, this time is not shown
235 * anymore by default and must be opted into by using
236 * {@link android.app.Notification.Builder#setShowWhen(boolean)}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800237 */
238 public long when;
239
240 /**
Selim Cinekb85f36fd2016-04-20 18:46:36 -0700241 * The creation time of the notification
242 */
243 private long creationTime;
244
245 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800246 * The resource id of a drawable to use as the icon in the status bar.
Dan Sandler86647982015-05-13 23:41:13 -0400247 *
248 * @deprecated Use {@link Builder#setSmallIcon(Icon)} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800249 */
Dan Sandler86647982015-05-13 23:41:13 -0400250 @Deprecated
Tor Norbye7b9c9122013-05-30 16:48:33 -0700251 @DrawableRes
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800252 public int icon;
253
254 /**
Joe Onorato46439ce2010-11-19 13:56:21 -0800255 * If the icon in the status bar is to have more than one level, you can set this. Otherwise,
256 * leave it at its default value of 0.
257 *
258 * @see android.widget.ImageView#setImageLevel
Griff Hazen959591e2014-05-15 22:26:18 -0700259 * @see android.graphics.drawable.Drawable#setLevel
Joe Onorato46439ce2010-11-19 13:56:21 -0800260 */
261 public int iconLevel;
262
263 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500264 * The number of events that this notification represents. For example, in a new mail
265 * notification, this could be the number of unread messages.
Joe Malin8d40d042012-11-05 11:36:40 -0800266 *
Julia Reynolds30331982017-04-27 10:12:50 -0400267 * The system may or may not use this field to modify the appearance of the notification.
Julia Reynolds13d898c2017-02-02 12:22:05 -0500268 * Starting with {@link android.os.Build.VERSION_CODES#O}, the number may be displayed as a
269 * badge icon in Launchers that support badging.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800270 */
Julia Reynolds30331982017-04-27 10:12:50 -0400271 public int number = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800272
273 /**
274 * The intent to execute when the expanded status entry is clicked. If
275 * this is an activity, it must include the
276 * {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} flag, which requires
Scott Main7aee61f2011-02-08 11:25:01 -0800277 * that you take care of task management as described in the
278 * <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back
Dianne Hackborn6ceca582012-01-10 15:24:26 -0800279 * Stack</a> document. In particular, make sure to read the notification section
280 * <a href="{@docRoot}guide/topics/ui/notifiers/notifications.html#HandlingNotifications">Handling
281 * Notifications</a> for the correct ways to launch an application from a
282 * notification.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800283 */
284 public PendingIntent contentIntent;
285
286 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500287 * The intent to execute when the notification is explicitly dismissed by the user, either with
288 * the "Clear All" button or by swiping it away individually.
289 *
290 * This probably shouldn't be launching an activity since several of those will be sent
291 * at the same time.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800292 */
293 public PendingIntent deleteIntent;
294
295 /**
Dianne Hackborn170bae72010-09-03 15:14:28 -0700296 * An intent to launch instead of posting the notification to the status bar.
Joe Onoratocb109a02011-01-18 17:57:41 -0800297 *
Chris Wren47c20a12014-06-18 17:27:29 -0400298 * <p>
299 * The system UI may choose to display a heads-up notification, instead of
300 * launching this intent, while the user is using the device.
301 * </p>
302 *
Joe Onoratocb109a02011-01-18 17:57:41 -0800303 * @see Notification.Builder#setFullScreenIntent
Daniel Sandlere46cbd32010-06-17 10:35:26 -0400304 */
305 public PendingIntent fullScreenIntent;
306
307 /**
Dan Sandler5fcdf6e2014-07-18 11:31:15 -0400308 * Text that summarizes this notification for accessibility services.
309 *
310 * As of the L release, this text is no longer shown on screen, but it is still useful to
311 * accessibility services (where it serves as an audible announcement of the notification's
312 * appearance).
Joe Onoratoef1e7762010-09-17 18:38:38 -0400313 *
Joe Onorato46439ce2010-11-19 13:56:21 -0800314 * @see #tickerView
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800315 */
316 public CharSequence tickerText;
317
318 /**
Dan Sandler5fcdf6e2014-07-18 11:31:15 -0400319 * Formerly, a view showing the {@link #tickerText}.
320 *
321 * No longer displayed in the status bar as of API 21.
Joe Onoratoef1e7762010-09-17 18:38:38 -0400322 */
Dan Sandler5fcdf6e2014-07-18 11:31:15 -0400323 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -0800324 public RemoteViews tickerView;
Joe Onoratoef1e7762010-09-17 18:38:38 -0400325
326 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400327 * The view that will represent this notification in the notification list (which is pulled
328 * down from the status bar).
329 *
Julia Reynoldsd4ea7412016-02-17 14:00:56 -0500330 * As of N, this field may be null. The notification view is determined by the inputs
331 * to {@link Notification.Builder}; a custom RemoteViews can optionally be
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400332 * supplied with {@link Notification.Builder#setCustomContentView(RemoteViews)}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800333 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400334 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800335 public RemoteViews contentView;
336
Daniel Sandlera0a938c2012-03-15 08:42:37 -0400337 /**
Daniel Sandler4dfbe832012-04-11 14:51:46 -0400338 * A large-format version of {@link #contentView}, giving the Notification an
Daniel Sandlerf3b73432012-03-27 15:01:25 -0400339 * opportunity to show more detail. The system UI may choose to show this
340 * instead of the normal content view at its discretion.
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400341 *
Julia Reynoldsd4ea7412016-02-17 14:00:56 -0500342 * As of N, this field may be null. The expanded notification view is determined by the
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400343 * inputs to {@link Notification.Builder}; a custom RemoteViews can optionally be
344 * supplied with {@link Notification.Builder#setCustomBigContentView(RemoteViews)}.
Daniel Sandlerf3b73432012-03-27 15:01:25 -0400345 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400346 @Deprecated
Daniel Sandlerf3b73432012-03-27 15:01:25 -0400347 public RemoteViews bigContentView;
348
Chris Wren8fd39ec2014-02-27 17:43:26 -0500349
350 /**
Chris Wren47c20a12014-06-18 17:27:29 -0400351 * A medium-format version of {@link #contentView}, providing the Notification an
352 * opportunity to add action buttons to contentView. At its discretion, the system UI may
353 * choose to show this as a heads-up notification, which will pop up so the user can see
354 * it without leaving their current activity.
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400355 *
Julia Reynoldsd4ea7412016-02-17 14:00:56 -0500356 * As of N, this field may be null. The heads-up notification view is determined by the
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400357 * inputs to {@link Notification.Builder}; a custom RemoteViews can optionally be
358 * supplied with {@link Notification.Builder#setCustomHeadsUpContentView(RemoteViews)}.
Chris Wren8fd39ec2014-02-27 17:43:26 -0500359 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400360 @Deprecated
Chris Wren8fd39ec2014-02-27 17:43:26 -0500361 public RemoteViews headsUpContentView;
362
Julia Reynoldsfc640012018-02-21 12:25:27 -0500363 private boolean mUsesStandardHeader;
364
365 private static final ArraySet<Integer> STANDARD_LAYOUTS = new ArraySet<>();
366 static {
367 STANDARD_LAYOUTS.add(R.layout.notification_template_material_base);
368 STANDARD_LAYOUTS.add(R.layout.notification_template_material_big_base);
369 STANDARD_LAYOUTS.add(R.layout.notification_template_material_big_picture);
370 STANDARD_LAYOUTS.add(R.layout.notification_template_material_big_text);
371 STANDARD_LAYOUTS.add(R.layout.notification_template_material_inbox);
372 STANDARD_LAYOUTS.add(R.layout.notification_template_material_messaging);
373 STANDARD_LAYOUTS.add(R.layout.notification_template_material_media);
374 STANDARD_LAYOUTS.add(R.layout.notification_template_material_big_media);
375 STANDARD_LAYOUTS.add(R.layout.notification_template_ambient_header);
376 STANDARD_LAYOUTS.add(R.layout.notification_template_header);
377 STANDARD_LAYOUTS.add(R.layout.notification_template_material_ambient);
378 }
379
Daniel Sandlerf3b73432012-03-27 15:01:25 -0400380 /**
Dan Sandler86647982015-05-13 23:41:13 -0400381 * A large bitmap to be shown in the notification content area.
382 *
383 * @deprecated Use {@link Builder#setLargeIcon(Icon)} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800384 */
Dan Sandler86647982015-05-13 23:41:13 -0400385 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -0800386 public Bitmap largeIcon;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800387
388 /**
389 * The sound to play.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500390 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800391 * <p>
Chris Wren47c20a12014-06-18 17:27:29 -0400392 * A notification that is noisy is more likely to be presented as a heads-up notification.
393 * </p>
394 *
395 * <p>
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500396 * To play the default notification sound, see {@link #defaults}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800397 * </p>
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500398 * @deprecated use {@link NotificationChannel#getSound()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800399 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500400 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800401 public Uri sound;
402
403 /**
404 * Use this constant as the value for audioStreamType to request that
405 * the default stream type for notifications be used. Currently the
Jeff Sharkey098d5802012-04-26 17:30:34 -0700406 * default stream type is {@link AudioManager#STREAM_NOTIFICATION}.
John Spurlockc0650f022014-07-19 13:22:39 -0400407 *
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500408 * @deprecated Use {@link NotificationChannel#getAudioAttributes()} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800409 */
Jean-Michel Trivi81f871e2014-08-06 16:32:38 -0700410 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800411 public static final int STREAM_DEFAULT = -1;
412
413 /**
414 * The audio stream type to use when playing the sound.
415 * Should be one of the STREAM_ constants from
416 * {@link android.media.AudioManager}.
John Spurlockc0650f022014-07-19 13:22:39 -0400417 *
418 * @deprecated Use {@link #audioAttributes} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800419 */
Jean-Michel Trivi81f871e2014-08-06 16:32:38 -0700420 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800421 public int audioStreamType = STREAM_DEFAULT;
422
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800423 /**
John Spurlockc0650f022014-07-19 13:22:39 -0400424 * The default value of {@link #audioAttributes}.
425 */
426 public static final AudioAttributes AUDIO_ATTRIBUTES_DEFAULT = new AudioAttributes.Builder()
427 .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
428 .setUsage(AudioAttributes.USAGE_NOTIFICATION)
429 .build();
430
431 /**
432 * The {@link AudioAttributes audio attributes} to use when playing the sound.
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500433 *
434 * @deprecated use {@link NotificationChannel#getAudioAttributes()} instead.
John Spurlockc0650f022014-07-19 13:22:39 -0400435 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500436 @Deprecated
John Spurlockc0650f022014-07-19 13:22:39 -0400437 public AudioAttributes audioAttributes = AUDIO_ATTRIBUTES_DEFAULT;
438
439 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500440 * The pattern with which to vibrate.
441 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800442 * <p>
443 * To vibrate the default pattern, see {@link #defaults}.
444 * </p>
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500445 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800446 * @see android.os.Vibrator#vibrate(long[],int)
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500447 * @deprecated use {@link NotificationChannel#getVibrationPattern()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800448 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500449 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800450 public long[] vibrate;
451
452 /**
453 * The color of the led. The hardware will do its best approximation.
454 *
455 * @see #FLAG_SHOW_LIGHTS
456 * @see #flags
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500457 * @deprecated use {@link NotificationChannel#shouldShowLights()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800458 */
Tor Norbye80756e32015-03-02 09:39:27 -0800459 @ColorInt
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500460 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800461 public int ledARGB;
462
463 /**
464 * The number of milliseconds for the LED to be on while it's flashing.
465 * The hardware will do its best approximation.
466 *
467 * @see #FLAG_SHOW_LIGHTS
468 * @see #flags
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500469 * @deprecated use {@link NotificationChannel#shouldShowLights()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800470 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500471 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800472 public int ledOnMS;
473
474 /**
475 * The number of milliseconds for the LED to be off while it's flashing.
476 * The hardware will do its best approximation.
477 *
478 * @see #FLAG_SHOW_LIGHTS
479 * @see #flags
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500480 *
481 * @deprecated use {@link NotificationChannel#shouldShowLights()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800482 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500483 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800484 public int ledOffMS;
485
486 /**
487 * Specifies which values should be taken from the defaults.
488 * <p>
489 * To set, OR the desired from {@link #DEFAULT_SOUND},
490 * {@link #DEFAULT_VIBRATE}, {@link #DEFAULT_LIGHTS}. For all default
491 * values, use {@link #DEFAULT_ALL}.
492 * </p>
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500493 *
494 * @deprecated use {@link NotificationChannel#getSound()} and
495 * {@link NotificationChannel#shouldShowLights()} and
496 * {@link NotificationChannel#shouldVibrate()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800497 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500498 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800499 public int defaults;
500
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800501 /**
502 * Bit to be bitwise-ored into the {@link #flags} field that should be
503 * set if you want the LED on for this notification.
504 * <ul>
505 * <li>To turn the LED off, pass 0 in the alpha channel for colorARGB
506 * or 0 for both ledOnMS and ledOffMS.</li>
507 * <li>To turn the LED on, pass 1 for ledOnMS and 0 for ledOffMS.</li>
508 * <li>To flash the LED, pass the number of milliseconds that it should
509 * be on and off to ledOnMS and ledOffMS.</li>
510 * </ul>
511 * <p>
512 * Since hardware varies, you are not guaranteed that any of the values
Ricardo Loo Forondaf8f6d0a2018-01-25 08:42:43 -0800513 * you pass are honored exactly. Use the system defaults if possible
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800514 * because they will be set to values that work on any given hardware.
515 * <p>
516 * The alpha channel must be set for forward compatibility.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500517 *
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500518 * @deprecated use {@link NotificationChannel#shouldShowLights()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800519 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500520 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800521 public static final int FLAG_SHOW_LIGHTS = 0x00000001;
522
523 /**
524 * Bit to be bitwise-ored into the {@link #flags} field that should be
525 * set if this notification is in reference to something that is ongoing,
526 * like a phone call. It should not be set if this notification is in
527 * reference to something that happened at a particular point in time,
528 * like a missed phone call.
529 */
530 public static final int FLAG_ONGOING_EVENT = 0x00000002;
531
532 /**
533 * Bit to be bitwise-ored into the {@link #flags} field that if set,
Scott Mainb8b36452009-04-26 15:50:49 -0700534 * the audio will be repeated until the notification is
535 * cancelled or the notification window is opened.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800536 */
537 public static final int FLAG_INSISTENT = 0x00000004;
538
539 /**
540 * Bit to be bitwise-ored into the {@link #flags} field that should be
Griff Hazen293977b2014-04-28 08:37:20 -0700541 * set if you would only like the sound, vibrate and ticker to be played
542 * if the notification was not already showing.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800543 */
544 public static final int FLAG_ONLY_ALERT_ONCE = 0x00000008;
545
546 /**
547 * Bit to be bitwise-ored into the {@link #flags} field that should be
548 * set if the notification should be canceled when it is clicked by the
Daniel Sandler8aa9ae62012-12-04 23:31:47 -0500549 * user.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800550 */
551 public static final int FLAG_AUTO_CANCEL = 0x00000010;
552
553 /**
554 * Bit to be bitwise-ored into the {@link #flags} field that should be
555 * set if the notification should not be canceled when the user clicks
556 * the Clear all button.
557 */
558 public static final int FLAG_NO_CLEAR = 0x00000020;
559
Dianne Hackbornd8a43f62009-08-17 23:33:56 -0700560 /**
561 * Bit to be bitwise-ored into the {@link #flags} field that should be
562 * set if this notification represents a currently running service. This
563 * will normally be set for you by {@link Service#startForeground}.
564 */
565 public static final int FLAG_FOREGROUND_SERVICE = 0x00000040;
566
Daniel Sandlere46cbd32010-06-17 10:35:26 -0400567 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500568 * Obsolete flag indicating high-priority notifications; use the priority field instead.
Joe Malin8d40d042012-11-05 11:36:40 -0800569 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500570 * @deprecated Use {@link #priority} with a positive value.
Daniel Sandlere46cbd32010-06-17 10:35:26 -0400571 */
Aurimas Liutikas514c5ef2016-05-24 15:22:55 -0700572 @Deprecated
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500573 public static final int FLAG_HIGH_PRIORITY = 0x00000080;
Daniel Sandlere46cbd32010-06-17 10:35:26 -0400574
Griff Hazendfcb0802014-02-11 12:00:00 -0800575 /**
576 * Bit to be bitswise-ored into the {@link #flags} field that should be
577 * set if this notification is relevant to the current device only
578 * and it is not recommended that it bridge to other devices.
579 */
580 public static final int FLAG_LOCAL_ONLY = 0x00000100;
581
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700582 /**
583 * Bit to be bitswise-ored into the {@link #flags} field that should be
584 * set if this notification is the group summary for a group of notifications.
585 * Grouped notifications may display in a cluster or stack on devices which
586 * support such rendering. Requires a group key also be set using {@link Builder#setGroup}.
587 */
588 public static final int FLAG_GROUP_SUMMARY = 0x00000200;
589
Julia Reynoldse46bb372016-03-17 11:05:58 -0400590 /**
591 * Bit to be bitswise-ored into the {@link #flags} field that should be
592 * set if this notification is the group summary for an auto-group of notifications.
593 *
594 * @hide
595 */
596 @SystemApi
597 public static final int FLAG_AUTOGROUP_SUMMARY = 0x00000400;
598
Julia Reynolds4db59552017-06-30 13:34:01 -0400599 /**
600 * @hide
601 */
602 public static final int FLAG_CAN_COLORIZE = 0x00000800;
603
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800604 public int flags;
605
Tor Norbyed9273d62013-05-30 15:59:53 -0700606 /** @hide */
Jeff Sharkeyce8db992017-12-13 20:05:05 -0700607 @IntDef(prefix = { "PRIORITY_" }, value = {
608 PRIORITY_DEFAULT,
609 PRIORITY_LOW,
610 PRIORITY_MIN,
611 PRIORITY_HIGH,
612 PRIORITY_MAX
613 })
Tor Norbyed9273d62013-05-30 15:59:53 -0700614 @Retention(RetentionPolicy.SOURCE)
615 public @interface Priority {}
616
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800617 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500618 * Default notification {@link #priority}. If your application does not prioritize its own
619 * notifications, use this value for all notifications.
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500620 *
621 * @deprecated use {@link NotificationManager#IMPORTANCE_DEFAULT} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500622 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500623 @Deprecated
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500624 public static final int PRIORITY_DEFAULT = 0;
625
626 /**
627 * Lower {@link #priority}, for items that are less important. The UI may choose to show these
628 * items smaller, or at a different position in the list, compared with your app's
629 * {@link #PRIORITY_DEFAULT} items.
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500630 *
631 * @deprecated use {@link NotificationManager#IMPORTANCE_LOW} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500632 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500633 @Deprecated
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500634 public static final int PRIORITY_LOW = -1;
635
636 /**
637 * Lowest {@link #priority}; these items might not be shown to the user except under special
638 * circumstances, such as detailed notification logs.
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500639 *
640 * @deprecated use {@link NotificationManager#IMPORTANCE_MIN} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500641 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500642 @Deprecated
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500643 public static final int PRIORITY_MIN = -2;
644
645 /**
646 * Higher {@link #priority}, for more important notifications or alerts. The UI may choose to
647 * show these items larger, or at a different position in notification lists, compared with
648 * your app's {@link #PRIORITY_DEFAULT} items.
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500649 *
650 * @deprecated use {@link NotificationManager#IMPORTANCE_HIGH} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500651 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500652 @Deprecated
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500653 public static final int PRIORITY_HIGH = 1;
654
655 /**
656 * Highest {@link #priority}, for your application's most important items that require the
657 * user's prompt attention or input.
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500658 *
659 * @deprecated use {@link NotificationManager#IMPORTANCE_HIGH} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500660 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500661 @Deprecated
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500662 public static final int PRIORITY_MAX = 2;
663
664 /**
665 * Relative priority for this notification.
Joe Malin8d40d042012-11-05 11:36:40 -0800666 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500667 * Priority is an indication of how much of the user's valuable attention should be consumed by
668 * this notification. Low-priority notifications may be hidden from the user in certain
669 * situations, while the user might be interrupted for a higher-priority notification. The
Daniel Sandler6738eee2012-11-16 12:03:32 -0500670 * system will make a determination about how to interpret this priority when presenting
671 * the notification.
Chris Wren47c20a12014-06-18 17:27:29 -0400672 *
673 * <p>
674 * A notification that is at least {@link #PRIORITY_HIGH} is more likely to be presented
675 * as a heads-up notification.
676 * </p>
677 *
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500678 * @deprecated use {@link NotificationChannel#getImportance()} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500679 */
Tor Norbyed9273d62013-05-30 15:59:53 -0700680 @Priority
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500681 @Deprecated
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500682 public int priority;
Joe Malin8d40d042012-11-05 11:36:40 -0800683
Dan Sandler26e81cf2014-05-06 10:01:27 -0400684 /**
685 * Accent color (an ARGB integer like the constants in {@link android.graphics.Color})
686 * to be applied by the standard Style templates when presenting this notification.
687 *
688 * The current template design constructs a colorful header image by overlaying the
689 * {@link #icon} image (stenciled in white) atop a field of this color. Alpha components are
690 * ignored.
691 */
Tor Norbye80756e32015-03-02 09:39:27 -0800692 @ColorInt
Dan Sandler26e81cf2014-05-06 10:01:27 -0400693 public int color = COLOR_DEFAULT;
694
695 /**
696 * Special value of {@link #color} telling the system not to decorate this notification with
697 * any special color but instead use default colors when presenting this notification.
698 */
Tor Norbye80756e32015-03-02 09:39:27 -0800699 @ColorInt
Dan Sandler26e81cf2014-05-06 10:01:27 -0400700 public static final int COLOR_DEFAULT = 0; // AKA Color.TRANSPARENT
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600701
702 /**
Adrian Roos4ff3b122016-02-01 12:26:13 -0800703 * Special value of {@link #color} used as a place holder for an invalid color.
Selim Cinek99104832017-01-25 14:47:33 -0800704 * @hide
Adrian Roos4ff3b122016-02-01 12:26:13 -0800705 */
706 @ColorInt
Selim Cinek99104832017-01-25 14:47:33 -0800707 public static final int COLOR_INVALID = 1;
Adrian Roos4ff3b122016-02-01 12:26:13 -0800708
709 /**
Adrian Roosc1a80b02016-04-05 14:54:55 -0700710 * Sphere of visibility of this notification, which affects how and when the SystemUI reveals
711 * the notification's presence and contents in untrusted situations (namely, on the secure
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600712 * lockscreen).
713 *
714 * The default level, {@link #VISIBILITY_PRIVATE}, behaves exactly as notifications have always
715 * done on Android: The notification's {@link #icon} and {@link #tickerText} (if available) are
716 * shown in all situations, but the contents are only available if the device is unlocked for
717 * the appropriate user.
718 *
719 * A more permissive policy can be expressed by {@link #VISIBILITY_PUBLIC}; such a notification
720 * can be read even in an "insecure" context (that is, above a secure lockscreen).
721 * To modify the public version of this notification—for example, to redact some portions—see
722 * {@link Builder#setPublicVersion(Notification)}.
723 *
724 * Finally, a notification can be made {@link #VISIBILITY_SECRET}, which will suppress its icon
725 * and ticker until the user has bypassed the lockscreen.
726 */
Jeff Sharkey30e06bb2017-04-24 11:18:03 -0600727 public @Visibility int visibility;
728
729 /** @hide */
730 @IntDef(prefix = { "VISIBILITY_" }, value = {
731 VISIBILITY_PUBLIC,
732 VISIBILITY_PRIVATE,
733 VISIBILITY_SECRET,
734 })
735 @Retention(RetentionPolicy.SOURCE)
736 public @interface Visibility {}
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600737
Griff Hazenfc3922d2014-08-20 11:56:44 -0700738 /**
739 * Notification visibility: Show this notification in its entirety on all lockscreens.
740 *
741 * {@see #visibility}
742 */
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600743 public static final int VISIBILITY_PUBLIC = 1;
Griff Hazenfc3922d2014-08-20 11:56:44 -0700744
745 /**
746 * Notification visibility: Show this notification on all lockscreens, but conceal sensitive or
747 * private information on secure lockscreens.
748 *
749 * {@see #visibility}
750 */
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600751 public static final int VISIBILITY_PRIVATE = 0;
Griff Hazenfc3922d2014-08-20 11:56:44 -0700752
753 /**
754 * Notification visibility: Do not reveal any part of this notification on a secure lockscreen.
755 *
756 * {@see #visibility}
757 */
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600758 public static final int VISIBILITY_SECRET = -1;
759
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500760 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400761 * Notification category: incoming call (voice or video) or similar synchronous communication request.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500762 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400763 public static final String CATEGORY_CALL = "call";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500764
765 /**
Adora Zhangaa90e872018-03-12 14:07:50 -0700766 * Notification category: map turn-by-turn navigation.
767 */
768 public static final String CATEGORY_NAVIGATION = "navigation";
769
770 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400771 * Notification category: incoming direct message (SMS, instant message, etc.).
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500772 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400773 public static final String CATEGORY_MESSAGE = "msg";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500774
775 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400776 * Notification category: asynchronous bulk message (email).
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500777 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400778 public static final String CATEGORY_EMAIL = "email";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500779
780 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400781 * Notification category: calendar event.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500782 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400783 public static final String CATEGORY_EVENT = "event";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500784
785 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400786 * Notification category: promotion or advertisement.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500787 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400788 public static final String CATEGORY_PROMO = "promo";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500789
790 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400791 * Notification category: alarm or timer.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500792 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400793 public static final String CATEGORY_ALARM = "alarm";
794
795 /**
796 * Notification category: progress of a long-running background operation.
797 */
798 public static final String CATEGORY_PROGRESS = "progress";
799
800 /**
801 * Notification category: social network or sharing update.
802 */
803 public static final String CATEGORY_SOCIAL = "social";
804
805 /**
806 * Notification category: error in background operation or authentication status.
807 */
808 public static final String CATEGORY_ERROR = "err";
809
810 /**
811 * Notification category: media transport control for playback.
812 */
813 public static final String CATEGORY_TRANSPORT = "transport";
814
815 /**
816 * Notification category: system or device status update. Reserved for system use.
817 */
818 public static final String CATEGORY_SYSTEM = "sys";
819
820 /**
821 * Notification category: indication of running background service.
822 */
823 public static final String CATEGORY_SERVICE = "service";
824
825 /**
John Spurlock0a69c8c2014-03-21 13:30:57 -0400826 * Notification category: a specific, timely recommendation for a single thing.
827 * For example, a news app might want to recommend a news story it believes the user will
828 * want to read next.
829 */
830 public static final String CATEGORY_RECOMMENDATION = "recommendation";
831
832 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400833 * Notification category: ongoing information about device or contextual status.
834 */
835 public static final String CATEGORY_STATUS = "status";
836
837 /**
John Spurlock24d3dad2015-04-02 12:24:02 -0400838 * Notification category: user-scheduled reminder.
839 */
840 public static final String CATEGORY_REMINDER = "reminder";
841
842 /**
Adora Zhangaa90e872018-03-12 14:07:50 -0700843 * Notification category: extreme car emergencies.
844 * @hide
845 */
846 @SystemApi
847 public static final String CATEGORY_CAR_EMERGENCY = "car_emergency";
848
849 /**
850 * Notification category: car warnings.
851 * @hide
852 */
853 @SystemApi
854 public static final String CATEGORY_CAR_WARNING = "car_warning";
855
856 /**
857 * Notification category: general car system information.
858 * @hide
859 */
860 @SystemApi
861 public static final String CATEGORY_CAR_INFORMATION = "car_information";
862
863 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400864 * One of the predefined notification categories (see the <code>CATEGORY_*</code> constants)
865 * that best describes this Notification. May be used by the system for ranking and filtering.
866 */
867 public String category;
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500868
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700869 private String mGroupKey;
870
871 /**
872 * Get the key used to group this notification into a cluster or stack
873 * with other notifications on devices which support such rendering.
874 */
875 public String getGroup() {
876 return mGroupKey;
877 }
878
879 private String mSortKey;
880
881 /**
882 * Get a sort key that orders this notification among other notifications from the
883 * same package. This can be useful if an external sort was already applied and an app
884 * would like to preserve this. Notifications will be sorted lexicographically using this
885 * value, although providing different priorities in addition to providing sort key may
886 * cause this value to be ignored.
887 *
888 * <p>This sort key can also be used to order members of a notification group. See
889 * {@link Builder#setGroup}.
890 *
891 * @see String#compareTo(String)
892 */
893 public String getSortKey() {
894 return mSortKey;
895 }
896
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500897 /**
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400898 * Additional semantic data to be carried around with this Notification.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400899 * <p>
900 * The extras keys defined here are intended to capture the original inputs to {@link Builder}
901 * APIs, and are intended to be used by
902 * {@link android.service.notification.NotificationListenerService} implementations to extract
903 * detailed information from notification objects.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500904 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400905 public Bundle extras = new Bundle();
906
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400907 /**
Felipe Lemedd85da62016-06-28 11:29:54 -0700908 * All pending intents in the notification as the system needs to be able to access them but
909 * touching the extras bundle in the system process is not safe because the bundle may contain
Svet Ganovddb94882016-06-23 19:55:24 -0700910 * custom parcelable objects.
911 *
912 * @hide
913 */
Felipe Lemedd85da62016-06-28 11:29:54 -0700914 public ArraySet<PendingIntent> allPendingIntents;
Svet Ganovddb94882016-06-23 19:55:24 -0700915
916 /**
Dianne Hackborn98305522017-05-05 17:53:53 -0700917 * Token identifying the notification that is applying doze/bgcheck whitelisting to the
918 * pending intents inside of it, so only those will get the behavior.
919 *
920 * @hide
921 */
Adrian Roosfb921842017-10-26 14:49:56 +0200922 private IBinder mWhitelistToken;
Dianne Hackborn98305522017-05-05 17:53:53 -0700923
924 /**
925 * Must be set by a process to start associating tokens with Notification objects
926 * coming in to it. This is set by NotificationManagerService.
927 *
928 * @hide
929 */
930 static public IBinder processWhitelistToken;
931
932 /**
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400933 * {@link #extras} key: this is the title of the notification,
934 * as supplied to {@link Builder#setContentTitle(CharSequence)}.
935 */
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -0500936 public static final String EXTRA_TITLE = "android.title";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400937
938 /**
939 * {@link #extras} key: this is the title of the notification when shown in expanded form,
940 * e.g. as supplied to {@link BigTextStyle#setBigContentTitle(CharSequence)}.
941 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400942 public static final String EXTRA_TITLE_BIG = EXTRA_TITLE + ".big";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400943
944 /**
945 * {@link #extras} key: this is the main text payload, as supplied to
946 * {@link Builder#setContentText(CharSequence)}.
947 */
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -0500948 public static final String EXTRA_TEXT = "android.text";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400949
950 /**
951 * {@link #extras} key: this is a third line of text, as supplied to
952 * {@link Builder#setSubText(CharSequence)}.
953 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400954 public static final String EXTRA_SUB_TEXT = "android.subText";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400955
956 /**
Adrian Roose458aa82015-12-08 16:17:19 -0800957 * {@link #extras} key: this is the remote input history, as supplied to
958 * {@link Builder#setRemoteInputHistory(CharSequence[])}.
Adrian Roos005e7742016-04-13 15:02:20 -0700959 *
960 * Apps can fill this through {@link Builder#setRemoteInputHistory(CharSequence[])}
961 * with the most recent inputs that have been sent through a {@link RemoteInput} of this
962 * Notification and are expected to clear it once the it is no longer relevant (e.g. for chat
963 * notifications once the other party has responded).
964 *
965 * The extra with this key is of type CharSequence[] and contains the most recent entry at
966 * the 0 index, the second most recent at the 1 index, etc.
967 *
968 * @see Builder#setRemoteInputHistory(CharSequence[])
Adrian Roose458aa82015-12-08 16:17:19 -0800969 */
970 public static final String EXTRA_REMOTE_INPUT_HISTORY = "android.remoteInputHistory";
971
972 /**
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400973 * {@link #extras} key: this is a small piece of additional text as supplied to
974 * {@link Builder#setContentInfo(CharSequence)}.
975 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400976 public static final String EXTRA_INFO_TEXT = "android.infoText";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400977
978 /**
979 * {@link #extras} key: this is a line of summary information intended to be shown
980 * alongside expanded notifications, as supplied to (e.g.)
981 * {@link BigTextStyle#setSummaryText(CharSequence)}.
982 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400983 public static final String EXTRA_SUMMARY_TEXT = "android.summaryText";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400984
985 /**
Christoph Studer4600f9b2014-07-22 22:44:43 +0200986 * {@link #extras} key: this is the longer text shown in the big form of a
987 * {@link BigTextStyle} notification, as supplied to
988 * {@link BigTextStyle#bigText(CharSequence)}.
989 */
990 public static final String EXTRA_BIG_TEXT = "android.bigText";
991
992 /**
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400993 * {@link #extras} key: this is the resource ID of the notification's main small icon, as
994 * supplied to {@link Builder#setSmallIcon(int)}.
Julia Reynoldse071abd2017-03-22 10:52:11 -0400995 *
996 * @deprecated Use {@link #getSmallIcon()}, which supports a wider variety of icon sources.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400997 */
Julia Reynoldse071abd2017-03-22 10:52:11 -0400998 @Deprecated
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -0500999 public static final String EXTRA_SMALL_ICON = "android.icon";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001000
1001 /**
1002 * {@link #extras} key: this is a bitmap to be used instead of the small icon when showing the
1003 * notification payload, as
1004 * supplied to {@link Builder#setLargeIcon(android.graphics.Bitmap)}.
Julia Reynoldse071abd2017-03-22 10:52:11 -04001005 *
1006 * @deprecated Use {@link #getLargeIcon()}, which supports a wider variety of icon sources.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001007 */
Julia Reynoldse071abd2017-03-22 10:52:11 -04001008 @Deprecated
Daniel Sandlerf45564e2013-04-15 15:05:08 -04001009 public static final String EXTRA_LARGE_ICON = "android.largeIcon";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001010
1011 /**
1012 * {@link #extras} key: this is a bitmap to be used instead of the one from
1013 * {@link Builder#setLargeIcon(android.graphics.Bitmap)} when the notification is
1014 * shown in its expanded form, as supplied to
1015 * {@link BigPictureStyle#bigLargeIcon(android.graphics.Bitmap)}.
1016 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -04001017 public static final String EXTRA_LARGE_ICON_BIG = EXTRA_LARGE_ICON + ".big";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001018
1019 /**
1020 * {@link #extras} key: this is the progress value supplied to
1021 * {@link Builder#setProgress(int, int, boolean)}.
1022 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -04001023 public static final String EXTRA_PROGRESS = "android.progress";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001024
1025 /**
1026 * {@link #extras} key: this is the maximum value supplied to
1027 * {@link Builder#setProgress(int, int, boolean)}.
1028 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -04001029 public static final String EXTRA_PROGRESS_MAX = "android.progressMax";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001030
1031 /**
1032 * {@link #extras} key: whether the progress bar is indeterminate, supplied to
1033 * {@link Builder#setProgress(int, int, boolean)}.
1034 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -04001035 public static final String EXTRA_PROGRESS_INDETERMINATE = "android.progressIndeterminate";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001036
1037 /**
1038 * {@link #extras} key: whether {@link #when} should be shown as a count-up timer (specifically
1039 * a {@link android.widget.Chronometer}) instead of a timestamp, as supplied to
1040 * {@link Builder#setUsesChronometer(boolean)}.
1041 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -04001042 public static final String EXTRA_SHOW_CHRONOMETER = "android.showChronometer";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001043
1044 /**
Selim Cinek81c23aa2016-02-25 16:23:13 -08001045 * {@link #extras} key: whether the chronometer set on the notification should count down
1046 * instead of counting up. Is only relevant if key {@link #EXTRA_SHOW_CHRONOMETER} is present.
Adrian Roos96b7e202016-05-17 13:50:38 -07001047 * This extra is a boolean. The default is false.
Selim Cinek81c23aa2016-02-25 16:23:13 -08001048 */
Adrian Roos96b7e202016-05-17 13:50:38 -07001049 public static final String EXTRA_CHRONOMETER_COUNT_DOWN = "android.chronometerCountDown";
Selim Cinek81c23aa2016-02-25 16:23:13 -08001050
1051 /**
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001052 * {@link #extras} key: whether {@link #when} should be shown,
1053 * as supplied to {@link Builder#setShowWhen(boolean)}.
1054 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -04001055 public static final String EXTRA_SHOW_WHEN = "android.showWhen";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001056
1057 /**
1058 * {@link #extras} key: this is a bitmap to be shown in {@link BigPictureStyle} expanded
1059 * notifications, supplied to {@link BigPictureStyle#bigPicture(android.graphics.Bitmap)}.
1060 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -04001061 public static final String EXTRA_PICTURE = "android.picture";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001062
1063 /**
1064 * {@link #extras} key: An array of CharSequences to show in {@link InboxStyle} expanded
1065 * notifications, each of which was supplied to {@link InboxStyle#addLine(CharSequence)}.
1066 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -04001067 public static final String EXTRA_TEXT_LINES = "android.textLines";
Dan Sandler842dd772014-05-15 09:36:47 -04001068
1069 /**
1070 * {@link #extras} key: A string representing the name of the specific
1071 * {@link android.app.Notification.Style} used to create this notification.
1072 */
Chris Wren91ad5632013-06-05 15:05:57 -04001073 public static final String EXTRA_TEMPLATE = "android.template";
Daniel Sandlerf45564e2013-04-15 15:05:08 -04001074
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001075 /**
Chris Wrene6c48932014-09-29 17:19:27 -04001076 * {@link #extras} key: A String array containing the people that this notification relates to,
1077 * each of which was supplied to {@link Builder#addPerson(String)}.
Selim Cineke7238dd2017-12-14 17:48:32 -08001078 *
1079 * @deprecated the actual objects are now in {@link #EXTRA_PEOPLE_LIST}
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001080 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -04001081 public static final String EXTRA_PEOPLE = "android.people";
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001082
1083 /**
Selim Cineke7238dd2017-12-14 17:48:32 -08001084 * {@link #extras} key: An arrayList of {@link Person} objects containing the people that
1085 * this notification relates to.
1086 */
1087 public static final String EXTRA_PEOPLE_LIST = "android.people.list";
1088
1089 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -04001090 * Allow certain system-generated notifications to appear before the device is provisioned.
1091 * Only available to notifications coming from the android package.
1092 * @hide
1093 */
Maurice Lam96c10032017-03-29 15:42:38 -07001094 @SystemApi
Jeff Sharkeyd86b8fe2017-06-02 17:36:26 -06001095 @RequiresPermission(android.Manifest.permission.NOTIFICATION_DURING_SETUP)
John Spurlockfd7f1e02014-03-18 16:41:57 -04001096 public static final String EXTRA_ALLOW_DURING_SETUP = "android.allowDuringSetup";
1097
1098 /**
Jose Limae9e3b3b2014-05-18 23:44:50 -07001099 * {@link #extras} key: A
1100 * {@link android.content.ContentUris content URI} pointing to an image that can be displayed
Julia Reynoldse0d711f2017-09-01 08:50:47 -04001101 * in the background when the notification is selected. Used on television platforms.
1102 * The URI must point to an image stream suitable for passing into
Jose Limae9e3b3b2014-05-18 23:44:50 -07001103 * {@link android.graphics.BitmapFactory#decodeStream(java.io.InputStream)
Julia Reynoldse0d711f2017-09-01 08:50:47 -04001104 * BitmapFactory.decodeStream}; all other content types will be ignored.
Jose Limae9e3b3b2014-05-18 23:44:50 -07001105 */
1106 public static final String EXTRA_BACKGROUND_IMAGE_URI = "android.backgroundImageUri";
1107
1108 /**
Dan Sandler842dd772014-05-15 09:36:47 -04001109 * {@link #extras} key: A
Jeff Browndba34ba2014-06-24 20:46:03 -07001110 * {@link android.media.session.MediaSession.Token} associated with a
Dan Sandler842dd772014-05-15 09:36:47 -04001111 * {@link android.app.Notification.MediaStyle} notification.
1112 */
1113 public static final String EXTRA_MEDIA_SESSION = "android.mediaSession";
1114
1115 /**
Bryan Mawhinneye191f902014-07-22 12:50:09 +01001116 * {@link #extras} key: the indices of actions to be shown in the compact view,
1117 * as supplied to (e.g.) {@link MediaStyle#setShowActionsInCompactView(int...)}.
1118 */
1119 public static final String EXTRA_COMPACT_ACTIONS = "android.compactActions";
1120
Christoph Studer943aa672014-08-03 20:31:16 +02001121 /**
Alex Hillsfc737de2016-03-23 17:33:02 -04001122 * {@link #extras} key: the username to be displayed for all messages sent by the user including
1123 * direct replies
Adrian Roos96b7e202016-05-17 13:50:38 -07001124 * {@link android.app.Notification.MessagingStyle} notification. This extra is a
1125 * {@link CharSequence}
Selim Cinekcb8b9852017-12-15 18:01:52 -08001126 *
1127 * @deprecated use {@link #EXTRA_MESSAGING_PERSON}
Alex Hillsfc737de2016-03-23 17:33:02 -04001128 */
1129 public static final String EXTRA_SELF_DISPLAY_NAME = "android.selfDisplayName";
1130
1131 /**
Selim Cinekcb8b9852017-12-15 18:01:52 -08001132 * {@link #extras} key: the person to be displayed for all messages sent by the user including
1133 * direct replies
1134 * {@link android.app.Notification.MessagingStyle} notification. This extra is a
1135 * {@link Person}
1136 */
1137 public static final String EXTRA_MESSAGING_PERSON = "android.messagingUser";
1138
1139 /**
Adrian Roos96b7e202016-05-17 13:50:38 -07001140 * {@link #extras} key: a {@link CharSequence} to be displayed as the title to a conversation
Alex Hillsd9b04d92016-04-11 16:38:16 -04001141 * represented by a {@link android.app.Notification.MessagingStyle}
Alex Hillsfc737de2016-03-23 17:33:02 -04001142 */
Alex Hillsd9b04d92016-04-11 16:38:16 -04001143 public static final String EXTRA_CONVERSATION_TITLE = "android.conversationTitle";
Alex Hillsfc737de2016-03-23 17:33:02 -04001144
1145 /**
1146 * {@link #extras} key: an array of {@link android.app.Notification.MessagingStyle.Message}
1147 * bundles provided by a
Adrian Roos96b7e202016-05-17 13:50:38 -07001148 * {@link android.app.Notification.MessagingStyle} notification. This extra is a parcelable
1149 * array of bundles.
Alex Hillsfc737de2016-03-23 17:33:02 -04001150 */
1151 public static final String EXTRA_MESSAGES = "android.messages";
1152
1153 /**
Adrian Roos437cd562017-01-18 15:47:03 -08001154 * {@link #extras} key: an array of
1155 * {@link android.app.Notification.MessagingStyle#addHistoricMessage historic}
1156 * {@link android.app.Notification.MessagingStyle.Message} bundles provided by a
1157 * {@link android.app.Notification.MessagingStyle} notification. This extra is a parcelable
1158 * array of bundles.
1159 */
1160 public static final String EXTRA_HISTORIC_MESSAGES = "android.messages.historic";
1161
1162 /**
Kodlee Yin9ac617c2017-12-19 11:20:50 -08001163 * {@link #extras} key: whether the {@link android.app.Notification.MessagingStyle} notification
1164 * represents a group conversation.
1165 */
1166 public static final String EXTRA_IS_GROUP_CONVERSATION = "android.isGroupConversation";
1167
1168 /**
Selim Cinek7b9605b2017-01-19 17:36:00 -08001169 * {@link #extras} key: whether the notification should be colorized as
1170 * supplied to {@link Builder#setColorized(boolean)}}.
1171 */
1172 public static final String EXTRA_COLORIZED = "android.colorized";
1173
1174 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04001175 * @hide
1176 */
1177 public static final String EXTRA_BUILDER_APPLICATION_INFO = "android.appInfo";
1178
Selim Cinek247fa012016-02-18 09:50:48 -08001179 /**
1180 * @hide
1181 */
1182 public static final String EXTRA_CONTAINS_CUSTOM_VIEW = "android.contains.customView";
1183
Shane Brennan472a3b32016-12-12 15:28:10 -08001184 /**
Selim Cinekd0426622017-07-11 13:19:59 +02001185 * @hide
1186 */
1187 public static final String EXTRA_REDUCED_IMAGES = "android.reduced.images";
1188
1189 /**
Shane Brennan472a3b32016-12-12 15:28:10 -08001190 * {@link #extras} key: the audio contents of this notification.
1191 *
1192 * This is for use when rendering the notification on an audio-focused interface;
1193 * the audio contents are a complete sound sample that contains the contents/body of the
1194 * notification. This may be used in substitute of a Text-to-Speech reading of the
1195 * notification. For example if the notification represents a voice message this should point
1196 * to the audio of that message.
1197 *
1198 * The data stored under this key should be a String representation of a Uri that contains the
1199 * audio contents in one of the following formats: WAV, PCM 16-bit, AMR-WB.
1200 *
1201 * This extra is unnecessary if you are using {@code MessagingStyle} since each {@code Message}
1202 * has a field for holding data URI. That field can be used for audio.
1203 * See {@code Message#setData}.
1204 *
1205 * Example usage:
1206 * <pre>
1207 * {@code
1208 * Notification.Builder myBuilder = (build your Notification as normal);
1209 * myBuilder.getExtras().putString(EXTRA_AUDIO_CONTENTS_URI, myAudioUri.toString());
1210 * }
1211 * </pre>
1212 */
1213 public static final String EXTRA_AUDIO_CONTENTS_URI = "android.audioContents";
1214
Dan Sandler80eaa592016-04-14 23:34:54 -04001215 /** @hide */
1216 @SystemApi
Jeff Sharkeyd86b8fe2017-06-02 17:36:26 -06001217 @RequiresPermission(android.Manifest.permission.SUBSTITUTE_NOTIFICATION_APP_NAME)
Dan Sandler732bd6c2016-04-12 14:20:32 -04001218 public static final String EXTRA_SUBSTITUTE_APP_NAME = "android.substName";
1219
Dianne Hackbornfb5d4b52017-05-16 17:03:44 -07001220 /**
1221 * This is set on the notification shown by the activity manager about all apps
1222 * running in the background. It indicates that the notification should be shown
1223 * only if any of the given apps do not already have a {@link #FLAG_FOREGROUND_SERVICE}
1224 * notification currently visible to the user. This is a string array of all
1225 * package names of the apps.
1226 * @hide
1227 */
1228 public static final String EXTRA_FOREGROUND_APPS = "android.foregroundApps";
1229
Dan Sandlerd63f9322015-05-06 15:18:49 -04001230 private Icon mSmallIcon;
1231 private Icon mLargeIcon;
1232
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04001233 private String mChannelId;
Julia Reynolds2a128742016-11-28 14:29:25 -05001234 private long mTimeout;
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04001235
Julia Reynolds13d898c2017-02-02 12:22:05 -05001236 private String mShortcutId;
Julia Reynolds3aedded2017-03-31 14:42:09 -04001237 private CharSequence mSettingsText;
Julia Reynolds13d898c2017-02-02 12:22:05 -05001238
Julia Reynoldsa79c3712017-04-21 10:29:57 -04001239 /** @hide */
1240 @IntDef(prefix = { "GROUP_ALERT_" }, value = {
1241 GROUP_ALERT_ALL, GROUP_ALERT_CHILDREN, GROUP_ALERT_SUMMARY
1242 })
1243 @Retention(RetentionPolicy.SOURCE)
1244 public @interface GroupAlertBehavior {}
1245
1246 /**
1247 * Constant for {@link Builder#setGroupAlertBehavior(int)}, meaning that all notifications in a
1248 * group with sound or vibration ought to make sound or vibrate (respectively), so this
1249 * notification will not be muted when it is in a group.
1250 */
1251 public static final int GROUP_ALERT_ALL = 0;
1252
1253 /**
1254 * Constant for {@link Builder#setGroupAlertBehavior(int)}, meaning that all children
1255 * notification in a group should be silenced (no sound or vibration) even if they are posted
1256 * to a {@link NotificationChannel} that has sound and/or vibration. Use this constant to
Julia Reynolds399d9bf2017-08-11 12:52:14 -04001257 * mute this notification if this notification is a group child. This must be applied to all
1258 * children notifications you want to mute.
Julia Reynoldsa79c3712017-04-21 10:29:57 -04001259 *
1260 * <p> For example, you might want to use this constant if you post a number of children
1261 * notifications at once (say, after a periodic sync), and only need to notify the user
1262 * audibly once.
1263 */
1264 public static final int GROUP_ALERT_SUMMARY = 1;
1265
1266 /**
1267 * Constant for {@link Builder#setGroupAlertBehavior(int)}, meaning that the summary
1268 * notification in a group should be silenced (no sound or vibration) even if they are
1269 * posted to a {@link NotificationChannel} that has sound and/or vibration. Use this constant
1270 * to mute this notification if this notification is a group summary.
1271 *
1272 * <p>For example, you might want to use this constant if only the children notifications
Julia Reynolds399d9bf2017-08-11 12:52:14 -04001273 * in your group have content and the summary is only used to visually group notifications
1274 * rather than to alert the user that new information is available.
Julia Reynoldsa79c3712017-04-21 10:29:57 -04001275 */
1276 public static final int GROUP_ALERT_CHILDREN = 2;
1277
Julia Reynolds2f431e22017-06-07 14:12:09 +00001278 private int mGroupAlertBehavior = GROUP_ALERT_ALL;
Julia Reynoldsa79c3712017-04-21 10:29:57 -04001279
Julia Reynolds13d898c2017-02-02 12:22:05 -05001280 /**
1281 * If this notification is being shown as a badge, always show as a number.
1282 */
1283 public static final int BADGE_ICON_NONE = 0;
1284
1285 /**
1286 * If this notification is being shown as a badge, use the {@link #getSmallIcon()} to
1287 * represent this notification.
1288 */
1289 public static final int BADGE_ICON_SMALL = 1;
1290
1291 /**
1292 * If this notification is being shown as a badge, use the {@link #getLargeIcon()} to
1293 * represent this notification.
1294 */
1295 public static final int BADGE_ICON_LARGE = 2;
Julia Reynolds7d5b4da2017-03-20 10:18:49 -04001296 private int mBadgeIcon = BADGE_ICON_NONE;
Julia Reynolds13d898c2017-02-02 12:22:05 -05001297
Chris Wren51c75102013-07-16 20:49:17 -04001298 /**
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001299 * Structure to encapsulate a named action that can be shown as part of this notification.
1300 * It must include an icon, a label, and a {@link PendingIntent} to be fired when the action is
1301 * selected by the user.
1302 * <p>
Griff Hazen959591e2014-05-15 22:26:18 -07001303 * Apps should use {@link Notification.Builder#addAction(int, CharSequence, PendingIntent)}
1304 * or {@link Notification.Builder#addAction(Notification.Action)}
1305 * to attach actions.
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001306 */
Daniel Sandlerea2a3172013-02-20 22:24:20 -05001307 public static class Action implements Parcelable {
Shane Brennan472a3b32016-12-12 15:28:10 -08001308 /**
1309 * {@link #extras} key: Keys to a {@link Parcelable} {@link ArrayList} of
1310 * {@link RemoteInput}s.
1311 *
1312 * This is intended for {@link RemoteInput}s that only accept data, meaning
1313 * {@link RemoteInput#getAllowFreeFormInput} is false, {@link RemoteInput#getChoices}
1314 * is null or empty, and {@link RemoteInput#getAllowedDataTypes is non-null and not
1315 * empty. These {@link RemoteInput}s will be ignored by devices that do not
1316 * support non-text-based {@link RemoteInput}s. See {@link Builder#build}.
1317 *
1318 * You can test if a RemoteInput matches these constraints using
1319 * {@link RemoteInput#isDataOnly}.
1320 */
1321 private static final String EXTRA_DATA_ONLY_INPUTS = "android.extra.DATA_ONLY_INPUTS";
1322
Kodlee Yinc72c44d2017-12-21 22:07:15 +00001323 /**
1324 * {@link }: No semantic action defined.
1325 */
1326 public static final int SEMANTIC_ACTION_NONE = 0;
1327
1328 /**
1329 * {@code SemanticAction}: Reply to a conversation, chat, group, or wherever replies
1330 * may be appropriate.
1331 */
1332 public static final int SEMANTIC_ACTION_REPLY = 1;
1333
1334 /**
1335 * {@code SemanticAction}: Mark content as read.
1336 */
1337 public static final int SEMANTIC_ACTION_MARK_AS_READ = 2;
1338
1339 /**
1340 * {@code SemanticAction}: Mark content as unread.
1341 */
1342 public static final int SEMANTIC_ACTION_MARK_AS_UNREAD = 3;
1343
1344 /**
1345 * {@code SemanticAction}: Delete the content associated with the notification. This
1346 * could mean deleting an email, message, etc.
1347 */
1348 public static final int SEMANTIC_ACTION_DELETE = 4;
1349
1350 /**
1351 * {@code SemanticAction}: Archive the content associated with the notification. This
1352 * could mean archiving an email, message, etc.
1353 */
1354 public static final int SEMANTIC_ACTION_ARCHIVE = 5;
1355
1356 /**
1357 * {@code SemanticAction}: Mute the content associated with the notification. This could
1358 * mean silencing a conversation or currently playing media.
1359 */
1360 public static final int SEMANTIC_ACTION_MUTE = 6;
1361
1362 /**
1363 * {@code SemanticAction}: Unmute the content associated with the notification. This could
1364 * mean un-silencing a conversation or currently playing media.
1365 */
1366 public static final int SEMANTIC_ACTION_UNMUTE = 7;
1367
1368 /**
1369 * {@code SemanticAction}: Mark content with a thumbs up.
1370 */
1371 public static final int SEMANTIC_ACTION_THUMBS_UP = 8;
1372
1373 /**
1374 * {@code SemanticAction}: Mark content with a thumbs down.
1375 */
1376 public static final int SEMANTIC_ACTION_THUMBS_DOWN = 9;
1377
Kodlee Yincda5b092018-02-15 15:34:53 -08001378 /**
1379 * {@code SemanticAction}: Call a contact, group, etc.
1380 */
1381 public static final int SEMANTIC_ACTION_CALL = 10;
Kodlee Yinc72c44d2017-12-21 22:07:15 +00001382
Griff Hazen959591e2014-05-15 22:26:18 -07001383 private final Bundle mExtras;
Dan Sandler86647982015-05-13 23:41:13 -04001384 private Icon mIcon;
Griff Hazen61a9e862014-05-22 16:05:19 -07001385 private final RemoteInput[] mRemoteInputs;
Alex Hills1f27f882017-01-12 11:24:46 -05001386 private boolean mAllowGeneratedReplies = true;
Kodlee Yinc72c44d2017-12-21 22:07:15 +00001387 private final @SemanticAction int mSemanticAction;
Griff Hazen959591e2014-05-15 22:26:18 -07001388
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001389 /**
1390 * Small icon representing the action.
Dan Sandler86647982015-05-13 23:41:13 -04001391 *
1392 * @deprecated Use {@link Action#getIcon()} instead.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001393 */
Dan Sandler86647982015-05-13 23:41:13 -04001394 @Deprecated
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001395 public int icon;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001396
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001397 /**
1398 * Title of the action.
1399 */
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001400 public CharSequence title;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001401
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001402 /**
1403 * Intent to send when the user invokes this action. May be null, in which case the action
1404 * may be rendered in a disabled presentation by the system UI.
1405 */
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001406 public PendingIntent actionIntent;
Griff Hazen959591e2014-05-15 22:26:18 -07001407
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001408 private Action(Parcel in) {
Dan Sandler86647982015-05-13 23:41:13 -04001409 if (in.readInt() != 0) {
1410 mIcon = Icon.CREATOR.createFromParcel(in);
Dan Sandler68079d52015-07-22 10:45:30 -04001411 if (mIcon.getType() == Icon.TYPE_RESOURCE) {
1412 icon = mIcon.getResId();
1413 }
Dan Sandler86647982015-05-13 23:41:13 -04001414 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001415 title = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
1416 if (in.readInt() == 1) {
1417 actionIntent = PendingIntent.CREATOR.createFromParcel(in);
1418 }
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001419 mExtras = Bundle.setDefusable(in.readBundle(), true);
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001420 mRemoteInputs = in.createTypedArray(RemoteInput.CREATOR);
Alex Hills42b0c4d2016-04-26 13:35:36 -04001421 mAllowGeneratedReplies = in.readInt() == 1;
Kodlee Yinc72c44d2017-12-21 22:07:15 +00001422 mSemanticAction = in.readInt();
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001423 }
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001424
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001425 /**
Dan Sandler86647982015-05-13 23:41:13 -04001426 * @deprecated Use {@link android.app.Notification.Action.Builder}.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001427 */
Dan Sandler86647982015-05-13 23:41:13 -04001428 @Deprecated
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001429 public Action(int icon, CharSequence title, PendingIntent intent) {
Kodlee Yinc72c44d2017-12-21 22:07:15 +00001430 this(Icon.createWithResource("", icon), title, intent, new Bundle(), null, true,
1431 SEMANTIC_ACTION_NONE);
Griff Hazen959591e2014-05-15 22:26:18 -07001432 }
1433
Adrian Roos7af53622016-10-12 13:44:05 -07001434 /** Keep in sync with {@link Notification.Action.Builder#Builder(Action)}! */
Dan Sandler86647982015-05-13 23:41:13 -04001435 private Action(Icon icon, CharSequence title, PendingIntent intent, Bundle extras,
Kodlee Yinc72c44d2017-12-21 22:07:15 +00001436 RemoteInput[] remoteInputs, boolean allowGeneratedReplies,
1437 @SemanticAction int semanticAction) {
Dan Sandler86647982015-05-13 23:41:13 -04001438 this.mIcon = icon;
Gus Prevasf5bff46f2015-08-24 10:34:28 -04001439 if (icon != null && icon.getType() == Icon.TYPE_RESOURCE) {
1440 this.icon = icon.getResId();
1441 }
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001442 this.title = title;
1443 this.actionIntent = intent;
Griff Hazen959591e2014-05-15 22:26:18 -07001444 this.mExtras = extras != null ? extras : new Bundle();
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001445 this.mRemoteInputs = remoteInputs;
Alex Hills42b0c4d2016-04-26 13:35:36 -04001446 this.mAllowGeneratedReplies = allowGeneratedReplies;
Kodlee Yinc72c44d2017-12-21 22:07:15 +00001447 this.mSemanticAction = semanticAction;
Griff Hazen959591e2014-05-15 22:26:18 -07001448 }
1449
1450 /**
Dan Sandler86647982015-05-13 23:41:13 -04001451 * Return an icon representing the action.
1452 */
1453 public Icon getIcon() {
1454 if (mIcon == null && icon != 0) {
1455 // you snuck an icon in here without using the builder; let's try to keep it
1456 mIcon = Icon.createWithResource("", icon);
1457 }
1458 return mIcon;
1459 }
1460
1461 /**
Griff Hazen959591e2014-05-15 22:26:18 -07001462 * Get additional metadata carried around with this Action.
1463 */
1464 public Bundle getExtras() {
1465 return mExtras;
1466 }
1467
1468 /**
Alex Hills42b0c4d2016-04-26 13:35:36 -04001469 * Return whether the platform should automatically generate possible replies for this
1470 * {@link Action}
1471 */
1472 public boolean getAllowGeneratedReplies() {
1473 return mAllowGeneratedReplies;
1474 }
1475
1476 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001477 * Get the list of inputs to be collected from the user when this action is sent.
Shane Brennan472a3b32016-12-12 15:28:10 -08001478 * May return null if no remote inputs were added. Only returns inputs which accept
1479 * a text input. For inputs which only accept data use {@link #getDataOnlyRemoteInputs}.
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001480 */
1481 public RemoteInput[] getRemoteInputs() {
1482 return mRemoteInputs;
1483 }
1484
1485 /**
Kodlee Yinc72c44d2017-12-21 22:07:15 +00001486 * Returns the {@code SemanticAction} associated with this {@link Action}. A
1487 * {@code SemanticAction} denotes what an {@link Action}'s {@link PendingIntent} will do
1488 * (eg. reply, mark as read, delete, etc).
1489 */
1490 public @SemanticAction int getSemanticAction() {
1491 return mSemanticAction;
1492 }
1493
1494 /**
Shane Brennan472a3b32016-12-12 15:28:10 -08001495 * Get the list of inputs to be collected from the user that ONLY accept data when this
1496 * action is sent. These remote inputs are guaranteed to return true on a call to
1497 * {@link RemoteInput#isDataOnly}.
1498 *
Shane Brennanf670d6c2017-04-25 13:05:45 -07001499 * Returns null if there are no data-only remote inputs.
Shane Brennan472a3b32016-12-12 15:28:10 -08001500 *
1501 * This method exists so that legacy RemoteInput collectors that pre-date the addition
1502 * of non-textual RemoteInputs do not access these remote inputs.
1503 */
1504 public RemoteInput[] getDataOnlyRemoteInputs() {
1505 return (RemoteInput[]) mExtras.getParcelableArray(EXTRA_DATA_ONLY_INPUTS);
1506 }
1507
1508 /**
Griff Hazen959591e2014-05-15 22:26:18 -07001509 * Builder class for {@link Action} objects.
1510 */
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001511 public static final class Builder {
Dan Sandler86647982015-05-13 23:41:13 -04001512 private final Icon mIcon;
Griff Hazen959591e2014-05-15 22:26:18 -07001513 private final CharSequence mTitle;
1514 private final PendingIntent mIntent;
Alex Hills1f27f882017-01-12 11:24:46 -05001515 private boolean mAllowGeneratedReplies = true;
Griff Hazen959591e2014-05-15 22:26:18 -07001516 private final Bundle mExtras;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001517 private ArrayList<RemoteInput> mRemoteInputs;
Kodlee Yinc72c44d2017-12-21 22:07:15 +00001518 private @SemanticAction int mSemanticAction;
Griff Hazen959591e2014-05-15 22:26:18 -07001519
1520 /**
1521 * Construct a new builder for {@link Action} object.
1522 * @param icon icon to show for this action
1523 * @param title the title of the action
1524 * @param intent the {@link PendingIntent} to fire when users trigger this action
1525 */
Dan Sandler86647982015-05-13 23:41:13 -04001526 @Deprecated
Griff Hazen959591e2014-05-15 22:26:18 -07001527 public Builder(int icon, CharSequence title, PendingIntent intent) {
Adrian Roos7af53622016-10-12 13:44:05 -07001528 this(Icon.createWithResource("", icon), title, intent);
Dan Sandler86647982015-05-13 23:41:13 -04001529 }
1530
1531 /**
1532 * Construct a new builder for {@link Action} object.
1533 * @param icon icon to show for this action
1534 * @param title the title of the action
1535 * @param intent the {@link PendingIntent} to fire when users trigger this action
1536 */
1537 public Builder(Icon icon, CharSequence title, PendingIntent intent) {
Kodlee Yinc72c44d2017-12-21 22:07:15 +00001538 this(icon, title, intent, new Bundle(), null, true, SEMANTIC_ACTION_NONE);
Griff Hazen959591e2014-05-15 22:26:18 -07001539 }
1540
1541 /**
1542 * Construct a new builder for {@link Action} object using the fields from an
1543 * {@link Action}.
1544 * @param action the action to read fields from.
1545 */
1546 public Builder(Action action) {
Adrian Roos7af53622016-10-12 13:44:05 -07001547 this(action.getIcon(), action.title, action.actionIntent,
1548 new Bundle(action.mExtras), action.getRemoteInputs(),
Kodlee Yinc72c44d2017-12-21 22:07:15 +00001549 action.getAllowGeneratedReplies(), action.getSemanticAction());
Griff Hazen959591e2014-05-15 22:26:18 -07001550 }
1551
Dan Sandler86647982015-05-13 23:41:13 -04001552 private Builder(Icon icon, CharSequence title, PendingIntent intent, Bundle extras,
Kodlee Yinc72c44d2017-12-21 22:07:15 +00001553 RemoteInput[] remoteInputs, boolean allowGeneratedReplies,
1554 @SemanticAction int semanticAction) {
Griff Hazen959591e2014-05-15 22:26:18 -07001555 mIcon = icon;
1556 mTitle = title;
1557 mIntent = intent;
1558 mExtras = extras;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001559 if (remoteInputs != null) {
1560 mRemoteInputs = new ArrayList<RemoteInput>(remoteInputs.length);
1561 Collections.addAll(mRemoteInputs, remoteInputs);
1562 }
Adrian Roos7af53622016-10-12 13:44:05 -07001563 mAllowGeneratedReplies = allowGeneratedReplies;
Kodlee Yinc72c44d2017-12-21 22:07:15 +00001564 mSemanticAction = semanticAction;
Griff Hazen959591e2014-05-15 22:26:18 -07001565 }
1566
1567 /**
1568 * Merge additional metadata into this builder.
1569 *
1570 * <p>Values within the Bundle will replace existing extras values in this Builder.
1571 *
1572 * @see Notification.Action#extras
1573 */
1574 public Builder addExtras(Bundle extras) {
1575 if (extras != null) {
1576 mExtras.putAll(extras);
1577 }
1578 return this;
1579 }
1580
1581 /**
1582 * Get the metadata Bundle used by this Builder.
1583 *
1584 * <p>The returned Bundle is shared with this Builder.
1585 */
1586 public Bundle getExtras() {
1587 return mExtras;
1588 }
1589
1590 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001591 * Add an input to be collected from the user when this action is sent.
1592 * Response values can be retrieved from the fired intent by using the
1593 * {@link RemoteInput#getResultsFromIntent} function.
1594 * @param remoteInput a {@link RemoteInput} to add to the action
1595 * @return this object for method chaining
1596 */
1597 public Builder addRemoteInput(RemoteInput remoteInput) {
1598 if (mRemoteInputs == null) {
1599 mRemoteInputs = new ArrayList<RemoteInput>();
1600 }
1601 mRemoteInputs.add(remoteInput);
1602 return this;
1603 }
1604
1605 /**
Alex Hills42b0c4d2016-04-26 13:35:36 -04001606 * Set whether the platform should automatically generate possible replies to add to
1607 * {@link RemoteInput#getChoices()}. If the {@link Action} doesn't have a
1608 * {@link RemoteInput}, this has no effect.
1609 * @param allowGeneratedReplies {@code true} to allow generated replies, {@code false}
1610 * otherwise
1611 * @return this object for method chaining
Alex Hills1f27f882017-01-12 11:24:46 -05001612 * The default value is {@code true}
Alex Hills42b0c4d2016-04-26 13:35:36 -04001613 */
1614 public Builder setAllowGeneratedReplies(boolean allowGeneratedReplies) {
1615 mAllowGeneratedReplies = allowGeneratedReplies;
1616 return this;
1617 }
1618
1619 /**
Kodlee Yinc72c44d2017-12-21 22:07:15 +00001620 * Sets the {@code SemanticAction} for this {@link Action}. A
1621 * {@code SemanticAction} denotes what an {@link Action}'s
1622 * {@link PendingIntent} will do (eg. reply, mark as read, delete, etc).
1623 * @param semanticAction a SemanticAction defined within {@link Action} with
1624 * {@code SEMANTIC_ACTION_} prefixes
1625 * @return this object for method chaining
1626 */
1627 public Builder setSemanticAction(@SemanticAction int semanticAction) {
1628 mSemanticAction = semanticAction;
1629 return this;
1630 }
1631
1632 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001633 * Apply an extender to this action builder. Extenders may be used to add
1634 * metadata or change options on this builder.
1635 */
Griff Hazen61a9e862014-05-22 16:05:19 -07001636 public Builder extend(Extender extender) {
1637 extender.extend(this);
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001638 return this;
1639 }
1640
1641 /**
Griff Hazen959591e2014-05-15 22:26:18 -07001642 * Combine all of the options that have been set and return a new {@link Action}
1643 * object.
1644 * @return the built action
1645 */
1646 public Action build() {
Shane Brennan472a3b32016-12-12 15:28:10 -08001647 ArrayList<RemoteInput> dataOnlyInputs = new ArrayList<>();
1648 RemoteInput[] previousDataInputs =
1649 (RemoteInput[]) mExtras.getParcelableArray(EXTRA_DATA_ONLY_INPUTS);
Felipe Lemedfd11962017-01-18 18:38:46 -08001650 if (previousDataInputs != null) {
Shane Brennan472a3b32016-12-12 15:28:10 -08001651 for (RemoteInput input : previousDataInputs) {
1652 dataOnlyInputs.add(input);
1653 }
1654 }
1655 List<RemoteInput> textInputs = new ArrayList<>();
1656 if (mRemoteInputs != null) {
1657 for (RemoteInput input : mRemoteInputs) {
1658 if (input.isDataOnly()) {
1659 dataOnlyInputs.add(input);
1660 } else {
1661 textInputs.add(input);
1662 }
1663 }
1664 }
1665 if (!dataOnlyInputs.isEmpty()) {
1666 RemoteInput[] dataInputsArr =
1667 dataOnlyInputs.toArray(new RemoteInput[dataOnlyInputs.size()]);
1668 mExtras.putParcelableArray(EXTRA_DATA_ONLY_INPUTS, dataInputsArr);
1669 }
1670 RemoteInput[] textInputsArr = textInputs.isEmpty()
1671 ? null : textInputs.toArray(new RemoteInput[textInputs.size()]);
1672 return new Action(mIcon, mTitle, mIntent, mExtras, textInputsArr,
Kodlee Yinc72c44d2017-12-21 22:07:15 +00001673 mAllowGeneratedReplies, mSemanticAction);
Griff Hazen959591e2014-05-15 22:26:18 -07001674 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001675 }
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001676
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001677 @Override
1678 public Action clone() {
1679 return new Action(
Dan Sandler86647982015-05-13 23:41:13 -04001680 getIcon(),
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001681 title,
1682 actionIntent, // safe to alias
Julia Reynolds53c934c2016-09-16 14:03:43 -04001683 mExtras == null ? new Bundle() : new Bundle(mExtras),
Alex Hills42b0c4d2016-04-26 13:35:36 -04001684 getRemoteInputs(),
Kodlee Yinc72c44d2017-12-21 22:07:15 +00001685 getAllowGeneratedReplies(),
1686 getSemanticAction());
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001687 }
Kodlee Yinc72c44d2017-12-21 22:07:15 +00001688
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001689 @Override
1690 public int describeContents() {
1691 return 0;
1692 }
Kodlee Yinc72c44d2017-12-21 22:07:15 +00001693
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001694 @Override
1695 public void writeToParcel(Parcel out, int flags) {
Dan Sandler86647982015-05-13 23:41:13 -04001696 final Icon ic = getIcon();
1697 if (ic != null) {
1698 out.writeInt(1);
1699 ic.writeToParcel(out, 0);
1700 } else {
1701 out.writeInt(0);
1702 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001703 TextUtils.writeToParcel(title, out, flags);
1704 if (actionIntent != null) {
1705 out.writeInt(1);
1706 actionIntent.writeToParcel(out, flags);
1707 } else {
1708 out.writeInt(0);
1709 }
Griff Hazen959591e2014-05-15 22:26:18 -07001710 out.writeBundle(mExtras);
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001711 out.writeTypedArray(mRemoteInputs, flags);
Alex Hills42b0c4d2016-04-26 13:35:36 -04001712 out.writeInt(mAllowGeneratedReplies ? 1 : 0);
Kodlee Yinc72c44d2017-12-21 22:07:15 +00001713 out.writeInt(mSemanticAction);
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001714 }
Kodlee Yinc72c44d2017-12-21 22:07:15 +00001715
Griff Hazen959591e2014-05-15 22:26:18 -07001716 public static final Parcelable.Creator<Action> CREATOR =
1717 new Parcelable.Creator<Action>() {
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001718 public Action createFromParcel(Parcel in) {
1719 return new Action(in);
1720 }
1721 public Action[] newArray(int size) {
1722 return new Action[size];
1723 }
1724 };
Griff Hazen61a9e862014-05-22 16:05:19 -07001725
1726 /**
1727 * Extender interface for use with {@link Builder#extend}. Extenders may be used to add
1728 * metadata or change options on an action builder.
1729 */
1730 public interface Extender {
1731 /**
1732 * Apply this extender to a notification action builder.
1733 * @param builder the builder to be modified.
1734 * @return the build object for chaining.
1735 */
1736 public Builder extend(Builder builder);
1737 }
1738
1739 /**
1740 * Wearable extender for notification actions. To add extensions to an action,
1741 * create a new {@link android.app.Notification.Action.WearableExtender} object using
1742 * the {@code WearableExtender()} constructor and apply it to a
1743 * {@link android.app.Notification.Action.Builder} using
1744 * {@link android.app.Notification.Action.Builder#extend}.
1745 *
1746 * <pre class="prettyprint">
1747 * Notification.Action action = new Notification.Action.Builder(
1748 * R.drawable.archive_all, "Archive all", actionIntent)
Griff Hazen14f57992014-05-26 09:07:14 -07001749 * .extend(new Notification.Action.WearableExtender()
Griff Hazen61a9e862014-05-22 16:05:19 -07001750 * .setAvailableOffline(false))
Griff Hazen14f57992014-05-26 09:07:14 -07001751 * .build();</pre>
Griff Hazen61a9e862014-05-22 16:05:19 -07001752 */
1753 public static final class WearableExtender implements Extender {
1754 /** Notification action extra which contains wearable extensions */
1755 private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS";
1756
Pete Gastaf6781d2014-10-07 15:17:05 -04001757 // Keys within EXTRA_WEARABLE_EXTENSIONS for wearable options.
Griff Hazen61a9e862014-05-22 16:05:19 -07001758 private static final String KEY_FLAGS = "flags";
Pete Gastaf6781d2014-10-07 15:17:05 -04001759 private static final String KEY_IN_PROGRESS_LABEL = "inProgressLabel";
1760 private static final String KEY_CONFIRM_LABEL = "confirmLabel";
1761 private static final String KEY_CANCEL_LABEL = "cancelLabel";
Griff Hazen61a9e862014-05-22 16:05:19 -07001762
1763 // Flags bitwise-ored to mFlags
1764 private static final int FLAG_AVAILABLE_OFFLINE = 0x1;
Alex Hills9ab3a232016-04-05 14:54:56 -04001765 private static final int FLAG_HINT_LAUNCHES_ACTIVITY = 1 << 1;
Alex Hills9f087612016-06-07 09:08:59 -04001766 private static final int FLAG_HINT_DISPLAY_INLINE = 1 << 2;
Griff Hazen61a9e862014-05-22 16:05:19 -07001767
1768 // Default value for flags integer
1769 private static final int DEFAULT_FLAGS = FLAG_AVAILABLE_OFFLINE;
1770
1771 private int mFlags = DEFAULT_FLAGS;
1772
Pete Gastaf6781d2014-10-07 15:17:05 -04001773 private CharSequence mInProgressLabel;
1774 private CharSequence mConfirmLabel;
1775 private CharSequence mCancelLabel;
1776
Griff Hazen61a9e862014-05-22 16:05:19 -07001777 /**
1778 * Create a {@link android.app.Notification.Action.WearableExtender} with default
1779 * options.
1780 */
1781 public WearableExtender() {
1782 }
1783
1784 /**
1785 * Create a {@link android.app.Notification.Action.WearableExtender} by reading
1786 * wearable options present in an existing notification action.
1787 * @param action the notification action to inspect.
1788 */
1789 public WearableExtender(Action action) {
1790 Bundle wearableBundle = action.getExtras().getBundle(EXTRA_WEARABLE_EXTENSIONS);
1791 if (wearableBundle != null) {
1792 mFlags = wearableBundle.getInt(KEY_FLAGS, DEFAULT_FLAGS);
Pete Gastaf6781d2014-10-07 15:17:05 -04001793 mInProgressLabel = wearableBundle.getCharSequence(KEY_IN_PROGRESS_LABEL);
1794 mConfirmLabel = wearableBundle.getCharSequence(KEY_CONFIRM_LABEL);
1795 mCancelLabel = wearableBundle.getCharSequence(KEY_CANCEL_LABEL);
Griff Hazen61a9e862014-05-22 16:05:19 -07001796 }
1797 }
1798
1799 /**
1800 * Apply wearable extensions to a notification action that is being built. This is
1801 * typically called by the {@link android.app.Notification.Action.Builder#extend}
1802 * method of {@link android.app.Notification.Action.Builder}.
1803 */
1804 @Override
1805 public Action.Builder extend(Action.Builder builder) {
1806 Bundle wearableBundle = new Bundle();
1807
1808 if (mFlags != DEFAULT_FLAGS) {
1809 wearableBundle.putInt(KEY_FLAGS, mFlags);
1810 }
Pete Gastaf6781d2014-10-07 15:17:05 -04001811 if (mInProgressLabel != null) {
1812 wearableBundle.putCharSequence(KEY_IN_PROGRESS_LABEL, mInProgressLabel);
1813 }
1814 if (mConfirmLabel != null) {
1815 wearableBundle.putCharSequence(KEY_CONFIRM_LABEL, mConfirmLabel);
1816 }
1817 if (mCancelLabel != null) {
1818 wearableBundle.putCharSequence(KEY_CANCEL_LABEL, mCancelLabel);
1819 }
Griff Hazen61a9e862014-05-22 16:05:19 -07001820
1821 builder.getExtras().putBundle(EXTRA_WEARABLE_EXTENSIONS, wearableBundle);
1822 return builder;
1823 }
1824
1825 @Override
1826 public WearableExtender clone() {
1827 WearableExtender that = new WearableExtender();
1828 that.mFlags = this.mFlags;
Pete Gastaf6781d2014-10-07 15:17:05 -04001829 that.mInProgressLabel = this.mInProgressLabel;
1830 that.mConfirmLabel = this.mConfirmLabel;
1831 that.mCancelLabel = this.mCancelLabel;
Griff Hazen61a9e862014-05-22 16:05:19 -07001832 return that;
1833 }
1834
1835 /**
1836 * Set whether this action is available when the wearable device is not connected to
1837 * a companion device. The user can still trigger this action when the wearable device is
1838 * offline, but a visual hint will indicate that the action may not be available.
1839 * Defaults to true.
1840 */
1841 public WearableExtender setAvailableOffline(boolean availableOffline) {
1842 setFlag(FLAG_AVAILABLE_OFFLINE, availableOffline);
1843 return this;
1844 }
1845
1846 /**
1847 * Get whether this action is available when the wearable device is not connected to
1848 * a companion device. The user can still trigger this action when the wearable device is
1849 * offline, but a visual hint will indicate that the action may not be available.
1850 * Defaults to true.
1851 */
1852 public boolean isAvailableOffline() {
1853 return (mFlags & FLAG_AVAILABLE_OFFLINE) != 0;
1854 }
1855
1856 private void setFlag(int mask, boolean value) {
1857 if (value) {
1858 mFlags |= mask;
1859 } else {
1860 mFlags &= ~mask;
1861 }
1862 }
Pete Gastaf6781d2014-10-07 15:17:05 -04001863
1864 /**
1865 * Set a label to display while the wearable is preparing to automatically execute the
1866 * action. This is usually a 'ing' verb ending in ellipsis like "Sending..."
1867 *
1868 * @param label the label to display while the action is being prepared to execute
1869 * @return this object for method chaining
1870 */
Gus Prevasb5f9cf52018-02-21 14:21:50 -05001871 @Deprecated
Pete Gastaf6781d2014-10-07 15:17:05 -04001872 public WearableExtender setInProgressLabel(CharSequence label) {
1873 mInProgressLabel = label;
1874 return this;
1875 }
1876
1877 /**
1878 * Get the label to display while the wearable is preparing to automatically execute
1879 * the action. This is usually a 'ing' verb ending in ellipsis like "Sending..."
1880 *
1881 * @return the label to display while the action is being prepared to execute
1882 */
Gus Prevasb5f9cf52018-02-21 14:21:50 -05001883 @Deprecated
Pete Gastaf6781d2014-10-07 15:17:05 -04001884 public CharSequence getInProgressLabel() {
1885 return mInProgressLabel;
1886 }
1887
1888 /**
1889 * Set a label to display to confirm that the action should be executed.
1890 * This is usually an imperative verb like "Send".
1891 *
1892 * @param label the label to confirm the action should be executed
1893 * @return this object for method chaining
1894 */
Gus Prevasb5f9cf52018-02-21 14:21:50 -05001895 @Deprecated
Pete Gastaf6781d2014-10-07 15:17:05 -04001896 public WearableExtender setConfirmLabel(CharSequence label) {
1897 mConfirmLabel = label;
1898 return this;
1899 }
1900
1901 /**
1902 * Get the label to display to confirm that the action should be executed.
1903 * This is usually an imperative verb like "Send".
1904 *
1905 * @return the label to confirm the action should be executed
1906 */
Gus Prevasb5f9cf52018-02-21 14:21:50 -05001907 @Deprecated
Pete Gastaf6781d2014-10-07 15:17:05 -04001908 public CharSequence getConfirmLabel() {
1909 return mConfirmLabel;
1910 }
1911
1912 /**
1913 * Set a label to display to cancel the action.
1914 * This is usually an imperative verb, like "Cancel".
1915 *
1916 * @param label the label to display to cancel the action
1917 * @return this object for method chaining
1918 */
Gus Prevasb5f9cf52018-02-21 14:21:50 -05001919 @Deprecated
Pete Gastaf6781d2014-10-07 15:17:05 -04001920 public WearableExtender setCancelLabel(CharSequence label) {
1921 mCancelLabel = label;
1922 return this;
1923 }
1924
1925 /**
1926 * Get the label to display to cancel the action.
1927 * This is usually an imperative verb like "Cancel".
1928 *
1929 * @return the label to display to cancel the action
1930 */
Gus Prevasb5f9cf52018-02-21 14:21:50 -05001931 @Deprecated
Pete Gastaf6781d2014-10-07 15:17:05 -04001932 public CharSequence getCancelLabel() {
1933 return mCancelLabel;
1934 }
Alex Hills9ab3a232016-04-05 14:54:56 -04001935
1936 /**
1937 * Set a hint that this Action will launch an {@link Activity} directly, telling the
1938 * platform that it can generate the appropriate transitions.
1939 * @param hintLaunchesActivity {@code true} if the content intent will launch
1940 * an activity and transitions should be generated, false otherwise.
1941 * @return this object for method chaining
1942 */
Alex Hills4ec3ff42016-04-12 11:36:18 -04001943 public WearableExtender setHintLaunchesActivity(
Alex Hills9ab3a232016-04-05 14:54:56 -04001944 boolean hintLaunchesActivity) {
1945 setFlag(FLAG_HINT_LAUNCHES_ACTIVITY, hintLaunchesActivity);
1946 return this;
1947 }
1948
1949 /**
1950 * Get a hint that this Action will launch an {@link Activity} directly, telling the
1951 * platform that it can generate the appropriate transitions
1952 * @return {@code true} if the content intent will launch an activity and transitions
1953 * should be generated, false otherwise. The default value is {@code false} if this was
1954 * never set.
1955 */
Alex Hills4ec3ff42016-04-12 11:36:18 -04001956 public boolean getHintLaunchesActivity() {
Alex Hills9ab3a232016-04-05 14:54:56 -04001957 return (mFlags & FLAG_HINT_LAUNCHES_ACTIVITY) != 0;
1958 }
Alex Hills9f087612016-06-07 09:08:59 -04001959
1960 /**
1961 * Set a hint that this Action should be displayed inline.
1962 *
1963 * @param hintDisplayInline {@code true} if action should be displayed inline, false
1964 * otherwise
1965 * @return this object for method chaining
1966 */
1967 public WearableExtender setHintDisplayActionInline(
1968 boolean hintDisplayInline) {
1969 setFlag(FLAG_HINT_DISPLAY_INLINE, hintDisplayInline);
1970 return this;
1971 }
1972
1973 /**
1974 * Get a hint that this Action should be displayed inline.
1975 *
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08001976 * @return {@code true} if the Action should be displayed inline, {@code false}
Alex Hills9f087612016-06-07 09:08:59 -04001977 * otherwise. The default value is {@code false} if this was never set.
1978 */
1979 public boolean getHintDisplayActionInline() {
1980 return (mFlags & FLAG_HINT_DISPLAY_INLINE) != 0;
1981 }
Griff Hazen61a9e862014-05-22 16:05:19 -07001982 }
Kodlee Yinc72c44d2017-12-21 22:07:15 +00001983
1984 /**
1985 * Provides meaning to an {@link Action} that hints at what the associated
1986 * {@link PendingIntent} will do. For example, an {@link Action} with a
1987 * {@link PendingIntent} that replies to a text message notification may have the
1988 * {@link #SEMANTIC_ACTION_REPLY} {@code SemanticAction} set within it.
1989 *
1990 * @hide
1991 */
1992 @IntDef(prefix = { "SEMANTIC_ACTION_" }, value = {
1993 SEMANTIC_ACTION_NONE,
1994 SEMANTIC_ACTION_REPLY,
1995 SEMANTIC_ACTION_MARK_AS_READ,
1996 SEMANTIC_ACTION_MARK_AS_UNREAD,
1997 SEMANTIC_ACTION_DELETE,
1998 SEMANTIC_ACTION_ARCHIVE,
1999 SEMANTIC_ACTION_MUTE,
2000 SEMANTIC_ACTION_UNMUTE,
2001 SEMANTIC_ACTION_THUMBS_UP,
Kodlee Yincda5b092018-02-15 15:34:53 -08002002 SEMANTIC_ACTION_THUMBS_DOWN,
2003 SEMANTIC_ACTION_CALL
Kodlee Yinc72c44d2017-12-21 22:07:15 +00002004 })
2005 @Retention(RetentionPolicy.SOURCE)
2006 public @interface SemanticAction {}
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002007 }
2008
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04002009 /**
2010 * Array of all {@link Action} structures attached to this notification by
2011 * {@link Builder#addAction(int, CharSequence, PendingIntent)}. Mostly useful for instances of
2012 * {@link android.service.notification.NotificationListenerService} that provide an alternative
2013 * interface for invoking actions.
2014 */
Daniel Sandlerea2a3172013-02-20 22:24:20 -05002015 public Action[] actions;
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002016
2017 /**
Dan Sandler0bf2ed82013-12-21 23:33:41 -06002018 * Replacement version of this notification whose content will be shown
2019 * in an insecure context such as atop a secure keyguard. See {@link #visibility}
2020 * and {@link #VISIBILITY_PUBLIC}.
2021 */
2022 public Notification publicVersion;
2023
2024 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002025 * Constructs a Notification object with default values.
Joe Onorato46439ce2010-11-19 13:56:21 -08002026 * You might want to consider using {@link Builder} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002027 */
2028 public Notification()
2029 {
2030 this.when = System.currentTimeMillis();
Selim Cinekb85f36fd2016-04-20 18:46:36 -07002031 this.creationTime = System.currentTimeMillis();
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002032 this.priority = PRIORITY_DEFAULT;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002033 }
2034
2035 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002036 * @hide
2037 */
2038 public Notification(Context context, int icon, CharSequence tickerText, long when,
2039 CharSequence contentTitle, CharSequence contentText, Intent contentIntent)
2040 {
Chris Wren1ce4b6d2015-06-11 10:19:43 -04002041 new Builder(context)
2042 .setWhen(when)
2043 .setSmallIcon(icon)
2044 .setTicker(tickerText)
2045 .setContentTitle(contentTitle)
2046 .setContentText(contentText)
2047 .setContentIntent(PendingIntent.getActivity(context, 0, contentIntent, 0))
2048 .buildInto(this);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002049 }
2050
2051 /**
2052 * Constructs a Notification object with the information needed to
2053 * have a status bar icon without the standard expanded view.
2054 *
2055 * @param icon The resource id of the icon to put in the status bar.
2056 * @param tickerText The text that flows by in the status bar when the notification first
2057 * activates.
2058 * @param when The time to show in the time field. In the System.currentTimeMillis
2059 * timebase.
Joe Onorato46439ce2010-11-19 13:56:21 -08002060 *
2061 * @deprecated Use {@link Builder} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002062 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002063 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002064 public Notification(int icon, CharSequence tickerText, long when)
2065 {
2066 this.icon = icon;
2067 this.tickerText = tickerText;
2068 this.when = when;
Selim Cinekb85f36fd2016-04-20 18:46:36 -07002069 this.creationTime = System.currentTimeMillis();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002070 }
2071
2072 /**
2073 * Unflatten the notification from a parcel.
2074 */
Svet Ganovddb94882016-06-23 19:55:24 -07002075 @SuppressWarnings("unchecked")
2076 public Notification(Parcel parcel) {
2077 // IMPORTANT: Add unmarshaling code in readFromParcel as the pending
2078 // intents in extras are always written as the last entry.
2079 readFromParcelImpl(parcel);
2080 // Must be read last!
Felipe Lemedd85da62016-06-28 11:29:54 -07002081 allPendingIntents = (ArraySet<PendingIntent>) parcel.readArraySet(null);
Svet Ganovddb94882016-06-23 19:55:24 -07002082 }
2083
2084 private void readFromParcelImpl(Parcel parcel)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002085 {
2086 int version = parcel.readInt();
2087
Adrian Roosfb921842017-10-26 14:49:56 +02002088 mWhitelistToken = parcel.readStrongBinder();
2089 if (mWhitelistToken == null) {
2090 mWhitelistToken = processWhitelistToken;
Dianne Hackborn98305522017-05-05 17:53:53 -07002091 }
2092 // Propagate this token to all pending intents that are unmarshalled from the parcel.
Adrian Roosfb921842017-10-26 14:49:56 +02002093 parcel.setClassCookie(PendingIntent.class, mWhitelistToken);
Dianne Hackborn98305522017-05-05 17:53:53 -07002094
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002095 when = parcel.readLong();
Selim Cinekb85f36fd2016-04-20 18:46:36 -07002096 creationTime = parcel.readLong();
Dan Sandler3936e7a2015-05-19 20:59:12 -04002097 if (parcel.readInt() != 0) {
2098 mSmallIcon = Icon.CREATOR.createFromParcel(parcel);
Dan Sandler4e787062015-06-17 15:09:48 -04002099 if (mSmallIcon.getType() == Icon.TYPE_RESOURCE) {
2100 icon = mSmallIcon.getResId();
2101 }
Dan Sandler3936e7a2015-05-19 20:59:12 -04002102 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002103 number = parcel.readInt();
2104 if (parcel.readInt() != 0) {
2105 contentIntent = PendingIntent.CREATOR.createFromParcel(parcel);
2106 }
2107 if (parcel.readInt() != 0) {
2108 deleteIntent = PendingIntent.CREATOR.createFromParcel(parcel);
2109 }
2110 if (parcel.readInt() != 0) {
2111 tickerText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
2112 }
2113 if (parcel.readInt() != 0) {
Joe Onorato46439ce2010-11-19 13:56:21 -08002114 tickerView = RemoteViews.CREATOR.createFromParcel(parcel);
Joe Onoratoef1e7762010-09-17 18:38:38 -04002115 }
2116 if (parcel.readInt() != 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002117 contentView = RemoteViews.CREATOR.createFromParcel(parcel);
2118 }
Joe Onorato561d3852010-11-20 18:09:34 -08002119 if (parcel.readInt() != 0) {
Dan Sandlerd63f9322015-05-06 15:18:49 -04002120 mLargeIcon = Icon.CREATOR.createFromParcel(parcel);
Joe Onorato561d3852010-11-20 18:09:34 -08002121 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002122 defaults = parcel.readInt();
2123 flags = parcel.readInt();
2124 if (parcel.readInt() != 0) {
2125 sound = Uri.CREATOR.createFromParcel(parcel);
2126 }
2127
2128 audioStreamType = parcel.readInt();
John Spurlockc0650f022014-07-19 13:22:39 -04002129 if (parcel.readInt() != 0) {
2130 audioAttributes = AudioAttributes.CREATOR.createFromParcel(parcel);
2131 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002132 vibrate = parcel.createLongArray();
2133 ledARGB = parcel.readInt();
2134 ledOnMS = parcel.readInt();
2135 ledOffMS = parcel.readInt();
2136 iconLevel = parcel.readInt();
Daniel Sandlere46cbd32010-06-17 10:35:26 -04002137
2138 if (parcel.readInt() != 0) {
2139 fullScreenIntent = PendingIntent.CREATOR.createFromParcel(parcel);
2140 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002141
2142 priority = parcel.readInt();
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002143
John Spurlockfd7f1e02014-03-18 16:41:57 -04002144 category = parcel.readString();
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002145
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002146 mGroupKey = parcel.readString();
2147
2148 mSortKey = parcel.readString();
2149
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06002150 extras = Bundle.setDefusable(parcel.readBundle(), true); // may be null
Robin Leead7e72a2017-12-04 15:45:46 +01002151 fixDuplicateExtras();
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002152
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002153 actions = parcel.createTypedArray(Action.CREATOR); // may be null
2154
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002155 if (parcel.readInt() != 0) {
Daniel Sandlerf3b73432012-03-27 15:01:25 -04002156 bigContentView = RemoteViews.CREATOR.createFromParcel(parcel);
2157 }
Dan Sandler0bf2ed82013-12-21 23:33:41 -06002158
Chris Wren8fd39ec2014-02-27 17:43:26 -05002159 if (parcel.readInt() != 0) {
2160 headsUpContentView = RemoteViews.CREATOR.createFromParcel(parcel);
2161 }
2162
Dan Sandler0bf2ed82013-12-21 23:33:41 -06002163 visibility = parcel.readInt();
2164
2165 if (parcel.readInt() != 0) {
2166 publicVersion = Notification.CREATOR.createFromParcel(parcel);
2167 }
Dan Sandler26e81cf2014-05-06 10:01:27 -04002168
2169 color = parcel.readInt();
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04002170
2171 if (parcel.readInt() != 0) {
2172 mChannelId = parcel.readString();
2173 }
Julia Reynolds2a128742016-11-28 14:29:25 -05002174 mTimeout = parcel.readLong();
Julia Reynolds13d898c2017-02-02 12:22:05 -05002175
2176 if (parcel.readInt() != 0) {
2177 mShortcutId = parcel.readString();
2178 }
2179
2180 mBadgeIcon = parcel.readInt();
Julia Reynolds3aedded2017-03-31 14:42:09 -04002181
2182 if (parcel.readInt() != 0) {
2183 mSettingsText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
2184 }
Julia Reynoldsa79c3712017-04-21 10:29:57 -04002185
2186 mGroupAlertBehavior = parcel.readInt();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002187 }
2188
Andy Stadler110988c2010-12-03 14:29:16 -08002189 @Override
Joe Onorato18e69df2010-05-17 22:26:12 -07002190 public Notification clone() {
2191 Notification that = new Notification();
Daniel Sandler1a497d32013-04-18 14:52:45 -04002192 cloneInto(that, true);
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002193 return that;
2194 }
Joe Onorato18e69df2010-05-17 22:26:12 -07002195
Daniel Sandler1a497d32013-04-18 14:52:45 -04002196 /**
2197 * Copy all (or if heavy is false, all except Bitmaps and RemoteViews) members
2198 * of this into that.
2199 * @hide
2200 */
2201 public void cloneInto(Notification that, boolean heavy) {
Adrian Roosfb921842017-10-26 14:49:56 +02002202 that.mWhitelistToken = this.mWhitelistToken;
Joe Onorato18e69df2010-05-17 22:26:12 -07002203 that.when = this.when;
Selim Cinekb85f36fd2016-04-20 18:46:36 -07002204 that.creationTime = this.creationTime;
Dan Sandlerd63f9322015-05-06 15:18:49 -04002205 that.mSmallIcon = this.mSmallIcon;
Joe Onorato18e69df2010-05-17 22:26:12 -07002206 that.number = this.number;
2207
2208 // PendingIntents are global, so there's no reason (or way) to clone them.
2209 that.contentIntent = this.contentIntent;
2210 that.deleteIntent = this.deleteIntent;
Daniel Sandlere46cbd32010-06-17 10:35:26 -04002211 that.fullScreenIntent = this.fullScreenIntent;
Joe Onorato18e69df2010-05-17 22:26:12 -07002212
2213 if (this.tickerText != null) {
2214 that.tickerText = this.tickerText.toString();
2215 }
Daniel Sandler1a497d32013-04-18 14:52:45 -04002216 if (heavy && this.tickerView != null) {
Joe Onorato46439ce2010-11-19 13:56:21 -08002217 that.tickerView = this.tickerView.clone();
Joe Onoratoef1e7762010-09-17 18:38:38 -04002218 }
Daniel Sandler1a497d32013-04-18 14:52:45 -04002219 if (heavy && this.contentView != null) {
Joe Onorato18e69df2010-05-17 22:26:12 -07002220 that.contentView = this.contentView.clone();
2221 }
Dan Sandlerd63f9322015-05-06 15:18:49 -04002222 if (heavy && this.mLargeIcon != null) {
2223 that.mLargeIcon = this.mLargeIcon;
Joe Onorato561d3852010-11-20 18:09:34 -08002224 }
Jozef BABJAKa8b91832011-02-22 08:05:08 +01002225 that.iconLevel = this.iconLevel;
Joe Onorato18e69df2010-05-17 22:26:12 -07002226 that.sound = this.sound; // android.net.Uri is immutable
2227 that.audioStreamType = this.audioStreamType;
John Spurlockc0650f022014-07-19 13:22:39 -04002228 if (this.audioAttributes != null) {
2229 that.audioAttributes = new AudioAttributes.Builder(this.audioAttributes).build();
2230 }
Joe Onorato18e69df2010-05-17 22:26:12 -07002231
2232 final long[] vibrate = this.vibrate;
2233 if (vibrate != null) {
2234 final int N = vibrate.length;
2235 final long[] vib = that.vibrate = new long[N];
2236 System.arraycopy(vibrate, 0, vib, 0, N);
2237 }
2238
2239 that.ledARGB = this.ledARGB;
2240 that.ledOnMS = this.ledOnMS;
2241 that.ledOffMS = this.ledOffMS;
2242 that.defaults = this.defaults;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002243
Joe Onorato18e69df2010-05-17 22:26:12 -07002244 that.flags = this.flags;
2245
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002246 that.priority = this.priority;
Joe Malin8d40d042012-11-05 11:36:40 -08002247
John Spurlockfd7f1e02014-03-18 16:41:57 -04002248 that.category = this.category;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002249
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002250 that.mGroupKey = this.mGroupKey;
2251
2252 that.mSortKey = this.mSortKey;
2253
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002254 if (this.extras != null) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04002255 try {
2256 that.extras = new Bundle(this.extras);
2257 // will unparcel
2258 that.extras.size();
2259 } catch (BadParcelableException e) {
2260 Log.e(TAG, "could not unparcel extras from notification: " + this, e);
2261 that.extras = null;
2262 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002263 }
2264
Felipe Lemedd85da62016-06-28 11:29:54 -07002265 if (!ArrayUtils.isEmpty(allPendingIntents)) {
2266 that.allPendingIntents = new ArraySet<>(allPendingIntents);
Svet Ganovddb94882016-06-23 19:55:24 -07002267 }
2268
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002269 if (this.actions != null) {
2270 that.actions = new Action[this.actions.length];
2271 for(int i=0; i<this.actions.length; i++) {
liangweikang63b03b52017-03-16 19:22:15 +08002272 if ( this.actions[i] != null) {
2273 that.actions[i] = this.actions[i].clone();
2274 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002275 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002276 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002277
Daniel Sandler1a497d32013-04-18 14:52:45 -04002278 if (heavy && this.bigContentView != null) {
Daniel Sandlerf3b73432012-03-27 15:01:25 -04002279 that.bigContentView = this.bigContentView.clone();
2280 }
Daniel Sandler1a497d32013-04-18 14:52:45 -04002281
Chris Wren8fd39ec2014-02-27 17:43:26 -05002282 if (heavy && this.headsUpContentView != null) {
2283 that.headsUpContentView = this.headsUpContentView.clone();
2284 }
2285
Dan Sandler0bf2ed82013-12-21 23:33:41 -06002286 that.visibility = this.visibility;
2287
2288 if (this.publicVersion != null) {
2289 that.publicVersion = new Notification();
2290 this.publicVersion.cloneInto(that.publicVersion, heavy);
2291 }
2292
Dan Sandler26e81cf2014-05-06 10:01:27 -04002293 that.color = this.color;
2294
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04002295 that.mChannelId = this.mChannelId;
Julia Reynolds2a128742016-11-28 14:29:25 -05002296 that.mTimeout = this.mTimeout;
Julia Reynolds3aedded2017-03-31 14:42:09 -04002297 that.mShortcutId = this.mShortcutId;
2298 that.mBadgeIcon = this.mBadgeIcon;
2299 that.mSettingsText = this.mSettingsText;
Julia Reynoldsa79c3712017-04-21 10:29:57 -04002300 that.mGroupAlertBehavior = this.mGroupAlertBehavior;
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04002301
Daniel Sandler1a497d32013-04-18 14:52:45 -04002302 if (!heavy) {
2303 that.lightenPayload(); // will clean out extras
2304 }
2305 }
2306
2307 /**
2308 * Removes heavyweight parts of the Notification object for archival or for sending to
2309 * listeners when the full contents are not necessary.
2310 * @hide
2311 */
2312 public final void lightenPayload() {
2313 tickerView = null;
2314 contentView = null;
2315 bigContentView = null;
Chris Wren8fd39ec2014-02-27 17:43:26 -05002316 headsUpContentView = null;
Dan Sandlerd63f9322015-05-06 15:18:49 -04002317 mLargeIcon = null;
Dan Sandler50128532015-12-08 15:42:41 -05002318 if (extras != null && !extras.isEmpty()) {
2319 final Set<String> keyset = extras.keySet();
2320 final int N = keyset.size();
2321 final String[] keys = keyset.toArray(new String[N]);
2322 for (int i=0; i<N; i++) {
2323 final String key = keys[i];
Dmitri Plotnikov22281362017-01-30 11:16:21 -08002324 if (TvExtender.EXTRA_TV_EXTENDER.equals(key)) {
2325 continue;
2326 }
Dan Sandler50128532015-12-08 15:42:41 -05002327 final Object obj = extras.get(key);
2328 if (obj != null &&
2329 ( obj instanceof Parcelable
2330 || obj instanceof Parcelable[]
2331 || obj instanceof SparseArray
2332 || obj instanceof ArrayList)) {
2333 extras.remove(key);
2334 }
2335 }
Daniel Sandler1a497d32013-04-18 14:52:45 -04002336 }
Joe Onorato18e69df2010-05-17 22:26:12 -07002337 }
2338
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04002339 /**
2340 * Make sure this CharSequence is safe to put into a bundle, which basically
2341 * means it had better not be some custom Parcelable implementation.
2342 * @hide
2343 */
2344 public static CharSequence safeCharSequence(CharSequence cs) {
Christoph Studer535ec612014-09-03 15:47:47 +02002345 if (cs == null) return cs;
2346 if (cs.length() > MAX_CHARSEQUENCE_LENGTH) {
2347 cs = cs.subSequence(0, MAX_CHARSEQUENCE_LENGTH);
2348 }
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04002349 if (cs instanceof Parcelable) {
2350 Log.e(TAG, "warning: " + cs.getClass().getCanonicalName()
2351 + " instance is a custom Parcelable and not allowed in Notification");
2352 return cs.toString();
2353 }
Selim Cinek60a54252016-02-26 17:03:25 -08002354 return removeTextSizeSpans(cs);
2355 }
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04002356
Selim Cinek60a54252016-02-26 17:03:25 -08002357 private static CharSequence removeTextSizeSpans(CharSequence charSequence) {
2358 if (charSequence instanceof Spanned) {
2359 Spanned ss = (Spanned) charSequence;
2360 Object[] spans = ss.getSpans(0, ss.length(), Object.class);
2361 SpannableStringBuilder builder = new SpannableStringBuilder(ss.toString());
2362 for (Object span : spans) {
2363 Object resultSpan = span;
Selim Cinek89991a22016-03-07 19:51:54 -08002364 if (resultSpan instanceof CharacterStyle) {
2365 resultSpan = ((CharacterStyle) span).getUnderlying();
2366 }
2367 if (resultSpan instanceof TextAppearanceSpan) {
2368 TextAppearanceSpan originalSpan = (TextAppearanceSpan) resultSpan;
Selim Cinek60a54252016-02-26 17:03:25 -08002369 resultSpan = new TextAppearanceSpan(
2370 originalSpan.getFamily(),
2371 originalSpan.getTextStyle(),
2372 -1,
2373 originalSpan.getTextColor(),
2374 originalSpan.getLinkTextColor());
Selim Cinek89991a22016-03-07 19:51:54 -08002375 } else if (resultSpan instanceof RelativeSizeSpan
2376 || resultSpan instanceof AbsoluteSizeSpan) {
Selim Cinek60a54252016-02-26 17:03:25 -08002377 continue;
Selim Cinek89991a22016-03-07 19:51:54 -08002378 } else {
2379 resultSpan = span;
Selim Cinek60a54252016-02-26 17:03:25 -08002380 }
2381 builder.setSpan(resultSpan, ss.getSpanStart(span), ss.getSpanEnd(span),
2382 ss.getSpanFlags(span));
2383 }
2384 return builder;
2385 }
2386 return charSequence;
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04002387 }
2388
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002389 public int describeContents() {
2390 return 0;
2391 }
2392
2393 /**
Dan Sandler4e787062015-06-17 15:09:48 -04002394 * Flatten this notification into a parcel.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002395 */
Svet Ganovddb94882016-06-23 19:55:24 -07002396 public void writeToParcel(Parcel parcel, int flags) {
2397 // We need to mark all pending intents getting into the notification
2398 // system as being put there to later allow the notification ranker
2399 // to launch them and by doing so add the app to the battery saver white
2400 // list for a short period of time. The problem is that the system
2401 // cannot look into the extras as there may be parcelables there that
2402 // the platform does not know how to handle. To go around that we have
2403 // an explicit list of the pending intents in the extras bundle.
Felipe Lemedd85da62016-06-28 11:29:54 -07002404 final boolean collectPendingIntents = (allPendingIntents == null);
Svet Ganovddb94882016-06-23 19:55:24 -07002405 if (collectPendingIntents) {
2406 PendingIntent.setOnMarshaledListener(
2407 (PendingIntent intent, Parcel out, int outFlags) -> {
2408 if (parcel == out) {
Felipe Lemedd85da62016-06-28 11:29:54 -07002409 if (allPendingIntents == null) {
2410 allPendingIntents = new ArraySet<>();
Svet Ganovddb94882016-06-23 19:55:24 -07002411 }
Felipe Lemedd85da62016-06-28 11:29:54 -07002412 allPendingIntents.add(intent);
Svet Ganovddb94882016-06-23 19:55:24 -07002413 }
2414 });
2415 }
2416 try {
2417 // IMPORTANT: Add marshaling code in writeToParcelImpl as we
Julia Reynolds13d898c2017-02-02 12:22:05 -05002418 // want to intercept all pending events written to the parcel.
Svet Ganovddb94882016-06-23 19:55:24 -07002419 writeToParcelImpl(parcel, flags);
2420 // Must be written last!
Felipe Lemedd85da62016-06-28 11:29:54 -07002421 parcel.writeArraySet(allPendingIntents);
Svet Ganovddb94882016-06-23 19:55:24 -07002422 } finally {
2423 if (collectPendingIntents) {
2424 PendingIntent.setOnMarshaledListener(null);
2425 }
2426 }
2427 }
2428
2429 private void writeToParcelImpl(Parcel parcel, int flags) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002430 parcel.writeInt(1);
2431
Adrian Roosfb921842017-10-26 14:49:56 +02002432 parcel.writeStrongBinder(mWhitelistToken);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002433 parcel.writeLong(when);
Selim Cinekb85f36fd2016-04-20 18:46:36 -07002434 parcel.writeLong(creationTime);
Dan Sandler4e787062015-06-17 15:09:48 -04002435 if (mSmallIcon == null && icon != 0) {
2436 // you snuck an icon in here without using the builder; let's try to keep it
2437 mSmallIcon = Icon.createWithResource("", icon);
2438 }
Dan Sandler3936e7a2015-05-19 20:59:12 -04002439 if (mSmallIcon != null) {
2440 parcel.writeInt(1);
2441 mSmallIcon.writeToParcel(parcel, 0);
2442 } else {
2443 parcel.writeInt(0);
2444 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002445 parcel.writeInt(number);
2446 if (contentIntent != null) {
2447 parcel.writeInt(1);
2448 contentIntent.writeToParcel(parcel, 0);
2449 } else {
2450 parcel.writeInt(0);
2451 }
2452 if (deleteIntent != null) {
2453 parcel.writeInt(1);
2454 deleteIntent.writeToParcel(parcel, 0);
2455 } else {
2456 parcel.writeInt(0);
2457 }
2458 if (tickerText != null) {
2459 parcel.writeInt(1);
2460 TextUtils.writeToParcel(tickerText, parcel, flags);
2461 } else {
2462 parcel.writeInt(0);
2463 }
Joe Onorato46439ce2010-11-19 13:56:21 -08002464 if (tickerView != null) {
Joe Onoratoef1e7762010-09-17 18:38:38 -04002465 parcel.writeInt(1);
Joe Onorato46439ce2010-11-19 13:56:21 -08002466 tickerView.writeToParcel(parcel, 0);
Joe Onoratoef1e7762010-09-17 18:38:38 -04002467 } else {
2468 parcel.writeInt(0);
2469 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002470 if (contentView != null) {
2471 parcel.writeInt(1);
2472 contentView.writeToParcel(parcel, 0);
2473 } else {
2474 parcel.writeInt(0);
2475 }
Selim Cinek279fa862016-06-14 10:57:25 -07002476 if (mLargeIcon == null && largeIcon != null) {
2477 // you snuck an icon in here without using the builder; let's try to keep it
2478 mLargeIcon = Icon.createWithBitmap(largeIcon);
2479 }
Dan Sandlerd63f9322015-05-06 15:18:49 -04002480 if (mLargeIcon != null) {
Joe Onorato561d3852010-11-20 18:09:34 -08002481 parcel.writeInt(1);
Dan Sandlerd63f9322015-05-06 15:18:49 -04002482 mLargeIcon.writeToParcel(parcel, 0);
Joe Onorato561d3852010-11-20 18:09:34 -08002483 } else {
2484 parcel.writeInt(0);
2485 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002486
2487 parcel.writeInt(defaults);
2488 parcel.writeInt(this.flags);
2489
2490 if (sound != null) {
2491 parcel.writeInt(1);
2492 sound.writeToParcel(parcel, 0);
2493 } else {
2494 parcel.writeInt(0);
2495 }
2496 parcel.writeInt(audioStreamType);
John Spurlockc0650f022014-07-19 13:22:39 -04002497
2498 if (audioAttributes != null) {
2499 parcel.writeInt(1);
2500 audioAttributes.writeToParcel(parcel, 0);
2501 } else {
2502 parcel.writeInt(0);
2503 }
2504
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002505 parcel.writeLongArray(vibrate);
2506 parcel.writeInt(ledARGB);
2507 parcel.writeInt(ledOnMS);
2508 parcel.writeInt(ledOffMS);
2509 parcel.writeInt(iconLevel);
Daniel Sandlere46cbd32010-06-17 10:35:26 -04002510
2511 if (fullScreenIntent != null) {
2512 parcel.writeInt(1);
2513 fullScreenIntent.writeToParcel(parcel, 0);
2514 } else {
2515 parcel.writeInt(0);
2516 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002517
2518 parcel.writeInt(priority);
Joe Malin8d40d042012-11-05 11:36:40 -08002519
John Spurlockfd7f1e02014-03-18 16:41:57 -04002520 parcel.writeString(category);
Joe Malin8d40d042012-11-05 11:36:40 -08002521
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002522 parcel.writeString(mGroupKey);
2523
2524 parcel.writeString(mSortKey);
2525
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002526 parcel.writeBundle(extras); // null ok
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002527
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002528 parcel.writeTypedArray(actions, 0); // null ok
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002529
Daniel Sandlerf3b73432012-03-27 15:01:25 -04002530 if (bigContentView != null) {
2531 parcel.writeInt(1);
2532 bigContentView.writeToParcel(parcel, 0);
2533 } else {
2534 parcel.writeInt(0);
2535 }
Dan Sandler0bf2ed82013-12-21 23:33:41 -06002536
Chris Wren8fd39ec2014-02-27 17:43:26 -05002537 if (headsUpContentView != null) {
2538 parcel.writeInt(1);
2539 headsUpContentView.writeToParcel(parcel, 0);
2540 } else {
2541 parcel.writeInt(0);
2542 }
2543
Dan Sandler0bf2ed82013-12-21 23:33:41 -06002544 parcel.writeInt(visibility);
2545
2546 if (publicVersion != null) {
2547 parcel.writeInt(1);
2548 publicVersion.writeToParcel(parcel, 0);
2549 } else {
2550 parcel.writeInt(0);
2551 }
Dan Sandler26e81cf2014-05-06 10:01:27 -04002552
2553 parcel.writeInt(color);
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04002554
2555 if (mChannelId != null) {
2556 parcel.writeInt(1);
2557 parcel.writeString(mChannelId);
2558 } else {
2559 parcel.writeInt(0);
2560 }
Julia Reynolds2a128742016-11-28 14:29:25 -05002561 parcel.writeLong(mTimeout);
Julia Reynolds13d898c2017-02-02 12:22:05 -05002562
2563 if (mShortcutId != null) {
2564 parcel.writeInt(1);
2565 parcel.writeString(mShortcutId);
2566 } else {
2567 parcel.writeInt(0);
2568 }
2569
2570 parcel.writeInt(mBadgeIcon);
Julia Reynolds3aedded2017-03-31 14:42:09 -04002571
2572 if (mSettingsText != null) {
2573 parcel.writeInt(1);
2574 TextUtils.writeToParcel(mSettingsText, parcel, flags);
2575 } else {
2576 parcel.writeInt(0);
2577 }
Julia Reynoldsa79c3712017-04-21 10:29:57 -04002578
2579 parcel.writeInt(mGroupAlertBehavior);
Julia Reynoldsfc640012018-02-21 12:25:27 -05002580
2581 // mUsesStandardHeader is not written because it should be recomputed in listeners
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002582 }
2583
2584 /**
2585 * Parcelable.Creator that instantiates Notification objects
2586 */
2587 public static final Parcelable.Creator<Notification> CREATOR
2588 = new Parcelable.Creator<Notification>()
2589 {
2590 public Notification createFromParcel(Parcel parcel)
2591 {
2592 return new Notification(parcel);
2593 }
2594
2595 public Notification[] newArray(int size)
2596 {
2597 return new Notification[size];
2598 }
2599 };
2600
2601 /**
Robin Leead7e72a2017-12-04 15:45:46 +01002602 * Parcelling creates multiple copies of objects in {@code extras}. Fix them.
2603 * <p>
2604 * For backwards compatibility {@code extras} holds some references to "real" member data such
2605 * as {@link getLargeIcon()} which is mirrored by {@link #EXTRA_LARGE_ICON}. This is mostly
2606 * fine as long as the object stays in one process.
2607 * <p>
2608 * However, once the notification goes into a parcel each reference gets marshalled separately,
2609 * wasting memory. Especially with large images on Auto and TV, this is worth fixing.
2610 */
2611 private void fixDuplicateExtras() {
2612 if (extras != null) {
2613 fixDuplicateExtra(mSmallIcon, EXTRA_SMALL_ICON);
2614 fixDuplicateExtra(mLargeIcon, EXTRA_LARGE_ICON);
2615 }
2616 }
2617
2618 /**
2619 * If we find an extra that's exactly the same as one of the "real" fields but refers to a
2620 * separate object, replace it with the field's version to avoid holding duplicate copies.
2621 */
2622 private void fixDuplicateExtra(@Nullable Parcelable original, @NonNull String extraName) {
2623 if (original != null && extras.getParcelable(extraName) != null) {
2624 extras.putParcelable(extraName, original);
2625 }
2626 }
2627
2628 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002629 * Sets the {@link #contentView} field to be a view with the standard "Latest Event"
2630 * layout.
2631 *
2632 * <p>Uses the {@link #icon} and {@link #when} fields to set the icon and time fields
2633 * in the view.</p>
2634 * @param context The context for your application / activity.
2635 * @param contentTitle The title that goes in the expanded entry.
2636 * @param contentText The text that goes in the expanded entry.
2637 * @param contentIntent The intent to launch when the user clicks the expanded notification.
2638 * If this is an activity, it must include the
2639 * {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} flag, which requires
Scott Main7aee61f2011-02-08 11:25:01 -08002640 * that you take care of task management as described in the
2641 * <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back
2642 * Stack</a> document.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002643 *
Joe Onorato46439ce2010-11-19 13:56:21 -08002644 * @deprecated Use {@link Builder} instead.
Chris Wrena05db382015-06-24 15:18:34 -04002645 * @removed
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002646 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002647 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002648 public void setLatestEventInfo(Context context,
2649 CharSequence contentTitle, CharSequence contentText, PendingIntent contentIntent) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002650 if (context.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1){
2651 Log.e(TAG, "setLatestEventInfo() is deprecated and you should feel deprecated.",
2652 new Throwable());
2653 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002654
Selim Cinek4ac6f602016-06-13 15:47:03 -07002655 if (context.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N) {
2656 extras.putBoolean(EXTRA_SHOW_WHEN, true);
2657 }
2658
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002659 // ensure that any information already set directly is preserved
2660 final Notification.Builder builder = new Notification.Builder(context, this);
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002661
2662 // now apply the latestEventInfo fields
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002663 if (contentTitle != null) {
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002664 builder.setContentTitle(contentTitle);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002665 }
2666 if (contentText != null) {
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002667 builder.setContentText(contentText);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002668 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002669 builder.setContentIntent(contentIntent);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002670
2671 builder.build(); // callers expect this notification to be ready to use
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002672 }
2673
Julia Reynoldsda303542015-11-23 14:00:20 -05002674 /**
2675 * @hide
2676 */
2677 public static void addFieldsFromContext(Context context, Notification notification) {
Julia Reynoldse071abd2017-03-22 10:52:11 -04002678 addFieldsFromContext(context.getApplicationInfo(), notification);
Jeff Sharkey012bc7b2016-04-11 16:30:27 -06002679 }
2680
2681 /**
2682 * @hide
2683 */
Julia Reynoldse071abd2017-03-22 10:52:11 -04002684 public static void addFieldsFromContext(ApplicationInfo ai, Notification notification) {
Jeff Sharkey012bc7b2016-04-11 16:30:27 -06002685 notification.extras.putParcelable(EXTRA_BUILDER_APPLICATION_INFO, ai);
Julia Reynoldsda303542015-11-23 14:00:20 -05002686 }
2687
Yi Jin6b514142017-10-30 14:54:12 -07002688 /**
2689 * @hide
2690 */
2691 public void writeToProto(ProtoOutputStream proto, long fieldId) {
2692 long token = proto.start(fieldId);
2693 proto.write(NotificationProto.CHANNEL_ID, getChannelId());
2694 proto.write(NotificationProto.HAS_TICKER_TEXT, this.tickerText != null);
2695 proto.write(NotificationProto.FLAGS, this.flags);
2696 proto.write(NotificationProto.COLOR, this.color);
2697 proto.write(NotificationProto.CATEGORY, this.category);
2698 proto.write(NotificationProto.GROUP_KEY, this.mGroupKey);
2699 proto.write(NotificationProto.SORT_KEY, this.mSortKey);
2700 if (this.actions != null) {
2701 proto.write(NotificationProto.ACTION_LENGTH, this.actions.length);
2702 }
2703 if (this.visibility >= VISIBILITY_SECRET && this.visibility <= VISIBILITY_PUBLIC) {
2704 proto.write(NotificationProto.VISIBILITY, this.visibility);
2705 }
2706 if (publicVersion != null) {
2707 publicVersion.writeToProto(proto, NotificationProto.PUBLIC_VERSION);
2708 }
2709 proto.end(token);
2710 }
2711
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002712 @Override
2713 public String toString() {
2714 StringBuilder sb = new StringBuilder();
Julia Reynoldsb9e712e2017-04-17 10:31:03 -04002715 sb.append("Notification(channel=");
Julia Reynoldsbad42972017-04-25 13:52:49 -04002716 sb.append(getChannelId());
Julia Reynoldsb9e712e2017-04-17 10:31:03 -04002717 sb.append(" pri=");
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002718 sb.append(priority);
2719 sb.append(" contentView=");
Joe Onoratoc9596d62011-01-12 17:03:11 -08002720 if (contentView != null) {
2721 sb.append(contentView.getPackage());
2722 sb.append("/0x");
2723 sb.append(Integer.toHexString(contentView.getLayoutId()));
2724 } else {
2725 sb.append("null");
2726 }
2727 sb.append(" vibrate=");
Daniel Sandler6738eee2012-11-16 12:03:32 -05002728 if ((this.defaults & DEFAULT_VIBRATE) != 0) {
2729 sb.append("default");
2730 } else if (this.vibrate != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002731 int N = this.vibrate.length-1;
2732 sb.append("[");
2733 for (int i=0; i<N; i++) {
2734 sb.append(this.vibrate[i]);
2735 sb.append(',');
2736 }
Simon Schoar8cf97d92009-06-10 22:08:37 +02002737 if (N != -1) {
2738 sb.append(this.vibrate[N]);
2739 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002740 sb.append("]");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002741 } else {
2742 sb.append("null");
2743 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002744 sb.append(" sound=");
Daniel Sandler6738eee2012-11-16 12:03:32 -05002745 if ((this.defaults & DEFAULT_SOUND) != 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002746 sb.append("default");
Daniel Sandler6738eee2012-11-16 12:03:32 -05002747 } else if (this.sound != null) {
2748 sb.append(this.sound.toString());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002749 } else {
2750 sb.append("null");
2751 }
Chris Wren365b6d32015-07-16 10:39:26 -04002752 if (this.tickerText != null) {
2753 sb.append(" tick");
2754 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002755 sb.append(" defaults=0x");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002756 sb.append(Integer.toHexString(this.defaults));
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002757 sb.append(" flags=0x");
Daniel Sandlere46cbd32010-06-17 10:35:26 -04002758 sb.append(Integer.toHexString(this.flags));
Dan Sandler26e81cf2014-05-06 10:01:27 -04002759 sb.append(String.format(" color=0x%08x", this.color));
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002760 if (this.category != null) {
2761 sb.append(" category=");
2762 sb.append(this.category);
2763 }
2764 if (this.mGroupKey != null) {
2765 sb.append(" groupKey=");
2766 sb.append(this.mGroupKey);
2767 }
2768 if (this.mSortKey != null) {
2769 sb.append(" sortKey=");
2770 sb.append(this.mSortKey);
2771 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002772 if (actions != null) {
Dan Sandler1b718782014-07-18 12:43:45 -04002773 sb.append(" actions=");
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002774 sb.append(actions.length);
Dan Sandler1b718782014-07-18 12:43:45 -04002775 }
2776 sb.append(" vis=");
2777 sb.append(visibilityToString(this.visibility));
2778 if (this.publicVersion != null) {
2779 sb.append(" publicVersion=");
2780 sb.append(publicVersion.toString());
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002781 }
2782 sb.append(")");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002783 return sb.toString();
2784 }
Joe Onorato46439ce2010-11-19 13:56:21 -08002785
Dan Sandler1b718782014-07-18 12:43:45 -04002786 /**
2787 * {@hide}
2788 */
2789 public static String visibilityToString(int vis) {
2790 switch (vis) {
2791 case VISIBILITY_PRIVATE:
2792 return "PRIVATE";
2793 case VISIBILITY_PUBLIC:
2794 return "PUBLIC";
2795 case VISIBILITY_SECRET:
2796 return "SECRET";
2797 default:
2798 return "UNKNOWN(" + String.valueOf(vis) + ")";
2799 }
2800 }
2801
Joe Onoratocb109a02011-01-18 17:57:41 -08002802 /**
John Spurlock1d881a12015-03-18 19:21:54 -04002803 * {@hide}
2804 */
2805 public static String priorityToString(@Priority int pri) {
2806 switch (pri) {
2807 case PRIORITY_MIN:
2808 return "MIN";
2809 case PRIORITY_LOW:
2810 return "LOW";
2811 case PRIORITY_DEFAULT:
2812 return "DEFAULT";
2813 case PRIORITY_HIGH:
2814 return "HIGH";
2815 case PRIORITY_MAX:
2816 return "MAX";
2817 default:
2818 return "UNKNOWN(" + String.valueOf(pri) + ")";
2819 }
2820 }
2821
Julia Reynolds6ad0aec2017-07-05 08:47:03 -04002822 /**
2823 * @hide
2824 */
2825 public boolean hasCompletedProgress() {
2826 // not a progress notification; can't be complete
2827 if (!extras.containsKey(EXTRA_PROGRESS)
2828 || !extras.containsKey(EXTRA_PROGRESS_MAX)) {
2829 return false;
2830 }
2831 // many apps use max 0 for 'indeterminate'; not complete
2832 if (extras.getInt(EXTRA_PROGRESS_MAX) == 0) {
2833 return false;
2834 }
2835 return extras.getInt(EXTRA_PROGRESS) == extras.getInt(EXTRA_PROGRESS_MAX);
2836 }
2837
Jeff Sharkey000ce802017-04-29 13:13:27 -06002838 /** @removed */
2839 @Deprecated
Julia Reynolds37856052016-11-11 09:20:07 -05002840 public String getChannel() {
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04002841 return mChannelId;
2842 }
2843
2844 /**
Julia Reynoldsbad42972017-04-25 13:52:49 -04002845 * Returns the id of the channel this notification posts to.
2846 */
2847 public String getChannelId() {
2848 return mChannelId;
2849 }
2850
Jeff Sharkey000ce802017-04-29 13:13:27 -06002851 /** @removed */
2852 @Deprecated
Julia Reynolds2a128742016-11-28 14:29:25 -05002853 public long getTimeout() {
2854 return mTimeout;
2855 }
2856
2857 /**
Julia Reynoldsbad42972017-04-25 13:52:49 -04002858 * Returns the duration from posting after which this notification should be canceled by the
2859 * system, if it's not canceled already.
2860 */
2861 public long getTimeoutAfter() {
2862 return mTimeout;
2863 }
2864
2865 /**
Julia Reynoldse071abd2017-03-22 10:52:11 -04002866 * Returns what icon should be shown for this notification if it is being displayed in a
2867 * Launcher that supports badging. Will be one of {@link #BADGE_ICON_NONE},
2868 * {@link #BADGE_ICON_SMALL}, or {@link #BADGE_ICON_LARGE}.
2869 */
2870 public int getBadgeIconType() {
2871 return mBadgeIcon;
2872 }
2873
2874 /**
Julia Reynolds13d898c2017-02-02 12:22:05 -05002875 * Returns the {@link ShortcutInfo#getId() id} that this notification supersedes, if any.
Julia Reynoldsbad42972017-04-25 13:52:49 -04002876 *
2877 * <p>Used by some Launchers that display notification content to hide shortcuts that duplicate
2878 * notifications.
Julia Reynolds13d898c2017-02-02 12:22:05 -05002879 */
2880 public String getShortcutId() {
2881 return mShortcutId;
2882 }
2883
Julia Reynolds3aedded2017-03-31 14:42:09 -04002884
2885 /**
2886 * Returns the settings text provided to {@link Builder#setSettingsText(CharSequence)}.
2887 */
2888 public CharSequence getSettingsText() {
2889 return mSettingsText;
2890 }
2891
Julia Reynolds13d898c2017-02-02 12:22:05 -05002892 /**
Julia Reynoldsa79c3712017-04-21 10:29:57 -04002893 * Returns which type of notifications in a group are responsible for audibly alerting the
2894 * user. See {@link #GROUP_ALERT_ALL}, {@link #GROUP_ALERT_CHILDREN},
2895 * {@link #GROUP_ALERT_SUMMARY}.
2896 */
2897 public @GroupAlertBehavior int getGroupAlertBehavior() {
2898 return mGroupAlertBehavior;
2899 }
2900
2901 /**
Dan Sandlerd63f9322015-05-06 15:18:49 -04002902 * The small icon representing this notification in the status bar and content view.
2903 *
2904 * @return the small icon representing this notification.
2905 *
2906 * @see Builder#getSmallIcon()
2907 * @see Builder#setSmallIcon(Icon)
2908 */
2909 public Icon getSmallIcon() {
2910 return mSmallIcon;
2911 }
2912
2913 /**
2914 * Used when notifying to clean up legacy small icons.
2915 * @hide
2916 */
2917 public void setSmallIcon(Icon icon) {
2918 mSmallIcon = icon;
2919 }
2920
2921 /**
2922 * The large icon shown in this notification's content view.
2923 * @see Builder#getLargeIcon()
2924 * @see Builder#setLargeIcon(Icon)
2925 */
2926 public Icon getLargeIcon() {
2927 return mLargeIcon;
2928 }
2929
2930 /**
Christoph Studer4600f9b2014-07-22 22:44:43 +02002931 * @hide
2932 */
Christoph Studerc8db24b2014-07-25 17:50:30 +02002933 public boolean isGroupSummary() {
2934 return mGroupKey != null && (flags & FLAG_GROUP_SUMMARY) != 0;
2935 }
2936
2937 /**
2938 * @hide
2939 */
2940 public boolean isGroupChild() {
2941 return mGroupKey != null && (flags & FLAG_GROUP_SUMMARY) == 0;
2942 }
2943
2944 /**
Julia Reynolds30203152017-05-26 13:36:31 -04002945 * @hide
2946 */
2947 public boolean suppressAlertingDueToGrouping() {
2948 if (isGroupSummary()
2949 && getGroupAlertBehavior() == Notification.GROUP_ALERT_CHILDREN) {
2950 return true;
2951 } else if (isGroupChild()
2952 && getGroupAlertBehavior() == Notification.GROUP_ALERT_SUMMARY) {
2953 return true;
2954 }
2955 return false;
2956 }
2957
2958 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002959 * Builder class for {@link Notification} objects.
Joe Malin8d40d042012-11-05 11:36:40 -08002960 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002961 * Provides a convenient way to set the various fields of a {@link Notification} and generate
Scott Main183bf112012-08-13 19:12:13 -07002962 * content views using the platform's notification layout template. If your app supports
2963 * versions of Android as old as API level 4, you can instead use
2964 * {@link android.support.v4.app.NotificationCompat.Builder NotificationCompat.Builder},
2965 * available in the <a href="{@docRoot}tools/extras/support-library.html">Android Support
2966 * library</a>.
Joe Malin8d40d042012-11-05 11:36:40 -08002967 *
Scott Main183bf112012-08-13 19:12:13 -07002968 * <p>Example:
Joe Malin8d40d042012-11-05 11:36:40 -08002969 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002970 * <pre class="prettyprint">
Scott Main183bf112012-08-13 19:12:13 -07002971 * Notification noti = new Notification.Builder(mContext)
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002972 * .setContentTitle(&quot;New mail from &quot; + sender.toString())
2973 * .setContentText(subject)
2974 * .setSmallIcon(R.drawable.new_mail)
2975 * .setLargeIcon(aBitmap)
Chris Wrenfbd96ba2012-05-01 12:03:58 -04002976 * .build();
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002977 * </pre>
Joe Onoratocb109a02011-01-18 17:57:41 -08002978 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002979 public static class Builder {
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05002980 /**
2981 * @hide
2982 */
2983 public static final String EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT =
2984 "android.rebuild.contentViewActionCount";
2985 /**
2986 * @hide
2987 */
2988 public static final String EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT
2989 = "android.rebuild.bigViewActionCount";
2990 /**
2991 * @hide
2992 */
2993 public static final String EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT
2994 = "android.rebuild.hudViewActionCount";
2995
Daniel Sandler602ad1c2012-06-12 16:06:27 -04002996 private static final int MAX_ACTION_BUTTONS = 3;
Daniel Sandler8680bf82012-05-15 16:52:52 -04002997
Selim Cinek6743c0b2017-01-18 18:24:01 -08002998 private static final boolean USE_ONLY_TITLE_IN_LOW_PRIORITY_SUMMARY =
2999 SystemProperties.getBoolean("notifications.only_title", true);
3000
Selim Cinek389edcd2017-05-11 19:16:44 -07003001 /**
3002 * The lightness difference that has to be added to the primary text color to obtain the
3003 * secondary text color when the background is light.
3004 */
3005 private static final int LIGHTNESS_TEXT_DIFFERENCE_LIGHT = 20;
3006
3007 /**
3008 * The lightness difference that has to be added to the primary text color to obtain the
3009 * secondary text color when the background is dark.
3010 * A bit less then the above value, since it looks better on dark backgrounds.
3011 */
3012 private static final int LIGHTNESS_TEXT_DIFFERENCE_DARK = -10;
3013
Joe Onorato46439ce2010-11-19 13:56:21 -08003014 private Context mContext;
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003015 private Notification mN;
3016 private Bundle mUserExtras = new Bundle();
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003017 private Style mStyle;
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003018 private ArrayList<Action> mActions = new ArrayList<Action>(MAX_ACTION_BUTTONS);
Selim Cineke7238dd2017-12-14 17:48:32 -08003019 private ArrayList<Person> mPersonList = new ArrayList<>();
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003020 private NotificationColorUtil mColorUtil;
Selim Cinek7b9605b2017-01-19 17:36:00 -08003021 private boolean mIsLegacy;
3022 private boolean mIsLegacyInitialized;
Christoph Studer7ac80e62014-08-04 16:01:57 +02003023
3024 /**
Adrian Roos4ff3b122016-02-01 12:26:13 -08003025 * Caches a contrast-enhanced version of {@link #mCachedContrastColorIsFor}.
3026 */
3027 private int mCachedContrastColor = COLOR_INVALID;
3028 private int mCachedContrastColorIsFor = COLOR_INVALID;
Adrian Roos487374f2017-01-11 15:48:14 -08003029 /**
3030 * Caches a ambient version of {@link #mCachedContrastColorIsFor}.
3031 */
3032 private int mCachedAmbientColor = COLOR_INVALID;
3033 private int mCachedAmbientColorIsFor = COLOR_INVALID;
Adrian Roos4ff3b122016-02-01 12:26:13 -08003034
3035 /**
Adrian Roos70d7aa32017-01-11 15:39:06 -08003036 * Caches an instance of StandardTemplateParams. Note that this may have been used before,
3037 * so make sure to call {@link StandardTemplateParams#reset()} before using it.
3038 */
3039 StandardTemplateParams mParams = new StandardTemplateParams();
Selim Cinek7b9605b2017-01-19 17:36:00 -08003040 private int mTextColorsAreForBackground = COLOR_INVALID;
3041 private int mPrimaryTextColor = COLOR_INVALID;
3042 private int mSecondaryTextColor = COLOR_INVALID;
3043 private int mActionBarColor = COLOR_INVALID;
Selim Cinek5fb73f82017-04-20 16:55:38 -07003044 private int mBackgroundColor = COLOR_INVALID;
3045 private int mForegroundColor = COLOR_INVALID;
Selim Cinekac5f0272017-05-02 16:05:41 -07003046 private int mBackgroundColorHint = COLOR_INVALID;
Selim Cinek5d6ef8d2017-05-18 22:16:00 -07003047 /**
3048 * A temporary location where actions are stored. If != null the view originally has action
3049 * but doesn't have any for this inflation.
3050 */
3051 private ArrayList<Action> mOriginalActions;
Selim Cineka7679b62017-05-10 16:33:25 -07003052 private boolean mRebuildStyledRemoteViews;
Adrian Roos70d7aa32017-01-11 15:39:06 -08003053
Anthony Chenad4d1582017-04-10 16:07:58 -07003054 private boolean mTintActionButtons;
3055 private boolean mInNightMode;
3056
Adrian Roos70d7aa32017-01-11 15:39:06 -08003057 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003058 * Constructs a new Builder with the defaults:
Joe Onoratocb109a02011-01-18 17:57:41 -08003059 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003060 * @param context
3061 * A {@link Context} that will be used by the Builder to construct the
3062 * RemoteViews. The Context will not be held past the lifetime of this Builder
3063 * object.
Geoffrey Pitsch5caa2762017-01-12 09:35:54 -05003064 * @param channelId
3065 * The constructed Notification will be posted on this
3066 * {@link NotificationChannel}. To use a NotificationChannel, it must first be
3067 * created using {@link NotificationManager#createNotificationChannel}.
Joe Onoratocb109a02011-01-18 17:57:41 -08003068 */
Geoffrey Pitsch5caa2762017-01-12 09:35:54 -05003069 public Builder(Context context, String channelId) {
3070 this(context, (Notification) null);
3071 mN.mChannelId = channelId;
3072 }
3073
3074 /**
3075 * @deprecated use {@link Notification.Builder#Notification.Builder(Context, String)}
3076 * instead. All posted Notifications must specify a NotificationChannel Id.
3077 */
3078 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08003079 public Builder(Context context) {
Geoffrey Pitsch5caa2762017-01-12 09:35:54 -05003080 this(context, (Notification) null);
Joe Onorato46439ce2010-11-19 13:56:21 -08003081 }
3082
Joe Onoratocb109a02011-01-18 17:57:41 -08003083 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003084 * @hide
Christoph Studer4600f9b2014-07-22 22:44:43 +02003085 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003086 public Builder(Context context, Notification toAdopt) {
3087 mContext = context;
Anthony Chenad4d1582017-04-10 16:07:58 -07003088 Resources res = mContext.getResources();
3089 mTintActionButtons = res.getBoolean(R.bool.config_tintNotificationActionButtons);
3090
3091 if (res.getBoolean(R.bool.config_enableNightMode)) {
3092 Configuration currentConfig = res.getConfiguration();
3093 mInNightMode = (currentConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK)
3094 == Configuration.UI_MODE_NIGHT_YES;
3095 }
Christoph Studer4600f9b2014-07-22 22:44:43 +02003096
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003097 if (toAdopt == null) {
3098 mN = new Notification();
Selim Cinek0ff1ce602016-04-05 18:27:16 -07003099 if (context.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N) {
3100 mN.extras.putBoolean(EXTRA_SHOW_WHEN, true);
3101 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003102 mN.priority = PRIORITY_DEFAULT;
3103 mN.visibility = VISIBILITY_PRIVATE;
3104 } else {
3105 mN = toAdopt;
3106 if (mN.actions != null) {
3107 Collections.addAll(mActions, mN.actions);
Christoph Studer4600f9b2014-07-22 22:44:43 +02003108 }
3109
Selim Cineke7238dd2017-12-14 17:48:32 -08003110 if (mN.extras.containsKey(EXTRA_PEOPLE_LIST)) {
3111 ArrayList<Person> people = mN.extras.getParcelableArrayList(EXTRA_PEOPLE_LIST);
3112 mPersonList.addAll(people);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003113 }
3114
Selim Cinek4ac6f602016-06-13 15:47:03 -07003115 if (mN.getSmallIcon() == null && mN.icon != 0) {
3116 setSmallIcon(mN.icon);
3117 }
3118
3119 if (mN.getLargeIcon() == null && mN.largeIcon != null) {
3120 setLargeIcon(mN.largeIcon);
3121 }
3122
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003123 String templateClass = mN.extras.getString(EXTRA_TEMPLATE);
3124 if (!TextUtils.isEmpty(templateClass)) {
3125 final Class<? extends Style> styleClass
3126 = getNotificationStyleClass(templateClass);
3127 if (styleClass == null) {
3128 Log.d(TAG, "Unknown style class: " + templateClass);
3129 } else {
3130 try {
Adrian Roosc1a80b02016-04-05 14:54:55 -07003131 final Constructor<? extends Style> ctor =
3132 styleClass.getDeclaredConstructor();
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003133 ctor.setAccessible(true);
3134 final Style style = ctor.newInstance();
3135 style.restoreFromExtras(mN.extras);
3136
3137 if (style != null) {
3138 setStyle(style);
3139 }
3140 } catch (Throwable t) {
3141 Log.e(TAG, "Could not create Style", t);
3142 }
3143 }
3144 }
3145
3146 }
3147 }
3148
3149 private NotificationColorUtil getColorUtil() {
Selim Cinek99104832017-01-25 14:47:33 -08003150 if (mColorUtil == null) {
3151 mColorUtil = NotificationColorUtil.getInstance(mContext);
Christoph Studer4600f9b2014-07-22 22:44:43 +02003152 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003153 return mColorUtil;
Christoph Studer4600f9b2014-07-22 22:44:43 +02003154 }
3155
3156 /**
Julia Reynolds13d898c2017-02-02 12:22:05 -05003157 * If this notification is duplicative of a Launcher shortcut, sets the
3158 * {@link ShortcutInfo#getId() id} of the shortcut, in case the Launcher wants to hide
3159 * the shortcut.
3160 *
Julia Reynoldsbad42972017-04-25 13:52:49 -04003161 * This field will be ignored by Launchers that don't support badging, don't show
3162 * notification content, or don't show {@link android.content.pm.ShortcutManager shortcuts}.
Julia Reynolds13d898c2017-02-02 12:22:05 -05003163 *
3164 * @param shortcutId the {@link ShortcutInfo#getId() id} of the shortcut this notification
3165 * supersedes
3166 */
3167 public Builder setShortcutId(String shortcutId) {
3168 mN.mShortcutId = shortcutId;
3169 return this;
3170 }
3171
3172 /**
Julia Reynoldse071abd2017-03-22 10:52:11 -04003173 * Sets which icon to display as a badge for this notification.
3174 *
3175 * Must be one of {@link #BADGE_ICON_NONE}, {@link #BADGE_ICON_SMALL},
3176 * {@link #BADGE_ICON_LARGE}.
3177 *
3178 * Note: This value might be ignored, for launchers that don't support badge icons.
3179 */
Julia Reynolds612beb22017-03-30 10:48:30 -04003180 public Builder setBadgeIconType(int icon) {
Julia Reynoldse071abd2017-03-22 10:52:11 -04003181 mN.mBadgeIcon = icon;
3182 return this;
3183 }
3184
3185 /**
Julia Reynoldsa79c3712017-04-21 10:29:57 -04003186 * Sets the group alert behavior for this notification. Use this method to mute this
3187 * notification if alerts for this notification's group should be handled by a different
3188 * notification. This is only applicable for notifications that belong to a
Julia Reynolds399d9bf2017-08-11 12:52:14 -04003189 * {@link #setGroup(String) group}. This must be called on all notifications you want to
3190 * mute. For example, if you want only the summary of your group to make noise, all
3191 * children in the group should have the group alert behavior {@link #GROUP_ALERT_SUMMARY}.
Julia Reynoldsa79c3712017-04-21 10:29:57 -04003192 *
3193 * <p> The default value is {@link #GROUP_ALERT_ALL}.</p>
3194 */
3195 public Builder setGroupAlertBehavior(@GroupAlertBehavior int groupAlertBehavior) {
3196 mN.mGroupAlertBehavior = groupAlertBehavior;
3197 return this;
3198 }
3199
Jeff Sharkey000ce802017-04-29 13:13:27 -06003200 /** @removed */
3201 @Deprecated
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04003202 public Builder setChannel(String channelId) {
3203 mN.mChannelId = channelId;
3204 return this;
3205 }
3206
3207 /**
Julia Reynoldsbad42972017-04-25 13:52:49 -04003208 * Specifies the channel the notification should be delivered on.
3209 */
3210 public Builder setChannelId(String channelId) {
3211 mN.mChannelId = channelId;
3212 return this;
3213 }
3214
Jeff Sharkey000ce802017-04-29 13:13:27 -06003215 /** @removed */
3216 @Deprecated
Julia Reynolds50989772017-02-23 14:32:16 -05003217 public Builder setTimeout(long durationMs) {
3218 mN.mTimeout = durationMs;
Julia Reynolds2a128742016-11-28 14:29:25 -05003219 return this;
3220 }
3221
3222 /**
Julia Reynoldsbad42972017-04-25 13:52:49 -04003223 * Specifies a duration in milliseconds after which this notification should be canceled,
3224 * if it is not already canceled.
3225 */
3226 public Builder setTimeoutAfter(long durationMs) {
3227 mN.mTimeout = durationMs;
3228 return this;
3229 }
3230
3231 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003232 * Add a timestamp pertaining to the notification (usually the time the event occurred).
Selim Cinek0ff1ce602016-04-05 18:27:16 -07003233 *
3234 * For apps targeting {@link android.os.Build.VERSION_CODES#N} and above, this time is not
3235 * shown anymore by default and must be opted into by using
3236 * {@link android.app.Notification.Builder#setShowWhen(boolean)}
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003237 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003238 * @see Notification#when
Joe Onoratocb109a02011-01-18 17:57:41 -08003239 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003240 public Builder setWhen(long when) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003241 mN.when = when;
Joe Onorato46439ce2010-11-19 13:56:21 -08003242 return this;
3243 }
3244
Joe Onoratocb109a02011-01-18 17:57:41 -08003245 /**
Griff Hazen50c11652014-05-16 09:46:31 -07003246 * Control whether the timestamp set with {@link #setWhen(long) setWhen} is shown
Daniel Sandler0c890492012-09-12 17:23:10 -07003247 * in the content view.
Selim Cinek0ff1ce602016-04-05 18:27:16 -07003248 * For apps targeting {@link android.os.Build.VERSION_CODES#N} and above, this defaults to
3249 * {@code false}. For earlier apps, the default is {@code true}.
Daniel Sandler0c890492012-09-12 17:23:10 -07003250 */
3251 public Builder setShowWhen(boolean show) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003252 mN.extras.putBoolean(EXTRA_SHOW_WHEN, show);
Daniel Sandler0c890492012-09-12 17:23:10 -07003253 return this;
3254 }
3255
3256 /**
Daniel Sandlerd33b8032012-05-10 11:41:48 -04003257 * Show the {@link Notification#when} field as a stopwatch.
Joe Malin8d40d042012-11-05 11:36:40 -08003258 *
3259 * Instead of presenting <code>when</code> as a timestamp, the notification will show an
Daniel Sandlerd33b8032012-05-10 11:41:48 -04003260 * automatically updating display of the minutes and seconds since <code>when</code>.
Daniel Sandlera2985ed2012-04-03 16:42:00 -04003261 *
Daniel Sandlerd33b8032012-05-10 11:41:48 -04003262 * Useful when showing an elapsed time (like an ongoing phone call).
3263 *
Selim Cinek81c23aa2016-02-25 16:23:13 -08003264 * The counter can also be set to count down to <code>when</code> when using
Adrian Roos96b7e202016-05-17 13:50:38 -07003265 * {@link #setChronometerCountDown(boolean)}.
Selim Cinek81c23aa2016-02-25 16:23:13 -08003266 *
Daniel Sandlerd33b8032012-05-10 11:41:48 -04003267 * @see android.widget.Chronometer
Daniel Sandlera2985ed2012-04-03 16:42:00 -04003268 * @see Notification#when
Adrian Roos96b7e202016-05-17 13:50:38 -07003269 * @see #setChronometerCountDown(boolean)
Daniel Sandlera2985ed2012-04-03 16:42:00 -04003270 */
3271 public Builder setUsesChronometer(boolean b) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003272 mN.extras.putBoolean(EXTRA_SHOW_CHRONOMETER, b);
Daniel Sandlera2985ed2012-04-03 16:42:00 -04003273 return this;
3274 }
3275
3276 /**
Selim Cinek81c23aa2016-02-25 16:23:13 -08003277 * Sets the Chronometer to count down instead of counting up.
3278 *
3279 * <p>This is only relevant if {@link #setUsesChronometer(boolean)} has been set to true.
3280 * If it isn't set the chronometer will count up.
3281 *
3282 * @see #setUsesChronometer(boolean)
3283 */
Adrian Roos96b7e202016-05-17 13:50:38 -07003284 public Builder setChronometerCountDown(boolean countDown) {
3285 mN.extras.putBoolean(EXTRA_CHRONOMETER_COUNT_DOWN, countDown);
Selim Cinek81c23aa2016-02-25 16:23:13 -08003286 return this;
3287 }
3288
3289 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003290 * Set the small icon resource, which will be used to represent the notification in the
3291 * status bar.
Joe Onoratocb109a02011-01-18 17:57:41 -08003292 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003293
3294 * The platform template for the expanded view will draw this icon in the left, unless a
3295 * {@link #setLargeIcon(Bitmap) large icon} has also been specified, in which case the small
3296 * icon will be moved to the right-hand side.
3297 *
3298
3299 * @param icon
3300 * A resource ID in the application's package of the drawable to use.
3301 * @see Notification#icon
Joe Onoratocb109a02011-01-18 17:57:41 -08003302 */
Tor Norbye7b9c9122013-05-30 16:48:33 -07003303 public Builder setSmallIcon(@DrawableRes int icon) {
Dan Sandlerd63f9322015-05-06 15:18:49 -04003304 return setSmallIcon(icon != 0
3305 ? Icon.createWithResource(mContext, icon)
3306 : null);
Joe Onorato46439ce2010-11-19 13:56:21 -08003307 }
3308
Joe Onoratocb109a02011-01-18 17:57:41 -08003309 /**
3310 * A variant of {@link #setSmallIcon(int) setSmallIcon(int)} that takes an additional
3311 * level parameter for when the icon is a {@link android.graphics.drawable.LevelListDrawable
3312 * LevelListDrawable}.
3313 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003314 * @param icon A resource ID in the application's package of the drawable to use.
Joe Onoratocb109a02011-01-18 17:57:41 -08003315 * @param level The level to use for the icon.
3316 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003317 * @see Notification#icon
3318 * @see Notification#iconLevel
Joe Onoratocb109a02011-01-18 17:57:41 -08003319 */
Tor Norbye7b9c9122013-05-30 16:48:33 -07003320 public Builder setSmallIcon(@DrawableRes int icon, int level) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003321 mN.iconLevel = level;
Dan Sandlerd63f9322015-05-06 15:18:49 -04003322 return setSmallIcon(icon);
3323 }
3324
3325 /**
3326 * Set the small icon, which will be used to represent the notification in the
3327 * status bar and content view (unless overriden there by a
3328 * {@link #setLargeIcon(Bitmap) large icon}).
3329 *
3330 * @param icon An Icon object to use.
3331 * @see Notification#icon
3332 */
3333 public Builder setSmallIcon(Icon icon) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003334 mN.setSmallIcon(icon);
3335 if (icon != null && icon.getType() == Icon.TYPE_RESOURCE) {
3336 mN.icon = icon.getResId();
3337 }
Joe Onorato46439ce2010-11-19 13:56:21 -08003338 return this;
3339 }
3340
Joe Onoratocb109a02011-01-18 17:57:41 -08003341 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003342 * Set the first line of text in the platform notification template.
Joe Onoratocb109a02011-01-18 17:57:41 -08003343 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003344 public Builder setContentTitle(CharSequence title) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003345 mN.extras.putCharSequence(EXTRA_TITLE, safeCharSequence(title));
Joe Onorato46439ce2010-11-19 13:56:21 -08003346 return this;
3347 }
3348
Joe Onoratocb109a02011-01-18 17:57:41 -08003349 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003350 * Set the second line of text in the platform notification template.
Joe Onoratocb109a02011-01-18 17:57:41 -08003351 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003352 public Builder setContentText(CharSequence text) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003353 mN.extras.putCharSequence(EXTRA_TEXT, safeCharSequence(text));
Joe Onorato46439ce2010-11-19 13:56:21 -08003354 return this;
3355 }
3356
Joe Onoratocb109a02011-01-18 17:57:41 -08003357 /**
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07003358 * This provides some additional information that is displayed in the notification. No
3359 * guarantees are given where exactly it is displayed.
3360 *
3361 * <p>This information should only be provided if it provides an essential
3362 * benefit to the understanding of the notification. The more text you provide the
3363 * less readable it becomes. For example, an email client should only provide the account
3364 * name here if more than one email account has been added.</p>
3365 *
3366 * <p>As of {@link android.os.Build.VERSION_CODES#N} this information is displayed in the
3367 * notification header area.
3368 *
3369 * On Android versions before {@link android.os.Build.VERSION_CODES#N}
3370 * this will be shown in the third line of text in the platform notification template.
3371 * You should not be using {@link #setProgress(int, int, boolean)} at the
3372 * same time on those versions; they occupy the same place.
3373 * </p>
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003374 */
3375 public Builder setSubText(CharSequence text) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003376 mN.extras.putCharSequence(EXTRA_SUB_TEXT, safeCharSequence(text));
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003377 return this;
3378 }
3379
3380 /**
Julia Reynolds3aedded2017-03-31 14:42:09 -04003381 * Provides text that will appear as a link to your application's settings.
3382 *
3383 * <p>This text does not appear within notification {@link Style templates} but may
3384 * appear when the user uses an affordance to learn more about the notification.
3385 * Additionally, this text will not appear unless you provide a valid link target by
3386 * handling {@link #INTENT_CATEGORY_NOTIFICATION_PREFERENCES}.
3387 *
3388 * <p>This text is meant to be concise description about what the user can customize
3389 * when they click on this link. The recommended maximum length is 40 characters.
3390 * @param text
3391 * @return
3392 */
3393 public Builder setSettingsText(CharSequence text) {
3394 mN.mSettingsText = safeCharSequence(text);
3395 return this;
3396 }
3397
3398 /**
Adrian Roose458aa82015-12-08 16:17:19 -08003399 * Set the remote input history.
3400 *
3401 * This should be set to the most recent inputs that have been sent
3402 * through a {@link RemoteInput} of this Notification and cleared once the it is no
3403 * longer relevant (e.g. for chat notifications once the other party has responded).
3404 *
3405 * The most recent input must be stored at the 0 index, the second most recent at the
3406 * 1 index, etc. Note that the system will limit both how far back the inputs will be shown
3407 * and how much of each individual input is shown.
3408 *
3409 * <p>Note: The reply text will only be shown on notifications that have least one action
3410 * with a {@code RemoteInput}.</p>
3411 */
3412 public Builder setRemoteInputHistory(CharSequence[] text) {
3413 if (text == null) {
3414 mN.extras.putCharSequenceArray(EXTRA_REMOTE_INPUT_HISTORY, null);
3415 } else {
3416 final int N = Math.min(MAX_REPLY_HISTORY, text.length);
3417 CharSequence[] safe = new CharSequence[N];
3418 for (int i = 0; i < N; i++) {
3419 safe[i] = safeCharSequence(text[i]);
3420 }
3421 mN.extras.putCharSequenceArray(EXTRA_REMOTE_INPUT_HISTORY, safe);
3422 }
3423 return this;
3424 }
3425
3426 /**
Julia Reynolds13d898c2017-02-02 12:22:05 -05003427 * Sets the number of items this notification represents. May be displayed as a badge count
3428 * for Launchers that support badging.
Joe Onoratocb109a02011-01-18 17:57:41 -08003429 */
Joe Onorato8595a3d2010-11-19 18:12:07 -08003430 public Builder setNumber(int number) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003431 mN.number = number;
Joe Onorato8595a3d2010-11-19 18:12:07 -08003432 return this;
3433 }
3434
Joe Onoratocb109a02011-01-18 17:57:41 -08003435 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003436 * A small piece of additional information pertaining to this notification.
3437 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003438 * The platform template will draw this on the last line of the notification, at the far
3439 * right (to the right of a smallIcon if it has been placed there).
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07003440 *
3441 * @deprecated use {@link #setSubText(CharSequence)} instead to set a text in the header.
3442 * For legacy apps targeting a version below {@link android.os.Build.VERSION_CODES#N} this
3443 * field will still show up, but the subtext will take precedence.
Joe Onoratocb109a02011-01-18 17:57:41 -08003444 */
Aurimas Liutikas514c5ef2016-05-24 15:22:55 -07003445 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08003446 public Builder setContentInfo(CharSequence info) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003447 mN.extras.putCharSequence(EXTRA_INFO_TEXT, safeCharSequence(info));
Joe Onorato46439ce2010-11-19 13:56:21 -08003448 return this;
3449 }
3450
Joe Onoratocb109a02011-01-18 17:57:41 -08003451 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003452 * Set the progress this notification represents.
3453 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003454 * The platform template will represent this using a {@link ProgressBar}.
Jeff Sharkey1c400132011-08-05 14:50:13 -07003455 */
3456 public Builder setProgress(int max, int progress, boolean indeterminate) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003457 mN.extras.putInt(EXTRA_PROGRESS, progress);
3458 mN.extras.putInt(EXTRA_PROGRESS_MAX, max);
3459 mN.extras.putBoolean(EXTRA_PROGRESS_INDETERMINATE, indeterminate);
Jeff Sharkey1c400132011-08-05 14:50:13 -07003460 return this;
3461 }
3462
3463 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003464 * Supply a custom RemoteViews to use instead of the platform template.
3465 *
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003466 * Use {@link #setCustomContentView(RemoteViews)} instead.
Joe Onoratocb109a02011-01-18 17:57:41 -08003467 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003468 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08003469 public Builder setContent(RemoteViews views) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003470 return setCustomContentView(views);
3471 }
3472
3473 /**
3474 * Supply custom RemoteViews to use instead of the platform template.
3475 *
3476 * This will override the layout that would otherwise be constructed by this Builder
3477 * object.
3478 */
3479 public Builder setCustomContentView(RemoteViews contentView) {
3480 mN.contentView = contentView;
3481 return this;
3482 }
3483
3484 /**
3485 * Supply custom RemoteViews to use instead of the platform template in the expanded form.
3486 *
3487 * This will override the expanded layout that would otherwise be constructed by this
3488 * Builder object.
3489 */
3490 public Builder setCustomBigContentView(RemoteViews contentView) {
3491 mN.bigContentView = contentView;
3492 return this;
3493 }
3494
3495 /**
3496 * Supply custom RemoteViews to use instead of the platform template in the heads up dialog.
3497 *
3498 * This will override the heads-up layout that would otherwise be constructed by this
3499 * Builder object.
3500 */
3501 public Builder setCustomHeadsUpContentView(RemoteViews contentView) {
3502 mN.headsUpContentView = contentView;
Joe Onorato46439ce2010-11-19 13:56:21 -08003503 return this;
3504 }
3505
Joe Onoratocb109a02011-01-18 17:57:41 -08003506 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003507 * Supply a {@link PendingIntent} to be sent when the notification is clicked.
3508 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003509 * As of {@link android.os.Build.VERSION_CODES#HONEYCOMB}, if this field is unset and you
3510 * have specified a custom RemoteViews with {@link #setContent(RemoteViews)}, you can use
3511 * {@link RemoteViews#setOnClickPendingIntent RemoteViews.setOnClickPendingIntent(int,PendingIntent)}
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003512 * to assign PendingIntents to individual views in that custom layout (i.e., to create
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003513 * clickable buttons inside the notification view).
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003514 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003515 * @see Notification#contentIntent Notification.contentIntent
Joe Onoratocb109a02011-01-18 17:57:41 -08003516 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003517 public Builder setContentIntent(PendingIntent intent) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003518 mN.contentIntent = intent;
Joe Onorato46439ce2010-11-19 13:56:21 -08003519 return this;
3520 }
3521
Joe Onoratocb109a02011-01-18 17:57:41 -08003522 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003523 * Supply a {@link PendingIntent} to send when the notification is cleared explicitly by the user.
3524 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003525 * @see Notification#deleteIntent
Joe Onoratocb109a02011-01-18 17:57:41 -08003526 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003527 public Builder setDeleteIntent(PendingIntent intent) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003528 mN.deleteIntent = intent;
Joe Onorato46439ce2010-11-19 13:56:21 -08003529 return this;
3530 }
3531
Joe Onoratocb109a02011-01-18 17:57:41 -08003532 /**
3533 * An intent to launch instead of posting the notification to the status bar.
3534 * Only for use with extremely high-priority notifications demanding the user's
3535 * <strong>immediate</strong> attention, such as an incoming phone call or
3536 * alarm clock that the user has explicitly set to a particular time.
3537 * If this facility is used for something else, please give the user an option
3538 * to turn it off and use a normal notification, as this can be extremely
3539 * disruptive.
3540 *
Chris Wren47c20a12014-06-18 17:27:29 -04003541 * <p>
3542 * The system UI may choose to display a heads-up notification, instead of
3543 * launching this intent, while the user is using the device.
3544 * </p>
3545 *
Joe Onoratocb109a02011-01-18 17:57:41 -08003546 * @param intent The pending intent to launch.
3547 * @param highPriority Passing true will cause this notification to be sent
3548 * even if other notifications are suppressed.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003549 *
3550 * @see Notification#fullScreenIntent
Joe Onoratocb109a02011-01-18 17:57:41 -08003551 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003552 public Builder setFullScreenIntent(PendingIntent intent, boolean highPriority) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003553 mN.fullScreenIntent = intent;
Joe Onorato46439ce2010-11-19 13:56:21 -08003554 setFlag(FLAG_HIGH_PRIORITY, highPriority);
3555 return this;
3556 }
3557
Joe Onoratocb109a02011-01-18 17:57:41 -08003558 /**
Dan Sandler5fcdf6e2014-07-18 11:31:15 -04003559 * Set the "ticker" text which is sent to accessibility services.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003560 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003561 * @see Notification#tickerText
Joe Onoratocb109a02011-01-18 17:57:41 -08003562 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003563 public Builder setTicker(CharSequence tickerText) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003564 mN.tickerText = safeCharSequence(tickerText);
Joe Onorato46439ce2010-11-19 13:56:21 -08003565 return this;
3566 }
3567
Joe Onoratocb109a02011-01-18 17:57:41 -08003568 /**
Dan Sandler5fcdf6e2014-07-18 11:31:15 -04003569 * Obsolete version of {@link #setTicker(CharSequence)}.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003570 *
Joe Onoratocb109a02011-01-18 17:57:41 -08003571 */
Dan Sandler5fcdf6e2014-07-18 11:31:15 -04003572 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08003573 public Builder setTicker(CharSequence tickerText, RemoteViews views) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003574 setTicker(tickerText);
3575 // views is ignored
Joe Onorato46439ce2010-11-19 13:56:21 -08003576 return this;
3577 }
3578
Joe Onoratocb109a02011-01-18 17:57:41 -08003579 /**
Dan Sandlerd63f9322015-05-06 15:18:49 -04003580 * Add a large icon to the notification content view.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003581 *
3582 * In the platform template, this image will be shown on the left of the notification view
Dan Sandlerd63f9322015-05-06 15:18:49 -04003583 * in place of the {@link #setSmallIcon(Icon) small icon} (which will be placed in a small
3584 * badge atop the large icon).
Dan Sandler08a04c12015-05-06 15:18:49 -04003585 */
Dan Sandlerd63f9322015-05-06 15:18:49 -04003586 public Builder setLargeIcon(Bitmap b) {
3587 return setLargeIcon(b != null ? Icon.createWithBitmap(b) : null);
3588 }
3589
3590 /**
3591 * Add a large icon to the notification content view.
3592 *
3593 * In the platform template, this image will be shown on the left of the notification view
3594 * in place of the {@link #setSmallIcon(Icon) small icon} (which will be placed in a small
3595 * badge atop the large icon).
3596 */
3597 public Builder setLargeIcon(Icon icon) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003598 mN.mLargeIcon = icon;
3599 mN.extras.putParcelable(EXTRA_LARGE_ICON, icon);
Joe Onorato46439ce2010-11-19 13:56:21 -08003600 return this;
3601 }
3602
Joe Onoratocb109a02011-01-18 17:57:41 -08003603 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003604 * Set the sound to play.
3605 *
John Spurlockc0650f022014-07-19 13:22:39 -04003606 * It will be played using the {@link #AUDIO_ATTRIBUTES_DEFAULT default audio attributes}
3607 * for notifications.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003608 *
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003609 * @deprecated use {@link NotificationChannel#setSound(Uri, AudioAttributes)} instead.
Joe Onoratocb109a02011-01-18 17:57:41 -08003610 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003611 @Deprecated
Joe Onorato52f80cd2010-11-21 15:34:48 -08003612 public Builder setSound(Uri sound) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003613 mN.sound = sound;
3614 mN.audioAttributes = AUDIO_ATTRIBUTES_DEFAULT;
Joe Onorato52f80cd2010-11-21 15:34:48 -08003615 return this;
3616 }
3617
Joe Onoratocb109a02011-01-18 17:57:41 -08003618 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003619 * Set the sound to play, along with a specific stream on which to play it.
Joe Onoratocb109a02011-01-18 17:57:41 -08003620 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003621 * See {@link android.media.AudioManager} for the <code>STREAM_</code> constants.
3622 *
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003623 * @deprecated use {@link NotificationChannel#setSound(Uri, AudioAttributes)}.
Joe Onoratocb109a02011-01-18 17:57:41 -08003624 */
Jean-Michel Trivi81f871e2014-08-06 16:32:38 -07003625 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08003626 public Builder setSound(Uri sound, int streamType) {
Jean-Michel Trivi2f7511f2016-11-28 15:40:27 -08003627 PlayerBase.deprecateStreamTypeForPlayback(streamType, "Notification", "setSound()");
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003628 mN.sound = sound;
3629 mN.audioStreamType = streamType;
Joe Onorato46439ce2010-11-19 13:56:21 -08003630 return this;
3631 }
3632
Joe Onoratocb109a02011-01-18 17:57:41 -08003633 /**
John Spurlockc0650f022014-07-19 13:22:39 -04003634 * Set the sound to play, along with specific {@link AudioAttributes audio attributes} to
3635 * use during playback.
3636 *
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003637 * @deprecated use {@link NotificationChannel#setSound(Uri, AudioAttributes)} instead.
John Spurlockc0650f022014-07-19 13:22:39 -04003638 * @see Notification#sound
3639 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003640 @Deprecated
John Spurlockc0650f022014-07-19 13:22:39 -04003641 public Builder setSound(Uri sound, AudioAttributes audioAttributes) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003642 mN.sound = sound;
3643 mN.audioAttributes = audioAttributes;
John Spurlockc0650f022014-07-19 13:22:39 -04003644 return this;
3645 }
3646
3647 /**
Joe Onoratocb109a02011-01-18 17:57:41 -08003648 * Set the vibration pattern to use.
3649 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003650 * See {@link android.os.Vibrator#vibrate(long[], int)} for a discussion of the
3651 * <code>pattern</code> parameter.
3652 *
Chris Wren47c20a12014-06-18 17:27:29 -04003653 * <p>
3654 * A notification that vibrates is more likely to be presented as a heads-up notification.
3655 * </p>
3656 *
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003657 * @deprecated use {@link NotificationChannel#setVibrationPattern(long[])} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003658 * @see Notification#vibrate
Joe Onoratocb109a02011-01-18 17:57:41 -08003659 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003660 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08003661 public Builder setVibrate(long[] pattern) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003662 mN.vibrate = pattern;
Joe Onorato46439ce2010-11-19 13:56:21 -08003663 return this;
3664 }
3665
Joe Onoratocb109a02011-01-18 17:57:41 -08003666 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003667 * Set the desired color for the indicator LED on the device, as well as the
3668 * blink duty cycle (specified in milliseconds).
3669 *
3670
3671 * Not all devices will honor all (or even any) of these values.
3672 *
Julia Reynolds529e3322017-02-06 08:33:01 -05003673 * @deprecated use {@link NotificationChannel#enableLights(boolean)} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003674 * @see Notification#ledARGB
3675 * @see Notification#ledOnMS
3676 * @see Notification#ledOffMS
Joe Onoratocb109a02011-01-18 17:57:41 -08003677 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003678 @Deprecated
Tor Norbye80756e32015-03-02 09:39:27 -08003679 public Builder setLights(@ColorInt int argb, int onMs, int offMs) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003680 mN.ledARGB = argb;
3681 mN.ledOnMS = onMs;
3682 mN.ledOffMS = offMs;
Julia Reynolds10ee1fc2015-11-09 11:04:55 -05003683 if (onMs != 0 || offMs != 0) {
3684 mN.flags |= FLAG_SHOW_LIGHTS;
3685 }
Joe Onorato46439ce2010-11-19 13:56:21 -08003686 return this;
3687 }
3688
Joe Onoratocb109a02011-01-18 17:57:41 -08003689 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003690 * Set whether this is an "ongoing" notification.
Joe Onoratocb109a02011-01-18 17:57:41 -08003691 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003692
3693 * Ongoing notifications cannot be dismissed by the user, so your application or service
3694 * must take care of canceling them.
3695 *
3696
3697 * They are typically used to indicate a background task that the user is actively engaged
3698 * with (e.g., playing music) or is pending in some way and therefore occupying the device
3699 * (e.g., a file download, sync operation, active network connection).
3700 *
3701
3702 * @see Notification#FLAG_ONGOING_EVENT
3703 * @see Service#setForeground(boolean)
Joe Onoratocb109a02011-01-18 17:57:41 -08003704 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003705 public Builder setOngoing(boolean ongoing) {
3706 setFlag(FLAG_ONGOING_EVENT, ongoing);
3707 return this;
3708 }
3709
Joe Onoratocb109a02011-01-18 17:57:41 -08003710 /**
Selim Cinek7b9605b2017-01-19 17:36:00 -08003711 * Set whether this notification should be colorized. When set, the color set with
3712 * {@link #setColor(int)} will be used as the background color of this notification.
3713 * <p>
Selim Cinek7b9605b2017-01-19 17:36:00 -08003714 * This should only be used for high priority ongoing tasks like navigation, an ongoing
3715 * call, or other similarly high-priority events for the user.
Selim Cinek99104832017-01-25 14:47:33 -08003716 * <p>
Selim Cinek22714f12017-04-13 16:23:53 -07003717 * For most styles, the coloring will only be applied if the notification is for a
3718 * foreground service notification.
Selim Cinek99104832017-01-25 14:47:33 -08003719 * However, for {@link MediaStyle} and {@link DecoratedMediaCustomViewStyle} notifications
Selim Cinek22714f12017-04-13 16:23:53 -07003720 * that have a media session attached there is no such requirement.
Selim Cinek7b9605b2017-01-19 17:36:00 -08003721 *
Selim Cinek7b9605b2017-01-19 17:36:00 -08003722 * @see Builder#setColor(int)
Selim Cinek99104832017-01-25 14:47:33 -08003723 * @see MediaStyle#setMediaSession(MediaSession.Token)
Selim Cinek7b9605b2017-01-19 17:36:00 -08003724 */
3725 public Builder setColorized(boolean colorize) {
3726 mN.extras.putBoolean(EXTRA_COLORIZED, colorize);
3727 return this;
3728 }
3729
3730 /**
Joe Onoratocb109a02011-01-18 17:57:41 -08003731 * Set this flag if you would only like the sound, vibrate
3732 * and ticker to be played if the notification is not already showing.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003733 *
3734 * @see Notification#FLAG_ONLY_ALERT_ONCE
Joe Onoratocb109a02011-01-18 17:57:41 -08003735 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003736 public Builder setOnlyAlertOnce(boolean onlyAlertOnce) {
3737 setFlag(FLAG_ONLY_ALERT_ONCE, onlyAlertOnce);
3738 return this;
3739 }
3740
Joe Onoratocb109a02011-01-18 17:57:41 -08003741 /**
Julia Reynolds04499532016-09-13 14:04:53 -04003742 * Make this notification automatically dismissed when the user touches it.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003743 *
3744 * @see Notification#FLAG_AUTO_CANCEL
Joe Onoratocb109a02011-01-18 17:57:41 -08003745 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003746 public Builder setAutoCancel(boolean autoCancel) {
Joe Onorato281d83f2011-01-04 17:13:10 -08003747 setFlag(FLAG_AUTO_CANCEL, autoCancel);
Joe Onorato46439ce2010-11-19 13:56:21 -08003748 return this;
3749 }
3750
Joe Onoratocb109a02011-01-18 17:57:41 -08003751 /**
Griff Hazendfcb0802014-02-11 12:00:00 -08003752 * Set whether or not this notification should not bridge to other devices.
3753 *
3754 * <p>Some notifications can be bridged to other devices for remote display.
3755 * This hint can be set to recommend this notification not be bridged.
3756 */
3757 public Builder setLocalOnly(boolean localOnly) {
3758 setFlag(FLAG_LOCAL_ONLY, localOnly);
3759 return this;
3760 }
3761
3762 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003763 * Set which notification properties will be inherited from system defaults.
Joe Onoratocb109a02011-01-18 17:57:41 -08003764 * <p>
3765 * The value should be one or more of the following fields combined with
3766 * bitwise-or:
3767 * {@link #DEFAULT_SOUND}, {@link #DEFAULT_VIBRATE}, {@link #DEFAULT_LIGHTS}.
3768 * <p>
3769 * For all default values, use {@link #DEFAULT_ALL}.
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003770 *
3771 * @deprecated use {@link NotificationChannel#enableVibration(boolean)} and
Julia Reynolds529e3322017-02-06 08:33:01 -05003772 * {@link NotificationChannel#enableLights(boolean)} and
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003773 * {@link NotificationChannel#setSound(Uri, AudioAttributes)} instead.
Joe Onoratocb109a02011-01-18 17:57:41 -08003774 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003775 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08003776 public Builder setDefaults(int defaults) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003777 mN.defaults = defaults;
Joe Onorato46439ce2010-11-19 13:56:21 -08003778 return this;
3779 }
3780
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003781 /**
3782 * Set the priority of this notification.
3783 *
3784 * @see Notification#priority
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003785 * @deprecated use {@link NotificationChannel#setImportance(int)} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003786 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003787 @Deprecated
Tor Norbyed9273d62013-05-30 15:59:53 -07003788 public Builder setPriority(@Priority int pri) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003789 mN.priority = pri;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003790 return this;
3791 }
Joe Malin8d40d042012-11-05 11:36:40 -08003792
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003793 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -04003794 * Set the notification category.
Joe Malin8d40d042012-11-05 11:36:40 -08003795 *
John Spurlockfd7f1e02014-03-18 16:41:57 -04003796 * @see Notification#category
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003797 */
John Spurlockfd7f1e02014-03-18 16:41:57 -04003798 public Builder setCategory(String category) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003799 mN.category = category;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003800 return this;
3801 }
3802
3803 /**
Chris Wrendde75302014-03-26 17:24:15 -04003804 * Add a person that is relevant to this notification.
3805 *
Chris Wrene6c48932014-09-29 17:19:27 -04003806 * <P>
3807 * Depending on user preferences, this annotation may allow the notification to pass
Julia Reynoldse071abd2017-03-22 10:52:11 -04003808 * through interruption filters, if this notification is of category {@link #CATEGORY_CALL}
3809 * or {@link #CATEGORY_MESSAGE}. The addition of people may also cause this notification to
3810 * appear more prominently in the user interface.
Chris Wrene6c48932014-09-29 17:19:27 -04003811 * </P>
3812 *
3813 * <P>
3814 * The person should be specified by the {@code String} representation of a
3815 * {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI}.
3816 * </P>
3817 *
3818 * <P>The system will also attempt to resolve {@code mailto:} and {@code tel:} schema
3819 * URIs. The path part of these URIs must exist in the contacts database, in the
3820 * appropriate column, or the reference will be discarded as invalid. Telephone schema
3821 * URIs will be resolved by {@link android.provider.ContactsContract.PhoneLookup}.
Selim Cineke7238dd2017-12-14 17:48:32 -08003822 * It is also possible to provide a URI with the schema {@code name:} in order to uniquely
3823 * identify a person without an entry in the contacts database.
Chris Wrene6c48932014-09-29 17:19:27 -04003824 * </P>
3825 *
3826 * @param uri A URI for the person.
Chris Wrendde75302014-03-26 17:24:15 -04003827 * @see Notification#EXTRA_PEOPLE
Selim Cineke7238dd2017-12-14 17:48:32 -08003828 * @deprecated use {@link #addPerson(Person)}
Chris Wrendde75302014-03-26 17:24:15 -04003829 */
Chris Wrene6c48932014-09-29 17:19:27 -04003830 public Builder addPerson(String uri) {
Selim Cineke7238dd2017-12-14 17:48:32 -08003831 addPerson(new Person().setUri(uri));
3832 return this;
3833 }
3834
3835 /**
3836 * Add a person that is relevant to this notification.
3837 *
3838 * <P>
3839 * Depending on user preferences, this annotation may allow the notification to pass
3840 * through interruption filters, if this notification is of category {@link #CATEGORY_CALL}
3841 * or {@link #CATEGORY_MESSAGE}. The addition of people may also cause this notification to
3842 * appear more prominently in the user interface.
3843 * </P>
3844 *
3845 * <P>
3846 * A person should usually contain a uri in order to benefit from the ranking boost.
3847 * However, even if no uri is provided, it's beneficial to provide other people in the
3848 * notification, such that listeners and voice only devices can announce and handle them
3849 * properly.
3850 * </P>
3851 *
3852 * @param person the person to add.
3853 * @see Notification#EXTRA_PEOPLE_LIST
3854 */
3855 public Builder addPerson(Person person) {
3856 mPersonList.add(person);
Chris Wrendde75302014-03-26 17:24:15 -04003857 return this;
3858 }
3859
3860 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003861 * Set this notification to be part of a group of notifications sharing the same key.
3862 * Grouped notifications may display in a cluster or stack on devices which
3863 * support such rendering.
3864 *
3865 * <p>To make this notification the summary for its group, also call
3866 * {@link #setGroupSummary}. A sort order can be specified for group members by using
3867 * {@link #setSortKey}.
3868 * @param groupKey The group key of the group.
3869 * @return this object for method chaining
3870 */
3871 public Builder setGroup(String groupKey) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003872 mN.mGroupKey = groupKey;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003873 return this;
3874 }
3875
3876 /**
3877 * Set this notification to be the group summary for a group of notifications.
3878 * Grouped notifications may display in a cluster or stack on devices which
Julia Reynolds04499532016-09-13 14:04:53 -04003879 * support such rendering. If thereRequires a group key also be set using {@link #setGroup}.
3880 * The group summary may be suppressed if too few notifications are included in the group.
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003881 * @param isGroupSummary Whether this notification should be a group summary.
3882 * @return this object for method chaining
3883 */
3884 public Builder setGroupSummary(boolean isGroupSummary) {
3885 setFlag(FLAG_GROUP_SUMMARY, isGroupSummary);
3886 return this;
3887 }
3888
3889 /**
3890 * Set a sort key that orders this notification among other notifications from the
3891 * same package. This can be useful if an external sort was already applied and an app
3892 * would like to preserve this. Notifications will be sorted lexicographically using this
3893 * value, although providing different priorities in addition to providing sort key may
3894 * cause this value to be ignored.
3895 *
3896 * <p>This sort key can also be used to order members of a notification group. See
Griff Hazen9e1379f2014-05-20 12:50:51 -07003897 * {@link #setGroup}.
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003898 *
3899 * @see String#compareTo(String)
3900 */
3901 public Builder setSortKey(String sortKey) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003902 mN.mSortKey = sortKey;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003903 return this;
3904 }
3905
3906 /**
Griff Hazen720042b2014-02-24 15:46:56 -08003907 * Merge additional metadata into this notification.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003908 *
Griff Hazen720042b2014-02-24 15:46:56 -08003909 * <p>Values within the Bundle will replace existing extras values in this Builder.
3910 *
3911 * @see Notification#extras
3912 */
Griff Hazen959591e2014-05-15 22:26:18 -07003913 public Builder addExtras(Bundle extras) {
3914 if (extras != null) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003915 mUserExtras.putAll(extras);
Griff Hazen720042b2014-02-24 15:46:56 -08003916 }
3917 return this;
3918 }
3919
3920 /**
3921 * Set metadata for this notification.
3922 *
3923 * <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 -04003924 * current contents are copied into the Notification each time {@link #build()} is
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003925 * called.
3926 *
Griff Hazen720042b2014-02-24 15:46:56 -08003927 * <p>Replaces any existing extras values with those from the provided Bundle.
3928 * Use {@link #addExtras} to merge in metadata instead.
3929 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003930 * @see Notification#extras
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003931 */
Griff Hazen959591e2014-05-15 22:26:18 -07003932 public Builder setExtras(Bundle extras) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003933 if (extras != null) {
3934 mUserExtras = extras;
3935 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003936 return this;
3937 }
3938
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003939 /**
Griff Hazen720042b2014-02-24 15:46:56 -08003940 * Get the current metadata Bundle used by this notification Builder.
3941 *
3942 * <p>The returned Bundle is shared with this Builder.
3943 *
3944 * <p>The current contents of this Bundle are copied into the Notification each time
3945 * {@link #build()} is called.
3946 *
3947 * @see Notification#extras
3948 */
3949 public Bundle getExtras() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003950 return mUserExtras;
3951 }
3952
3953 private Bundle getAllExtras() {
3954 final Bundle saveExtras = (Bundle) mUserExtras.clone();
3955 saveExtras.putAll(mN.extras);
3956 return saveExtras;
Griff Hazen720042b2014-02-24 15:46:56 -08003957 }
3958
3959 /**
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003960 * Add an action to this notification. Actions are typically displayed by
3961 * the system as a button adjacent to the notification content.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04003962 * <p>
3963 * Every action must have an icon (32dp square and matching the
3964 * <a href="{@docRoot}design/style/iconography.html#action-bar">Holo
3965 * Dark action bar</a> visual style), a textual label, and a {@link PendingIntent}.
3966 * <p>
3967 * A notification in its expanded form can display up to 3 actions, from left to right in
3968 * the order they were added. Actions will not be displayed when the notification is
3969 * collapsed, however, so be sure that any essential functions may be accessed by the user
3970 * in some other way (for example, in the Activity pointed to by {@link #contentIntent}).
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003971 *
3972 * @param icon Resource ID of a drawable that represents the action.
3973 * @param title Text describing the action.
3974 * @param intent PendingIntent to be fired when the action is invoked.
Dan Sandler86647982015-05-13 23:41:13 -04003975 *
3976 * @deprecated Use {@link #addAction(Action)} instead.
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003977 */
Dan Sandler86647982015-05-13 23:41:13 -04003978 @Deprecated
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003979 public Builder addAction(int icon, CharSequence title, PendingIntent intent) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04003980 mActions.add(new Action(icon, safeCharSequence(title), intent));
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003981 return this;
3982 }
3983
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003984 /**
Griff Hazen959591e2014-05-15 22:26:18 -07003985 * Add an action to this notification. Actions are typically displayed by
3986 * the system as a button adjacent to the notification content.
3987 * <p>
3988 * Every action must have an icon (32dp square and matching the
3989 * <a href="{@docRoot}design/style/iconography.html#action-bar">Holo
3990 * Dark action bar</a> visual style), a textual label, and a {@link PendingIntent}.
3991 * <p>
3992 * A notification in its expanded form can display up to 3 actions, from left to right in
3993 * the order they were added. Actions will not be displayed when the notification is
3994 * collapsed, however, so be sure that any essential functions may be accessed by the user
3995 * in some other way (for example, in the Activity pointed to by {@link #contentIntent}).
3996 *
3997 * @param action The action to add.
3998 */
3999 public Builder addAction(Action action) {
liangweikang63b03b52017-03-16 19:22:15 +08004000 if (action != null) {
4001 mActions.add(action);
4002 }
Griff Hazen959591e2014-05-15 22:26:18 -07004003 return this;
4004 }
4005
4006 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004007 * Alter the complete list of actions attached to this notification.
4008 * @see #addAction(Action).
4009 *
4010 * @param actions
4011 * @return
4012 */
4013 public Builder setActions(Action... actions) {
4014 mActions.clear();
4015 for (int i = 0; i < actions.length; i++) {
liangweikang63b03b52017-03-16 19:22:15 +08004016 if (actions[i] != null) {
4017 mActions.add(actions[i]);
4018 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004019 }
4020 return this;
4021 }
4022
4023 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004024 * Add a rich notification style to be applied at build time.
4025 *
4026 * @param style Object responsible for modifying the notification style.
4027 */
4028 public Builder setStyle(Style style) {
4029 if (mStyle != style) {
4030 mStyle = style;
Daniel Sandlerc08dea22012-06-28 08:35:24 -07004031 if (mStyle != null) {
4032 mStyle.setBuilder(this);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004033 mN.extras.putString(EXTRA_TEMPLATE, style.getClass().getName());
4034 } else {
4035 mN.extras.remove(EXTRA_TEMPLATE);
Daniel Sandlerc08dea22012-06-28 08:35:24 -07004036 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004037 }
4038 return this;
4039 }
4040
Dan Sandler0bf2ed82013-12-21 23:33:41 -06004041 /**
4042 * Specify the value of {@link #visibility}.
Griff Hazenb720abe2014-05-20 13:15:30 -07004043 *
Dan Sandler0bf2ed82013-12-21 23:33:41 -06004044 * @return The same Builder.
4045 */
Jeff Sharkey30e06bb2017-04-24 11:18:03 -06004046 public Builder setVisibility(@Visibility int visibility) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004047 mN.visibility = visibility;
Dan Sandler0bf2ed82013-12-21 23:33:41 -06004048 return this;
4049 }
4050
4051 /**
4052 * Supply a replacement Notification whose contents should be shown in insecure contexts
4053 * (i.e. atop the secure lockscreen). See {@link #visibility} and {@link #VISIBILITY_PUBLIC}.
4054 * @param n A replacement notification, presumably with some or all info redacted.
4055 * @return The same Builder.
4056 */
4057 public Builder setPublicVersion(Notification n) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004058 if (n != null) {
4059 mN.publicVersion = new Notification();
4060 n.cloneInto(mN.publicVersion, /*heavy=*/ true);
4061 } else {
4062 mN.publicVersion = null;
4063 }
Dan Sandler0bf2ed82013-12-21 23:33:41 -06004064 return this;
4065 }
4066
Griff Hazenb720abe2014-05-20 13:15:30 -07004067 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07004068 * Apply an extender to this notification builder. Extenders may be used to add
4069 * metadata or change options on this builder.
4070 */
Griff Hazen61a9e862014-05-22 16:05:19 -07004071 public Builder extend(Extender extender) {
4072 extender.extend(this);
Griff Hazen5cadc3b2014-05-20 09:55:39 -07004073 return this;
4074 }
4075
Dan Sandler4e787062015-06-17 15:09:48 -04004076 /**
4077 * @hide
4078 */
Julia Reynoldse46bb372016-03-17 11:05:58 -04004079 public Builder setFlag(int mask, boolean value) {
Joe Onorato46439ce2010-11-19 13:56:21 -08004080 if (value) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004081 mN.flags |= mask;
Joe Onorato46439ce2010-11-19 13:56:21 -08004082 } else {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004083 mN.flags &= ~mask;
Joe Onorato46439ce2010-11-19 13:56:21 -08004084 }
Julia Reynoldse46bb372016-03-17 11:05:58 -04004085 return this;
Joe Onorato46439ce2010-11-19 13:56:21 -08004086 }
4087
Dan Sandler26e81cf2014-05-06 10:01:27 -04004088 /**
4089 * Sets {@link Notification#color}.
4090 *
4091 * @param argb The accent color to use
4092 *
4093 * @return The same Builder.
4094 */
Tor Norbye80756e32015-03-02 09:39:27 -08004095 public Builder setColor(@ColorInt int argb) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004096 mN.color = argb;
Julia Reynolds10ee1fc2015-11-09 11:04:55 -05004097 sanitizeColor();
Dan Sandler26e81cf2014-05-06 10:01:27 -04004098 return this;
4099 }
4100
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02004101 private Drawable getProfileBadgeDrawable() {
Chris Wren66619a22016-05-12 16:42:37 -04004102 if (mContext.getUserId() == UserHandle.USER_SYSTEM) {
4103 // This user can never be a badged profile,
4104 // and also includes USER_ALL system notifications.
4105 return null;
4106 }
Christoph Studer7ac80e62014-08-04 16:01:57 +02004107 // Note: This assumes that the current user can read the profile badge of the
4108 // originating user.
Selim Cineke6ff9462016-01-15 15:07:06 -08004109 return mContext.getPackageManager().getUserBadgeForDensityNoBackground(
Julia Reynoldsda303542015-11-23 14:00:20 -05004110 new UserHandle(mContext.getUserId()), 0);
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02004111 }
4112
4113 private Bitmap getProfileBadge() {
4114 Drawable badge = getProfileBadgeDrawable();
Kenny Guy8a0101b2014-05-08 23:34:12 +01004115 if (badge == null) {
4116 return null;
4117 }
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02004118 final int size = mContext.getResources().getDimensionPixelSize(
4119 R.dimen.notification_badge_size);
4120 Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
Kenny Guy8a0101b2014-05-08 23:34:12 +01004121 Canvas canvas = new Canvas(bitmap);
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02004122 badge.setBounds(0, 0, size, size);
Kenny Guy8a0101b2014-05-08 23:34:12 +01004123 badge.draw(canvas);
4124 return bitmap;
4125 }
4126
Selim Cinekc848c3a2016-01-13 15:27:30 -08004127 private void bindProfileBadge(RemoteViews contentView) {
Kenny Guy98193ea2014-07-24 19:54:37 +01004128 Bitmap profileBadge = getProfileBadge();
4129
Kenny Guy98193ea2014-07-24 19:54:37 +01004130 if (profileBadge != null) {
Selim Cinekc848c3a2016-01-13 15:27:30 -08004131 contentView.setImageViewBitmap(R.id.profile_badge, profileBadge);
4132 contentView.setViewVisibility(R.id.profile_badge, View.VISIBLE);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004133 if (isColorized()) {
Sunny Goyal5b153922017-09-21 21:00:36 -07004134 contentView.setDrawableTint(R.id.profile_badge, false,
4135 getPrimaryTextColor(), PorterDuff.Mode.SRC_ATOP);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004136 }
Kenny Guy98193ea2014-07-24 19:54:37 +01004137 }
Kenny Guy98193ea2014-07-24 19:54:37 +01004138 }
4139
Julia Reynoldsfc640012018-02-21 12:25:27 -05004140 /**
4141 * @hide
4142 */
4143 public boolean usesStandardHeader() {
4144 if (mN.mUsesStandardHeader) {
4145 return true;
4146 }
4147 if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.N) {
4148 if (mN.contentView == null && mN.bigContentView == null) {
4149 return true;
4150 }
4151 }
4152 boolean contentViewUsesHeader = mN.contentView == null
4153 || STANDARD_LAYOUTS.contains(mN.contentView.getLayoutId());
4154 boolean bigContentViewUsesHeader = mN.bigContentView == null
4155 || STANDARD_LAYOUTS.contains(mN.bigContentView.getLayoutId());
4156 return contentViewUsesHeader && bigContentViewUsesHeader;
4157 }
4158
Christoph Studerfe718432014-09-01 18:21:18 +02004159 private void resetStandardTemplate(RemoteViews contentView) {
Selim Cinekeaa29ca2015-11-23 13:51:13 -08004160 resetNotificationHeader(contentView);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004161 resetContentMargins(contentView);
Christoph Studerfe718432014-09-01 18:21:18 +02004162 contentView.setViewVisibility(R.id.right_icon, View.GONE);
Selim Cinek860b6da2015-12-16 19:02:19 -08004163 contentView.setViewVisibility(R.id.title, View.GONE);
Christoph Studerfe718432014-09-01 18:21:18 +02004164 contentView.setTextViewText(R.id.title, null);
Selim Cinek41598732016-01-11 16:58:37 -08004165 contentView.setViewVisibility(R.id.text, View.GONE);
Christoph Studerfe718432014-09-01 18:21:18 +02004166 contentView.setTextViewText(R.id.text, null);
Selim Cinek29603462015-11-17 19:04:39 -08004167 contentView.setViewVisibility(R.id.text_line_1, View.GONE);
Selim Cinek41598732016-01-11 16:58:37 -08004168 contentView.setTextViewText(R.id.text_line_1, null);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004169 }
4170
Selim Cinekeaa29ca2015-11-23 13:51:13 -08004171 /**
4172 * Resets the notification header to its original state
4173 */
4174 private void resetNotificationHeader(RemoteViews contentView) {
Adrian Roosc4337a32016-08-02 18:30:34 -07004175 // Small icon doesn't need to be reset, as it's always set. Resetting would prevent
4176 // re-using the drawable when the notification is updated.
Selim Cinek7b836392015-12-04 20:02:59 -08004177 contentView.setBoolean(R.id.notification_header, "setExpanded", false);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004178 contentView.setTextViewText(R.id.app_name_text, null);
Christoph Studerca1db712014-09-10 17:31:33 +02004179 contentView.setViewVisibility(R.id.chronometer, View.GONE);
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07004180 contentView.setViewVisibility(R.id.header_text, View.GONE);
Adrian Roos9dfb78f2016-06-30 15:43:44 -07004181 contentView.setTextViewText(R.id.header_text, null);
Selim Cinekafeed292017-12-12 17:32:44 -08004182 contentView.setViewVisibility(R.id.header_text_secondary, View.GONE);
4183 contentView.setTextViewText(R.id.header_text_secondary, null);
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07004184 contentView.setViewVisibility(R.id.header_text_divider, View.GONE);
Selim Cinekafeed292017-12-12 17:32:44 -08004185 contentView.setViewVisibility(R.id.header_text_secondary_divider, View.GONE);
Selim Cinek29603462015-11-17 19:04:39 -08004186 contentView.setViewVisibility(R.id.time_divider, View.GONE);
Selim Cinekb85f36fd2016-04-20 18:46:36 -07004187 contentView.setViewVisibility(R.id.time, View.GONE);
Selim Cinekc848c3a2016-01-13 15:27:30 -08004188 contentView.setImageViewIcon(R.id.profile_badge, null);
4189 contentView.setViewVisibility(R.id.profile_badge, View.GONE);
Julia Reynoldsfc640012018-02-21 12:25:27 -05004190 mN.mUsesStandardHeader = false;
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004191 }
4192
4193 private void resetContentMargins(RemoteViews contentView) {
Adrian Roos2d5dbba2016-06-08 17:11:53 -07004194 contentView.setViewLayoutMarginEndDimen(R.id.line1, 0);
4195 contentView.setViewLayoutMarginEndDimen(R.id.text, 0);
Christoph Studerfe718432014-09-01 18:21:18 +02004196 }
4197
Jorim Jaggi445d3c02014-08-19 22:33:42 +02004198 private RemoteViews applyStandardTemplate(int resId) {
Adrian Roos70d7aa32017-01-11 15:39:06 -08004199 return applyStandardTemplate(resId, mParams.reset().fillTextsFrom(this));
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004200 }
4201
4202 /**
4203 * @param hasProgress whether the progress bar should be shown and set
4204 */
4205 private RemoteViews applyStandardTemplate(int resId, boolean hasProgress) {
Adrian Roos70d7aa32017-01-11 15:39:06 -08004206 return applyStandardTemplate(resId, mParams.reset().hasProgress(hasProgress)
4207 .fillTextsFrom(this));
Adrian Roosc1a80b02016-04-05 14:54:55 -07004208 }
4209
Adrian Roos70d7aa32017-01-11 15:39:06 -08004210 private RemoteViews applyStandardTemplate(int resId, StandardTemplateParams p) {
Kenny Guy77320062014-08-27 21:37:15 +01004211 RemoteViews contentView = new BuilderRemoteViews(mContext.getApplicationInfo(), resId);
Dan Sandler539aad42014-08-04 00:43:39 -04004212
Christoph Studerfe718432014-09-01 18:21:18 +02004213 resetStandardTemplate(contentView);
4214
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004215 final Bundle ex = mN.extras;
Selim Cinek7b9605b2017-01-19 17:36:00 -08004216 updateBackgroundColor(contentView);
Selim Cinekafeed292017-12-12 17:32:44 -08004217 bindNotificationHeader(contentView, p.ambient, p.headerTextSecondary);
Lucas Dupin90158d02018-01-23 16:36:12 -08004218 bindLargeIcon(contentView, p.hideLargeIcon || p.ambient, p.alwaysShowReply);
Adrian Roos70d7aa32017-01-11 15:39:06 -08004219 boolean showProgress = handleProgressBar(p.hasProgress, contentView, ex);
4220 if (p.title != null) {
Selim Cinek860b6da2015-12-16 19:02:19 -08004221 contentView.setViewVisibility(R.id.title, View.VISIBLE);
Selim Cinek48f66b72017-08-18 16:17:51 -07004222 contentView.setTextViewText(R.id.title, processTextSpans(p.title));
Adrian Roos72171622017-01-27 10:32:06 -08004223 if (!p.ambient) {
4224 setTextViewColorPrimary(contentView, R.id.title);
4225 }
Selim Cinek954cc232016-05-20 13:29:23 -07004226 contentView.setViewLayoutWidth(R.id.title, showProgress
4227 ? ViewGroup.LayoutParams.WRAP_CONTENT
4228 : ViewGroup.LayoutParams.MATCH_PARENT);
Joe Onorato561d3852010-11-20 18:09:34 -08004229 }
Adrian Roos70d7aa32017-01-11 15:39:06 -08004230 if (p.text != null) {
Selim Cinek41598732016-01-11 16:58:37 -08004231 int textId = showProgress ? com.android.internal.R.id.text_line_1
4232 : com.android.internal.R.id.text;
Selim Cinek48f66b72017-08-18 16:17:51 -07004233 contentView.setTextViewText(textId, processTextSpans(p.text));
Adrian Roos72171622017-01-27 10:32:06 -08004234 if (!p.ambient) {
4235 setTextViewColorSecondary(contentView, textId);
4236 }
Selim Cinek41598732016-01-11 16:58:37 -08004237 contentView.setViewVisibility(textId, View.VISIBLE);
Joe Onorato561d3852010-11-20 18:09:34 -08004238 }
Selim Cinekc848c3a2016-01-13 15:27:30 -08004239
Selim Cinek279fa862016-06-14 10:57:25 -07004240 setContentMinHeight(contentView, showProgress || mN.hasLargeIcon());
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004241
Selim Cinek29603462015-11-17 19:04:39 -08004242 return contentView;
4243 }
4244
Selim Cinek48f66b72017-08-18 16:17:51 -07004245 private CharSequence processTextSpans(CharSequence text) {
4246 if (hasForegroundColor()) {
Selim Cinek87c31532017-08-18 18:53:44 -07004247 return NotificationColorUtil.clearColorSpans(text);
Selim Cinek48f66b72017-08-18 16:17:51 -07004248 }
4249 return text;
4250 }
4251
Selim Cinek7b9605b2017-01-19 17:36:00 -08004252 private void setTextViewColorPrimary(RemoteViews contentView, int id) {
4253 ensureColors();
4254 contentView.setTextColor(id, mPrimaryTextColor);
4255 }
4256
Selim Cinek48f66b72017-08-18 16:17:51 -07004257 private boolean hasForegroundColor() {
4258 return mForegroundColor != COLOR_INVALID;
4259 }
4260
Selim Cinek389edcd2017-05-11 19:16:44 -07004261 /**
4262 * @return the primary text color
4263 * @hide
4264 */
4265 @VisibleForTesting
4266 public int getPrimaryTextColor() {
Selim Cinek7b9605b2017-01-19 17:36:00 -08004267 ensureColors();
4268 return mPrimaryTextColor;
4269 }
4270
Selim Cinek389edcd2017-05-11 19:16:44 -07004271 /**
4272 * @return the secondary text color
4273 * @hide
4274 */
4275 @VisibleForTesting
4276 public int getSecondaryTextColor() {
4277 ensureColors();
4278 return mSecondaryTextColor;
4279 }
4280
Selim Cinek7b9605b2017-01-19 17:36:00 -08004281 private int getActionBarColor() {
4282 ensureColors();
4283 return mActionBarColor;
4284 }
4285
Selim Cinek622c64a2017-04-17 17:10:05 -07004286 private int getActionBarColorDeEmphasized() {
4287 int backgroundColor = getBackgroundColor();
4288 return NotificationColorUtil.getShiftedColor(backgroundColor, 12);
4289 }
4290
Selim Cinek7b9605b2017-01-19 17:36:00 -08004291 private void setTextViewColorSecondary(RemoteViews contentView, int id) {
4292 ensureColors();
4293 contentView.setTextColor(id, mSecondaryTextColor);
4294 }
4295
4296 private void ensureColors() {
4297 int backgroundColor = getBackgroundColor();
4298 if (mPrimaryTextColor == COLOR_INVALID
4299 || mSecondaryTextColor == COLOR_INVALID
4300 || mActionBarColor == COLOR_INVALID
4301 || mTextColorsAreForBackground != backgroundColor) {
4302 mTextColorsAreForBackground = backgroundColor;
Selim Cinek48f66b72017-08-18 16:17:51 -07004303 if (!hasForegroundColor() || !isColorized()) {
Selim Cinek5fb73f82017-04-20 16:55:38 -07004304 mPrimaryTextColor = NotificationColorUtil.resolvePrimaryColor(mContext,
4305 backgroundColor);
4306 mSecondaryTextColor = NotificationColorUtil.resolveSecondaryColor(mContext,
4307 backgroundColor);
Selim Cinekac5f0272017-05-02 16:05:41 -07004308 if (backgroundColor != COLOR_DEFAULT
4309 && (mBackgroundColorHint != COLOR_INVALID || isColorized())) {
4310 mPrimaryTextColor = NotificationColorUtil.findAlphaToMeetContrast(
4311 mPrimaryTextColor, backgroundColor, 4.5);
4312 mSecondaryTextColor = NotificationColorUtil.findAlphaToMeetContrast(
4313 mSecondaryTextColor, backgroundColor, 4.5);
4314 }
Selim Cinek5fb73f82017-04-20 16:55:38 -07004315 } else {
4316 double backLum = NotificationColorUtil.calculateLuminance(backgroundColor);
4317 double textLum = NotificationColorUtil.calculateLuminance(mForegroundColor);
4318 double contrast = NotificationColorUtil.calculateContrast(mForegroundColor,
4319 backgroundColor);
Selim Cinek389edcd2017-05-11 19:16:44 -07004320 // We only respect the given colors if worst case Black or White still has
4321 // contrast
4322 boolean backgroundLight = backLum > textLum
4323 && satisfiesTextContrast(backgroundColor, Color.BLACK)
4324 || backLum <= textLum
4325 && !satisfiesTextContrast(backgroundColor, Color.WHITE);
Selim Cinek5fb73f82017-04-20 16:55:38 -07004326 if (contrast < 4.5f) {
Selim Cinek389edcd2017-05-11 19:16:44 -07004327 if (backgroundLight) {
Selim Cinek5fb73f82017-04-20 16:55:38 -07004328 mSecondaryTextColor = NotificationColorUtil.findContrastColor(
4329 mForegroundColor,
4330 backgroundColor,
4331 true /* findFG */,
4332 4.5f);
4333 mPrimaryTextColor = NotificationColorUtil.changeColorLightness(
Selim Cinek389edcd2017-05-11 19:16:44 -07004334 mSecondaryTextColor, -LIGHTNESS_TEXT_DIFFERENCE_LIGHT);
Selim Cinek5fb73f82017-04-20 16:55:38 -07004335 } else {
4336 mSecondaryTextColor =
4337 NotificationColorUtil.findContrastColorAgainstDark(
4338 mForegroundColor,
4339 backgroundColor,
4340 true /* findFG */,
4341 4.5f);
4342 mPrimaryTextColor = NotificationColorUtil.changeColorLightness(
Selim Cinek389edcd2017-05-11 19:16:44 -07004343 mSecondaryTextColor, -LIGHTNESS_TEXT_DIFFERENCE_DARK);
Selim Cinek5fb73f82017-04-20 16:55:38 -07004344 }
4345 } else {
4346 mPrimaryTextColor = mForegroundColor;
4347 mSecondaryTextColor = NotificationColorUtil.changeColorLightness(
Selim Cinek389edcd2017-05-11 19:16:44 -07004348 mPrimaryTextColor, backgroundLight ? LIGHTNESS_TEXT_DIFFERENCE_LIGHT
4349 : LIGHTNESS_TEXT_DIFFERENCE_DARK);
Selim Cinek5fb73f82017-04-20 16:55:38 -07004350 if (NotificationColorUtil.calculateContrast(mSecondaryTextColor,
4351 backgroundColor) < 4.5f) {
4352 // oh well the secondary is not good enough
Selim Cinek389edcd2017-05-11 19:16:44 -07004353 if (backgroundLight) {
Selim Cinek5fb73f82017-04-20 16:55:38 -07004354 mSecondaryTextColor = NotificationColorUtil.findContrastColor(
4355 mSecondaryTextColor,
4356 backgroundColor,
4357 true /* findFG */,
4358 4.5f);
4359 } else {
4360 mSecondaryTextColor
4361 = NotificationColorUtil.findContrastColorAgainstDark(
4362 mSecondaryTextColor,
4363 backgroundColor,
4364 true /* findFG */,
4365 4.5f);
4366 }
4367 mPrimaryTextColor = NotificationColorUtil.changeColorLightness(
Selim Cinek389edcd2017-05-11 19:16:44 -07004368 mSecondaryTextColor, backgroundLight
4369 ? -LIGHTNESS_TEXT_DIFFERENCE_LIGHT
4370 : -LIGHTNESS_TEXT_DIFFERENCE_DARK);
Selim Cinek5fb73f82017-04-20 16:55:38 -07004371 }
4372 }
4373 }
Selim Cinek875ba9b2017-02-13 16:20:17 -08004374 mActionBarColor = NotificationColorUtil.resolveActionBarColor(mContext,
4375 backgroundColor);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004376 }
4377 }
4378
4379 private void updateBackgroundColor(RemoteViews contentView) {
4380 if (isColorized()) {
4381 contentView.setInt(R.id.status_bar_latest_event_content, "setBackgroundColor",
4382 getBackgroundColor());
4383 } else {
4384 // Clear it!
4385 contentView.setInt(R.id.status_bar_latest_event_content, "setBackgroundResource",
4386 0);
4387 }
4388 }
4389
Selim Cinek860b6da2015-12-16 19:02:19 -08004390 /**
4391 * @param remoteView the remote view to update the minheight in
4392 * @param hasMinHeight does it have a mimHeight
4393 * @hide
4394 */
4395 void setContentMinHeight(RemoteViews remoteView, boolean hasMinHeight) {
4396 int minHeight = 0;
4397 if (hasMinHeight) {
4398 // we need to set the minHeight of the notification
4399 minHeight = mContext.getResources().getDimensionPixelSize(
4400 com.android.internal.R.dimen.notification_min_content_height);
4401 }
4402 remoteView.setInt(R.id.notification_main_column, "setMinimumHeight", minHeight);
4403 }
4404
Selim Cinek29603462015-11-17 19:04:39 -08004405 private boolean handleProgressBar(boolean hasProgress, RemoteViews contentView, Bundle ex) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004406 final int max = ex.getInt(EXTRA_PROGRESS_MAX, 0);
4407 final int progress = ex.getInt(EXTRA_PROGRESS, 0);
4408 final boolean ind = ex.getBoolean(EXTRA_PROGRESS_INDETERMINATE);
4409 if (hasProgress && (max != 0 || ind)) {
Selim Cinek29603462015-11-17 19:04:39 -08004410 contentView.setViewVisibility(com.android.internal.R.id.progress, View.VISIBLE);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004411 contentView.setProgressBar(
Selim Cinek29603462015-11-17 19:04:39 -08004412 R.id.progress, max, progress, ind);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004413 contentView.setProgressBackgroundTintList(
4414 R.id.progress, ColorStateList.valueOf(mContext.getColor(
4415 R.color.notification_progress_background_color)));
Selim Cinek29603462015-11-17 19:04:39 -08004416 if (mN.color != COLOR_DEFAULT) {
Adrian Roos4ff3b122016-02-01 12:26:13 -08004417 ColorStateList colorStateList = ColorStateList.valueOf(resolveContrastColor());
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004418 contentView.setProgressTintList(R.id.progress, colorStateList);
4419 contentView.setProgressIndeterminateTintList(R.id.progress, colorStateList);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004420 }
Selim Cinek29603462015-11-17 19:04:39 -08004421 return true;
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004422 } else {
4423 contentView.setViewVisibility(R.id.progress, View.GONE);
Selim Cinek29603462015-11-17 19:04:39 -08004424 return false;
Jeff Sharkey1c400132011-08-05 14:50:13 -07004425 }
Joe Onorato561d3852010-11-20 18:09:34 -08004426 }
4427
Selim Cinek88188f22017-09-19 16:46:56 -07004428 private void bindLargeIcon(RemoteViews contentView, boolean hideLargeIcon,
4429 boolean alwaysShowReply) {
Selim Cinek279fa862016-06-14 10:57:25 -07004430 if (mN.mLargeIcon == null && mN.largeIcon != null) {
4431 mN.mLargeIcon = Icon.createWithBitmap(mN.largeIcon);
4432 }
Selim Cinek88188f22017-09-19 16:46:56 -07004433 boolean showLargeIcon = mN.mLargeIcon != null && !hideLargeIcon;
4434 if (showLargeIcon) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004435 contentView.setViewVisibility(R.id.right_icon, View.VISIBLE);
4436 contentView.setImageViewIcon(R.id.right_icon, mN.mLargeIcon);
4437 processLargeLegacyIcon(mN.mLargeIcon, contentView);
Adrian Roos2d5dbba2016-06-08 17:11:53 -07004438 int endMargin = R.dimen.notification_content_picture_margin;
4439 contentView.setViewLayoutMarginEndDimen(R.id.line1, endMargin);
4440 contentView.setViewLayoutMarginEndDimen(R.id.text, endMargin);
4441 contentView.setViewLayoutMarginEndDimen(R.id.progress, endMargin);
Selim Cinek88188f22017-09-19 16:46:56 -07004442 }
4443 // Bind the reply action
4444 Action action = findReplyAction();
Selim Cinek5d6ef8d2017-05-18 22:16:00 -07004445
Selim Cinek88188f22017-09-19 16:46:56 -07004446 boolean actionVisible = action != null && (showLargeIcon || alwaysShowReply);
4447 int replyId = showLargeIcon ? R.id.reply_icon_action : R.id.right_icon;
4448 if (actionVisible) {
4449 // We're only showing the icon as big if we're hiding the large icon
4450 int contrastColor = resolveContrastColor();
4451 int iconColor;
4452 if (showLargeIcon) {
Sunny Goyal5b153922017-09-21 21:00:36 -07004453 contentView.setDrawableTint(R.id.reply_icon_action,
Selim Cinek5d6ef8d2017-05-18 22:16:00 -07004454 true /* targetBackground */,
Sunny Goyal5b153922017-09-21 21:00:36 -07004455 contrastColor, PorterDuff.Mode.SRC_ATOP);
Selim Cinek5d6ef8d2017-05-18 22:16:00 -07004456 contentView.setOnClickPendingIntent(R.id.right_icon,
4457 action.actionIntent);
Selim Cinek5d6ef8d2017-05-18 22:16:00 -07004458 contentView.setRemoteInputs(R.id.right_icon, action.mRemoteInputs);
Selim Cinek88188f22017-09-19 16:46:56 -07004459 iconColor = NotificationColorUtil.isColorLight(contrastColor)
4460 ? Color.BLACK : Color.WHITE;
4461 } else {
4462 contentView.setImageViewResource(R.id.right_icon,
4463 R.drawable.ic_reply_notification_large);
4464 contentView.setViewVisibility(R.id.right_icon, View.VISIBLE);
4465 iconColor = contrastColor;
Selim Cinek5d6ef8d2017-05-18 22:16:00 -07004466 }
Selim Cinek88188f22017-09-19 16:46:56 -07004467 contentView.setDrawableTint(replyId,
4468 false /* targetBackground */,
4469 iconColor,
4470 PorterDuff.Mode.SRC_ATOP);
4471 contentView.setOnClickPendingIntent(replyId,
4472 action.actionIntent);
4473 contentView.setRemoteInputs(replyId, action.mRemoteInputs);
4474 } else {
4475 contentView.setRemoteInputs(R.id.right_icon, null);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004476 }
Selim Cinek88188f22017-09-19 16:46:56 -07004477 contentView.setViewVisibility(R.id.reply_icon_action, actionVisible && showLargeIcon
4478 ? View.VISIBLE
4479 : View.GONE);
4480 contentView.setViewVisibility(R.id.right_icon_container, actionVisible || showLargeIcon
Selim Cinek5d6ef8d2017-05-18 22:16:00 -07004481 ? View.VISIBLE
4482 : View.GONE);
4483 }
4484
4485 private Action findReplyAction() {
4486 ArrayList<Action> actions = mActions;
4487 if (mOriginalActions != null) {
4488 actions = mOriginalActions;
4489 }
4490 int numActions = actions.size();
4491 for (int i = 0; i < numActions; i++) {
4492 Action action = actions.get(i);
4493 if (hasValidRemoteInput(action)) {
4494 return action;
4495 }
4496 }
4497 return null;
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004498 }
4499
Selim Cinekafeed292017-12-12 17:32:44 -08004500 private void bindNotificationHeader(RemoteViews contentView, boolean ambient,
4501 CharSequence secondaryHeaderText) {
Adrian Roos487374f2017-01-11 15:48:14 -08004502 bindSmallIcon(contentView, ambient);
4503 bindHeaderAppName(contentView, ambient);
4504 if (!ambient) {
4505 // Ambient view does not have these
4506 bindHeaderText(contentView);
Selim Cinekafeed292017-12-12 17:32:44 -08004507 bindHeaderTextSecondary(contentView, secondaryHeaderText);
Adrian Roos487374f2017-01-11 15:48:14 -08004508 bindHeaderChronometerAndTime(contentView);
Adrian Roos487374f2017-01-11 15:48:14 -08004509 bindProfileBadge(contentView);
4510 }
Adrian Roosd83e9992017-03-16 15:17:57 -07004511 bindExpandButton(contentView);
Julia Reynoldsfc640012018-02-21 12:25:27 -05004512 mN.mUsesStandardHeader = true;
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004513 }
4514
4515 private void bindExpandButton(RemoteViews contentView) {
Selim Cinek99104832017-01-25 14:47:33 -08004516 int color = getPrimaryHighlightColor();
Sunny Goyal5b153922017-09-21 21:00:36 -07004517 contentView.setDrawableTint(R.id.expand_button, false, color,
4518 PorterDuff.Mode.SRC_ATOP);
Selim Cinekea4bef72015-12-02 15:51:10 -08004519 contentView.setInt(R.id.notification_header, "setOriginalNotificationColor",
Selim Cinek7b9605b2017-01-19 17:36:00 -08004520 color);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004521 }
4522
Selim Cinek99104832017-01-25 14:47:33 -08004523 /**
4524 * @return the color that is used as the first primary highlight color. This is applied
4525 * in several places like the action buttons or the app name in the header.
4526 */
4527 private int getPrimaryHighlightColor() {
4528 return isColorized() ? getPrimaryTextColor() : resolveContrastColor();
4529 }
4530
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004531 private void bindHeaderChronometerAndTime(RemoteViews contentView) {
4532 if (showsTimeOrChronometer()) {
Selim Cinek29603462015-11-17 19:04:39 -08004533 contentView.setViewVisibility(R.id.time_divider, View.VISIBLE);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004534 setTextViewColorSecondary(contentView, R.id.time_divider);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004535 if (mN.extras.getBoolean(EXTRA_SHOW_CHRONOMETER)) {
4536 contentView.setViewVisibility(R.id.chronometer, View.VISIBLE);
4537 contentView.setLong(R.id.chronometer, "setBase",
4538 mN.when + (SystemClock.elapsedRealtime() - System.currentTimeMillis()));
4539 contentView.setBoolean(R.id.chronometer, "setStarted", true);
Adrian Roos96b7e202016-05-17 13:50:38 -07004540 boolean countsDown = mN.extras.getBoolean(EXTRA_CHRONOMETER_COUNT_DOWN);
Selim Cinekc3b752e2016-04-20 16:13:59 -07004541 contentView.setChronometerCountDown(R.id.chronometer, countsDown);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004542 setTextViewColorSecondary(contentView, R.id.chronometer);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004543 } else {
4544 contentView.setViewVisibility(R.id.time, View.VISIBLE);
4545 contentView.setLong(R.id.time, "setTime", mN.when);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004546 setTextViewColorSecondary(contentView, R.id.time);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004547 }
Selim Cinekb85f36fd2016-04-20 18:46:36 -07004548 } else {
4549 // We still want a time to be set but gone, such that we can show and hide it
4550 // on demand in case it's a child notification without anything in the header
4551 contentView.setLong(R.id.time, "setTime", mN.when != 0 ? mN.when : mN.creationTime);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004552 }
4553 }
4554
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07004555 private void bindHeaderText(RemoteViews contentView) {
4556 CharSequence headerText = mN.extras.getCharSequence(EXTRA_SUB_TEXT);
4557 if (headerText == null && mStyle != null && mStyle.mSummaryTextSet
Selim Cinek03d0d652015-11-13 13:18:09 -05004558 && mStyle.hasSummaryInHeader()) {
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07004559 headerText = mStyle.mSummaryText;
Selim Cinek03d0d652015-11-13 13:18:09 -05004560 }
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07004561 if (headerText == null
4562 && mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N
4563 && mN.extras.getCharSequence(EXTRA_INFO_TEXT) != null) {
4564 headerText = mN.extras.getCharSequence(EXTRA_INFO_TEXT);
4565 }
4566 if (headerText != null) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004567 // TODO: Remove the span entirely to only have the string with propper formating.
Selim Cinek48f66b72017-08-18 16:17:51 -07004568 contentView.setTextViewText(R.id.header_text, processTextSpans(
4569 processLegacyText(headerText)));
Selim Cinek7b9605b2017-01-19 17:36:00 -08004570 setTextViewColorSecondary(contentView, R.id.header_text);
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07004571 contentView.setViewVisibility(R.id.header_text, View.VISIBLE);
4572 contentView.setViewVisibility(R.id.header_text_divider, View.VISIBLE);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004573 setTextViewColorSecondary(contentView, R.id.header_text_divider);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004574 }
4575 }
4576
Selim Cinekafeed292017-12-12 17:32:44 -08004577 private void bindHeaderTextSecondary(RemoteViews contentView, CharSequence secondaryText) {
4578 if (!TextUtils.isEmpty(secondaryText)) {
4579 contentView.setTextViewText(R.id.header_text_secondary, processTextSpans(
4580 processLegacyText(secondaryText)));
4581 setTextViewColorSecondary(contentView, R.id.header_text_secondary);
4582 contentView.setViewVisibility(R.id.header_text_secondary, View.VISIBLE);
4583 contentView.setViewVisibility(R.id.header_text_secondary_divider, View.VISIBLE);
4584 setTextViewColorSecondary(contentView, R.id.header_text_secondary_divider);
4585 }
4586 }
4587
Adrian Rooseba05822016-04-22 17:09:27 -07004588 /**
4589 * @hide
4590 */
4591 public String loadHeaderAppName() {
Dan Sandler732bd6c2016-04-12 14:20:32 -04004592 CharSequence name = null;
4593 final PackageManager pm = mContext.getPackageManager();
4594 if (mN.extras.containsKey(EXTRA_SUBSTITUTE_APP_NAME)) {
4595 // only system packages which lump together a bunch of unrelated stuff
4596 // may substitute a different name to make the purpose of the
4597 // notification more clear. the correct package label should always
4598 // be accessible via SystemUI.
4599 final String pkg = mContext.getPackageName();
4600 final String subName = mN.extras.getString(EXTRA_SUBSTITUTE_APP_NAME);
4601 if (PackageManager.PERMISSION_GRANTED == pm.checkPermission(
4602 android.Manifest.permission.SUBSTITUTE_NOTIFICATION_APP_NAME, pkg)) {
4603 name = subName;
4604 } else {
4605 Log.w(TAG, "warning: pkg "
4606 + pkg + " attempting to substitute app name '" + subName
4607 + "' without holding perm "
4608 + android.Manifest.permission.SUBSTITUTE_NOTIFICATION_APP_NAME);
4609 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004610 }
Dan Sandler732bd6c2016-04-12 14:20:32 -04004611 if (TextUtils.isEmpty(name)) {
4612 name = pm.getApplicationLabel(mContext.getApplicationInfo());
4613 }
4614 if (TextUtils.isEmpty(name)) {
4615 // still nothing?
4616 return null;
4617 }
4618
4619 return String.valueOf(name);
4620 }
Adrian Roos487374f2017-01-11 15:48:14 -08004621 private void bindHeaderAppName(RemoteViews contentView, boolean ambient) {
Dan Sandler732bd6c2016-04-12 14:20:32 -04004622 contentView.setTextViewText(R.id.app_name_text, loadHeaderAppName());
Adrian Roos72171622017-01-27 10:32:06 -08004623 if (isColorized() && !ambient) {
Selim Cinek7b9605b2017-01-19 17:36:00 -08004624 setTextViewColorPrimary(contentView, R.id.app_name_text);
4625 } else {
4626 contentView.setTextColor(R.id.app_name_text,
4627 ambient ? resolveAmbientColor() : resolveContrastColor());
4628 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004629 }
4630
Adrian Roos487374f2017-01-11 15:48:14 -08004631 private void bindSmallIcon(RemoteViews contentView, boolean ambient) {
Selim Cinek279fa862016-06-14 10:57:25 -07004632 if (mN.mSmallIcon == null && mN.icon != 0) {
4633 mN.mSmallIcon = Icon.createWithResource(mContext, mN.icon);
4634 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004635 contentView.setImageViewIcon(R.id.icon, mN.mSmallIcon);
Sunny Goyal5b153922017-09-21 21:00:36 -07004636 contentView.setInt(R.id.icon, "setImageLevel", mN.iconLevel);
Adrian Roos487374f2017-01-11 15:48:14 -08004637 processSmallIconColor(mN.mSmallIcon, contentView, ambient);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004638 }
4639
Jorim Jaggi445d3c02014-08-19 22:33:42 +02004640 /**
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004641 * @return true if the built notification will show the time or the chronometer; false
4642 * otherwise
4643 */
4644 private boolean showsTimeOrChronometer() {
Selim Cinekc2c0b042016-05-18 17:13:46 -07004645 return mN.showsTime() || mN.showsChronometer();
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004646 }
4647
Christoph Studerfe718432014-09-01 18:21:18 +02004648 private void resetStandardTemplateWithActions(RemoteViews big) {
Adrian Roos4c1fcc82016-03-31 14:39:39 -07004649 // actions_container is only reset when there are no actions to avoid focus issues with
4650 // remote inputs.
Christoph Studerfe718432014-09-01 18:21:18 +02004651 big.setViewVisibility(R.id.actions, View.GONE);
Christoph Studerfe718432014-09-01 18:21:18 +02004652 big.removeAllViews(R.id.actions);
Adrian Roose458aa82015-12-08 16:17:19 -08004653
4654 big.setViewVisibility(R.id.notification_material_reply_container, View.GONE);
4655 big.setTextViewText(R.id.notification_material_reply_text_1, null);
4656
4657 big.setViewVisibility(R.id.notification_material_reply_text_2, View.GONE);
4658 big.setTextViewText(R.id.notification_material_reply_text_2, null);
4659 big.setViewVisibility(R.id.notification_material_reply_text_3, View.GONE);
4660 big.setTextViewText(R.id.notification_material_reply_text_3, null);
Adrian Roosf852a422016-06-03 13:33:43 -07004661
Selim Cineked64a142018-02-06 18:06:01 -08004662 big.setViewLayoutMarginBottomDimen(R.id.notification_action_list_margin_target,
4663 R.dimen.notification_content_margin);
Christoph Studerfe718432014-09-01 18:21:18 +02004664 }
4665
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004666 private RemoteViews applyStandardTemplateWithActions(int layoutId) {
Adrian Roos70d7aa32017-01-11 15:39:06 -08004667 return applyStandardTemplateWithActions(layoutId, mParams.reset().fillTextsFrom(this));
Adrian Roos48d746a2016-04-12 14:57:28 -07004668 }
4669
Adrian Roos70d7aa32017-01-11 15:39:06 -08004670 private RemoteViews applyStandardTemplateWithActions(int layoutId,
4671 StandardTemplateParams p) {
4672 RemoteViews big = applyStandardTemplate(layoutId, p);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004673
Christoph Studerfe718432014-09-01 18:21:18 +02004674 resetStandardTemplateWithActions(big);
4675
Adrian Roose458aa82015-12-08 16:17:19 -08004676 boolean validRemoteInput = false;
4677
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004678 int N = mActions.size();
Adrian Roos487374f2017-01-11 15:48:14 -08004679 boolean emphazisedMode = mN.fullScreenIntent != null && !p.ambient;
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004680 big.setBoolean(R.id.actions, "setEmphasizedMode", emphazisedMode);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004681 if (N > 0) {
Adrian Roos7052de52016-03-03 15:53:34 -08004682 big.setViewVisibility(R.id.actions_container, View.VISIBLE);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004683 big.setViewVisibility(R.id.actions, View.VISIBLE);
Selim Cineked64a142018-02-06 18:06:01 -08004684 big.setViewLayoutMarginBottomDimen(R.id.notification_action_list_margin_target, 0);
Daniel Sandler8680bf82012-05-15 16:52:52 -04004685 if (N>MAX_ACTION_BUTTONS) N=MAX_ACTION_BUTTONS;
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004686 for (int i=0; i<N; i++) {
Adrian Roose458aa82015-12-08 16:17:19 -08004687 Action action = mActions.get(i);
Selim Cinekffcc7cf2018-02-06 17:43:51 -08004688 boolean actionHasValidInput = hasValidRemoteInput(action);
4689 validRemoteInput |= actionHasValidInput;
Adrian Roose458aa82015-12-08 16:17:19 -08004690
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004691 final RemoteViews button = generateActionButton(action, emphazisedMode,
Adrian Roos487374f2017-01-11 15:48:14 -08004692 i % 2 != 0, p.ambient);
Selim Cinekffcc7cf2018-02-06 17:43:51 -08004693 if (actionHasValidInput) {
4694 // Clear the drawable
4695 button.setInt(R.id.action0, "setBackgroundResource", 0);
4696 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004697 big.addView(R.id.actions, button);
4698 }
Adrian Roos4c1fcc82016-03-31 14:39:39 -07004699 } else {
4700 big.setViewVisibility(R.id.actions_container, View.GONE);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004701 }
Adrian Roose458aa82015-12-08 16:17:19 -08004702
4703 CharSequence[] replyText = mN.extras.getCharSequenceArray(EXTRA_REMOTE_INPUT_HISTORY);
Adrian Roos487374f2017-01-11 15:48:14 -08004704 if (!p.ambient && validRemoteInput && replyText != null
Adrian Roose458aa82015-12-08 16:17:19 -08004705 && replyText.length > 0 && !TextUtils.isEmpty(replyText[0])) {
4706 big.setViewVisibility(R.id.notification_material_reply_container, View.VISIBLE);
Selim Cinek48f66b72017-08-18 16:17:51 -07004707 big.setTextViewText(R.id.notification_material_reply_text_1,
4708 processTextSpans(replyText[0]));
Selim Cinek7b9605b2017-01-19 17:36:00 -08004709 setTextViewColorSecondary(big, R.id.notification_material_reply_text_1);
Adrian Roose458aa82015-12-08 16:17:19 -08004710
4711 if (replyText.length > 1 && !TextUtils.isEmpty(replyText[1])) {
4712 big.setViewVisibility(R.id.notification_material_reply_text_2, View.VISIBLE);
Selim Cinek48f66b72017-08-18 16:17:51 -07004713 big.setTextViewText(R.id.notification_material_reply_text_2,
4714 processTextSpans(replyText[1]));
Selim Cinek7b9605b2017-01-19 17:36:00 -08004715 setTextViewColorSecondary(big, R.id.notification_material_reply_text_2);
Adrian Roose458aa82015-12-08 16:17:19 -08004716
4717 if (replyText.length > 2 && !TextUtils.isEmpty(replyText[2])) {
4718 big.setViewVisibility(
4719 R.id.notification_material_reply_text_3, View.VISIBLE);
Selim Cinek48f66b72017-08-18 16:17:51 -07004720 big.setTextViewText(R.id.notification_material_reply_text_3,
4721 processTextSpans(replyText[2]));
Selim Cinek7b9605b2017-01-19 17:36:00 -08004722 setTextViewColorSecondary(big, R.id.notification_material_reply_text_3);
Adrian Roose458aa82015-12-08 16:17:19 -08004723 }
4724 }
4725 }
4726
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004727 return big;
4728 }
4729
Adrian Roose458aa82015-12-08 16:17:19 -08004730 private boolean hasValidRemoteInput(Action action) {
4731 if (TextUtils.isEmpty(action.title) || action.actionIntent == null) {
4732 // Weird actions
4733 return false;
4734 }
4735
4736 RemoteInput[] remoteInputs = action.getRemoteInputs();
4737 if (remoteInputs == null) {
4738 return false;
4739 }
4740
4741 for (RemoteInput r : remoteInputs) {
4742 CharSequence[] choices = r.getChoices();
4743 if (r.getAllowFreeFormInput() || (choices != null && choices.length != 0)) {
4744 return true;
4745 }
4746 }
4747 return false;
4748 }
4749
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004750 /**
4751 * Construct a RemoteViews for the final 1U notification layout. In order:
4752 * 1. Custom contentView from the caller
4753 * 2. Style's proposed content view
4754 * 3. Standard template view
4755 */
Julia Reynolds3b848122016-02-26 10:45:32 -05004756 public RemoteViews createContentView() {
Selim Cinek7d1009b2017-01-25 15:28:28 -08004757 return createContentView(false /* increasedheight */ );
4758 }
4759
4760 /**
4761 * Construct a RemoteViews for the smaller content view.
4762 *
4763 * @param increasedHeight true if this layout be created with an increased height. Some
4764 * styles may support showing more then just that basic 1U size
4765 * and the system may decide to render important notifications
4766 * slightly bigger even when collapsed.
4767 *
4768 * @hide
4769 */
4770 public RemoteViews createContentView(boolean increasedHeight) {
Selim Cineka7679b62017-05-10 16:33:25 -07004771 if (mN.contentView != null && useExistingRemoteView()) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004772 return mN.contentView;
4773 } else if (mStyle != null) {
Selim Cinek7d1009b2017-01-25 15:28:28 -08004774 final RemoteViews styleView = mStyle.makeContentView(increasedHeight);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004775 if (styleView != null) {
4776 return styleView;
4777 }
Joe Onorato46439ce2010-11-19 13:56:21 -08004778 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004779 return applyStandardTemplate(getBaseLayoutResource());
Joe Onorato46439ce2010-11-19 13:56:21 -08004780 }
4781
Selim Cineka7679b62017-05-10 16:33:25 -07004782 private boolean useExistingRemoteView() {
4783 return mStyle == null || (!mStyle.displayCustomViewInline()
4784 && !mRebuildStyledRemoteViews);
4785 }
4786
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004787 /**
4788 * Construct a RemoteViews for the final big notification layout.
4789 */
Julia Reynolds3b848122016-02-26 10:45:32 -05004790 public RemoteViews createBigContentView() {
Selim Cinek850a8542015-11-11 11:48:36 -05004791 RemoteViews result = null;
Selim Cineka7679b62017-05-10 16:33:25 -07004792 if (mN.bigContentView != null && useExistingRemoteView()) {
Julia Reynolds089e3e42015-11-18 09:59:57 -05004793 return mN.bigContentView;
4794 } else if (mStyle != null) {
Selim Cinek850a8542015-11-11 11:48:36 -05004795 result = mStyle.makeBigContentView();
Selim Cinek90dcf6d2015-11-18 20:24:13 -08004796 hideLine1Text(result);
Selim Cinekcc10bfb2016-02-10 16:24:21 -08004797 } else if (mActions.size() != 0) {
4798 result = applyStandardTemplateWithActions(getBigBaseLayoutResource());
Selim Cinek850a8542015-11-11 11:48:36 -05004799 }
Selim Cinek6743c0b2017-01-18 18:24:01 -08004800 makeHeaderExpanded(result);
Selim Cinek850a8542015-11-11 11:48:36 -05004801 return result;
4802 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004803
Selim Cinekeaa29ca2015-11-23 13:51:13 -08004804 /**
Selim Cinek414ad332017-02-24 19:06:12 -08004805 * Construct a RemoteViews for the final notification header only. This will not be
4806 * colorized.
Selim Cinekeaa29ca2015-11-23 13:51:13 -08004807 *
Adrian Roos6f6e1592017-05-02 16:22:53 -07004808 * @param ambient if true, generate the header for the ambient display layout.
Selim Cinekeaa29ca2015-11-23 13:51:13 -08004809 * @hide
4810 */
Adrian Roos6f6e1592017-05-02 16:22:53 -07004811 public RemoteViews makeNotificationHeader(boolean ambient) {
Selim Cinek414ad332017-02-24 19:06:12 -08004812 Boolean colorized = (Boolean) mN.extras.get(EXTRA_COLORIZED);
4813 mN.extras.putBoolean(EXTRA_COLORIZED, false);
Selim Cinekeaa29ca2015-11-23 13:51:13 -08004814 RemoteViews header = new BuilderRemoteViews(mContext.getApplicationInfo(),
Adrian Roos6f6e1592017-05-02 16:22:53 -07004815 ambient ? R.layout.notification_template_ambient_header
4816 : R.layout.notification_template_header);
Selim Cinekeaa29ca2015-11-23 13:51:13 -08004817 resetNotificationHeader(header);
Selim Cinekafeed292017-12-12 17:32:44 -08004818 bindNotificationHeader(header, ambient, null);
Selim Cinek414ad332017-02-24 19:06:12 -08004819 if (colorized != null) {
4820 mN.extras.putBoolean(EXTRA_COLORIZED, colorized);
4821 } else {
4822 mN.extras.remove(EXTRA_COLORIZED);
4823 }
Selim Cinekeaa29ca2015-11-23 13:51:13 -08004824 return header;
4825 }
4826
Adrian Roos487374f2017-01-11 15:48:14 -08004827 /**
4828 * Construct a RemoteViews for the ambient version of the notification.
4829 *
4830 * @hide
4831 */
4832 public RemoteViews makeAmbientNotification() {
4833 RemoteViews ambient = applyStandardTemplateWithActions(
4834 R.layout.notification_template_material_ambient,
Adrian Roos0bc3f6a2017-03-06 11:54:05 -08004835 mParams.reset().ambient(true).fillTextsFrom(this).hasProgress(false));
Adrian Roos487374f2017-01-11 15:48:14 -08004836 return ambient;
4837 }
4838
Selim Cinek29603462015-11-17 19:04:39 -08004839 private void hideLine1Text(RemoteViews result) {
Selim Cinekcc10bfb2016-02-10 16:24:21 -08004840 if (result != null) {
4841 result.setViewVisibility(R.id.text_line_1, View.GONE);
4842 }
Selim Cinek29603462015-11-17 19:04:39 -08004843 }
4844
Selim Cinek6743c0b2017-01-18 18:24:01 -08004845 /**
4846 * Adapt the Notification header if this view is used as an expanded view.
4847 *
4848 * @hide
4849 */
4850 public static void makeHeaderExpanded(RemoteViews result) {
Selim Cinekcc10bfb2016-02-10 16:24:21 -08004851 if (result != null) {
4852 result.setBoolean(R.id.notification_header, "setExpanded", true);
4853 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004854 }
4855
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004856 /**
4857 * Construct a RemoteViews for the final heads-up notification layout.
Selim Cinek87ed69b2017-02-09 15:59:43 -08004858 *
4859 * @param increasedHeight true if this layout be created with an increased height. Some
4860 * styles may support showing more then just that basic 1U size
4861 * and the system may decide to render important notifications
4862 * slightly bigger even when collapsed.
4863 *
4864 * @hide
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004865 */
Selim Cinek87ed69b2017-02-09 15:59:43 -08004866 public RemoteViews createHeadsUpContentView(boolean increasedHeight) {
Selim Cineka7679b62017-05-10 16:33:25 -07004867 if (mN.headsUpContentView != null && useExistingRemoteView()) {
Julia Reynolds089e3e42015-11-18 09:59:57 -05004868 return mN.headsUpContentView;
4869 } else if (mStyle != null) {
Selim Cinek87ed69b2017-02-09 15:59:43 -08004870 final RemoteViews styleView = mStyle.makeHeadsUpContentView(increasedHeight);
4871 if (styleView != null) {
4872 return styleView;
4873 }
Julia Reynolds089e3e42015-11-18 09:59:57 -05004874 } else if (mActions.size() == 0) {
4875 return null;
4876 }
4877
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004878 return applyStandardTemplateWithActions(getBigBaseLayoutResource());
Chris Wren8fd39ec2014-02-27 17:43:26 -05004879 }
4880
Selim Cinek624c02db2015-12-14 21:00:02 -08004881 /**
Selim Cinek87ed69b2017-02-09 15:59:43 -08004882 * Construct a RemoteViews for the final heads-up notification layout.
4883 */
4884 public RemoteViews createHeadsUpContentView() {
4885 return createHeadsUpContentView(false /* useIncreasedHeight */);
4886 }
4887
4888 /**
Selim Cinek624c02db2015-12-14 21:00:02 -08004889 * Construct a RemoteViews for the display in public contexts like on the lockscreen.
4890 *
4891 * @hide
4892 */
4893 public RemoteViews makePublicContentView() {
Adrian Roos1a1ecfc2017-04-17 11:17:59 -07004894 return makePublicView(false /* ambient */);
4895 }
4896
4897 /**
4898 * Construct a RemoteViews for the display in public contexts like on the lockscreen.
4899 *
4900 * @hide
4901 */
4902 public RemoteViews makePublicAmbientNotification() {
4903 return makePublicView(true /* ambient */);
4904 }
4905
4906 private RemoteViews makePublicView(boolean ambient) {
Selim Cinek624c02db2015-12-14 21:00:02 -08004907 if (mN.publicVersion != null) {
4908 final Builder builder = recoverBuilder(mContext, mN.publicVersion);
Adrian Roos1a1ecfc2017-04-17 11:17:59 -07004909 return ambient ? builder.makeAmbientNotification() : builder.createContentView();
Selim Cinek624c02db2015-12-14 21:00:02 -08004910 }
4911 Bundle savedBundle = mN.extras;
4912 Style style = mStyle;
4913 mStyle = null;
4914 Icon largeIcon = mN.mLargeIcon;
4915 mN.mLargeIcon = null;
Selim Cinek279fa862016-06-14 10:57:25 -07004916 Bitmap largeIconLegacy = mN.largeIcon;
4917 mN.largeIcon = null;
Adrian Roosb19b06b2017-05-02 18:54:40 -07004918 ArrayList<Action> actions = mActions;
4919 mActions = new ArrayList<>();
Selim Cinek624c02db2015-12-14 21:00:02 -08004920 Bundle publicExtras = new Bundle();
4921 publicExtras.putBoolean(EXTRA_SHOW_WHEN,
4922 savedBundle.getBoolean(EXTRA_SHOW_WHEN));
4923 publicExtras.putBoolean(EXTRA_SHOW_CHRONOMETER,
4924 savedBundle.getBoolean(EXTRA_SHOW_CHRONOMETER));
Adrian Roos96b7e202016-05-17 13:50:38 -07004925 publicExtras.putBoolean(EXTRA_CHRONOMETER_COUNT_DOWN,
4926 savedBundle.getBoolean(EXTRA_CHRONOMETER_COUNT_DOWN));
Selim Cinek624c02db2015-12-14 21:00:02 -08004927 mN.extras = publicExtras;
Selim Cinek499c20f2017-07-20 14:06:09 -07004928 RemoteViews view;
4929 if (ambient) {
4930 publicExtras.putCharSequence(EXTRA_TITLE,
4931 mContext.getString(com.android.internal.R.string.notification_hidden_text));
4932 view = makeAmbientNotification();
4933 } else{
4934 view = makeNotificationHeader(false /* ambient */);
4935 view.setBoolean(R.id.notification_header, "setExpandOnlyOnButton", true);
4936 }
Selim Cinek624c02db2015-12-14 21:00:02 -08004937 mN.extras = savedBundle;
4938 mN.mLargeIcon = largeIcon;
Selim Cinek279fa862016-06-14 10:57:25 -07004939 mN.largeIcon = largeIconLegacy;
Adrian Roosb19b06b2017-05-02 18:54:40 -07004940 mActions = actions;
Selim Cinek624c02db2015-12-14 21:00:02 -08004941 mStyle = style;
Adrian Roos1a1ecfc2017-04-17 11:17:59 -07004942 return view;
Selim Cinek624c02db2015-12-14 21:00:02 -08004943 }
4944
Selim Cinek6743c0b2017-01-18 18:24:01 -08004945 /**
4946 * Construct a content view for the display when low - priority
4947 *
4948 * @param useRegularSubtext uses the normal subtext set if there is one available. Otherwise
4949 * a new subtext is created consisting of the content of the
4950 * notification.
4951 * @hide
4952 */
4953 public RemoteViews makeLowPriorityContentView(boolean useRegularSubtext) {
4954 int color = mN.color;
4955 mN.color = COLOR_DEFAULT;
4956 CharSequence summary = mN.extras.getCharSequence(EXTRA_SUB_TEXT);
4957 if (!useRegularSubtext || TextUtils.isEmpty(summary)) {
4958 CharSequence newSummary = createSummaryText();
4959 if (!TextUtils.isEmpty(newSummary)) {
4960 mN.extras.putCharSequence(EXTRA_SUB_TEXT, newSummary);
4961 }
4962 }
Selim Cinek875ba9b2017-02-13 16:20:17 -08004963
Adrian Roos6f6e1592017-05-02 16:22:53 -07004964 RemoteViews header = makeNotificationHeader(false /* ambient */);
Selim Cinek1b554392017-02-28 17:22:49 -08004965 header.setBoolean(R.id.notification_header, "setAcceptAllTouches", true);
Selim Cinek6743c0b2017-01-18 18:24:01 -08004966 if (summary != null) {
4967 mN.extras.putCharSequence(EXTRA_SUB_TEXT, summary);
4968 } else {
4969 mN.extras.remove(EXTRA_SUB_TEXT);
4970 }
4971 mN.color = color;
4972 return header;
4973 }
Selim Cinek624c02db2015-12-14 21:00:02 -08004974
Selim Cinek6743c0b2017-01-18 18:24:01 -08004975 private CharSequence createSummaryText() {
4976 CharSequence titleText = mN.extras.getCharSequence(Notification.EXTRA_TITLE);
4977 if (USE_ONLY_TITLE_IN_LOW_PRIORITY_SUMMARY) {
4978 return titleText;
4979 }
4980 SpannableStringBuilder summary = new SpannableStringBuilder();
4981 if (titleText == null) {
4982 titleText = mN.extras.getCharSequence(Notification.EXTRA_TITLE_BIG);
4983 }
4984 BidiFormatter bidi = BidiFormatter.getInstance();
4985 if (titleText != null) {
4986 summary.append(bidi.unicodeWrap(titleText));
4987 }
4988 CharSequence contentText = mN.extras.getCharSequence(Notification.EXTRA_TEXT);
4989 if (titleText != null && contentText != null) {
4990 summary.append(bidi.unicodeWrap(mContext.getText(
4991 R.string.notification_header_divider_symbol_with_spaces)));
4992 }
4993 if (contentText != null) {
4994 summary.append(bidi.unicodeWrap(contentText));
4995 }
4996 return summary;
4997 }
Chris Wren8fd39ec2014-02-27 17:43:26 -05004998
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004999 private RemoteViews generateActionButton(Action action, boolean emphazisedMode,
Adrian Roos487374f2017-01-11 15:48:14 -08005000 boolean oddAction, boolean ambient) {
Daniel Sandler8680bf82012-05-15 16:52:52 -04005001 final boolean tombstone = (action.actionIntent == null);
Selim Cinekf33b1112015-07-15 17:45:11 -07005002 RemoteViews button = new BuilderRemoteViews(mContext.getApplicationInfo(),
Selim Cinek06e9e1f2016-07-08 17:14:16 -07005003 emphazisedMode ? getEmphasizedActionLayoutResource()
5004 : tombstone ? getActionTombstoneLayoutResource()
5005 : getActionLayoutResource());
Daniel Sandler8680bf82012-05-15 16:52:52 -04005006 if (!tombstone) {
Daniel Sandlere5518842012-05-10 16:20:40 -04005007 button.setOnClickPendingIntent(R.id.action0, action.actionIntent);
Daniel Sandlere5518842012-05-10 16:20:40 -04005008 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04005009 button.setContentDescription(R.id.action0, action.title);
Adrian Roosfe84e1f2015-11-04 15:55:39 -08005010 if (action.mRemoteInputs != null) {
5011 button.setRemoteInputs(R.id.action0, action.mRemoteInputs);
5012 }
Selim Cinek7b9605b2017-01-19 17:36:00 -08005013 // TODO: handle emphasized mode / actions right
Selim Cinek06e9e1f2016-07-08 17:14:16 -07005014 if (emphazisedMode) {
Selim Cinek981962e2016-07-20 20:41:58 -07005015 // change the background bgColor
Selim Cinek622c64a2017-04-17 17:10:05 -07005016 int bgColor;
5017 if (isColorized()) {
5018 bgColor = oddAction ? getActionBarColor() : getActionBarColorDeEmphasized();
5019 } else {
5020 bgColor = mContext.getColor(oddAction ? R.color.notification_action_list
5021 : R.color.notification_action_list_dark);
5022 }
Sunny Goyal5b153922017-09-21 21:00:36 -07005023 button.setDrawableTint(R.id.button_holder, true,
5024 bgColor, PorterDuff.Mode.SRC_ATOP);
Selim Cinek981962e2016-07-20 20:41:58 -07005025 CharSequence title = action.title;
5026 ColorStateList[] outResultColor = null;
5027 if (isLegacy()) {
Selim Cinek87c31532017-08-18 18:53:44 -07005028 title = NotificationColorUtil.clearColorSpans(title);
Selim Cinek981962e2016-07-20 20:41:58 -07005029 } else {
5030 outResultColor = new ColorStateList[1];
5031 title = ensureColorSpanContrast(title, bgColor, outResultColor);
5032 }
Selim Cinek48f66b72017-08-18 16:17:51 -07005033 button.setTextViewText(R.id.action0, processTextSpans(title));
Selim Cinek7b9605b2017-01-19 17:36:00 -08005034 setTextViewColorPrimary(button, R.id.action0);
Selim Cinek981962e2016-07-20 20:41:58 -07005035 if (outResultColor != null && outResultColor[0] != null) {
5036 // We need to set the text color as well since changing a text to uppercase
5037 // clears its spans.
5038 button.setTextColor(R.id.action0, outResultColor[0]);
Anthony Chenad4d1582017-04-10 16:07:58 -07005039 } else if (mN.color != COLOR_DEFAULT && !isColorized() && mTintActionButtons) {
Selim Cinek981962e2016-07-20 20:41:58 -07005040 button.setTextColor(R.id.action0,resolveContrastColor());
5041 }
Selim Cinek06e9e1f2016-07-08 17:14:16 -07005042 } else {
Selim Cinek48f66b72017-08-18 16:17:51 -07005043 button.setTextViewText(R.id.action0, processTextSpans(
5044 processLegacyText(action.title)));
Adrian Roos72171622017-01-27 10:32:06 -08005045 if (isColorized() && !ambient) {
Selim Cinek7b9605b2017-01-19 17:36:00 -08005046 setTextViewColorPrimary(button, R.id.action0);
Anthony Chenad4d1582017-04-10 16:07:58 -07005047 } else if (mN.color != COLOR_DEFAULT && mTintActionButtons) {
Adrian Roos487374f2017-01-11 15:48:14 -08005048 button.setTextColor(R.id.action0,
5049 ambient ? resolveAmbientColor() : resolveContrastColor());
Selim Cinek06e9e1f2016-07-08 17:14:16 -07005050 }
Adrian Roosfe84e1f2015-11-04 15:55:39 -08005051 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04005052 return button;
5053 }
5054
Joe Onoratocb109a02011-01-18 17:57:41 -08005055 /**
Selim Cinek981962e2016-07-20 20:41:58 -07005056 * Ensures contrast on color spans against a background color. also returns the color of the
5057 * text if a span was found that spans over the whole text.
5058 *
5059 * @param charSequence the charSequence on which the spans are
5060 * @param background the background color to ensure the contrast against
5061 * @param outResultColor an array in which a color will be returned as the first element if
5062 * there exists a full length color span.
5063 * @return the contrasted charSequence
5064 */
5065 private CharSequence ensureColorSpanContrast(CharSequence charSequence, int background,
5066 ColorStateList[] outResultColor) {
5067 if (charSequence instanceof Spanned) {
5068 Spanned ss = (Spanned) charSequence;
5069 Object[] spans = ss.getSpans(0, ss.length(), Object.class);
5070 SpannableStringBuilder builder = new SpannableStringBuilder(ss.toString());
5071 for (Object span : spans) {
5072 Object resultSpan = span;
5073 int spanStart = ss.getSpanStart(span);
5074 int spanEnd = ss.getSpanEnd(span);
5075 boolean fullLength = (spanEnd - spanStart) == charSequence.length();
5076 if (resultSpan instanceof CharacterStyle) {
5077 resultSpan = ((CharacterStyle) span).getUnderlying();
5078 }
5079 if (resultSpan instanceof TextAppearanceSpan) {
5080 TextAppearanceSpan originalSpan = (TextAppearanceSpan) resultSpan;
5081 ColorStateList textColor = originalSpan.getTextColor();
5082 if (textColor != null) {
5083 int[] colors = textColor.getColors();
5084 int[] newColors = new int[colors.length];
5085 for (int i = 0; i < newColors.length; i++) {
5086 newColors[i] = NotificationColorUtil.ensureLargeTextContrast(
Anthony Chenad4d1582017-04-10 16:07:58 -07005087 colors[i], background, mInNightMode);
Selim Cinek981962e2016-07-20 20:41:58 -07005088 }
5089 textColor = new ColorStateList(textColor.getStates().clone(),
5090 newColors);
5091 resultSpan = new TextAppearanceSpan(
5092 originalSpan.getFamily(),
5093 originalSpan.getTextStyle(),
5094 originalSpan.getTextSize(),
5095 textColor,
5096 originalSpan.getLinkTextColor());
5097 if (fullLength) {
5098 outResultColor[0] = new ColorStateList(
5099 textColor.getStates().clone(), newColors);
5100 }
5101 }
5102 } else if (resultSpan instanceof ForegroundColorSpan) {
5103 ForegroundColorSpan originalSpan = (ForegroundColorSpan) resultSpan;
5104 int foregroundColor = originalSpan.getForegroundColor();
5105 foregroundColor = NotificationColorUtil.ensureLargeTextContrast(
Anthony Chenad4d1582017-04-10 16:07:58 -07005106 foregroundColor, background, mInNightMode);
Selim Cinek981962e2016-07-20 20:41:58 -07005107 resultSpan = new ForegroundColorSpan(foregroundColor);
5108 if (fullLength) {
5109 outResultColor[0] = ColorStateList.valueOf(foregroundColor);
5110 }
5111 } else {
5112 resultSpan = span;
5113 }
5114
5115 builder.setSpan(resultSpan, spanStart, spanEnd, ss.getSpanFlags(span));
5116 }
5117 return builder;
5118 }
5119 return charSequence;
5120 }
5121
5122 /**
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01005123 * @return Whether we are currently building a notification from a legacy (an app that
Alan Viverette3cb07a462014-06-06 14:19:53 -07005124 * doesn't create material notifications by itself) app.
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01005125 */
5126 private boolean isLegacy() {
Selim Cinek7b9605b2017-01-19 17:36:00 -08005127 if (!mIsLegacyInitialized) {
5128 mIsLegacy = mContext.getApplicationInfo().targetSdkVersion
5129 < Build.VERSION_CODES.LOLLIPOP;
5130 mIsLegacyInitialized = true;
5131 }
5132 return mIsLegacy;
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01005133 }
5134
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01005135 private CharSequence processLegacyText(CharSequence charSequence) {
Adrian Roos0bc3f6a2017-03-06 11:54:05 -08005136 return processLegacyText(charSequence, false /* ambient */);
5137 }
5138
5139 private CharSequence processLegacyText(CharSequence charSequence, boolean ambient) {
5140 boolean isAlreadyLightText = isLegacy() || textColorsNeedInversion();
5141 boolean wantLightText = ambient;
5142 if (isAlreadyLightText != wantLightText) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005143 return getColorUtil().invertCharSequenceColors(charSequence);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01005144 } else {
5145 return charSequence;
5146 }
5147 }
5148
Dan Sandler26e81cf2014-05-06 10:01:27 -04005149 /**
Selim Cinek65b2e7c2015-10-26 14:11:31 -07005150 * Apply any necessariy colors to the small icon
Dan Sandler26e81cf2014-05-06 10:01:27 -04005151 */
Adrian Roos487374f2017-01-11 15:48:14 -08005152 private void processSmallIconColor(Icon smallIcon, RemoteViews contentView,
5153 boolean ambient) {
Selim Cinekea4bef72015-12-02 15:51:10 -08005154 boolean colorable = !isLegacy() || getColorUtil().isGrayscaleIcon(mContext, smallIcon);
Selim Cinek99104832017-01-25 14:47:33 -08005155 int color = ambient ? resolveAmbientColor() : getPrimaryHighlightColor();
Selim Cinekea4bef72015-12-02 15:51:10 -08005156 if (colorable) {
Sunny Goyal5b153922017-09-21 21:00:36 -07005157 contentView.setDrawableTint(R.id.icon, false, color,
5158 PorterDuff.Mode.SRC_ATOP);
Selim Cinekea4bef72015-12-02 15:51:10 -08005159
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01005160 }
Selim Cinekea4bef72015-12-02 15:51:10 -08005161 contentView.setInt(R.id.notification_header, "setOriginalIconColor",
Adrian Roos487374f2017-01-11 15:48:14 -08005162 colorable ? color : NotificationHeaderView.NO_COLOR);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01005163 }
5164
Dan Sandler26e81cf2014-05-06 10:01:27 -04005165 /**
Selim Cinek65b2e7c2015-10-26 14:11:31 -07005166 * Make the largeIcon dark if it's a fake smallIcon (that is,
Dan Sandler26e81cf2014-05-06 10:01:27 -04005167 * if it's grayscale).
5168 */
5169 // TODO: also check bounds, transparency, that sort of thing.
Dan Sandlerd63f9322015-05-06 15:18:49 -04005170 private void processLargeLegacyIcon(Icon largeIcon, RemoteViews contentView) {
5171 if (largeIcon != null && isLegacy()
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005172 && getColorUtil().isGrayscaleIcon(mContext, largeIcon)) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07005173 // resolve color will fall back to the default when legacy
Sunny Goyal5b153922017-09-21 21:00:36 -07005174 contentView.setDrawableTint(R.id.icon, false, resolveContrastColor(),
5175 PorterDuff.Mode.SRC_ATOP);
Jorim Jaggi92df1f22014-12-16 19:44:41 +01005176 }
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01005177 }
5178
Julia Reynolds10ee1fc2015-11-09 11:04:55 -05005179 private void sanitizeColor() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005180 if (mN.color != COLOR_DEFAULT) {
5181 mN.color |= 0xFF000000; // no alpha for custom colors
Jorim Jaggi74419312014-06-10 20:57:21 +02005182 }
Jorim Jaggi74419312014-06-10 20:57:21 +02005183 }
5184
Adrian Roos4ff3b122016-02-01 12:26:13 -08005185 int resolveContrastColor() {
5186 if (mCachedContrastColorIsFor == mN.color && mCachedContrastColor != COLOR_INVALID) {
5187 return mCachedContrastColor;
Dan Sandler26e81cf2014-05-06 10:01:27 -04005188 }
Adrian Roos4ff3b122016-02-01 12:26:13 -08005189
Selim Cinekac5f0272017-05-02 16:05:41 -07005190 int color;
5191 int background = mBackgroundColorHint;
5192 if (mBackgroundColorHint == COLOR_INVALID) {
5193 background = mContext.getColor(
5194 com.android.internal.R.color.notification_material_background_color);
5195 }
5196 if (mN.color == COLOR_DEFAULT) {
5197 ensureColors();
5198 color = mSecondaryTextColor;
5199 } else {
5200 color = NotificationColorUtil.resolveContrastColor(mContext, mN.color,
Anthony Chenad4d1582017-04-10 16:07:58 -07005201 background, mInNightMode);
Selim Cinekac5f0272017-05-02 16:05:41 -07005202 }
5203 if (Color.alpha(color) < 255) {
5204 // alpha doesn't go well for color filters, so let's blend it manually
5205 color = NotificationColorUtil.compositeColors(color, background);
5206 }
Adrian Roos4ff3b122016-02-01 12:26:13 -08005207 mCachedContrastColorIsFor = mN.color;
Selim Cinekac5f0272017-05-02 16:05:41 -07005208 return mCachedContrastColor = color;
Dan Sandler26e81cf2014-05-06 10:01:27 -04005209 }
5210
Adrian Roos487374f2017-01-11 15:48:14 -08005211 int resolveAmbientColor() {
5212 if (mCachedAmbientColorIsFor == mN.color && mCachedAmbientColorIsFor != COLOR_INVALID) {
5213 return mCachedAmbientColor;
5214 }
5215 final int contrasted = NotificationColorUtil.resolveAmbientColor(mContext, mN.color);
5216
5217 mCachedAmbientColorIsFor = mN.color;
5218 return mCachedAmbientColor = contrasted;
5219 }
5220
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01005221 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005222 * Apply the unstyled operations and return a new {@link Notification} object.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04005223 * @hide
Joe Onoratocb109a02011-01-18 17:57:41 -08005224 */
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04005225 public Notification buildUnstyled() {
Daniel Sandlera0a938c2012-03-15 08:42:37 -04005226 if (mActions.size() > 0) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005227 mN.actions = new Action[mActions.size()];
5228 mActions.toArray(mN.actions);
Daniel Sandlera0a938c2012-03-15 08:42:37 -04005229 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005230 if (!mPersonList.isEmpty()) {
Selim Cineke7238dd2017-12-14 17:48:32 -08005231 mN.extras.putParcelableArrayList(EXTRA_PEOPLE_LIST, mPersonList);
Dan Sandler0bf2ed82013-12-21 23:33:41 -06005232 }
Selim Cinek247fa012016-02-18 09:50:48 -08005233 if (mN.bigContentView != null || mN.contentView != null
5234 || mN.headsUpContentView != null) {
5235 mN.extras.putBoolean(EXTRA_CONTAINS_CUSTOM_VIEW, true);
5236 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005237 return mN;
Joe Onorato46439ce2010-11-19 13:56:21 -08005238 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005239
Julia Reynolds3b848122016-02-26 10:45:32 -05005240 /**
5241 * Creates a Builder from an existing notification so further changes can be made.
5242 * @param context The context for your application / activity.
5243 * @param n The notification to create a Builder from.
5244 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005245 public static Notification.Builder recoverBuilder(Context context, Notification n) {
Christoph Studer4600f9b2014-07-22 22:44:43 +02005246 // Re-create notification context so we can access app resources.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005247 ApplicationInfo applicationInfo = n.extras.getParcelable(
5248 EXTRA_BUILDER_APPLICATION_INFO);
Christoph Studer4600f9b2014-07-22 22:44:43 +02005249 Context builderContext;
Julia Reynoldsda303542015-11-23 14:00:20 -05005250 if (applicationInfo != null) {
5251 try {
5252 builderContext = context.createApplicationContext(applicationInfo,
5253 Context.CONTEXT_RESTRICTED);
5254 } catch (NameNotFoundException e) {
5255 Log.e(TAG, "ApplicationInfo " + applicationInfo + " not found");
5256 builderContext = context; // try with our context
5257 }
5258 } else {
5259 builderContext = context; // try with given context
Christoph Studer4600f9b2014-07-22 22:44:43 +02005260 }
5261
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005262 return new Builder(builderContext, n);
Christoph Studer4600f9b2014-07-22 22:44:43 +02005263 }
5264
Daniel Sandlerf45564e2013-04-15 15:05:08 -04005265 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005266 * @deprecated Use {@link #build()} instead.
5267 */
5268 @Deprecated
5269 public Notification getNotification() {
5270 return build();
5271 }
5272
5273 /**
5274 * Combine all of the options that have been set and return a new {@link Notification}
5275 * object.
5276 */
5277 public Notification build() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005278 // first, add any extras from the calling code
5279 if (mUserExtras != null) {
5280 mN.extras = getAllExtras();
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07005281 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005282
Selim Cinekb85f36fd2016-04-20 18:46:36 -07005283 mN.creationTime = System.currentTimeMillis();
5284
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005285 // lazy stuff from mContext; see comment in Builder(Context, Notification)
Julia Reynoldsda303542015-11-23 14:00:20 -05005286 Notification.addFieldsFromContext(mContext, mN);
Christoph Studer943aa672014-08-03 20:31:16 +02005287
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005288 buildUnstyled();
Daniel Sandlerf45564e2013-04-15 15:05:08 -04005289
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005290 if (mStyle != null) {
Selim Cinekd0426622017-07-11 13:19:59 +02005291 mStyle.reduceImageSizes(mContext);
5292 mStyle.purgeResources();
Selim Cinek90343862018-02-01 11:07:11 -08005293 mStyle.validate(mContext);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005294 mStyle.buildStyled(mN);
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005295 }
Selim Cinekd0426622017-07-11 13:19:59 +02005296 mN.reduceImageSizes(mContext);
5297
Adrian Roos5081c0d2016-02-26 16:04:19 -08005298 if (mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N
Selim Cineka7679b62017-05-10 16:33:25 -07005299 && (useExistingRemoteView())) {
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05005300 if (mN.contentView == null) {
Julia Reynolds3b848122016-02-26 10:45:32 -05005301 mN.contentView = createContentView();
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05005302 mN.extras.putInt(EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT,
5303 mN.contentView.getSequenceNumber());
5304 }
5305 if (mN.bigContentView == null) {
Julia Reynolds3b848122016-02-26 10:45:32 -05005306 mN.bigContentView = createBigContentView();
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05005307 if (mN.bigContentView != null) {
5308 mN.extras.putInt(EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT,
5309 mN.bigContentView.getSequenceNumber());
5310 }
5311 }
5312 if (mN.headsUpContentView == null) {
Julia Reynolds3b848122016-02-26 10:45:32 -05005313 mN.headsUpContentView = createHeadsUpContentView();
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05005314 if (mN.headsUpContentView != null) {
5315 mN.extras.putInt(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT,
5316 mN.headsUpContentView.getSequenceNumber());
5317 }
5318 }
5319 }
5320
Julia Reynolds4c0c2022016-02-02 15:11:59 -05005321 if ((mN.defaults & DEFAULT_LIGHTS) != 0) {
5322 mN.flags |= FLAG_SHOW_LIGHTS;
5323 }
5324
Adrian Roosfb921842017-10-26 14:49:56 +02005325 mN.allPendingIntents = null;
5326
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005327 return mN;
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005328 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05005329
5330 /**
5331 * Apply this Builder to an existing {@link Notification} object.
5332 *
5333 * @hide
5334 */
5335 public Notification buildInto(Notification n) {
Daniel Sandler1a497d32013-04-18 14:52:45 -04005336 build().cloneInto(n, true);
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05005337 return n;
5338 }
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01005339
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05005340 /**
Adrian Roos184bfe022016-03-03 13:41:44 -08005341 * Removes RemoteViews that were created for compatibility from {@param n}, if they did not
Julia Reynolds8a3b4592017-06-26 17:15:14 -04005342 * change. Also removes extenders on low ram devices, as
5343 * {@link android.service.notification.NotificationListenerService} services are disabled.
Adrian Roos184bfe022016-03-03 13:41:44 -08005344 *
5345 * @return {@param n}, if no stripping is needed, otherwise a stripped clone of {@param n}.
5346 *
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05005347 * @hide
5348 */
Julia Reynolds8a3b4592017-06-26 17:15:14 -04005349 public static Notification maybeCloneStrippedForDelivery(Notification n, boolean isLowRam) {
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05005350 String templateClass = n.extras.getString(EXTRA_TEMPLATE);
Adrian Roos184bfe022016-03-03 13:41:44 -08005351
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05005352 // Only strip views for known Styles because we won't know how to
5353 // re-create them otherwise.
Julia Reynolds8a3b4592017-06-26 17:15:14 -04005354 if (!isLowRam
5355 && !TextUtils.isEmpty(templateClass)
Adrian Roos184bfe022016-03-03 13:41:44 -08005356 && getNotificationStyleClass(templateClass) == null) {
5357 return n;
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05005358 }
Adrian Roos184bfe022016-03-03 13:41:44 -08005359
5360 // Only strip unmodified BuilderRemoteViews.
5361 boolean stripContentView = n.contentView instanceof BuilderRemoteViews &&
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05005362 n.extras.getInt(EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT, -1) ==
Adrian Roos184bfe022016-03-03 13:41:44 -08005363 n.contentView.getSequenceNumber();
5364 boolean stripBigContentView = n.bigContentView instanceof BuilderRemoteViews &&
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05005365 n.extras.getInt(EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT, -1) ==
Adrian Roos184bfe022016-03-03 13:41:44 -08005366 n.bigContentView.getSequenceNumber();
5367 boolean stripHeadsUpContentView = n.headsUpContentView instanceof BuilderRemoteViews &&
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05005368 n.extras.getInt(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT, -1) ==
Adrian Roos184bfe022016-03-03 13:41:44 -08005369 n.headsUpContentView.getSequenceNumber();
5370
5371 // Nothing to do here, no need to clone.
Julia Reynolds8a3b4592017-06-26 17:15:14 -04005372 if (!isLowRam
5373 && !stripContentView && !stripBigContentView && !stripHeadsUpContentView) {
Adrian Roos184bfe022016-03-03 13:41:44 -08005374 return n;
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05005375 }
Adrian Roos184bfe022016-03-03 13:41:44 -08005376
5377 Notification clone = n.clone();
5378 if (stripContentView) {
5379 clone.contentView = null;
5380 clone.extras.remove(EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT);
5381 }
5382 if (stripBigContentView) {
5383 clone.bigContentView = null;
5384 clone.extras.remove(EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT);
5385 }
5386 if (stripHeadsUpContentView) {
5387 clone.headsUpContentView = null;
5388 clone.extras.remove(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT);
5389 }
Julia Reynolds8a3b4592017-06-26 17:15:14 -04005390 if (isLowRam) {
5391 clone.extras.remove(Notification.TvExtender.EXTRA_TV_EXTENDER);
5392 clone.extras.remove(WearableExtender.EXTRA_WEARABLE_EXTENSIONS);
5393 clone.extras.remove(CarExtender.EXTRA_CAR_EXTENDER);
5394 }
Adrian Roos184bfe022016-03-03 13:41:44 -08005395 return clone;
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05005396 }
5397
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01005398 private int getBaseLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07005399 return R.layout.notification_template_material_base;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01005400 }
5401
5402 private int getBigBaseLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07005403 return R.layout.notification_template_material_big_base;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01005404 }
5405
5406 private int getBigPictureLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07005407 return R.layout.notification_template_material_big_picture;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01005408 }
5409
5410 private int getBigTextLayoutResource() {
Jorim Jaggi445d3c02014-08-19 22:33:42 +02005411 return R.layout.notification_template_material_big_text;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01005412 }
5413
5414 private int getInboxLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07005415 return R.layout.notification_template_material_inbox;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01005416 }
5417
Adrian Roosc1a80b02016-04-05 14:54:55 -07005418 private int getMessagingLayoutResource() {
5419 return R.layout.notification_template_material_messaging;
5420 }
5421
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01005422 private int getActionLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07005423 return R.layout.notification_material_action;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01005424 }
5425
Selim Cinek06e9e1f2016-07-08 17:14:16 -07005426 private int getEmphasizedActionLayoutResource() {
5427 return R.layout.notification_material_action_emphasized;
5428 }
5429
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01005430 private int getActionTombstoneLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07005431 return R.layout.notification_material_action_tombstone;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01005432 }
Selim Cinek7b9605b2017-01-19 17:36:00 -08005433
5434 private int getBackgroundColor() {
5435 if (isColorized()) {
Selim Cinek5fb73f82017-04-20 16:55:38 -07005436 return mBackgroundColor != COLOR_INVALID ? mBackgroundColor : mN.color;
Selim Cinek7b9605b2017-01-19 17:36:00 -08005437 } else {
Selim Cinekac5f0272017-05-02 16:05:41 -07005438 return mBackgroundColorHint != COLOR_INVALID ? mBackgroundColorHint
5439 : COLOR_DEFAULT;
Selim Cinek7b9605b2017-01-19 17:36:00 -08005440 }
5441 }
5442
5443 private boolean isColorized() {
5444 return mN.isColorized();
5445 }
Selim Cinek99104832017-01-25 14:47:33 -08005446
Anthony Chenad4d1582017-04-10 16:07:58 -07005447 private boolean shouldTintActionButtons() {
5448 return mTintActionButtons;
5449 }
5450
Selim Cinek99104832017-01-25 14:47:33 -08005451 private boolean textColorsNeedInversion() {
5452 if (mStyle == null || !MediaStyle.class.equals(mStyle.getClass())) {
5453 return false;
5454 }
5455 int targetSdkVersion = mContext.getApplicationInfo().targetSdkVersion;
5456 return targetSdkVersion > Build.VERSION_CODES.M
5457 && targetSdkVersion < Build.VERSION_CODES.O;
5458 }
Selim Cinek5fb73f82017-04-20 16:55:38 -07005459
5460 /**
5461 * Set a color palette to be used as the background and textColors
5462 *
5463 * @param backgroundColor the color to be used as the background
5464 * @param foregroundColor the color to be used as the foreground
5465 *
5466 * @hide
5467 */
5468 public void setColorPalette(int backgroundColor, int foregroundColor) {
5469 mBackgroundColor = backgroundColor;
5470 mForegroundColor = foregroundColor;
5471 mTextColorsAreForBackground = COLOR_INVALID;
5472 ensureColors();
5473 }
Selim Cinekac5f0272017-05-02 16:05:41 -07005474
5475 /**
5476 * Sets the background color for this notification to be a different one then the default.
5477 * This is mainly used to calculate contrast and won't necessarily be applied to the
5478 * background.
5479 *
5480 * @hide
5481 */
5482 public void setBackgroundColorHint(int backgroundColor) {
5483 mBackgroundColorHint = backgroundColor;
5484 }
Selim Cineka7679b62017-05-10 16:33:25 -07005485
5486
5487 /**
5488 * Forces all styled remoteViews to be built from scratch and not use any cached
5489 * RemoteViews.
5490 * This is needed for legacy apps that are baking in their remoteviews into the
5491 * notification.
5492 *
5493 * @hide
5494 */
5495 public void setRebuildStyledRemoteViews(boolean rebuild) {
5496 mRebuildStyledRemoteViews = rebuild;
5497 }
Selim Cinek7b9605b2017-01-19 17:36:00 -08005498 }
5499
5500 /**
Selim Cinekd0426622017-07-11 13:19:59 +02005501 * Reduces the image sizes to conform to a maximum allowed size. This also processes all custom
5502 * remote views.
5503 *
5504 * @hide
5505 */
5506 void reduceImageSizes(Context context) {
5507 if (extras.getBoolean(EXTRA_REDUCED_IMAGES)) {
5508 return;
5509 }
Selim Cineka8cb1262017-08-15 16:53:44 -07005510 boolean isLowRam = ActivityManager.isLowRamDeviceStatic();
Selim Cinekd0426622017-07-11 13:19:59 +02005511 if (mLargeIcon != null || largeIcon != null) {
5512 Resources resources = context.getResources();
5513 Class<? extends Style> style = getNotificationStyle();
Selim Cineka8cb1262017-08-15 16:53:44 -07005514 int maxWidth = resources.getDimensionPixelSize(isLowRam
5515 ? R.dimen.notification_right_icon_size_low_ram
5516 : R.dimen.notification_right_icon_size);
Selim Cinekd0426622017-07-11 13:19:59 +02005517 int maxHeight = maxWidth;
5518 if (MediaStyle.class.equals(style)
5519 || DecoratedMediaCustomViewStyle.class.equals(style)) {
Selim Cineka8cb1262017-08-15 16:53:44 -07005520 maxHeight = resources.getDimensionPixelSize(isLowRam
5521 ? R.dimen.notification_media_image_max_height_low_ram
5522 : R.dimen.notification_media_image_max_height);
5523 maxWidth = resources.getDimensionPixelSize(isLowRam
5524 ? R.dimen.notification_media_image_max_width_low_ram
5525 : R.dimen.notification_media_image_max_width);
Selim Cinekd0426622017-07-11 13:19:59 +02005526 }
5527 if (mLargeIcon != null) {
5528 mLargeIcon.scaleDownIfNecessary(maxWidth, maxHeight);
5529 }
5530 if (largeIcon != null) {
5531 largeIcon = Icon.scaleDownIfNecessary(largeIcon, maxWidth, maxHeight);
5532 }
5533 }
Selim Cineka8cb1262017-08-15 16:53:44 -07005534 reduceImageSizesForRemoteView(contentView, context, isLowRam);
5535 reduceImageSizesForRemoteView(headsUpContentView, context, isLowRam);
5536 reduceImageSizesForRemoteView(bigContentView, context, isLowRam);
Selim Cinekd0426622017-07-11 13:19:59 +02005537 extras.putBoolean(EXTRA_REDUCED_IMAGES, true);
5538 }
5539
Selim Cineka8cb1262017-08-15 16:53:44 -07005540 private void reduceImageSizesForRemoteView(RemoteViews remoteView, Context context,
5541 boolean isLowRam) {
Selim Cinekd0426622017-07-11 13:19:59 +02005542 if (remoteView != null) {
5543 Resources resources = context.getResources();
Selim Cineka8cb1262017-08-15 16:53:44 -07005544 int maxWidth = resources.getDimensionPixelSize(isLowRam
5545 ? R.dimen.notification_custom_view_max_image_width_low_ram
5546 : R.dimen.notification_custom_view_max_image_width);
5547 int maxHeight = resources.getDimensionPixelSize(isLowRam
5548 ? R.dimen.notification_custom_view_max_image_height_low_ram
5549 : R.dimen.notification_custom_view_max_image_height);
Selim Cinekd0426622017-07-11 13:19:59 +02005550 remoteView.reduceImageSizes(maxWidth, maxHeight);
5551 }
5552 }
5553
5554 /**
Selim Cinek22714f12017-04-13 16:23:53 -07005555 * @return whether this notification is a foreground service notification
Selim Cinek7b9605b2017-01-19 17:36:00 -08005556 */
Selim Cinek22714f12017-04-13 16:23:53 -07005557 private boolean isForegroundService() {
5558 return (flags & Notification.FLAG_FOREGROUND_SERVICE) != 0;
Selim Cinek7b9605b2017-01-19 17:36:00 -08005559 }
5560
5561 /**
Selim Cinek99104832017-01-25 14:47:33 -08005562 * @return whether this notification has a media session attached
5563 * @hide
5564 */
5565 public boolean hasMediaSession() {
5566 return extras.getParcelable(Notification.EXTRA_MEDIA_SESSION) != null;
5567 }
5568
5569 /**
Selim Cinek7d1009b2017-01-25 15:28:28 -08005570 * @return the style class of this notification
5571 * @hide
5572 */
5573 public Class<? extends Notification.Style> getNotificationStyle() {
5574 String templateClass = extras.getString(Notification.EXTRA_TEMPLATE);
5575
5576 if (!TextUtils.isEmpty(templateClass)) {
5577 return Notification.getNotificationStyleClass(templateClass);
5578 }
5579 return null;
5580 }
5581
5582 /**
Selim Cinek22714f12017-04-13 16:23:53 -07005583 * @return true if this notification is colorized.
Selim Cinek7b9605b2017-01-19 17:36:00 -08005584 *
5585 * @hide
5586 */
5587 public boolean isColorized() {
Selim Cinek5fb73f82017-04-20 16:55:38 -07005588 if (isColorizedMedia()) {
5589 return true;
5590 }
Julia Reynolds4db59552017-06-30 13:34:01 -04005591 return extras.getBoolean(EXTRA_COLORIZED)
5592 && (hasColorizedPermission() || isForegroundService());
5593 }
5594
5595 /**
5596 * Returns whether an app can colorize due to the android.permission.USE_COLORIZED_NOTIFICATIONS
5597 * permission. The permission is checked when a notification is enqueued.
5598 */
5599 private boolean hasColorizedPermission() {
5600 return (flags & Notification.FLAG_CAN_COLORIZE) != 0;
Selim Cinek5fb73f82017-04-20 16:55:38 -07005601 }
5602
5603 /**
5604 * @return true if this notification is colorized and it is a media notification
5605 *
5606 * @hide
5607 */
5608 public boolean isColorizedMedia() {
Selim Cinek99104832017-01-25 14:47:33 -08005609 Class<? extends Style> style = getNotificationStyle();
5610 if (MediaStyle.class.equals(style)) {
5611 Boolean colorized = (Boolean) extras.get(EXTRA_COLORIZED);
5612 if ((colorized == null || colorized) && hasMediaSession()) {
5613 return true;
5614 }
5615 } else if (DecoratedMediaCustomViewStyle.class.equals(style)) {
5616 if (extras.getBoolean(EXTRA_COLORIZED) && hasMediaSession()) {
5617 return true;
5618 }
5619 }
Selim Cinek5fb73f82017-04-20 16:55:38 -07005620 return false;
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005621 }
5622
Selim Cinek0847acd2017-04-24 19:48:29 -07005623
5624 /**
5625 * @return true if this is a media notification
5626 *
5627 * @hide
5628 */
5629 public boolean isMediaNotification() {
5630 Class<? extends Style> style = getNotificationStyle();
5631 if (MediaStyle.class.equals(style)) {
5632 return true;
5633 } else if (DecoratedMediaCustomViewStyle.class.equals(style)) {
5634 return true;
5635 }
5636 return false;
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005637 }
5638
Selim Cinek279fa862016-06-14 10:57:25 -07005639 private boolean hasLargeIcon() {
5640 return mLargeIcon != null || largeIcon != null;
5641 }
5642
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005643 /**
Selim Cinekc2c0b042016-05-18 17:13:46 -07005644 * @return true if the notification will show the time; false otherwise
Selim Cinekb85f36fd2016-04-20 18:46:36 -07005645 * @hide
5646 */
Selim Cinekc2c0b042016-05-18 17:13:46 -07005647 public boolean showsTime() {
Selim Cinekb85f36fd2016-04-20 18:46:36 -07005648 return when != 0 && extras.getBoolean(EXTRA_SHOW_WHEN);
5649 }
5650
5651 /**
Selim Cinekc2c0b042016-05-18 17:13:46 -07005652 * @return true if the notification will show a chronometer; false otherwise
5653 * @hide
5654 */
5655 public boolean showsChronometer() {
5656 return when != 0 && extras.getBoolean(EXTRA_SHOW_CHRONOMETER);
5657 }
5658
5659 /**
Julia Reynolds7ca33072017-06-29 13:58:24 -04005660 * @removed
Julia Reynolds4a02afb2016-12-13 13:39:52 -05005661 */
5662 @SystemApi
5663 public static Class<? extends Style> getNotificationStyleClass(String templateClass) {
5664 Class<? extends Style>[] classes = new Class[] {
5665 BigTextStyle.class, BigPictureStyle.class, InboxStyle.class, MediaStyle.class,
5666 DecoratedCustomViewStyle.class, DecoratedMediaCustomViewStyle.class,
5667 MessagingStyle.class };
5668 for (Class<? extends Style> innerClass : classes) {
5669 if (templateClass.equals(innerClass.getName())) {
5670 return innerClass;
5671 }
5672 }
5673 return null;
5674 }
5675
5676 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005677 * An object that can apply a rich notification style to a {@link Notification.Builder}
5678 * object.
5679 */
Griff Hazendfcb0802014-02-11 12:00:00 -08005680 public static abstract class Style {
Chris Wrend6297db2012-05-03 16:20:13 -04005681 private CharSequence mBigContentTitle;
Jorim Jaggi457a10d2014-09-08 16:18:23 +02005682
5683 /**
5684 * @hide
5685 */
5686 protected CharSequence mSummaryText = null;
5687
5688 /**
5689 * @hide
5690 */
5691 protected boolean mSummaryTextSet = false;
Chris Wrend6297db2012-05-03 16:20:13 -04005692
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005693 protected Builder mBuilder;
5694
Chris Wrend6297db2012-05-03 16:20:13 -04005695 /**
5696 * Overrides ContentTitle in the big form of the template.
5697 * This defaults to the value passed to setContentTitle().
5698 */
5699 protected void internalSetBigContentTitle(CharSequence title) {
5700 mBigContentTitle = title;
5701 }
5702
5703 /**
5704 * Set the first line of text after the detail section in the big form of the template.
5705 */
5706 protected void internalSetSummaryText(CharSequence cs) {
5707 mSummaryText = cs;
Daniel Sandler619738c2012-06-07 16:33:08 -04005708 mSummaryTextSet = true;
Chris Wrend6297db2012-05-03 16:20:13 -04005709 }
5710
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005711 public void setBuilder(Builder builder) {
5712 if (mBuilder != builder) {
5713 mBuilder = builder;
Daniel Sandlerc08dea22012-06-28 08:35:24 -07005714 if (mBuilder != null) {
5715 mBuilder.setStyle(this);
5716 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005717 }
5718 }
5719
Chris Wrend6297db2012-05-03 16:20:13 -04005720 protected void checkBuilder() {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005721 if (mBuilder == null) {
5722 throw new IllegalArgumentException("Style requires a valid Builder object");
5723 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005724 }
Chris Wrend6297db2012-05-03 16:20:13 -04005725
5726 protected RemoteViews getStandardView(int layoutId) {
5727 checkBuilder();
5728
Christoph Studer4600f9b2014-07-22 22:44:43 +02005729 // Nasty.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005730 CharSequence oldBuilderContentTitle =
5731 mBuilder.getAllExtras().getCharSequence(EXTRA_TITLE);
Chris Wrend6297db2012-05-03 16:20:13 -04005732 if (mBigContentTitle != null) {
5733 mBuilder.setContentTitle(mBigContentTitle);
5734 }
5735
Chris Wrend6297db2012-05-03 16:20:13 -04005736 RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(layoutId);
5737
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005738 mBuilder.getAllExtras().putCharSequence(EXTRA_TITLE, oldBuilderContentTitle);
Christoph Studer4600f9b2014-07-22 22:44:43 +02005739
Chris Wrend6297db2012-05-03 16:20:13 -04005740 if (mBigContentTitle != null && mBigContentTitle.equals("")) {
5741 contentView.setViewVisibility(R.id.line1, View.GONE);
Chris Wren67dc9a02012-05-16 01:03:20 -04005742 } else {
5743 contentView.setViewVisibility(R.id.line1, View.VISIBLE);
Chris Wrend6297db2012-05-03 16:20:13 -04005744 }
5745
Chris Wrend6297db2012-05-03 16:20:13 -04005746 return contentView;
5747 }
5748
Daniel Sandlerf45564e2013-04-15 15:05:08 -04005749 /**
Selim Cinek7d1009b2017-01-25 15:28:28 -08005750 * Construct a Style-specific RemoteViews for the collapsed notification layout.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005751 * The default implementation has nothing additional to add.
Selim Cinek7d1009b2017-01-25 15:28:28 -08005752 *
5753 * @param increasedHeight true if this layout be created with an increased height.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005754 * @hide
5755 */
Selim Cinek7d1009b2017-01-25 15:28:28 -08005756 public RemoteViews makeContentView(boolean increasedHeight) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005757 return null;
5758 }
5759
5760 /**
5761 * Construct a Style-specific RemoteViews for the final big notification layout.
5762 * @hide
5763 */
5764 public RemoteViews makeBigContentView() {
5765 return null;
5766 }
5767
5768 /**
5769 * Construct a Style-specific RemoteViews for the final HUN layout.
Selim Cinek87ed69b2017-02-09 15:59:43 -08005770 *
5771 * @param increasedHeight true if this layout be created with an increased height.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005772 * @hide
5773 */
Selim Cinek87ed69b2017-02-09 15:59:43 -08005774 public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005775 return null;
5776 }
5777
5778 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005779 * Apply any style-specific extras to this notification before shipping it out.
Daniel Sandlerf45564e2013-04-15 15:05:08 -04005780 * @hide
5781 */
5782 public void addExtras(Bundle extras) {
5783 if (mSummaryTextSet) {
5784 extras.putCharSequence(EXTRA_SUMMARY_TEXT, mSummaryText);
5785 }
5786 if (mBigContentTitle != null) {
5787 extras.putCharSequence(EXTRA_TITLE_BIG, mBigContentTitle);
5788 }
Chris Wren91ad5632013-06-05 15:05:57 -04005789 extras.putString(EXTRA_TEMPLATE, this.getClass().getName());
Daniel Sandlerf45564e2013-04-15 15:05:08 -04005790 }
5791
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04005792 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005793 * Reconstruct the internal state of this Style object from extras.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04005794 * @hide
5795 */
Christoph Studer4600f9b2014-07-22 22:44:43 +02005796 protected void restoreFromExtras(Bundle extras) {
5797 if (extras.containsKey(EXTRA_SUMMARY_TEXT)) {
5798 mSummaryText = extras.getCharSequence(EXTRA_SUMMARY_TEXT);
5799 mSummaryTextSet = true;
5800 }
5801 if (extras.containsKey(EXTRA_TITLE_BIG)) {
5802 mBigContentTitle = extras.getCharSequence(EXTRA_TITLE_BIG);
5803 }
5804 }
5805
5806
5807 /**
5808 * @hide
5809 */
5810 public Notification buildStyled(Notification wip) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005811 addExtras(wip.extras);
Christoph Studer4600f9b2014-07-22 22:44:43 +02005812 return wip;
5813 }
5814
Daniel Sandler0ec46202015-06-24 01:27:05 -04005815 /**
5816 * @hide
5817 */
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07005818 public void purgeResources() {}
5819
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04005820 /**
5821 * Calls {@link android.app.Notification.Builder#build()} on the Builder this Style is
5822 * attached to.
5823 *
5824 * @return the fully constructed Notification.
5825 */
5826 public Notification build() {
5827 checkBuilder();
5828 return mBuilder.build();
5829 }
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02005830
5831 /**
5832 * @hide
5833 * @return true if the style positions the progress bar on the second line; false if the
5834 * style hides the progress bar
5835 */
5836 protected boolean hasProgress() {
5837 return true;
5838 }
Selim Cinek03d0d652015-11-13 13:18:09 -05005839
5840 /**
5841 * @hide
5842 * @return Whether we should put the summary be put into the notification header
5843 */
5844 public boolean hasSummaryInHeader() {
5845 return true;
5846 }
Selim Cinek593610c2016-02-16 18:42:57 -08005847
5848 /**
5849 * @hide
5850 * @return Whether custom content views are displayed inline in the style
5851 */
5852 public boolean displayCustomViewInline() {
5853 return false;
5854 }
Selim Cinekd0426622017-07-11 13:19:59 +02005855
5856 /**
5857 * Reduces the image sizes contained in this style.
5858 *
5859 * @hide
5860 */
5861 public void reduceImageSizes(Context context) {
5862 }
Selim Cinek90343862018-02-01 11:07:11 -08005863
5864 /**
5865 * Validate that this style was properly composed. This is called at build time.
5866 * @hide
5867 */
5868 public void validate(Context context) {
5869 }
Joe Onorato46439ce2010-11-19 13:56:21 -08005870 }
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005871
5872 /**
Daniel Sandler4dfbe832012-04-11 14:51:46 -04005873 * Helper class for generating large-format notifications that include a large image attachment.
Joe Malin8d40d042012-11-05 11:36:40 -08005874 *
Robert Ly91c5ce32014-06-08 15:37:00 -07005875 * Here's how you'd set the <code>BigPictureStyle</code> on a notification:
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005876 * <pre class="prettyprint">
Robert Ly91c5ce32014-06-08 15:37:00 -07005877 * Notification notif = new Notification.Builder(mContext)
5878 * .setContentTitle(&quot;New photo from &quot; + sender.toString())
5879 * .setContentText(subject)
5880 * .setSmallIcon(R.drawable.new_post)
5881 * .setLargeIcon(aBitmap)
5882 * .setStyle(new Notification.BigPictureStyle()
5883 * .bigPicture(aBigBitmap))
5884 * .build();
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005885 * </pre>
Joe Malin8d40d042012-11-05 11:36:40 -08005886 *
Daniel Sandler4dfbe832012-04-11 14:51:46 -04005887 * @see Notification#bigContentView
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005888 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005889 public static class BigPictureStyle extends Style {
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005890 private Bitmap mPicture;
Dan Sandlerd63f9322015-05-06 15:18:49 -04005891 private Icon mBigLargeIcon;
Chris Wren3745a3d2012-05-22 15:11:52 -04005892 private boolean mBigLargeIconSet = false;
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005893
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005894 public BigPictureStyle() {
5895 }
5896
Adrian Roosf5faf9d2016-05-23 13:56:15 -07005897 /**
5898 * @deprecated use {@code BigPictureStyle()}.
5899 */
5900 @Deprecated
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005901 public BigPictureStyle(Builder builder) {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005902 setBuilder(builder);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005903 }
5904
Chris Wrend6297db2012-05-03 16:20:13 -04005905 /**
5906 * Overrides ContentTitle in the big form of the template.
5907 * This defaults to the value passed to setContentTitle().
5908 */
5909 public BigPictureStyle setBigContentTitle(CharSequence title) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04005910 internalSetBigContentTitle(safeCharSequence(title));
Chris Wrend6297db2012-05-03 16:20:13 -04005911 return this;
5912 }
5913
5914 /**
5915 * Set the first line of text after the detail section in the big form of the template.
5916 */
5917 public BigPictureStyle setSummaryText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04005918 internalSetSummaryText(safeCharSequence(cs));
Chris Wrend6297db2012-05-03 16:20:13 -04005919 return this;
5920 }
5921
Chris Wren0bd664d2012-08-01 13:56:56 -04005922 /**
5923 * Provide the bitmap to be used as the payload for the BigPicture notification.
5924 */
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005925 public BigPictureStyle bigPicture(Bitmap b) {
5926 mPicture = b;
5927 return this;
5928 }
5929
Chris Wren3745a3d2012-05-22 15:11:52 -04005930 /**
Chris Wren3745a3d2012-05-22 15:11:52 -04005931 * Override the large icon when the big notification is shown.
5932 */
5933 public BigPictureStyle bigLargeIcon(Bitmap b) {
Dan Sandlerd63f9322015-05-06 15:18:49 -04005934 return bigLargeIcon(b != null ? Icon.createWithBitmap(b) : null);
5935 }
5936
5937 /**
5938 * Override the large icon when the big notification is shown.
5939 */
5940 public BigPictureStyle bigLargeIcon(Icon icon) {
Chris Wren3745a3d2012-05-22 15:11:52 -04005941 mBigLargeIconSet = true;
Dan Sandlerd63f9322015-05-06 15:18:49 -04005942 mBigLargeIcon = icon;
Chris Wren3745a3d2012-05-22 15:11:52 -04005943 return this;
5944 }
5945
Riley Andrews0394a0c2015-11-03 23:36:52 -08005946 /** @hide */
5947 public static final int MIN_ASHMEM_BITMAP_SIZE = 128 * (1 << 10);
5948
Daniel Sandler0ec46202015-06-24 01:27:05 -04005949 /**
5950 * @hide
5951 */
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07005952 @Override
5953 public void purgeResources() {
5954 super.purgeResources();
Riley Andrews8cee7c12015-11-01 23:36:04 -08005955 if (mPicture != null &&
5956 mPicture.isMutable() &&
Riley Andrews0394a0c2015-11-03 23:36:52 -08005957 mPicture.getAllocationByteCount() >= MIN_ASHMEM_BITMAP_SIZE) {
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07005958 mPicture = mPicture.createAshmemBitmap();
5959 }
5960 if (mBigLargeIcon != null) {
5961 mBigLargeIcon.convertToAshmem();
5962 }
5963 }
Christoph Studer5c510ee2014-12-15 16:32:27 +01005964
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005965 /**
5966 * @hide
5967 */
Selim Cinekd0426622017-07-11 13:19:59 +02005968 @Override
5969 public void reduceImageSizes(Context context) {
5970 super.reduceImageSizes(context);
5971 Resources resources = context.getResources();
Selim Cineka8cb1262017-08-15 16:53:44 -07005972 boolean isLowRam = ActivityManager.isLowRamDeviceStatic();
Selim Cinekd0426622017-07-11 13:19:59 +02005973 if (mPicture != null) {
Selim Cineka8cb1262017-08-15 16:53:44 -07005974 int maxPictureWidth = resources.getDimensionPixelSize(isLowRam
5975 ? R.dimen.notification_big_picture_max_height_low_ram
5976 : R.dimen.notification_big_picture_max_height);
5977 int maxPictureHeight = resources.getDimensionPixelSize(isLowRam
5978 ? R.dimen.notification_big_picture_max_width_low_ram
5979 : R.dimen.notification_big_picture_max_width);
Selim Cinekd0426622017-07-11 13:19:59 +02005980 mPicture = Icon.scaleDownIfNecessary(mPicture, maxPictureWidth, maxPictureHeight);
5981 }
5982 if (mBigLargeIcon != null) {
Selim Cineka8cb1262017-08-15 16:53:44 -07005983 int rightIconSize = resources.getDimensionPixelSize(isLowRam
5984 ? R.dimen.notification_right_icon_size_low_ram
5985 : R.dimen.notification_right_icon_size);
Selim Cinekd0426622017-07-11 13:19:59 +02005986 mBigLargeIcon.scaleDownIfNecessary(rightIconSize, rightIconSize);
5987 }
5988 }
5989
5990 /**
5991 * @hide
5992 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005993 public RemoteViews makeBigContentView() {
5994 // Replace mN.mLargeIcon with mBigLargeIcon if mBigLargeIconSet
Christoph Studer5c510ee2014-12-15 16:32:27 +01005995 // This covers the following cases:
5996 // 1. mBigLargeIconSet -> mBigLargeIcon (null or non-null) applies, overrides
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005997 // mN.mLargeIcon
5998 // 2. !mBigLargeIconSet -> mN.mLargeIcon applies
Dan Sandlerd63f9322015-05-06 15:18:49 -04005999 Icon oldLargeIcon = null;
Selim Cineke99acb22016-08-04 12:55:48 -07006000 Bitmap largeIconLegacy = null;
Christoph Studer5c510ee2014-12-15 16:32:27 +01006001 if (mBigLargeIconSet) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04006002 oldLargeIcon = mBuilder.mN.mLargeIcon;
6003 mBuilder.mN.mLargeIcon = mBigLargeIcon;
Selim Cineke99acb22016-08-04 12:55:48 -07006004 // The legacy largeIcon might not allow us to clear the image, as it's taken in
6005 // replacement if the other one is null. Because we're restoring these legacy icons
6006 // for old listeners, this is in general non-null.
6007 largeIconLegacy = mBuilder.mN.largeIcon;
6008 mBuilder.mN.largeIcon = null;
Christoph Studer5c510ee2014-12-15 16:32:27 +01006009 }
6010
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01006011 RemoteViews contentView = getStandardView(mBuilder.getBigPictureLayoutResource());
Selim Cinek03d0d652015-11-13 13:18:09 -05006012 if (mSummaryTextSet) {
Selim Cinek48f66b72017-08-18 16:17:51 -07006013 contentView.setTextViewText(R.id.text, mBuilder.processTextSpans(
6014 mBuilder.processLegacyText(mSummaryText)));
Selim Cinek7b9605b2017-01-19 17:36:00 -08006015 mBuilder.setTextViewColorSecondary(contentView, R.id.text);
Selim Cinekc848c3a2016-01-13 15:27:30 -08006016 contentView.setViewVisibility(R.id.text, View.VISIBLE);
Selim Cinek03d0d652015-11-13 13:18:09 -05006017 }
Selim Cinek279fa862016-06-14 10:57:25 -07006018 mBuilder.setContentMinHeight(contentView, mBuilder.mN.hasLargeIcon());
Selim Cinek53e64a42015-11-16 10:40:56 -08006019
Christoph Studer5c510ee2014-12-15 16:32:27 +01006020 if (mBigLargeIconSet) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04006021 mBuilder.mN.mLargeIcon = oldLargeIcon;
Selim Cineke99acb22016-08-04 12:55:48 -07006022 mBuilder.mN.largeIcon = largeIconLegacy;
Christoph Studer5c510ee2014-12-15 16:32:27 +01006023 }
6024
Daniel Sandlerf3b73432012-03-27 15:01:25 -04006025 contentView.setImageViewBitmap(R.id.big_picture, mPicture);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04006026 return contentView;
6027 }
6028
Daniel Sandlerf45564e2013-04-15 15:05:08 -04006029 /**
6030 * @hide
6031 */
6032 public void addExtras(Bundle extras) {
6033 super.addExtras(extras);
6034
6035 if (mBigLargeIconSet) {
6036 extras.putParcelable(EXTRA_LARGE_ICON_BIG, mBigLargeIcon);
6037 }
6038 extras.putParcelable(EXTRA_PICTURE, mPicture);
6039 }
6040
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04006041 /**
6042 * @hide
6043 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04006044 @Override
Christoph Studer4600f9b2014-07-22 22:44:43 +02006045 protected void restoreFromExtras(Bundle extras) {
6046 super.restoreFromExtras(extras);
6047
6048 if (extras.containsKey(EXTRA_LARGE_ICON_BIG)) {
Christoph Studer5c510ee2014-12-15 16:32:27 +01006049 mBigLargeIconSet = true;
Christoph Studer4600f9b2014-07-22 22:44:43 +02006050 mBigLargeIcon = extras.getParcelable(EXTRA_LARGE_ICON_BIG);
Chris Wren3745a3d2012-05-22 15:11:52 -04006051 }
Christoph Studer4600f9b2014-07-22 22:44:43 +02006052 mPicture = extras.getParcelable(EXTRA_PICTURE);
6053 }
Selim Cinek03d0d652015-11-13 13:18:09 -05006054
6055 /**
6056 * @hide
6057 */
6058 @Override
6059 public boolean hasSummaryInHeader() {
6060 return false;
6061 }
Daniel Sandlerf3b73432012-03-27 15:01:25 -04006062 }
6063
6064 /**
Daniel Sandler4dfbe832012-04-11 14:51:46 -04006065 * Helper class for generating large-format notifications that include a lot of text.
Joe Malin8d40d042012-11-05 11:36:40 -08006066 *
Robert Ly91c5ce32014-06-08 15:37:00 -07006067 * Here's how you'd set the <code>BigTextStyle</code> on a notification:
Daniel Sandlerf3b73432012-03-27 15:01:25 -04006068 * <pre class="prettyprint">
Robert Ly91c5ce32014-06-08 15:37:00 -07006069 * Notification notif = new Notification.Builder(mContext)
6070 * .setContentTitle(&quot;New mail from &quot; + sender.toString())
6071 * .setContentText(subject)
6072 * .setSmallIcon(R.drawable.new_mail)
6073 * .setLargeIcon(aBitmap)
6074 * .setStyle(new Notification.BigTextStyle()
6075 * .bigText(aVeryLongString))
6076 * .build();
Daniel Sandlerf3b73432012-03-27 15:01:25 -04006077 * </pre>
Joe Malin8d40d042012-11-05 11:36:40 -08006078 *
Daniel Sandler4dfbe832012-04-11 14:51:46 -04006079 * @see Notification#bigContentView
Daniel Sandlerf3b73432012-03-27 15:01:25 -04006080 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04006081 public static class BigTextStyle extends Style {
Jorim Jaggi457a10d2014-09-08 16:18:23 +02006082
Daniel Sandlerf3b73432012-03-27 15:01:25 -04006083 private CharSequence mBigText;
6084
Chris Wrenfbd96ba2012-05-01 12:03:58 -04006085 public BigTextStyle() {
6086 }
6087
Adrian Roosf5faf9d2016-05-23 13:56:15 -07006088 /**
6089 * @deprecated use {@code BigTextStyle()}.
6090 */
6091 @Deprecated
Daniel Sandlerf3b73432012-03-27 15:01:25 -04006092 public BigTextStyle(Builder builder) {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04006093 setBuilder(builder);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04006094 }
6095
Chris Wrend6297db2012-05-03 16:20:13 -04006096 /**
6097 * Overrides ContentTitle in the big form of the template.
6098 * This defaults to the value passed to setContentTitle().
6099 */
6100 public BigTextStyle setBigContentTitle(CharSequence title) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04006101 internalSetBigContentTitle(safeCharSequence(title));
Chris Wrend6297db2012-05-03 16:20:13 -04006102 return this;
6103 }
6104
6105 /**
6106 * Set the first line of text after the detail section in the big form of the template.
6107 */
6108 public BigTextStyle setSummaryText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04006109 internalSetSummaryText(safeCharSequence(cs));
Chris Wrend6297db2012-05-03 16:20:13 -04006110 return this;
6111 }
6112
Chris Wren0bd664d2012-08-01 13:56:56 -04006113 /**
6114 * Provide the longer text to be displayed in the big form of the
6115 * template in place of the content text.
6116 */
Daniel Sandlerf3b73432012-03-27 15:01:25 -04006117 public BigTextStyle bigText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04006118 mBigText = safeCharSequence(cs);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04006119 return this;
6120 }
6121
Daniel Sandlerf45564e2013-04-15 15:05:08 -04006122 /**
6123 * @hide
6124 */
6125 public void addExtras(Bundle extras) {
6126 super.addExtras(extras);
6127
Christoph Studer4600f9b2014-07-22 22:44:43 +02006128 extras.putCharSequence(EXTRA_BIG_TEXT, mBigText);
6129 }
6130
6131 /**
6132 * @hide
6133 */
6134 @Override
6135 protected void restoreFromExtras(Bundle extras) {
6136 super.restoreFromExtras(extras);
6137
6138 mBigText = extras.getCharSequence(EXTRA_BIG_TEXT);
Daniel Sandlerf45564e2013-04-15 15:05:08 -04006139 }
6140
Julia Reynoldsd9228f12015-10-20 10:37:27 -04006141 /**
Selim Cinek7d1009b2017-01-25 15:28:28 -08006142 * @param increasedHeight true if this layout be created with an increased height.
6143 *
6144 * @hide
6145 */
6146 @Override
6147 public RemoteViews makeContentView(boolean increasedHeight) {
6148 if (increasedHeight) {
Selim Cinek5d6ef8d2017-05-18 22:16:00 -07006149 mBuilder.mOriginalActions = mBuilder.mActions;
Selim Cinek7d1009b2017-01-25 15:28:28 -08006150 mBuilder.mActions = new ArrayList<>();
6151 RemoteViews remoteViews = makeBigContentView();
Selim Cinek5d6ef8d2017-05-18 22:16:00 -07006152 mBuilder.mActions = mBuilder.mOriginalActions;
6153 mBuilder.mOriginalActions = null;
Selim Cinek7d1009b2017-01-25 15:28:28 -08006154 return remoteViews;
6155 }
6156 return super.makeContentView(increasedHeight);
6157 }
6158
6159 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04006160 * @hide
6161 */
Selim Cinek87ed69b2017-02-09 15:59:43 -08006162 @Override
6163 public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
6164 if (increasedHeight && mBuilder.mActions.size() > 0) {
6165 return makeBigContentView();
6166 }
6167 return super.makeHeadsUpContentView(increasedHeight);
6168 }
6169
6170 /**
6171 * @hide
6172 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -04006173 public RemoteViews makeBigContentView() {
Christoph Studer4600f9b2014-07-22 22:44:43 +02006174
6175 // Nasty
Selim Cinek75998782016-04-26 10:39:17 -07006176 CharSequence text = mBuilder.getAllExtras().getCharSequence(EXTRA_TEXT);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04006177 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, null);
Daniel Sandler916ad912012-06-13 12:17:07 -04006178
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01006179 RemoteViews contentView = getStandardView(mBuilder.getBigTextLayoutResource());
Joe Malin8d40d042012-11-05 11:36:40 -08006180
Selim Cinek75998782016-04-26 10:39:17 -07006181 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, text);
Christoph Studer4600f9b2014-07-22 22:44:43 +02006182
Selim Cinek3a2c4b92015-12-17 17:01:17 -08006183 CharSequence bigTextText = mBuilder.processLegacyText(mBigText);
Selim Cinek75998782016-04-26 10:39:17 -07006184 if (TextUtils.isEmpty(bigTextText)) {
6185 // In case the bigtext is null / empty fall back to the normal text to avoid a weird
6186 // experience
6187 bigTextText = mBuilder.processLegacyText(text);
6188 }
Adrian Roosb1f427c2016-05-26 12:27:15 -07006189 applyBigTextContentView(mBuilder, contentView, bigTextText);
Selim Cinek4fb12d32015-11-19 18:10:48 -08006190
Daniel Sandlerf3b73432012-03-27 15:01:25 -04006191 return contentView;
6192 }
6193
Adrian Roosb1f427c2016-05-26 12:27:15 -07006194 static void applyBigTextContentView(Builder builder,
6195 RemoteViews contentView, CharSequence bigTextText) {
Selim Cinek48f66b72017-08-18 16:17:51 -07006196 contentView.setTextViewText(R.id.big_text, builder.processTextSpans(bigTextText));
Selim Cinek7b9605b2017-01-19 17:36:00 -08006197 builder.setTextViewColorSecondary(contentView, R.id.big_text);
Adrian Roosb1f427c2016-05-26 12:27:15 -07006198 contentView.setViewVisibility(R.id.big_text,
6199 TextUtils.isEmpty(bigTextText) ? View.GONE : View.VISIBLE);
Selim Cinek279fa862016-06-14 10:57:25 -07006200 contentView.setBoolean(R.id.big_text, "setHasImage", builder.mN.hasLargeIcon());
Adrian Roosb1f427c2016-05-26 12:27:15 -07006201 }
Daniel Sandlerf3b73432012-03-27 15:01:25 -04006202 }
Daniel Sandler879c5e02012-04-17 16:46:51 -04006203
6204 /**
Alex Hillsfc737de2016-03-23 17:33:02 -04006205 * Helper class for generating large-format notifications that include multiple back-and-forth
6206 * messages of varying types between any number of people.
6207 *
6208 * <br>
6209 * If the platform does not provide large-format notifications, this method has no effect. The
6210 * user will always see the normal notification view.
6211 * <br>
6212 * This class is a "rebuilder": It attaches to a Builder object and modifies its behavior, like
6213 * so:
6214 * <pre class="prettyprint">
6215 *
6216 * Notification noti = new Notification.Builder()
6217 * .setContentTitle(&quot;2 new messages wtih &quot; + sender.toString())
6218 * .setContentText(subject)
6219 * .setSmallIcon(R.drawable.new_message)
6220 * .setLargeIcon(aBitmap)
6221 * .setStyle(new Notification.MessagingStyle(resources.getString(R.string.reply_name))
6222 * .addMessage(messages[0].getText(), messages[0].getTime(), messages[0].getSender())
6223 * .addMessage(messages[1].getText(), messages[1].getTime(), messages[1].getSender()))
6224 * .build();
6225 * </pre>
6226 */
6227 public static class MessagingStyle extends Style {
6228
6229 /**
6230 * The maximum number of messages that will be retained in the Notification itself (the
6231 * number displayed is up to the platform).
6232 */
6233 public static final int MAXIMUM_RETAINED_MESSAGES = 25;
6234
Selim Cinekcb8b9852017-12-15 18:01:52 -08006235 @NonNull Person mUser;
Kodlee Yin9ac617c2017-12-19 11:20:50 -08006236 @Nullable CharSequence mConversationTitle;
Alex Hillsd9b04d92016-04-11 16:38:16 -04006237 List<Message> mMessages = new ArrayList<>();
Adrian Roos437cd562017-01-18 15:47:03 -08006238 List<Message> mHistoricMessages = new ArrayList<>();
Kodlee Yin9ac617c2017-12-19 11:20:50 -08006239 boolean mIsGroupConversation;
Alex Hillsfc737de2016-03-23 17:33:02 -04006240
6241 MessagingStyle() {
6242 }
6243
6244 /**
Alex Hillsfd590442016-10-07 09:52:44 -04006245 * @param userDisplayName Required - the name to be displayed for any replies sent by the
6246 * user before the posting app reposts the notification with those messages after they've
6247 * been actually sent and in previous messages sent by the user added in
Alex Hillsfc737de2016-03-23 17:33:02 -04006248 * {@link #addMessage(Notification.MessagingStyle.Message)}
Selim Cinekcb8b9852017-12-15 18:01:52 -08006249 *
6250 * @deprecated use {@code MessagingStyle(Person)}
Alex Hillsfc737de2016-03-23 17:33:02 -04006251 */
Alex Hillsfd590442016-10-07 09:52:44 -04006252 public MessagingStyle(@NonNull CharSequence userDisplayName) {
Selim Cinekcb8b9852017-12-15 18:01:52 -08006253 this(new Person().setName(userDisplayName));
6254 }
6255
6256 /**
6257 * @param user Required - The person displayed for any messages that are sent by the
6258 * user. Any messages added with {@link #addMessage(Notification.MessagingStyle.Message)}
6259 * who don't have a Person associated with it will be displayed as if they were sent
Selim Cinek90343862018-02-01 11:07:11 -08006260 * by this user. The user also needs to have a valid name associated with it, which will
6261 * be enforced starting in Android P.
Selim Cinekcb8b9852017-12-15 18:01:52 -08006262 */
6263 public MessagingStyle(@NonNull Person user) {
6264 mUser = user;
Selim Cinek90343862018-02-01 11:07:11 -08006265 }
6266
6267 /**
6268 * Validate that this style was properly composed. This is called at build time.
6269 * @hide
6270 */
6271 @Override
6272 public void validate(Context context) {
6273 super.validate(context);
6274 if (context.getApplicationInfo().targetSdkVersion
6275 >= Build.VERSION_CODES.P && (mUser == null || mUser.getName() == null)) {
6276 throw new RuntimeException("User must be valid and have a name.");
Selim Cinekcb8b9852017-12-15 18:01:52 -08006277 }
6278 }
6279
6280 /**
6281 * @return the user to be displayed for any replies sent by the user
6282 */
6283 public Person getUser() {
6284 return mUser;
Alex Hillsfc737de2016-03-23 17:33:02 -04006285 }
6286
6287 /**
6288 * Returns the name to be displayed for any replies sent by the user
Selim Cinekcb8b9852017-12-15 18:01:52 -08006289 *
6290 * @deprecated use {@link #getUser()} instead
Alex Hillsfc737de2016-03-23 17:33:02 -04006291 */
6292 public CharSequence getUserDisplayName() {
Selim Cinekcb8b9852017-12-15 18:01:52 -08006293 return mUser.getName();
Alex Hillsfc737de2016-03-23 17:33:02 -04006294 }
6295
6296 /**
Kodlee Yin9ac617c2017-12-19 11:20:50 -08006297 * Sets the title to be displayed on this conversation. May be set to {@code null}.
6298 *
Kodlee Yin14656422017-12-22 17:00:46 -08006299 * <p>This API's behavior was changed in SDK version {@link Build.VERSION_CODES#P}. If your
6300 * application's target version is less than {@link Build.VERSION_CODES#P}, setting a
6301 * conversation title to a non-null value will make {@link #isGroupConversation()} return
6302 * {@code true} and passing {@code null} will make it return {@code false}. In
6303 * {@link Build.VERSION_CODES#P} and beyond, use {@link #setGroupConversation(boolean)}
6304 * to set group conversation status.
6305 *
6306 * @param conversationTitle Title displayed for this conversation
6307 * @return this object for method chaining
Alex Hillsfc737de2016-03-23 17:33:02 -04006308 */
Kodlee Yin9ac617c2017-12-19 11:20:50 -08006309 public MessagingStyle setConversationTitle(@Nullable CharSequence conversationTitle) {
Alex Hillsfc737de2016-03-23 17:33:02 -04006310 mConversationTitle = conversationTitle;
6311 return this;
6312 }
6313
6314 /**
Kodlee Yin9ac617c2017-12-19 11:20:50 -08006315 * Return the title to be displayed on this conversation. May return {@code null}.
Alex Hillsfc737de2016-03-23 17:33:02 -04006316 */
Kodlee Yin9ac617c2017-12-19 11:20:50 -08006317 @Nullable
Alex Hillsfc737de2016-03-23 17:33:02 -04006318 public CharSequence getConversationTitle() {
6319 return mConversationTitle;
6320 }
6321
6322 /**
6323 * Adds a message for display by this notification. Convenience call for a simple
6324 * {@link Message} in {@link #addMessage(Notification.MessagingStyle.Message)}.
6325 * @param text A {@link CharSequence} to be displayed as the message content
6326 * @param timestamp Time at which the message arrived
6327 * @param sender A {@link CharSequence} to be used for displaying the name of the
6328 * sender. Should be <code>null</code> for messages by the current user, in which case
6329 * the platform will insert {@link #getUserDisplayName()}.
6330 * Should be unique amongst all individuals in the conversation, and should be
6331 * consistent during re-posts of the notification.
6332 *
6333 * @see Message#Message(CharSequence, long, CharSequence)
6334 *
6335 * @return this object for method chaining
Selim Cinekcb8b9852017-12-15 18:01:52 -08006336 *
6337 * @deprecated use {@link #addMessage(CharSequence, long, Person)}
Alex Hillsfc737de2016-03-23 17:33:02 -04006338 */
6339 public MessagingStyle addMessage(CharSequence text, long timestamp, CharSequence sender) {
Selim Cinekcb8b9852017-12-15 18:01:52 -08006340 return addMessage(text, timestamp,
6341 sender == null ? null : new Person().setName(sender));
6342 }
6343
6344 /**
6345 * Adds a message for display by this notification. Convenience call for a simple
6346 * {@link Message} in {@link #addMessage(Notification.MessagingStyle.Message)}.
6347 * @param text A {@link CharSequence} to be displayed as the message content
6348 * @param timestamp Time at which the message arrived
6349 * @param sender The {@link Person} who sent the message.
6350 * Should be <code>null</code> for messages by the current user, in which case
6351 * the platform will insert the user set in {@code MessagingStyle(Person)}.
6352 *
6353 * @see Message#Message(CharSequence, long, CharSequence)
6354 *
6355 * @return this object for method chaining
6356 */
6357 public MessagingStyle addMessage(CharSequence text, long timestamp, Person sender) {
Adrian Roos437cd562017-01-18 15:47:03 -08006358 return addMessage(new Message(text, timestamp, sender));
Alex Hillsfc737de2016-03-23 17:33:02 -04006359 }
6360
6361 /**
6362 * Adds a {@link Message} for display in this notification.
Adrian Roos437cd562017-01-18 15:47:03 -08006363 *
6364 * <p>The messages should be added in chronologic order, i.e. the oldest first,
6365 * the newest last.
6366 *
Alex Hillsfc737de2016-03-23 17:33:02 -04006367 * @param message The {@link Message} to be displayed
6368 * @return this object for method chaining
6369 */
6370 public MessagingStyle addMessage(Message message) {
6371 mMessages.add(message);
6372 if (mMessages.size() > MAXIMUM_RETAINED_MESSAGES) {
6373 mMessages.remove(0);
6374 }
6375 return this;
6376 }
6377
6378 /**
Adrian Roos437cd562017-01-18 15:47:03 -08006379 * Adds a {@link Message} for historic context in this notification.
6380 *
6381 * <p>Messages should be added as historic if they are not the main subject of the
6382 * notification but may give context to a conversation. The system may choose to present
6383 * them only when relevant, e.g. when replying to a message through a {@link RemoteInput}.
6384 *
6385 * <p>The messages should be added in chronologic order, i.e. the oldest first,
6386 * the newest last.
6387 *
6388 * @param message The historic {@link Message} to be added
6389 * @return this object for method chaining
6390 */
6391 public MessagingStyle addHistoricMessage(Message message) {
6392 mHistoricMessages.add(message);
6393 if (mHistoricMessages.size() > MAXIMUM_RETAINED_MESSAGES) {
6394 mHistoricMessages.remove(0);
6395 }
6396 return this;
6397 }
6398
6399 /**
Alex Hillsfc737de2016-03-23 17:33:02 -04006400 * Gets the list of {@code Message} objects that represent the notification
6401 */
6402 public List<Message> getMessages() {
6403 return mMessages;
6404 }
6405
6406 /**
Adrian Roos437cd562017-01-18 15:47:03 -08006407 * Gets the list of historic {@code Message}s in the notification.
6408 */
6409 public List<Message> getHistoricMessages() {
6410 return mHistoricMessages;
6411 }
6412
6413 /**
Kodlee Yin9ac617c2017-12-19 11:20:50 -08006414 * Sets whether this conversation notification represents a group.
Kodlee Yin14656422017-12-22 17:00:46 -08006415 *
Kodlee Yin9ac617c2017-12-19 11:20:50 -08006416 * @param isGroupConversation {@code true} if the conversation represents a group,
6417 * {@code false} otherwise.
6418 * @return this object for method chaining
6419 */
6420 public MessagingStyle setGroupConversation(boolean isGroupConversation) {
6421 mIsGroupConversation = isGroupConversation;
6422 return this;
6423 }
6424
6425 /**
Kodlee Yin14656422017-12-22 17:00:46 -08006426 * Returns {@code true} if this notification represents a group conversation, otherwise
6427 * {@code false}.
6428 *
6429 * <p> If the application that generated this {@link MessagingStyle} targets an SDK version
6430 * less than {@link Build.VERSION_CODES#P}, this method becomes dependent on whether or
6431 * not the conversation title is set; returning {@code true} if the conversation title is
6432 * a non-null value, or {@code false} otherwise. From {@link Build.VERSION_CODES#P} forward,
6433 * this method returns what's set by {@link #setGroupConversation(boolean)} allowing for
6434 * named, non-group conversations.
6435 *
6436 * @see #setConversationTitle(CharSequence)
Kodlee Yin9ac617c2017-12-19 11:20:50 -08006437 */
6438 public boolean isGroupConversation() {
Kodlee Yin14656422017-12-22 17:00:46 -08006439 // When target SDK version is < P, a non-null conversation title dictates if this is
6440 // as group conversation.
6441 if (mBuilder != null
6442 && mBuilder.mContext.getApplicationInfo().targetSdkVersion
6443 < Build.VERSION_CODES.P) {
6444 return mConversationTitle != null;
6445 }
6446
Kodlee Yin9ac617c2017-12-19 11:20:50 -08006447 return mIsGroupConversation;
6448 }
6449
6450 /**
Alex Hillsfc737de2016-03-23 17:33:02 -04006451 * @hide
6452 */
6453 @Override
6454 public void addExtras(Bundle extras) {
6455 super.addExtras(extras);
Selim Cinekcb8b9852017-12-15 18:01:52 -08006456 if (mUser != null) {
6457 // For legacy usages
6458 extras.putCharSequence(EXTRA_SELF_DISPLAY_NAME, mUser.getName());
6459 extras.putParcelable(EXTRA_MESSAGING_PERSON, mUser);
Alex Hillsfc737de2016-03-23 17:33:02 -04006460 }
6461 if (mConversationTitle != null) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04006462 extras.putCharSequence(EXTRA_CONVERSATION_TITLE, mConversationTitle);
Alex Hillsfc737de2016-03-23 17:33:02 -04006463 }
Alex Hillsd9b04d92016-04-11 16:38:16 -04006464 if (!mMessages.isEmpty()) { extras.putParcelableArray(EXTRA_MESSAGES,
6465 Message.getBundleArrayForMessages(mMessages));
Alex Hillsfc737de2016-03-23 17:33:02 -04006466 }
Adrian Roos437cd562017-01-18 15:47:03 -08006467 if (!mHistoricMessages.isEmpty()) { extras.putParcelableArray(EXTRA_HISTORIC_MESSAGES,
6468 Message.getBundleArrayForMessages(mHistoricMessages));
6469 }
Adrian Roos33fbd2c2016-05-27 15:35:28 -07006470
6471 fixTitleAndTextExtras(extras);
Kodlee Yin9ac617c2017-12-19 11:20:50 -08006472 extras.putBoolean(EXTRA_IS_GROUP_CONVERSATION, mIsGroupConversation);
Adrian Roos33fbd2c2016-05-27 15:35:28 -07006473 }
6474
6475 private void fixTitleAndTextExtras(Bundle extras) {
6476 Message m = findLatestIncomingMessage();
6477 CharSequence text = (m == null) ? null : m.mText;
6478 CharSequence sender = m == null ? null
Selim Cinekcb8b9852017-12-15 18:01:52 -08006479 : m.mSender == null || TextUtils.isEmpty(m.mSender.getName())
6480 ? mUser.getName() : m.mSender.getName();
Adrian Roos33fbd2c2016-05-27 15:35:28 -07006481 CharSequence title;
6482 if (!TextUtils.isEmpty(mConversationTitle)) {
6483 if (!TextUtils.isEmpty(sender)) {
6484 BidiFormatter bidi = BidiFormatter.getInstance();
6485 title = mBuilder.mContext.getString(
6486 com.android.internal.R.string.notification_messaging_title_template,
Selim Cinekcb8b9852017-12-15 18:01:52 -08006487 bidi.unicodeWrap(mConversationTitle), bidi.unicodeWrap(sender));
Adrian Roos33fbd2c2016-05-27 15:35:28 -07006488 } else {
6489 title = mConversationTitle;
6490 }
6491 } else {
6492 title = sender;
6493 }
6494
6495 if (title != null) {
6496 extras.putCharSequence(EXTRA_TITLE, title);
6497 }
6498 if (text != null) {
6499 extras.putCharSequence(EXTRA_TEXT, text);
6500 }
Alex Hillsfc737de2016-03-23 17:33:02 -04006501 }
6502
6503 /**
6504 * @hide
6505 */
6506 @Override
6507 protected void restoreFromExtras(Bundle extras) {
6508 super.restoreFromExtras(extras);
6509
Selim Cinekcb8b9852017-12-15 18:01:52 -08006510 mUser = extras.getParcelable(EXTRA_MESSAGING_PERSON);
6511 if (mUser == null) {
6512 CharSequence displayName = extras.getCharSequence(EXTRA_SELF_DISPLAY_NAME);
6513 mUser = new Person().setName(displayName);
6514 }
Adrian Roos96b7e202016-05-17 13:50:38 -07006515 mConversationTitle = extras.getCharSequence(EXTRA_CONVERSATION_TITLE);
Adrian Roos437cd562017-01-18 15:47:03 -08006516 Parcelable[] messages = extras.getParcelableArray(EXTRA_MESSAGES);
Selim Cinek88188f22017-09-19 16:46:56 -07006517 mMessages = Message.getMessagesFromBundleArray(messages);
Adrian Roos437cd562017-01-18 15:47:03 -08006518 Parcelable[] histMessages = extras.getParcelableArray(EXTRA_HISTORIC_MESSAGES);
Selim Cinek88188f22017-09-19 16:46:56 -07006519 mHistoricMessages = Message.getMessagesFromBundleArray(histMessages);
Kodlee Yin9ac617c2017-12-19 11:20:50 -08006520 mIsGroupConversation = extras.getBoolean(EXTRA_IS_GROUP_CONVERSATION);
Alex Hillsfc737de2016-03-23 17:33:02 -04006521 }
6522
6523 /**
6524 * @hide
6525 */
Adrian Roosc1a80b02016-04-05 14:54:55 -07006526 @Override
Selim Cinek7d1009b2017-01-25 15:28:28 -08006527 public RemoteViews makeContentView(boolean increasedHeight) {
Selim Cineke62255c2017-09-28 18:23:23 -07006528 mBuilder.mOriginalActions = mBuilder.mActions;
6529 mBuilder.mActions = new ArrayList<>();
Selim Cinek7199ed92018-01-26 13:36:43 -08006530 RemoteViews remoteViews = makeMessagingView(true /* isCollapsed */);
Selim Cineke62255c2017-09-28 18:23:23 -07006531 mBuilder.mActions = mBuilder.mOriginalActions;
6532 mBuilder.mOriginalActions = null;
6533 return remoteViews;
Adrian Roosc1a80b02016-04-05 14:54:55 -07006534 }
6535
6536 private Message findLatestIncomingMessage() {
Selim Cinek88188f22017-09-19 16:46:56 -07006537 return findLatestIncomingMessage(mMessages);
6538 }
6539
6540 /**
6541 * @hide
6542 */
6543 @Nullable
6544 public static Message findLatestIncomingMessage(
6545 List<Message> messages) {
6546 for (int i = messages.size() - 1; i >= 0; i--) {
6547 Message m = messages.get(i);
Adrian Roosc1a80b02016-04-05 14:54:55 -07006548 // Incoming messages have a non-empty sender.
Selim Cinekcb8b9852017-12-15 18:01:52 -08006549 if (m.mSender != null && !TextUtils.isEmpty(m.mSender.getName())) {
Adrian Roosc1a80b02016-04-05 14:54:55 -07006550 return m;
6551 }
6552 }
Selim Cinek88188f22017-09-19 16:46:56 -07006553 if (!messages.isEmpty()) {
Adrian Roos33fbd2c2016-05-27 15:35:28 -07006554 // No incoming messages, fall back to outgoing message
Selim Cinek88188f22017-09-19 16:46:56 -07006555 return messages.get(messages.size() - 1);
Adrian Roos33fbd2c2016-05-27 15:35:28 -07006556 }
Adrian Roosc1a80b02016-04-05 14:54:55 -07006557 return null;
6558 }
6559
6560 /**
6561 * @hide
6562 */
6563 @Override
6564 public RemoteViews makeBigContentView() {
Selim Cinek7199ed92018-01-26 13:36:43 -08006565 return makeMessagingView(false /* isCollapsed */);
Selim Cinekafeed292017-12-12 17:32:44 -08006566 }
6567
6568 @NonNull
Selim Cinek7199ed92018-01-26 13:36:43 -08006569 private RemoteViews makeMessagingView(boolean isCollapsed) {
Selim Cinek88188f22017-09-19 16:46:56 -07006570 CharSequence conversationTitle = !TextUtils.isEmpty(super.mBigContentTitle)
Adrian Roosc1a80b02016-04-05 14:54:55 -07006571 ? super.mBigContentTitle
6572 : mConversationTitle;
Selim Cinek88188f22017-09-19 16:46:56 -07006573 boolean isOneToOne = TextUtils.isEmpty(conversationTitle);
Selim Cinek2dd3e722018-01-19 11:06:06 -08006574 CharSequence nameReplacement = null;
Selim Cinekafeed292017-12-12 17:32:44 -08006575 if (hasOnlyWhiteSpaceSenders()) {
Selim Cinekf7409db2017-10-24 16:17:14 -07006576 isOneToOne = true;
Selim Cinek2dd3e722018-01-19 11:06:06 -08006577 nameReplacement = conversationTitle;
6578 conversationTitle = null;
Adrian Roosb1f427c2016-05-26 12:27:15 -07006579 }
Selim Cinek7199ed92018-01-26 13:36:43 -08006580 boolean hideLargeIcon = !isCollapsed || isOneToOne;
Adrian Roos48d746a2016-04-12 14:57:28 -07006581 RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(
Adrian Roosc1a80b02016-04-05 14:54:55 -07006582 mBuilder.getMessagingLayoutResource(),
Selim Cinek88188f22017-09-19 16:46:56 -07006583 mBuilder.mParams.reset().hasProgress(false).title(conversationTitle).text(null)
Selim Cinek7199ed92018-01-26 13:36:43 -08006584 .hideLargeIcon(hideLargeIcon)
Selim Cinekafeed292017-12-12 17:32:44 -08006585 .headerTextSecondary(conversationTitle)
Selim Cinek7199ed92018-01-26 13:36:43 -08006586 .alwaysShowReply(isCollapsed));
Selim Cinek88188f22017-09-19 16:46:56 -07006587 addExtras(mBuilder.mN.extras);
Selim Cinekafeed292017-12-12 17:32:44 -08006588 // also update the end margin if there is an image
6589 int endMargin = R.dimen.notification_content_margin_end;
Selim Cinek7199ed92018-01-26 13:36:43 -08006590 if (isCollapsed) {
Selim Cinekafeed292017-12-12 17:32:44 -08006591 endMargin = R.dimen.notification_content_plus_picture_margin_end;
6592 }
6593 contentView.setViewLayoutMarginEndDimen(R.id.notification_main_column, endMargin);
Selim Cinek88188f22017-09-19 16:46:56 -07006594 contentView.setInt(R.id.status_bar_latest_event_content, "setLayoutColor",
6595 mBuilder.resolveContrastColor());
Selim Cinek7199ed92018-01-26 13:36:43 -08006596 contentView.setBoolean(R.id.status_bar_latest_event_content, "setIsCollapsed",
6597 isCollapsed);
Selim Cinek88188f22017-09-19 16:46:56 -07006598 contentView.setIcon(R.id.status_bar_latest_event_content, "setLargeIcon",
6599 mBuilder.mN.mLargeIcon);
Selim Cinek2dd3e722018-01-19 11:06:06 -08006600 contentView.setCharSequence(R.id.status_bar_latest_event_content, "setNameReplacement",
6601 nameReplacement);
Selim Cinek88188f22017-09-19 16:46:56 -07006602 contentView.setBoolean(R.id.status_bar_latest_event_content, "setIsOneToOne",
6603 isOneToOne);
6604 contentView.setBundle(R.id.status_bar_latest_event_content, "setData",
6605 mBuilder.mN.extras);
Alex Hillsfc737de2016-03-23 17:33:02 -04006606 return contentView;
6607 }
6608
Selim Cinekf7409db2017-10-24 16:17:14 -07006609 private boolean hasOnlyWhiteSpaceSenders() {
6610 for (int i = 0; i < mMessages.size(); i++) {
6611 Message m = mMessages.get(i);
Selim Cinekcb8b9852017-12-15 18:01:52 -08006612 Person sender = m.getSenderPerson();
6613 if (sender != null && !isWhiteSpace(sender.getName())) {
Selim Cinekf7409db2017-10-24 16:17:14 -07006614 return false;
6615 }
6616 }
6617 return true;
6618 }
6619
6620 private boolean isWhiteSpace(CharSequence sender) {
6621 if (TextUtils.isEmpty(sender)) {
6622 return true;
6623 }
6624 if (sender.toString().matches("^\\s*$")) {
6625 return true;
6626 }
6627 // Let's check if we only have 0 whitespace chars. Some apps did this as a workaround
6628 // For the presentation that we had.
6629 for (int i = 0; i < sender.length(); i++) {
6630 char c = sender.charAt(i);
6631 if (c != '\u200B') {
6632 return false;
6633 }
6634 }
6635 return true;
6636 }
6637
Selim Cinek88188f22017-09-19 16:46:56 -07006638 private CharSequence createConversationTitleFromMessages() {
6639 ArraySet<CharSequence> names = new ArraySet<>();
6640 for (int i = 0; i < mMessages.size(); i++) {
6641 Message m = mMessages.get(i);
Selim Cinekcb8b9852017-12-15 18:01:52 -08006642 Person sender = m.getSenderPerson();
Selim Cinek88188f22017-09-19 16:46:56 -07006643 if (sender != null) {
Selim Cinekcb8b9852017-12-15 18:01:52 -08006644 names.add(sender.getName());
Selim Cinek88188f22017-09-19 16:46:56 -07006645 }
6646 }
6647 SpannableStringBuilder title = new SpannableStringBuilder();
6648 int size = names.size();
6649 for (int i = 0; i < size; i++) {
6650 CharSequence name = names.valueAt(i);
6651 if (!TextUtils.isEmpty(title)) {
6652 title.append(", ");
6653 }
6654 title.append(BidiFormatter.getInstance().unicodeWrap(name));
6655 }
6656 return title;
6657 }
6658
Adrian Roosdedd1df2016-04-26 16:38:47 -07006659 /**
6660 * @hide
6661 */
6662 @Override
Selim Cinek87ed69b2017-02-09 15:59:43 -08006663 public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
Selim Cinek7199ed92018-01-26 13:36:43 -08006664 RemoteViews remoteViews = makeMessagingView(true /* isCollapsed */);
Selim Cineke62255c2017-09-28 18:23:23 -07006665 remoteViews.setInt(R.id.notification_messaging, "setMaxDisplayedLines", 1);
6666 return remoteViews;
Adrian Roosdedd1df2016-04-26 16:38:47 -07006667 }
6668
Adrian Roosc1a80b02016-04-05 14:54:55 -07006669 private static TextAppearanceSpan makeFontColorSpan(int color) {
6670 return new TextAppearanceSpan(null, 0, 0,
6671 ColorStateList.valueOf(color), null);
6672 }
6673
Alex Hillsd9b04d92016-04-11 16:38:16 -04006674 public static final class Message {
6675
6676 static final String KEY_TEXT = "text";
6677 static final String KEY_TIMESTAMP = "time";
6678 static final String KEY_SENDER = "sender";
Selim Cinekcb8b9852017-12-15 18:01:52 -08006679 static final String KEY_SENDER_PERSON = "sender_person";
Alex Hillsd9b04d92016-04-11 16:38:16 -04006680 static final String KEY_DATA_MIME_TYPE = "type";
6681 static final String KEY_DATA_URI= "uri";
Shane Brennan5a871862017-03-11 13:14:17 -08006682 static final String KEY_EXTRAS_BUNDLE = "extras";
Alex Hillsfc737de2016-03-23 17:33:02 -04006683
6684 private final CharSequence mText;
6685 private final long mTimestamp;
Selim Cinekcb8b9852017-12-15 18:01:52 -08006686 @Nullable
6687 private final Person mSender;
Alex Hillsfc737de2016-03-23 17:33:02 -04006688
Shane Brennan5a871862017-03-11 13:14:17 -08006689 private Bundle mExtras = new Bundle();
Alex Hillsfc737de2016-03-23 17:33:02 -04006690 private String mDataMimeType;
6691 private Uri mDataUri;
6692
6693 /**
6694 * Constructor
6695 * @param text A {@link CharSequence} to be displayed as the message content
6696 * @param timestamp Time at which the message arrived
6697 * @param sender A {@link CharSequence} to be used for displaying the name of the
6698 * sender. Should be <code>null</code> for messages by the current user, in which case
6699 * the platform will insert {@link MessagingStyle#getUserDisplayName()}.
6700 * Should be unique amongst all individuals in the conversation, and should be
6701 * consistent during re-posts of the notification.
Selim Cinekcb8b9852017-12-15 18:01:52 -08006702 *
6703 * @deprecated use {@code Message(CharSequence, long, Person)}
Alex Hillsfc737de2016-03-23 17:33:02 -04006704 */
6705 public Message(CharSequence text, long timestamp, CharSequence sender){
Selim Cinekcb8b9852017-12-15 18:01:52 -08006706 this(text, timestamp, sender == null ? null : new Person().setName(sender));
6707 }
6708
6709 /**
6710 * Constructor
6711 * @param text A {@link CharSequence} to be displayed as the message content
6712 * @param timestamp Time at which the message arrived
6713 * @param sender The {@link Person} who sent the message.
6714 * Should be <code>null</code> for messages by the current user, in which case
6715 * the platform will insert the user set in {@code MessagingStyle(Person)}.
6716 * <p>
6717 * The person provided should contain an Icon, set with {@link Person#setIcon(Icon)}
6718 * and also have a name provided with {@link Person#setName(CharSequence)}. If multiple
6719 * users have the same name, consider providing a key with {@link Person#setKey(String)}
6720 * in order to differentiate between the different users.
6721 * </p>
6722 */
6723 public Message(CharSequence text, long timestamp, @Nullable Person sender){
Alex Hillsfc737de2016-03-23 17:33:02 -04006724 mText = text;
6725 mTimestamp = timestamp;
6726 mSender = sender;
6727 }
6728
6729 /**
6730 * Sets a binary blob of data and an associated MIME type for a message. In the case
6731 * where the platform doesn't support the MIME type, the original text provided in the
6732 * constructor will be used.
6733 * @param dataMimeType The MIME type of the content. See
6734 * <a href="{@docRoot}notifications/messaging.html"> for the list of supported MIME
6735 * types on Android and Android Wear.
6736 * @param dataUri The uri containing the content whose type is given by the MIME type.
6737 * <p class="note">
6738 * <ol>
6739 * <li>Notification Listeners including the System UI need permission to access the
6740 * data the Uri points to. The recommended ways to do this are:</li>
6741 * <li>Store the data in your own ContentProvider, making sure that other apps have
6742 * the correct permission to access your provider. The preferred mechanism for
6743 * providing access is to use per-URI permissions which are temporary and only
6744 * grant access to the receiving application. An easy way to create a
6745 * ContentProvider like this is to use the FileProvider helper class.</li>
6746 * <li>Use the system MediaStore. The MediaStore is primarily aimed at video, audio
6747 * and image MIME types, however beginning with Android 3.0 (API level 11) it can
6748 * also store non-media types (see MediaStore.Files for more info). Files can be
6749 * inserted into the MediaStore using scanFile() after which a content:// style
6750 * Uri suitable for sharing is passed to the provided onScanCompleted() callback.
6751 * Note that once added to the system MediaStore the content is accessible to any
6752 * app on the device.</li>
6753 * </ol>
6754 * @return this object for method chaining
6755 */
6756 public Message setData(String dataMimeType, Uri dataUri) {
6757 mDataMimeType = dataMimeType;
6758 mDataUri = dataUri;
6759 return this;
6760 }
6761
Alex Hillsfc737de2016-03-23 17:33:02 -04006762 /**
6763 * Get the text to be used for this message, or the fallback text if a type and content
6764 * Uri have been set
6765 */
6766 public CharSequence getText() {
6767 return mText;
6768 }
6769
6770 /**
6771 * Get the time at which this message arrived
6772 */
6773 public long getTimestamp() {
6774 return mTimestamp;
6775 }
6776
6777 /**
Shane Brennan5a871862017-03-11 13:14:17 -08006778 * Get the extras Bundle for this message.
6779 */
6780 public Bundle getExtras() {
6781 return mExtras;
6782 }
6783
6784 /**
Alex Hillsfc737de2016-03-23 17:33:02 -04006785 * Get the text used to display the contact's name in the messaging experience
Selim Cinekcb8b9852017-12-15 18:01:52 -08006786 *
6787 * @deprecated use {@link #getSenderPerson()}
Alex Hillsfc737de2016-03-23 17:33:02 -04006788 */
6789 public CharSequence getSender() {
Selim Cinekcb8b9852017-12-15 18:01:52 -08006790 return mSender == null ? null : mSender.getName();
6791 }
6792
6793 /**
6794 * Get the sender associated with this message.
6795 */
6796 @Nullable
6797 public Person getSenderPerson() {
Alex Hillsfc737de2016-03-23 17:33:02 -04006798 return mSender;
6799 }
6800
6801 /**
6802 * Get the MIME type of the data pointed to by the Uri
6803 */
6804 public String getDataMimeType() {
6805 return mDataMimeType;
6806 }
6807
6808 /**
6809 * Get the the Uri pointing to the content of the message. Can be null, in which case
6810 * {@see #getText()} is used.
6811 */
6812 public Uri getDataUri() {
6813 return mDataUri;
6814 }
6815
Alex Hillsd9b04d92016-04-11 16:38:16 -04006816 private Bundle toBundle() {
6817 Bundle bundle = new Bundle();
Alex Hillsfc737de2016-03-23 17:33:02 -04006818 if (mText != null) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04006819 bundle.putCharSequence(KEY_TEXT, mText);
Alex Hillsfc737de2016-03-23 17:33:02 -04006820 }
Alex Hillsd9b04d92016-04-11 16:38:16 -04006821 bundle.putLong(KEY_TIMESTAMP, mTimestamp);
Alex Hillsfc737de2016-03-23 17:33:02 -04006822 if (mSender != null) {
Selim Cinekcb8b9852017-12-15 18:01:52 -08006823 // Legacy listeners need this
6824 bundle.putCharSequence(KEY_SENDER, mSender.getName());
6825 bundle.putParcelable(KEY_SENDER_PERSON, mSender);
Alex Hillsfc737de2016-03-23 17:33:02 -04006826 }
6827 if (mDataMimeType != null) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04006828 bundle.putString(KEY_DATA_MIME_TYPE, mDataMimeType);
Alex Hillsfc737de2016-03-23 17:33:02 -04006829 }
6830 if (mDataUri != null) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04006831 bundle.putParcelable(KEY_DATA_URI, mDataUri);
Alex Hillsfc737de2016-03-23 17:33:02 -04006832 }
Shane Brennan5a871862017-03-11 13:14:17 -08006833 if (mExtras != null) {
6834 bundle.putBundle(KEY_EXTRAS_BUNDLE, mExtras);
6835 }
Alex Hillsd9b04d92016-04-11 16:38:16 -04006836 return bundle;
Alex Hillsfc737de2016-03-23 17:33:02 -04006837 }
6838
Alex Hillsd9b04d92016-04-11 16:38:16 -04006839 static Bundle[] getBundleArrayForMessages(List<Message> messages) {
6840 Bundle[] bundles = new Bundle[messages.size()];
6841 final int N = messages.size();
6842 for (int i = 0; i < N; i++) {
6843 bundles[i] = messages.get(i).toBundle();
6844 }
6845 return bundles;
6846 }
6847
Selim Cinek88188f22017-09-19 16:46:56 -07006848 /**
6849 * @return A list of messages read from the bundles.
6850 *
6851 * @hide
6852 */
6853 public static List<Message> getMessagesFromBundleArray(Parcelable[] bundles) {
6854 if (bundles == null) {
6855 return new ArrayList<>();
6856 }
Alex Hillsd9b04d92016-04-11 16:38:16 -04006857 List<Message> messages = new ArrayList<>(bundles.length);
6858 for (int i = 0; i < bundles.length; i++) {
Adrian Roosdedd1df2016-04-26 16:38:47 -07006859 if (bundles[i] instanceof Bundle) {
6860 Message message = getMessageFromBundle((Bundle)bundles[i]);
6861 if (message != null) {
6862 messages.add(message);
6863 }
Alex Hillsd9b04d92016-04-11 16:38:16 -04006864 }
6865 }
6866 return messages;
6867 }
6868
6869 static Message getMessageFromBundle(Bundle bundle) {
6870 try {
Adrian Roosfbddd2c2016-05-13 12:57:20 -07006871 if (!bundle.containsKey(KEY_TEXT) || !bundle.containsKey(KEY_TIMESTAMP)) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04006872 return null;
6873 } else {
Selim Cinekcb8b9852017-12-15 18:01:52 -08006874
6875 Person senderPerson = bundle.getParcelable(KEY_SENDER_PERSON);
6876 if (senderPerson == null) {
6877 // Legacy apps that use compat don't actually provide the sender objects
6878 // We need to fix the compat version to provide people / use
6879 // the native api instead
6880 CharSequence senderName = bundle.getCharSequence(KEY_SENDER);
6881 if (senderName != null) {
6882 senderPerson = new Person().setName(senderName);
6883 }
6884 }
Alex Hillsd9b04d92016-04-11 16:38:16 -04006885 Message message = new Message(bundle.getCharSequence(KEY_TEXT),
Selim Cinekcb8b9852017-12-15 18:01:52 -08006886 bundle.getLong(KEY_TIMESTAMP),
6887 senderPerson);
Alex Hillsd9b04d92016-04-11 16:38:16 -04006888 if (bundle.containsKey(KEY_DATA_MIME_TYPE) &&
6889 bundle.containsKey(KEY_DATA_URI)) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04006890 message.setData(bundle.getString(KEY_DATA_MIME_TYPE),
6891 (Uri) bundle.getParcelable(KEY_DATA_URI));
Alex Hillsfc737de2016-03-23 17:33:02 -04006892 }
Shane Brennan5a871862017-03-11 13:14:17 -08006893 if (bundle.containsKey(KEY_EXTRAS_BUNDLE)) {
6894 message.getExtras().putAll(bundle.getBundle(KEY_EXTRAS_BUNDLE));
6895 }
Alex Hillsd9b04d92016-04-11 16:38:16 -04006896 return message;
6897 }
6898 } catch (ClassCastException e) {
6899 return null;
6900 }
6901 }
Alex Hillsfc737de2016-03-23 17:33:02 -04006902 }
6903 }
6904
6905 /**
Daniel Sandler879c5e02012-04-17 16:46:51 -04006906 * Helper class for generating large-format notifications that include a list of (up to 5) strings.
Joe Malin8d40d042012-11-05 11:36:40 -08006907 *
Robert Ly91c5ce32014-06-08 15:37:00 -07006908 * Here's how you'd set the <code>InboxStyle</code> on a notification:
Daniel Sandler879c5e02012-04-17 16:46:51 -04006909 * <pre class="prettyprint">
Robert Ly91c5ce32014-06-08 15:37:00 -07006910 * Notification notif = new Notification.Builder(mContext)
6911 * .setContentTitle(&quot;5 New mails from &quot; + sender.toString())
6912 * .setContentText(subject)
6913 * .setSmallIcon(R.drawable.new_mail)
6914 * .setLargeIcon(aBitmap)
6915 * .setStyle(new Notification.InboxStyle()
6916 * .addLine(str1)
6917 * .addLine(str2)
6918 * .setContentTitle(&quot;&quot;)
6919 * .setSummaryText(&quot;+3 more&quot;))
6920 * .build();
Daniel Sandler879c5e02012-04-17 16:46:51 -04006921 * </pre>
Joe Malin8d40d042012-11-05 11:36:40 -08006922 *
Daniel Sandler879c5e02012-04-17 16:46:51 -04006923 * @see Notification#bigContentView
6924 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04006925 public static class InboxStyle extends Style {
Daniel Sandler879c5e02012-04-17 16:46:51 -04006926 private ArrayList<CharSequence> mTexts = new ArrayList<CharSequence>(5);
6927
Chris Wrenfbd96ba2012-05-01 12:03:58 -04006928 public InboxStyle() {
6929 }
6930
Adrian Roosf5faf9d2016-05-23 13:56:15 -07006931 /**
6932 * @deprecated use {@code InboxStyle()}.
6933 */
6934 @Deprecated
Daniel Sandler879c5e02012-04-17 16:46:51 -04006935 public InboxStyle(Builder builder) {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04006936 setBuilder(builder);
Daniel Sandler879c5e02012-04-17 16:46:51 -04006937 }
6938
Chris Wrend6297db2012-05-03 16:20:13 -04006939 /**
6940 * Overrides ContentTitle in the big form of the template.
6941 * This defaults to the value passed to setContentTitle().
6942 */
6943 public InboxStyle setBigContentTitle(CharSequence title) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04006944 internalSetBigContentTitle(safeCharSequence(title));
Chris Wrend6297db2012-05-03 16:20:13 -04006945 return this;
6946 }
6947
6948 /**
6949 * Set the first line of text after the detail section in the big form of the template.
6950 */
6951 public InboxStyle setSummaryText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04006952 internalSetSummaryText(safeCharSequence(cs));
Chris Wrend6297db2012-05-03 16:20:13 -04006953 return this;
6954 }
6955
Chris Wren0bd664d2012-08-01 13:56:56 -04006956 /**
6957 * Append a line to the digest section of the Inbox notification.
6958 */
Daniel Sandler879c5e02012-04-17 16:46:51 -04006959 public InboxStyle addLine(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04006960 mTexts.add(safeCharSequence(cs));
Daniel Sandler879c5e02012-04-17 16:46:51 -04006961 return this;
6962 }
6963
Daniel Sandlerf45564e2013-04-15 15:05:08 -04006964 /**
6965 * @hide
6966 */
6967 public void addExtras(Bundle extras) {
6968 super.addExtras(extras);
Christoph Studer4600f9b2014-07-22 22:44:43 +02006969
Daniel Sandlerf45564e2013-04-15 15:05:08 -04006970 CharSequence[] a = new CharSequence[mTexts.size()];
6971 extras.putCharSequenceArray(EXTRA_TEXT_LINES, mTexts.toArray(a));
6972 }
6973
Christoph Studer4600f9b2014-07-22 22:44:43 +02006974 /**
6975 * @hide
6976 */
6977 @Override
6978 protected void restoreFromExtras(Bundle extras) {
6979 super.restoreFromExtras(extras);
6980
6981 mTexts.clear();
6982 if (extras.containsKey(EXTRA_TEXT_LINES)) {
6983 Collections.addAll(mTexts, extras.getCharSequenceArray(EXTRA_TEXT_LINES));
6984 }
6985 }
6986
Julia Reynoldsd9228f12015-10-20 10:37:27 -04006987 /**
6988 * @hide
6989 */
6990 public RemoteViews makeBigContentView() {
Selim Cinekc848c3a2016-01-13 15:27:30 -08006991 // Remove the content text so it disappears unless you have a summary
Christoph Studer4600f9b2014-07-22 22:44:43 +02006992 // Nasty
Julia Reynoldsd9228f12015-10-20 10:37:27 -04006993 CharSequence oldBuilderContentText = mBuilder.mN.extras.getCharSequence(EXTRA_TEXT);
6994 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, null);
Christoph Studer4600f9b2014-07-22 22:44:43 +02006995
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01006996 RemoteViews contentView = getStandardView(mBuilder.getInboxLayoutResource());
Daniel Sandler619738c2012-06-07 16:33:08 -04006997
Julia Reynoldsd9228f12015-10-20 10:37:27 -04006998 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, oldBuilderContentText);
Christoph Studer4600f9b2014-07-22 22:44:43 +02006999
Chris Wrend6297db2012-05-03 16:20:13 -04007000 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 -04007001 R.id.inbox_text4, R.id.inbox_text5, R.id.inbox_text6};
Chris Wrend6297db2012-05-03 16:20:13 -04007002
Chris Wren4ed80d52012-05-17 09:30:03 -04007003 // Make sure all rows are gone in case we reuse a view.
7004 for (int rowId : rowIds) {
7005 contentView.setViewVisibility(rowId, View.GONE);
7006 }
7007
Daniel Sandler879c5e02012-04-17 16:46:51 -04007008 int i=0;
Selim Cinek07c80172016-04-21 16:40:47 -07007009 int topPadding = mBuilder.mContext.getResources().getDimensionPixelSize(
7010 R.dimen.notification_inbox_item_top_padding);
Selim Cinek247fa012016-02-18 09:50:48 -08007011 boolean first = true;
Selim Cinek07c80172016-04-21 16:40:47 -07007012 int onlyViewId = 0;
7013 int maxRows = rowIds.length;
7014 if (mBuilder.mActions.size() > 0) {
7015 maxRows--;
7016 }
7017 while (i < mTexts.size() && i < maxRows) {
Daniel Sandler879c5e02012-04-17 16:46:51 -04007018 CharSequence str = mTexts.get(i);
Selim Cinek07c80172016-04-21 16:40:47 -07007019 if (!TextUtils.isEmpty(str)) {
Daniel Sandler879c5e02012-04-17 16:46:51 -04007020 contentView.setViewVisibility(rowIds[i], View.VISIBLE);
Selim Cinek48f66b72017-08-18 16:17:51 -07007021 contentView.setTextViewText(rowIds[i],
7022 mBuilder.processTextSpans(mBuilder.processLegacyText(str)));
Selim Cinek7b9605b2017-01-19 17:36:00 -08007023 mBuilder.setTextViewColorSecondary(contentView, rowIds[i]);
Selim Cinek07c80172016-04-21 16:40:47 -07007024 contentView.setViewPadding(rowIds[i], 0, topPadding, 0, 0);
Selim Cinek247fa012016-02-18 09:50:48 -08007025 handleInboxImageMargin(contentView, rowIds[i], first);
Selim Cinek07c80172016-04-21 16:40:47 -07007026 if (first) {
7027 onlyViewId = rowIds[i];
7028 } else {
7029 onlyViewId = 0;
7030 }
Selim Cinek247fa012016-02-18 09:50:48 -08007031 first = false;
Daniel Sandler879c5e02012-04-17 16:46:51 -04007032 }
7033 i++;
7034 }
Selim Cinek07c80172016-04-21 16:40:47 -07007035 if (onlyViewId != 0) {
7036 // We only have 1 entry, lets make it look like the normal Text of a Bigtext
7037 topPadding = mBuilder.mContext.getResources().getDimensionPixelSize(
7038 R.dimen.notification_text_margin_top);
7039 contentView.setViewPadding(onlyViewId, 0, topPadding, 0, 0);
7040 }
Selim Cinek1e0bf612015-11-20 15:57:26 -08007041
Daniel Sandler879c5e02012-04-17 16:46:51 -04007042 return contentView;
7043 }
Selim Cinek1e0bf612015-11-20 15:57:26 -08007044
Selim Cinek247fa012016-02-18 09:50:48 -08007045 private void handleInboxImageMargin(RemoteViews contentView, int id, boolean first) {
Selim Cinek1e0bf612015-11-20 15:57:26 -08007046 int endMargin = 0;
Selim Cinek247fa012016-02-18 09:50:48 -08007047 if (first) {
7048 final int max = mBuilder.mN.extras.getInt(EXTRA_PROGRESS_MAX, 0);
7049 final boolean ind = mBuilder.mN.extras.getBoolean(EXTRA_PROGRESS_INDETERMINATE);
7050 boolean hasProgress = max != 0 || ind;
Selim Cinek279fa862016-06-14 10:57:25 -07007051 if (mBuilder.mN.hasLargeIcon() && !hasProgress) {
Adrian Roos2d5dbba2016-06-08 17:11:53 -07007052 endMargin = R.dimen.notification_content_picture_margin;
Selim Cinek247fa012016-02-18 09:50:48 -08007053 }
Selim Cinek1e0bf612015-11-20 15:57:26 -08007054 }
Adrian Roos2d5dbba2016-06-08 17:11:53 -07007055 contentView.setViewLayoutMarginEndDimen(id, endMargin);
Selim Cinek1e0bf612015-11-20 15:57:26 -08007056 }
Daniel Sandler879c5e02012-04-17 16:46:51 -04007057 }
Dan Sandler842dd772014-05-15 09:36:47 -04007058
7059 /**
7060 * Notification style for media playback notifications.
7061 *
7062 * In the expanded form, {@link Notification#bigContentView}, up to 5
7063 * {@link Notification.Action}s specified with
Dan Sandler86647982015-05-13 23:41:13 -04007064 * {@link Notification.Builder#addAction(Action) addAction} will be
Dan Sandler842dd772014-05-15 09:36:47 -04007065 * shown as icon-only pushbuttons, suitable for transport controls. The Bitmap given to
7066 * {@link Notification.Builder#setLargeIcon(android.graphics.Bitmap) setLargeIcon()} will be
7067 * treated as album artwork.
Selim Cinek99104832017-01-25 14:47:33 -08007068 * <p>
Dan Sandler842dd772014-05-15 09:36:47 -04007069 * Unlike the other styles provided here, MediaStyle can also modify the standard-size
7070 * {@link Notification#contentView}; by providing action indices to
Christoph Studerfde6f4d2014-12-12 13:23:26 +01007071 * {@link #setShowActionsInCompactView(int...)} you can promote up to 3 actions to be displayed
Dan Sandler842dd772014-05-15 09:36:47 -04007072 * in the standard view alongside the usual content.
Selim Cinek99104832017-01-25 14:47:33 -08007073 * <p>
Bryan Mawhinney6be8de32014-07-18 10:35:12 +01007074 * Notifications created with MediaStyle will have their category set to
7075 * {@link Notification#CATEGORY_TRANSPORT CATEGORY_TRANSPORT} unless you set a different
7076 * category using {@link Notification.Builder#setCategory(String) setCategory()}.
Selim Cinek99104832017-01-25 14:47:33 -08007077 * <p>
Jeff Browndba34ba2014-06-24 20:46:03 -07007078 * Finally, if you attach a {@link android.media.session.MediaSession.Token} using
7079 * {@link android.app.Notification.MediaStyle#setMediaSession(MediaSession.Token)},
Dan Sandler842dd772014-05-15 09:36:47 -04007080 * the System UI can identify this as a notification representing an active media session
7081 * and respond accordingly (by showing album artwork in the lockscreen, for example).
7082 *
Selim Cinek99104832017-01-25 14:47:33 -08007083 * <p>
7084 * Starting at {@link android.os.Build.VERSION_CODES#O Android O} any notification that has a
7085 * media session attached with {@link #setMediaSession(MediaSession.Token)} will be colorized.
7086 * You can opt-out of this behavior by using {@link Notification.Builder#setColorized(boolean)}.
7087 * <p>
7088 *
Dan Sandler842dd772014-05-15 09:36:47 -04007089 * To use this style with your Notification, feed it to
7090 * {@link Notification.Builder#setStyle(android.app.Notification.Style)} like so:
7091 * <pre class="prettyprint">
7092 * Notification noti = new Notification.Builder()
7093 * .setSmallIcon(R.drawable.ic_stat_player)
Christoph Studere935fe92014-11-24 14:18:06 +01007094 * .setContentTitle(&quot;Track title&quot;)
7095 * .setContentText(&quot;Artist - Album&quot;)
7096 * .setLargeIcon(albumArtBitmap))
Bryan Mawhinney6be8de32014-07-18 10:35:12 +01007097 * .setStyle(<b>new Notification.MediaStyle()</b>
7098 * .setMediaSession(mySession))
Dan Sandler842dd772014-05-15 09:36:47 -04007099 * .build();
7100 * </pre>
7101 *
7102 * @see Notification#bigContentView
Selim Cinek99104832017-01-25 14:47:33 -08007103 * @see Notification.Builder#setColorized(boolean)
Dan Sandler842dd772014-05-15 09:36:47 -04007104 */
7105 public static class MediaStyle extends Style {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02007106 static final int MAX_MEDIA_BUTTONS_IN_COMPACT = 3;
Dan Sandler842dd772014-05-15 09:36:47 -04007107 static final int MAX_MEDIA_BUTTONS = 5;
7108
7109 private int[] mActionsToShowInCompact = null;
Jeff Browndba34ba2014-06-24 20:46:03 -07007110 private MediaSession.Token mToken;
Dan Sandler842dd772014-05-15 09:36:47 -04007111
7112 public MediaStyle() {
7113 }
7114
Adrian Roosf5faf9d2016-05-23 13:56:15 -07007115 /**
7116 * @deprecated use {@code MediaStyle()}.
7117 */
7118 @Deprecated
Dan Sandler842dd772014-05-15 09:36:47 -04007119 public MediaStyle(Builder builder) {
7120 setBuilder(builder);
7121 }
7122
7123 /**
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02007124 * 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 -04007125 * notification view.
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02007126 *
7127 * @param actions the indices of the actions to show in the compact notification view
Dan Sandler842dd772014-05-15 09:36:47 -04007128 */
7129 public MediaStyle setShowActionsInCompactView(int...actions) {
7130 mActionsToShowInCompact = actions;
7131 return this;
7132 }
7133
7134 /**
Jeff Browndba34ba2014-06-24 20:46:03 -07007135 * Attach a {@link android.media.session.MediaSession.Token} to this Notification
7136 * to provide additional playback information and control to the SystemUI.
Dan Sandler842dd772014-05-15 09:36:47 -04007137 */
Jeff Browndba34ba2014-06-24 20:46:03 -07007138 public MediaStyle setMediaSession(MediaSession.Token token) {
Dan Sandler842dd772014-05-15 09:36:47 -04007139 mToken = token;
7140 return this;
7141 }
7142
Christoph Studer4600f9b2014-07-22 22:44:43 +02007143 /**
7144 * @hide
7145 */
Dan Sandler842dd772014-05-15 09:36:47 -04007146 @Override
7147 public Notification buildStyled(Notification wip) {
Christoph Studer4600f9b2014-07-22 22:44:43 +02007148 super.buildStyled(wip);
Bryan Mawhinney6be8de32014-07-18 10:35:12 +01007149 if (wip.category == null) {
7150 wip.category = Notification.CATEGORY_TRANSPORT;
7151 }
Dan Sandler842dd772014-05-15 09:36:47 -04007152 return wip;
7153 }
7154
Christoph Studer4600f9b2014-07-22 22:44:43 +02007155 /**
7156 * @hide
7157 */
7158 @Override
Selim Cinek7d1009b2017-01-25 15:28:28 -08007159 public RemoteViews makeContentView(boolean increasedHeight) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04007160 return makeMediaContentView();
Christoph Studer4600f9b2014-07-22 22:44:43 +02007161 }
7162
7163 /**
7164 * @hide
7165 */
7166 @Override
Julia Reynoldsd9228f12015-10-20 10:37:27 -04007167 public RemoteViews makeBigContentView() {
7168 return makeMediaBigContentView();
Christoph Studer4600f9b2014-07-22 22:44:43 +02007169 }
7170
Selim Cinekcc10bfb2016-02-10 16:24:21 -08007171 /**
7172 * @hide
7173 */
7174 @Override
Selim Cinek87ed69b2017-02-09 15:59:43 -08007175 public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
Selim Cinekcc10bfb2016-02-10 16:24:21 -08007176 RemoteViews expanded = makeMediaBigContentView();
7177 return expanded != null ? expanded : makeMediaContentView();
7178 }
7179
Dan Sandler842dd772014-05-15 09:36:47 -04007180 /** @hide */
7181 @Override
7182 public void addExtras(Bundle extras) {
7183 super.addExtras(extras);
7184
7185 if (mToken != null) {
7186 extras.putParcelable(EXTRA_MEDIA_SESSION, mToken);
7187 }
Bryan Mawhinneye191f902014-07-22 12:50:09 +01007188 if (mActionsToShowInCompact != null) {
7189 extras.putIntArray(EXTRA_COMPACT_ACTIONS, mActionsToShowInCompact);
7190 }
Dan Sandler842dd772014-05-15 09:36:47 -04007191 }
7192
Christoph Studer4600f9b2014-07-22 22:44:43 +02007193 /**
7194 * @hide
7195 */
7196 @Override
7197 protected void restoreFromExtras(Bundle extras) {
7198 super.restoreFromExtras(extras);
7199
7200 if (extras.containsKey(EXTRA_MEDIA_SESSION)) {
7201 mToken = extras.getParcelable(EXTRA_MEDIA_SESSION);
7202 }
7203 if (extras.containsKey(EXTRA_COMPACT_ACTIONS)) {
7204 mActionsToShowInCompact = extras.getIntArray(EXTRA_COMPACT_ACTIONS);
7205 }
7206 }
7207
Selim Cinek5bf069a2015-11-10 19:14:27 -05007208 private RemoteViews generateMediaActionButton(Action action, int color) {
Dan Sandler842dd772014-05-15 09:36:47 -04007209 final boolean tombstone = (action.actionIntent == null);
Selim Cinekf33b1112015-07-15 17:45:11 -07007210 RemoteViews button = new BuilderRemoteViews(mBuilder.mContext.getApplicationInfo(),
Alan Viverette3cb07a462014-06-06 14:19:53 -07007211 R.layout.notification_material_media_action);
Dan Sandler68079d52015-07-22 10:45:30 -04007212 button.setImageViewIcon(R.id.action0, action.getIcon());
Anthony Chenad4d1582017-04-10 16:07:58 -07007213
7214 // If the action buttons should not be tinted, then just use the default
7215 // notification color. Otherwise, just use the passed-in color.
7216 int tintColor = mBuilder.shouldTintActionButtons() || mBuilder.isColorized()
7217 ? color
7218 : NotificationColorUtil.resolveColor(mBuilder.mContext,
7219 Notification.COLOR_DEFAULT);
7220
Sunny Goyal5b153922017-09-21 21:00:36 -07007221 button.setDrawableTint(R.id.action0, false, tintColor,
7222 PorterDuff.Mode.SRC_ATOP);
Dan Sandler842dd772014-05-15 09:36:47 -04007223 if (!tombstone) {
7224 button.setOnClickPendingIntent(R.id.action0, action.actionIntent);
7225 }
7226 button.setContentDescription(R.id.action0, action.title);
7227 return button;
7228 }
7229
7230 private RemoteViews makeMediaContentView() {
7231 RemoteViews view = mBuilder.applyStandardTemplate(
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02007232 R.layout.notification_template_material_media, false /* hasProgress */);
Dan Sandler842dd772014-05-15 09:36:47 -04007233
7234 final int numActions = mBuilder.mActions.size();
7235 final int N = mActionsToShowInCompact == null
7236 ? 0
7237 : Math.min(mActionsToShowInCompact.length, MAX_MEDIA_BUTTONS_IN_COMPACT);
7238 if (N > 0) {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02007239 view.removeAllViews(com.android.internal.R.id.media_actions);
Dan Sandler842dd772014-05-15 09:36:47 -04007240 for (int i = 0; i < N; i++) {
7241 if (i >= numActions) {
7242 throw new IllegalArgumentException(String.format(
7243 "setShowActionsInCompactView: action %d out of bounds (max %d)",
7244 i, numActions - 1));
7245 }
7246
7247 final Action action = mBuilder.mActions.get(mActionsToShowInCompact[i]);
Selim Cinek5bf069a2015-11-10 19:14:27 -05007248 final RemoteViews button = generateMediaActionButton(action,
Selim Cinek99104832017-01-25 14:47:33 -08007249 getPrimaryHighlightColor());
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02007250 view.addView(com.android.internal.R.id.media_actions, button);
Dan Sandler842dd772014-05-15 09:36:47 -04007251 }
7252 }
Selim Cinekfdc738f2016-01-27 20:04:27 -08007253 handleImage(view);
7254 // handle the content margin
Adrian Roos2d5dbba2016-06-08 17:11:53 -07007255 int endMargin = R.dimen.notification_content_margin_end;
Selim Cinek279fa862016-06-14 10:57:25 -07007256 if (mBuilder.mN.hasLargeIcon()) {
Adrian Roos2d5dbba2016-06-08 17:11:53 -07007257 endMargin = R.dimen.notification_content_plus_picture_margin_end;
Selim Cinekfdc738f2016-01-27 20:04:27 -08007258 }
Adrian Roos2d5dbba2016-06-08 17:11:53 -07007259 view.setViewLayoutMarginEndDimen(R.id.notification_main_column, endMargin);
Dan Sandler842dd772014-05-15 09:36:47 -04007260 return view;
7261 }
7262
Selim Cinek99104832017-01-25 14:47:33 -08007263 private int getPrimaryHighlightColor() {
7264 return mBuilder.getPrimaryHighlightColor();
7265 }
7266
Dan Sandler842dd772014-05-15 09:36:47 -04007267 private RemoteViews makeMediaBigContentView() {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02007268 final int actionCount = Math.min(mBuilder.mActions.size(), MAX_MEDIA_BUTTONS);
Selim Cinekcc10bfb2016-02-10 16:24:21 -08007269 // Dont add an expanded view if there is no more content to be revealed
7270 int actionsInCompact = mActionsToShowInCompact == null
7271 ? 0
7272 : Math.min(mActionsToShowInCompact.length, MAX_MEDIA_BUTTONS_IN_COMPACT);
Selim Cinek279fa862016-06-14 10:57:25 -07007273 if (!mBuilder.mN.hasLargeIcon() && actionCount <= actionsInCompact) {
Selim Cinekcc10bfb2016-02-10 16:24:21 -08007274 return null;
7275 }
Selim Cinek5bf069a2015-11-10 19:14:27 -05007276 RemoteViews big = mBuilder.applyStandardTemplate(
7277 R.layout.notification_template_material_big_media,
7278 false);
Dan Sandler842dd772014-05-15 09:36:47 -04007279
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02007280 if (actionCount > 0) {
7281 big.removeAllViews(com.android.internal.R.id.media_actions);
7282 for (int i = 0; i < actionCount; i++) {
Selim Cinek5bf069a2015-11-10 19:14:27 -05007283 final RemoteViews button = generateMediaActionButton(mBuilder.mActions.get(i),
Selim Cinek99104832017-01-25 14:47:33 -08007284 getPrimaryHighlightColor());
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02007285 big.addView(com.android.internal.R.id.media_actions, button);
Dan Sandler842dd772014-05-15 09:36:47 -04007286 }
7287 }
Selim Cinek5bf069a2015-11-10 19:14:27 -05007288 handleImage(big);
Dan Sandler842dd772014-05-15 09:36:47 -04007289 return big;
7290 }
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02007291
Selim Cinek5bf069a2015-11-10 19:14:27 -05007292 private void handleImage(RemoteViews contentView) {
Selim Cinek279fa862016-06-14 10:57:25 -07007293 if (mBuilder.mN.hasLargeIcon()) {
Adrian Roos2d5dbba2016-06-08 17:11:53 -07007294 contentView.setViewLayoutMarginEndDimen(R.id.line1, 0);
7295 contentView.setViewLayoutMarginEndDimen(R.id.text, 0);
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02007296 }
7297 }
7298
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02007299 /**
7300 * @hide
7301 */
7302 @Override
7303 protected boolean hasProgress() {
7304 return false;
7305 }
Dan Sandler842dd772014-05-15 09:36:47 -04007306 }
Griff Hazen61a9e862014-05-22 16:05:19 -07007307
Selim Cinek593610c2016-02-16 18:42:57 -08007308 /**
7309 * Notification style for custom views that are decorated by the system
7310 *
7311 * <p>Instead of providing a notification that is completely custom, a developer can set this
7312 * style and still obtain system decorations like the notification header with the expand
7313 * affordance and actions.
7314 *
7315 * <p>Use {@link android.app.Notification.Builder#setCustomContentView(RemoteViews)},
7316 * {@link android.app.Notification.Builder#setCustomBigContentView(RemoteViews)} and
7317 * {@link android.app.Notification.Builder#setCustomHeadsUpContentView(RemoteViews)} to set the
7318 * corresponding custom views to display.
7319 *
7320 * To use this style with your Notification, feed it to
7321 * {@link Notification.Builder#setStyle(android.app.Notification.Style)} like so:
7322 * <pre class="prettyprint">
7323 * Notification noti = new Notification.Builder()
7324 * .setSmallIcon(R.drawable.ic_stat_player)
7325 * .setLargeIcon(albumArtBitmap))
7326 * .setCustomContentView(contentView);
7327 * .setStyle(<b>new Notification.DecoratedCustomViewStyle()</b>)
7328 * .build();
7329 * </pre>
7330 */
7331 public static class DecoratedCustomViewStyle extends Style {
7332
7333 public DecoratedCustomViewStyle() {
7334 }
7335
Selim Cinek593610c2016-02-16 18:42:57 -08007336 /**
7337 * @hide
7338 */
7339 public boolean displayCustomViewInline() {
7340 return true;
7341 }
7342
7343 /**
7344 * @hide
7345 */
7346 @Override
Selim Cinek7d1009b2017-01-25 15:28:28 -08007347 public RemoteViews makeContentView(boolean increasedHeight) {
Selim Cinek593610c2016-02-16 18:42:57 -08007348 return makeStandardTemplateWithCustomContent(mBuilder.mN.contentView);
7349 }
7350
7351 /**
7352 * @hide
7353 */
7354 @Override
7355 public RemoteViews makeBigContentView() {
7356 return makeDecoratedBigContentView();
7357 }
7358
7359 /**
7360 * @hide
7361 */
7362 @Override
Selim Cinek87ed69b2017-02-09 15:59:43 -08007363 public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
Selim Cinek593610c2016-02-16 18:42:57 -08007364 return makeDecoratedHeadsUpContentView();
7365 }
7366
Selim Cinek593610c2016-02-16 18:42:57 -08007367 private RemoteViews makeDecoratedHeadsUpContentView() {
7368 RemoteViews headsUpContentView = mBuilder.mN.headsUpContentView == null
7369 ? mBuilder.mN.contentView
7370 : mBuilder.mN.headsUpContentView;
7371 if (mBuilder.mActions.size() == 0) {
7372 return makeStandardTemplateWithCustomContent(headsUpContentView);
7373 }
7374 RemoteViews remoteViews = mBuilder.applyStandardTemplateWithActions(
7375 mBuilder.getBigBaseLayoutResource());
Selim Cinek247fa012016-02-18 09:50:48 -08007376 buildIntoRemoteViewContent(remoteViews, headsUpContentView);
Selim Cinek593610c2016-02-16 18:42:57 -08007377 return remoteViews;
7378 }
7379
Selim Cinek593610c2016-02-16 18:42:57 -08007380 private RemoteViews makeStandardTemplateWithCustomContent(RemoteViews customContent) {
7381 RemoteViews remoteViews = mBuilder.applyStandardTemplate(
7382 mBuilder.getBaseLayoutResource());
Selim Cinek247fa012016-02-18 09:50:48 -08007383 buildIntoRemoteViewContent(remoteViews, customContent);
Selim Cinek593610c2016-02-16 18:42:57 -08007384 return remoteViews;
7385 }
7386
Selim Cinek593610c2016-02-16 18:42:57 -08007387 private RemoteViews makeDecoratedBigContentView() {
7388 RemoteViews bigContentView = mBuilder.mN.bigContentView == null
7389 ? mBuilder.mN.contentView
7390 : mBuilder.mN.bigContentView;
7391 if (mBuilder.mActions.size() == 0) {
7392 return makeStandardTemplateWithCustomContent(bigContentView);
7393 }
7394 RemoteViews remoteViews = mBuilder.applyStandardTemplateWithActions(
7395 mBuilder.getBigBaseLayoutResource());
Selim Cinek247fa012016-02-18 09:50:48 -08007396 buildIntoRemoteViewContent(remoteViews, bigContentView);
Selim Cinek593610c2016-02-16 18:42:57 -08007397 return remoteViews;
7398 }
Selim Cinek247fa012016-02-18 09:50:48 -08007399
7400 private void buildIntoRemoteViewContent(RemoteViews remoteViews,
7401 RemoteViews customContent) {
Adrian Roos5081c0d2016-02-26 16:04:19 -08007402 if (customContent != null) {
Selim Cinekf91017e2016-03-14 12:25:09 -07007403 // Need to clone customContent before adding, because otherwise it can no longer be
7404 // parceled independently of remoteViews.
Adrian Roos5081c0d2016-02-26 16:04:19 -08007405 customContent = customContent.clone();
Anthony Chen8f5f3582017-04-11 11:18:37 -07007406 remoteViews.removeAllViewsExceptId(R.id.notification_main_column, R.id.progress);
7407 remoteViews.addView(R.id.notification_main_column, customContent, 0 /* index */);
Selim Cinekfc8073c2017-08-16 17:50:20 -07007408 remoteViews.setReapplyDisallowed();
Adrian Roos5081c0d2016-02-26 16:04:19 -08007409 }
Selim Cinek247fa012016-02-18 09:50:48 -08007410 // also update the end margin if there is an image
Adrian Roos2d5dbba2016-06-08 17:11:53 -07007411 int endMargin = R.dimen.notification_content_margin_end;
Selim Cinek279fa862016-06-14 10:57:25 -07007412 if (mBuilder.mN.hasLargeIcon()) {
Adrian Roos2d5dbba2016-06-08 17:11:53 -07007413 endMargin = R.dimen.notification_content_plus_picture_margin_end;
Selim Cinek247fa012016-02-18 09:50:48 -08007414 }
Adrian Roos2d5dbba2016-06-08 17:11:53 -07007415 remoteViews.setViewLayoutMarginEndDimen(R.id.notification_main_column, endMargin);
Selim Cinek247fa012016-02-18 09:50:48 -08007416 }
Selim Cinek593610c2016-02-16 18:42:57 -08007417 }
7418
Selim Cinek03eb3b72016-02-18 10:39:45 -08007419 /**
7420 * Notification style for media custom views that are decorated by the system
7421 *
7422 * <p>Instead of providing a media notification that is completely custom, a developer can set
7423 * this style and still obtain system decorations like the notification header with the expand
7424 * affordance and actions.
7425 *
7426 * <p>Use {@link android.app.Notification.Builder#setCustomContentView(RemoteViews)},
7427 * {@link android.app.Notification.Builder#setCustomBigContentView(RemoteViews)} and
7428 * {@link android.app.Notification.Builder#setCustomHeadsUpContentView(RemoteViews)} to set the
7429 * corresponding custom views to display.
Selim Cinek99104832017-01-25 14:47:33 -08007430 * <p>
7431 * Contrary to {@link MediaStyle} a developer has to opt-in to the colorizing of the
7432 * notification by using {@link Notification.Builder#setColorized(boolean)}.
7433 * <p>
Selim Cinek03eb3b72016-02-18 10:39:45 -08007434 * To use this style with your Notification, feed it to
7435 * {@link Notification.Builder#setStyle(android.app.Notification.Style)} like so:
7436 * <pre class="prettyprint">
7437 * Notification noti = new Notification.Builder()
7438 * .setSmallIcon(R.drawable.ic_stat_player)
7439 * .setLargeIcon(albumArtBitmap))
7440 * .setCustomContentView(contentView);
7441 * .setStyle(<b>new Notification.DecoratedMediaCustomViewStyle()</b>
7442 * .setMediaSession(mySession))
7443 * .build();
7444 * </pre>
7445 *
7446 * @see android.app.Notification.DecoratedCustomViewStyle
7447 * @see android.app.Notification.MediaStyle
7448 */
7449 public static class DecoratedMediaCustomViewStyle extends MediaStyle {
7450
7451 public DecoratedMediaCustomViewStyle() {
7452 }
7453
Selim Cinek03eb3b72016-02-18 10:39:45 -08007454 /**
7455 * @hide
7456 */
7457 public boolean displayCustomViewInline() {
7458 return true;
7459 }
7460
7461 /**
7462 * @hide
7463 */
7464 @Override
Selim Cinek7d1009b2017-01-25 15:28:28 -08007465 public RemoteViews makeContentView(boolean increasedHeight) {
7466 RemoteViews remoteViews = super.makeContentView(false /* increasedHeight */);
Selim Cinek03eb3b72016-02-18 10:39:45 -08007467 return buildIntoRemoteView(remoteViews, R.id.notification_content_container,
7468 mBuilder.mN.contentView);
7469 }
7470
7471 /**
7472 * @hide
7473 */
7474 @Override
7475 public RemoteViews makeBigContentView() {
7476 RemoteViews customRemoteView = mBuilder.mN.bigContentView != null
7477 ? mBuilder.mN.bigContentView
7478 : mBuilder.mN.contentView;
7479 return makeBigContentViewWithCustomContent(customRemoteView);
7480 }
7481
7482 private RemoteViews makeBigContentViewWithCustomContent(RemoteViews customRemoteView) {
7483 RemoteViews remoteViews = super.makeBigContentView();
7484 if (remoteViews != null) {
7485 return buildIntoRemoteView(remoteViews, R.id.notification_main_column,
7486 customRemoteView);
7487 } else if (customRemoteView != mBuilder.mN.contentView){
Selim Cinek7d1009b2017-01-25 15:28:28 -08007488 remoteViews = super.makeContentView(false /* increasedHeight */);
Selim Cinek03eb3b72016-02-18 10:39:45 -08007489 return buildIntoRemoteView(remoteViews, R.id.notification_content_container,
7490 customRemoteView);
7491 } else {
7492 return null;
7493 }
7494 }
7495
7496 /**
7497 * @hide
7498 */
7499 @Override
Selim Cinek87ed69b2017-02-09 15:59:43 -08007500 public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
Selim Cinek03eb3b72016-02-18 10:39:45 -08007501 RemoteViews customRemoteView = mBuilder.mN.headsUpContentView != null
7502 ? mBuilder.mN.headsUpContentView
7503 : mBuilder.mN.contentView;
7504 return makeBigContentViewWithCustomContent(customRemoteView);
7505 }
7506
7507 private RemoteViews buildIntoRemoteView(RemoteViews remoteViews, int id,
7508 RemoteViews customContent) {
Adrian Roos5081c0d2016-02-26 16:04:19 -08007509 if (customContent != null) {
Selim Cinekf91017e2016-03-14 12:25:09 -07007510 // Need to clone customContent before adding, because otherwise it can no longer be
7511 // parceled independently of remoteViews.
Adrian Roos5081c0d2016-02-26 16:04:19 -08007512 customContent = customContent.clone();
Selim Cinek87c31532017-08-18 18:53:44 -07007513 customContent.overrideTextColors(mBuilder.getPrimaryTextColor());
Selim Cinekf91017e2016-03-14 12:25:09 -07007514 remoteViews.removeAllViews(id);
7515 remoteViews.addView(id, customContent);
Selim Cinekfc8073c2017-08-16 17:50:20 -07007516 remoteViews.setReapplyDisallowed();
Adrian Roos5081c0d2016-02-26 16:04:19 -08007517 }
Selim Cinek03eb3b72016-02-18 10:39:45 -08007518 return remoteViews;
7519 }
7520 }
7521
Selim Cineke7238dd2017-12-14 17:48:32 -08007522 /**
7523 * A Person associated with this Notification.
7524 */
7525 public static final class Person implements Parcelable {
7526 @Nullable private CharSequence mName;
7527 @Nullable private Icon mIcon;
7528 @Nullable private String mUri;
7529 @Nullable private String mKey;
7530
7531 protected Person(Parcel in) {
7532 mName = in.readCharSequence();
7533 if (in.readInt() != 0) {
7534 mIcon = Icon.CREATOR.createFromParcel(in);
7535 }
7536 mUri = in.readString();
7537 mKey = in.readString();
7538 }
7539
7540 /**
7541 * Create a new person.
7542 */
7543 public Person() {
7544 }
7545
7546 /**
7547 * Give this person a name.
7548 *
7549 * @param name the name of this person
7550 */
7551 public Person setName(@Nullable CharSequence name) {
7552 this.mName = name;
7553 return this;
7554 }
7555
7556 /**
7557 * Add an icon for this person.
7558 * <br />
7559 * This is currently only used for {@link MessagingStyle} notifications and should not be
7560 * provided otherwise, in order to save memory. The system will prefer this icon over any
7561 * images that are resolved from the URI.
7562 *
7563 * @param icon the icon of the person
7564 */
7565 public Person setIcon(@Nullable Icon icon) {
7566 this.mIcon = icon;
7567 return this;
7568 }
7569
7570 /**
7571 * Set a URI associated with this person.
7572 *
7573 * <P>
7574 * Depending on user preferences, adding a URI to a Person may allow the notification to
7575 * pass through interruption filters, if this notification is of
7576 * category {@link #CATEGORY_CALL} or {@link #CATEGORY_MESSAGE}.
7577 * The addition of people may also cause this notification to appear more prominently in
7578 * the user interface.
7579 * </P>
7580 *
7581 * <P>
7582 * The person should be specified by the {@code String} representation of a
7583 * {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI}.
7584 * </P>
7585 *
7586 * <P>The system will also attempt to resolve {@code mailto:} and {@code tel:} schema
7587 * URIs. The path part of these URIs must exist in the contacts database, in the
7588 * appropriate column, or the reference will be discarded as invalid. Telephone schema
7589 * URIs will be resolved by {@link android.provider.ContactsContract.PhoneLookup}.
7590 * </P>
7591 *
7592 * @param uri a URI for the person
7593 */
7594 public Person setUri(@Nullable String uri) {
7595 mUri = uri;
7596 return this;
7597 }
7598
7599 /**
7600 * Add a key to this person in order to uniquely identify it.
7601 * This is especially useful if the name doesn't uniquely identify this person or if the
7602 * display name is a short handle of the actual name.
7603 *
7604 * <P>If no key is provided, the name serves as as the key for the purpose of
7605 * identification.</P>
7606 *
7607 * @param key the key that uniquely identifies this person
7608 */
7609 public Person setKey(@Nullable String key) {
7610 mKey = key;
7611 return this;
7612 }
7613
7614
7615 /**
7616 * @return the uri provided for this person or {@code null} if no Uri was provided
7617 */
7618 @Nullable
7619 public String getUri() {
7620 return mUri;
7621 }
7622
7623 /**
7624 * @return the name provided for this person or {@code null} if no name was provided
7625 */
7626 @Nullable
7627 public CharSequence getName() {
7628 return mName;
7629 }
7630
7631 /**
7632 * @return the icon provided for this person or {@code null} if no icon was provided
7633 */
7634 @Nullable
7635 public Icon getIcon() {
7636 return mIcon;
7637 }
7638
7639 /**
7640 * @return the key provided for this person or {@code null} if no key was provided
7641 */
7642 @Nullable
7643 public String getKey() {
7644 return mKey;
7645 }
7646
7647 /**
7648 * @return the URI associated with this person, or "name:mName" otherwise
7649 * @hide
7650 */
7651 public String resolveToLegacyUri() {
7652 if (mUri != null) {
7653 return mUri;
7654 }
7655 if (mName != null) {
7656 return "name:" + mName;
7657 }
7658 return "";
7659 }
7660
7661 @Override
7662 public int describeContents() {
7663 return 0;
7664 }
7665
7666 @Override
7667 public void writeToParcel(Parcel dest, @WriteFlags int flags) {
7668 dest.writeCharSequence(mName);
7669 if (mIcon != null) {
7670 dest.writeInt(1);
7671 mIcon.writeToParcel(dest, 0);
7672 } else {
7673 dest.writeInt(0);
7674 }
7675 dest.writeString(mUri);
7676 dest.writeString(mKey);
7677 }
7678
7679 public static final Creator<Person> CREATOR = new Creator<Person>() {
7680 @Override
7681 public Person createFromParcel(Parcel in) {
7682 return new Person(in);
7683 }
7684
7685 @Override
7686 public Person[] newArray(int size) {
7687 return new Person[size];
7688 }
7689 };
7690 }
7691
Christoph Studer4600f9b2014-07-22 22:44:43 +02007692 // When adding a new Style subclass here, don't forget to update
7693 // Builder.getNotificationStyleClass.
7694
Griff Hazen61a9e862014-05-22 16:05:19 -07007695 /**
7696 * Extender interface for use with {@link Builder#extend}. Extenders may be used to add
7697 * metadata or change options on a notification builder.
7698 */
7699 public interface Extender {
7700 /**
7701 * Apply this extender to a notification builder.
7702 * @param builder the builder to be modified.
7703 * @return the build object for chaining.
7704 */
7705 public Builder extend(Builder builder);
7706 }
7707
7708 /**
7709 * Helper class to add wearable extensions to notifications.
7710 * <p class="note"> See
7711 * <a href="{@docRoot}wear/notifications/creating.html">Creating Notifications
7712 * for Android Wear</a> for more information on how to use this class.
7713 * <p>
7714 * To create a notification with wearable extensions:
7715 * <ol>
7716 * <li>Create a {@link android.app.Notification.Builder}, setting any desired
7717 * properties.
7718 * <li>Create a {@link android.app.Notification.WearableExtender}.
7719 * <li>Set wearable-specific properties using the
7720 * {@code add} and {@code set} methods of {@link android.app.Notification.WearableExtender}.
7721 * <li>Call {@link android.app.Notification.Builder#extend} to apply the extensions to a
7722 * notification.
7723 * <li>Post the notification to the notification system with the
7724 * {@code NotificationManager.notify(...)} methods.
7725 * </ol>
7726 *
7727 * <pre class="prettyprint">
7728 * Notification notif = new Notification.Builder(mContext)
7729 * .setContentTitle(&quot;New mail from &quot; + sender.toString())
7730 * .setContentText(subject)
7731 * .setSmallIcon(R.drawable.new_mail)
7732 * .extend(new Notification.WearableExtender()
7733 * .setContentIcon(R.drawable.new_mail))
7734 * .build();
7735 * NotificationManager notificationManger =
7736 * (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
7737 * notificationManger.notify(0, notif);</pre>
7738 *
7739 * <p>Wearable extensions can be accessed on an existing notification by using the
7740 * {@code WearableExtender(Notification)} constructor,
7741 * and then using the {@code get} methods to access values.
7742 *
7743 * <pre class="prettyprint">
7744 * Notification.WearableExtender wearableExtender = new Notification.WearableExtender(
7745 * notification);
Griff Hazen14f57992014-05-26 09:07:14 -07007746 * List&lt;Notification&gt; pages = wearableExtender.getPages();</pre>
Griff Hazen61a9e862014-05-22 16:05:19 -07007747 */
7748 public static final class WearableExtender implements Extender {
7749 /**
7750 * Sentinel value for an action index that is unset.
7751 */
7752 public static final int UNSET_ACTION_INDEX = -1;
7753
7754 /**
7755 * Size value for use with {@link #setCustomSizePreset} to show this notification with
7756 * default sizing.
7757 * <p>For custom display notifications created using {@link #setDisplayIntent},
Paul Soulosaa4f4bf2015-08-04 11:59:45 -07007758 * the default is {@link #SIZE_MEDIUM}. All other notifications size automatically based
Griff Hazen61a9e862014-05-22 16:05:19 -07007759 * on their content.
7760 */
7761 public static final int SIZE_DEFAULT = 0;
7762
7763 /**
7764 * Size value for use with {@link #setCustomSizePreset} to show this notification
7765 * with an extra small size.
7766 * <p>This value is only applicable for custom display notifications created using
7767 * {@link #setDisplayIntent}.
7768 */
7769 public static final int SIZE_XSMALL = 1;
7770
7771 /**
7772 * Size value for use with {@link #setCustomSizePreset} to show this notification
7773 * with a small size.
7774 * <p>This value is only applicable for custom display notifications created using
7775 * {@link #setDisplayIntent}.
7776 */
7777 public static final int SIZE_SMALL = 2;
7778
7779 /**
7780 * Size value for use with {@link #setCustomSizePreset} to show this notification
7781 * with a medium size.
7782 * <p>This value is only applicable for custom display notifications created using
7783 * {@link #setDisplayIntent}.
7784 */
7785 public static final int SIZE_MEDIUM = 3;
7786
7787 /**
7788 * Size value for use with {@link #setCustomSizePreset} to show this notification
7789 * with a large size.
7790 * <p>This value is only applicable for custom display notifications created using
7791 * {@link #setDisplayIntent}.
7792 */
7793 public static final int SIZE_LARGE = 4;
7794
Griff Hazend5f11f92014-05-27 15:40:09 -07007795 /**
7796 * Size value for use with {@link #setCustomSizePreset} to show this notification
7797 * full screen.
7798 * <p>This value is only applicable for custom display notifications created using
7799 * {@link #setDisplayIntent}.
7800 */
7801 public static final int SIZE_FULL_SCREEN = 5;
7802
Griff Hazen5f2edfc2014-09-29 16:28:44 -07007803 /**
7804 * Sentinel value for use with {@link #setHintScreenTimeout} to keep the screen on for a
7805 * short amount of time when this notification is displayed on the screen. This
7806 * is the default value.
7807 */
7808 public static final int SCREEN_TIMEOUT_SHORT = 0;
7809
7810 /**
7811 * Sentinel value for use with {@link #setHintScreenTimeout} to keep the screen on
7812 * for a longer amount of time when this notification is displayed on the screen.
7813 */
7814 public static final int SCREEN_TIMEOUT_LONG = -1;
7815
Griff Hazen61a9e862014-05-22 16:05:19 -07007816 /** Notification extra which contains wearable extensions */
7817 private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS";
7818
Pete Gastaf6781d2014-10-07 15:17:05 -04007819 // Keys within EXTRA_WEARABLE_EXTENSIONS for wearable options.
Griff Hazen61a9e862014-05-22 16:05:19 -07007820 private static final String KEY_ACTIONS = "actions";
7821 private static final String KEY_FLAGS = "flags";
7822 private static final String KEY_DISPLAY_INTENT = "displayIntent";
7823 private static final String KEY_PAGES = "pages";
7824 private static final String KEY_BACKGROUND = "background";
7825 private static final String KEY_CONTENT_ICON = "contentIcon";
7826 private static final String KEY_CONTENT_ICON_GRAVITY = "contentIconGravity";
7827 private static final String KEY_CONTENT_ACTION_INDEX = "contentActionIndex";
7828 private static final String KEY_CUSTOM_SIZE_PRESET = "customSizePreset";
7829 private static final String KEY_CUSTOM_CONTENT_HEIGHT = "customContentHeight";
7830 private static final String KEY_GRAVITY = "gravity";
Griff Hazen5f2edfc2014-09-29 16:28:44 -07007831 private static final String KEY_HINT_SCREEN_TIMEOUT = "hintScreenTimeout";
Nadia Benbernou948627e2016-04-14 14:41:08 -04007832 private static final String KEY_DISMISSAL_ID = "dismissalId";
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04007833 private static final String KEY_BRIDGE_TAG = "bridgeTag";
Griff Hazen61a9e862014-05-22 16:05:19 -07007834
7835 // Flags bitwise-ored to mFlags
7836 private static final int FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE = 0x1;
7837 private static final int FLAG_HINT_HIDE_ICON = 1 << 1;
7838 private static final int FLAG_HINT_SHOW_BACKGROUND_ONLY = 1 << 2;
7839 private static final int FLAG_START_SCROLL_BOTTOM = 1 << 3;
Griff Hazen5f2edfc2014-09-29 16:28:44 -07007840 private static final int FLAG_HINT_AVOID_BACKGROUND_CLIPPING = 1 << 4;
Alex Hills4bcb06b2016-04-05 14:26:25 -04007841 private static final int FLAG_BIG_PICTURE_AMBIENT = 1 << 5;
Alex Hills9ab3a232016-04-05 14:54:56 -04007842 private static final int FLAG_HINT_CONTENT_INTENT_LAUNCHES_ACTIVITY = 1 << 6;
Griff Hazen61a9e862014-05-22 16:05:19 -07007843
7844 // Default value for flags integer
7845 private static final int DEFAULT_FLAGS = FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE;
7846
7847 private static final int DEFAULT_CONTENT_ICON_GRAVITY = Gravity.END;
7848 private static final int DEFAULT_GRAVITY = Gravity.BOTTOM;
7849
7850 private ArrayList<Action> mActions = new ArrayList<Action>();
7851 private int mFlags = DEFAULT_FLAGS;
7852 private PendingIntent mDisplayIntent;
7853 private ArrayList<Notification> mPages = new ArrayList<Notification>();
7854 private Bitmap mBackground;
7855 private int mContentIcon;
7856 private int mContentIconGravity = DEFAULT_CONTENT_ICON_GRAVITY;
7857 private int mContentActionIndex = UNSET_ACTION_INDEX;
7858 private int mCustomSizePreset = SIZE_DEFAULT;
7859 private int mCustomContentHeight;
7860 private int mGravity = DEFAULT_GRAVITY;
Griff Hazen5f2edfc2014-09-29 16:28:44 -07007861 private int mHintScreenTimeout;
Nadia Benbernou948627e2016-04-14 14:41:08 -04007862 private String mDismissalId;
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04007863 private String mBridgeTag;
Griff Hazen61a9e862014-05-22 16:05:19 -07007864
7865 /**
7866 * Create a {@link android.app.Notification.WearableExtender} with default
7867 * options.
7868 */
7869 public WearableExtender() {
7870 }
7871
7872 public WearableExtender(Notification notif) {
7873 Bundle wearableBundle = notif.extras.getBundle(EXTRA_WEARABLE_EXTENSIONS);
7874 if (wearableBundle != null) {
7875 List<Action> actions = wearableBundle.getParcelableArrayList(KEY_ACTIONS);
7876 if (actions != null) {
7877 mActions.addAll(actions);
7878 }
7879
7880 mFlags = wearableBundle.getInt(KEY_FLAGS, DEFAULT_FLAGS);
7881 mDisplayIntent = wearableBundle.getParcelable(KEY_DISPLAY_INTENT);
7882
7883 Notification[] pages = getNotificationArrayFromBundle(
7884 wearableBundle, KEY_PAGES);
7885 if (pages != null) {
7886 Collections.addAll(mPages, pages);
7887 }
7888
7889 mBackground = wearableBundle.getParcelable(KEY_BACKGROUND);
7890 mContentIcon = wearableBundle.getInt(KEY_CONTENT_ICON);
7891 mContentIconGravity = wearableBundle.getInt(KEY_CONTENT_ICON_GRAVITY,
7892 DEFAULT_CONTENT_ICON_GRAVITY);
7893 mContentActionIndex = wearableBundle.getInt(KEY_CONTENT_ACTION_INDEX,
7894 UNSET_ACTION_INDEX);
7895 mCustomSizePreset = wearableBundle.getInt(KEY_CUSTOM_SIZE_PRESET,
7896 SIZE_DEFAULT);
7897 mCustomContentHeight = wearableBundle.getInt(KEY_CUSTOM_CONTENT_HEIGHT);
7898 mGravity = wearableBundle.getInt(KEY_GRAVITY, DEFAULT_GRAVITY);
Griff Hazen5f2edfc2014-09-29 16:28:44 -07007899 mHintScreenTimeout = wearableBundle.getInt(KEY_HINT_SCREEN_TIMEOUT);
Nadia Benbernou948627e2016-04-14 14:41:08 -04007900 mDismissalId = wearableBundle.getString(KEY_DISMISSAL_ID);
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04007901 mBridgeTag = wearableBundle.getString(KEY_BRIDGE_TAG);
Griff Hazen61a9e862014-05-22 16:05:19 -07007902 }
7903 }
7904
7905 /**
7906 * Apply wearable extensions to a notification that is being built. This is typically
7907 * called by the {@link android.app.Notification.Builder#extend} method of
7908 * {@link android.app.Notification.Builder}.
7909 */
7910 @Override
7911 public Notification.Builder extend(Notification.Builder builder) {
7912 Bundle wearableBundle = new Bundle();
7913
7914 if (!mActions.isEmpty()) {
7915 wearableBundle.putParcelableArrayList(KEY_ACTIONS, mActions);
7916 }
7917 if (mFlags != DEFAULT_FLAGS) {
7918 wearableBundle.putInt(KEY_FLAGS, mFlags);
7919 }
7920 if (mDisplayIntent != null) {
7921 wearableBundle.putParcelable(KEY_DISPLAY_INTENT, mDisplayIntent);
7922 }
7923 if (!mPages.isEmpty()) {
7924 wearableBundle.putParcelableArray(KEY_PAGES, mPages.toArray(
7925 new Notification[mPages.size()]));
7926 }
7927 if (mBackground != null) {
7928 wearableBundle.putParcelable(KEY_BACKGROUND, mBackground);
7929 }
7930 if (mContentIcon != 0) {
7931 wearableBundle.putInt(KEY_CONTENT_ICON, mContentIcon);
7932 }
7933 if (mContentIconGravity != DEFAULT_CONTENT_ICON_GRAVITY) {
7934 wearableBundle.putInt(KEY_CONTENT_ICON_GRAVITY, mContentIconGravity);
7935 }
7936 if (mContentActionIndex != UNSET_ACTION_INDEX) {
7937 wearableBundle.putInt(KEY_CONTENT_ACTION_INDEX,
7938 mContentActionIndex);
7939 }
7940 if (mCustomSizePreset != SIZE_DEFAULT) {
7941 wearableBundle.putInt(KEY_CUSTOM_SIZE_PRESET, mCustomSizePreset);
7942 }
7943 if (mCustomContentHeight != 0) {
7944 wearableBundle.putInt(KEY_CUSTOM_CONTENT_HEIGHT, mCustomContentHeight);
7945 }
7946 if (mGravity != DEFAULT_GRAVITY) {
7947 wearableBundle.putInt(KEY_GRAVITY, mGravity);
7948 }
Griff Hazen5f2edfc2014-09-29 16:28:44 -07007949 if (mHintScreenTimeout != 0) {
7950 wearableBundle.putInt(KEY_HINT_SCREEN_TIMEOUT, mHintScreenTimeout);
7951 }
Nadia Benbernou948627e2016-04-14 14:41:08 -04007952 if (mDismissalId != null) {
7953 wearableBundle.putString(KEY_DISMISSAL_ID, mDismissalId);
7954 }
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04007955 if (mBridgeTag != null) {
7956 wearableBundle.putString(KEY_BRIDGE_TAG, mBridgeTag);
7957 }
Griff Hazen61a9e862014-05-22 16:05:19 -07007958
7959 builder.getExtras().putBundle(EXTRA_WEARABLE_EXTENSIONS, wearableBundle);
7960 return builder;
7961 }
7962
7963 @Override
7964 public WearableExtender clone() {
7965 WearableExtender that = new WearableExtender();
7966 that.mActions = new ArrayList<Action>(this.mActions);
7967 that.mFlags = this.mFlags;
7968 that.mDisplayIntent = this.mDisplayIntent;
7969 that.mPages = new ArrayList<Notification>(this.mPages);
7970 that.mBackground = this.mBackground;
7971 that.mContentIcon = this.mContentIcon;
7972 that.mContentIconGravity = this.mContentIconGravity;
7973 that.mContentActionIndex = this.mContentActionIndex;
7974 that.mCustomSizePreset = this.mCustomSizePreset;
7975 that.mCustomContentHeight = this.mCustomContentHeight;
7976 that.mGravity = this.mGravity;
Griff Hazen5f2edfc2014-09-29 16:28:44 -07007977 that.mHintScreenTimeout = this.mHintScreenTimeout;
Nadia Benbernou948627e2016-04-14 14:41:08 -04007978 that.mDismissalId = this.mDismissalId;
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04007979 that.mBridgeTag = this.mBridgeTag;
Griff Hazen61a9e862014-05-22 16:05:19 -07007980 return that;
7981 }
7982
7983 /**
7984 * Add a wearable action to this notification.
7985 *
7986 * <p>When wearable actions are added using this method, the set of actions that
7987 * show on a wearable device splits from devices that only show actions added
7988 * using {@link android.app.Notification.Builder#addAction}. This allows for customization
7989 * of which actions display on different devices.
7990 *
7991 * @param action the action to add to this notification
7992 * @return this object for method chaining
7993 * @see android.app.Notification.Action
7994 */
7995 public WearableExtender addAction(Action action) {
7996 mActions.add(action);
7997 return this;
7998 }
7999
8000 /**
8001 * Adds wearable actions to this notification.
8002 *
8003 * <p>When wearable actions are added using this method, the set of actions that
8004 * show on a wearable device splits from devices that only show actions added
8005 * using {@link android.app.Notification.Builder#addAction}. This allows for customization
8006 * of which actions display on different devices.
8007 *
8008 * @param actions the actions to add to this notification
8009 * @return this object for method chaining
8010 * @see android.app.Notification.Action
8011 */
8012 public WearableExtender addActions(List<Action> actions) {
8013 mActions.addAll(actions);
8014 return this;
8015 }
8016
8017 /**
8018 * Clear all wearable actions present on this builder.
8019 * @return this object for method chaining.
8020 * @see #addAction
8021 */
8022 public WearableExtender clearActions() {
8023 mActions.clear();
8024 return this;
8025 }
8026
8027 /**
8028 * Get the wearable actions present on this notification.
8029 */
8030 public List<Action> getActions() {
8031 return mActions;
8032 }
8033
8034 /**
8035 * Set an intent to launch inside of an activity view when displaying
Griff Hazen14f57992014-05-26 09:07:14 -07008036 * this notification. The {@link PendingIntent} provided should be for an activity.
8037 *
8038 * <pre class="prettyprint">
8039 * Intent displayIntent = new Intent(context, MyDisplayActivity.class);
8040 * PendingIntent displayPendingIntent = PendingIntent.getActivity(context,
8041 * 0, displayIntent, PendingIntent.FLAG_UPDATE_CURRENT);
8042 * Notification notif = new Notification.Builder(context)
8043 * .extend(new Notification.WearableExtender()
8044 * .setDisplayIntent(displayPendingIntent)
8045 * .setCustomSizePreset(Notification.WearableExtender.SIZE_MEDIUM))
8046 * .build();</pre>
8047 *
8048 * <p>The activity to launch needs to allow embedding, must be exported, and
Griff Hazen831ca9d2014-06-17 00:38:38 -07008049 * should have an empty task affinity. It is also recommended to use the device
8050 * default light theme.
Griff Hazen14f57992014-05-26 09:07:14 -07008051 *
8052 * <p>Example AndroidManifest.xml entry:
8053 * <pre class="prettyprint">
8054 * &lt;activity android:name=&quot;com.example.MyDisplayActivity&quot;
8055 * android:exported=&quot;true&quot;
8056 * android:allowEmbedded=&quot;true&quot;
Griff Hazen831ca9d2014-06-17 00:38:38 -07008057 * android:taskAffinity=&quot;&quot;
8058 * android:theme=&quot;@android:style/Theme.DeviceDefault.Light&quot; /&gt;</pre>
Griff Hazen61a9e862014-05-22 16:05:19 -07008059 *
8060 * @param intent the {@link PendingIntent} for an activity
8061 * @return this object for method chaining
8062 * @see android.app.Notification.WearableExtender#getDisplayIntent
8063 */
8064 public WearableExtender setDisplayIntent(PendingIntent intent) {
8065 mDisplayIntent = intent;
8066 return this;
8067 }
8068
8069 /**
8070 * Get the intent to launch inside of an activity view when displaying this
8071 * notification. This {@code PendingIntent} should be for an activity.
8072 */
8073 public PendingIntent getDisplayIntent() {
8074 return mDisplayIntent;
8075 }
8076
8077 /**
8078 * Add an additional page of content to display with this notification. The current
8079 * notification forms the first page, and pages added using this function form
8080 * subsequent pages. This field can be used to separate a notification into multiple
8081 * sections.
8082 *
8083 * @param page the notification to add as another page
8084 * @return this object for method chaining
8085 * @see android.app.Notification.WearableExtender#getPages
8086 */
8087 public WearableExtender addPage(Notification page) {
8088 mPages.add(page);
8089 return this;
8090 }
8091
8092 /**
8093 * Add additional pages of content to display with this notification. The current
8094 * notification forms the first page, and pages added using this function form
8095 * subsequent pages. This field can be used to separate a notification into multiple
8096 * sections.
8097 *
8098 * @param pages a list of notifications
8099 * @return this object for method chaining
8100 * @see android.app.Notification.WearableExtender#getPages
8101 */
8102 public WearableExtender addPages(List<Notification> pages) {
8103 mPages.addAll(pages);
8104 return this;
8105 }
8106
8107 /**
8108 * Clear all additional pages present on this builder.
8109 * @return this object for method chaining.
8110 * @see #addPage
8111 */
8112 public WearableExtender clearPages() {
8113 mPages.clear();
8114 return this;
8115 }
8116
8117 /**
8118 * Get the array of additional pages of content for displaying this notification. The
8119 * current notification forms the first page, and elements within this array form
8120 * subsequent pages. This field can be used to separate a notification into multiple
8121 * sections.
8122 * @return the pages for this notification
8123 */
8124 public List<Notification> getPages() {
8125 return mPages;
8126 }
8127
8128 /**
8129 * Set a background image to be displayed behind the notification content.
8130 * Contrary to the {@link android.app.Notification.BigPictureStyle}, this background
8131 * will work with any notification style.
8132 *
8133 * @param background the background bitmap
8134 * @return this object for method chaining
8135 * @see android.app.Notification.WearableExtender#getBackground
8136 */
8137 public WearableExtender setBackground(Bitmap background) {
8138 mBackground = background;
8139 return this;
8140 }
8141
8142 /**
8143 * Get a background image to be displayed behind the notification content.
8144 * Contrary to the {@link android.app.Notification.BigPictureStyle}, this background
8145 * will work with any notification style.
8146 *
8147 * @return the background image
8148 * @see android.app.Notification.WearableExtender#setBackground
8149 */
8150 public Bitmap getBackground() {
8151 return mBackground;
8152 }
8153
8154 /**
8155 * Set an icon that goes with the content of this notification.
8156 */
Gus Prevasb5f9cf52018-02-21 14:21:50 -05008157 @Deprecated
Griff Hazen61a9e862014-05-22 16:05:19 -07008158 public WearableExtender setContentIcon(int icon) {
8159 mContentIcon = icon;
8160 return this;
8161 }
8162
8163 /**
8164 * Get an icon that goes with the content of this notification.
8165 */
Gus Prevasb5f9cf52018-02-21 14:21:50 -05008166 @Deprecated
Griff Hazen61a9e862014-05-22 16:05:19 -07008167 public int getContentIcon() {
8168 return mContentIcon;
8169 }
8170
8171 /**
8172 * Set the gravity that the content icon should have within the notification display.
8173 * Supported values include {@link android.view.Gravity#START} and
8174 * {@link android.view.Gravity#END}. The default value is {@link android.view.Gravity#END}.
8175 * @see #setContentIcon
8176 */
Gus Prevasb5f9cf52018-02-21 14:21:50 -05008177 @Deprecated
Griff Hazen61a9e862014-05-22 16:05:19 -07008178 public WearableExtender setContentIconGravity(int contentIconGravity) {
8179 mContentIconGravity = contentIconGravity;
8180 return this;
8181 }
8182
8183 /**
8184 * Get the gravity that the content icon should have within the notification display.
8185 * Supported values include {@link android.view.Gravity#START} and
8186 * {@link android.view.Gravity#END}. The default value is {@link android.view.Gravity#END}.
8187 * @see #getContentIcon
8188 */
Gus Prevasb5f9cf52018-02-21 14:21:50 -05008189 @Deprecated
Griff Hazen61a9e862014-05-22 16:05:19 -07008190 public int getContentIconGravity() {
8191 return mContentIconGravity;
8192 }
8193
8194 /**
8195 * Set an action from this notification's actions to be clickable with the content of
Griff Hazen14f57992014-05-26 09:07:14 -07008196 * this notification. This action will no longer display separately from the
8197 * notification's content.
8198 *
Griff Hazenca48d352014-05-28 22:37:13 -07008199 * <p>For notifications with multiple pages, child pages can also have content actions
Griff Hazen14f57992014-05-26 09:07:14 -07008200 * set, although the list of available actions comes from the main notification and not
8201 * from the child page's notification.
8202 *
8203 * @param actionIndex The index of the action to hoist onto the current notification page.
8204 * If wearable actions were added to the main notification, this index
8205 * will apply to that list, otherwise it will apply to the regular
8206 * actions list.
Griff Hazen61a9e862014-05-22 16:05:19 -07008207 */
8208 public WearableExtender setContentAction(int actionIndex) {
8209 mContentActionIndex = actionIndex;
8210 return this;
8211 }
8212
8213 /**
Griff Hazenca48d352014-05-28 22:37:13 -07008214 * Get the index of the notification action, if any, that was specified as being clickable
8215 * with the content of this notification. This action will no longer display separately
Griff Hazen14f57992014-05-26 09:07:14 -07008216 * from the notification's content.
Griff Hazen61a9e862014-05-22 16:05:19 -07008217 *
Griff Hazenca48d352014-05-28 22:37:13 -07008218 * <p>For notifications with multiple pages, child pages can also have content actions
Griff Hazen14f57992014-05-26 09:07:14 -07008219 * set, although the list of available actions comes from the main notification and not
8220 * from the child page's notification.
8221 *
8222 * <p>If wearable specific actions were added to the main notification, this index will
8223 * apply to that list, otherwise it will apply to the regular actions list.
Griff Hazenca48d352014-05-28 22:37:13 -07008224 *
8225 * @return the action index or {@link #UNSET_ACTION_INDEX} if no action was selected.
Griff Hazen61a9e862014-05-22 16:05:19 -07008226 */
8227 public int getContentAction() {
8228 return mContentActionIndex;
8229 }
8230
8231 /**
8232 * Set the gravity that this notification should have within the available viewport space.
8233 * Supported values include {@link android.view.Gravity#TOP},
8234 * {@link android.view.Gravity#CENTER_VERTICAL} and {@link android.view.Gravity#BOTTOM}.
8235 * The default value is {@link android.view.Gravity#BOTTOM}.
8236 */
Gus Prevasb5f9cf52018-02-21 14:21:50 -05008237 @Deprecated
Griff Hazen61a9e862014-05-22 16:05:19 -07008238 public WearableExtender setGravity(int gravity) {
8239 mGravity = gravity;
8240 return this;
8241 }
8242
8243 /**
8244 * Get the gravity that this notification should have within the available viewport space.
8245 * Supported values include {@link android.view.Gravity#TOP},
8246 * {@link android.view.Gravity#CENTER_VERTICAL} and {@link android.view.Gravity#BOTTOM}.
8247 * The default value is {@link android.view.Gravity#BOTTOM}.
8248 */
Gus Prevasb5f9cf52018-02-21 14:21:50 -05008249 @Deprecated
Griff Hazen61a9e862014-05-22 16:05:19 -07008250 public int getGravity() {
8251 return mGravity;
8252 }
8253
8254 /**
8255 * Set the custom size preset for the display of this notification out of the available
8256 * presets found in {@link android.app.Notification.WearableExtender}, e.g.
8257 * {@link #SIZE_LARGE}.
8258 * <p>Some custom size presets are only applicable for custom display notifications created
8259 * using {@link android.app.Notification.WearableExtender#setDisplayIntent}. Check the
8260 * documentation for the preset in question. See also
8261 * {@link #setCustomContentHeight} and {@link #getCustomSizePreset}.
8262 */
Gus Prevasb5f9cf52018-02-21 14:21:50 -05008263 @Deprecated
Griff Hazen61a9e862014-05-22 16:05:19 -07008264 public WearableExtender setCustomSizePreset(int sizePreset) {
8265 mCustomSizePreset = sizePreset;
8266 return this;
8267 }
8268
8269 /**
8270 * Get the custom size preset for the display of this notification out of the available
8271 * presets found in {@link android.app.Notification.WearableExtender}, e.g.
8272 * {@link #SIZE_LARGE}.
8273 * <p>Some custom size presets are only applicable for custom display notifications created
8274 * using {@link #setDisplayIntent}. Check the documentation for the preset in question.
8275 * See also {@link #setCustomContentHeight} and {@link #setCustomSizePreset}.
8276 */
Gus Prevasb5f9cf52018-02-21 14:21:50 -05008277 @Deprecated
Griff Hazen61a9e862014-05-22 16:05:19 -07008278 public int getCustomSizePreset() {
8279 return mCustomSizePreset;
8280 }
8281
8282 /**
8283 * Set the custom height in pixels for the display of this notification's content.
8284 * <p>This option is only available for custom display notifications created
8285 * using {@link android.app.Notification.WearableExtender#setDisplayIntent}. See also
8286 * {@link android.app.Notification.WearableExtender#setCustomSizePreset} and
8287 * {@link #getCustomContentHeight}.
8288 */
Gus Prevasb5f9cf52018-02-21 14:21:50 -05008289 @Deprecated
Griff Hazen61a9e862014-05-22 16:05:19 -07008290 public WearableExtender setCustomContentHeight(int height) {
8291 mCustomContentHeight = height;
8292 return this;
8293 }
8294
8295 /**
8296 * Get the custom height in pixels for the display of this notification's content.
8297 * <p>This option is only available for custom display notifications created
8298 * using {@link #setDisplayIntent}. See also {@link #setCustomSizePreset} and
8299 * {@link #setCustomContentHeight}.
8300 */
Gus Prevasb5f9cf52018-02-21 14:21:50 -05008301 @Deprecated
Griff Hazen61a9e862014-05-22 16:05:19 -07008302 public int getCustomContentHeight() {
8303 return mCustomContentHeight;
8304 }
8305
8306 /**
8307 * Set whether the scrolling position for the contents of this notification should start
8308 * at the bottom of the contents instead of the top when the contents are too long to
8309 * display within the screen. Default is false (start scroll at the top).
8310 */
8311 public WearableExtender setStartScrollBottom(boolean startScrollBottom) {
8312 setFlag(FLAG_START_SCROLL_BOTTOM, startScrollBottom);
8313 return this;
8314 }
8315
8316 /**
8317 * Get whether the scrolling position for the contents of this notification should start
8318 * at the bottom of the contents instead of the top when the contents are too long to
8319 * display within the screen. Default is false (start scroll at the top).
8320 */
8321 public boolean getStartScrollBottom() {
8322 return (mFlags & FLAG_START_SCROLL_BOTTOM) != 0;
8323 }
8324
8325 /**
8326 * Set whether the content intent is available when the wearable device is not connected
8327 * to a companion device. The user can still trigger this intent when the wearable device
8328 * is offline, but a visual hint will indicate that the content intent may not be available.
8329 * Defaults to true.
8330 */
8331 public WearableExtender setContentIntentAvailableOffline(
8332 boolean contentIntentAvailableOffline) {
8333 setFlag(FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE, contentIntentAvailableOffline);
8334 return this;
8335 }
8336
8337 /**
8338 * Get whether the content intent is available when the wearable device is not connected
8339 * to a companion device. The user can still trigger this intent when the wearable device
8340 * is offline, but a visual hint will indicate that the content intent may not be available.
8341 * Defaults to true.
8342 */
8343 public boolean getContentIntentAvailableOffline() {
8344 return (mFlags & FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE) != 0;
8345 }
8346
8347 /**
8348 * Set a hint that this notification's icon should not be displayed.
8349 * @param hintHideIcon {@code true} to hide the icon, {@code false} otherwise.
8350 * @return this object for method chaining
8351 */
Gus Prevasb5f9cf52018-02-21 14:21:50 -05008352 @Deprecated
Griff Hazen61a9e862014-05-22 16:05:19 -07008353 public WearableExtender setHintHideIcon(boolean hintHideIcon) {
8354 setFlag(FLAG_HINT_HIDE_ICON, hintHideIcon);
8355 return this;
8356 }
8357
8358 /**
8359 * Get a hint that this notification's icon should not be displayed.
8360 * @return {@code true} if this icon should not be displayed, false otherwise.
8361 * The default value is {@code false} if this was never set.
8362 */
Gus Prevasb5f9cf52018-02-21 14:21:50 -05008363 @Deprecated
Griff Hazen61a9e862014-05-22 16:05:19 -07008364 public boolean getHintHideIcon() {
8365 return (mFlags & FLAG_HINT_HIDE_ICON) != 0;
8366 }
8367
8368 /**
8369 * Set a visual hint that only the background image of this notification should be
8370 * displayed, and other semantic content should be hidden. This hint is only applicable
8371 * to sub-pages added using {@link #addPage}.
8372 */
Gus Prevasb5f9cf52018-02-21 14:21:50 -05008373 @Deprecated
Griff Hazen61a9e862014-05-22 16:05:19 -07008374 public WearableExtender setHintShowBackgroundOnly(boolean hintShowBackgroundOnly) {
8375 setFlag(FLAG_HINT_SHOW_BACKGROUND_ONLY, hintShowBackgroundOnly);
8376 return this;
8377 }
8378
8379 /**
8380 * Get a visual hint that only the background image of this notification should be
8381 * displayed, and other semantic content should be hidden. This hint is only applicable
8382 * to sub-pages added using {@link android.app.Notification.WearableExtender#addPage}.
8383 */
Gus Prevasb5f9cf52018-02-21 14:21:50 -05008384 @Deprecated
Griff Hazen61a9e862014-05-22 16:05:19 -07008385 public boolean getHintShowBackgroundOnly() {
8386 return (mFlags & FLAG_HINT_SHOW_BACKGROUND_ONLY) != 0;
8387 }
8388
Griff Hazen5f2edfc2014-09-29 16:28:44 -07008389 /**
Griff Hazen9c5be4e2014-11-17 17:47:50 -08008390 * Set a hint that this notification's background should not be clipped if possible,
8391 * and should instead be resized to fully display on the screen, retaining the aspect
8392 * ratio of the image. This can be useful for images like barcodes or qr codes.
Griff Hazen5f2edfc2014-09-29 16:28:44 -07008393 * @param hintAvoidBackgroundClipping {@code true} to avoid clipping if possible.
8394 * @return this object for method chaining
8395 */
Gus Prevasb5f9cf52018-02-21 14:21:50 -05008396 @Deprecated
Griff Hazen5f2edfc2014-09-29 16:28:44 -07008397 public WearableExtender setHintAvoidBackgroundClipping(
8398 boolean hintAvoidBackgroundClipping) {
8399 setFlag(FLAG_HINT_AVOID_BACKGROUND_CLIPPING, hintAvoidBackgroundClipping);
8400 return this;
8401 }
8402
8403 /**
Griff Hazen9c5be4e2014-11-17 17:47:50 -08008404 * Get a hint that this notification's background should not be clipped if possible,
8405 * and should instead be resized to fully display on the screen, retaining the aspect
8406 * ratio of the image. This can be useful for images like barcodes or qr codes.
Griff Hazen5f2edfc2014-09-29 16:28:44 -07008407 * @return {@code true} if it's ok if the background is clipped on the screen, false
8408 * otherwise. The default value is {@code false} if this was never set.
8409 */
Gus Prevasb5f9cf52018-02-21 14:21:50 -05008410 @Deprecated
Griff Hazen5f2edfc2014-09-29 16:28:44 -07008411 public boolean getHintAvoidBackgroundClipping() {
8412 return (mFlags & FLAG_HINT_AVOID_BACKGROUND_CLIPPING) != 0;
8413 }
8414
8415 /**
8416 * Set a hint that the screen should remain on for at least this duration when
8417 * this notification is displayed on the screen.
8418 * @param timeout The requested screen timeout in milliseconds. Can also be either
8419 * {@link #SCREEN_TIMEOUT_SHORT} or {@link #SCREEN_TIMEOUT_LONG}.
8420 * @return this object for method chaining
8421 */
Gus Prevasb5f9cf52018-02-21 14:21:50 -05008422 @Deprecated
Griff Hazen5f2edfc2014-09-29 16:28:44 -07008423 public WearableExtender setHintScreenTimeout(int timeout) {
8424 mHintScreenTimeout = timeout;
8425 return this;
8426 }
8427
8428 /**
8429 * Get the duration, in milliseconds, that the screen should remain on for
8430 * when this notification is displayed.
8431 * @return the duration in milliseconds if > 0, or either one of the sentinel values
8432 * {@link #SCREEN_TIMEOUT_SHORT} or {@link #SCREEN_TIMEOUT_LONG}.
8433 */
Gus Prevasb5f9cf52018-02-21 14:21:50 -05008434 @Deprecated
Griff Hazen5f2edfc2014-09-29 16:28:44 -07008435 public int getHintScreenTimeout() {
8436 return mHintScreenTimeout;
8437 }
8438
Alex Hills9ab3a232016-04-05 14:54:56 -04008439 /**
Alex Hills4bcb06b2016-04-05 14:26:25 -04008440 * Set a hint that this notification's {@link BigPictureStyle} (if present) should be
8441 * converted to low-bit and displayed in ambient mode, especially useful for barcodes and
8442 * qr codes, as well as other simple black-and-white tickets.
8443 * @param hintAmbientBigPicture {@code true} to enable converstion and ambient.
8444 * @return this object for method chaining
8445 */
8446 public WearableExtender setHintAmbientBigPicture(boolean hintAmbientBigPicture) {
8447 setFlag(FLAG_BIG_PICTURE_AMBIENT, hintAmbientBigPicture);
8448 return this;
8449 }
8450
8451 /**
8452 * Get a hint that this notification's {@link BigPictureStyle} (if present) should be
8453 * converted to low-bit and displayed in ambient mode, especially useful for barcodes and
8454 * qr codes, as well as other simple black-and-white tickets.
8455 * @return {@code true} if it should be displayed in ambient, false otherwise
8456 * otherwise. The default value is {@code false} if this was never set.
8457 */
8458 public boolean getHintAmbientBigPicture() {
8459 return (mFlags & FLAG_BIG_PICTURE_AMBIENT) != 0;
8460 }
8461
8462 /**
Alex Hills9ab3a232016-04-05 14:54:56 -04008463 * Set a hint that this notification's content intent will launch an {@link Activity}
8464 * directly, telling the platform that it can generate the appropriate transitions.
8465 * @param hintContentIntentLaunchesActivity {@code true} if the content intent will launch
8466 * an activity and transitions should be generated, false otherwise.
8467 * @return this object for method chaining
8468 */
8469 public WearableExtender setHintContentIntentLaunchesActivity(
8470 boolean hintContentIntentLaunchesActivity) {
8471 setFlag(FLAG_HINT_CONTENT_INTENT_LAUNCHES_ACTIVITY, hintContentIntentLaunchesActivity);
8472 return this;
8473 }
8474
8475 /**
8476 * Get a hint that this notification's content intent will launch an {@link Activity}
8477 * directly, telling the platform that it can generate the appropriate transitions
8478 * @return {@code true} if the content intent will launch an activity and transitions should
8479 * be generated, false otherwise. The default value is {@code false} if this was never set.
8480 */
8481 public boolean getHintContentIntentLaunchesActivity() {
8482 return (mFlags & FLAG_HINT_CONTENT_INTENT_LAUNCHES_ACTIVITY) != 0;
8483 }
8484
Nadia Benbernou948627e2016-04-14 14:41:08 -04008485 /**
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04008486 * Sets the dismissal id for this notification. If a notification is posted with a
8487 * dismissal id, then when that notification is canceled, notifications on other wearables
8488 * and the paired Android phone having that same dismissal id will also be canceled. See
Nadia Benbernou948627e2016-04-14 14:41:08 -04008489 * <a href="{@docRoot}wear/notifications/index.html">Adding Wearable Features to
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04008490 * Notifications</a> for more information.
Nadia Benbernou948627e2016-04-14 14:41:08 -04008491 * @param dismissalId the dismissal id of the notification.
8492 * @return this object for method chaining
8493 */
8494 public WearableExtender setDismissalId(String dismissalId) {
8495 mDismissalId = dismissalId;
8496 return this;
8497 }
8498
8499 /**
8500 * Returns the dismissal id of the notification.
8501 * @return the dismissal id of the notification or null if it has not been set.
8502 */
8503 public String getDismissalId() {
8504 return mDismissalId;
8505 }
8506
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04008507 /**
8508 * Sets a bridge tag for this notification. A bridge tag can be set for notifications
8509 * posted from a phone to provide finer-grained control on what notifications are bridged
8510 * to wearables. See <a href="{@docRoot}wear/notifications/index.html">Adding Wearable
8511 * Features to Notifications</a> for more information.
8512 * @param bridgeTag the bridge tag of the notification.
8513 * @return this object for method chaining
8514 */
8515 public WearableExtender setBridgeTag(String bridgeTag) {
8516 mBridgeTag = bridgeTag;
8517 return this;
8518 }
8519
8520 /**
8521 * Returns the bridge tag of the notification.
8522 * @return the bridge tag or null if not present.
8523 */
8524 public String getBridgeTag() {
8525 return mBridgeTag;
8526 }
8527
Griff Hazen61a9e862014-05-22 16:05:19 -07008528 private void setFlag(int mask, boolean value) {
8529 if (value) {
8530 mFlags |= mask;
8531 } else {
8532 mFlags &= ~mask;
8533 }
8534 }
8535 }
8536
8537 /**
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08008538 * <p>Helper class to add Android Auto extensions to notifications. To create a notification
8539 * with car extensions:
8540 *
8541 * <ol>
8542 * <li>Create an {@link Notification.Builder}, setting any desired
8543 * properties.
8544 * <li>Create a {@link CarExtender}.
8545 * <li>Set car-specific properties using the {@code add} and {@code set} methods of
8546 * {@link CarExtender}.
8547 * <li>Call {@link Notification.Builder#extend(Notification.Extender)}
8548 * to apply the extensions to a notification.
8549 * </ol>
8550 *
8551 * <pre class="prettyprint">
8552 * Notification notification = new Notification.Builder(context)
8553 * ...
8554 * .extend(new CarExtender()
8555 * .set*(...))
8556 * .build();
8557 * </pre>
8558 *
8559 * <p>Car extensions can be accessed on an existing notification by using the
8560 * {@code CarExtender(Notification)} constructor, and then using the {@code get} methods
8561 * to access values.
8562 */
8563 public static final class CarExtender implements Extender {
8564 private static final String TAG = "CarExtender";
8565
8566 private static final String EXTRA_CAR_EXTENDER = "android.car.EXTENSIONS";
8567 private static final String EXTRA_LARGE_ICON = "large_icon";
8568 private static final String EXTRA_CONVERSATION = "car_conversation";
8569 private static final String EXTRA_COLOR = "app_color";
8570
8571 private Bitmap mLargeIcon;
8572 private UnreadConversation mUnreadConversation;
8573 private int mColor = Notification.COLOR_DEFAULT;
8574
8575 /**
8576 * Create a {@link CarExtender} with default options.
8577 */
8578 public CarExtender() {
8579 }
8580
8581 /**
8582 * Create a {@link CarExtender} from the CarExtender options of an existing Notification.
8583 *
8584 * @param notif The notification from which to copy options.
8585 */
8586 public CarExtender(Notification notif) {
8587 Bundle carBundle = notif.extras == null ?
8588 null : notif.extras.getBundle(EXTRA_CAR_EXTENDER);
8589 if (carBundle != null) {
8590 mLargeIcon = carBundle.getParcelable(EXTRA_LARGE_ICON);
8591 mColor = carBundle.getInt(EXTRA_COLOR, Notification.COLOR_DEFAULT);
8592
8593 Bundle b = carBundle.getBundle(EXTRA_CONVERSATION);
8594 mUnreadConversation = UnreadConversation.getUnreadConversationFromBundle(b);
8595 }
8596 }
8597
8598 /**
8599 * Apply car extensions to a notification that is being built. This is typically called by
8600 * the {@link Notification.Builder#extend(Notification.Extender)}
8601 * method of {@link Notification.Builder}.
8602 */
8603 @Override
8604 public Notification.Builder extend(Notification.Builder builder) {
8605 Bundle carExtensions = new Bundle();
8606
8607 if (mLargeIcon != null) {
8608 carExtensions.putParcelable(EXTRA_LARGE_ICON, mLargeIcon);
8609 }
8610 if (mColor != Notification.COLOR_DEFAULT) {
8611 carExtensions.putInt(EXTRA_COLOR, mColor);
8612 }
8613
8614 if (mUnreadConversation != null) {
8615 Bundle b = mUnreadConversation.getBundleForUnreadConversation();
8616 carExtensions.putBundle(EXTRA_CONVERSATION, b);
8617 }
8618
8619 builder.getExtras().putBundle(EXTRA_CAR_EXTENDER, carExtensions);
8620 return builder;
8621 }
8622
8623 /**
8624 * Sets the accent color to use when Android Auto presents the notification.
8625 *
8626 * Android Auto uses the color set with {@link Notification.Builder#setColor(int)}
8627 * to accent the displayed notification. However, not all colors are acceptable in an
8628 * automotive setting. This method can be used to override the color provided in the
8629 * notification in such a situation.
8630 */
Tor Norbye80756e32015-03-02 09:39:27 -08008631 public CarExtender setColor(@ColorInt int color) {
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08008632 mColor = color;
8633 return this;
8634 }
8635
8636 /**
8637 * Gets the accent color.
8638 *
Julia Reynoldsd9228f12015-10-20 10:37:27 -04008639 * @see #setColor
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08008640 */
Tor Norbye80756e32015-03-02 09:39:27 -08008641 @ColorInt
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08008642 public int getColor() {
8643 return mColor;
8644 }
8645
8646 /**
8647 * Sets the large icon of the car notification.
8648 *
8649 * If no large icon is set in the extender, Android Auto will display the icon
8650 * specified by {@link Notification.Builder#setLargeIcon(android.graphics.Bitmap)}
8651 *
8652 * @param largeIcon The large icon to use in the car notification.
8653 * @return This object for method chaining.
8654 */
8655 public CarExtender setLargeIcon(Bitmap largeIcon) {
8656 mLargeIcon = largeIcon;
8657 return this;
8658 }
8659
8660 /**
8661 * Gets the large icon used in this car notification, or null if no icon has been set.
8662 *
8663 * @return The large icon for the car notification.
8664 * @see CarExtender#setLargeIcon
8665 */
8666 public Bitmap getLargeIcon() {
8667 return mLargeIcon;
8668 }
8669
8670 /**
8671 * Sets the unread conversation in a message notification.
8672 *
8673 * @param unreadConversation The unread part of the conversation this notification conveys.
8674 * @return This object for method chaining.
8675 */
8676 public CarExtender setUnreadConversation(UnreadConversation unreadConversation) {
8677 mUnreadConversation = unreadConversation;
8678 return this;
8679 }
8680
8681 /**
8682 * Returns the unread conversation conveyed by this notification.
8683 * @see #setUnreadConversation(UnreadConversation)
8684 */
8685 public UnreadConversation getUnreadConversation() {
8686 return mUnreadConversation;
8687 }
8688
8689 /**
8690 * A class which holds the unread messages from a conversation.
8691 */
8692 public static class UnreadConversation {
8693 private static final String KEY_AUTHOR = "author";
8694 private static final String KEY_TEXT = "text";
8695 private static final String KEY_MESSAGES = "messages";
8696 private static final String KEY_REMOTE_INPUT = "remote_input";
8697 private static final String KEY_ON_REPLY = "on_reply";
8698 private static final String KEY_ON_READ = "on_read";
8699 private static final String KEY_PARTICIPANTS = "participants";
8700 private static final String KEY_TIMESTAMP = "timestamp";
8701
8702 private final String[] mMessages;
8703 private final RemoteInput mRemoteInput;
8704 private final PendingIntent mReplyPendingIntent;
8705 private final PendingIntent mReadPendingIntent;
8706 private final String[] mParticipants;
8707 private final long mLatestTimestamp;
8708
8709 UnreadConversation(String[] messages, RemoteInput remoteInput,
8710 PendingIntent replyPendingIntent, PendingIntent readPendingIntent,
8711 String[] participants, long latestTimestamp) {
8712 mMessages = messages;
8713 mRemoteInput = remoteInput;
8714 mReadPendingIntent = readPendingIntent;
8715 mReplyPendingIntent = replyPendingIntent;
8716 mParticipants = participants;
8717 mLatestTimestamp = latestTimestamp;
8718 }
8719
8720 /**
8721 * Gets the list of messages conveyed by this notification.
8722 */
8723 public String[] getMessages() {
8724 return mMessages;
8725 }
8726
8727 /**
8728 * Gets the remote input that will be used to convey the response to a message list, or
8729 * null if no such remote input exists.
8730 */
8731 public RemoteInput getRemoteInput() {
8732 return mRemoteInput;
8733 }
8734
8735 /**
8736 * Gets the pending intent that will be triggered when the user replies to this
8737 * notification.
8738 */
8739 public PendingIntent getReplyPendingIntent() {
8740 return mReplyPendingIntent;
8741 }
8742
8743 /**
8744 * Gets the pending intent that Android Auto will send after it reads aloud all messages
8745 * in this object's message list.
8746 */
8747 public PendingIntent getReadPendingIntent() {
8748 return mReadPendingIntent;
8749 }
8750
8751 /**
8752 * Gets the participants in the conversation.
8753 */
8754 public String[] getParticipants() {
8755 return mParticipants;
8756 }
8757
8758 /**
8759 * Gets the firs participant in the conversation.
8760 */
8761 public String getParticipant() {
8762 return mParticipants.length > 0 ? mParticipants[0] : null;
8763 }
8764
8765 /**
8766 * Gets the timestamp of the conversation.
8767 */
8768 public long getLatestTimestamp() {
8769 return mLatestTimestamp;
8770 }
8771
8772 Bundle getBundleForUnreadConversation() {
8773 Bundle b = new Bundle();
8774 String author = null;
8775 if (mParticipants != null && mParticipants.length > 1) {
8776 author = mParticipants[0];
8777 }
8778 Parcelable[] messages = new Parcelable[mMessages.length];
8779 for (int i = 0; i < messages.length; i++) {
8780 Bundle m = new Bundle();
8781 m.putString(KEY_TEXT, mMessages[i]);
8782 m.putString(KEY_AUTHOR, author);
8783 messages[i] = m;
8784 }
8785 b.putParcelableArray(KEY_MESSAGES, messages);
8786 if (mRemoteInput != null) {
8787 b.putParcelable(KEY_REMOTE_INPUT, mRemoteInput);
8788 }
8789 b.putParcelable(KEY_ON_REPLY, mReplyPendingIntent);
8790 b.putParcelable(KEY_ON_READ, mReadPendingIntent);
8791 b.putStringArray(KEY_PARTICIPANTS, mParticipants);
8792 b.putLong(KEY_TIMESTAMP, mLatestTimestamp);
8793 return b;
8794 }
8795
8796 static UnreadConversation getUnreadConversationFromBundle(Bundle b) {
8797 if (b == null) {
8798 return null;
8799 }
8800 Parcelable[] parcelableMessages = b.getParcelableArray(KEY_MESSAGES);
8801 String[] messages = null;
8802 if (parcelableMessages != null) {
8803 String[] tmp = new String[parcelableMessages.length];
8804 boolean success = true;
8805 for (int i = 0; i < tmp.length; i++) {
8806 if (!(parcelableMessages[i] instanceof Bundle)) {
8807 success = false;
8808 break;
8809 }
8810 tmp[i] = ((Bundle) parcelableMessages[i]).getString(KEY_TEXT);
8811 if (tmp[i] == null) {
8812 success = false;
8813 break;
8814 }
8815 }
8816 if (success) {
8817 messages = tmp;
8818 } else {
8819 return null;
8820 }
8821 }
8822
8823 PendingIntent onRead = b.getParcelable(KEY_ON_READ);
8824 PendingIntent onReply = b.getParcelable(KEY_ON_REPLY);
8825
8826 RemoteInput remoteInput = b.getParcelable(KEY_REMOTE_INPUT);
8827
8828 String[] participants = b.getStringArray(KEY_PARTICIPANTS);
8829 if (participants == null || participants.length != 1) {
8830 return null;
8831 }
8832
8833 return new UnreadConversation(messages,
8834 remoteInput,
8835 onReply,
8836 onRead,
8837 participants, b.getLong(KEY_TIMESTAMP));
8838 }
8839 };
8840
8841 /**
8842 * Builder class for {@link CarExtender.UnreadConversation} objects.
8843 */
8844 public static class Builder {
8845 private final List<String> mMessages = new ArrayList<String>();
8846 private final String mParticipant;
8847 private RemoteInput mRemoteInput;
8848 private PendingIntent mReadPendingIntent;
8849 private PendingIntent mReplyPendingIntent;
8850 private long mLatestTimestamp;
8851
8852 /**
8853 * Constructs a new builder for {@link CarExtender.UnreadConversation}.
8854 *
8855 * @param name The name of the other participant in the conversation.
8856 */
8857 public Builder(String name) {
8858 mParticipant = name;
8859 }
8860
8861 /**
8862 * Appends a new unread message to the list of messages for this conversation.
8863 *
8864 * The messages should be added from oldest to newest.
8865 *
8866 * @param message The text of the new unread message.
8867 * @return This object for method chaining.
8868 */
8869 public Builder addMessage(String message) {
8870 mMessages.add(message);
8871 return this;
8872 }
8873
8874 /**
8875 * Sets the pending intent and remote input which will convey the reply to this
8876 * notification.
8877 *
8878 * @param pendingIntent The pending intent which will be triggered on a reply.
8879 * @param remoteInput The remote input parcelable which will carry the reply.
8880 * @return This object for method chaining.
8881 *
8882 * @see CarExtender.UnreadConversation#getRemoteInput
8883 * @see CarExtender.UnreadConversation#getReplyPendingIntent
8884 */
8885 public Builder setReplyAction(
8886 PendingIntent pendingIntent, RemoteInput remoteInput) {
8887 mRemoteInput = remoteInput;
8888 mReplyPendingIntent = pendingIntent;
8889
8890 return this;
8891 }
8892
8893 /**
8894 * Sets the pending intent that will be sent once the messages in this notification
8895 * are read.
8896 *
8897 * @param pendingIntent The pending intent to use.
8898 * @return This object for method chaining.
8899 */
8900 public Builder setReadPendingIntent(PendingIntent pendingIntent) {
8901 mReadPendingIntent = pendingIntent;
8902 return this;
8903 }
8904
8905 /**
8906 * Sets the timestamp of the most recent message in an unread conversation.
8907 *
8908 * If a messaging notification has been posted by your application and has not
8909 * yet been cancelled, posting a later notification with the same id and tag
8910 * but without a newer timestamp may result in Android Auto not displaying a
8911 * heads up notification for the later notification.
8912 *
8913 * @param timestamp The timestamp of the most recent message in the conversation.
8914 * @return This object for method chaining.
8915 */
8916 public Builder setLatestTimestamp(long timestamp) {
8917 mLatestTimestamp = timestamp;
8918 return this;
8919 }
8920
8921 /**
8922 * Builds a new unread conversation object.
8923 *
8924 * @return The new unread conversation object.
8925 */
8926 public UnreadConversation build() {
8927 String[] messages = mMessages.toArray(new String[mMessages.size()]);
8928 String[] participants = { mParticipant };
8929 return new UnreadConversation(messages, mRemoteInput, mReplyPendingIntent,
8930 mReadPendingIntent, participants, mLatestTimestamp);
8931 }
8932 }
8933 }
8934
8935 /**
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08008936 * <p>Helper class to add Android TV extensions to notifications. To create a notification
8937 * with a TV extension:
8938 *
8939 * <ol>
8940 * <li>Create an {@link Notification.Builder}, setting any desired properties.
8941 * <li>Create a {@link TvExtender}.
8942 * <li>Set TV-specific properties using the {@code set} methods of
8943 * {@link TvExtender}.
8944 * <li>Call {@link Notification.Builder#extend(Notification.Extender)}
8945 * to apply the extension to a notification.
8946 * </ol>
8947 *
8948 * <pre class="prettyprint">
8949 * Notification notification = new Notification.Builder(context)
8950 * ...
8951 * .extend(new TvExtender()
8952 * .set*(...))
8953 * .build();
8954 * </pre>
8955 *
8956 * <p>TV extensions can be accessed on an existing notification by using the
8957 * {@code TvExtender(Notification)} constructor, and then using the {@code get} methods
8958 * to access values.
8959 *
8960 * @hide
8961 */
8962 @SystemApi
8963 public static final class TvExtender implements Extender {
8964 private static final String TAG = "TvExtender";
8965
8966 private static final String EXTRA_TV_EXTENDER = "android.tv.EXTENSIONS";
8967 private static final String EXTRA_FLAGS = "flags";
8968 private static final String EXTRA_CONTENT_INTENT = "content_intent";
8969 private static final String EXTRA_DELETE_INTENT = "delete_intent";
Dmitri Plotnikovb8a04ee2017-01-20 10:39:22 -08008970 private static final String EXTRA_CHANNEL_ID = "channel_id";
Rhiannon Malia1a083932018-01-24 15:02:30 -08008971 private static final String EXTRA_SUPPRESS_SHOW_OVER_APPS = "suppressShowOverApps";
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08008972
8973 // Flags bitwise-ored to mFlags
8974 private static final int FLAG_AVAILABLE_ON_TV = 0x1;
8975
8976 private int mFlags;
Dmitri Plotnikovb8a04ee2017-01-20 10:39:22 -08008977 private String mChannelId;
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08008978 private PendingIntent mContentIntent;
8979 private PendingIntent mDeleteIntent;
Rhiannon Malia1a083932018-01-24 15:02:30 -08008980 private boolean mSuppressShowOverApps;
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08008981
8982 /**
8983 * Create a {@link TvExtender} with default options.
8984 */
8985 public TvExtender() {
8986 mFlags = FLAG_AVAILABLE_ON_TV;
8987 }
8988
8989 /**
8990 * Create a {@link TvExtender} from the TvExtender options of an existing Notification.
8991 *
8992 * @param notif The notification from which to copy options.
8993 */
8994 public TvExtender(Notification notif) {
8995 Bundle bundle = notif.extras == null ?
8996 null : notif.extras.getBundle(EXTRA_TV_EXTENDER);
8997 if (bundle != null) {
8998 mFlags = bundle.getInt(EXTRA_FLAGS);
Dmitri Plotnikovb8a04ee2017-01-20 10:39:22 -08008999 mChannelId = bundle.getString(EXTRA_CHANNEL_ID);
Rhiannon Malia1a083932018-01-24 15:02:30 -08009000 mSuppressShowOverApps = bundle.getBoolean(EXTRA_SUPPRESS_SHOW_OVER_APPS);
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08009001 mContentIntent = bundle.getParcelable(EXTRA_CONTENT_INTENT);
9002 mDeleteIntent = bundle.getParcelable(EXTRA_DELETE_INTENT);
9003 }
9004 }
9005
9006 /**
9007 * Apply a TV extension to a notification that is being built. This is typically called by
9008 * the {@link Notification.Builder#extend(Notification.Extender)}
9009 * method of {@link Notification.Builder}.
9010 */
9011 @Override
9012 public Notification.Builder extend(Notification.Builder builder) {
9013 Bundle bundle = new Bundle();
9014
9015 bundle.putInt(EXTRA_FLAGS, mFlags);
Dmitri Plotnikovb8a04ee2017-01-20 10:39:22 -08009016 bundle.putString(EXTRA_CHANNEL_ID, mChannelId);
Rhiannon Malia1a083932018-01-24 15:02:30 -08009017 bundle.putBoolean(EXTRA_SUPPRESS_SHOW_OVER_APPS, mSuppressShowOverApps);
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08009018 if (mContentIntent != null) {
9019 bundle.putParcelable(EXTRA_CONTENT_INTENT, mContentIntent);
9020 }
9021
9022 if (mDeleteIntent != null) {
9023 bundle.putParcelable(EXTRA_DELETE_INTENT, mDeleteIntent);
9024 }
9025
9026 builder.getExtras().putBundle(EXTRA_TV_EXTENDER, bundle);
9027 return builder;
9028 }
9029
9030 /**
9031 * Returns true if this notification should be shown on TV. This method return true
9032 * if the notification was extended with a TvExtender.
9033 */
9034 public boolean isAvailableOnTv() {
9035 return (mFlags & FLAG_AVAILABLE_ON_TV) != 0;
9036 }
9037
9038 /**
Dmitri Plotnikovb8a04ee2017-01-20 10:39:22 -08009039 * Specifies the channel the notification should be delivered on when shown on TV.
9040 * It can be different from the channel that the notification is delivered to when
9041 * posting on a non-TV device.
9042 */
9043 public TvExtender setChannel(String channelId) {
9044 mChannelId = channelId;
9045 return this;
9046 }
9047
9048 /**
Julia Reynoldsbad42972017-04-25 13:52:49 -04009049 * Specifies the channel the notification should be delivered on when shown on TV.
9050 * It can be different from the channel that the notification is delivered to when
9051 * posting on a non-TV device.
9052 */
9053 public TvExtender setChannelId(String channelId) {
9054 mChannelId = channelId;
9055 return this;
9056 }
9057
Jeff Sharkey000ce802017-04-29 13:13:27 -06009058 /** @removed */
9059 @Deprecated
Dmitri Plotnikovb8a04ee2017-01-20 10:39:22 -08009060 public String getChannel() {
9061 return mChannelId;
9062 }
9063
9064 /**
Julia Reynoldsbad42972017-04-25 13:52:49 -04009065 * Returns the id of the channel this notification posts to on TV.
9066 */
9067 public String getChannelId() {
9068 return mChannelId;
9069 }
9070
9071 /**
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08009072 * Supplies a {@link PendingIntent} to be sent when the notification is selected on TV.
9073 * If provided, it is used instead of the content intent specified
9074 * at the level of Notification.
9075 */
9076 public TvExtender setContentIntent(PendingIntent intent) {
9077 mContentIntent = intent;
9078 return this;
9079 }
9080
9081 /**
9082 * Returns the TV-specific content intent. If this method returns null, the
9083 * main content intent on the notification should be used.
9084 *
9085 * @see {@link Notification#contentIntent}
9086 */
9087 public PendingIntent getContentIntent() {
9088 return mContentIntent;
9089 }
9090
9091 /**
9092 * Supplies a {@link PendingIntent} to send when the notification is cleared explicitly
9093 * by the user on TV. If provided, it is used instead of the delete intent specified
9094 * at the level of Notification.
9095 */
9096 public TvExtender setDeleteIntent(PendingIntent intent) {
9097 mDeleteIntent = intent;
9098 return this;
9099 }
9100
9101 /**
9102 * Returns the TV-specific delete intent. If this method returns null, the
9103 * main delete intent on the notification should be used.
9104 *
9105 * @see {@link Notification#deleteIntent}
9106 */
9107 public PendingIntent getDeleteIntent() {
9108 return mDeleteIntent;
9109 }
Rhiannon Malia1a083932018-01-24 15:02:30 -08009110
9111 /**
9112 * Specifies whether this notification should suppress showing a message over top of apps
9113 * outside of the launcher.
9114 */
9115 public TvExtender setSuppressShowOverApps(boolean suppress) {
9116 mSuppressShowOverApps = suppress;
9117 return this;
9118 }
9119
9120 /**
9121 * Returns true if this notification should not show messages over top of apps
9122 * outside of the launcher.
9123 */
9124 public boolean getSuppressShowOverApps() {
9125 return mSuppressShowOverApps;
9126 }
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08009127 }
9128
9129 /**
Griff Hazen61a9e862014-05-22 16:05:19 -07009130 * Get an array of Notification objects from a parcelable array bundle field.
9131 * Update the bundle to have a typed array so fetches in the future don't need
9132 * to do an array copy.
9133 */
9134 private static Notification[] getNotificationArrayFromBundle(Bundle bundle, String key) {
9135 Parcelable[] array = bundle.getParcelableArray(key);
9136 if (array instanceof Notification[] || array == null) {
9137 return (Notification[]) array;
9138 }
9139 Notification[] typedArray = Arrays.copyOf(array, array.length,
9140 Notification[].class);
9141 bundle.putParcelableArray(key, typedArray);
9142 return typedArray;
9143 }
Christoph Studer4600f9b2014-07-22 22:44:43 +02009144
9145 private static class BuilderRemoteViews extends RemoteViews {
9146 public BuilderRemoteViews(Parcel parcel) {
9147 super(parcel);
9148 }
9149
Kenny Guy77320062014-08-27 21:37:15 +01009150 public BuilderRemoteViews(ApplicationInfo appInfo, int layoutId) {
9151 super(appInfo, layoutId);
Christoph Studer4600f9b2014-07-22 22:44:43 +02009152 }
9153
9154 @Override
9155 public BuilderRemoteViews clone() {
9156 Parcel p = Parcel.obtain();
9157 writeToParcel(p, 0);
9158 p.setDataPosition(0);
9159 BuilderRemoteViews brv = new BuilderRemoteViews(p);
9160 p.recycle();
9161 return brv;
9162 }
9163 }
Adrian Roos70d7aa32017-01-11 15:39:06 -08009164
9165 private static class StandardTemplateParams {
9166 boolean hasProgress = true;
9167 boolean ambient = false;
9168 CharSequence title;
9169 CharSequence text;
Selim Cinekafeed292017-12-12 17:32:44 -08009170 CharSequence headerTextSecondary;
Selim Cinek88188f22017-09-19 16:46:56 -07009171 boolean hideLargeIcon;
9172 public boolean alwaysShowReply;
Adrian Roos70d7aa32017-01-11 15:39:06 -08009173
9174 final StandardTemplateParams reset() {
9175 hasProgress = true;
9176 ambient = false;
9177 title = null;
9178 text = null;
Selim Cinekafeed292017-12-12 17:32:44 -08009179 headerTextSecondary = null;
Adrian Roos70d7aa32017-01-11 15:39:06 -08009180 return this;
9181 }
9182
9183 final StandardTemplateParams hasProgress(boolean hasProgress) {
9184 this.hasProgress = hasProgress;
9185 return this;
9186 }
9187
9188 final StandardTemplateParams title(CharSequence title) {
9189 this.title = title;
9190 return this;
9191 }
9192
9193 final StandardTemplateParams text(CharSequence text) {
9194 this.text = text;
9195 return this;
9196 }
9197
Selim Cinekafeed292017-12-12 17:32:44 -08009198 final StandardTemplateParams headerTextSecondary(CharSequence text) {
9199 this.headerTextSecondary = text;
9200 return this;
9201 }
9202
Selim Cinek88188f22017-09-19 16:46:56 -07009203 final StandardTemplateParams alwaysShowReply(boolean alwaysShowReply) {
9204 this.alwaysShowReply = alwaysShowReply;
9205 return this;
9206 }
9207
9208 final StandardTemplateParams hideLargeIcon(boolean hideLargeIcon) {
9209 this.hideLargeIcon = hideLargeIcon;
9210 return this;
9211 }
9212
Adrian Roos70d7aa32017-01-11 15:39:06 -08009213 final StandardTemplateParams ambient(boolean ambient) {
Adrian Roos0bc3f6a2017-03-06 11:54:05 -08009214 Preconditions.checkState(title == null && text == null, "must set ambient before text");
Adrian Roos70d7aa32017-01-11 15:39:06 -08009215 this.ambient = ambient;
9216 return this;
9217 }
9218
9219 final StandardTemplateParams fillTextsFrom(Builder b) {
9220 Bundle extras = b.mN.extras;
Lucas Dupin06c5e642017-09-13 16:34:58 -07009221 this.title = b.processLegacyText(extras.getCharSequence(EXTRA_TITLE), ambient);
9222
9223 // Big text notifications should contain their content when viewed in ambient mode.
9224 CharSequence text = extras.getCharSequence(EXTRA_BIG_TEXT);
9225 if (!ambient || TextUtils.isEmpty(text)) {
9226 text = extras.getCharSequence(EXTRA_TEXT);
9227 }
9228 this.text = b.processLegacyText(text, ambient);
Adrian Roos70d7aa32017-01-11 15:39:06 -08009229 return this;
9230 }
9231 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08009232}