blob: 3638bc48d2b5387ce3f1e7c8007644d570713c1e [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
Lucas Dupina291d192018-06-07 13:59:42 -070019import static com.android.internal.util.ContrastColorUtil.satisfiesTextContrast;
Selim Cinek389edcd2017-05-11 19:16:44 -070020
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;
Mathew Inwood61e8ae62018-08-14 14:17:44 +010030import android.annotation.UnsupportedAppUsage;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080031import android.content.Context;
32import android.content.Intent;
Kenny Guy77320062014-08-27 21:37:15 +010033import android.content.pm.ApplicationInfo;
Dan Sandler732bd6c2016-04-12 14:20:32 -040034import android.content.pm.PackageManager;
Christoph Studer4600f9b2014-07-22 22:44:43 +020035import android.content.pm.PackageManager.NameNotFoundException;
Julia Reynolds13d898c2017-02-02 12:22:05 -050036import android.content.pm.ShortcutInfo;
Jorim Jaggief72a192014-08-26 21:57:46 +020037import android.content.res.ColorStateList;
Anthony Chenad4d1582017-04-10 16:07:58 -070038import android.content.res.Configuration;
39import android.content.res.Resources;
Joe Onoratoef1e7762010-09-17 18:38:38 -040040import android.graphics.Bitmap;
Kenny Guy8a0101b2014-05-08 23:34:12 +010041import android.graphics.Canvas;
Adrian Roosc1a80b02016-04-05 14:54:55 -070042import android.graphics.Color;
Jorim Jaggi5c2d8462014-03-21 17:37:00 +010043import android.graphics.PorterDuff;
Kenny Guy8a0101b2014-05-08 23:34:12 +010044import android.graphics.drawable.Drawable;
Dan Sandlerd63f9322015-05-06 15:18:49 -040045import android.graphics.drawable.Icon;
John Spurlockc0650f022014-07-19 13:22:39 -040046import android.media.AudioAttributes;
Jeff Sharkey098d5802012-04-26 17:30:34 -070047import android.media.AudioManager;
Jean-Michel Trivi2f7511f2016-11-28 15:40:27 -080048import android.media.PlayerBase;
Jeff Browndba34ba2014-06-24 20:46:03 -070049import android.media.session.MediaSession;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080050import android.net.Uri;
Daniel Sandlerdcbaf662013-04-26 16:23:09 -040051import android.os.BadParcelableException;
Christoph Studer239f8352014-08-25 15:13:18 +020052import android.os.Build;
Daniel Sandler2561b0b2012-02-13 21:04:12 -050053import android.os.Bundle;
Dianne Hackborn98305522017-05-05 17:53:53 -070054import android.os.IBinder;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080055import android.os.Parcel;
56import android.os.Parcelable;
Daniel Sandlera2985ed2012-04-03 16:42:00 -040057import android.os.SystemClock;
Selim Cinek6743c0b2017-01-18 18:24:01 -080058import android.os.SystemProperties;
Jeff Sharkey6d515712012-09-20 16:06:08 -070059import android.os.UserHandle;
Adrian Roosc1a80b02016-04-05 14:54:55 -070060import android.text.BidiFormatter;
Selim Cinek60a54252016-02-26 17:03:25 -080061import android.text.SpannableStringBuilder;
62import android.text.Spanned;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080063import android.text.TextUtils;
Selim Cinek60a54252016-02-26 17:03:25 -080064import android.text.style.AbsoluteSizeSpan;
Selim Cinek89991a22016-03-07 19:51:54 -080065import android.text.style.CharacterStyle;
Selim Cinek981962e2016-07-20 20:41:58 -070066import android.text.style.ForegroundColorSpan;
Selim Cinek60a54252016-02-26 17:03:25 -080067import android.text.style.RelativeSizeSpan;
68import android.text.style.TextAppearanceSpan;
Svet Ganovddb94882016-06-23 19:55:24 -070069import android.util.ArraySet;
Daniel Sandlerdcbaf662013-04-26 16:23:09 -040070import android.util.Log;
Dan Sandler50128532015-12-08 15:42:41 -050071import android.util.SparseArray;
Yi Jin6b514142017-10-30 14:54:12 -070072import android.util.proto.ProtoOutputStream;
Griff Hazen61a9e862014-05-22 16:05:19 -070073import android.view.Gravity;
Selim Cinekea4bef72015-12-02 15:51:10 -080074import android.view.NotificationHeaderView;
Joe Onorato8595a3d2010-11-19 18:12:07 -080075import android.view.View;
Selim Cinek954cc232016-05-20 13:29:23 -070076import android.view.ViewGroup;
Jeff Sharkey1c400132011-08-05 14:50:13 -070077import android.widget.ProgressBar;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080078import android.widget.RemoteViews;
79
Griff Hazen959591e2014-05-15 22:26:18 -070080import com.android.internal.R;
Selim Cinek389edcd2017-05-11 19:16:44 -070081import com.android.internal.annotations.VisibleForTesting;
Svet Ganovddb94882016-06-23 19:55:24 -070082import com.android.internal.util.ArrayUtils;
Lucas Dupina291d192018-06-07 13:59:42 -070083import com.android.internal.util.ContrastColorUtil;
Adrian Roos0bc3f6a2017-03-06 11:54:05 -080084import com.android.internal.util.Preconditions;
Griff Hazen959591e2014-05-15 22:26:18 -070085
Tor Norbyed9273d62013-05-30 15:59:53 -070086import java.lang.annotation.Retention;
87import java.lang.annotation.RetentionPolicy;
Christoph Studer4600f9b2014-07-22 22:44:43 +020088import java.lang.reflect.Constructor;
Daniel Sandler2561b0b2012-02-13 21:04:12 -050089import java.util.ArrayList;
Griff Hazen61a9e862014-05-22 16:05:19 -070090import java.util.Arrays;
Griff Hazen5cadc3b2014-05-20 09:55:39 -070091import java.util.Collections;
Griff Hazen61a9e862014-05-22 16:05:19 -070092import java.util.List;
Julia Reynolds7217dc92018-03-07 12:12:09 -050093import java.util.Objects;
Dan Sandler50128532015-12-08 15:42:41 -050094import java.util.Set;
Jeff Sharkey23b31182018-04-18 21:32:12 -060095import java.util.function.Consumer;
Joe Onorato561d3852010-11-20 18:09:34 -080096
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080097/**
98 * A class that represents how a persistent notification is to be presented to
99 * the user using the {@link android.app.NotificationManager}.
100 *
Joe Onoratocb109a02011-01-18 17:57:41 -0800101 * <p>The {@link Notification.Builder Notification.Builder} has been added to make it
102 * easier to construct Notifications.</p>
103 *
Joe Fernandez558459f2011-10-13 16:47:36 -0700104 * <div class="special reference">
105 * <h3>Developer Guides</h3>
106 * <p>For a guide to creating notifications, read the
107 * <a href="{@docRoot}guide/topics/ui/notifiers/notifications.html">Status Bar Notifications</a>
108 * developer guide.</p>
109 * </div>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800110 */
111public class Notification implements Parcelable
112{
Daniel Sandlerdcbaf662013-04-26 16:23:09 -0400113 private static final String TAG = "Notification";
114
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800115 /**
Daniel Sandler01df1c62014-06-09 10:54:01 -0400116 * An activity that provides a user interface for adjusting notification preferences for its
Julia Reynolds3aedded2017-03-31 14:42:09 -0400117 * containing application.
Daniel Sandler01df1c62014-06-09 10:54:01 -0400118 */
119 @SdkConstant(SdkConstantType.INTENT_CATEGORY)
120 public static final String INTENT_CATEGORY_NOTIFICATION_PREFERENCES
121 = "android.intent.category.NOTIFICATION_PREFERENCES";
122
123 /**
Julia Reynolds2619b5e2017-02-09 09:58:15 -0500124 * Optional extra for {@link #INTENT_CATEGORY_NOTIFICATION_PREFERENCES}. If provided, will
125 * contain a {@link NotificationChannel#getId() channel id} that can be used to narrow down
Julia Reynolds3aedded2017-03-31 14:42:09 -0400126 * what settings should be shown in the target app.
Julia Reynolds2619b5e2017-02-09 09:58:15 -0500127 */
128 public static final String EXTRA_CHANNEL_ID = "android.intent.extra.CHANNEL_ID";
129
130 /**
Julia Reynolds3aedded2017-03-31 14:42:09 -0400131 * Optional extra for {@link #INTENT_CATEGORY_NOTIFICATION_PREFERENCES}. If provided, will
Julia Reynolds005c8b92017-08-24 10:35:53 -0400132 * contain a {@link NotificationChannelGroup#getId() group id} that can be used to narrow down
133 * what settings should be shown in the target app.
134 */
135 public static final String EXTRA_CHANNEL_GROUP_ID = "android.intent.extra.CHANNEL_GROUP_ID";
136
137 /**
138 * Optional extra for {@link #INTENT_CATEGORY_NOTIFICATION_PREFERENCES}. If provided, will
Julia Reynolds3aedded2017-03-31 14:42:09 -0400139 * contain the tag provided to {@link NotificationManager#notify(String, int, Notification)}
140 * that can be used to narrow down what settings should be shown in the target app.
141 */
142 public static final String EXTRA_NOTIFICATION_TAG = "android.intent.extra.NOTIFICATION_TAG";
143
144 /**
145 * Optional extra for {@link #INTENT_CATEGORY_NOTIFICATION_PREFERENCES}. If provided, will
146 * contain the id provided to {@link NotificationManager#notify(String, int, Notification)}
147 * that can be used to narrow down what settings should be shown in the target app.
148 */
149 public static final String EXTRA_NOTIFICATION_ID = "android.intent.extra.NOTIFICATION_ID";
150
151 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800152 * Use all default values (where applicable).
153 */
154 public static final int DEFAULT_ALL = ~0;
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500155
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800156 /**
157 * Use the default notification sound. This will ignore any given
158 * {@link #sound}.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500159 *
Chris Wren47c20a12014-06-18 17:27:29 -0400160 * <p>
161 * A notification that is noisy is more likely to be presented as a heads-up notification.
162 * </p>
163 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800164 * @see #defaults
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500165 */
166
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800167 public static final int DEFAULT_SOUND = 1;
168
169 /**
170 * Use the default notification vibrate. This will ignore any given
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500171 * {@link #vibrate}. Using phone vibration requires the
Scott Mainb8b36452009-04-26 15:50:49 -0700172 * {@link android.Manifest.permission#VIBRATE VIBRATE} permission.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500173 *
Chris Wren47c20a12014-06-18 17:27:29 -0400174 * <p>
175 * A notification that vibrates is more likely to be presented as a heads-up notification.
176 * </p>
177 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800178 * @see #defaults
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500179 */
180
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800181 public static final int DEFAULT_VIBRATE = 2;
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500182
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800183 /**
184 * Use the default notification lights. This will ignore the
185 * {@link #FLAG_SHOW_LIGHTS} bit, and {@link #ledARGB}, {@link #ledOffMS}, or
186 * {@link #ledOnMS}.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500187 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800188 * @see #defaults
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500189 */
190
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800191 public static final int DEFAULT_LIGHTS = 4;
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500192
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800193 /**
Christoph Studer535ec612014-09-03 15:47:47 +0200194 * Maximum length of CharSequences accepted by Builder and friends.
195 *
196 * <p>
197 * Avoids spamming the system with overly large strings such as full e-mails.
198 */
199 private static final int MAX_CHARSEQUENCE_LENGTH = 5 * 1024;
200
201 /**
Adrian Roose458aa82015-12-08 16:17:19 -0800202 * Maximum entries of reply text that are accepted by Builder and friends.
203 */
204 private static final int MAX_REPLY_HISTORY = 5;
205
Tony Mak09db2ea2018-06-27 18:12:48 +0100206 /**
207 * Maximum numbers of action buttons in a notification.
208 * @hide
209 */
210 public static final int MAX_ACTION_BUTTONS = 3;
Selim Cinekde4de0e2018-01-24 16:21:07 -0800211
212 /**
213 * If the notification contained an unsent draft for a RemoteInput when the user clicked on it,
214 * we're adding the draft as a String extra to the {@link #contentIntent} using this key.
215 *
216 * <p>Apps may use this extra to prepopulate text fields in the app, where the user usually
217 * sends messages.</p>
218 */
219 public static final String EXTRA_REMOTE_INPUT_DRAFT = "android.remoteInputDraft";
220
Adrian Roose458aa82015-12-08 16:17:19 -0800221 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500222 * A timestamp related to this notification, in milliseconds since the epoch.
Joe Malin8d40d042012-11-05 11:36:40 -0800223 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500224 * Default value: {@link System#currentTimeMillis() Now}.
225 *
226 * Choose a timestamp that will be most relevant to the user. For most finite events, this
227 * corresponds to the time the event happened (or will happen, in the case of events that have
228 * yet to occur but about which the user is being informed). Indefinite events should be
Joe Malin8d40d042012-11-05 11:36:40 -0800229 * timestamped according to when the activity began.
230 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500231 * Some examples:
Joe Malin8d40d042012-11-05 11:36:40 -0800232 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500233 * <ul>
234 * <li>Notification of a new chat message should be stamped when the message was received.</li>
235 * <li>Notification of an ongoing file download (with a progress bar, for example) should be stamped when the download started.</li>
236 * <li>Notification of a completed file download should be stamped when the download finished.</li>
237 * <li>Notification of an upcoming meeting should be stamped with the time the meeting will begin (that is, in the future).</li>
238 * <li>Notification of an ongoing stopwatch (increasing timer) should be stamped with the watch's start time.
239 * <li>Notification of an ongoing countdown timer should be stamped with the timer's end time.
Joe Malin8d40d042012-11-05 11:36:40 -0800240 * </ul>
241 *
Selim Cinek0ff1ce602016-04-05 18:27:16 -0700242 * For apps targeting {@link android.os.Build.VERSION_CODES#N} and above, this time is not shown
243 * anymore by default and must be opted into by using
244 * {@link android.app.Notification.Builder#setShowWhen(boolean)}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800245 */
246 public long when;
247
248 /**
Selim Cinekb85f36fd2016-04-20 18:46:36 -0700249 * The creation time of the notification
250 */
251 private long creationTime;
252
253 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800254 * The resource id of a drawable to use as the icon in the status bar.
Dan Sandler86647982015-05-13 23:41:13 -0400255 *
256 * @deprecated Use {@link Builder#setSmallIcon(Icon)} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800257 */
Dan Sandler86647982015-05-13 23:41:13 -0400258 @Deprecated
Tor Norbye7b9c9122013-05-30 16:48:33 -0700259 @DrawableRes
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800260 public int icon;
261
262 /**
Joe Onorato46439ce2010-11-19 13:56:21 -0800263 * If the icon in the status bar is to have more than one level, you can set this. Otherwise,
264 * leave it at its default value of 0.
265 *
266 * @see android.widget.ImageView#setImageLevel
Griff Hazen959591e2014-05-15 22:26:18 -0700267 * @see android.graphics.drawable.Drawable#setLevel
Joe Onorato46439ce2010-11-19 13:56:21 -0800268 */
269 public int iconLevel;
270
271 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500272 * The number of events that this notification represents. For example, in a new mail
273 * notification, this could be the number of unread messages.
Joe Malin8d40d042012-11-05 11:36:40 -0800274 *
Julia Reynolds30331982017-04-27 10:12:50 -0400275 * The system may or may not use this field to modify the appearance of the notification.
Julia Reynolds13d898c2017-02-02 12:22:05 -0500276 * Starting with {@link android.os.Build.VERSION_CODES#O}, the number may be displayed as a
277 * badge icon in Launchers that support badging.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800278 */
Julia Reynolds30331982017-04-27 10:12:50 -0400279 public int number = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800280
281 /**
282 * The intent to execute when the expanded status entry is clicked. If
283 * this is an activity, it must include the
284 * {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} flag, which requires
Scott Main7aee61f2011-02-08 11:25:01 -0800285 * that you take care of task management as described in the
286 * <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back
Dianne Hackborn6ceca582012-01-10 15:24:26 -0800287 * Stack</a> document. In particular, make sure to read the notification section
288 * <a href="{@docRoot}guide/topics/ui/notifiers/notifications.html#HandlingNotifications">Handling
289 * Notifications</a> for the correct ways to launch an application from a
290 * notification.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800291 */
292 public PendingIntent contentIntent;
293
294 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500295 * The intent to execute when the notification is explicitly dismissed by the user, either with
296 * the "Clear All" button or by swiping it away individually.
297 *
298 * This probably shouldn't be launching an activity since several of those will be sent
299 * at the same time.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800300 */
301 public PendingIntent deleteIntent;
302
303 /**
Dianne Hackborn170bae72010-09-03 15:14:28 -0700304 * An intent to launch instead of posting the notification to the status bar.
Joe Onoratocb109a02011-01-18 17:57:41 -0800305 *
Chris Wren47c20a12014-06-18 17:27:29 -0400306 * <p>
307 * The system UI may choose to display a heads-up notification, instead of
308 * launching this intent, while the user is using the device.
309 * </p>
310 *
Joe Onoratocb109a02011-01-18 17:57:41 -0800311 * @see Notification.Builder#setFullScreenIntent
Daniel Sandlere46cbd32010-06-17 10:35:26 -0400312 */
313 public PendingIntent fullScreenIntent;
314
315 /**
Dan Sandler5fcdf6e2014-07-18 11:31:15 -0400316 * Text that summarizes this notification for accessibility services.
317 *
318 * As of the L release, this text is no longer shown on screen, but it is still useful to
319 * accessibility services (where it serves as an audible announcement of the notification's
320 * appearance).
Joe Onoratoef1e7762010-09-17 18:38:38 -0400321 *
Joe Onorato46439ce2010-11-19 13:56:21 -0800322 * @see #tickerView
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800323 */
324 public CharSequence tickerText;
325
326 /**
Dan Sandler5fcdf6e2014-07-18 11:31:15 -0400327 * Formerly, a view showing the {@link #tickerText}.
328 *
329 * No longer displayed in the status bar as of API 21.
Joe Onoratoef1e7762010-09-17 18:38:38 -0400330 */
Dan Sandler5fcdf6e2014-07-18 11:31:15 -0400331 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -0800332 public RemoteViews tickerView;
Joe Onoratoef1e7762010-09-17 18:38:38 -0400333
334 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400335 * The view that will represent this notification in the notification list (which is pulled
336 * down from the status bar).
337 *
Julia Reynoldsd4ea7412016-02-17 14:00:56 -0500338 * As of N, this field may be null. The notification view is determined by the inputs
339 * to {@link Notification.Builder}; a custom RemoteViews can optionally be
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400340 * supplied with {@link Notification.Builder#setCustomContentView(RemoteViews)}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800341 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400342 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800343 public RemoteViews contentView;
344
Daniel Sandlera0a938c2012-03-15 08:42:37 -0400345 /**
Daniel Sandler4dfbe832012-04-11 14:51:46 -0400346 * A large-format version of {@link #contentView}, giving the Notification an
Daniel Sandlerf3b73432012-03-27 15:01:25 -0400347 * opportunity to show more detail. The system UI may choose to show this
348 * instead of the normal content view at its discretion.
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400349 *
Julia Reynoldsd4ea7412016-02-17 14:00:56 -0500350 * As of N, this field may be null. The expanded notification view is determined by the
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400351 * inputs to {@link Notification.Builder}; a custom RemoteViews can optionally be
352 * supplied with {@link Notification.Builder#setCustomBigContentView(RemoteViews)}.
Daniel Sandlerf3b73432012-03-27 15:01:25 -0400353 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400354 @Deprecated
Daniel Sandlerf3b73432012-03-27 15:01:25 -0400355 public RemoteViews bigContentView;
356
Chris Wren8fd39ec2014-02-27 17:43:26 -0500357
358 /**
Chris Wren47c20a12014-06-18 17:27:29 -0400359 * A medium-format version of {@link #contentView}, providing the Notification an
360 * opportunity to add action buttons to contentView. At its discretion, the system UI may
361 * choose to show this as a heads-up notification, which will pop up so the user can see
362 * it without leaving their current activity.
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400363 *
Julia Reynoldsd4ea7412016-02-17 14:00:56 -0500364 * As of N, this field may be null. The heads-up notification view is determined by the
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400365 * inputs to {@link Notification.Builder}; a custom RemoteViews can optionally be
366 * supplied with {@link Notification.Builder#setCustomHeadsUpContentView(RemoteViews)}.
Chris Wren8fd39ec2014-02-27 17:43:26 -0500367 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400368 @Deprecated
Chris Wren8fd39ec2014-02-27 17:43:26 -0500369 public RemoteViews headsUpContentView;
370
Julia Reynoldsfc640012018-02-21 12:25:27 -0500371 private boolean mUsesStandardHeader;
372
373 private static final ArraySet<Integer> STANDARD_LAYOUTS = new ArraySet<>();
374 static {
375 STANDARD_LAYOUTS.add(R.layout.notification_template_material_base);
376 STANDARD_LAYOUTS.add(R.layout.notification_template_material_big_base);
377 STANDARD_LAYOUTS.add(R.layout.notification_template_material_big_picture);
378 STANDARD_LAYOUTS.add(R.layout.notification_template_material_big_text);
379 STANDARD_LAYOUTS.add(R.layout.notification_template_material_inbox);
380 STANDARD_LAYOUTS.add(R.layout.notification_template_material_messaging);
381 STANDARD_LAYOUTS.add(R.layout.notification_template_material_media);
382 STANDARD_LAYOUTS.add(R.layout.notification_template_material_big_media);
383 STANDARD_LAYOUTS.add(R.layout.notification_template_ambient_header);
384 STANDARD_LAYOUTS.add(R.layout.notification_template_header);
385 STANDARD_LAYOUTS.add(R.layout.notification_template_material_ambient);
386 }
387
Daniel Sandlerf3b73432012-03-27 15:01:25 -0400388 /**
Dan Sandler86647982015-05-13 23:41:13 -0400389 * A large bitmap to be shown in the notification content area.
390 *
391 * @deprecated Use {@link Builder#setLargeIcon(Icon)} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800392 */
Dan Sandler86647982015-05-13 23:41:13 -0400393 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -0800394 public Bitmap largeIcon;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800395
396 /**
397 * The sound to play.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500398 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800399 * <p>
Chris Wren47c20a12014-06-18 17:27:29 -0400400 * A notification that is noisy is more likely to be presented as a heads-up notification.
401 * </p>
402 *
403 * <p>
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500404 * To play the default notification sound, see {@link #defaults}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800405 * </p>
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500406 * @deprecated use {@link NotificationChannel#getSound()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800407 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500408 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800409 public Uri sound;
410
411 /**
412 * Use this constant as the value for audioStreamType to request that
413 * the default stream type for notifications be used. Currently the
Jeff Sharkey098d5802012-04-26 17:30:34 -0700414 * default stream type is {@link AudioManager#STREAM_NOTIFICATION}.
John Spurlockc0650f022014-07-19 13:22:39 -0400415 *
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500416 * @deprecated Use {@link NotificationChannel#getAudioAttributes()} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800417 */
Jean-Michel Trivi81f871e2014-08-06 16:32:38 -0700418 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800419 public static final int STREAM_DEFAULT = -1;
420
421 /**
422 * The audio stream type to use when playing the sound.
423 * Should be one of the STREAM_ constants from
424 * {@link android.media.AudioManager}.
John Spurlockc0650f022014-07-19 13:22:39 -0400425 *
426 * @deprecated Use {@link #audioAttributes} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800427 */
Jean-Michel Trivi81f871e2014-08-06 16:32:38 -0700428 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800429 public int audioStreamType = STREAM_DEFAULT;
430
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800431 /**
John Spurlockc0650f022014-07-19 13:22:39 -0400432 * The default value of {@link #audioAttributes}.
433 */
434 public static final AudioAttributes AUDIO_ATTRIBUTES_DEFAULT = new AudioAttributes.Builder()
435 .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
436 .setUsage(AudioAttributes.USAGE_NOTIFICATION)
437 .build();
438
439 /**
440 * The {@link AudioAttributes audio attributes} to use when playing the sound.
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500441 *
442 * @deprecated use {@link NotificationChannel#getAudioAttributes()} instead.
John Spurlockc0650f022014-07-19 13:22:39 -0400443 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500444 @Deprecated
John Spurlockc0650f022014-07-19 13:22:39 -0400445 public AudioAttributes audioAttributes = AUDIO_ATTRIBUTES_DEFAULT;
446
447 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500448 * The pattern with which to vibrate.
449 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800450 * <p>
451 * To vibrate the default pattern, see {@link #defaults}.
452 * </p>
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500453 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800454 * @see android.os.Vibrator#vibrate(long[],int)
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500455 * @deprecated use {@link NotificationChannel#getVibrationPattern()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800456 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500457 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800458 public long[] vibrate;
459
460 /**
461 * The color of the led. The hardware will do its best approximation.
462 *
463 * @see #FLAG_SHOW_LIGHTS
464 * @see #flags
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500465 * @deprecated use {@link NotificationChannel#shouldShowLights()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800466 */
Tor Norbye80756e32015-03-02 09:39:27 -0800467 @ColorInt
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500468 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800469 public int ledARGB;
470
471 /**
472 * The number of milliseconds for the LED to be on while it's flashing.
473 * The hardware will do its best approximation.
474 *
475 * @see #FLAG_SHOW_LIGHTS
476 * @see #flags
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500477 * @deprecated use {@link NotificationChannel#shouldShowLights()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800478 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500479 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800480 public int ledOnMS;
481
482 /**
483 * The number of milliseconds for the LED to be off while it's flashing.
484 * The hardware will do its best approximation.
485 *
486 * @see #FLAG_SHOW_LIGHTS
487 * @see #flags
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500488 *
489 * @deprecated use {@link NotificationChannel#shouldShowLights()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800490 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500491 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800492 public int ledOffMS;
493
494 /**
495 * Specifies which values should be taken from the defaults.
496 * <p>
497 * To set, OR the desired from {@link #DEFAULT_SOUND},
498 * {@link #DEFAULT_VIBRATE}, {@link #DEFAULT_LIGHTS}. For all default
499 * values, use {@link #DEFAULT_ALL}.
500 * </p>
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500501 *
502 * @deprecated use {@link NotificationChannel#getSound()} and
503 * {@link NotificationChannel#shouldShowLights()} and
504 * {@link NotificationChannel#shouldVibrate()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800505 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500506 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800507 public int defaults;
508
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800509 /**
510 * Bit to be bitwise-ored into the {@link #flags} field that should be
511 * set if you want the LED on for this notification.
512 * <ul>
513 * <li>To turn the LED off, pass 0 in the alpha channel for colorARGB
514 * or 0 for both ledOnMS and ledOffMS.</li>
515 * <li>To turn the LED on, pass 1 for ledOnMS and 0 for ledOffMS.</li>
516 * <li>To flash the LED, pass the number of milliseconds that it should
517 * be on and off to ledOnMS and ledOffMS.</li>
518 * </ul>
519 * <p>
520 * Since hardware varies, you are not guaranteed that any of the values
Ricardo Loo Forondaf8f6d0a2018-01-25 08:42:43 -0800521 * you pass are honored exactly. Use the system defaults if possible
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800522 * because they will be set to values that work on any given hardware.
523 * <p>
524 * The alpha channel must be set for forward compatibility.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500525 *
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500526 * @deprecated use {@link NotificationChannel#shouldShowLights()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800527 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500528 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800529 public static final int FLAG_SHOW_LIGHTS = 0x00000001;
530
531 /**
532 * Bit to be bitwise-ored into the {@link #flags} field that should be
533 * set if this notification is in reference to something that is ongoing,
534 * like a phone call. It should not be set if this notification is in
535 * reference to something that happened at a particular point in time,
536 * like a missed phone call.
537 */
538 public static final int FLAG_ONGOING_EVENT = 0x00000002;
539
540 /**
541 * Bit to be bitwise-ored into the {@link #flags} field that if set,
Scott Mainb8b36452009-04-26 15:50:49 -0700542 * the audio will be repeated until the notification is
543 * cancelled or the notification window is opened.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800544 */
545 public static final int FLAG_INSISTENT = 0x00000004;
546
547 /**
548 * Bit to be bitwise-ored into the {@link #flags} field that should be
Griff Hazen293977b2014-04-28 08:37:20 -0700549 * set if you would only like the sound, vibrate and ticker to be played
550 * if the notification was not already showing.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800551 */
552 public static final int FLAG_ONLY_ALERT_ONCE = 0x00000008;
553
554 /**
555 * Bit to be bitwise-ored into the {@link #flags} field that should be
556 * set if the notification should be canceled when it is clicked by the
Daniel Sandler8aa9ae62012-12-04 23:31:47 -0500557 * user.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800558 */
559 public static final int FLAG_AUTO_CANCEL = 0x00000010;
560
561 /**
562 * Bit to be bitwise-ored into the {@link #flags} field that should be
563 * set if the notification should not be canceled when the user clicks
564 * the Clear all button.
565 */
566 public static final int FLAG_NO_CLEAR = 0x00000020;
567
Dianne Hackbornd8a43f62009-08-17 23:33:56 -0700568 /**
569 * Bit to be bitwise-ored into the {@link #flags} field that should be
570 * set if this notification represents a currently running service. This
571 * will normally be set for you by {@link Service#startForeground}.
572 */
573 public static final int FLAG_FOREGROUND_SERVICE = 0x00000040;
574
Daniel Sandlere46cbd32010-06-17 10:35:26 -0400575 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500576 * Obsolete flag indicating high-priority notifications; use the priority field instead.
Joe Malin8d40d042012-11-05 11:36:40 -0800577 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500578 * @deprecated Use {@link #priority} with a positive value.
Daniel Sandlere46cbd32010-06-17 10:35:26 -0400579 */
Aurimas Liutikas514c5ef2016-05-24 15:22:55 -0700580 @Deprecated
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500581 public static final int FLAG_HIGH_PRIORITY = 0x00000080;
Daniel Sandlere46cbd32010-06-17 10:35:26 -0400582
Griff Hazendfcb0802014-02-11 12:00:00 -0800583 /**
584 * Bit to be bitswise-ored into the {@link #flags} field that should be
585 * set if this notification is relevant to the current device only
586 * and it is not recommended that it bridge to other devices.
587 */
588 public static final int FLAG_LOCAL_ONLY = 0x00000100;
589
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700590 /**
591 * Bit to be bitswise-ored into the {@link #flags} field that should be
592 * set if this notification is the group summary for a group of notifications.
593 * Grouped notifications may display in a cluster or stack on devices which
594 * support such rendering. Requires a group key also be set using {@link Builder#setGroup}.
595 */
596 public static final int FLAG_GROUP_SUMMARY = 0x00000200;
597
Julia Reynoldse46bb372016-03-17 11:05:58 -0400598 /**
599 * Bit to be bitswise-ored into the {@link #flags} field that should be
600 * set if this notification is the group summary for an auto-group of notifications.
601 *
602 * @hide
603 */
604 @SystemApi
605 public static final int FLAG_AUTOGROUP_SUMMARY = 0x00000400;
606
Julia Reynolds4db59552017-06-30 13:34:01 -0400607 /**
608 * @hide
609 */
610 public static final int FLAG_CAN_COLORIZE = 0x00000800;
611
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800612 public int flags;
613
Tor Norbyed9273d62013-05-30 15:59:53 -0700614 /** @hide */
Jeff Sharkeyce8db992017-12-13 20:05:05 -0700615 @IntDef(prefix = { "PRIORITY_" }, value = {
616 PRIORITY_DEFAULT,
617 PRIORITY_LOW,
618 PRIORITY_MIN,
619 PRIORITY_HIGH,
620 PRIORITY_MAX
621 })
Tor Norbyed9273d62013-05-30 15:59:53 -0700622 @Retention(RetentionPolicy.SOURCE)
623 public @interface Priority {}
624
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800625 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500626 * Default notification {@link #priority}. If your application does not prioritize its own
627 * notifications, use this value for all notifications.
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500628 *
629 * @deprecated use {@link NotificationManager#IMPORTANCE_DEFAULT} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500630 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500631 @Deprecated
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500632 public static final int PRIORITY_DEFAULT = 0;
633
634 /**
635 * Lower {@link #priority}, for items that are less important. The UI may choose to show these
636 * items smaller, or at a different position in the list, compared with your app's
637 * {@link #PRIORITY_DEFAULT} items.
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500638 *
639 * @deprecated use {@link NotificationManager#IMPORTANCE_LOW} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500640 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500641 @Deprecated
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500642 public static final int PRIORITY_LOW = -1;
643
644 /**
645 * Lowest {@link #priority}; these items might not be shown to the user except under special
646 * circumstances, such as detailed notification logs.
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500647 *
648 * @deprecated use {@link NotificationManager#IMPORTANCE_MIN} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500649 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500650 @Deprecated
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500651 public static final int PRIORITY_MIN = -2;
652
653 /**
654 * Higher {@link #priority}, for more important notifications or alerts. The UI may choose to
655 * show these items larger, or at a different position in notification lists, compared with
656 * your app's {@link #PRIORITY_DEFAULT} items.
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500657 *
658 * @deprecated use {@link NotificationManager#IMPORTANCE_HIGH} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500659 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500660 @Deprecated
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500661 public static final int PRIORITY_HIGH = 1;
662
663 /**
664 * Highest {@link #priority}, for your application's most important items that require the
665 * user's prompt attention or input.
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500666 *
667 * @deprecated use {@link NotificationManager#IMPORTANCE_HIGH} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500668 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500669 @Deprecated
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500670 public static final int PRIORITY_MAX = 2;
671
672 /**
673 * Relative priority for this notification.
Joe Malin8d40d042012-11-05 11:36:40 -0800674 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500675 * Priority is an indication of how much of the user's valuable attention should be consumed by
676 * this notification. Low-priority notifications may be hidden from the user in certain
677 * situations, while the user might be interrupted for a higher-priority notification. The
Daniel Sandler6738eee2012-11-16 12:03:32 -0500678 * system will make a determination about how to interpret this priority when presenting
679 * the notification.
Chris Wren47c20a12014-06-18 17:27:29 -0400680 *
681 * <p>
682 * A notification that is at least {@link #PRIORITY_HIGH} is more likely to be presented
683 * as a heads-up notification.
684 * </p>
685 *
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500686 * @deprecated use {@link NotificationChannel#getImportance()} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500687 */
Tor Norbyed9273d62013-05-30 15:59:53 -0700688 @Priority
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500689 @Deprecated
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500690 public int priority;
Joe Malin8d40d042012-11-05 11:36:40 -0800691
Dan Sandler26e81cf2014-05-06 10:01:27 -0400692 /**
693 * Accent color (an ARGB integer like the constants in {@link android.graphics.Color})
694 * to be applied by the standard Style templates when presenting this notification.
695 *
696 * The current template design constructs a colorful header image by overlaying the
697 * {@link #icon} image (stenciled in white) atop a field of this color. Alpha components are
698 * ignored.
699 */
Tor Norbye80756e32015-03-02 09:39:27 -0800700 @ColorInt
Dan Sandler26e81cf2014-05-06 10:01:27 -0400701 public int color = COLOR_DEFAULT;
702
703 /**
704 * Special value of {@link #color} telling the system not to decorate this notification with
705 * any special color but instead use default colors when presenting this notification.
706 */
Tor Norbye80756e32015-03-02 09:39:27 -0800707 @ColorInt
Dan Sandler26e81cf2014-05-06 10:01:27 -0400708 public static final int COLOR_DEFAULT = 0; // AKA Color.TRANSPARENT
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600709
710 /**
Adrian Roos4ff3b122016-02-01 12:26:13 -0800711 * Special value of {@link #color} used as a place holder for an invalid color.
Selim Cinek99104832017-01-25 14:47:33 -0800712 * @hide
Adrian Roos4ff3b122016-02-01 12:26:13 -0800713 */
714 @ColorInt
Selim Cinek99104832017-01-25 14:47:33 -0800715 public static final int COLOR_INVALID = 1;
Adrian Roos4ff3b122016-02-01 12:26:13 -0800716
717 /**
Adrian Roosc1a80b02016-04-05 14:54:55 -0700718 * Sphere of visibility of this notification, which affects how and when the SystemUI reveals
719 * the notification's presence and contents in untrusted situations (namely, on the secure
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600720 * lockscreen).
721 *
722 * The default level, {@link #VISIBILITY_PRIVATE}, behaves exactly as notifications have always
723 * done on Android: The notification's {@link #icon} and {@link #tickerText} (if available) are
724 * shown in all situations, but the contents are only available if the device is unlocked for
725 * the appropriate user.
726 *
727 * A more permissive policy can be expressed by {@link #VISIBILITY_PUBLIC}; such a notification
728 * can be read even in an "insecure" context (that is, above a secure lockscreen).
729 * To modify the public version of this notification—for example, to redact some portions—see
730 * {@link Builder#setPublicVersion(Notification)}.
731 *
732 * Finally, a notification can be made {@link #VISIBILITY_SECRET}, which will suppress its icon
733 * and ticker until the user has bypassed the lockscreen.
734 */
Jeff Sharkey30e06bb2017-04-24 11:18:03 -0600735 public @Visibility int visibility;
736
737 /** @hide */
738 @IntDef(prefix = { "VISIBILITY_" }, value = {
739 VISIBILITY_PUBLIC,
740 VISIBILITY_PRIVATE,
741 VISIBILITY_SECRET,
742 })
743 @Retention(RetentionPolicy.SOURCE)
744 public @interface Visibility {}
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600745
Griff Hazenfc3922d2014-08-20 11:56:44 -0700746 /**
747 * Notification visibility: Show this notification in its entirety on all lockscreens.
748 *
749 * {@see #visibility}
750 */
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600751 public static final int VISIBILITY_PUBLIC = 1;
Griff Hazenfc3922d2014-08-20 11:56:44 -0700752
753 /**
754 * Notification visibility: Show this notification on all lockscreens, but conceal sensitive or
755 * private information on secure lockscreens.
756 *
757 * {@see #visibility}
758 */
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600759 public static final int VISIBILITY_PRIVATE = 0;
Griff Hazenfc3922d2014-08-20 11:56:44 -0700760
761 /**
762 * Notification visibility: Do not reveal any part of this notification on a secure lockscreen.
763 *
764 * {@see #visibility}
765 */
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600766 public static final int VISIBILITY_SECRET = -1;
767
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500768 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400769 * Notification category: incoming call (voice or video) or similar synchronous communication request.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500770 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400771 public static final String CATEGORY_CALL = "call";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500772
773 /**
Adora Zhangaa90e872018-03-12 14:07:50 -0700774 * Notification category: map turn-by-turn navigation.
775 */
776 public static final String CATEGORY_NAVIGATION = "navigation";
777
778 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400779 * Notification category: incoming direct message (SMS, instant message, etc.).
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500780 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400781 public static final String CATEGORY_MESSAGE = "msg";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500782
783 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400784 * Notification category: asynchronous bulk message (email).
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500785 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400786 public static final String CATEGORY_EMAIL = "email";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500787
788 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400789 * Notification category: calendar event.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500790 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400791 public static final String CATEGORY_EVENT = "event";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500792
793 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400794 * Notification category: promotion or advertisement.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500795 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400796 public static final String CATEGORY_PROMO = "promo";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500797
798 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400799 * Notification category: alarm or timer.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500800 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400801 public static final String CATEGORY_ALARM = "alarm";
802
803 /**
804 * Notification category: progress of a long-running background operation.
805 */
806 public static final String CATEGORY_PROGRESS = "progress";
807
808 /**
809 * Notification category: social network or sharing update.
810 */
811 public static final String CATEGORY_SOCIAL = "social";
812
813 /**
814 * Notification category: error in background operation or authentication status.
815 */
816 public static final String CATEGORY_ERROR = "err";
817
818 /**
819 * Notification category: media transport control for playback.
820 */
821 public static final String CATEGORY_TRANSPORT = "transport";
822
823 /**
824 * Notification category: system or device status update. Reserved for system use.
825 */
826 public static final String CATEGORY_SYSTEM = "sys";
827
828 /**
829 * Notification category: indication of running background service.
830 */
831 public static final String CATEGORY_SERVICE = "service";
832
833 /**
John Spurlock0a69c8c2014-03-21 13:30:57 -0400834 * Notification category: a specific, timely recommendation for a single thing.
835 * For example, a news app might want to recommend a news story it believes the user will
836 * want to read next.
837 */
838 public static final String CATEGORY_RECOMMENDATION = "recommendation";
839
840 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400841 * Notification category: ongoing information about device or contextual status.
842 */
843 public static final String CATEGORY_STATUS = "status";
844
845 /**
John Spurlock24d3dad2015-04-02 12:24:02 -0400846 * Notification category: user-scheduled reminder.
847 */
848 public static final String CATEGORY_REMINDER = "reminder";
849
850 /**
Adora Zhangaa90e872018-03-12 14:07:50 -0700851 * Notification category: extreme car emergencies.
852 * @hide
853 */
854 @SystemApi
855 public static final String CATEGORY_CAR_EMERGENCY = "car_emergency";
856
857 /**
858 * Notification category: car warnings.
859 * @hide
860 */
861 @SystemApi
862 public static final String CATEGORY_CAR_WARNING = "car_warning";
863
864 /**
865 * Notification category: general car system information.
866 * @hide
867 */
868 @SystemApi
869 public static final String CATEGORY_CAR_INFORMATION = "car_information";
870
871 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400872 * One of the predefined notification categories (see the <code>CATEGORY_*</code> constants)
873 * that best describes this Notification. May be used by the system for ranking and filtering.
874 */
875 public String category;
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500876
Mathew Inwood61e8ae62018-08-14 14:17:44 +0100877 @UnsupportedAppUsage
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700878 private String mGroupKey;
879
880 /**
881 * Get the key used to group this notification into a cluster or stack
882 * with other notifications on devices which support such rendering.
883 */
884 public String getGroup() {
885 return mGroupKey;
886 }
887
888 private String mSortKey;
889
890 /**
891 * Get a sort key that orders this notification among other notifications from the
892 * same package. This can be useful if an external sort was already applied and an app
893 * would like to preserve this. Notifications will be sorted lexicographically using this
894 * value, although providing different priorities in addition to providing sort key may
895 * cause this value to be ignored.
896 *
897 * <p>This sort key can also be used to order members of a notification group. See
898 * {@link Builder#setGroup}.
899 *
900 * @see String#compareTo(String)
901 */
902 public String getSortKey() {
903 return mSortKey;
904 }
905
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500906 /**
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400907 * Additional semantic data to be carried around with this Notification.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400908 * <p>
909 * The extras keys defined here are intended to capture the original inputs to {@link Builder}
910 * APIs, and are intended to be used by
911 * {@link android.service.notification.NotificationListenerService} implementations to extract
912 * detailed information from notification objects.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500913 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400914 public Bundle extras = new Bundle();
915
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400916 /**
Felipe Lemedd85da62016-06-28 11:29:54 -0700917 * All pending intents in the notification as the system needs to be able to access them but
918 * touching the extras bundle in the system process is not safe because the bundle may contain
Svet Ganovddb94882016-06-23 19:55:24 -0700919 * custom parcelable objects.
920 *
921 * @hide
922 */
Mathew Inwood61e8ae62018-08-14 14:17:44 +0100923 @UnsupportedAppUsage
Felipe Lemedd85da62016-06-28 11:29:54 -0700924 public ArraySet<PendingIntent> allPendingIntents;
Svet Ganovddb94882016-06-23 19:55:24 -0700925
926 /**
Dianne Hackborn98305522017-05-05 17:53:53 -0700927 * Token identifying the notification that is applying doze/bgcheck whitelisting to the
928 * pending intents inside of it, so only those will get the behavior.
929 *
930 * @hide
931 */
Adrian Roosfb921842017-10-26 14:49:56 +0200932 private IBinder mWhitelistToken;
Dianne Hackborn98305522017-05-05 17:53:53 -0700933
934 /**
935 * Must be set by a process to start associating tokens with Notification objects
936 * coming in to it. This is set by NotificationManagerService.
937 *
938 * @hide
939 */
940 static public IBinder processWhitelistToken;
941
942 /**
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400943 * {@link #extras} key: this is the title of the notification,
944 * as supplied to {@link Builder#setContentTitle(CharSequence)}.
945 */
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -0500946 public static final String EXTRA_TITLE = "android.title";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400947
948 /**
949 * {@link #extras} key: this is the title of the notification when shown in expanded form,
950 * e.g. as supplied to {@link BigTextStyle#setBigContentTitle(CharSequence)}.
951 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400952 public static final String EXTRA_TITLE_BIG = EXTRA_TITLE + ".big";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400953
954 /**
955 * {@link #extras} key: this is the main text payload, as supplied to
956 * {@link Builder#setContentText(CharSequence)}.
957 */
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -0500958 public static final String EXTRA_TEXT = "android.text";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400959
960 /**
961 * {@link #extras} key: this is a third line of text, as supplied to
962 * {@link Builder#setSubText(CharSequence)}.
963 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400964 public static final String EXTRA_SUB_TEXT = "android.subText";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400965
966 /**
Adrian Roose458aa82015-12-08 16:17:19 -0800967 * {@link #extras} key: this is the remote input history, as supplied to
968 * {@link Builder#setRemoteInputHistory(CharSequence[])}.
Adrian Roos005e7742016-04-13 15:02:20 -0700969 *
970 * Apps can fill this through {@link Builder#setRemoteInputHistory(CharSequence[])}
971 * with the most recent inputs that have been sent through a {@link RemoteInput} of this
972 * Notification and are expected to clear it once the it is no longer relevant (e.g. for chat
973 * notifications once the other party has responded).
974 *
975 * The extra with this key is of type CharSequence[] and contains the most recent entry at
976 * the 0 index, the second most recent at the 1 index, etc.
977 *
978 * @see Builder#setRemoteInputHistory(CharSequence[])
Adrian Roose458aa82015-12-08 16:17:19 -0800979 */
980 public static final String EXTRA_REMOTE_INPUT_HISTORY = "android.remoteInputHistory";
981
982 /**
Kenny Guya0f6de82018-04-06 16:20:16 +0100983 * {@link #extras} key: boolean as supplied to
984 * {@link Builder#setShowRemoteInputSpinner(boolean)}.
985 *
986 * If set to true, then the view displaying the remote input history from
987 * {@link Builder#setRemoteInputHistory(CharSequence[])} will have a progress spinner.
988 *
989 * @see Builder#setShowRemoteInputSpinner(boolean)
990 * @hide
991 */
992 public static final String EXTRA_SHOW_REMOTE_INPUT_SPINNER = "android.remoteInputSpinner";
993
994 /**
Kenny Guy8cc15d22018-05-09 09:50:55 +0100995 * {@link #extras} key: boolean as supplied to
996 * {@link Builder#setHideSmartReplies(boolean)}.
997 *
998 * If set to true, then any smart reply buttons will be hidden.
999 *
1000 * @see Builder#setHideSmartReplies(boolean)
1001 * @hide
1002 */
1003 public static final String EXTRA_HIDE_SMART_REPLIES = "android.hideSmartReplies";
1004
1005 /**
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001006 * {@link #extras} key: this is a small piece of additional text as supplied to
1007 * {@link Builder#setContentInfo(CharSequence)}.
1008 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -04001009 public static final String EXTRA_INFO_TEXT = "android.infoText";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001010
1011 /**
1012 * {@link #extras} key: this is a line of summary information intended to be shown
1013 * alongside expanded notifications, as supplied to (e.g.)
1014 * {@link BigTextStyle#setSummaryText(CharSequence)}.
1015 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -04001016 public static final String EXTRA_SUMMARY_TEXT = "android.summaryText";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001017
1018 /**
Christoph Studer4600f9b2014-07-22 22:44:43 +02001019 * {@link #extras} key: this is the longer text shown in the big form of a
1020 * {@link BigTextStyle} notification, as supplied to
1021 * {@link BigTextStyle#bigText(CharSequence)}.
1022 */
1023 public static final String EXTRA_BIG_TEXT = "android.bigText";
1024
1025 /**
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001026 * {@link #extras} key: this is the resource ID of the notification's main small icon, as
1027 * supplied to {@link Builder#setSmallIcon(int)}.
Julia Reynoldse071abd2017-03-22 10:52:11 -04001028 *
1029 * @deprecated Use {@link #getSmallIcon()}, which supports a wider variety of icon sources.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001030 */
Julia Reynoldse071abd2017-03-22 10:52:11 -04001031 @Deprecated
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001032 public static final String EXTRA_SMALL_ICON = "android.icon";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001033
1034 /**
1035 * {@link #extras} key: this is a bitmap to be used instead of the small icon when showing the
1036 * notification payload, as
1037 * supplied to {@link Builder#setLargeIcon(android.graphics.Bitmap)}.
Julia Reynoldse071abd2017-03-22 10:52:11 -04001038 *
1039 * @deprecated Use {@link #getLargeIcon()}, which supports a wider variety of icon sources.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001040 */
Julia Reynoldse071abd2017-03-22 10:52:11 -04001041 @Deprecated
Daniel Sandlerf45564e2013-04-15 15:05:08 -04001042 public static final String EXTRA_LARGE_ICON = "android.largeIcon";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001043
1044 /**
1045 * {@link #extras} key: this is a bitmap to be used instead of the one from
1046 * {@link Builder#setLargeIcon(android.graphics.Bitmap)} when the notification is
1047 * shown in its expanded form, as supplied to
1048 * {@link BigPictureStyle#bigLargeIcon(android.graphics.Bitmap)}.
1049 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -04001050 public static final String EXTRA_LARGE_ICON_BIG = EXTRA_LARGE_ICON + ".big";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001051
1052 /**
1053 * {@link #extras} key: this is the progress value supplied to
1054 * {@link Builder#setProgress(int, int, boolean)}.
1055 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -04001056 public static final String EXTRA_PROGRESS = "android.progress";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001057
1058 /**
1059 * {@link #extras} key: this is the maximum value supplied to
1060 * {@link Builder#setProgress(int, int, boolean)}.
1061 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -04001062 public static final String EXTRA_PROGRESS_MAX = "android.progressMax";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001063
1064 /**
1065 * {@link #extras} key: whether the progress bar is indeterminate, supplied to
1066 * {@link Builder#setProgress(int, int, boolean)}.
1067 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -04001068 public static final String EXTRA_PROGRESS_INDETERMINATE = "android.progressIndeterminate";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001069
1070 /**
1071 * {@link #extras} key: whether {@link #when} should be shown as a count-up timer (specifically
1072 * a {@link android.widget.Chronometer}) instead of a timestamp, as supplied to
1073 * {@link Builder#setUsesChronometer(boolean)}.
1074 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -04001075 public static final String EXTRA_SHOW_CHRONOMETER = "android.showChronometer";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001076
1077 /**
Selim Cinek81c23aa2016-02-25 16:23:13 -08001078 * {@link #extras} key: whether the chronometer set on the notification should count down
1079 * instead of counting up. Is only relevant if key {@link #EXTRA_SHOW_CHRONOMETER} is present.
Adrian Roos96b7e202016-05-17 13:50:38 -07001080 * This extra is a boolean. The default is false.
Selim Cinek81c23aa2016-02-25 16:23:13 -08001081 */
Adrian Roos96b7e202016-05-17 13:50:38 -07001082 public static final String EXTRA_CHRONOMETER_COUNT_DOWN = "android.chronometerCountDown";
Selim Cinek81c23aa2016-02-25 16:23:13 -08001083
1084 /**
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001085 * {@link #extras} key: whether {@link #when} should be shown,
1086 * as supplied to {@link Builder#setShowWhen(boolean)}.
1087 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -04001088 public static final String EXTRA_SHOW_WHEN = "android.showWhen";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001089
1090 /**
1091 * {@link #extras} key: this is a bitmap to be shown in {@link BigPictureStyle} expanded
1092 * notifications, supplied to {@link BigPictureStyle#bigPicture(android.graphics.Bitmap)}.
1093 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -04001094 public static final String EXTRA_PICTURE = "android.picture";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001095
1096 /**
1097 * {@link #extras} key: An array of CharSequences to show in {@link InboxStyle} expanded
1098 * notifications, each of which was supplied to {@link InboxStyle#addLine(CharSequence)}.
1099 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -04001100 public static final String EXTRA_TEXT_LINES = "android.textLines";
Dan Sandler842dd772014-05-15 09:36:47 -04001101
1102 /**
1103 * {@link #extras} key: A string representing the name of the specific
1104 * {@link android.app.Notification.Style} used to create this notification.
1105 */
Chris Wren91ad5632013-06-05 15:05:57 -04001106 public static final String EXTRA_TEMPLATE = "android.template";
Daniel Sandlerf45564e2013-04-15 15:05:08 -04001107
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001108 /**
Chris Wrene6c48932014-09-29 17:19:27 -04001109 * {@link #extras} key: A String array containing the people that this notification relates to,
1110 * each of which was supplied to {@link Builder#addPerson(String)}.
Selim Cineke7238dd2017-12-14 17:48:32 -08001111 *
1112 * @deprecated the actual objects are now in {@link #EXTRA_PEOPLE_LIST}
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001113 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -04001114 public static final String EXTRA_PEOPLE = "android.people";
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001115
1116 /**
Selim Cineke7238dd2017-12-14 17:48:32 -08001117 * {@link #extras} key: An arrayList of {@link Person} objects containing the people that
1118 * this notification relates to.
1119 */
1120 public static final String EXTRA_PEOPLE_LIST = "android.people.list";
1121
1122 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -04001123 * Allow certain system-generated notifications to appear before the device is provisioned.
1124 * Only available to notifications coming from the android package.
1125 * @hide
1126 */
Maurice Lam96c10032017-03-29 15:42:38 -07001127 @SystemApi
Jeff Sharkeyd86b8fe2017-06-02 17:36:26 -06001128 @RequiresPermission(android.Manifest.permission.NOTIFICATION_DURING_SETUP)
John Spurlockfd7f1e02014-03-18 16:41:57 -04001129 public static final String EXTRA_ALLOW_DURING_SETUP = "android.allowDuringSetup";
1130
1131 /**
Robin Leed107af62018-04-27 13:55:56 +02001132 * {@link #extras} key:
1133 * flat {@link String} representation of a {@link android.content.ContentUris content URI}
1134 * pointing to an image that can be displayed in the background when the notification is
1135 * selected. Used on television platforms. The URI must point to an image stream suitable for
1136 * passing into {@link android.graphics.BitmapFactory#decodeStream(java.io.InputStream)
Julia Reynoldse0d711f2017-09-01 08:50:47 -04001137 * BitmapFactory.decodeStream}; all other content types will be ignored.
Jose Limae9e3b3b2014-05-18 23:44:50 -07001138 */
1139 public static final String EXTRA_BACKGROUND_IMAGE_URI = "android.backgroundImageUri";
1140
1141 /**
Dan Sandler842dd772014-05-15 09:36:47 -04001142 * {@link #extras} key: A
Jeff Browndba34ba2014-06-24 20:46:03 -07001143 * {@link android.media.session.MediaSession.Token} associated with a
Dan Sandler842dd772014-05-15 09:36:47 -04001144 * {@link android.app.Notification.MediaStyle} notification.
1145 */
1146 public static final String EXTRA_MEDIA_SESSION = "android.mediaSession";
1147
1148 /**
Bryan Mawhinneye191f902014-07-22 12:50:09 +01001149 * {@link #extras} key: the indices of actions to be shown in the compact view,
1150 * as supplied to (e.g.) {@link MediaStyle#setShowActionsInCompactView(int...)}.
1151 */
1152 public static final String EXTRA_COMPACT_ACTIONS = "android.compactActions";
1153
Christoph Studer943aa672014-08-03 20:31:16 +02001154 /**
Alex Hillsfc737de2016-03-23 17:33:02 -04001155 * {@link #extras} key: the username to be displayed for all messages sent by the user including
1156 * direct replies
Adrian Roos96b7e202016-05-17 13:50:38 -07001157 * {@link android.app.Notification.MessagingStyle} notification. This extra is a
1158 * {@link CharSequence}
Selim Cinekcb8b9852017-12-15 18:01:52 -08001159 *
1160 * @deprecated use {@link #EXTRA_MESSAGING_PERSON}
Alex Hillsfc737de2016-03-23 17:33:02 -04001161 */
1162 public static final String EXTRA_SELF_DISPLAY_NAME = "android.selfDisplayName";
1163
1164 /**
Selim Cinekcb8b9852017-12-15 18:01:52 -08001165 * {@link #extras} key: the person to be displayed for all messages sent by the user including
1166 * direct replies
1167 * {@link android.app.Notification.MessagingStyle} notification. This extra is a
1168 * {@link Person}
1169 */
1170 public static final String EXTRA_MESSAGING_PERSON = "android.messagingUser";
1171
1172 /**
Adrian Roos96b7e202016-05-17 13:50:38 -07001173 * {@link #extras} key: a {@link CharSequence} to be displayed as the title to a conversation
Alex Hillsd9b04d92016-04-11 16:38:16 -04001174 * represented by a {@link android.app.Notification.MessagingStyle}
Alex Hillsfc737de2016-03-23 17:33:02 -04001175 */
Alex Hillsd9b04d92016-04-11 16:38:16 -04001176 public static final String EXTRA_CONVERSATION_TITLE = "android.conversationTitle";
Alex Hillsfc737de2016-03-23 17:33:02 -04001177
1178 /**
1179 * {@link #extras} key: an array of {@link android.app.Notification.MessagingStyle.Message}
1180 * bundles provided by a
Adrian Roos96b7e202016-05-17 13:50:38 -07001181 * {@link android.app.Notification.MessagingStyle} notification. This extra is a parcelable
1182 * array of bundles.
Alex Hillsfc737de2016-03-23 17:33:02 -04001183 */
1184 public static final String EXTRA_MESSAGES = "android.messages";
1185
1186 /**
Adrian Roos437cd562017-01-18 15:47:03 -08001187 * {@link #extras} key: an array of
1188 * {@link android.app.Notification.MessagingStyle#addHistoricMessage historic}
1189 * {@link android.app.Notification.MessagingStyle.Message} bundles provided by a
1190 * {@link android.app.Notification.MessagingStyle} notification. This extra is a parcelable
1191 * array of bundles.
1192 */
1193 public static final String EXTRA_HISTORIC_MESSAGES = "android.messages.historic";
1194
1195 /**
Kodlee Yin9ac617c2017-12-19 11:20:50 -08001196 * {@link #extras} key: whether the {@link android.app.Notification.MessagingStyle} notification
1197 * represents a group conversation.
1198 */
1199 public static final String EXTRA_IS_GROUP_CONVERSATION = "android.isGroupConversation";
1200
1201 /**
Selim Cinek7b9605b2017-01-19 17:36:00 -08001202 * {@link #extras} key: whether the notification should be colorized as
1203 * supplied to {@link Builder#setColorized(boolean)}}.
1204 */
1205 public static final String EXTRA_COLORIZED = "android.colorized";
1206
1207 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04001208 * @hide
1209 */
1210 public static final String EXTRA_BUILDER_APPLICATION_INFO = "android.appInfo";
1211
Selim Cinek247fa012016-02-18 09:50:48 -08001212 /**
1213 * @hide
1214 */
1215 public static final String EXTRA_CONTAINS_CUSTOM_VIEW = "android.contains.customView";
1216
Shane Brennan472a3b32016-12-12 15:28:10 -08001217 /**
Selim Cinekd0426622017-07-11 13:19:59 +02001218 * @hide
1219 */
1220 public static final String EXTRA_REDUCED_IMAGES = "android.reduced.images";
1221
1222 /**
Shane Brennan472a3b32016-12-12 15:28:10 -08001223 * {@link #extras} key: the audio contents of this notification.
1224 *
1225 * This is for use when rendering the notification on an audio-focused interface;
1226 * the audio contents are a complete sound sample that contains the contents/body of the
1227 * notification. This may be used in substitute of a Text-to-Speech reading of the
1228 * notification. For example if the notification represents a voice message this should point
1229 * to the audio of that message.
1230 *
1231 * The data stored under this key should be a String representation of a Uri that contains the
1232 * audio contents in one of the following formats: WAV, PCM 16-bit, AMR-WB.
1233 *
1234 * This extra is unnecessary if you are using {@code MessagingStyle} since each {@code Message}
1235 * has a field for holding data URI. That field can be used for audio.
1236 * See {@code Message#setData}.
1237 *
1238 * Example usage:
1239 * <pre>
1240 * {@code
1241 * Notification.Builder myBuilder = (build your Notification as normal);
1242 * myBuilder.getExtras().putString(EXTRA_AUDIO_CONTENTS_URI, myAudioUri.toString());
1243 * }
1244 * </pre>
1245 */
1246 public static final String EXTRA_AUDIO_CONTENTS_URI = "android.audioContents";
1247
Dan Sandler80eaa592016-04-14 23:34:54 -04001248 /** @hide */
1249 @SystemApi
Jeff Sharkeyd86b8fe2017-06-02 17:36:26 -06001250 @RequiresPermission(android.Manifest.permission.SUBSTITUTE_NOTIFICATION_APP_NAME)
Dan Sandler732bd6c2016-04-12 14:20:32 -04001251 public static final String EXTRA_SUBSTITUTE_APP_NAME = "android.substName";
1252
Dianne Hackbornfb5d4b52017-05-16 17:03:44 -07001253 /**
Julia Reynolds3c7de112018-03-28 09:33:13 -04001254 * This is set on the notifications shown by system_server about apps running foreground
1255 * services. It indicates that the notification should be shown
1256 * only if any of the given apps do not already have a properly tagged
1257 * {@link #FLAG_FOREGROUND_SERVICE} notification currently visible to the user.
1258 * This is a string array of all package names of the apps.
Dianne Hackbornfb5d4b52017-05-16 17:03:44 -07001259 * @hide
1260 */
1261 public static final String EXTRA_FOREGROUND_APPS = "android.foregroundApps";
1262
Mathew Inwood61e8ae62018-08-14 14:17:44 +01001263 @UnsupportedAppUsage
Dan Sandlerd63f9322015-05-06 15:18:49 -04001264 private Icon mSmallIcon;
Mathew Inwood61e8ae62018-08-14 14:17:44 +01001265 @UnsupportedAppUsage
Dan Sandlerd63f9322015-05-06 15:18:49 -04001266 private Icon mLargeIcon;
1267
Mathew Inwood61e8ae62018-08-14 14:17:44 +01001268 @UnsupportedAppUsage
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04001269 private String mChannelId;
Julia Reynolds2a128742016-11-28 14:29:25 -05001270 private long mTimeout;
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04001271
Julia Reynolds13d898c2017-02-02 12:22:05 -05001272 private String mShortcutId;
Julia Reynolds3aedded2017-03-31 14:42:09 -04001273 private CharSequence mSettingsText;
Julia Reynolds13d898c2017-02-02 12:22:05 -05001274
Julia Reynoldsa79c3712017-04-21 10:29:57 -04001275 /** @hide */
1276 @IntDef(prefix = { "GROUP_ALERT_" }, value = {
1277 GROUP_ALERT_ALL, GROUP_ALERT_CHILDREN, GROUP_ALERT_SUMMARY
1278 })
1279 @Retention(RetentionPolicy.SOURCE)
1280 public @interface GroupAlertBehavior {}
1281
1282 /**
1283 * Constant for {@link Builder#setGroupAlertBehavior(int)}, meaning that all notifications in a
1284 * group with sound or vibration ought to make sound or vibrate (respectively), so this
1285 * notification will not be muted when it is in a group.
1286 */
1287 public static final int GROUP_ALERT_ALL = 0;
1288
1289 /**
1290 * Constant for {@link Builder#setGroupAlertBehavior(int)}, meaning that all children
1291 * notification in a group should be silenced (no sound or vibration) even if they are posted
1292 * to a {@link NotificationChannel} that has sound and/or vibration. Use this constant to
Julia Reynolds399d9bf2017-08-11 12:52:14 -04001293 * mute this notification if this notification is a group child. This must be applied to all
1294 * children notifications you want to mute.
Julia Reynoldsa79c3712017-04-21 10:29:57 -04001295 *
1296 * <p> For example, you might want to use this constant if you post a number of children
1297 * notifications at once (say, after a periodic sync), and only need to notify the user
1298 * audibly once.
1299 */
1300 public static final int GROUP_ALERT_SUMMARY = 1;
1301
1302 /**
1303 * Constant for {@link Builder#setGroupAlertBehavior(int)}, meaning that the summary
1304 * notification in a group should be silenced (no sound or vibration) even if they are
1305 * posted to a {@link NotificationChannel} that has sound and/or vibration. Use this constant
1306 * to mute this notification if this notification is a group summary.
1307 *
1308 * <p>For example, you might want to use this constant if only the children notifications
Julia Reynolds399d9bf2017-08-11 12:52:14 -04001309 * in your group have content and the summary is only used to visually group notifications
1310 * rather than to alert the user that new information is available.
Julia Reynoldsa79c3712017-04-21 10:29:57 -04001311 */
1312 public static final int GROUP_ALERT_CHILDREN = 2;
1313
Julia Reynolds2f431e22017-06-07 14:12:09 +00001314 private int mGroupAlertBehavior = GROUP_ALERT_ALL;
Julia Reynoldsa79c3712017-04-21 10:29:57 -04001315
Julia Reynolds13d898c2017-02-02 12:22:05 -05001316 /**
1317 * If this notification is being shown as a badge, always show as a number.
1318 */
1319 public static final int BADGE_ICON_NONE = 0;
1320
1321 /**
1322 * If this notification is being shown as a badge, use the {@link #getSmallIcon()} to
1323 * represent this notification.
1324 */
1325 public static final int BADGE_ICON_SMALL = 1;
1326
1327 /**
1328 * If this notification is being shown as a badge, use the {@link #getLargeIcon()} to
1329 * represent this notification.
1330 */
1331 public static final int BADGE_ICON_LARGE = 2;
Julia Reynolds7d5b4da2017-03-20 10:18:49 -04001332 private int mBadgeIcon = BADGE_ICON_NONE;
Julia Reynolds13d898c2017-02-02 12:22:05 -05001333
Chris Wren51c75102013-07-16 20:49:17 -04001334 /**
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001335 * Structure to encapsulate a named action that can be shown as part of this notification.
1336 * It must include an icon, a label, and a {@link PendingIntent} to be fired when the action is
1337 * selected by the user.
1338 * <p>
Griff Hazen959591e2014-05-15 22:26:18 -07001339 * Apps should use {@link Notification.Builder#addAction(int, CharSequence, PendingIntent)}
1340 * or {@link Notification.Builder#addAction(Notification.Action)}
1341 * to attach actions.
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001342 */
Daniel Sandlerea2a3172013-02-20 22:24:20 -05001343 public static class Action implements Parcelable {
Shane Brennan472a3b32016-12-12 15:28:10 -08001344 /**
1345 * {@link #extras} key: Keys to a {@link Parcelable} {@link ArrayList} of
1346 * {@link RemoteInput}s.
1347 *
1348 * This is intended for {@link RemoteInput}s that only accept data, meaning
1349 * {@link RemoteInput#getAllowFreeFormInput} is false, {@link RemoteInput#getChoices}
1350 * is null or empty, and {@link RemoteInput#getAllowedDataTypes is non-null and not
1351 * empty. These {@link RemoteInput}s will be ignored by devices that do not
1352 * support non-text-based {@link RemoteInput}s. See {@link Builder#build}.
1353 *
1354 * You can test if a RemoteInput matches these constraints using
1355 * {@link RemoteInput#isDataOnly}.
1356 */
1357 private static final String EXTRA_DATA_ONLY_INPUTS = "android.extra.DATA_ONLY_INPUTS";
1358
Kodlee Yinc72c44d2017-12-21 22:07:15 +00001359 /**
1360 * {@link }: No semantic action defined.
1361 */
1362 public static final int SEMANTIC_ACTION_NONE = 0;
1363
1364 /**
1365 * {@code SemanticAction}: Reply to a conversation, chat, group, or wherever replies
1366 * may be appropriate.
1367 */
1368 public static final int SEMANTIC_ACTION_REPLY = 1;
1369
1370 /**
1371 * {@code SemanticAction}: Mark content as read.
1372 */
1373 public static final int SEMANTIC_ACTION_MARK_AS_READ = 2;
1374
1375 /**
1376 * {@code SemanticAction}: Mark content as unread.
1377 */
1378 public static final int SEMANTIC_ACTION_MARK_AS_UNREAD = 3;
1379
1380 /**
1381 * {@code SemanticAction}: Delete the content associated with the notification. This
1382 * could mean deleting an email, message, etc.
1383 */
1384 public static final int SEMANTIC_ACTION_DELETE = 4;
1385
1386 /**
1387 * {@code SemanticAction}: Archive the content associated with the notification. This
1388 * could mean archiving an email, message, etc.
1389 */
1390 public static final int SEMANTIC_ACTION_ARCHIVE = 5;
1391
1392 /**
1393 * {@code SemanticAction}: Mute the content associated with the notification. This could
1394 * mean silencing a conversation or currently playing media.
1395 */
1396 public static final int SEMANTIC_ACTION_MUTE = 6;
1397
1398 /**
1399 * {@code SemanticAction}: Unmute the content associated with the notification. This could
1400 * mean un-silencing a conversation or currently playing media.
1401 */
1402 public static final int SEMANTIC_ACTION_UNMUTE = 7;
1403
1404 /**
1405 * {@code SemanticAction}: Mark content with a thumbs up.
1406 */
1407 public static final int SEMANTIC_ACTION_THUMBS_UP = 8;
1408
1409 /**
1410 * {@code SemanticAction}: Mark content with a thumbs down.
1411 */
1412 public static final int SEMANTIC_ACTION_THUMBS_DOWN = 9;
1413
Kodlee Yincda5b092018-02-15 15:34:53 -08001414 /**
1415 * {@code SemanticAction}: Call a contact, group, etc.
1416 */
1417 public static final int SEMANTIC_ACTION_CALL = 10;
Kodlee Yinc72c44d2017-12-21 22:07:15 +00001418
Griff Hazen959591e2014-05-15 22:26:18 -07001419 private final Bundle mExtras;
Mathew Inwood8c854f82018-09-14 12:35:36 +01001420 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
Dan Sandler86647982015-05-13 23:41:13 -04001421 private Icon mIcon;
Griff Hazen61a9e862014-05-22 16:05:19 -07001422 private final RemoteInput[] mRemoteInputs;
Alex Hills1f27f882017-01-12 11:24:46 -05001423 private boolean mAllowGeneratedReplies = true;
Kodlee Yinc72c44d2017-12-21 22:07:15 +00001424 private final @SemanticAction int mSemanticAction;
Griff Hazen959591e2014-05-15 22:26:18 -07001425
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001426 /**
1427 * Small icon representing the action.
Dan Sandler86647982015-05-13 23:41:13 -04001428 *
1429 * @deprecated Use {@link Action#getIcon()} instead.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001430 */
Dan Sandler86647982015-05-13 23:41:13 -04001431 @Deprecated
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001432 public int icon;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001433
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001434 /**
1435 * Title of the action.
1436 */
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001437 public CharSequence title;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001438
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001439 /**
1440 * Intent to send when the user invokes this action. May be null, in which case the action
1441 * may be rendered in a disabled presentation by the system UI.
1442 */
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001443 public PendingIntent actionIntent;
Griff Hazen959591e2014-05-15 22:26:18 -07001444
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001445 private Action(Parcel in) {
Dan Sandler86647982015-05-13 23:41:13 -04001446 if (in.readInt() != 0) {
1447 mIcon = Icon.CREATOR.createFromParcel(in);
Dan Sandler68079d52015-07-22 10:45:30 -04001448 if (mIcon.getType() == Icon.TYPE_RESOURCE) {
1449 icon = mIcon.getResId();
1450 }
Dan Sandler86647982015-05-13 23:41:13 -04001451 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001452 title = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
1453 if (in.readInt() == 1) {
1454 actionIntent = PendingIntent.CREATOR.createFromParcel(in);
1455 }
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001456 mExtras = Bundle.setDefusable(in.readBundle(), true);
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001457 mRemoteInputs = in.createTypedArray(RemoteInput.CREATOR);
Alex Hills42b0c4d2016-04-26 13:35:36 -04001458 mAllowGeneratedReplies = in.readInt() == 1;
Kodlee Yinc72c44d2017-12-21 22:07:15 +00001459 mSemanticAction = in.readInt();
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001460 }
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001461
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001462 /**
Dan Sandler86647982015-05-13 23:41:13 -04001463 * @deprecated Use {@link android.app.Notification.Action.Builder}.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001464 */
Dan Sandler86647982015-05-13 23:41:13 -04001465 @Deprecated
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001466 public Action(int icon, CharSequence title, PendingIntent intent) {
Kodlee Yinc72c44d2017-12-21 22:07:15 +00001467 this(Icon.createWithResource("", icon), title, intent, new Bundle(), null, true,
1468 SEMANTIC_ACTION_NONE);
Griff Hazen959591e2014-05-15 22:26:18 -07001469 }
1470
Adrian Roos7af53622016-10-12 13:44:05 -07001471 /** Keep in sync with {@link Notification.Action.Builder#Builder(Action)}! */
Dan Sandler86647982015-05-13 23:41:13 -04001472 private Action(Icon icon, CharSequence title, PendingIntent intent, Bundle extras,
Kodlee Yinc72c44d2017-12-21 22:07:15 +00001473 RemoteInput[] remoteInputs, boolean allowGeneratedReplies,
1474 @SemanticAction int semanticAction) {
Dan Sandler86647982015-05-13 23:41:13 -04001475 this.mIcon = icon;
Gus Prevasf5bff46f2015-08-24 10:34:28 -04001476 if (icon != null && icon.getType() == Icon.TYPE_RESOURCE) {
1477 this.icon = icon.getResId();
1478 }
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001479 this.title = title;
1480 this.actionIntent = intent;
Griff Hazen959591e2014-05-15 22:26:18 -07001481 this.mExtras = extras != null ? extras : new Bundle();
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001482 this.mRemoteInputs = remoteInputs;
Alex Hills42b0c4d2016-04-26 13:35:36 -04001483 this.mAllowGeneratedReplies = allowGeneratedReplies;
Kodlee Yinc72c44d2017-12-21 22:07:15 +00001484 this.mSemanticAction = semanticAction;
Griff Hazen959591e2014-05-15 22:26:18 -07001485 }
1486
1487 /**
Dan Sandler86647982015-05-13 23:41:13 -04001488 * Return an icon representing the action.
1489 */
1490 public Icon getIcon() {
1491 if (mIcon == null && icon != 0) {
1492 // you snuck an icon in here without using the builder; let's try to keep it
1493 mIcon = Icon.createWithResource("", icon);
1494 }
1495 return mIcon;
1496 }
1497
1498 /**
Griff Hazen959591e2014-05-15 22:26:18 -07001499 * Get additional metadata carried around with this Action.
1500 */
1501 public Bundle getExtras() {
1502 return mExtras;
1503 }
1504
1505 /**
Alex Hills42b0c4d2016-04-26 13:35:36 -04001506 * Return whether the platform should automatically generate possible replies for this
1507 * {@link Action}
1508 */
1509 public boolean getAllowGeneratedReplies() {
1510 return mAllowGeneratedReplies;
1511 }
1512
1513 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001514 * Get the list of inputs to be collected from the user when this action is sent.
Shane Brennan472a3b32016-12-12 15:28:10 -08001515 * May return null if no remote inputs were added. Only returns inputs which accept
1516 * a text input. For inputs which only accept data use {@link #getDataOnlyRemoteInputs}.
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001517 */
1518 public RemoteInput[] getRemoteInputs() {
1519 return mRemoteInputs;
1520 }
1521
1522 /**
Kodlee Yinc72c44d2017-12-21 22:07:15 +00001523 * Returns the {@code SemanticAction} associated with this {@link Action}. A
1524 * {@code SemanticAction} denotes what an {@link Action}'s {@link PendingIntent} will do
1525 * (eg. reply, mark as read, delete, etc).
1526 */
1527 public @SemanticAction int getSemanticAction() {
1528 return mSemanticAction;
1529 }
1530
1531 /**
Shane Brennan472a3b32016-12-12 15:28:10 -08001532 * Get the list of inputs to be collected from the user that ONLY accept data when this
1533 * action is sent. These remote inputs are guaranteed to return true on a call to
1534 * {@link RemoteInput#isDataOnly}.
1535 *
Shane Brennanf670d6c2017-04-25 13:05:45 -07001536 * Returns null if there are no data-only remote inputs.
Shane Brennan472a3b32016-12-12 15:28:10 -08001537 *
1538 * This method exists so that legacy RemoteInput collectors that pre-date the addition
1539 * of non-textual RemoteInputs do not access these remote inputs.
1540 */
1541 public RemoteInput[] getDataOnlyRemoteInputs() {
1542 return (RemoteInput[]) mExtras.getParcelableArray(EXTRA_DATA_ONLY_INPUTS);
1543 }
1544
1545 /**
Griff Hazen959591e2014-05-15 22:26:18 -07001546 * Builder class for {@link Action} objects.
1547 */
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001548 public static final class Builder {
Dan Sandler86647982015-05-13 23:41:13 -04001549 private final Icon mIcon;
Griff Hazen959591e2014-05-15 22:26:18 -07001550 private final CharSequence mTitle;
1551 private final PendingIntent mIntent;
Alex Hills1f27f882017-01-12 11:24:46 -05001552 private boolean mAllowGeneratedReplies = true;
Griff Hazen959591e2014-05-15 22:26:18 -07001553 private final Bundle mExtras;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001554 private ArrayList<RemoteInput> mRemoteInputs;
Kodlee Yinc72c44d2017-12-21 22:07:15 +00001555 private @SemanticAction int mSemanticAction;
Griff Hazen959591e2014-05-15 22:26:18 -07001556
1557 /**
1558 * Construct a new builder for {@link Action} object.
1559 * @param icon icon to show for this action
1560 * @param title the title of the action
1561 * @param intent the {@link PendingIntent} to fire when users trigger this action
1562 */
Dan Sandler86647982015-05-13 23:41:13 -04001563 @Deprecated
Griff Hazen959591e2014-05-15 22:26:18 -07001564 public Builder(int icon, CharSequence title, PendingIntent intent) {
Adrian Roos7af53622016-10-12 13:44:05 -07001565 this(Icon.createWithResource("", icon), title, intent);
Dan Sandler86647982015-05-13 23:41:13 -04001566 }
1567
1568 /**
1569 * Construct a new builder for {@link Action} object.
1570 * @param icon icon to show for this action
1571 * @param title the title of the action
1572 * @param intent the {@link PendingIntent} to fire when users trigger this action
1573 */
1574 public Builder(Icon icon, CharSequence title, PendingIntent intent) {
Kodlee Yinc72c44d2017-12-21 22:07:15 +00001575 this(icon, title, intent, new Bundle(), null, true, SEMANTIC_ACTION_NONE);
Griff Hazen959591e2014-05-15 22:26:18 -07001576 }
1577
1578 /**
1579 * Construct a new builder for {@link Action} object using the fields from an
1580 * {@link Action}.
1581 * @param action the action to read fields from.
1582 */
1583 public Builder(Action action) {
Adrian Roos7af53622016-10-12 13:44:05 -07001584 this(action.getIcon(), action.title, action.actionIntent,
1585 new Bundle(action.mExtras), action.getRemoteInputs(),
Kodlee Yinc72c44d2017-12-21 22:07:15 +00001586 action.getAllowGeneratedReplies(), action.getSemanticAction());
Griff Hazen959591e2014-05-15 22:26:18 -07001587 }
1588
Dan Sandler86647982015-05-13 23:41:13 -04001589 private Builder(Icon icon, CharSequence title, PendingIntent intent, Bundle extras,
Kodlee Yinc72c44d2017-12-21 22:07:15 +00001590 RemoteInput[] remoteInputs, boolean allowGeneratedReplies,
1591 @SemanticAction int semanticAction) {
Griff Hazen959591e2014-05-15 22:26:18 -07001592 mIcon = icon;
1593 mTitle = title;
1594 mIntent = intent;
1595 mExtras = extras;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001596 if (remoteInputs != null) {
1597 mRemoteInputs = new ArrayList<RemoteInput>(remoteInputs.length);
1598 Collections.addAll(mRemoteInputs, remoteInputs);
1599 }
Adrian Roos7af53622016-10-12 13:44:05 -07001600 mAllowGeneratedReplies = allowGeneratedReplies;
Kodlee Yinc72c44d2017-12-21 22:07:15 +00001601 mSemanticAction = semanticAction;
Griff Hazen959591e2014-05-15 22:26:18 -07001602 }
1603
1604 /**
1605 * Merge additional metadata into this builder.
1606 *
1607 * <p>Values within the Bundle will replace existing extras values in this Builder.
1608 *
1609 * @see Notification.Action#extras
1610 */
1611 public Builder addExtras(Bundle extras) {
1612 if (extras != null) {
1613 mExtras.putAll(extras);
1614 }
1615 return this;
1616 }
1617
1618 /**
1619 * Get the metadata Bundle used by this Builder.
1620 *
1621 * <p>The returned Bundle is shared with this Builder.
1622 */
1623 public Bundle getExtras() {
1624 return mExtras;
1625 }
1626
1627 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001628 * Add an input to be collected from the user when this action is sent.
1629 * Response values can be retrieved from the fired intent by using the
1630 * {@link RemoteInput#getResultsFromIntent} function.
1631 * @param remoteInput a {@link RemoteInput} to add to the action
1632 * @return this object for method chaining
1633 */
1634 public Builder addRemoteInput(RemoteInput remoteInput) {
1635 if (mRemoteInputs == null) {
1636 mRemoteInputs = new ArrayList<RemoteInput>();
1637 }
1638 mRemoteInputs.add(remoteInput);
1639 return this;
1640 }
1641
1642 /**
Alex Hills42b0c4d2016-04-26 13:35:36 -04001643 * Set whether the platform should automatically generate possible replies to add to
1644 * {@link RemoteInput#getChoices()}. If the {@link Action} doesn't have a
1645 * {@link RemoteInput}, this has no effect.
1646 * @param allowGeneratedReplies {@code true} to allow generated replies, {@code false}
1647 * otherwise
1648 * @return this object for method chaining
Alex Hills1f27f882017-01-12 11:24:46 -05001649 * The default value is {@code true}
Alex Hills42b0c4d2016-04-26 13:35:36 -04001650 */
1651 public Builder setAllowGeneratedReplies(boolean allowGeneratedReplies) {
1652 mAllowGeneratedReplies = allowGeneratedReplies;
1653 return this;
1654 }
1655
1656 /**
Kodlee Yinc72c44d2017-12-21 22:07:15 +00001657 * Sets the {@code SemanticAction} for this {@link Action}. A
1658 * {@code SemanticAction} denotes what an {@link Action}'s
1659 * {@link PendingIntent} will do (eg. reply, mark as read, delete, etc).
1660 * @param semanticAction a SemanticAction defined within {@link Action} with
1661 * {@code SEMANTIC_ACTION_} prefixes
1662 * @return this object for method chaining
1663 */
1664 public Builder setSemanticAction(@SemanticAction int semanticAction) {
1665 mSemanticAction = semanticAction;
1666 return this;
1667 }
1668
1669 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001670 * Apply an extender to this action builder. Extenders may be used to add
1671 * metadata or change options on this builder.
1672 */
Griff Hazen61a9e862014-05-22 16:05:19 -07001673 public Builder extend(Extender extender) {
1674 extender.extend(this);
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001675 return this;
1676 }
1677
1678 /**
Griff Hazen959591e2014-05-15 22:26:18 -07001679 * Combine all of the options that have been set and return a new {@link Action}
1680 * object.
1681 * @return the built action
1682 */
1683 public Action build() {
Shane Brennan472a3b32016-12-12 15:28:10 -08001684 ArrayList<RemoteInput> dataOnlyInputs = new ArrayList<>();
1685 RemoteInput[] previousDataInputs =
1686 (RemoteInput[]) mExtras.getParcelableArray(EXTRA_DATA_ONLY_INPUTS);
Felipe Lemedfd11962017-01-18 18:38:46 -08001687 if (previousDataInputs != null) {
Shane Brennan472a3b32016-12-12 15:28:10 -08001688 for (RemoteInput input : previousDataInputs) {
1689 dataOnlyInputs.add(input);
1690 }
1691 }
1692 List<RemoteInput> textInputs = new ArrayList<>();
1693 if (mRemoteInputs != null) {
1694 for (RemoteInput input : mRemoteInputs) {
1695 if (input.isDataOnly()) {
1696 dataOnlyInputs.add(input);
1697 } else {
1698 textInputs.add(input);
1699 }
1700 }
1701 }
1702 if (!dataOnlyInputs.isEmpty()) {
1703 RemoteInput[] dataInputsArr =
1704 dataOnlyInputs.toArray(new RemoteInput[dataOnlyInputs.size()]);
1705 mExtras.putParcelableArray(EXTRA_DATA_ONLY_INPUTS, dataInputsArr);
1706 }
1707 RemoteInput[] textInputsArr = textInputs.isEmpty()
1708 ? null : textInputs.toArray(new RemoteInput[textInputs.size()]);
1709 return new Action(mIcon, mTitle, mIntent, mExtras, textInputsArr,
Kodlee Yinc72c44d2017-12-21 22:07:15 +00001710 mAllowGeneratedReplies, mSemanticAction);
Griff Hazen959591e2014-05-15 22:26:18 -07001711 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001712 }
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001713
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001714 @Override
1715 public Action clone() {
1716 return new Action(
Dan Sandler86647982015-05-13 23:41:13 -04001717 getIcon(),
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001718 title,
1719 actionIntent, // safe to alias
Julia Reynolds53c934c2016-09-16 14:03:43 -04001720 mExtras == null ? new Bundle() : new Bundle(mExtras),
Alex Hills42b0c4d2016-04-26 13:35:36 -04001721 getRemoteInputs(),
Kodlee Yinc72c44d2017-12-21 22:07:15 +00001722 getAllowGeneratedReplies(),
1723 getSemanticAction());
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001724 }
Kodlee Yinc72c44d2017-12-21 22:07:15 +00001725
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001726 @Override
1727 public int describeContents() {
1728 return 0;
1729 }
Kodlee Yinc72c44d2017-12-21 22:07:15 +00001730
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001731 @Override
1732 public void writeToParcel(Parcel out, int flags) {
Dan Sandler86647982015-05-13 23:41:13 -04001733 final Icon ic = getIcon();
1734 if (ic != null) {
1735 out.writeInt(1);
1736 ic.writeToParcel(out, 0);
1737 } else {
1738 out.writeInt(0);
1739 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001740 TextUtils.writeToParcel(title, out, flags);
1741 if (actionIntent != null) {
1742 out.writeInt(1);
1743 actionIntent.writeToParcel(out, flags);
1744 } else {
1745 out.writeInt(0);
1746 }
Griff Hazen959591e2014-05-15 22:26:18 -07001747 out.writeBundle(mExtras);
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001748 out.writeTypedArray(mRemoteInputs, flags);
Alex Hills42b0c4d2016-04-26 13:35:36 -04001749 out.writeInt(mAllowGeneratedReplies ? 1 : 0);
Kodlee Yinc72c44d2017-12-21 22:07:15 +00001750 out.writeInt(mSemanticAction);
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001751 }
Kodlee Yinc72c44d2017-12-21 22:07:15 +00001752
Griff Hazen959591e2014-05-15 22:26:18 -07001753 public static final Parcelable.Creator<Action> CREATOR =
1754 new Parcelable.Creator<Action>() {
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001755 public Action createFromParcel(Parcel in) {
1756 return new Action(in);
1757 }
1758 public Action[] newArray(int size) {
1759 return new Action[size];
1760 }
1761 };
Griff Hazen61a9e862014-05-22 16:05:19 -07001762
1763 /**
1764 * Extender interface for use with {@link Builder#extend}. Extenders may be used to add
1765 * metadata or change options on an action builder.
1766 */
1767 public interface Extender {
1768 /**
1769 * Apply this extender to a notification action builder.
1770 * @param builder the builder to be modified.
1771 * @return the build object for chaining.
1772 */
1773 public Builder extend(Builder builder);
1774 }
1775
1776 /**
1777 * Wearable extender for notification actions. To add extensions to an action,
1778 * create a new {@link android.app.Notification.Action.WearableExtender} object using
1779 * the {@code WearableExtender()} constructor and apply it to a
1780 * {@link android.app.Notification.Action.Builder} using
1781 * {@link android.app.Notification.Action.Builder#extend}.
1782 *
1783 * <pre class="prettyprint">
1784 * Notification.Action action = new Notification.Action.Builder(
1785 * R.drawable.archive_all, "Archive all", actionIntent)
Griff Hazen14f57992014-05-26 09:07:14 -07001786 * .extend(new Notification.Action.WearableExtender()
Griff Hazen61a9e862014-05-22 16:05:19 -07001787 * .setAvailableOffline(false))
Griff Hazen14f57992014-05-26 09:07:14 -07001788 * .build();</pre>
Griff Hazen61a9e862014-05-22 16:05:19 -07001789 */
1790 public static final class WearableExtender implements Extender {
1791 /** Notification action extra which contains wearable extensions */
1792 private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS";
1793
Pete Gastaf6781d2014-10-07 15:17:05 -04001794 // Keys within EXTRA_WEARABLE_EXTENSIONS for wearable options.
Griff Hazen61a9e862014-05-22 16:05:19 -07001795 private static final String KEY_FLAGS = "flags";
Pete Gastaf6781d2014-10-07 15:17:05 -04001796 private static final String KEY_IN_PROGRESS_LABEL = "inProgressLabel";
1797 private static final String KEY_CONFIRM_LABEL = "confirmLabel";
1798 private static final String KEY_CANCEL_LABEL = "cancelLabel";
Griff Hazen61a9e862014-05-22 16:05:19 -07001799
1800 // Flags bitwise-ored to mFlags
1801 private static final int FLAG_AVAILABLE_OFFLINE = 0x1;
Alex Hills9ab3a232016-04-05 14:54:56 -04001802 private static final int FLAG_HINT_LAUNCHES_ACTIVITY = 1 << 1;
Alex Hills9f087612016-06-07 09:08:59 -04001803 private static final int FLAG_HINT_DISPLAY_INLINE = 1 << 2;
Griff Hazen61a9e862014-05-22 16:05:19 -07001804
1805 // Default value for flags integer
1806 private static final int DEFAULT_FLAGS = FLAG_AVAILABLE_OFFLINE;
1807
1808 private int mFlags = DEFAULT_FLAGS;
1809
Pete Gastaf6781d2014-10-07 15:17:05 -04001810 private CharSequence mInProgressLabel;
1811 private CharSequence mConfirmLabel;
1812 private CharSequence mCancelLabel;
1813
Griff Hazen61a9e862014-05-22 16:05:19 -07001814 /**
1815 * Create a {@link android.app.Notification.Action.WearableExtender} with default
1816 * options.
1817 */
1818 public WearableExtender() {
1819 }
1820
1821 /**
1822 * Create a {@link android.app.Notification.Action.WearableExtender} by reading
1823 * wearable options present in an existing notification action.
1824 * @param action the notification action to inspect.
1825 */
1826 public WearableExtender(Action action) {
1827 Bundle wearableBundle = action.getExtras().getBundle(EXTRA_WEARABLE_EXTENSIONS);
1828 if (wearableBundle != null) {
1829 mFlags = wearableBundle.getInt(KEY_FLAGS, DEFAULT_FLAGS);
Pete Gastaf6781d2014-10-07 15:17:05 -04001830 mInProgressLabel = wearableBundle.getCharSequence(KEY_IN_PROGRESS_LABEL);
1831 mConfirmLabel = wearableBundle.getCharSequence(KEY_CONFIRM_LABEL);
1832 mCancelLabel = wearableBundle.getCharSequence(KEY_CANCEL_LABEL);
Griff Hazen61a9e862014-05-22 16:05:19 -07001833 }
1834 }
1835
1836 /**
1837 * Apply wearable extensions to a notification action that is being built. This is
1838 * typically called by the {@link android.app.Notification.Action.Builder#extend}
1839 * method of {@link android.app.Notification.Action.Builder}.
1840 */
1841 @Override
1842 public Action.Builder extend(Action.Builder builder) {
1843 Bundle wearableBundle = new Bundle();
1844
1845 if (mFlags != DEFAULT_FLAGS) {
1846 wearableBundle.putInt(KEY_FLAGS, mFlags);
1847 }
Pete Gastaf6781d2014-10-07 15:17:05 -04001848 if (mInProgressLabel != null) {
1849 wearableBundle.putCharSequence(KEY_IN_PROGRESS_LABEL, mInProgressLabel);
1850 }
1851 if (mConfirmLabel != null) {
1852 wearableBundle.putCharSequence(KEY_CONFIRM_LABEL, mConfirmLabel);
1853 }
1854 if (mCancelLabel != null) {
1855 wearableBundle.putCharSequence(KEY_CANCEL_LABEL, mCancelLabel);
1856 }
Griff Hazen61a9e862014-05-22 16:05:19 -07001857
1858 builder.getExtras().putBundle(EXTRA_WEARABLE_EXTENSIONS, wearableBundle);
1859 return builder;
1860 }
1861
1862 @Override
1863 public WearableExtender clone() {
1864 WearableExtender that = new WearableExtender();
1865 that.mFlags = this.mFlags;
Pete Gastaf6781d2014-10-07 15:17:05 -04001866 that.mInProgressLabel = this.mInProgressLabel;
1867 that.mConfirmLabel = this.mConfirmLabel;
1868 that.mCancelLabel = this.mCancelLabel;
Griff Hazen61a9e862014-05-22 16:05:19 -07001869 return that;
1870 }
1871
1872 /**
1873 * Set whether this action is available when the wearable device is not connected to
1874 * a companion device. The user can still trigger this action when the wearable device is
1875 * offline, but a visual hint will indicate that the action may not be available.
1876 * Defaults to true.
1877 */
1878 public WearableExtender setAvailableOffline(boolean availableOffline) {
1879 setFlag(FLAG_AVAILABLE_OFFLINE, availableOffline);
1880 return this;
1881 }
1882
1883 /**
1884 * Get whether this action is available when the wearable device is not connected to
1885 * a companion device. The user can still trigger this action when the wearable device is
1886 * offline, but a visual hint will indicate that the action may not be available.
1887 * Defaults to true.
1888 */
1889 public boolean isAvailableOffline() {
1890 return (mFlags & FLAG_AVAILABLE_OFFLINE) != 0;
1891 }
1892
1893 private void setFlag(int mask, boolean value) {
1894 if (value) {
1895 mFlags |= mask;
1896 } else {
1897 mFlags &= ~mask;
1898 }
1899 }
Pete Gastaf6781d2014-10-07 15:17:05 -04001900
1901 /**
1902 * Set a label to display while the wearable is preparing to automatically execute the
1903 * action. This is usually a 'ing' verb ending in ellipsis like "Sending..."
1904 *
1905 * @param label the label to display while the action is being prepared to execute
1906 * @return this object for method chaining
1907 */
Gus Prevasb5f9cf52018-02-21 14:21:50 -05001908 @Deprecated
Pete Gastaf6781d2014-10-07 15:17:05 -04001909 public WearableExtender setInProgressLabel(CharSequence label) {
1910 mInProgressLabel = label;
1911 return this;
1912 }
1913
1914 /**
1915 * Get the label to display while the wearable is preparing to automatically execute
1916 * the action. This is usually a 'ing' verb ending in ellipsis like "Sending..."
1917 *
1918 * @return the label to display while the action is being prepared to execute
1919 */
Gus Prevasb5f9cf52018-02-21 14:21:50 -05001920 @Deprecated
Pete Gastaf6781d2014-10-07 15:17:05 -04001921 public CharSequence getInProgressLabel() {
1922 return mInProgressLabel;
1923 }
1924
1925 /**
1926 * Set a label to display to confirm that the action should be executed.
1927 * This is usually an imperative verb like "Send".
1928 *
1929 * @param label the label to confirm the action should be executed
1930 * @return this object for method chaining
1931 */
Gus Prevasb5f9cf52018-02-21 14:21:50 -05001932 @Deprecated
Pete Gastaf6781d2014-10-07 15:17:05 -04001933 public WearableExtender setConfirmLabel(CharSequence label) {
1934 mConfirmLabel = label;
1935 return this;
1936 }
1937
1938 /**
1939 * Get the label to display to confirm that the action should be executed.
1940 * This is usually an imperative verb like "Send".
1941 *
1942 * @return the label to confirm the action should be executed
1943 */
Gus Prevasb5f9cf52018-02-21 14:21:50 -05001944 @Deprecated
Pete Gastaf6781d2014-10-07 15:17:05 -04001945 public CharSequence getConfirmLabel() {
1946 return mConfirmLabel;
1947 }
1948
1949 /**
1950 * Set a label to display to cancel the action.
1951 * This is usually an imperative verb, like "Cancel".
1952 *
1953 * @param label the label to display to cancel the action
1954 * @return this object for method chaining
1955 */
Gus Prevasb5f9cf52018-02-21 14:21:50 -05001956 @Deprecated
Pete Gastaf6781d2014-10-07 15:17:05 -04001957 public WearableExtender setCancelLabel(CharSequence label) {
1958 mCancelLabel = label;
1959 return this;
1960 }
1961
1962 /**
1963 * Get the label to display to cancel the action.
1964 * This is usually an imperative verb like "Cancel".
1965 *
1966 * @return the label to display to cancel the action
1967 */
Gus Prevasb5f9cf52018-02-21 14:21:50 -05001968 @Deprecated
Pete Gastaf6781d2014-10-07 15:17:05 -04001969 public CharSequence getCancelLabel() {
1970 return mCancelLabel;
1971 }
Alex Hills9ab3a232016-04-05 14:54:56 -04001972
1973 /**
1974 * Set a hint that this Action will launch an {@link Activity} directly, telling the
1975 * platform that it can generate the appropriate transitions.
1976 * @param hintLaunchesActivity {@code true} if the content intent will launch
1977 * an activity and transitions should be generated, false otherwise.
1978 * @return this object for method chaining
1979 */
Alex Hills4ec3ff42016-04-12 11:36:18 -04001980 public WearableExtender setHintLaunchesActivity(
Alex Hills9ab3a232016-04-05 14:54:56 -04001981 boolean hintLaunchesActivity) {
1982 setFlag(FLAG_HINT_LAUNCHES_ACTIVITY, hintLaunchesActivity);
1983 return this;
1984 }
1985
1986 /**
1987 * Get a hint that this Action will launch an {@link Activity} directly, telling the
1988 * platform that it can generate the appropriate transitions
1989 * @return {@code true} if the content intent will launch an activity and transitions
1990 * should be generated, false otherwise. The default value is {@code false} if this was
1991 * never set.
1992 */
Alex Hills4ec3ff42016-04-12 11:36:18 -04001993 public boolean getHintLaunchesActivity() {
Alex Hills9ab3a232016-04-05 14:54:56 -04001994 return (mFlags & FLAG_HINT_LAUNCHES_ACTIVITY) != 0;
1995 }
Alex Hills9f087612016-06-07 09:08:59 -04001996
1997 /**
1998 * Set a hint that this Action should be displayed inline.
1999 *
2000 * @param hintDisplayInline {@code true} if action should be displayed inline, false
2001 * otherwise
2002 * @return this object for method chaining
2003 */
2004 public WearableExtender setHintDisplayActionInline(
2005 boolean hintDisplayInline) {
2006 setFlag(FLAG_HINT_DISPLAY_INLINE, hintDisplayInline);
2007 return this;
2008 }
2009
2010 /**
2011 * Get a hint that this Action should be displayed inline.
2012 *
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08002013 * @return {@code true} if the Action should be displayed inline, {@code false}
Alex Hills9f087612016-06-07 09:08:59 -04002014 * otherwise. The default value is {@code false} if this was never set.
2015 */
2016 public boolean getHintDisplayActionInline() {
2017 return (mFlags & FLAG_HINT_DISPLAY_INLINE) != 0;
2018 }
Griff Hazen61a9e862014-05-22 16:05:19 -07002019 }
Kodlee Yinc72c44d2017-12-21 22:07:15 +00002020
2021 /**
2022 * Provides meaning to an {@link Action} that hints at what the associated
2023 * {@link PendingIntent} will do. For example, an {@link Action} with a
2024 * {@link PendingIntent} that replies to a text message notification may have the
2025 * {@link #SEMANTIC_ACTION_REPLY} {@code SemanticAction} set within it.
2026 *
2027 * @hide
2028 */
2029 @IntDef(prefix = { "SEMANTIC_ACTION_" }, value = {
2030 SEMANTIC_ACTION_NONE,
2031 SEMANTIC_ACTION_REPLY,
2032 SEMANTIC_ACTION_MARK_AS_READ,
2033 SEMANTIC_ACTION_MARK_AS_UNREAD,
2034 SEMANTIC_ACTION_DELETE,
2035 SEMANTIC_ACTION_ARCHIVE,
2036 SEMANTIC_ACTION_MUTE,
2037 SEMANTIC_ACTION_UNMUTE,
2038 SEMANTIC_ACTION_THUMBS_UP,
Kodlee Yincda5b092018-02-15 15:34:53 -08002039 SEMANTIC_ACTION_THUMBS_DOWN,
2040 SEMANTIC_ACTION_CALL
Kodlee Yinc72c44d2017-12-21 22:07:15 +00002041 })
2042 @Retention(RetentionPolicy.SOURCE)
2043 public @interface SemanticAction {}
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002044 }
2045
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04002046 /**
2047 * Array of all {@link Action} structures attached to this notification by
2048 * {@link Builder#addAction(int, CharSequence, PendingIntent)}. Mostly useful for instances of
2049 * {@link android.service.notification.NotificationListenerService} that provide an alternative
2050 * interface for invoking actions.
2051 */
Daniel Sandlerea2a3172013-02-20 22:24:20 -05002052 public Action[] actions;
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002053
2054 /**
Dan Sandler0bf2ed82013-12-21 23:33:41 -06002055 * Replacement version of this notification whose content will be shown
2056 * in an insecure context such as atop a secure keyguard. See {@link #visibility}
2057 * and {@link #VISIBILITY_PUBLIC}.
2058 */
2059 public Notification publicVersion;
2060
2061 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002062 * Constructs a Notification object with default values.
Joe Onorato46439ce2010-11-19 13:56:21 -08002063 * You might want to consider using {@link Builder} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002064 */
2065 public Notification()
2066 {
2067 this.when = System.currentTimeMillis();
Selim Cinekb85f36fd2016-04-20 18:46:36 -07002068 this.creationTime = System.currentTimeMillis();
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002069 this.priority = PRIORITY_DEFAULT;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002070 }
2071
2072 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002073 * @hide
2074 */
Mathew Inwood61e8ae62018-08-14 14:17:44 +01002075 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002076 public Notification(Context context, int icon, CharSequence tickerText, long when,
2077 CharSequence contentTitle, CharSequence contentText, Intent contentIntent)
2078 {
Chris Wren1ce4b6d2015-06-11 10:19:43 -04002079 new Builder(context)
2080 .setWhen(when)
2081 .setSmallIcon(icon)
2082 .setTicker(tickerText)
2083 .setContentTitle(contentTitle)
2084 .setContentText(contentText)
2085 .setContentIntent(PendingIntent.getActivity(context, 0, contentIntent, 0))
2086 .buildInto(this);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002087 }
2088
2089 /**
2090 * Constructs a Notification object with the information needed to
2091 * have a status bar icon without the standard expanded view.
2092 *
2093 * @param icon The resource id of the icon to put in the status bar.
2094 * @param tickerText The text that flows by in the status bar when the notification first
2095 * activates.
2096 * @param when The time to show in the time field. In the System.currentTimeMillis
2097 * timebase.
Joe Onorato46439ce2010-11-19 13:56:21 -08002098 *
2099 * @deprecated Use {@link Builder} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002100 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002101 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002102 public Notification(int icon, CharSequence tickerText, long when)
2103 {
2104 this.icon = icon;
2105 this.tickerText = tickerText;
2106 this.when = when;
Selim Cinekb85f36fd2016-04-20 18:46:36 -07002107 this.creationTime = System.currentTimeMillis();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002108 }
2109
2110 /**
2111 * Unflatten the notification from a parcel.
2112 */
Svet Ganovddb94882016-06-23 19:55:24 -07002113 @SuppressWarnings("unchecked")
2114 public Notification(Parcel parcel) {
2115 // IMPORTANT: Add unmarshaling code in readFromParcel as the pending
2116 // intents in extras are always written as the last entry.
2117 readFromParcelImpl(parcel);
2118 // Must be read last!
Felipe Lemedd85da62016-06-28 11:29:54 -07002119 allPendingIntents = (ArraySet<PendingIntent>) parcel.readArraySet(null);
Svet Ganovddb94882016-06-23 19:55:24 -07002120 }
2121
2122 private void readFromParcelImpl(Parcel parcel)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002123 {
2124 int version = parcel.readInt();
2125
Adrian Roosfb921842017-10-26 14:49:56 +02002126 mWhitelistToken = parcel.readStrongBinder();
2127 if (mWhitelistToken == null) {
2128 mWhitelistToken = processWhitelistToken;
Dianne Hackborn98305522017-05-05 17:53:53 -07002129 }
2130 // Propagate this token to all pending intents that are unmarshalled from the parcel.
Adrian Roosfb921842017-10-26 14:49:56 +02002131 parcel.setClassCookie(PendingIntent.class, mWhitelistToken);
Dianne Hackborn98305522017-05-05 17:53:53 -07002132
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002133 when = parcel.readLong();
Selim Cinekb85f36fd2016-04-20 18:46:36 -07002134 creationTime = parcel.readLong();
Dan Sandler3936e7a2015-05-19 20:59:12 -04002135 if (parcel.readInt() != 0) {
2136 mSmallIcon = Icon.CREATOR.createFromParcel(parcel);
Dan Sandler4e787062015-06-17 15:09:48 -04002137 if (mSmallIcon.getType() == Icon.TYPE_RESOURCE) {
2138 icon = mSmallIcon.getResId();
2139 }
Dan Sandler3936e7a2015-05-19 20:59:12 -04002140 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002141 number = parcel.readInt();
2142 if (parcel.readInt() != 0) {
2143 contentIntent = PendingIntent.CREATOR.createFromParcel(parcel);
2144 }
2145 if (parcel.readInt() != 0) {
2146 deleteIntent = PendingIntent.CREATOR.createFromParcel(parcel);
2147 }
2148 if (parcel.readInt() != 0) {
2149 tickerText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
2150 }
2151 if (parcel.readInt() != 0) {
Joe Onorato46439ce2010-11-19 13:56:21 -08002152 tickerView = RemoteViews.CREATOR.createFromParcel(parcel);
Joe Onoratoef1e7762010-09-17 18:38:38 -04002153 }
2154 if (parcel.readInt() != 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002155 contentView = RemoteViews.CREATOR.createFromParcel(parcel);
2156 }
Joe Onorato561d3852010-11-20 18:09:34 -08002157 if (parcel.readInt() != 0) {
Dan Sandlerd63f9322015-05-06 15:18:49 -04002158 mLargeIcon = Icon.CREATOR.createFromParcel(parcel);
Joe Onorato561d3852010-11-20 18:09:34 -08002159 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002160 defaults = parcel.readInt();
2161 flags = parcel.readInt();
2162 if (parcel.readInt() != 0) {
2163 sound = Uri.CREATOR.createFromParcel(parcel);
2164 }
2165
2166 audioStreamType = parcel.readInt();
John Spurlockc0650f022014-07-19 13:22:39 -04002167 if (parcel.readInt() != 0) {
2168 audioAttributes = AudioAttributes.CREATOR.createFromParcel(parcel);
2169 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002170 vibrate = parcel.createLongArray();
2171 ledARGB = parcel.readInt();
2172 ledOnMS = parcel.readInt();
2173 ledOffMS = parcel.readInt();
2174 iconLevel = parcel.readInt();
Daniel Sandlere46cbd32010-06-17 10:35:26 -04002175
2176 if (parcel.readInt() != 0) {
2177 fullScreenIntent = PendingIntent.CREATOR.createFromParcel(parcel);
2178 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002179
2180 priority = parcel.readInt();
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002181
John Spurlockfd7f1e02014-03-18 16:41:57 -04002182 category = parcel.readString();
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002183
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002184 mGroupKey = parcel.readString();
2185
2186 mSortKey = parcel.readString();
2187
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06002188 extras = Bundle.setDefusable(parcel.readBundle(), true); // may be null
Robin Leead7e72a2017-12-04 15:45:46 +01002189 fixDuplicateExtras();
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002190
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002191 actions = parcel.createTypedArray(Action.CREATOR); // may be null
2192
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002193 if (parcel.readInt() != 0) {
Daniel Sandlerf3b73432012-03-27 15:01:25 -04002194 bigContentView = RemoteViews.CREATOR.createFromParcel(parcel);
2195 }
Dan Sandler0bf2ed82013-12-21 23:33:41 -06002196
Chris Wren8fd39ec2014-02-27 17:43:26 -05002197 if (parcel.readInt() != 0) {
2198 headsUpContentView = RemoteViews.CREATOR.createFromParcel(parcel);
2199 }
2200
Dan Sandler0bf2ed82013-12-21 23:33:41 -06002201 visibility = parcel.readInt();
2202
2203 if (parcel.readInt() != 0) {
2204 publicVersion = Notification.CREATOR.createFromParcel(parcel);
2205 }
Dan Sandler26e81cf2014-05-06 10:01:27 -04002206
2207 color = parcel.readInt();
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04002208
2209 if (parcel.readInt() != 0) {
2210 mChannelId = parcel.readString();
2211 }
Julia Reynolds2a128742016-11-28 14:29:25 -05002212 mTimeout = parcel.readLong();
Julia Reynolds13d898c2017-02-02 12:22:05 -05002213
2214 if (parcel.readInt() != 0) {
2215 mShortcutId = parcel.readString();
2216 }
2217
2218 mBadgeIcon = parcel.readInt();
Julia Reynolds3aedded2017-03-31 14:42:09 -04002219
2220 if (parcel.readInt() != 0) {
2221 mSettingsText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
2222 }
Julia Reynoldsa79c3712017-04-21 10:29:57 -04002223
2224 mGroupAlertBehavior = parcel.readInt();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002225 }
2226
Andy Stadler110988c2010-12-03 14:29:16 -08002227 @Override
Joe Onorato18e69df2010-05-17 22:26:12 -07002228 public Notification clone() {
2229 Notification that = new Notification();
Daniel Sandler1a497d32013-04-18 14:52:45 -04002230 cloneInto(that, true);
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002231 return that;
2232 }
Joe Onorato18e69df2010-05-17 22:26:12 -07002233
Daniel Sandler1a497d32013-04-18 14:52:45 -04002234 /**
2235 * Copy all (or if heavy is false, all except Bitmaps and RemoteViews) members
2236 * of this into that.
2237 * @hide
2238 */
2239 public void cloneInto(Notification that, boolean heavy) {
Adrian Roosfb921842017-10-26 14:49:56 +02002240 that.mWhitelistToken = this.mWhitelistToken;
Joe Onorato18e69df2010-05-17 22:26:12 -07002241 that.when = this.when;
Selim Cinekb85f36fd2016-04-20 18:46:36 -07002242 that.creationTime = this.creationTime;
Dan Sandlerd63f9322015-05-06 15:18:49 -04002243 that.mSmallIcon = this.mSmallIcon;
Joe Onorato18e69df2010-05-17 22:26:12 -07002244 that.number = this.number;
2245
2246 // PendingIntents are global, so there's no reason (or way) to clone them.
2247 that.contentIntent = this.contentIntent;
2248 that.deleteIntent = this.deleteIntent;
Daniel Sandlere46cbd32010-06-17 10:35:26 -04002249 that.fullScreenIntent = this.fullScreenIntent;
Joe Onorato18e69df2010-05-17 22:26:12 -07002250
2251 if (this.tickerText != null) {
2252 that.tickerText = this.tickerText.toString();
2253 }
Daniel Sandler1a497d32013-04-18 14:52:45 -04002254 if (heavy && this.tickerView != null) {
Joe Onorato46439ce2010-11-19 13:56:21 -08002255 that.tickerView = this.tickerView.clone();
Joe Onoratoef1e7762010-09-17 18:38:38 -04002256 }
Daniel Sandler1a497d32013-04-18 14:52:45 -04002257 if (heavy && this.contentView != null) {
Joe Onorato18e69df2010-05-17 22:26:12 -07002258 that.contentView = this.contentView.clone();
2259 }
Dan Sandlerd63f9322015-05-06 15:18:49 -04002260 if (heavy && this.mLargeIcon != null) {
2261 that.mLargeIcon = this.mLargeIcon;
Joe Onorato561d3852010-11-20 18:09:34 -08002262 }
Jozef BABJAKa8b91832011-02-22 08:05:08 +01002263 that.iconLevel = this.iconLevel;
Joe Onorato18e69df2010-05-17 22:26:12 -07002264 that.sound = this.sound; // android.net.Uri is immutable
2265 that.audioStreamType = this.audioStreamType;
John Spurlockc0650f022014-07-19 13:22:39 -04002266 if (this.audioAttributes != null) {
2267 that.audioAttributes = new AudioAttributes.Builder(this.audioAttributes).build();
2268 }
Joe Onorato18e69df2010-05-17 22:26:12 -07002269
2270 final long[] vibrate = this.vibrate;
2271 if (vibrate != null) {
2272 final int N = vibrate.length;
2273 final long[] vib = that.vibrate = new long[N];
2274 System.arraycopy(vibrate, 0, vib, 0, N);
2275 }
2276
2277 that.ledARGB = this.ledARGB;
2278 that.ledOnMS = this.ledOnMS;
2279 that.ledOffMS = this.ledOffMS;
2280 that.defaults = this.defaults;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002281
Joe Onorato18e69df2010-05-17 22:26:12 -07002282 that.flags = this.flags;
2283
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002284 that.priority = this.priority;
Joe Malin8d40d042012-11-05 11:36:40 -08002285
John Spurlockfd7f1e02014-03-18 16:41:57 -04002286 that.category = this.category;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002287
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002288 that.mGroupKey = this.mGroupKey;
2289
2290 that.mSortKey = this.mSortKey;
2291
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002292 if (this.extras != null) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04002293 try {
2294 that.extras = new Bundle(this.extras);
2295 // will unparcel
2296 that.extras.size();
2297 } catch (BadParcelableException e) {
2298 Log.e(TAG, "could not unparcel extras from notification: " + this, e);
2299 that.extras = null;
2300 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002301 }
2302
Felipe Lemedd85da62016-06-28 11:29:54 -07002303 if (!ArrayUtils.isEmpty(allPendingIntents)) {
2304 that.allPendingIntents = new ArraySet<>(allPendingIntents);
Svet Ganovddb94882016-06-23 19:55:24 -07002305 }
2306
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002307 if (this.actions != null) {
2308 that.actions = new Action[this.actions.length];
2309 for(int i=0; i<this.actions.length; i++) {
liangweikang63b03b52017-03-16 19:22:15 +08002310 if ( this.actions[i] != null) {
2311 that.actions[i] = this.actions[i].clone();
2312 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002313 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002314 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002315
Daniel Sandler1a497d32013-04-18 14:52:45 -04002316 if (heavy && this.bigContentView != null) {
Daniel Sandlerf3b73432012-03-27 15:01:25 -04002317 that.bigContentView = this.bigContentView.clone();
2318 }
Daniel Sandler1a497d32013-04-18 14:52:45 -04002319
Chris Wren8fd39ec2014-02-27 17:43:26 -05002320 if (heavy && this.headsUpContentView != null) {
2321 that.headsUpContentView = this.headsUpContentView.clone();
2322 }
2323
Dan Sandler0bf2ed82013-12-21 23:33:41 -06002324 that.visibility = this.visibility;
2325
2326 if (this.publicVersion != null) {
2327 that.publicVersion = new Notification();
2328 this.publicVersion.cloneInto(that.publicVersion, heavy);
2329 }
2330
Dan Sandler26e81cf2014-05-06 10:01:27 -04002331 that.color = this.color;
2332
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04002333 that.mChannelId = this.mChannelId;
Julia Reynolds2a128742016-11-28 14:29:25 -05002334 that.mTimeout = this.mTimeout;
Julia Reynolds3aedded2017-03-31 14:42:09 -04002335 that.mShortcutId = this.mShortcutId;
2336 that.mBadgeIcon = this.mBadgeIcon;
2337 that.mSettingsText = this.mSettingsText;
Julia Reynoldsa79c3712017-04-21 10:29:57 -04002338 that.mGroupAlertBehavior = this.mGroupAlertBehavior;
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04002339
Daniel Sandler1a497d32013-04-18 14:52:45 -04002340 if (!heavy) {
2341 that.lightenPayload(); // will clean out extras
2342 }
2343 }
2344
2345 /**
Jeff Sharkey23b31182018-04-18 21:32:12 -06002346 * Note all {@link Uri} that are referenced internally, with the expectation
2347 * that Uri permission grants will need to be issued to ensure the recipient
2348 * of this object is able to render its contents.
2349 *
2350 * @hide
2351 */
2352 public void visitUris(@NonNull Consumer<Uri> visitor) {
2353 visitor.accept(sound);
2354
2355 if (tickerView != null) tickerView.visitUris(visitor);
2356 if (contentView != null) contentView.visitUris(visitor);
2357 if (bigContentView != null) bigContentView.visitUris(visitor);
2358 if (headsUpContentView != null) headsUpContentView.visitUris(visitor);
2359
2360 if (extras != null) {
2361 visitor.accept(extras.getParcelable(EXTRA_AUDIO_CONTENTS_URI));
Robin Leed107af62018-04-27 13:55:56 +02002362 if (extras.containsKey(EXTRA_BACKGROUND_IMAGE_URI)) {
2363 visitor.accept(Uri.parse(extras.getString(EXTRA_BACKGROUND_IMAGE_URI)));
2364 }
Jeff Sharkey23b31182018-04-18 21:32:12 -06002365 }
2366
2367 if (MessagingStyle.class.equals(getNotificationStyle()) && extras != null) {
2368 final Parcelable[] messages = extras.getParcelableArray(EXTRA_MESSAGES);
2369 if (!ArrayUtils.isEmpty(messages)) {
2370 for (MessagingStyle.Message message : MessagingStyle.Message
2371 .getMessagesFromBundleArray(messages)) {
2372 visitor.accept(message.getDataUri());
2373 }
2374 }
2375
2376 final Parcelable[] historic = extras.getParcelableArray(EXTRA_HISTORIC_MESSAGES);
2377 if (!ArrayUtils.isEmpty(historic)) {
2378 for (MessagingStyle.Message message : MessagingStyle.Message
2379 .getMessagesFromBundleArray(historic)) {
2380 visitor.accept(message.getDataUri());
2381 }
2382 }
2383 }
2384 }
2385
2386 /**
Daniel Sandler1a497d32013-04-18 14:52:45 -04002387 * Removes heavyweight parts of the Notification object for archival or for sending to
2388 * listeners when the full contents are not necessary.
2389 * @hide
2390 */
2391 public final void lightenPayload() {
2392 tickerView = null;
2393 contentView = null;
2394 bigContentView = null;
Chris Wren8fd39ec2014-02-27 17:43:26 -05002395 headsUpContentView = null;
Dan Sandlerd63f9322015-05-06 15:18:49 -04002396 mLargeIcon = null;
Dan Sandler50128532015-12-08 15:42:41 -05002397 if (extras != null && !extras.isEmpty()) {
2398 final Set<String> keyset = extras.keySet();
2399 final int N = keyset.size();
2400 final String[] keys = keyset.toArray(new String[N]);
2401 for (int i=0; i<N; i++) {
2402 final String key = keys[i];
Dmitri Plotnikov22281362017-01-30 11:16:21 -08002403 if (TvExtender.EXTRA_TV_EXTENDER.equals(key)) {
2404 continue;
2405 }
Dan Sandler50128532015-12-08 15:42:41 -05002406 final Object obj = extras.get(key);
2407 if (obj != null &&
2408 ( obj instanceof Parcelable
2409 || obj instanceof Parcelable[]
2410 || obj instanceof SparseArray
2411 || obj instanceof ArrayList)) {
2412 extras.remove(key);
2413 }
2414 }
Daniel Sandler1a497d32013-04-18 14:52:45 -04002415 }
Joe Onorato18e69df2010-05-17 22:26:12 -07002416 }
2417
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04002418 /**
2419 * Make sure this CharSequence is safe to put into a bundle, which basically
2420 * means it had better not be some custom Parcelable implementation.
2421 * @hide
2422 */
2423 public static CharSequence safeCharSequence(CharSequence cs) {
Christoph Studer535ec612014-09-03 15:47:47 +02002424 if (cs == null) return cs;
2425 if (cs.length() > MAX_CHARSEQUENCE_LENGTH) {
2426 cs = cs.subSequence(0, MAX_CHARSEQUENCE_LENGTH);
2427 }
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04002428 if (cs instanceof Parcelable) {
2429 Log.e(TAG, "warning: " + cs.getClass().getCanonicalName()
2430 + " instance is a custom Parcelable and not allowed in Notification");
2431 return cs.toString();
2432 }
Selim Cinek60a54252016-02-26 17:03:25 -08002433 return removeTextSizeSpans(cs);
2434 }
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04002435
Selim Cinek60a54252016-02-26 17:03:25 -08002436 private static CharSequence removeTextSizeSpans(CharSequence charSequence) {
2437 if (charSequence instanceof Spanned) {
2438 Spanned ss = (Spanned) charSequence;
2439 Object[] spans = ss.getSpans(0, ss.length(), Object.class);
2440 SpannableStringBuilder builder = new SpannableStringBuilder(ss.toString());
2441 for (Object span : spans) {
2442 Object resultSpan = span;
Selim Cinek89991a22016-03-07 19:51:54 -08002443 if (resultSpan instanceof CharacterStyle) {
2444 resultSpan = ((CharacterStyle) span).getUnderlying();
2445 }
2446 if (resultSpan instanceof TextAppearanceSpan) {
2447 TextAppearanceSpan originalSpan = (TextAppearanceSpan) resultSpan;
Selim Cinek60a54252016-02-26 17:03:25 -08002448 resultSpan = new TextAppearanceSpan(
2449 originalSpan.getFamily(),
2450 originalSpan.getTextStyle(),
2451 -1,
2452 originalSpan.getTextColor(),
2453 originalSpan.getLinkTextColor());
Selim Cinek89991a22016-03-07 19:51:54 -08002454 } else if (resultSpan instanceof RelativeSizeSpan
2455 || resultSpan instanceof AbsoluteSizeSpan) {
Selim Cinek60a54252016-02-26 17:03:25 -08002456 continue;
Selim Cinek89991a22016-03-07 19:51:54 -08002457 } else {
2458 resultSpan = span;
Selim Cinek60a54252016-02-26 17:03:25 -08002459 }
2460 builder.setSpan(resultSpan, ss.getSpanStart(span), ss.getSpanEnd(span),
2461 ss.getSpanFlags(span));
2462 }
2463 return builder;
2464 }
2465 return charSequence;
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04002466 }
2467
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002468 public int describeContents() {
2469 return 0;
2470 }
2471
2472 /**
Dan Sandler4e787062015-06-17 15:09:48 -04002473 * Flatten this notification into a parcel.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002474 */
Svet Ganovddb94882016-06-23 19:55:24 -07002475 public void writeToParcel(Parcel parcel, int flags) {
2476 // We need to mark all pending intents getting into the notification
2477 // system as being put there to later allow the notification ranker
2478 // to launch them and by doing so add the app to the battery saver white
2479 // list for a short period of time. The problem is that the system
2480 // cannot look into the extras as there may be parcelables there that
2481 // the platform does not know how to handle. To go around that we have
2482 // an explicit list of the pending intents in the extras bundle.
Felipe Lemedd85da62016-06-28 11:29:54 -07002483 final boolean collectPendingIntents = (allPendingIntents == null);
Svet Ganovddb94882016-06-23 19:55:24 -07002484 if (collectPendingIntents) {
2485 PendingIntent.setOnMarshaledListener(
2486 (PendingIntent intent, Parcel out, int outFlags) -> {
2487 if (parcel == out) {
Felipe Lemedd85da62016-06-28 11:29:54 -07002488 if (allPendingIntents == null) {
2489 allPendingIntents = new ArraySet<>();
Svet Ganovddb94882016-06-23 19:55:24 -07002490 }
Felipe Lemedd85da62016-06-28 11:29:54 -07002491 allPendingIntents.add(intent);
Svet Ganovddb94882016-06-23 19:55:24 -07002492 }
2493 });
2494 }
2495 try {
2496 // IMPORTANT: Add marshaling code in writeToParcelImpl as we
Julia Reynolds13d898c2017-02-02 12:22:05 -05002497 // want to intercept all pending events written to the parcel.
Svet Ganovddb94882016-06-23 19:55:24 -07002498 writeToParcelImpl(parcel, flags);
2499 // Must be written last!
Felipe Lemedd85da62016-06-28 11:29:54 -07002500 parcel.writeArraySet(allPendingIntents);
Svet Ganovddb94882016-06-23 19:55:24 -07002501 } finally {
2502 if (collectPendingIntents) {
2503 PendingIntent.setOnMarshaledListener(null);
2504 }
2505 }
2506 }
2507
2508 private void writeToParcelImpl(Parcel parcel, int flags) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002509 parcel.writeInt(1);
2510
Adrian Roosfb921842017-10-26 14:49:56 +02002511 parcel.writeStrongBinder(mWhitelistToken);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002512 parcel.writeLong(when);
Selim Cinekb85f36fd2016-04-20 18:46:36 -07002513 parcel.writeLong(creationTime);
Dan Sandler4e787062015-06-17 15:09:48 -04002514 if (mSmallIcon == null && icon != 0) {
2515 // you snuck an icon in here without using the builder; let's try to keep it
2516 mSmallIcon = Icon.createWithResource("", icon);
2517 }
Dan Sandler3936e7a2015-05-19 20:59:12 -04002518 if (mSmallIcon != null) {
2519 parcel.writeInt(1);
2520 mSmallIcon.writeToParcel(parcel, 0);
2521 } else {
2522 parcel.writeInt(0);
2523 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002524 parcel.writeInt(number);
2525 if (contentIntent != null) {
2526 parcel.writeInt(1);
2527 contentIntent.writeToParcel(parcel, 0);
2528 } else {
2529 parcel.writeInt(0);
2530 }
2531 if (deleteIntent != null) {
2532 parcel.writeInt(1);
2533 deleteIntent.writeToParcel(parcel, 0);
2534 } else {
2535 parcel.writeInt(0);
2536 }
2537 if (tickerText != null) {
2538 parcel.writeInt(1);
2539 TextUtils.writeToParcel(tickerText, parcel, flags);
2540 } else {
2541 parcel.writeInt(0);
2542 }
Joe Onorato46439ce2010-11-19 13:56:21 -08002543 if (tickerView != null) {
Joe Onoratoef1e7762010-09-17 18:38:38 -04002544 parcel.writeInt(1);
Joe Onorato46439ce2010-11-19 13:56:21 -08002545 tickerView.writeToParcel(parcel, 0);
Joe Onoratoef1e7762010-09-17 18:38:38 -04002546 } else {
2547 parcel.writeInt(0);
2548 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002549 if (contentView != null) {
2550 parcel.writeInt(1);
2551 contentView.writeToParcel(parcel, 0);
2552 } else {
2553 parcel.writeInt(0);
2554 }
Selim Cinek279fa862016-06-14 10:57:25 -07002555 if (mLargeIcon == null && largeIcon != null) {
2556 // you snuck an icon in here without using the builder; let's try to keep it
2557 mLargeIcon = Icon.createWithBitmap(largeIcon);
2558 }
Dan Sandlerd63f9322015-05-06 15:18:49 -04002559 if (mLargeIcon != null) {
Joe Onorato561d3852010-11-20 18:09:34 -08002560 parcel.writeInt(1);
Dan Sandlerd63f9322015-05-06 15:18:49 -04002561 mLargeIcon.writeToParcel(parcel, 0);
Joe Onorato561d3852010-11-20 18:09:34 -08002562 } else {
2563 parcel.writeInt(0);
2564 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002565
2566 parcel.writeInt(defaults);
2567 parcel.writeInt(this.flags);
2568
2569 if (sound != null) {
2570 parcel.writeInt(1);
2571 sound.writeToParcel(parcel, 0);
2572 } else {
2573 parcel.writeInt(0);
2574 }
2575 parcel.writeInt(audioStreamType);
John Spurlockc0650f022014-07-19 13:22:39 -04002576
2577 if (audioAttributes != null) {
2578 parcel.writeInt(1);
2579 audioAttributes.writeToParcel(parcel, 0);
2580 } else {
2581 parcel.writeInt(0);
2582 }
2583
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002584 parcel.writeLongArray(vibrate);
2585 parcel.writeInt(ledARGB);
2586 parcel.writeInt(ledOnMS);
2587 parcel.writeInt(ledOffMS);
2588 parcel.writeInt(iconLevel);
Daniel Sandlere46cbd32010-06-17 10:35:26 -04002589
2590 if (fullScreenIntent != null) {
2591 parcel.writeInt(1);
2592 fullScreenIntent.writeToParcel(parcel, 0);
2593 } else {
2594 parcel.writeInt(0);
2595 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002596
2597 parcel.writeInt(priority);
Joe Malin8d40d042012-11-05 11:36:40 -08002598
John Spurlockfd7f1e02014-03-18 16:41:57 -04002599 parcel.writeString(category);
Joe Malin8d40d042012-11-05 11:36:40 -08002600
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002601 parcel.writeString(mGroupKey);
2602
2603 parcel.writeString(mSortKey);
2604
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002605 parcel.writeBundle(extras); // null ok
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002606
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002607 parcel.writeTypedArray(actions, 0); // null ok
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002608
Daniel Sandlerf3b73432012-03-27 15:01:25 -04002609 if (bigContentView != null) {
2610 parcel.writeInt(1);
2611 bigContentView.writeToParcel(parcel, 0);
2612 } else {
2613 parcel.writeInt(0);
2614 }
Dan Sandler0bf2ed82013-12-21 23:33:41 -06002615
Chris Wren8fd39ec2014-02-27 17:43:26 -05002616 if (headsUpContentView != null) {
2617 parcel.writeInt(1);
2618 headsUpContentView.writeToParcel(parcel, 0);
2619 } else {
2620 parcel.writeInt(0);
2621 }
2622
Dan Sandler0bf2ed82013-12-21 23:33:41 -06002623 parcel.writeInt(visibility);
2624
2625 if (publicVersion != null) {
2626 parcel.writeInt(1);
2627 publicVersion.writeToParcel(parcel, 0);
2628 } else {
2629 parcel.writeInt(0);
2630 }
Dan Sandler26e81cf2014-05-06 10:01:27 -04002631
2632 parcel.writeInt(color);
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04002633
2634 if (mChannelId != null) {
2635 parcel.writeInt(1);
2636 parcel.writeString(mChannelId);
2637 } else {
2638 parcel.writeInt(0);
2639 }
Julia Reynolds2a128742016-11-28 14:29:25 -05002640 parcel.writeLong(mTimeout);
Julia Reynolds13d898c2017-02-02 12:22:05 -05002641
2642 if (mShortcutId != null) {
2643 parcel.writeInt(1);
2644 parcel.writeString(mShortcutId);
2645 } else {
2646 parcel.writeInt(0);
2647 }
2648
2649 parcel.writeInt(mBadgeIcon);
Julia Reynolds3aedded2017-03-31 14:42:09 -04002650
2651 if (mSettingsText != null) {
2652 parcel.writeInt(1);
2653 TextUtils.writeToParcel(mSettingsText, parcel, flags);
2654 } else {
2655 parcel.writeInt(0);
2656 }
Julia Reynoldsa79c3712017-04-21 10:29:57 -04002657
2658 parcel.writeInt(mGroupAlertBehavior);
Julia Reynoldsfc640012018-02-21 12:25:27 -05002659
2660 // mUsesStandardHeader is not written because it should be recomputed in listeners
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002661 }
2662
2663 /**
2664 * Parcelable.Creator that instantiates Notification objects
2665 */
2666 public static final Parcelable.Creator<Notification> CREATOR
2667 = new Parcelable.Creator<Notification>()
2668 {
2669 public Notification createFromParcel(Parcel parcel)
2670 {
2671 return new Notification(parcel);
2672 }
2673
2674 public Notification[] newArray(int size)
2675 {
2676 return new Notification[size];
2677 }
2678 };
2679
2680 /**
Julia Reynolds7217dc92018-03-07 12:12:09 -05002681 * @hide
2682 */
2683 public static boolean areActionsVisiblyDifferent(Notification first, Notification second) {
2684 Notification.Action[] firstAs = first.actions;
2685 Notification.Action[] secondAs = second.actions;
2686 if (firstAs == null && secondAs != null || firstAs != null && secondAs == null) {
2687 return true;
2688 }
2689 if (firstAs != null && secondAs != null) {
2690 if (firstAs.length != secondAs.length) {
2691 return true;
2692 }
2693 for (int i = 0; i < firstAs.length; i++) {
Julia Reynoldsa4fb9da2018-06-04 12:27:58 -04002694 if (!Objects.equals(String.valueOf(firstAs[i].title),
2695 String.valueOf(secondAs[i].title))) {
Julia Reynolds7217dc92018-03-07 12:12:09 -05002696 return true;
2697 }
2698 RemoteInput[] firstRs = firstAs[i].getRemoteInputs();
2699 RemoteInput[] secondRs = secondAs[i].getRemoteInputs();
2700 if (firstRs == null) {
2701 firstRs = new RemoteInput[0];
2702 }
2703 if (secondRs == null) {
2704 secondRs = new RemoteInput[0];
2705 }
2706 if (firstRs.length != secondRs.length) {
2707 return true;
2708 }
2709 for (int j = 0; j < firstRs.length; j++) {
Julia Reynoldsa4fb9da2018-06-04 12:27:58 -04002710 if (!Objects.equals(String.valueOf(firstRs[j].getLabel()),
2711 String.valueOf(secondRs[j].getLabel()))) {
Julia Reynolds7217dc92018-03-07 12:12:09 -05002712 return true;
2713 }
Julia Reynolds7217dc92018-03-07 12:12:09 -05002714 }
2715 }
2716 }
2717 return false;
2718 }
2719
2720 /**
2721 * @hide
2722 */
2723 public static boolean areStyledNotificationsVisiblyDifferent(Builder first, Builder second) {
2724 if (first.getStyle() == null) {
2725 return second.getStyle() != null;
2726 }
2727 if (second.getStyle() == null) {
2728 return true;
2729 }
2730 return first.getStyle().areNotificationsVisiblyDifferent(second.getStyle());
2731 }
2732
2733 /**
2734 * @hide
2735 */
2736 public static boolean areRemoteViewsChanged(Builder first, Builder second) {
Julia Reynoldse5c60452018-04-30 14:41:36 -04002737 if (!Objects.equals(first.usesStandardHeader(), second.usesStandardHeader())) {
2738 return true;
2739 }
2740
2741 if (areRemoteViewsChanged(first.mN.contentView, second.mN.contentView)) {
2742 return true;
2743 }
2744 if (areRemoteViewsChanged(first.mN.bigContentView, second.mN.bigContentView)) {
2745 return true;
2746 }
2747 if (areRemoteViewsChanged(first.mN.headsUpContentView, second.mN.headsUpContentView)) {
2748 return true;
2749 }
2750
2751 return false;
2752 }
2753
2754 private static boolean areRemoteViewsChanged(RemoteViews first, RemoteViews second) {
2755 if (first == null && second == null) {
2756 return false;
2757 }
2758 if (first == null && second != null || first != null && second == null) {
2759 return true;
2760 }
2761
2762 if (!Objects.equals(first.getLayoutId(), second.getLayoutId())) {
2763 return true;
2764 }
2765
2766 if (!Objects.equals(first.getSequenceNumber(), second.getSequenceNumber())) {
2767 return true;
2768 }
2769
2770 return false;
Julia Reynolds7217dc92018-03-07 12:12:09 -05002771 }
2772
2773 /**
Robin Leead7e72a2017-12-04 15:45:46 +01002774 * Parcelling creates multiple copies of objects in {@code extras}. Fix them.
2775 * <p>
2776 * For backwards compatibility {@code extras} holds some references to "real" member data such
2777 * as {@link getLargeIcon()} which is mirrored by {@link #EXTRA_LARGE_ICON}. This is mostly
2778 * fine as long as the object stays in one process.
2779 * <p>
2780 * However, once the notification goes into a parcel each reference gets marshalled separately,
2781 * wasting memory. Especially with large images on Auto and TV, this is worth fixing.
2782 */
2783 private void fixDuplicateExtras() {
2784 if (extras != null) {
Robin Leead7e72a2017-12-04 15:45:46 +01002785 fixDuplicateExtra(mLargeIcon, EXTRA_LARGE_ICON);
2786 }
2787 }
2788
2789 /**
2790 * If we find an extra that's exactly the same as one of the "real" fields but refers to a
2791 * separate object, replace it with the field's version to avoid holding duplicate copies.
2792 */
2793 private void fixDuplicateExtra(@Nullable Parcelable original, @NonNull String extraName) {
2794 if (original != null && extras.getParcelable(extraName) != null) {
2795 extras.putParcelable(extraName, original);
2796 }
2797 }
2798
2799 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002800 * Sets the {@link #contentView} field to be a view with the standard "Latest Event"
2801 * layout.
2802 *
2803 * <p>Uses the {@link #icon} and {@link #when} fields to set the icon and time fields
2804 * in the view.</p>
2805 * @param context The context for your application / activity.
2806 * @param contentTitle The title that goes in the expanded entry.
2807 * @param contentText The text that goes in the expanded entry.
2808 * @param contentIntent The intent to launch when the user clicks the expanded notification.
2809 * If this is an activity, it must include the
2810 * {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} flag, which requires
Scott Main7aee61f2011-02-08 11:25:01 -08002811 * that you take care of task management as described in the
2812 * <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back
2813 * Stack</a> document.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002814 *
Joe Onorato46439ce2010-11-19 13:56:21 -08002815 * @deprecated Use {@link Builder} instead.
Chris Wrena05db382015-06-24 15:18:34 -04002816 * @removed
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002817 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002818 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002819 public void setLatestEventInfo(Context context,
2820 CharSequence contentTitle, CharSequence contentText, PendingIntent contentIntent) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002821 if (context.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1){
2822 Log.e(TAG, "setLatestEventInfo() is deprecated and you should feel deprecated.",
2823 new Throwable());
2824 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002825
Selim Cinek4ac6f602016-06-13 15:47:03 -07002826 if (context.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N) {
2827 extras.putBoolean(EXTRA_SHOW_WHEN, true);
2828 }
2829
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002830 // ensure that any information already set directly is preserved
2831 final Notification.Builder builder = new Notification.Builder(context, this);
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002832
2833 // now apply the latestEventInfo fields
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002834 if (contentTitle != null) {
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002835 builder.setContentTitle(contentTitle);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002836 }
2837 if (contentText != null) {
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002838 builder.setContentText(contentText);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002839 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002840 builder.setContentIntent(contentIntent);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002841
2842 builder.build(); // callers expect this notification to be ready to use
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002843 }
2844
Julia Reynoldsda303542015-11-23 14:00:20 -05002845 /**
2846 * @hide
2847 */
2848 public static void addFieldsFromContext(Context context, Notification notification) {
Julia Reynoldse071abd2017-03-22 10:52:11 -04002849 addFieldsFromContext(context.getApplicationInfo(), notification);
Jeff Sharkey012bc7b2016-04-11 16:30:27 -06002850 }
2851
2852 /**
2853 * @hide
2854 */
Julia Reynoldse071abd2017-03-22 10:52:11 -04002855 public static void addFieldsFromContext(ApplicationInfo ai, Notification notification) {
Jeff Sharkey012bc7b2016-04-11 16:30:27 -06002856 notification.extras.putParcelable(EXTRA_BUILDER_APPLICATION_INFO, ai);
Julia Reynoldsda303542015-11-23 14:00:20 -05002857 }
2858
Yi Jin6b514142017-10-30 14:54:12 -07002859 /**
2860 * @hide
2861 */
2862 public void writeToProto(ProtoOutputStream proto, long fieldId) {
2863 long token = proto.start(fieldId);
2864 proto.write(NotificationProto.CHANNEL_ID, getChannelId());
2865 proto.write(NotificationProto.HAS_TICKER_TEXT, this.tickerText != null);
2866 proto.write(NotificationProto.FLAGS, this.flags);
2867 proto.write(NotificationProto.COLOR, this.color);
2868 proto.write(NotificationProto.CATEGORY, this.category);
2869 proto.write(NotificationProto.GROUP_KEY, this.mGroupKey);
2870 proto.write(NotificationProto.SORT_KEY, this.mSortKey);
2871 if (this.actions != null) {
2872 proto.write(NotificationProto.ACTION_LENGTH, this.actions.length);
2873 }
2874 if (this.visibility >= VISIBILITY_SECRET && this.visibility <= VISIBILITY_PUBLIC) {
2875 proto.write(NotificationProto.VISIBILITY, this.visibility);
2876 }
2877 if (publicVersion != null) {
2878 publicVersion.writeToProto(proto, NotificationProto.PUBLIC_VERSION);
2879 }
2880 proto.end(token);
2881 }
2882
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002883 @Override
2884 public String toString() {
2885 StringBuilder sb = new StringBuilder();
Julia Reynoldsb9e712e2017-04-17 10:31:03 -04002886 sb.append("Notification(channel=");
Julia Reynoldsbad42972017-04-25 13:52:49 -04002887 sb.append(getChannelId());
Julia Reynoldsb9e712e2017-04-17 10:31:03 -04002888 sb.append(" pri=");
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002889 sb.append(priority);
2890 sb.append(" contentView=");
Joe Onoratoc9596d62011-01-12 17:03:11 -08002891 if (contentView != null) {
2892 sb.append(contentView.getPackage());
2893 sb.append("/0x");
2894 sb.append(Integer.toHexString(contentView.getLayoutId()));
2895 } else {
2896 sb.append("null");
2897 }
2898 sb.append(" vibrate=");
Daniel Sandler6738eee2012-11-16 12:03:32 -05002899 if ((this.defaults & DEFAULT_VIBRATE) != 0) {
2900 sb.append("default");
2901 } else if (this.vibrate != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002902 int N = this.vibrate.length-1;
2903 sb.append("[");
2904 for (int i=0; i<N; i++) {
2905 sb.append(this.vibrate[i]);
2906 sb.append(',');
2907 }
Simon Schoar8cf97d92009-06-10 22:08:37 +02002908 if (N != -1) {
2909 sb.append(this.vibrate[N]);
2910 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002911 sb.append("]");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002912 } else {
2913 sb.append("null");
2914 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002915 sb.append(" sound=");
Daniel Sandler6738eee2012-11-16 12:03:32 -05002916 if ((this.defaults & DEFAULT_SOUND) != 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002917 sb.append("default");
Daniel Sandler6738eee2012-11-16 12:03:32 -05002918 } else if (this.sound != null) {
2919 sb.append(this.sound.toString());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002920 } else {
2921 sb.append("null");
2922 }
Chris Wren365b6d32015-07-16 10:39:26 -04002923 if (this.tickerText != null) {
2924 sb.append(" tick");
2925 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002926 sb.append(" defaults=0x");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002927 sb.append(Integer.toHexString(this.defaults));
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002928 sb.append(" flags=0x");
Daniel Sandlere46cbd32010-06-17 10:35:26 -04002929 sb.append(Integer.toHexString(this.flags));
Dan Sandler26e81cf2014-05-06 10:01:27 -04002930 sb.append(String.format(" color=0x%08x", this.color));
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002931 if (this.category != null) {
2932 sb.append(" category=");
2933 sb.append(this.category);
2934 }
2935 if (this.mGroupKey != null) {
2936 sb.append(" groupKey=");
2937 sb.append(this.mGroupKey);
2938 }
2939 if (this.mSortKey != null) {
2940 sb.append(" sortKey=");
2941 sb.append(this.mSortKey);
2942 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002943 if (actions != null) {
Dan Sandler1b718782014-07-18 12:43:45 -04002944 sb.append(" actions=");
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002945 sb.append(actions.length);
Dan Sandler1b718782014-07-18 12:43:45 -04002946 }
2947 sb.append(" vis=");
2948 sb.append(visibilityToString(this.visibility));
2949 if (this.publicVersion != null) {
2950 sb.append(" publicVersion=");
2951 sb.append(publicVersion.toString());
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002952 }
2953 sb.append(")");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002954 return sb.toString();
2955 }
Joe Onorato46439ce2010-11-19 13:56:21 -08002956
Dan Sandler1b718782014-07-18 12:43:45 -04002957 /**
2958 * {@hide}
2959 */
2960 public static String visibilityToString(int vis) {
2961 switch (vis) {
2962 case VISIBILITY_PRIVATE:
2963 return "PRIVATE";
2964 case VISIBILITY_PUBLIC:
2965 return "PUBLIC";
2966 case VISIBILITY_SECRET:
2967 return "SECRET";
2968 default:
2969 return "UNKNOWN(" + String.valueOf(vis) + ")";
2970 }
2971 }
2972
Joe Onoratocb109a02011-01-18 17:57:41 -08002973 /**
John Spurlock1d881a12015-03-18 19:21:54 -04002974 * {@hide}
2975 */
2976 public static String priorityToString(@Priority int pri) {
2977 switch (pri) {
2978 case PRIORITY_MIN:
2979 return "MIN";
2980 case PRIORITY_LOW:
2981 return "LOW";
2982 case PRIORITY_DEFAULT:
2983 return "DEFAULT";
2984 case PRIORITY_HIGH:
2985 return "HIGH";
2986 case PRIORITY_MAX:
2987 return "MAX";
2988 default:
2989 return "UNKNOWN(" + String.valueOf(pri) + ")";
2990 }
2991 }
2992
Julia Reynolds6ad0aec2017-07-05 08:47:03 -04002993 /**
2994 * @hide
2995 */
2996 public boolean hasCompletedProgress() {
2997 // not a progress notification; can't be complete
2998 if (!extras.containsKey(EXTRA_PROGRESS)
2999 || !extras.containsKey(EXTRA_PROGRESS_MAX)) {
3000 return false;
3001 }
3002 // many apps use max 0 for 'indeterminate'; not complete
3003 if (extras.getInt(EXTRA_PROGRESS_MAX) == 0) {
3004 return false;
3005 }
3006 return extras.getInt(EXTRA_PROGRESS) == extras.getInt(EXTRA_PROGRESS_MAX);
3007 }
3008
Jeff Sharkey000ce802017-04-29 13:13:27 -06003009 /** @removed */
3010 @Deprecated
Julia Reynolds37856052016-11-11 09:20:07 -05003011 public String getChannel() {
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04003012 return mChannelId;
3013 }
3014
3015 /**
Julia Reynoldsbad42972017-04-25 13:52:49 -04003016 * Returns the id of the channel this notification posts to.
3017 */
3018 public String getChannelId() {
3019 return mChannelId;
3020 }
3021
Jeff Sharkey000ce802017-04-29 13:13:27 -06003022 /** @removed */
3023 @Deprecated
Julia Reynolds2a128742016-11-28 14:29:25 -05003024 public long getTimeout() {
3025 return mTimeout;
3026 }
3027
3028 /**
Julia Reynoldsbad42972017-04-25 13:52:49 -04003029 * Returns the duration from posting after which this notification should be canceled by the
3030 * system, if it's not canceled already.
3031 */
3032 public long getTimeoutAfter() {
3033 return mTimeout;
3034 }
3035
3036 /**
Julia Reynoldse071abd2017-03-22 10:52:11 -04003037 * Returns what icon should be shown for this notification if it is being displayed in a
3038 * Launcher that supports badging. Will be one of {@link #BADGE_ICON_NONE},
3039 * {@link #BADGE_ICON_SMALL}, or {@link #BADGE_ICON_LARGE}.
3040 */
3041 public int getBadgeIconType() {
3042 return mBadgeIcon;
3043 }
3044
3045 /**
Julia Reynolds13d898c2017-02-02 12:22:05 -05003046 * Returns the {@link ShortcutInfo#getId() id} that this notification supersedes, if any.
Julia Reynoldsbad42972017-04-25 13:52:49 -04003047 *
3048 * <p>Used by some Launchers that display notification content to hide shortcuts that duplicate
3049 * notifications.
Julia Reynolds13d898c2017-02-02 12:22:05 -05003050 */
3051 public String getShortcutId() {
3052 return mShortcutId;
3053 }
3054
Julia Reynolds3aedded2017-03-31 14:42:09 -04003055
3056 /**
3057 * Returns the settings text provided to {@link Builder#setSettingsText(CharSequence)}.
3058 */
3059 public CharSequence getSettingsText() {
3060 return mSettingsText;
3061 }
3062
Julia Reynolds13d898c2017-02-02 12:22:05 -05003063 /**
Julia Reynoldsa79c3712017-04-21 10:29:57 -04003064 * Returns which type of notifications in a group are responsible for audibly alerting the
3065 * user. See {@link #GROUP_ALERT_ALL}, {@link #GROUP_ALERT_CHILDREN},
3066 * {@link #GROUP_ALERT_SUMMARY}.
3067 */
3068 public @GroupAlertBehavior int getGroupAlertBehavior() {
3069 return mGroupAlertBehavior;
3070 }
3071
3072 /**
Dan Sandlerd63f9322015-05-06 15:18:49 -04003073 * The small icon representing this notification in the status bar and content view.
3074 *
3075 * @return the small icon representing this notification.
3076 *
3077 * @see Builder#getSmallIcon()
3078 * @see Builder#setSmallIcon(Icon)
3079 */
3080 public Icon getSmallIcon() {
3081 return mSmallIcon;
3082 }
3083
3084 /**
3085 * Used when notifying to clean up legacy small icons.
3086 * @hide
3087 */
Mathew Inwood61e8ae62018-08-14 14:17:44 +01003088 @UnsupportedAppUsage
Dan Sandlerd63f9322015-05-06 15:18:49 -04003089 public void setSmallIcon(Icon icon) {
3090 mSmallIcon = icon;
3091 }
3092
3093 /**
3094 * The large icon shown in this notification's content view.
3095 * @see Builder#getLargeIcon()
3096 * @see Builder#setLargeIcon(Icon)
3097 */
3098 public Icon getLargeIcon() {
3099 return mLargeIcon;
3100 }
3101
3102 /**
Christoph Studer4600f9b2014-07-22 22:44:43 +02003103 * @hide
3104 */
Mathew Inwood61e8ae62018-08-14 14:17:44 +01003105 @UnsupportedAppUsage
Christoph Studerc8db24b2014-07-25 17:50:30 +02003106 public boolean isGroupSummary() {
3107 return mGroupKey != null && (flags & FLAG_GROUP_SUMMARY) != 0;
3108 }
3109
3110 /**
3111 * @hide
3112 */
Mathew Inwood61e8ae62018-08-14 14:17:44 +01003113 @UnsupportedAppUsage
Christoph Studerc8db24b2014-07-25 17:50:30 +02003114 public boolean isGroupChild() {
3115 return mGroupKey != null && (flags & FLAG_GROUP_SUMMARY) == 0;
3116 }
3117
3118 /**
Julia Reynolds30203152017-05-26 13:36:31 -04003119 * @hide
3120 */
3121 public boolean suppressAlertingDueToGrouping() {
3122 if (isGroupSummary()
3123 && getGroupAlertBehavior() == Notification.GROUP_ALERT_CHILDREN) {
3124 return true;
3125 } else if (isGroupChild()
3126 && getGroupAlertBehavior() == Notification.GROUP_ALERT_SUMMARY) {
3127 return true;
3128 }
3129 return false;
3130 }
3131
3132 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003133 * Builder class for {@link Notification} objects.
Joe Malin8d40d042012-11-05 11:36:40 -08003134 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003135 * Provides a convenient way to set the various fields of a {@link Notification} and generate
Scott Main183bf112012-08-13 19:12:13 -07003136 * content views using the platform's notification layout template. If your app supports
3137 * versions of Android as old as API level 4, you can instead use
3138 * {@link android.support.v4.app.NotificationCompat.Builder NotificationCompat.Builder},
3139 * available in the <a href="{@docRoot}tools/extras/support-library.html">Android Support
3140 * library</a>.
Joe Malin8d40d042012-11-05 11:36:40 -08003141 *
Scott Main183bf112012-08-13 19:12:13 -07003142 * <p>Example:
Joe Malin8d40d042012-11-05 11:36:40 -08003143 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003144 * <pre class="prettyprint">
Scott Main183bf112012-08-13 19:12:13 -07003145 * Notification noti = new Notification.Builder(mContext)
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003146 * .setContentTitle(&quot;New mail from &quot; + sender.toString())
3147 * .setContentText(subject)
3148 * .setSmallIcon(R.drawable.new_mail)
3149 * .setLargeIcon(aBitmap)
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003150 * .build();
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003151 * </pre>
Joe Onoratocb109a02011-01-18 17:57:41 -08003152 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003153 public static class Builder {
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05003154 /**
3155 * @hide
3156 */
3157 public static final String EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT =
3158 "android.rebuild.contentViewActionCount";
3159 /**
3160 * @hide
3161 */
3162 public static final String EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT
3163 = "android.rebuild.bigViewActionCount";
3164 /**
3165 * @hide
3166 */
3167 public static final String EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT
3168 = "android.rebuild.hudViewActionCount";
3169
Selim Cinek6743c0b2017-01-18 18:24:01 -08003170 private static final boolean USE_ONLY_TITLE_IN_LOW_PRIORITY_SUMMARY =
3171 SystemProperties.getBoolean("notifications.only_title", true);
3172
Selim Cinek389edcd2017-05-11 19:16:44 -07003173 /**
3174 * The lightness difference that has to be added to the primary text color to obtain the
3175 * secondary text color when the background is light.
3176 */
3177 private static final int LIGHTNESS_TEXT_DIFFERENCE_LIGHT = 20;
3178
3179 /**
3180 * The lightness difference that has to be added to the primary text color to obtain the
3181 * secondary text color when the background is dark.
3182 * A bit less then the above value, since it looks better on dark backgrounds.
3183 */
3184 private static final int LIGHTNESS_TEXT_DIFFERENCE_DARK = -10;
3185
Joe Onorato46439ce2010-11-19 13:56:21 -08003186 private Context mContext;
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003187 private Notification mN;
3188 private Bundle mUserExtras = new Bundle();
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003189 private Style mStyle;
Mathew Inwood61e8ae62018-08-14 14:17:44 +01003190 @UnsupportedAppUsage
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003191 private ArrayList<Action> mActions = new ArrayList<Action>(MAX_ACTION_BUTTONS);
Selim Cineke7238dd2017-12-14 17:48:32 -08003192 private ArrayList<Person> mPersonList = new ArrayList<>();
Lucas Dupina291d192018-06-07 13:59:42 -07003193 private ContrastColorUtil mColorUtil;
Selim Cinek7b9605b2017-01-19 17:36:00 -08003194 private boolean mIsLegacy;
3195 private boolean mIsLegacyInitialized;
Christoph Studer7ac80e62014-08-04 16:01:57 +02003196
3197 /**
Adrian Roos4ff3b122016-02-01 12:26:13 -08003198 * Caches a contrast-enhanced version of {@link #mCachedContrastColorIsFor}.
3199 */
3200 private int mCachedContrastColor = COLOR_INVALID;
3201 private int mCachedContrastColorIsFor = COLOR_INVALID;
Adrian Roos487374f2017-01-11 15:48:14 -08003202 /**
Selim Cinek4717d862018-04-19 09:19:15 +08003203 * Caches a ambient version of {@link #mCachedAmbientColorIsFor}.
Adrian Roos487374f2017-01-11 15:48:14 -08003204 */
3205 private int mCachedAmbientColor = COLOR_INVALID;
3206 private int mCachedAmbientColorIsFor = COLOR_INVALID;
Selim Cinek4717d862018-04-19 09:19:15 +08003207 /**
3208 * A neutral color color that can be used for icons.
3209 */
3210 private int mNeutralColor = COLOR_INVALID;
Adrian Roos4ff3b122016-02-01 12:26:13 -08003211
3212 /**
Adrian Roos70d7aa32017-01-11 15:39:06 -08003213 * Caches an instance of StandardTemplateParams. Note that this may have been used before,
3214 * so make sure to call {@link StandardTemplateParams#reset()} before using it.
3215 */
3216 StandardTemplateParams mParams = new StandardTemplateParams();
Selim Cinek7b9605b2017-01-19 17:36:00 -08003217 private int mTextColorsAreForBackground = COLOR_INVALID;
3218 private int mPrimaryTextColor = COLOR_INVALID;
3219 private int mSecondaryTextColor = COLOR_INVALID;
Selim Cinek5fb73f82017-04-20 16:55:38 -07003220 private int mBackgroundColor = COLOR_INVALID;
3221 private int mForegroundColor = COLOR_INVALID;
Selim Cinek5d6ef8d2017-05-18 22:16:00 -07003222 /**
3223 * A temporary location where actions are stored. If != null the view originally has action
3224 * but doesn't have any for this inflation.
3225 */
3226 private ArrayList<Action> mOriginalActions;
Selim Cineka7679b62017-05-10 16:33:25 -07003227 private boolean mRebuildStyledRemoteViews;
Adrian Roos70d7aa32017-01-11 15:39:06 -08003228
Anthony Chenad4d1582017-04-10 16:07:58 -07003229 private boolean mTintActionButtons;
3230 private boolean mInNightMode;
3231
Adrian Roos70d7aa32017-01-11 15:39:06 -08003232 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003233 * Constructs a new Builder with the defaults:
Joe Onoratocb109a02011-01-18 17:57:41 -08003234 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003235 * @param context
3236 * A {@link Context} that will be used by the Builder to construct the
3237 * RemoteViews. The Context will not be held past the lifetime of this Builder
3238 * object.
Geoffrey Pitsch5caa2762017-01-12 09:35:54 -05003239 * @param channelId
3240 * The constructed Notification will be posted on this
3241 * {@link NotificationChannel}. To use a NotificationChannel, it must first be
3242 * created using {@link NotificationManager#createNotificationChannel}.
Joe Onoratocb109a02011-01-18 17:57:41 -08003243 */
Geoffrey Pitsch5caa2762017-01-12 09:35:54 -05003244 public Builder(Context context, String channelId) {
3245 this(context, (Notification) null);
3246 mN.mChannelId = channelId;
3247 }
3248
3249 /**
3250 * @deprecated use {@link Notification.Builder#Notification.Builder(Context, String)}
3251 * instead. All posted Notifications must specify a NotificationChannel Id.
3252 */
3253 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08003254 public Builder(Context context) {
Geoffrey Pitsch5caa2762017-01-12 09:35:54 -05003255 this(context, (Notification) null);
Joe Onorato46439ce2010-11-19 13:56:21 -08003256 }
3257
Joe Onoratocb109a02011-01-18 17:57:41 -08003258 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003259 * @hide
Christoph Studer4600f9b2014-07-22 22:44:43 +02003260 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003261 public Builder(Context context, Notification toAdopt) {
3262 mContext = context;
Anthony Chenad4d1582017-04-10 16:07:58 -07003263 Resources res = mContext.getResources();
3264 mTintActionButtons = res.getBoolean(R.bool.config_tintNotificationActionButtons);
3265
3266 if (res.getBoolean(R.bool.config_enableNightMode)) {
3267 Configuration currentConfig = res.getConfiguration();
3268 mInNightMode = (currentConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK)
3269 == Configuration.UI_MODE_NIGHT_YES;
3270 }
Christoph Studer4600f9b2014-07-22 22:44:43 +02003271
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003272 if (toAdopt == null) {
3273 mN = new Notification();
Selim Cinek0ff1ce602016-04-05 18:27:16 -07003274 if (context.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N) {
3275 mN.extras.putBoolean(EXTRA_SHOW_WHEN, true);
3276 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003277 mN.priority = PRIORITY_DEFAULT;
3278 mN.visibility = VISIBILITY_PRIVATE;
3279 } else {
3280 mN = toAdopt;
3281 if (mN.actions != null) {
3282 Collections.addAll(mActions, mN.actions);
Christoph Studer4600f9b2014-07-22 22:44:43 +02003283 }
3284
Selim Cineke7238dd2017-12-14 17:48:32 -08003285 if (mN.extras.containsKey(EXTRA_PEOPLE_LIST)) {
3286 ArrayList<Person> people = mN.extras.getParcelableArrayList(EXTRA_PEOPLE_LIST);
3287 mPersonList.addAll(people);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003288 }
3289
Selim Cinek4ac6f602016-06-13 15:47:03 -07003290 if (mN.getSmallIcon() == null && mN.icon != 0) {
3291 setSmallIcon(mN.icon);
3292 }
3293
3294 if (mN.getLargeIcon() == null && mN.largeIcon != null) {
3295 setLargeIcon(mN.largeIcon);
3296 }
3297
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003298 String templateClass = mN.extras.getString(EXTRA_TEMPLATE);
3299 if (!TextUtils.isEmpty(templateClass)) {
3300 final Class<? extends Style> styleClass
3301 = getNotificationStyleClass(templateClass);
3302 if (styleClass == null) {
3303 Log.d(TAG, "Unknown style class: " + templateClass);
3304 } else {
3305 try {
Adrian Roosc1a80b02016-04-05 14:54:55 -07003306 final Constructor<? extends Style> ctor =
3307 styleClass.getDeclaredConstructor();
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003308 ctor.setAccessible(true);
3309 final Style style = ctor.newInstance();
3310 style.restoreFromExtras(mN.extras);
3311
3312 if (style != null) {
3313 setStyle(style);
3314 }
3315 } catch (Throwable t) {
3316 Log.e(TAG, "Could not create Style", t);
3317 }
3318 }
3319 }
3320
3321 }
3322 }
3323
Lucas Dupina291d192018-06-07 13:59:42 -07003324 private ContrastColorUtil getColorUtil() {
Selim Cinek99104832017-01-25 14:47:33 -08003325 if (mColorUtil == null) {
Lucas Dupina291d192018-06-07 13:59:42 -07003326 mColorUtil = ContrastColorUtil.getInstance(mContext);
Christoph Studer4600f9b2014-07-22 22:44:43 +02003327 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003328 return mColorUtil;
Christoph Studer4600f9b2014-07-22 22:44:43 +02003329 }
3330
3331 /**
Julia Reynolds13d898c2017-02-02 12:22:05 -05003332 * If this notification is duplicative of a Launcher shortcut, sets the
3333 * {@link ShortcutInfo#getId() id} of the shortcut, in case the Launcher wants to hide
3334 * the shortcut.
3335 *
Julia Reynoldsbad42972017-04-25 13:52:49 -04003336 * This field will be ignored by Launchers that don't support badging, don't show
3337 * notification content, or don't show {@link android.content.pm.ShortcutManager shortcuts}.
Julia Reynolds13d898c2017-02-02 12:22:05 -05003338 *
3339 * @param shortcutId the {@link ShortcutInfo#getId() id} of the shortcut this notification
3340 * supersedes
3341 */
3342 public Builder setShortcutId(String shortcutId) {
3343 mN.mShortcutId = shortcutId;
3344 return this;
3345 }
3346
3347 /**
Julia Reynoldse071abd2017-03-22 10:52:11 -04003348 * Sets which icon to display as a badge for this notification.
3349 *
3350 * Must be one of {@link #BADGE_ICON_NONE}, {@link #BADGE_ICON_SMALL},
3351 * {@link #BADGE_ICON_LARGE}.
3352 *
3353 * Note: This value might be ignored, for launchers that don't support badge icons.
3354 */
Julia Reynolds612beb22017-03-30 10:48:30 -04003355 public Builder setBadgeIconType(int icon) {
Julia Reynoldse071abd2017-03-22 10:52:11 -04003356 mN.mBadgeIcon = icon;
3357 return this;
3358 }
3359
3360 /**
Julia Reynoldsa79c3712017-04-21 10:29:57 -04003361 * Sets the group alert behavior for this notification. Use this method to mute this
3362 * notification if alerts for this notification's group should be handled by a different
3363 * notification. This is only applicable for notifications that belong to a
Julia Reynolds399d9bf2017-08-11 12:52:14 -04003364 * {@link #setGroup(String) group}. This must be called on all notifications you want to
3365 * mute. For example, if you want only the summary of your group to make noise, all
3366 * children in the group should have the group alert behavior {@link #GROUP_ALERT_SUMMARY}.
Julia Reynoldsa79c3712017-04-21 10:29:57 -04003367 *
3368 * <p> The default value is {@link #GROUP_ALERT_ALL}.</p>
3369 */
3370 public Builder setGroupAlertBehavior(@GroupAlertBehavior int groupAlertBehavior) {
3371 mN.mGroupAlertBehavior = groupAlertBehavior;
3372 return this;
3373 }
3374
Jeff Sharkey000ce802017-04-29 13:13:27 -06003375 /** @removed */
3376 @Deprecated
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04003377 public Builder setChannel(String channelId) {
3378 mN.mChannelId = channelId;
3379 return this;
3380 }
3381
3382 /**
Julia Reynoldsbad42972017-04-25 13:52:49 -04003383 * Specifies the channel the notification should be delivered on.
3384 */
3385 public Builder setChannelId(String channelId) {
3386 mN.mChannelId = channelId;
3387 return this;
3388 }
3389
Jeff Sharkey000ce802017-04-29 13:13:27 -06003390 /** @removed */
3391 @Deprecated
Julia Reynolds50989772017-02-23 14:32:16 -05003392 public Builder setTimeout(long durationMs) {
3393 mN.mTimeout = durationMs;
Julia Reynolds2a128742016-11-28 14:29:25 -05003394 return this;
3395 }
3396
3397 /**
Julia Reynoldsbad42972017-04-25 13:52:49 -04003398 * Specifies a duration in milliseconds after which this notification should be canceled,
3399 * if it is not already canceled.
3400 */
3401 public Builder setTimeoutAfter(long durationMs) {
3402 mN.mTimeout = durationMs;
3403 return this;
3404 }
3405
3406 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003407 * Add a timestamp pertaining to the notification (usually the time the event occurred).
Selim Cinek0ff1ce602016-04-05 18:27:16 -07003408 *
3409 * For apps targeting {@link android.os.Build.VERSION_CODES#N} and above, this time is not
3410 * shown anymore by default and must be opted into by using
3411 * {@link android.app.Notification.Builder#setShowWhen(boolean)}
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003412 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003413 * @see Notification#when
Joe Onoratocb109a02011-01-18 17:57:41 -08003414 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003415 public Builder setWhen(long when) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003416 mN.when = when;
Joe Onorato46439ce2010-11-19 13:56:21 -08003417 return this;
3418 }
3419
Joe Onoratocb109a02011-01-18 17:57:41 -08003420 /**
Griff Hazen50c11652014-05-16 09:46:31 -07003421 * Control whether the timestamp set with {@link #setWhen(long) setWhen} is shown
Daniel Sandler0c890492012-09-12 17:23:10 -07003422 * in the content view.
Selim Cinek0ff1ce602016-04-05 18:27:16 -07003423 * For apps targeting {@link android.os.Build.VERSION_CODES#N} and above, this defaults to
3424 * {@code false}. For earlier apps, the default is {@code true}.
Daniel Sandler0c890492012-09-12 17:23:10 -07003425 */
3426 public Builder setShowWhen(boolean show) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003427 mN.extras.putBoolean(EXTRA_SHOW_WHEN, show);
Daniel Sandler0c890492012-09-12 17:23:10 -07003428 return this;
3429 }
3430
3431 /**
Daniel Sandlerd33b8032012-05-10 11:41:48 -04003432 * Show the {@link Notification#when} field as a stopwatch.
Joe Malin8d40d042012-11-05 11:36:40 -08003433 *
3434 * Instead of presenting <code>when</code> as a timestamp, the notification will show an
Daniel Sandlerd33b8032012-05-10 11:41:48 -04003435 * automatically updating display of the minutes and seconds since <code>when</code>.
Daniel Sandlera2985ed2012-04-03 16:42:00 -04003436 *
Daniel Sandlerd33b8032012-05-10 11:41:48 -04003437 * Useful when showing an elapsed time (like an ongoing phone call).
3438 *
Selim Cinek81c23aa2016-02-25 16:23:13 -08003439 * The counter can also be set to count down to <code>when</code> when using
Adrian Roos96b7e202016-05-17 13:50:38 -07003440 * {@link #setChronometerCountDown(boolean)}.
Selim Cinek81c23aa2016-02-25 16:23:13 -08003441 *
Daniel Sandlerd33b8032012-05-10 11:41:48 -04003442 * @see android.widget.Chronometer
Daniel Sandlera2985ed2012-04-03 16:42:00 -04003443 * @see Notification#when
Adrian Roos96b7e202016-05-17 13:50:38 -07003444 * @see #setChronometerCountDown(boolean)
Daniel Sandlera2985ed2012-04-03 16:42:00 -04003445 */
3446 public Builder setUsesChronometer(boolean b) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003447 mN.extras.putBoolean(EXTRA_SHOW_CHRONOMETER, b);
Daniel Sandlera2985ed2012-04-03 16:42:00 -04003448 return this;
3449 }
3450
3451 /**
Selim Cinek81c23aa2016-02-25 16:23:13 -08003452 * Sets the Chronometer to count down instead of counting up.
3453 *
3454 * <p>This is only relevant if {@link #setUsesChronometer(boolean)} has been set to true.
3455 * If it isn't set the chronometer will count up.
3456 *
3457 * @see #setUsesChronometer(boolean)
3458 */
Adrian Roos96b7e202016-05-17 13:50:38 -07003459 public Builder setChronometerCountDown(boolean countDown) {
3460 mN.extras.putBoolean(EXTRA_CHRONOMETER_COUNT_DOWN, countDown);
Selim Cinek81c23aa2016-02-25 16:23:13 -08003461 return this;
3462 }
3463
3464 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003465 * Set the small icon resource, which will be used to represent the notification in the
3466 * status bar.
Joe Onoratocb109a02011-01-18 17:57:41 -08003467 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003468
3469 * The platform template for the expanded view will draw this icon in the left, unless a
3470 * {@link #setLargeIcon(Bitmap) large icon} has also been specified, in which case the small
3471 * icon will be moved to the right-hand side.
3472 *
3473
3474 * @param icon
3475 * A resource ID in the application's package of the drawable to use.
3476 * @see Notification#icon
Joe Onoratocb109a02011-01-18 17:57:41 -08003477 */
Tor Norbye7b9c9122013-05-30 16:48:33 -07003478 public Builder setSmallIcon(@DrawableRes int icon) {
Dan Sandlerd63f9322015-05-06 15:18:49 -04003479 return setSmallIcon(icon != 0
3480 ? Icon.createWithResource(mContext, icon)
3481 : null);
Joe Onorato46439ce2010-11-19 13:56:21 -08003482 }
3483
Joe Onoratocb109a02011-01-18 17:57:41 -08003484 /**
3485 * A variant of {@link #setSmallIcon(int) setSmallIcon(int)} that takes an additional
3486 * level parameter for when the icon is a {@link android.graphics.drawable.LevelListDrawable
3487 * LevelListDrawable}.
3488 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003489 * @param icon A resource ID in the application's package of the drawable to use.
Joe Onoratocb109a02011-01-18 17:57:41 -08003490 * @param level The level to use for the icon.
3491 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003492 * @see Notification#icon
3493 * @see Notification#iconLevel
Joe Onoratocb109a02011-01-18 17:57:41 -08003494 */
Tor Norbye7b9c9122013-05-30 16:48:33 -07003495 public Builder setSmallIcon(@DrawableRes int icon, int level) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003496 mN.iconLevel = level;
Dan Sandlerd63f9322015-05-06 15:18:49 -04003497 return setSmallIcon(icon);
3498 }
3499
3500 /**
3501 * Set the small icon, which will be used to represent the notification in the
koprivadebd4ee2018-09-13 10:59:46 -07003502 * status bar and content view (unless overridden there by a
Dan Sandlerd63f9322015-05-06 15:18:49 -04003503 * {@link #setLargeIcon(Bitmap) large icon}).
3504 *
3505 * @param icon An Icon object to use.
3506 * @see Notification#icon
3507 */
3508 public Builder setSmallIcon(Icon icon) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003509 mN.setSmallIcon(icon);
3510 if (icon != null && icon.getType() == Icon.TYPE_RESOURCE) {
3511 mN.icon = icon.getResId();
3512 }
Joe Onorato46439ce2010-11-19 13:56:21 -08003513 return this;
3514 }
3515
Joe Onoratocb109a02011-01-18 17:57:41 -08003516 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003517 * Set the first line of text in the platform notification template.
Joe Onoratocb109a02011-01-18 17:57:41 -08003518 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003519 public Builder setContentTitle(CharSequence title) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003520 mN.extras.putCharSequence(EXTRA_TITLE, safeCharSequence(title));
Joe Onorato46439ce2010-11-19 13:56:21 -08003521 return this;
3522 }
3523
Joe Onoratocb109a02011-01-18 17:57:41 -08003524 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003525 * Set the second line of text in the platform notification template.
Joe Onoratocb109a02011-01-18 17:57:41 -08003526 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003527 public Builder setContentText(CharSequence text) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003528 mN.extras.putCharSequence(EXTRA_TEXT, safeCharSequence(text));
Joe Onorato46439ce2010-11-19 13:56:21 -08003529 return this;
3530 }
3531
Joe Onoratocb109a02011-01-18 17:57:41 -08003532 /**
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07003533 * This provides some additional information that is displayed in the notification. No
3534 * guarantees are given where exactly it is displayed.
3535 *
3536 * <p>This information should only be provided if it provides an essential
3537 * benefit to the understanding of the notification. The more text you provide the
3538 * less readable it becomes. For example, an email client should only provide the account
3539 * name here if more than one email account has been added.</p>
3540 *
3541 * <p>As of {@link android.os.Build.VERSION_CODES#N} this information is displayed in the
3542 * notification header area.
3543 *
3544 * On Android versions before {@link android.os.Build.VERSION_CODES#N}
3545 * this will be shown in the third line of text in the platform notification template.
3546 * You should not be using {@link #setProgress(int, int, boolean)} at the
3547 * same time on those versions; they occupy the same place.
3548 * </p>
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003549 */
3550 public Builder setSubText(CharSequence text) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003551 mN.extras.putCharSequence(EXTRA_SUB_TEXT, safeCharSequence(text));
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003552 return this;
3553 }
3554
3555 /**
Julia Reynolds3aedded2017-03-31 14:42:09 -04003556 * Provides text that will appear as a link to your application's settings.
3557 *
3558 * <p>This text does not appear within notification {@link Style templates} but may
3559 * appear when the user uses an affordance to learn more about the notification.
3560 * Additionally, this text will not appear unless you provide a valid link target by
3561 * handling {@link #INTENT_CATEGORY_NOTIFICATION_PREFERENCES}.
3562 *
3563 * <p>This text is meant to be concise description about what the user can customize
3564 * when they click on this link. The recommended maximum length is 40 characters.
3565 * @param text
3566 * @return
3567 */
3568 public Builder setSettingsText(CharSequence text) {
3569 mN.mSettingsText = safeCharSequence(text);
3570 return this;
3571 }
3572
3573 /**
Adrian Roose458aa82015-12-08 16:17:19 -08003574 * Set the remote input history.
3575 *
3576 * This should be set to the most recent inputs that have been sent
3577 * through a {@link RemoteInput} of this Notification and cleared once the it is no
3578 * longer relevant (e.g. for chat notifications once the other party has responded).
3579 *
3580 * The most recent input must be stored at the 0 index, the second most recent at the
3581 * 1 index, etc. Note that the system will limit both how far back the inputs will be shown
3582 * and how much of each individual input is shown.
3583 *
3584 * <p>Note: The reply text will only be shown on notifications that have least one action
3585 * with a {@code RemoteInput}.</p>
3586 */
3587 public Builder setRemoteInputHistory(CharSequence[] text) {
3588 if (text == null) {
3589 mN.extras.putCharSequenceArray(EXTRA_REMOTE_INPUT_HISTORY, null);
3590 } else {
3591 final int N = Math.min(MAX_REPLY_HISTORY, text.length);
3592 CharSequence[] safe = new CharSequence[N];
3593 for (int i = 0; i < N; i++) {
3594 safe[i] = safeCharSequence(text[i]);
3595 }
3596 mN.extras.putCharSequenceArray(EXTRA_REMOTE_INPUT_HISTORY, safe);
3597 }
3598 return this;
3599 }
3600
3601 /**
Kenny Guya0f6de82018-04-06 16:20:16 +01003602 * Sets whether remote history entries view should have a spinner.
3603 * @hide
3604 */
3605 public Builder setShowRemoteInputSpinner(boolean showSpinner) {
3606 mN.extras.putBoolean(EXTRA_SHOW_REMOTE_INPUT_SPINNER, showSpinner);
3607 return this;
3608 }
3609
3610 /**
Kenny Guy8cc15d22018-05-09 09:50:55 +01003611 * Sets whether smart reply buttons should be hidden.
3612 * @hide
3613 */
3614 public Builder setHideSmartReplies(boolean hideSmartReplies) {
3615 mN.extras.putBoolean(EXTRA_HIDE_SMART_REPLIES, hideSmartReplies);
3616 return this;
3617 }
3618
3619 /**
Julia Reynolds13d898c2017-02-02 12:22:05 -05003620 * Sets the number of items this notification represents. May be displayed as a badge count
3621 * for Launchers that support badging.
Joe Onoratocb109a02011-01-18 17:57:41 -08003622 */
Joe Onorato8595a3d2010-11-19 18:12:07 -08003623 public Builder setNumber(int number) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003624 mN.number = number;
Joe Onorato8595a3d2010-11-19 18:12:07 -08003625 return this;
3626 }
3627
Joe Onoratocb109a02011-01-18 17:57:41 -08003628 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003629 * A small piece of additional information pertaining to this notification.
3630 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003631 * The platform template will draw this on the last line of the notification, at the far
3632 * right (to the right of a smallIcon if it has been placed there).
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07003633 *
3634 * @deprecated use {@link #setSubText(CharSequence)} instead to set a text in the header.
3635 * For legacy apps targeting a version below {@link android.os.Build.VERSION_CODES#N} this
3636 * field will still show up, but the subtext will take precedence.
Joe Onoratocb109a02011-01-18 17:57:41 -08003637 */
Aurimas Liutikas514c5ef2016-05-24 15:22:55 -07003638 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08003639 public Builder setContentInfo(CharSequence info) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003640 mN.extras.putCharSequence(EXTRA_INFO_TEXT, safeCharSequence(info));
Joe Onorato46439ce2010-11-19 13:56:21 -08003641 return this;
3642 }
3643
Joe Onoratocb109a02011-01-18 17:57:41 -08003644 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003645 * Set the progress this notification represents.
3646 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003647 * The platform template will represent this using a {@link ProgressBar}.
Jeff Sharkey1c400132011-08-05 14:50:13 -07003648 */
3649 public Builder setProgress(int max, int progress, boolean indeterminate) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003650 mN.extras.putInt(EXTRA_PROGRESS, progress);
3651 mN.extras.putInt(EXTRA_PROGRESS_MAX, max);
3652 mN.extras.putBoolean(EXTRA_PROGRESS_INDETERMINATE, indeterminate);
Jeff Sharkey1c400132011-08-05 14:50:13 -07003653 return this;
3654 }
3655
3656 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003657 * Supply a custom RemoteViews to use instead of the platform template.
3658 *
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003659 * Use {@link #setCustomContentView(RemoteViews)} instead.
Joe Onoratocb109a02011-01-18 17:57:41 -08003660 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003661 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08003662 public Builder setContent(RemoteViews views) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003663 return setCustomContentView(views);
3664 }
3665
3666 /**
3667 * Supply custom RemoteViews to use instead of the platform template.
3668 *
3669 * This will override the layout that would otherwise be constructed by this Builder
3670 * object.
3671 */
3672 public Builder setCustomContentView(RemoteViews contentView) {
3673 mN.contentView = contentView;
3674 return this;
3675 }
3676
3677 /**
3678 * Supply custom RemoteViews to use instead of the platform template in the expanded form.
3679 *
3680 * This will override the expanded layout that would otherwise be constructed by this
3681 * Builder object.
3682 */
3683 public Builder setCustomBigContentView(RemoteViews contentView) {
3684 mN.bigContentView = contentView;
3685 return this;
3686 }
3687
3688 /**
3689 * Supply custom RemoteViews to use instead of the platform template in the heads up dialog.
3690 *
3691 * This will override the heads-up layout that would otherwise be constructed by this
3692 * Builder object.
3693 */
3694 public Builder setCustomHeadsUpContentView(RemoteViews contentView) {
3695 mN.headsUpContentView = contentView;
Joe Onorato46439ce2010-11-19 13:56:21 -08003696 return this;
3697 }
3698
Joe Onoratocb109a02011-01-18 17:57:41 -08003699 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003700 * Supply a {@link PendingIntent} to be sent when the notification is clicked.
3701 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003702 * As of {@link android.os.Build.VERSION_CODES#HONEYCOMB}, if this field is unset and you
3703 * have specified a custom RemoteViews with {@link #setContent(RemoteViews)}, you can use
3704 * {@link RemoteViews#setOnClickPendingIntent RemoteViews.setOnClickPendingIntent(int,PendingIntent)}
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003705 * to assign PendingIntents to individual views in that custom layout (i.e., to create
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003706 * clickable buttons inside the notification view).
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003707 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003708 * @see Notification#contentIntent Notification.contentIntent
Joe Onoratocb109a02011-01-18 17:57:41 -08003709 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003710 public Builder setContentIntent(PendingIntent intent) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003711 mN.contentIntent = intent;
Joe Onorato46439ce2010-11-19 13:56:21 -08003712 return this;
3713 }
3714
Joe Onoratocb109a02011-01-18 17:57:41 -08003715 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003716 * Supply a {@link PendingIntent} to send when the notification is cleared explicitly by the user.
3717 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003718 * @see Notification#deleteIntent
Joe Onoratocb109a02011-01-18 17:57:41 -08003719 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003720 public Builder setDeleteIntent(PendingIntent intent) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003721 mN.deleteIntent = intent;
Joe Onorato46439ce2010-11-19 13:56:21 -08003722 return this;
3723 }
3724
Joe Onoratocb109a02011-01-18 17:57:41 -08003725 /**
3726 * An intent to launch instead of posting the notification to the status bar.
3727 * Only for use with extremely high-priority notifications demanding the user's
3728 * <strong>immediate</strong> attention, such as an incoming phone call or
3729 * alarm clock that the user has explicitly set to a particular time.
3730 * If this facility is used for something else, please give the user an option
3731 * to turn it off and use a normal notification, as this can be extremely
3732 * disruptive.
3733 *
Chris Wren47c20a12014-06-18 17:27:29 -04003734 * <p>
3735 * The system UI may choose to display a heads-up notification, instead of
3736 * launching this intent, while the user is using the device.
3737 * </p>
3738 *
Joe Onoratocb109a02011-01-18 17:57:41 -08003739 * @param intent The pending intent to launch.
3740 * @param highPriority Passing true will cause this notification to be sent
3741 * even if other notifications are suppressed.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003742 *
3743 * @see Notification#fullScreenIntent
Joe Onoratocb109a02011-01-18 17:57:41 -08003744 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003745 public Builder setFullScreenIntent(PendingIntent intent, boolean highPriority) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003746 mN.fullScreenIntent = intent;
Joe Onorato46439ce2010-11-19 13:56:21 -08003747 setFlag(FLAG_HIGH_PRIORITY, highPriority);
3748 return this;
3749 }
3750
Joe Onoratocb109a02011-01-18 17:57:41 -08003751 /**
Dan Sandler5fcdf6e2014-07-18 11:31:15 -04003752 * Set the "ticker" text which is sent to accessibility services.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003753 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003754 * @see Notification#tickerText
Joe Onoratocb109a02011-01-18 17:57:41 -08003755 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003756 public Builder setTicker(CharSequence tickerText) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003757 mN.tickerText = safeCharSequence(tickerText);
Joe Onorato46439ce2010-11-19 13:56:21 -08003758 return this;
3759 }
3760
Joe Onoratocb109a02011-01-18 17:57:41 -08003761 /**
Dan Sandler5fcdf6e2014-07-18 11:31:15 -04003762 * Obsolete version of {@link #setTicker(CharSequence)}.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003763 *
Joe Onoratocb109a02011-01-18 17:57:41 -08003764 */
Dan Sandler5fcdf6e2014-07-18 11:31:15 -04003765 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08003766 public Builder setTicker(CharSequence tickerText, RemoteViews views) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003767 setTicker(tickerText);
3768 // views is ignored
Joe Onorato46439ce2010-11-19 13:56:21 -08003769 return this;
3770 }
3771
Joe Onoratocb109a02011-01-18 17:57:41 -08003772 /**
Dan Sandlerd63f9322015-05-06 15:18:49 -04003773 * Add a large icon to the notification content view.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003774 *
3775 * In the platform template, this image will be shown on the left of the notification view
Dan Sandlerd63f9322015-05-06 15:18:49 -04003776 * in place of the {@link #setSmallIcon(Icon) small icon} (which will be placed in a small
3777 * badge atop the large icon).
Dan Sandler08a04c12015-05-06 15:18:49 -04003778 */
Dan Sandlerd63f9322015-05-06 15:18:49 -04003779 public Builder setLargeIcon(Bitmap b) {
3780 return setLargeIcon(b != null ? Icon.createWithBitmap(b) : null);
3781 }
3782
3783 /**
3784 * Add a large icon to the notification content view.
3785 *
3786 * In the platform template, this image will be shown on the left of the notification view
3787 * in place of the {@link #setSmallIcon(Icon) small icon} (which will be placed in a small
3788 * badge atop the large icon).
3789 */
3790 public Builder setLargeIcon(Icon icon) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003791 mN.mLargeIcon = icon;
3792 mN.extras.putParcelable(EXTRA_LARGE_ICON, icon);
Joe Onorato46439ce2010-11-19 13:56:21 -08003793 return this;
3794 }
3795
Joe Onoratocb109a02011-01-18 17:57:41 -08003796 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003797 * Set the sound to play.
3798 *
John Spurlockc0650f022014-07-19 13:22:39 -04003799 * It will be played using the {@link #AUDIO_ATTRIBUTES_DEFAULT default audio attributes}
3800 * for notifications.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003801 *
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003802 * @deprecated use {@link NotificationChannel#setSound(Uri, AudioAttributes)} instead.
Joe Onoratocb109a02011-01-18 17:57:41 -08003803 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003804 @Deprecated
Joe Onorato52f80cd2010-11-21 15:34:48 -08003805 public Builder setSound(Uri sound) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003806 mN.sound = sound;
3807 mN.audioAttributes = AUDIO_ATTRIBUTES_DEFAULT;
Joe Onorato52f80cd2010-11-21 15:34:48 -08003808 return this;
3809 }
3810
Joe Onoratocb109a02011-01-18 17:57:41 -08003811 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003812 * Set the sound to play, along with a specific stream on which to play it.
Joe Onoratocb109a02011-01-18 17:57:41 -08003813 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003814 * See {@link android.media.AudioManager} for the <code>STREAM_</code> constants.
3815 *
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003816 * @deprecated use {@link NotificationChannel#setSound(Uri, AudioAttributes)}.
Joe Onoratocb109a02011-01-18 17:57:41 -08003817 */
Jean-Michel Trivi81f871e2014-08-06 16:32:38 -07003818 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08003819 public Builder setSound(Uri sound, int streamType) {
Jean-Michel Trivi2f7511f2016-11-28 15:40:27 -08003820 PlayerBase.deprecateStreamTypeForPlayback(streamType, "Notification", "setSound()");
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003821 mN.sound = sound;
3822 mN.audioStreamType = streamType;
Joe Onorato46439ce2010-11-19 13:56:21 -08003823 return this;
3824 }
3825
Joe Onoratocb109a02011-01-18 17:57:41 -08003826 /**
John Spurlockc0650f022014-07-19 13:22:39 -04003827 * Set the sound to play, along with specific {@link AudioAttributes audio attributes} to
3828 * use during playback.
3829 *
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003830 * @deprecated use {@link NotificationChannel#setSound(Uri, AudioAttributes)} instead.
John Spurlockc0650f022014-07-19 13:22:39 -04003831 * @see Notification#sound
3832 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003833 @Deprecated
John Spurlockc0650f022014-07-19 13:22:39 -04003834 public Builder setSound(Uri sound, AudioAttributes audioAttributes) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003835 mN.sound = sound;
3836 mN.audioAttributes = audioAttributes;
John Spurlockc0650f022014-07-19 13:22:39 -04003837 return this;
3838 }
3839
3840 /**
Joe Onoratocb109a02011-01-18 17:57:41 -08003841 * Set the vibration pattern to use.
3842 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003843 * See {@link android.os.Vibrator#vibrate(long[], int)} for a discussion of the
3844 * <code>pattern</code> parameter.
3845 *
Chris Wren47c20a12014-06-18 17:27:29 -04003846 * <p>
3847 * A notification that vibrates is more likely to be presented as a heads-up notification.
3848 * </p>
3849 *
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003850 * @deprecated use {@link NotificationChannel#setVibrationPattern(long[])} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003851 * @see Notification#vibrate
Joe Onoratocb109a02011-01-18 17:57:41 -08003852 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003853 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08003854 public Builder setVibrate(long[] pattern) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003855 mN.vibrate = pattern;
Joe Onorato46439ce2010-11-19 13:56:21 -08003856 return this;
3857 }
3858
Joe Onoratocb109a02011-01-18 17:57:41 -08003859 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003860 * Set the desired color for the indicator LED on the device, as well as the
3861 * blink duty cycle (specified in milliseconds).
3862 *
3863
3864 * Not all devices will honor all (or even any) of these values.
3865 *
Julia Reynolds529e3322017-02-06 08:33:01 -05003866 * @deprecated use {@link NotificationChannel#enableLights(boolean)} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003867 * @see Notification#ledARGB
3868 * @see Notification#ledOnMS
3869 * @see Notification#ledOffMS
Joe Onoratocb109a02011-01-18 17:57:41 -08003870 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003871 @Deprecated
Tor Norbye80756e32015-03-02 09:39:27 -08003872 public Builder setLights(@ColorInt int argb, int onMs, int offMs) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003873 mN.ledARGB = argb;
3874 mN.ledOnMS = onMs;
3875 mN.ledOffMS = offMs;
Julia Reynolds10ee1fc2015-11-09 11:04:55 -05003876 if (onMs != 0 || offMs != 0) {
3877 mN.flags |= FLAG_SHOW_LIGHTS;
3878 }
Joe Onorato46439ce2010-11-19 13:56:21 -08003879 return this;
3880 }
3881
Joe Onoratocb109a02011-01-18 17:57:41 -08003882 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003883 * Set whether this is an "ongoing" notification.
Joe Onoratocb109a02011-01-18 17:57:41 -08003884 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003885
3886 * Ongoing notifications cannot be dismissed by the user, so your application or service
3887 * must take care of canceling them.
3888 *
3889
3890 * They are typically used to indicate a background task that the user is actively engaged
3891 * with (e.g., playing music) or is pending in some way and therefore occupying the device
3892 * (e.g., a file download, sync operation, active network connection).
3893 *
3894
3895 * @see Notification#FLAG_ONGOING_EVENT
Joe Onoratocb109a02011-01-18 17:57:41 -08003896 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003897 public Builder setOngoing(boolean ongoing) {
3898 setFlag(FLAG_ONGOING_EVENT, ongoing);
3899 return this;
3900 }
3901
Joe Onoratocb109a02011-01-18 17:57:41 -08003902 /**
Selim Cinek7b9605b2017-01-19 17:36:00 -08003903 * Set whether this notification should be colorized. When set, the color set with
3904 * {@link #setColor(int)} will be used as the background color of this notification.
3905 * <p>
Selim Cinek7b9605b2017-01-19 17:36:00 -08003906 * This should only be used for high priority ongoing tasks like navigation, an ongoing
3907 * call, or other similarly high-priority events for the user.
Selim Cinek99104832017-01-25 14:47:33 -08003908 * <p>
Selim Cinek22714f12017-04-13 16:23:53 -07003909 * For most styles, the coloring will only be applied if the notification is for a
3910 * foreground service notification.
Selim Cinek99104832017-01-25 14:47:33 -08003911 * However, for {@link MediaStyle} and {@link DecoratedMediaCustomViewStyle} notifications
Selim Cinek22714f12017-04-13 16:23:53 -07003912 * that have a media session attached there is no such requirement.
Selim Cinek7b9605b2017-01-19 17:36:00 -08003913 *
Aurimas Liutikas7f695332018-05-31 21:07:32 -07003914 * @see #setColor(int)
Selim Cinek99104832017-01-25 14:47:33 -08003915 * @see MediaStyle#setMediaSession(MediaSession.Token)
Selim Cinek7b9605b2017-01-19 17:36:00 -08003916 */
3917 public Builder setColorized(boolean colorize) {
3918 mN.extras.putBoolean(EXTRA_COLORIZED, colorize);
3919 return this;
3920 }
3921
3922 /**
Joe Onoratocb109a02011-01-18 17:57:41 -08003923 * Set this flag if you would only like the sound, vibrate
3924 * and ticker to be played if the notification is not already showing.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003925 *
3926 * @see Notification#FLAG_ONLY_ALERT_ONCE
Joe Onoratocb109a02011-01-18 17:57:41 -08003927 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003928 public Builder setOnlyAlertOnce(boolean onlyAlertOnce) {
3929 setFlag(FLAG_ONLY_ALERT_ONCE, onlyAlertOnce);
3930 return this;
3931 }
3932
Joe Onoratocb109a02011-01-18 17:57:41 -08003933 /**
Julia Reynolds04499532016-09-13 14:04:53 -04003934 * Make this notification automatically dismissed when the user touches it.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003935 *
3936 * @see Notification#FLAG_AUTO_CANCEL
Joe Onoratocb109a02011-01-18 17:57:41 -08003937 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003938 public Builder setAutoCancel(boolean autoCancel) {
Joe Onorato281d83f2011-01-04 17:13:10 -08003939 setFlag(FLAG_AUTO_CANCEL, autoCancel);
Joe Onorato46439ce2010-11-19 13:56:21 -08003940 return this;
3941 }
3942
Joe Onoratocb109a02011-01-18 17:57:41 -08003943 /**
Griff Hazendfcb0802014-02-11 12:00:00 -08003944 * Set whether or not this notification should not bridge to other devices.
3945 *
3946 * <p>Some notifications can be bridged to other devices for remote display.
3947 * This hint can be set to recommend this notification not be bridged.
3948 */
3949 public Builder setLocalOnly(boolean localOnly) {
3950 setFlag(FLAG_LOCAL_ONLY, localOnly);
3951 return this;
3952 }
3953
3954 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003955 * Set which notification properties will be inherited from system defaults.
Joe Onoratocb109a02011-01-18 17:57:41 -08003956 * <p>
3957 * The value should be one or more of the following fields combined with
3958 * bitwise-or:
3959 * {@link #DEFAULT_SOUND}, {@link #DEFAULT_VIBRATE}, {@link #DEFAULT_LIGHTS}.
3960 * <p>
3961 * For all default values, use {@link #DEFAULT_ALL}.
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003962 *
3963 * @deprecated use {@link NotificationChannel#enableVibration(boolean)} and
Julia Reynolds529e3322017-02-06 08:33:01 -05003964 * {@link NotificationChannel#enableLights(boolean)} and
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003965 * {@link NotificationChannel#setSound(Uri, AudioAttributes)} instead.
Joe Onoratocb109a02011-01-18 17:57:41 -08003966 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003967 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08003968 public Builder setDefaults(int defaults) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003969 mN.defaults = defaults;
Joe Onorato46439ce2010-11-19 13:56:21 -08003970 return this;
3971 }
3972
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003973 /**
3974 * Set the priority of this notification.
3975 *
3976 * @see Notification#priority
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003977 * @deprecated use {@link NotificationChannel#setImportance(int)} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003978 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003979 @Deprecated
Tor Norbyed9273d62013-05-30 15:59:53 -07003980 public Builder setPriority(@Priority int pri) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003981 mN.priority = pri;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003982 return this;
3983 }
Joe Malin8d40d042012-11-05 11:36:40 -08003984
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003985 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -04003986 * Set the notification category.
Joe Malin8d40d042012-11-05 11:36:40 -08003987 *
John Spurlockfd7f1e02014-03-18 16:41:57 -04003988 * @see Notification#category
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003989 */
John Spurlockfd7f1e02014-03-18 16:41:57 -04003990 public Builder setCategory(String category) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003991 mN.category = category;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003992 return this;
3993 }
3994
3995 /**
Chris Wrendde75302014-03-26 17:24:15 -04003996 * Add a person that is relevant to this notification.
3997 *
Chris Wrene6c48932014-09-29 17:19:27 -04003998 * <P>
3999 * Depending on user preferences, this annotation may allow the notification to pass
Julia Reynoldse071abd2017-03-22 10:52:11 -04004000 * through interruption filters, if this notification is of category {@link #CATEGORY_CALL}
4001 * or {@link #CATEGORY_MESSAGE}. The addition of people may also cause this notification to
4002 * appear more prominently in the user interface.
Chris Wrene6c48932014-09-29 17:19:27 -04004003 * </P>
4004 *
4005 * <P>
4006 * The person should be specified by the {@code String} representation of a
4007 * {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI}.
4008 * </P>
4009 *
4010 * <P>The system will also attempt to resolve {@code mailto:} and {@code tel:} schema
4011 * URIs. The path part of these URIs must exist in the contacts database, in the
4012 * appropriate column, or the reference will be discarded as invalid. Telephone schema
4013 * URIs will be resolved by {@link android.provider.ContactsContract.PhoneLookup}.
Selim Cineke7238dd2017-12-14 17:48:32 -08004014 * It is also possible to provide a URI with the schema {@code name:} in order to uniquely
4015 * identify a person without an entry in the contacts database.
Chris Wrene6c48932014-09-29 17:19:27 -04004016 * </P>
4017 *
4018 * @param uri A URI for the person.
Chris Wrendde75302014-03-26 17:24:15 -04004019 * @see Notification#EXTRA_PEOPLE
Selim Cineke7238dd2017-12-14 17:48:32 -08004020 * @deprecated use {@link #addPerson(Person)}
Chris Wrendde75302014-03-26 17:24:15 -04004021 */
Chris Wrene6c48932014-09-29 17:19:27 -04004022 public Builder addPerson(String uri) {
Selim Cinek9acd6732018-03-23 16:39:02 -07004023 addPerson(new Person.Builder().setUri(uri).build());
Selim Cineke7238dd2017-12-14 17:48:32 -08004024 return this;
4025 }
4026
4027 /**
4028 * Add a person that is relevant to this notification.
4029 *
4030 * <P>
4031 * Depending on user preferences, this annotation may allow the notification to pass
4032 * through interruption filters, if this notification is of category {@link #CATEGORY_CALL}
4033 * or {@link #CATEGORY_MESSAGE}. The addition of people may also cause this notification to
4034 * appear more prominently in the user interface.
4035 * </P>
4036 *
4037 * <P>
4038 * A person should usually contain a uri in order to benefit from the ranking boost.
4039 * However, even if no uri is provided, it's beneficial to provide other people in the
4040 * notification, such that listeners and voice only devices can announce and handle them
4041 * properly.
4042 * </P>
4043 *
4044 * @param person the person to add.
4045 * @see Notification#EXTRA_PEOPLE_LIST
4046 */
4047 public Builder addPerson(Person person) {
4048 mPersonList.add(person);
Chris Wrendde75302014-03-26 17:24:15 -04004049 return this;
4050 }
4051
4052 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07004053 * Set this notification to be part of a group of notifications sharing the same key.
4054 * Grouped notifications may display in a cluster or stack on devices which
4055 * support such rendering.
4056 *
4057 * <p>To make this notification the summary for its group, also call
4058 * {@link #setGroupSummary}. A sort order can be specified for group members by using
4059 * {@link #setSortKey}.
4060 * @param groupKey The group key of the group.
4061 * @return this object for method chaining
4062 */
4063 public Builder setGroup(String groupKey) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004064 mN.mGroupKey = groupKey;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07004065 return this;
4066 }
4067
4068 /**
4069 * Set this notification to be the group summary for a group of notifications.
4070 * Grouped notifications may display in a cluster or stack on devices which
Julia Reynolds04499532016-09-13 14:04:53 -04004071 * support such rendering. If thereRequires a group key also be set using {@link #setGroup}.
4072 * The group summary may be suppressed if too few notifications are included in the group.
Griff Hazen5cadc3b2014-05-20 09:55:39 -07004073 * @param isGroupSummary Whether this notification should be a group summary.
4074 * @return this object for method chaining
4075 */
4076 public Builder setGroupSummary(boolean isGroupSummary) {
4077 setFlag(FLAG_GROUP_SUMMARY, isGroupSummary);
4078 return this;
4079 }
4080
4081 /**
4082 * Set a sort key that orders this notification among other notifications from the
4083 * same package. This can be useful if an external sort was already applied and an app
4084 * would like to preserve this. Notifications will be sorted lexicographically using this
4085 * value, although providing different priorities in addition to providing sort key may
4086 * cause this value to be ignored.
4087 *
4088 * <p>This sort key can also be used to order members of a notification group. See
Griff Hazen9e1379f2014-05-20 12:50:51 -07004089 * {@link #setGroup}.
Griff Hazen5cadc3b2014-05-20 09:55:39 -07004090 *
4091 * @see String#compareTo(String)
4092 */
4093 public Builder setSortKey(String sortKey) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004094 mN.mSortKey = sortKey;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07004095 return this;
4096 }
4097
4098 /**
Griff Hazen720042b2014-02-24 15:46:56 -08004099 * Merge additional metadata into this notification.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05004100 *
Griff Hazen720042b2014-02-24 15:46:56 -08004101 * <p>Values within the Bundle will replace existing extras values in this Builder.
4102 *
4103 * @see Notification#extras
4104 */
Griff Hazen959591e2014-05-15 22:26:18 -07004105 public Builder addExtras(Bundle extras) {
4106 if (extras != null) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004107 mUserExtras.putAll(extras);
Griff Hazen720042b2014-02-24 15:46:56 -08004108 }
4109 return this;
4110 }
4111
4112 /**
4113 * Set metadata for this notification.
4114 *
4115 * <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 -04004116 * current contents are copied into the Notification each time {@link #build()} is
Daniel Sandler2561b0b2012-02-13 21:04:12 -05004117 * called.
4118 *
Griff Hazen720042b2014-02-24 15:46:56 -08004119 * <p>Replaces any existing extras values with those from the provided Bundle.
4120 * Use {@link #addExtras} to merge in metadata instead.
4121 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05004122 * @see Notification#extras
Daniel Sandler2561b0b2012-02-13 21:04:12 -05004123 */
Griff Hazen959591e2014-05-15 22:26:18 -07004124 public Builder setExtras(Bundle extras) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004125 if (extras != null) {
4126 mUserExtras = extras;
4127 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05004128 return this;
4129 }
4130
Daniel Sandlera0a938c2012-03-15 08:42:37 -04004131 /**
Griff Hazen720042b2014-02-24 15:46:56 -08004132 * Get the current metadata Bundle used by this notification Builder.
4133 *
4134 * <p>The returned Bundle is shared with this Builder.
4135 *
4136 * <p>The current contents of this Bundle are copied into the Notification each time
4137 * {@link #build()} is called.
4138 *
4139 * @see Notification#extras
4140 */
4141 public Bundle getExtras() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004142 return mUserExtras;
4143 }
4144
4145 private Bundle getAllExtras() {
4146 final Bundle saveExtras = (Bundle) mUserExtras.clone();
4147 saveExtras.putAll(mN.extras);
4148 return saveExtras;
Griff Hazen720042b2014-02-24 15:46:56 -08004149 }
4150
4151 /**
Daniel Sandlera0a938c2012-03-15 08:42:37 -04004152 * Add an action to this notification. Actions are typically displayed by
4153 * the system as a button adjacent to the notification content.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04004154 * <p>
4155 * Every action must have an icon (32dp square and matching the
4156 * <a href="{@docRoot}design/style/iconography.html#action-bar">Holo
4157 * Dark action bar</a> visual style), a textual label, and a {@link PendingIntent}.
4158 * <p>
4159 * A notification in its expanded form can display up to 3 actions, from left to right in
4160 * the order they were added. Actions will not be displayed when the notification is
4161 * collapsed, however, so be sure that any essential functions may be accessed by the user
4162 * in some other way (for example, in the Activity pointed to by {@link #contentIntent}).
Daniel Sandlera0a938c2012-03-15 08:42:37 -04004163 *
4164 * @param icon Resource ID of a drawable that represents the action.
4165 * @param title Text describing the action.
4166 * @param intent PendingIntent to be fired when the action is invoked.
Dan Sandler86647982015-05-13 23:41:13 -04004167 *
4168 * @deprecated Use {@link #addAction(Action)} instead.
Daniel Sandlera0a938c2012-03-15 08:42:37 -04004169 */
Dan Sandler86647982015-05-13 23:41:13 -04004170 @Deprecated
Daniel Sandlera0a938c2012-03-15 08:42:37 -04004171 public Builder addAction(int icon, CharSequence title, PendingIntent intent) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04004172 mActions.add(new Action(icon, safeCharSequence(title), intent));
Daniel Sandlera0a938c2012-03-15 08:42:37 -04004173 return this;
4174 }
4175
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004176 /**
Griff Hazen959591e2014-05-15 22:26:18 -07004177 * Add an action to this notification. Actions are typically displayed by
4178 * the system as a button adjacent to the notification content.
4179 * <p>
4180 * Every action must have an icon (32dp square and matching the
4181 * <a href="{@docRoot}design/style/iconography.html#action-bar">Holo
4182 * Dark action bar</a> visual style), a textual label, and a {@link PendingIntent}.
4183 * <p>
4184 * A notification in its expanded form can display up to 3 actions, from left to right in
4185 * the order they were added. Actions will not be displayed when the notification is
4186 * collapsed, however, so be sure that any essential functions may be accessed by the user
4187 * in some other way (for example, in the Activity pointed to by {@link #contentIntent}).
4188 *
4189 * @param action The action to add.
4190 */
4191 public Builder addAction(Action action) {
liangweikang63b03b52017-03-16 19:22:15 +08004192 if (action != null) {
4193 mActions.add(action);
4194 }
Griff Hazen959591e2014-05-15 22:26:18 -07004195 return this;
4196 }
4197
4198 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004199 * Alter the complete list of actions attached to this notification.
4200 * @see #addAction(Action).
4201 *
4202 * @param actions
4203 * @return
4204 */
4205 public Builder setActions(Action... actions) {
4206 mActions.clear();
4207 for (int i = 0; i < actions.length; i++) {
liangweikang63b03b52017-03-16 19:22:15 +08004208 if (actions[i] != null) {
4209 mActions.add(actions[i]);
4210 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004211 }
4212 return this;
4213 }
4214
4215 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004216 * Add a rich notification style to be applied at build time.
4217 *
4218 * @param style Object responsible for modifying the notification style.
4219 */
4220 public Builder setStyle(Style style) {
4221 if (mStyle != style) {
4222 mStyle = style;
Daniel Sandlerc08dea22012-06-28 08:35:24 -07004223 if (mStyle != null) {
4224 mStyle.setBuilder(this);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004225 mN.extras.putString(EXTRA_TEMPLATE, style.getClass().getName());
4226 } else {
4227 mN.extras.remove(EXTRA_TEMPLATE);
Daniel Sandlerc08dea22012-06-28 08:35:24 -07004228 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004229 }
4230 return this;
4231 }
4232
Dan Sandler0bf2ed82013-12-21 23:33:41 -06004233 /**
Julia Reynolds7217dc92018-03-07 12:12:09 -05004234 * Returns the style set by {@link #setStyle(Style)}.
4235 */
4236 public Style getStyle() {
4237 return mStyle;
4238 }
4239
4240 /**
Dan Sandler0bf2ed82013-12-21 23:33:41 -06004241 * Specify the value of {@link #visibility}.
Griff Hazenb720abe2014-05-20 13:15:30 -07004242 *
Dan Sandler0bf2ed82013-12-21 23:33:41 -06004243 * @return The same Builder.
4244 */
Jeff Sharkey30e06bb2017-04-24 11:18:03 -06004245 public Builder setVisibility(@Visibility int visibility) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004246 mN.visibility = visibility;
Dan Sandler0bf2ed82013-12-21 23:33:41 -06004247 return this;
4248 }
4249
4250 /**
4251 * Supply a replacement Notification whose contents should be shown in insecure contexts
4252 * (i.e. atop the secure lockscreen). See {@link #visibility} and {@link #VISIBILITY_PUBLIC}.
4253 * @param n A replacement notification, presumably with some or all info redacted.
4254 * @return The same Builder.
4255 */
4256 public Builder setPublicVersion(Notification n) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004257 if (n != null) {
4258 mN.publicVersion = new Notification();
4259 n.cloneInto(mN.publicVersion, /*heavy=*/ true);
4260 } else {
4261 mN.publicVersion = null;
4262 }
Dan Sandler0bf2ed82013-12-21 23:33:41 -06004263 return this;
4264 }
4265
Griff Hazenb720abe2014-05-20 13:15:30 -07004266 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07004267 * Apply an extender to this notification builder. Extenders may be used to add
4268 * metadata or change options on this builder.
4269 */
Griff Hazen61a9e862014-05-22 16:05:19 -07004270 public Builder extend(Extender extender) {
4271 extender.extend(this);
Griff Hazen5cadc3b2014-05-20 09:55:39 -07004272 return this;
4273 }
4274
Dan Sandler4e787062015-06-17 15:09:48 -04004275 /**
4276 * @hide
4277 */
Julia Reynoldse46bb372016-03-17 11:05:58 -04004278 public Builder setFlag(int mask, boolean value) {
Joe Onorato46439ce2010-11-19 13:56:21 -08004279 if (value) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004280 mN.flags |= mask;
Joe Onorato46439ce2010-11-19 13:56:21 -08004281 } else {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004282 mN.flags &= ~mask;
Joe Onorato46439ce2010-11-19 13:56:21 -08004283 }
Julia Reynoldse46bb372016-03-17 11:05:58 -04004284 return this;
Joe Onorato46439ce2010-11-19 13:56:21 -08004285 }
4286
Dan Sandler26e81cf2014-05-06 10:01:27 -04004287 /**
4288 * Sets {@link Notification#color}.
4289 *
4290 * @param argb The accent color to use
4291 *
4292 * @return The same Builder.
4293 */
Tor Norbye80756e32015-03-02 09:39:27 -08004294 public Builder setColor(@ColorInt int argb) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004295 mN.color = argb;
Julia Reynolds10ee1fc2015-11-09 11:04:55 -05004296 sanitizeColor();
Dan Sandler26e81cf2014-05-06 10:01:27 -04004297 return this;
4298 }
4299
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02004300 private Drawable getProfileBadgeDrawable() {
Chris Wren66619a22016-05-12 16:42:37 -04004301 if (mContext.getUserId() == UserHandle.USER_SYSTEM) {
4302 // This user can never be a badged profile,
4303 // and also includes USER_ALL system notifications.
4304 return null;
4305 }
Christoph Studer7ac80e62014-08-04 16:01:57 +02004306 // Note: This assumes that the current user can read the profile badge of the
4307 // originating user.
Selim Cineke6ff9462016-01-15 15:07:06 -08004308 return mContext.getPackageManager().getUserBadgeForDensityNoBackground(
Julia Reynoldsda303542015-11-23 14:00:20 -05004309 new UserHandle(mContext.getUserId()), 0);
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02004310 }
4311
4312 private Bitmap getProfileBadge() {
4313 Drawable badge = getProfileBadgeDrawable();
Kenny Guy8a0101b2014-05-08 23:34:12 +01004314 if (badge == null) {
4315 return null;
4316 }
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02004317 final int size = mContext.getResources().getDimensionPixelSize(
4318 R.dimen.notification_badge_size);
4319 Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
Kenny Guy8a0101b2014-05-08 23:34:12 +01004320 Canvas canvas = new Canvas(bitmap);
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02004321 badge.setBounds(0, 0, size, size);
Kenny Guy8a0101b2014-05-08 23:34:12 +01004322 badge.draw(canvas);
4323 return bitmap;
4324 }
4325
Selim Cinekc848c3a2016-01-13 15:27:30 -08004326 private void bindProfileBadge(RemoteViews contentView) {
Kenny Guy98193ea2014-07-24 19:54:37 +01004327 Bitmap profileBadge = getProfileBadge();
4328
Kenny Guy98193ea2014-07-24 19:54:37 +01004329 if (profileBadge != null) {
Selim Cinekc848c3a2016-01-13 15:27:30 -08004330 contentView.setImageViewBitmap(R.id.profile_badge, profileBadge);
4331 contentView.setViewVisibility(R.id.profile_badge, View.VISIBLE);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004332 if (isColorized()) {
Sunny Goyal5b153922017-09-21 21:00:36 -07004333 contentView.setDrawableTint(R.id.profile_badge, false,
4334 getPrimaryTextColor(), PorterDuff.Mode.SRC_ATOP);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004335 }
Kenny Guy98193ea2014-07-24 19:54:37 +01004336 }
Kenny Guy98193ea2014-07-24 19:54:37 +01004337 }
4338
Julia Reynoldsfc640012018-02-21 12:25:27 -05004339 /**
4340 * @hide
4341 */
4342 public boolean usesStandardHeader() {
4343 if (mN.mUsesStandardHeader) {
4344 return true;
4345 }
4346 if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.N) {
4347 if (mN.contentView == null && mN.bigContentView == null) {
4348 return true;
4349 }
4350 }
4351 boolean contentViewUsesHeader = mN.contentView == null
4352 || STANDARD_LAYOUTS.contains(mN.contentView.getLayoutId());
4353 boolean bigContentViewUsesHeader = mN.bigContentView == null
4354 || STANDARD_LAYOUTS.contains(mN.bigContentView.getLayoutId());
4355 return contentViewUsesHeader && bigContentViewUsesHeader;
4356 }
4357
Christoph Studerfe718432014-09-01 18:21:18 +02004358 private void resetStandardTemplate(RemoteViews contentView) {
Selim Cinekeaa29ca2015-11-23 13:51:13 -08004359 resetNotificationHeader(contentView);
Christoph Studerfe718432014-09-01 18:21:18 +02004360 contentView.setViewVisibility(R.id.right_icon, View.GONE);
Selim Cinek860b6da2015-12-16 19:02:19 -08004361 contentView.setViewVisibility(R.id.title, View.GONE);
Christoph Studerfe718432014-09-01 18:21:18 +02004362 contentView.setTextViewText(R.id.title, null);
Selim Cinek41598732016-01-11 16:58:37 -08004363 contentView.setViewVisibility(R.id.text, View.GONE);
Christoph Studerfe718432014-09-01 18:21:18 +02004364 contentView.setTextViewText(R.id.text, null);
Selim Cinek29603462015-11-17 19:04:39 -08004365 contentView.setViewVisibility(R.id.text_line_1, View.GONE);
Selim Cinek41598732016-01-11 16:58:37 -08004366 contentView.setTextViewText(R.id.text_line_1, null);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004367 }
4368
Selim Cinekeaa29ca2015-11-23 13:51:13 -08004369 /**
4370 * Resets the notification header to its original state
4371 */
4372 private void resetNotificationHeader(RemoteViews contentView) {
Adrian Roosc4337a32016-08-02 18:30:34 -07004373 // Small icon doesn't need to be reset, as it's always set. Resetting would prevent
4374 // re-using the drawable when the notification is updated.
Selim Cinek7b836392015-12-04 20:02:59 -08004375 contentView.setBoolean(R.id.notification_header, "setExpanded", false);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004376 contentView.setTextViewText(R.id.app_name_text, null);
Christoph Studerca1db712014-09-10 17:31:33 +02004377 contentView.setViewVisibility(R.id.chronometer, View.GONE);
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07004378 contentView.setViewVisibility(R.id.header_text, View.GONE);
Adrian Roos9dfb78f2016-06-30 15:43:44 -07004379 contentView.setTextViewText(R.id.header_text, null);
Selim Cinekafeed292017-12-12 17:32:44 -08004380 contentView.setViewVisibility(R.id.header_text_secondary, View.GONE);
4381 contentView.setTextViewText(R.id.header_text_secondary, null);
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07004382 contentView.setViewVisibility(R.id.header_text_divider, View.GONE);
Selim Cinekafeed292017-12-12 17:32:44 -08004383 contentView.setViewVisibility(R.id.header_text_secondary_divider, View.GONE);
Selim Cinek29603462015-11-17 19:04:39 -08004384 contentView.setViewVisibility(R.id.time_divider, View.GONE);
Selim Cinekb85f36fd2016-04-20 18:46:36 -07004385 contentView.setViewVisibility(R.id.time, View.GONE);
Selim Cinekc848c3a2016-01-13 15:27:30 -08004386 contentView.setImageViewIcon(R.id.profile_badge, null);
4387 contentView.setViewVisibility(R.id.profile_badge, View.GONE);
Julia Reynoldsfc640012018-02-21 12:25:27 -05004388 mN.mUsesStandardHeader = false;
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004389 }
4390
Selim Cinek384804b2018-04-18 14:31:07 +08004391 private RemoteViews applyStandardTemplate(int resId, TemplateBindResult result) {
4392 return applyStandardTemplate(resId, mParams.reset().fillTextsFrom(this),
4393 result);
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004394 }
4395
4396 /**
4397 * @param hasProgress whether the progress bar should be shown and set
Selim Cinek384804b2018-04-18 14:31:07 +08004398 * @param result
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004399 */
Selim Cinek384804b2018-04-18 14:31:07 +08004400 private RemoteViews applyStandardTemplate(int resId, boolean hasProgress,
4401 TemplateBindResult result) {
Adrian Roos70d7aa32017-01-11 15:39:06 -08004402 return applyStandardTemplate(resId, mParams.reset().hasProgress(hasProgress)
Selim Cinek384804b2018-04-18 14:31:07 +08004403 .fillTextsFrom(this), result);
Adrian Roosc1a80b02016-04-05 14:54:55 -07004404 }
4405
Selim Cinek384804b2018-04-18 14:31:07 +08004406 private RemoteViews applyStandardTemplate(int resId, StandardTemplateParams p,
4407 TemplateBindResult result) {
Kenny Guy77320062014-08-27 21:37:15 +01004408 RemoteViews contentView = new BuilderRemoteViews(mContext.getApplicationInfo(), resId);
Dan Sandler539aad42014-08-04 00:43:39 -04004409
Christoph Studerfe718432014-09-01 18:21:18 +02004410 resetStandardTemplate(contentView);
4411
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004412 final Bundle ex = mN.extras;
Selim Cinek7b9605b2017-01-19 17:36:00 -08004413 updateBackgroundColor(contentView);
Selim Cinekafeed292017-12-12 17:32:44 -08004414 bindNotificationHeader(contentView, p.ambient, p.headerTextSecondary);
Selim Cinek384804b2018-04-18 14:31:07 +08004415 bindLargeIconAndReply(contentView, p, result);
Adrian Roos70d7aa32017-01-11 15:39:06 -08004416 boolean showProgress = handleProgressBar(p.hasProgress, contentView, ex);
4417 if (p.title != null) {
Selim Cinek860b6da2015-12-16 19:02:19 -08004418 contentView.setViewVisibility(R.id.title, View.VISIBLE);
Selim Cinek48f66b72017-08-18 16:17:51 -07004419 contentView.setTextViewText(R.id.title, processTextSpans(p.title));
Adrian Roos72171622017-01-27 10:32:06 -08004420 if (!p.ambient) {
4421 setTextViewColorPrimary(contentView, R.id.title);
4422 }
Selim Cinek954cc232016-05-20 13:29:23 -07004423 contentView.setViewLayoutWidth(R.id.title, showProgress
4424 ? ViewGroup.LayoutParams.WRAP_CONTENT
4425 : ViewGroup.LayoutParams.MATCH_PARENT);
Joe Onorato561d3852010-11-20 18:09:34 -08004426 }
Adrian Roos70d7aa32017-01-11 15:39:06 -08004427 if (p.text != null) {
Selim Cinek41598732016-01-11 16:58:37 -08004428 int textId = showProgress ? com.android.internal.R.id.text_line_1
4429 : com.android.internal.R.id.text;
Selim Cinek48f66b72017-08-18 16:17:51 -07004430 contentView.setTextViewText(textId, processTextSpans(p.text));
Adrian Roos72171622017-01-27 10:32:06 -08004431 if (!p.ambient) {
4432 setTextViewColorSecondary(contentView, textId);
4433 }
Selim Cinek41598732016-01-11 16:58:37 -08004434 contentView.setViewVisibility(textId, View.VISIBLE);
Joe Onorato561d3852010-11-20 18:09:34 -08004435 }
Selim Cinekc848c3a2016-01-13 15:27:30 -08004436
Selim Cinek279fa862016-06-14 10:57:25 -07004437 setContentMinHeight(contentView, showProgress || mN.hasLargeIcon());
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004438
Selim Cinek29603462015-11-17 19:04:39 -08004439 return contentView;
4440 }
4441
Selim Cinek48f66b72017-08-18 16:17:51 -07004442 private CharSequence processTextSpans(CharSequence text) {
4443 if (hasForegroundColor()) {
Lucas Dupina291d192018-06-07 13:59:42 -07004444 return ContrastColorUtil.clearColorSpans(text);
Selim Cinek48f66b72017-08-18 16:17:51 -07004445 }
4446 return text;
4447 }
4448
Selim Cinek7b9605b2017-01-19 17:36:00 -08004449 private void setTextViewColorPrimary(RemoteViews contentView, int id) {
4450 ensureColors();
4451 contentView.setTextColor(id, mPrimaryTextColor);
4452 }
4453
Selim Cinek48f66b72017-08-18 16:17:51 -07004454 private boolean hasForegroundColor() {
4455 return mForegroundColor != COLOR_INVALID;
4456 }
4457
Selim Cinek389edcd2017-05-11 19:16:44 -07004458 /**
4459 * @return the primary text color
4460 * @hide
4461 */
4462 @VisibleForTesting
4463 public int getPrimaryTextColor() {
Selim Cinek7b9605b2017-01-19 17:36:00 -08004464 ensureColors();
4465 return mPrimaryTextColor;
4466 }
4467
Selim Cinek389edcd2017-05-11 19:16:44 -07004468 /**
4469 * @return the secondary text color
4470 * @hide
4471 */
4472 @VisibleForTesting
4473 public int getSecondaryTextColor() {
4474 ensureColors();
4475 return mSecondaryTextColor;
4476 }
4477
Selim Cinek7b9605b2017-01-19 17:36:00 -08004478 private void setTextViewColorSecondary(RemoteViews contentView, int id) {
4479 ensureColors();
4480 contentView.setTextColor(id, mSecondaryTextColor);
4481 }
4482
4483 private void ensureColors() {
4484 int backgroundColor = getBackgroundColor();
4485 if (mPrimaryTextColor == COLOR_INVALID
4486 || mSecondaryTextColor == COLOR_INVALID
Selim Cinek7b9605b2017-01-19 17:36:00 -08004487 || mTextColorsAreForBackground != backgroundColor) {
4488 mTextColorsAreForBackground = backgroundColor;
Selim Cinek48f66b72017-08-18 16:17:51 -07004489 if (!hasForegroundColor() || !isColorized()) {
Lucas Dupina291d192018-06-07 13:59:42 -07004490 mPrimaryTextColor = ContrastColorUtil.resolvePrimaryColor(mContext,
Lucas Dupinf03e7522018-06-25 16:21:13 -07004491 backgroundColor, mInNightMode);
Lucas Dupina291d192018-06-07 13:59:42 -07004492 mSecondaryTextColor = ContrastColorUtil.resolveSecondaryColor(mContext,
Lucas Dupinf03e7522018-06-25 16:21:13 -07004493 backgroundColor, mInNightMode);
Selim Cinekc7f5a822018-03-20 19:32:06 -07004494 if (backgroundColor != COLOR_DEFAULT && isColorized()) {
Lucas Dupina291d192018-06-07 13:59:42 -07004495 mPrimaryTextColor = ContrastColorUtil.findAlphaToMeetContrast(
Selim Cinekac5f0272017-05-02 16:05:41 -07004496 mPrimaryTextColor, backgroundColor, 4.5);
Lucas Dupina291d192018-06-07 13:59:42 -07004497 mSecondaryTextColor = ContrastColorUtil.findAlphaToMeetContrast(
Selim Cinekac5f0272017-05-02 16:05:41 -07004498 mSecondaryTextColor, backgroundColor, 4.5);
4499 }
Selim Cinek5fb73f82017-04-20 16:55:38 -07004500 } else {
Lucas Dupina291d192018-06-07 13:59:42 -07004501 double backLum = ContrastColorUtil.calculateLuminance(backgroundColor);
4502 double textLum = ContrastColorUtil.calculateLuminance(mForegroundColor);
4503 double contrast = ContrastColorUtil.calculateContrast(mForegroundColor,
Selim Cinek5fb73f82017-04-20 16:55:38 -07004504 backgroundColor);
Selim Cinek389edcd2017-05-11 19:16:44 -07004505 // We only respect the given colors if worst case Black or White still has
4506 // contrast
4507 boolean backgroundLight = backLum > textLum
4508 && satisfiesTextContrast(backgroundColor, Color.BLACK)
4509 || backLum <= textLum
4510 && !satisfiesTextContrast(backgroundColor, Color.WHITE);
Selim Cinek5fb73f82017-04-20 16:55:38 -07004511 if (contrast < 4.5f) {
Selim Cinek389edcd2017-05-11 19:16:44 -07004512 if (backgroundLight) {
Lucas Dupina291d192018-06-07 13:59:42 -07004513 mSecondaryTextColor = ContrastColorUtil.findContrastColor(
Selim Cinek5fb73f82017-04-20 16:55:38 -07004514 mForegroundColor,
4515 backgroundColor,
4516 true /* findFG */,
4517 4.5f);
Lucas Dupina291d192018-06-07 13:59:42 -07004518 mPrimaryTextColor = ContrastColorUtil.changeColorLightness(
Selim Cinek389edcd2017-05-11 19:16:44 -07004519 mSecondaryTextColor, -LIGHTNESS_TEXT_DIFFERENCE_LIGHT);
Selim Cinek5fb73f82017-04-20 16:55:38 -07004520 } else {
4521 mSecondaryTextColor =
Lucas Dupina291d192018-06-07 13:59:42 -07004522 ContrastColorUtil.findContrastColorAgainstDark(
Selim Cinek5fb73f82017-04-20 16:55:38 -07004523 mForegroundColor,
4524 backgroundColor,
4525 true /* findFG */,
4526 4.5f);
Lucas Dupina291d192018-06-07 13:59:42 -07004527 mPrimaryTextColor = ContrastColorUtil.changeColorLightness(
Selim Cinek389edcd2017-05-11 19:16:44 -07004528 mSecondaryTextColor, -LIGHTNESS_TEXT_DIFFERENCE_DARK);
Selim Cinek5fb73f82017-04-20 16:55:38 -07004529 }
4530 } else {
4531 mPrimaryTextColor = mForegroundColor;
Lucas Dupina291d192018-06-07 13:59:42 -07004532 mSecondaryTextColor = ContrastColorUtil.changeColorLightness(
Selim Cinek389edcd2017-05-11 19:16:44 -07004533 mPrimaryTextColor, backgroundLight ? LIGHTNESS_TEXT_DIFFERENCE_LIGHT
4534 : LIGHTNESS_TEXT_DIFFERENCE_DARK);
Lucas Dupina291d192018-06-07 13:59:42 -07004535 if (ContrastColorUtil.calculateContrast(mSecondaryTextColor,
Selim Cinek5fb73f82017-04-20 16:55:38 -07004536 backgroundColor) < 4.5f) {
4537 // oh well the secondary is not good enough
Selim Cinek389edcd2017-05-11 19:16:44 -07004538 if (backgroundLight) {
Lucas Dupina291d192018-06-07 13:59:42 -07004539 mSecondaryTextColor = ContrastColorUtil.findContrastColor(
Selim Cinek5fb73f82017-04-20 16:55:38 -07004540 mSecondaryTextColor,
4541 backgroundColor,
4542 true /* findFG */,
4543 4.5f);
4544 } else {
4545 mSecondaryTextColor
Lucas Dupina291d192018-06-07 13:59:42 -07004546 = ContrastColorUtil.findContrastColorAgainstDark(
Selim Cinek5fb73f82017-04-20 16:55:38 -07004547 mSecondaryTextColor,
4548 backgroundColor,
4549 true /* findFG */,
4550 4.5f);
4551 }
Lucas Dupina291d192018-06-07 13:59:42 -07004552 mPrimaryTextColor = ContrastColorUtil.changeColorLightness(
Selim Cinek389edcd2017-05-11 19:16:44 -07004553 mSecondaryTextColor, backgroundLight
4554 ? -LIGHTNESS_TEXT_DIFFERENCE_LIGHT
4555 : -LIGHTNESS_TEXT_DIFFERENCE_DARK);
Selim Cinek5fb73f82017-04-20 16:55:38 -07004556 }
4557 }
4558 }
Selim Cinek7b9605b2017-01-19 17:36:00 -08004559 }
4560 }
4561
4562 private void updateBackgroundColor(RemoteViews contentView) {
4563 if (isColorized()) {
4564 contentView.setInt(R.id.status_bar_latest_event_content, "setBackgroundColor",
4565 getBackgroundColor());
4566 } else {
4567 // Clear it!
4568 contentView.setInt(R.id.status_bar_latest_event_content, "setBackgroundResource",
4569 0);
4570 }
4571 }
4572
Selim Cinek860b6da2015-12-16 19:02:19 -08004573 /**
4574 * @param remoteView the remote view to update the minheight in
4575 * @param hasMinHeight does it have a mimHeight
4576 * @hide
4577 */
4578 void setContentMinHeight(RemoteViews remoteView, boolean hasMinHeight) {
4579 int minHeight = 0;
4580 if (hasMinHeight) {
4581 // we need to set the minHeight of the notification
4582 minHeight = mContext.getResources().getDimensionPixelSize(
4583 com.android.internal.R.dimen.notification_min_content_height);
4584 }
4585 remoteView.setInt(R.id.notification_main_column, "setMinimumHeight", minHeight);
4586 }
4587
Selim Cinek29603462015-11-17 19:04:39 -08004588 private boolean handleProgressBar(boolean hasProgress, RemoteViews contentView, Bundle ex) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004589 final int max = ex.getInt(EXTRA_PROGRESS_MAX, 0);
4590 final int progress = ex.getInt(EXTRA_PROGRESS, 0);
4591 final boolean ind = ex.getBoolean(EXTRA_PROGRESS_INDETERMINATE);
4592 if (hasProgress && (max != 0 || ind)) {
Selim Cinek29603462015-11-17 19:04:39 -08004593 contentView.setViewVisibility(com.android.internal.R.id.progress, View.VISIBLE);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004594 contentView.setProgressBar(
Selim Cinek29603462015-11-17 19:04:39 -08004595 R.id.progress, max, progress, ind);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004596 contentView.setProgressBackgroundTintList(
4597 R.id.progress, ColorStateList.valueOf(mContext.getColor(
4598 R.color.notification_progress_background_color)));
Selim Cinek29603462015-11-17 19:04:39 -08004599 if (mN.color != COLOR_DEFAULT) {
Adrian Roos4ff3b122016-02-01 12:26:13 -08004600 ColorStateList colorStateList = ColorStateList.valueOf(resolveContrastColor());
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004601 contentView.setProgressTintList(R.id.progress, colorStateList);
4602 contentView.setProgressIndeterminateTintList(R.id.progress, colorStateList);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004603 }
Selim Cinek29603462015-11-17 19:04:39 -08004604 return true;
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004605 } else {
4606 contentView.setViewVisibility(R.id.progress, View.GONE);
Selim Cinek29603462015-11-17 19:04:39 -08004607 return false;
Jeff Sharkey1c400132011-08-05 14:50:13 -07004608 }
Joe Onorato561d3852010-11-20 18:09:34 -08004609 }
4610
Selim Cinek384804b2018-04-18 14:31:07 +08004611 private void bindLargeIconAndReply(RemoteViews contentView, StandardTemplateParams p,
4612 TemplateBindResult result) {
4613 boolean largeIconShown = bindLargeIcon(contentView, p.hideLargeIcon || p.ambient);
Selim Cinek1c72fa02018-04-23 18:00:54 +08004614 boolean replyIconShown = bindReplyIcon(contentView, p.hideReplyIcon || p.ambient);
Selim Cinek384804b2018-04-18 14:31:07 +08004615 contentView.setViewVisibility(R.id.right_icon_container,
Selim Cinek1c72fa02018-04-23 18:00:54 +08004616 largeIconShown || replyIconShown ? View.VISIBLE : View.GONE);
4617 int marginEnd = calculateMarginEnd(largeIconShown, replyIconShown);
4618 contentView.setViewLayoutMarginEnd(R.id.line1, marginEnd);
4619 contentView.setViewLayoutMarginEnd(R.id.text, marginEnd);
4620 contentView.setViewLayoutMarginEnd(R.id.progress, marginEnd);
Selim Cinek384804b2018-04-18 14:31:07 +08004621 if (result != null) {
Selim Cinek1c72fa02018-04-23 18:00:54 +08004622 result.setIconMarginEnd(marginEnd);
Selim Cinek384804b2018-04-18 14:31:07 +08004623 }
4624 }
4625
Selim Cinek1c72fa02018-04-23 18:00:54 +08004626 private int calculateMarginEnd(boolean largeIconShown, boolean replyIconShown) {
4627 int marginEnd = 0;
4628 int contentMargin = mContext.getResources().getDimensionPixelSize(
4629 R.dimen.notification_content_margin_end);
4630 int iconSize = mContext.getResources().getDimensionPixelSize(
4631 R.dimen.notification_right_icon_size);
4632 if (replyIconShown) {
4633 // The size of the reply icon
4634 marginEnd += iconSize;
4635
4636 int replyInset = mContext.getResources().getDimensionPixelSize(
4637 R.dimen.notification_reply_inset);
4638 // We're subtracting the inset of the reply icon to make sure it's
4639 // aligned nicely on the right, and remove it from the following padding
4640 marginEnd -= replyInset * 2;
4641 }
4642 if (largeIconShown) {
4643 // adding size of the right icon
4644 marginEnd += iconSize;
4645
4646 if (replyIconShown) {
4647 // We also add some padding to the reply icon if it's around
4648 marginEnd += contentMargin;
4649 }
4650 }
4651 if (replyIconShown || largeIconShown) {
4652 // The padding to the content
4653 marginEnd += contentMargin;
4654 }
4655 return marginEnd;
4656 }
4657
Selim Cinek384804b2018-04-18 14:31:07 +08004658 /**
4659 * Bind the large icon.
4660 * @return if the largeIcon is visible
4661 */
4662 private boolean bindLargeIcon(RemoteViews contentView, boolean hideLargeIcon) {
Selim Cinek279fa862016-06-14 10:57:25 -07004663 if (mN.mLargeIcon == null && mN.largeIcon != null) {
4664 mN.mLargeIcon = Icon.createWithBitmap(mN.largeIcon);
4665 }
Selim Cinek88188f22017-09-19 16:46:56 -07004666 boolean showLargeIcon = mN.mLargeIcon != null && !hideLargeIcon;
4667 if (showLargeIcon) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004668 contentView.setViewVisibility(R.id.right_icon, View.VISIBLE);
4669 contentView.setImageViewIcon(R.id.right_icon, mN.mLargeIcon);
4670 processLargeLegacyIcon(mN.mLargeIcon, contentView);
Selim Cinek88188f22017-09-19 16:46:56 -07004671 }
Selim Cinek384804b2018-04-18 14:31:07 +08004672 return showLargeIcon;
4673 }
Selim Cinek5d6ef8d2017-05-18 22:16:00 -07004674
Selim Cinek384804b2018-04-18 14:31:07 +08004675 /**
4676 * Bind the reply icon.
4677 * @return if the reply icon is visible
4678 */
4679 private boolean bindReplyIcon(RemoteViews contentView, boolean hideReplyIcon) {
4680 boolean actionVisible = !hideReplyIcon;
4681 Action action = null;
Selim Cinek88188f22017-09-19 16:46:56 -07004682 if (actionVisible) {
Selim Cinek384804b2018-04-18 14:31:07 +08004683 action = findReplyAction();
4684 actionVisible = action != null;
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004685 }
Selim Cinek384804b2018-04-18 14:31:07 +08004686 if (actionVisible) {
4687 contentView.setViewVisibility(R.id.reply_icon_action, View.VISIBLE);
4688 contentView.setDrawableTint(R.id.reply_icon_action,
4689 false /* targetBackground */,
Selim Cinek4717d862018-04-19 09:19:15 +08004690 getNeutralColor(),
Selim Cinek384804b2018-04-18 14:31:07 +08004691 PorterDuff.Mode.SRC_ATOP);
4692 contentView.setOnClickPendingIntent(R.id.reply_icon_action, action.actionIntent);
4693 contentView.setRemoteInputs(R.id.reply_icon_action, action.mRemoteInputs);
4694 } else {
4695 contentView.setRemoteInputs(R.id.reply_icon_action, null);
4696 }
4697 contentView.setViewVisibility(R.id.reply_icon_action,
4698 actionVisible ? View.VISIBLE : View.GONE);
4699 return actionVisible;
Selim Cinek5d6ef8d2017-05-18 22:16:00 -07004700 }
4701
4702 private Action findReplyAction() {
4703 ArrayList<Action> actions = mActions;
4704 if (mOriginalActions != null) {
4705 actions = mOriginalActions;
4706 }
4707 int numActions = actions.size();
4708 for (int i = 0; i < numActions; i++) {
4709 Action action = actions.get(i);
4710 if (hasValidRemoteInput(action)) {
4711 return action;
4712 }
4713 }
4714 return null;
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004715 }
4716
Selim Cinekafeed292017-12-12 17:32:44 -08004717 private void bindNotificationHeader(RemoteViews contentView, boolean ambient,
4718 CharSequence secondaryHeaderText) {
Adrian Roos487374f2017-01-11 15:48:14 -08004719 bindSmallIcon(contentView, ambient);
4720 bindHeaderAppName(contentView, ambient);
4721 if (!ambient) {
4722 // Ambient view does not have these
4723 bindHeaderText(contentView);
Selim Cinekafeed292017-12-12 17:32:44 -08004724 bindHeaderTextSecondary(contentView, secondaryHeaderText);
Adrian Roos487374f2017-01-11 15:48:14 -08004725 bindHeaderChronometerAndTime(contentView);
Adrian Roos487374f2017-01-11 15:48:14 -08004726 bindProfileBadge(contentView);
4727 }
Julia Reynolds6013e5c2018-04-18 08:43:34 -04004728 bindActivePermissions(contentView, ambient);
Adrian Roosd83e9992017-03-16 15:17:57 -07004729 bindExpandButton(contentView);
Julia Reynoldsfc640012018-02-21 12:25:27 -05004730 mN.mUsesStandardHeader = true;
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004731 }
4732
Julia Reynolds6013e5c2018-04-18 08:43:34 -04004733 private void bindActivePermissions(RemoteViews contentView, boolean ambient) {
Selim Cinek4717d862018-04-19 09:19:15 +08004734 int color = ambient ? resolveAmbientColor() : getNeutralColor();
Julia Reynoldsb887b232018-04-10 16:38:08 -04004735 contentView.setDrawableTint(R.id.camera, false, color, PorterDuff.Mode.SRC_ATOP);
4736 contentView.setDrawableTint(R.id.mic, false, color, PorterDuff.Mode.SRC_ATOP);
4737 contentView.setDrawableTint(R.id.overlay, false, color, PorterDuff.Mode.SRC_ATOP);
4738 }
4739
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004740 private void bindExpandButton(RemoteViews contentView) {
Selim Cinekc7f5a822018-03-20 19:32:06 -07004741 int color = isColorized() ? getPrimaryTextColor() : getSecondaryTextColor();
Sunny Goyal5b153922017-09-21 21:00:36 -07004742 contentView.setDrawableTint(R.id.expand_button, false, color,
4743 PorterDuff.Mode.SRC_ATOP);
Selim Cinekea4bef72015-12-02 15:51:10 -08004744 contentView.setInt(R.id.notification_header, "setOriginalNotificationColor",
Selim Cinek7b9605b2017-01-19 17:36:00 -08004745 color);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004746 }
4747
4748 private void bindHeaderChronometerAndTime(RemoteViews contentView) {
4749 if (showsTimeOrChronometer()) {
Selim Cinek29603462015-11-17 19:04:39 -08004750 contentView.setViewVisibility(R.id.time_divider, View.VISIBLE);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004751 setTextViewColorSecondary(contentView, R.id.time_divider);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004752 if (mN.extras.getBoolean(EXTRA_SHOW_CHRONOMETER)) {
4753 contentView.setViewVisibility(R.id.chronometer, View.VISIBLE);
4754 contentView.setLong(R.id.chronometer, "setBase",
4755 mN.when + (SystemClock.elapsedRealtime() - System.currentTimeMillis()));
4756 contentView.setBoolean(R.id.chronometer, "setStarted", true);
Adrian Roos96b7e202016-05-17 13:50:38 -07004757 boolean countsDown = mN.extras.getBoolean(EXTRA_CHRONOMETER_COUNT_DOWN);
Selim Cinekc3b752e2016-04-20 16:13:59 -07004758 contentView.setChronometerCountDown(R.id.chronometer, countsDown);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004759 setTextViewColorSecondary(contentView, R.id.chronometer);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004760 } else {
4761 contentView.setViewVisibility(R.id.time, View.VISIBLE);
4762 contentView.setLong(R.id.time, "setTime", mN.when);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004763 setTextViewColorSecondary(contentView, R.id.time);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004764 }
Selim Cinekb85f36fd2016-04-20 18:46:36 -07004765 } else {
4766 // We still want a time to be set but gone, such that we can show and hide it
4767 // on demand in case it's a child notification without anything in the header
4768 contentView.setLong(R.id.time, "setTime", mN.when != 0 ? mN.when : mN.creationTime);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004769 }
4770 }
4771
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07004772 private void bindHeaderText(RemoteViews contentView) {
4773 CharSequence headerText = mN.extras.getCharSequence(EXTRA_SUB_TEXT);
4774 if (headerText == null && mStyle != null && mStyle.mSummaryTextSet
Selim Cinek03d0d652015-11-13 13:18:09 -05004775 && mStyle.hasSummaryInHeader()) {
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07004776 headerText = mStyle.mSummaryText;
Selim Cinek03d0d652015-11-13 13:18:09 -05004777 }
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07004778 if (headerText == null
4779 && mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N
4780 && mN.extras.getCharSequence(EXTRA_INFO_TEXT) != null) {
4781 headerText = mN.extras.getCharSequence(EXTRA_INFO_TEXT);
4782 }
4783 if (headerText != null) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004784 // TODO: Remove the span entirely to only have the string with propper formating.
Selim Cinek48f66b72017-08-18 16:17:51 -07004785 contentView.setTextViewText(R.id.header_text, processTextSpans(
4786 processLegacyText(headerText)));
Selim Cinek7b9605b2017-01-19 17:36:00 -08004787 setTextViewColorSecondary(contentView, R.id.header_text);
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07004788 contentView.setViewVisibility(R.id.header_text, View.VISIBLE);
4789 contentView.setViewVisibility(R.id.header_text_divider, View.VISIBLE);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004790 setTextViewColorSecondary(contentView, R.id.header_text_divider);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004791 }
4792 }
4793
Selim Cinekafeed292017-12-12 17:32:44 -08004794 private void bindHeaderTextSecondary(RemoteViews contentView, CharSequence secondaryText) {
4795 if (!TextUtils.isEmpty(secondaryText)) {
4796 contentView.setTextViewText(R.id.header_text_secondary, processTextSpans(
4797 processLegacyText(secondaryText)));
4798 setTextViewColorSecondary(contentView, R.id.header_text_secondary);
4799 contentView.setViewVisibility(R.id.header_text_secondary, View.VISIBLE);
4800 contentView.setViewVisibility(R.id.header_text_secondary_divider, View.VISIBLE);
4801 setTextViewColorSecondary(contentView, R.id.header_text_secondary_divider);
4802 }
4803 }
4804
Adrian Rooseba05822016-04-22 17:09:27 -07004805 /**
4806 * @hide
4807 */
Mathew Inwood61e8ae62018-08-14 14:17:44 +01004808 @UnsupportedAppUsage
Adrian Rooseba05822016-04-22 17:09:27 -07004809 public String loadHeaderAppName() {
Dan Sandler732bd6c2016-04-12 14:20:32 -04004810 CharSequence name = null;
4811 final PackageManager pm = mContext.getPackageManager();
4812 if (mN.extras.containsKey(EXTRA_SUBSTITUTE_APP_NAME)) {
4813 // only system packages which lump together a bunch of unrelated stuff
4814 // may substitute a different name to make the purpose of the
4815 // notification more clear. the correct package label should always
4816 // be accessible via SystemUI.
4817 final String pkg = mContext.getPackageName();
4818 final String subName = mN.extras.getString(EXTRA_SUBSTITUTE_APP_NAME);
4819 if (PackageManager.PERMISSION_GRANTED == pm.checkPermission(
4820 android.Manifest.permission.SUBSTITUTE_NOTIFICATION_APP_NAME, pkg)) {
4821 name = subName;
4822 } else {
4823 Log.w(TAG, "warning: pkg "
4824 + pkg + " attempting to substitute app name '" + subName
4825 + "' without holding perm "
4826 + android.Manifest.permission.SUBSTITUTE_NOTIFICATION_APP_NAME);
4827 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004828 }
Dan Sandler732bd6c2016-04-12 14:20:32 -04004829 if (TextUtils.isEmpty(name)) {
4830 name = pm.getApplicationLabel(mContext.getApplicationInfo());
4831 }
4832 if (TextUtils.isEmpty(name)) {
4833 // still nothing?
4834 return null;
4835 }
4836
4837 return String.valueOf(name);
4838 }
Adrian Roos487374f2017-01-11 15:48:14 -08004839 private void bindHeaderAppName(RemoteViews contentView, boolean ambient) {
Dan Sandler732bd6c2016-04-12 14:20:32 -04004840 contentView.setTextViewText(R.id.app_name_text, loadHeaderAppName());
Adrian Roos72171622017-01-27 10:32:06 -08004841 if (isColorized() && !ambient) {
Selim Cinek7b9605b2017-01-19 17:36:00 -08004842 setTextViewColorPrimary(contentView, R.id.app_name_text);
4843 } else {
4844 contentView.setTextColor(R.id.app_name_text,
Selim Cinekc7f5a822018-03-20 19:32:06 -07004845 ambient ? resolveAmbientColor() : getSecondaryTextColor());
Selim Cinek7b9605b2017-01-19 17:36:00 -08004846 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004847 }
4848
Adrian Roos487374f2017-01-11 15:48:14 -08004849 private void bindSmallIcon(RemoteViews contentView, boolean ambient) {
Selim Cinek279fa862016-06-14 10:57:25 -07004850 if (mN.mSmallIcon == null && mN.icon != 0) {
4851 mN.mSmallIcon = Icon.createWithResource(mContext, mN.icon);
4852 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004853 contentView.setImageViewIcon(R.id.icon, mN.mSmallIcon);
Sunny Goyal5b153922017-09-21 21:00:36 -07004854 contentView.setInt(R.id.icon, "setImageLevel", mN.iconLevel);
Adrian Roos487374f2017-01-11 15:48:14 -08004855 processSmallIconColor(mN.mSmallIcon, contentView, ambient);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004856 }
4857
Jorim Jaggi445d3c02014-08-19 22:33:42 +02004858 /**
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004859 * @return true if the built notification will show the time or the chronometer; false
4860 * otherwise
4861 */
4862 private boolean showsTimeOrChronometer() {
Selim Cinekc2c0b042016-05-18 17:13:46 -07004863 return mN.showsTime() || mN.showsChronometer();
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004864 }
4865
Christoph Studerfe718432014-09-01 18:21:18 +02004866 private void resetStandardTemplateWithActions(RemoteViews big) {
Adrian Roos4c1fcc82016-03-31 14:39:39 -07004867 // actions_container is only reset when there are no actions to avoid focus issues with
4868 // remote inputs.
Christoph Studerfe718432014-09-01 18:21:18 +02004869 big.setViewVisibility(R.id.actions, View.GONE);
Christoph Studerfe718432014-09-01 18:21:18 +02004870 big.removeAllViews(R.id.actions);
Adrian Roose458aa82015-12-08 16:17:19 -08004871
4872 big.setViewVisibility(R.id.notification_material_reply_container, View.GONE);
4873 big.setTextViewText(R.id.notification_material_reply_text_1, null);
Kenny Guya0f6de82018-04-06 16:20:16 +01004874 big.setViewVisibility(R.id.notification_material_reply_text_1_container, View.GONE);
4875 big.setViewVisibility(R.id.notification_material_reply_progress, View.GONE);
Adrian Roose458aa82015-12-08 16:17:19 -08004876
4877 big.setViewVisibility(R.id.notification_material_reply_text_2, View.GONE);
4878 big.setTextViewText(R.id.notification_material_reply_text_2, null);
4879 big.setViewVisibility(R.id.notification_material_reply_text_3, View.GONE);
4880 big.setTextViewText(R.id.notification_material_reply_text_3, null);
Adrian Roosf852a422016-06-03 13:33:43 -07004881
Selim Cineked64a142018-02-06 18:06:01 -08004882 big.setViewLayoutMarginBottomDimen(R.id.notification_action_list_margin_target,
4883 R.dimen.notification_content_margin);
Christoph Studerfe718432014-09-01 18:21:18 +02004884 }
4885
Selim Cinek384804b2018-04-18 14:31:07 +08004886 private RemoteViews applyStandardTemplateWithActions(int layoutId,
4887 TemplateBindResult result) {
4888 return applyStandardTemplateWithActions(layoutId, mParams.reset().fillTextsFrom(this),
4889 result);
Adrian Roos48d746a2016-04-12 14:57:28 -07004890 }
4891
Adrian Roos70d7aa32017-01-11 15:39:06 -08004892 private RemoteViews applyStandardTemplateWithActions(int layoutId,
Selim Cinek384804b2018-04-18 14:31:07 +08004893 StandardTemplateParams p, TemplateBindResult result) {
4894 RemoteViews big = applyStandardTemplate(layoutId, p, result);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004895
Christoph Studerfe718432014-09-01 18:21:18 +02004896 resetStandardTemplateWithActions(big);
4897
Adrian Roose458aa82015-12-08 16:17:19 -08004898 boolean validRemoteInput = false;
4899
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004900 int N = mActions.size();
Adrian Roos487374f2017-01-11 15:48:14 -08004901 boolean emphazisedMode = mN.fullScreenIntent != null && !p.ambient;
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004902 big.setBoolean(R.id.actions, "setEmphasizedMode", emphazisedMode);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004903 if (N > 0) {
Adrian Roos7052de52016-03-03 15:53:34 -08004904 big.setViewVisibility(R.id.actions_container, View.VISIBLE);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004905 big.setViewVisibility(R.id.actions, View.VISIBLE);
Selim Cineked64a142018-02-06 18:06:01 -08004906 big.setViewLayoutMarginBottomDimen(R.id.notification_action_list_margin_target, 0);
Daniel Sandler8680bf82012-05-15 16:52:52 -04004907 if (N>MAX_ACTION_BUTTONS) N=MAX_ACTION_BUTTONS;
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004908 for (int i=0; i<N; i++) {
Adrian Roose458aa82015-12-08 16:17:19 -08004909 Action action = mActions.get(i);
Selim Cinekffcc7cf2018-02-06 17:43:51 -08004910 boolean actionHasValidInput = hasValidRemoteInput(action);
4911 validRemoteInput |= actionHasValidInput;
Adrian Roose458aa82015-12-08 16:17:19 -08004912
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004913 final RemoteViews button = generateActionButton(action, emphazisedMode,
Selim Cinek396caca2018-04-10 17:46:46 -07004914 p.ambient);
4915 if (actionHasValidInput && !emphazisedMode) {
Selim Cinekffcc7cf2018-02-06 17:43:51 -08004916 // Clear the drawable
4917 button.setInt(R.id.action0, "setBackgroundResource", 0);
4918 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004919 big.addView(R.id.actions, button);
4920 }
Adrian Roos4c1fcc82016-03-31 14:39:39 -07004921 } else {
4922 big.setViewVisibility(R.id.actions_container, View.GONE);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004923 }
Adrian Roose458aa82015-12-08 16:17:19 -08004924
4925 CharSequence[] replyText = mN.extras.getCharSequenceArray(EXTRA_REMOTE_INPUT_HISTORY);
Adrian Roos487374f2017-01-11 15:48:14 -08004926 if (!p.ambient && validRemoteInput && replyText != null
Selim Cinekbee4e072018-05-21 22:06:43 -07004927 && replyText.length > 0 && !TextUtils.isEmpty(replyText[0])
4928 && p.maxRemoteInputHistory > 0) {
Kenny Guya0f6de82018-04-06 16:20:16 +01004929 boolean showSpinner = mN.extras.getBoolean(EXTRA_SHOW_REMOTE_INPUT_SPINNER);
Adrian Roose458aa82015-12-08 16:17:19 -08004930 big.setViewVisibility(R.id.notification_material_reply_container, View.VISIBLE);
Kenny Guya0f6de82018-04-06 16:20:16 +01004931 big.setViewVisibility(R.id.notification_material_reply_text_1_container,
4932 View.VISIBLE);
Selim Cinek48f66b72017-08-18 16:17:51 -07004933 big.setTextViewText(R.id.notification_material_reply_text_1,
4934 processTextSpans(replyText[0]));
Selim Cinek7b9605b2017-01-19 17:36:00 -08004935 setTextViewColorSecondary(big, R.id.notification_material_reply_text_1);
Kenny Guya0f6de82018-04-06 16:20:16 +01004936 big.setViewVisibility(R.id.notification_material_reply_progress,
4937 showSpinner ? View.VISIBLE : View.GONE);
4938 big.setProgressIndeterminateTintList(
4939 R.id.notification_material_reply_progress,
4940 ColorStateList.valueOf(
4941 isColorized() ? getPrimaryTextColor() : resolveContrastColor()));
Adrian Roose458aa82015-12-08 16:17:19 -08004942
Selim Cinekbee4e072018-05-21 22:06:43 -07004943 if (replyText.length > 1 && !TextUtils.isEmpty(replyText[1])
4944 && p.maxRemoteInputHistory > 1) {
Adrian Roose458aa82015-12-08 16:17:19 -08004945 big.setViewVisibility(R.id.notification_material_reply_text_2, View.VISIBLE);
Selim Cinek48f66b72017-08-18 16:17:51 -07004946 big.setTextViewText(R.id.notification_material_reply_text_2,
4947 processTextSpans(replyText[1]));
Selim Cinek7b9605b2017-01-19 17:36:00 -08004948 setTextViewColorSecondary(big, R.id.notification_material_reply_text_2);
Adrian Roose458aa82015-12-08 16:17:19 -08004949
Selim Cinekbee4e072018-05-21 22:06:43 -07004950 if (replyText.length > 2 && !TextUtils.isEmpty(replyText[2])
4951 && p.maxRemoteInputHistory > 2) {
Adrian Roose458aa82015-12-08 16:17:19 -08004952 big.setViewVisibility(
4953 R.id.notification_material_reply_text_3, View.VISIBLE);
Selim Cinek48f66b72017-08-18 16:17:51 -07004954 big.setTextViewText(R.id.notification_material_reply_text_3,
4955 processTextSpans(replyText[2]));
Selim Cinek7b9605b2017-01-19 17:36:00 -08004956 setTextViewColorSecondary(big, R.id.notification_material_reply_text_3);
Adrian Roose458aa82015-12-08 16:17:19 -08004957 }
4958 }
4959 }
4960
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004961 return big;
4962 }
4963
Adrian Roose458aa82015-12-08 16:17:19 -08004964 private boolean hasValidRemoteInput(Action action) {
4965 if (TextUtils.isEmpty(action.title) || action.actionIntent == null) {
4966 // Weird actions
4967 return false;
4968 }
4969
4970 RemoteInput[] remoteInputs = action.getRemoteInputs();
4971 if (remoteInputs == null) {
4972 return false;
4973 }
4974
4975 for (RemoteInput r : remoteInputs) {
4976 CharSequence[] choices = r.getChoices();
4977 if (r.getAllowFreeFormInput() || (choices != null && choices.length != 0)) {
4978 return true;
4979 }
4980 }
4981 return false;
4982 }
4983
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004984 /**
4985 * Construct a RemoteViews for the final 1U notification layout. In order:
4986 * 1. Custom contentView from the caller
4987 * 2. Style's proposed content view
4988 * 3. Standard template view
4989 */
Julia Reynolds3b848122016-02-26 10:45:32 -05004990 public RemoteViews createContentView() {
Selim Cinek7d1009b2017-01-25 15:28:28 -08004991 return createContentView(false /* increasedheight */ );
4992 }
4993
4994 /**
4995 * Construct a RemoteViews for the smaller content view.
4996 *
4997 * @param increasedHeight true if this layout be created with an increased height. Some
4998 * styles may support showing more then just that basic 1U size
4999 * and the system may decide to render important notifications
5000 * slightly bigger even when collapsed.
5001 *
5002 * @hide
5003 */
5004 public RemoteViews createContentView(boolean increasedHeight) {
Selim Cineka7679b62017-05-10 16:33:25 -07005005 if (mN.contentView != null && useExistingRemoteView()) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005006 return mN.contentView;
5007 } else if (mStyle != null) {
Selim Cinek7d1009b2017-01-25 15:28:28 -08005008 final RemoteViews styleView = mStyle.makeContentView(increasedHeight);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005009 if (styleView != null) {
5010 return styleView;
5011 }
Joe Onorato46439ce2010-11-19 13:56:21 -08005012 }
Selim Cinek384804b2018-04-18 14:31:07 +08005013 return applyStandardTemplate(getBaseLayoutResource(), null /* result */);
Joe Onorato46439ce2010-11-19 13:56:21 -08005014 }
5015
Selim Cineka7679b62017-05-10 16:33:25 -07005016 private boolean useExistingRemoteView() {
5017 return mStyle == null || (!mStyle.displayCustomViewInline()
5018 && !mRebuildStyledRemoteViews);
5019 }
5020
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005021 /**
5022 * Construct a RemoteViews for the final big notification layout.
5023 */
Julia Reynolds3b848122016-02-26 10:45:32 -05005024 public RemoteViews createBigContentView() {
Selim Cinek850a8542015-11-11 11:48:36 -05005025 RemoteViews result = null;
Selim Cineka7679b62017-05-10 16:33:25 -07005026 if (mN.bigContentView != null && useExistingRemoteView()) {
Julia Reynolds089e3e42015-11-18 09:59:57 -05005027 return mN.bigContentView;
5028 } else if (mStyle != null) {
Selim Cinek850a8542015-11-11 11:48:36 -05005029 result = mStyle.makeBigContentView();
Selim Cinek90dcf6d2015-11-18 20:24:13 -08005030 hideLine1Text(result);
Selim Cinekcc10bfb2016-02-10 16:24:21 -08005031 } else if (mActions.size() != 0) {
Selim Cinek384804b2018-04-18 14:31:07 +08005032 result = applyStandardTemplateWithActions(getBigBaseLayoutResource(),
5033 null /* result */);
Selim Cinek850a8542015-11-11 11:48:36 -05005034 }
Selim Cinek6743c0b2017-01-18 18:24:01 -08005035 makeHeaderExpanded(result);
Selim Cinek850a8542015-11-11 11:48:36 -05005036 return result;
5037 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04005038
Selim Cinekeaa29ca2015-11-23 13:51:13 -08005039 /**
Selim Cinek414ad332017-02-24 19:06:12 -08005040 * Construct a RemoteViews for the final notification header only. This will not be
5041 * colorized.
Selim Cinekeaa29ca2015-11-23 13:51:13 -08005042 *
Adrian Roos6f6e1592017-05-02 16:22:53 -07005043 * @param ambient if true, generate the header for the ambient display layout.
Selim Cinekeaa29ca2015-11-23 13:51:13 -08005044 * @hide
5045 */
Adrian Roos6f6e1592017-05-02 16:22:53 -07005046 public RemoteViews makeNotificationHeader(boolean ambient) {
Selim Cinek414ad332017-02-24 19:06:12 -08005047 Boolean colorized = (Boolean) mN.extras.get(EXTRA_COLORIZED);
5048 mN.extras.putBoolean(EXTRA_COLORIZED, false);
Selim Cinekeaa29ca2015-11-23 13:51:13 -08005049 RemoteViews header = new BuilderRemoteViews(mContext.getApplicationInfo(),
Adrian Roos6f6e1592017-05-02 16:22:53 -07005050 ambient ? R.layout.notification_template_ambient_header
5051 : R.layout.notification_template_header);
Selim Cinekeaa29ca2015-11-23 13:51:13 -08005052 resetNotificationHeader(header);
Selim Cinekafeed292017-12-12 17:32:44 -08005053 bindNotificationHeader(header, ambient, null);
Selim Cinek414ad332017-02-24 19:06:12 -08005054 if (colorized != null) {
5055 mN.extras.putBoolean(EXTRA_COLORIZED, colorized);
5056 } else {
5057 mN.extras.remove(EXTRA_COLORIZED);
5058 }
Selim Cinekeaa29ca2015-11-23 13:51:13 -08005059 return header;
5060 }
5061
Adrian Roos487374f2017-01-11 15:48:14 -08005062 /**
5063 * Construct a RemoteViews for the ambient version of the notification.
5064 *
5065 * @hide
5066 */
5067 public RemoteViews makeAmbientNotification() {
5068 RemoteViews ambient = applyStandardTemplateWithActions(
5069 R.layout.notification_template_material_ambient,
Selim Cinek384804b2018-04-18 14:31:07 +08005070 mParams.reset().ambient(true).fillTextsFrom(this).hasProgress(false),
5071 null /* result */);
Adrian Roos487374f2017-01-11 15:48:14 -08005072 return ambient;
5073 }
5074
Selim Cinek29603462015-11-17 19:04:39 -08005075 private void hideLine1Text(RemoteViews result) {
Selim Cinekcc10bfb2016-02-10 16:24:21 -08005076 if (result != null) {
5077 result.setViewVisibility(R.id.text_line_1, View.GONE);
5078 }
Selim Cinek29603462015-11-17 19:04:39 -08005079 }
5080
Selim Cinek6743c0b2017-01-18 18:24:01 -08005081 /**
5082 * Adapt the Notification header if this view is used as an expanded view.
5083 *
5084 * @hide
5085 */
5086 public static void makeHeaderExpanded(RemoteViews result) {
Selim Cinekcc10bfb2016-02-10 16:24:21 -08005087 if (result != null) {
5088 result.setBoolean(R.id.notification_header, "setExpanded", true);
5089 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04005090 }
5091
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005092 /**
5093 * Construct a RemoteViews for the final heads-up notification layout.
Selim Cinek87ed69b2017-02-09 15:59:43 -08005094 *
5095 * @param increasedHeight true if this layout be created with an increased height. Some
5096 * styles may support showing more then just that basic 1U size
5097 * and the system may decide to render important notifications
5098 * slightly bigger even when collapsed.
5099 *
5100 * @hide
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005101 */
Selim Cinek87ed69b2017-02-09 15:59:43 -08005102 public RemoteViews createHeadsUpContentView(boolean increasedHeight) {
Selim Cineka7679b62017-05-10 16:33:25 -07005103 if (mN.headsUpContentView != null && useExistingRemoteView()) {
Julia Reynolds089e3e42015-11-18 09:59:57 -05005104 return mN.headsUpContentView;
5105 } else if (mStyle != null) {
Selim Cinek87ed69b2017-02-09 15:59:43 -08005106 final RemoteViews styleView = mStyle.makeHeadsUpContentView(increasedHeight);
5107 if (styleView != null) {
5108 return styleView;
5109 }
Julia Reynolds089e3e42015-11-18 09:59:57 -05005110 } else if (mActions.size() == 0) {
5111 return null;
5112 }
5113
Selim Cinekbee4e072018-05-21 22:06:43 -07005114 // We only want at most a single remote input history to be shown here, otherwise
5115 // the content would become squished.
5116 StandardTemplateParams p = mParams.reset().fillTextsFrom(this)
5117 .setMaxRemoteInputHistory(1);
5118 return applyStandardTemplateWithActions(getBigBaseLayoutResource(),
5119 p,
5120 null /* result */);
Chris Wren8fd39ec2014-02-27 17:43:26 -05005121 }
5122
Selim Cinek624c02db2015-12-14 21:00:02 -08005123 /**
Selim Cinek87ed69b2017-02-09 15:59:43 -08005124 * Construct a RemoteViews for the final heads-up notification layout.
5125 */
5126 public RemoteViews createHeadsUpContentView() {
5127 return createHeadsUpContentView(false /* useIncreasedHeight */);
5128 }
5129
5130 /**
Selim Cinek624c02db2015-12-14 21:00:02 -08005131 * Construct a RemoteViews for the display in public contexts like on the lockscreen.
5132 *
5133 * @hide
5134 */
Mathew Inwood61e8ae62018-08-14 14:17:44 +01005135 @UnsupportedAppUsage
Selim Cinek624c02db2015-12-14 21:00:02 -08005136 public RemoteViews makePublicContentView() {
Adrian Roos1a1ecfc2017-04-17 11:17:59 -07005137 return makePublicView(false /* ambient */);
5138 }
5139
5140 /**
5141 * Construct a RemoteViews for the display in public contexts like on the lockscreen.
5142 *
5143 * @hide
5144 */
5145 public RemoteViews makePublicAmbientNotification() {
5146 return makePublicView(true /* ambient */);
5147 }
5148
5149 private RemoteViews makePublicView(boolean ambient) {
Selim Cinek624c02db2015-12-14 21:00:02 -08005150 if (mN.publicVersion != null) {
5151 final Builder builder = recoverBuilder(mContext, mN.publicVersion);
Adrian Roos1a1ecfc2017-04-17 11:17:59 -07005152 return ambient ? builder.makeAmbientNotification() : builder.createContentView();
Selim Cinek624c02db2015-12-14 21:00:02 -08005153 }
5154 Bundle savedBundle = mN.extras;
5155 Style style = mStyle;
5156 mStyle = null;
5157 Icon largeIcon = mN.mLargeIcon;
5158 mN.mLargeIcon = null;
Selim Cinek279fa862016-06-14 10:57:25 -07005159 Bitmap largeIconLegacy = mN.largeIcon;
5160 mN.largeIcon = null;
Adrian Roosb19b06b2017-05-02 18:54:40 -07005161 ArrayList<Action> actions = mActions;
5162 mActions = new ArrayList<>();
Selim Cinek624c02db2015-12-14 21:00:02 -08005163 Bundle publicExtras = new Bundle();
5164 publicExtras.putBoolean(EXTRA_SHOW_WHEN,
5165 savedBundle.getBoolean(EXTRA_SHOW_WHEN));
5166 publicExtras.putBoolean(EXTRA_SHOW_CHRONOMETER,
5167 savedBundle.getBoolean(EXTRA_SHOW_CHRONOMETER));
Adrian Roos96b7e202016-05-17 13:50:38 -07005168 publicExtras.putBoolean(EXTRA_CHRONOMETER_COUNT_DOWN,
5169 savedBundle.getBoolean(EXTRA_CHRONOMETER_COUNT_DOWN));
Selim Cineked07b962018-04-30 14:39:35 -07005170 String appName = savedBundle.getString(EXTRA_SUBSTITUTE_APP_NAME);
5171 if (appName != null) {
5172 publicExtras.putString(EXTRA_SUBSTITUTE_APP_NAME, appName);
5173 }
Selim Cinek624c02db2015-12-14 21:00:02 -08005174 mN.extras = publicExtras;
Selim Cinek499c20f2017-07-20 14:06:09 -07005175 RemoteViews view;
5176 if (ambient) {
5177 publicExtras.putCharSequence(EXTRA_TITLE,
5178 mContext.getString(com.android.internal.R.string.notification_hidden_text));
5179 view = makeAmbientNotification();
5180 } else{
5181 view = makeNotificationHeader(false /* ambient */);
5182 view.setBoolean(R.id.notification_header, "setExpandOnlyOnButton", true);
5183 }
Selim Cinek624c02db2015-12-14 21:00:02 -08005184 mN.extras = savedBundle;
5185 mN.mLargeIcon = largeIcon;
Selim Cinek279fa862016-06-14 10:57:25 -07005186 mN.largeIcon = largeIconLegacy;
Adrian Roosb19b06b2017-05-02 18:54:40 -07005187 mActions = actions;
Selim Cinek624c02db2015-12-14 21:00:02 -08005188 mStyle = style;
Adrian Roos1a1ecfc2017-04-17 11:17:59 -07005189 return view;
Selim Cinek624c02db2015-12-14 21:00:02 -08005190 }
5191
Selim Cinek6743c0b2017-01-18 18:24:01 -08005192 /**
5193 * Construct a content view for the display when low - priority
5194 *
5195 * @param useRegularSubtext uses the normal subtext set if there is one available. Otherwise
5196 * a new subtext is created consisting of the content of the
5197 * notification.
5198 * @hide
5199 */
5200 public RemoteViews makeLowPriorityContentView(boolean useRegularSubtext) {
5201 int color = mN.color;
5202 mN.color = COLOR_DEFAULT;
5203 CharSequence summary = mN.extras.getCharSequence(EXTRA_SUB_TEXT);
5204 if (!useRegularSubtext || TextUtils.isEmpty(summary)) {
5205 CharSequence newSummary = createSummaryText();
5206 if (!TextUtils.isEmpty(newSummary)) {
5207 mN.extras.putCharSequence(EXTRA_SUB_TEXT, newSummary);
5208 }
5209 }
Selim Cinek875ba9b2017-02-13 16:20:17 -08005210
Adrian Roos6f6e1592017-05-02 16:22:53 -07005211 RemoteViews header = makeNotificationHeader(false /* ambient */);
Selim Cinek1b554392017-02-28 17:22:49 -08005212 header.setBoolean(R.id.notification_header, "setAcceptAllTouches", true);
Selim Cinek6743c0b2017-01-18 18:24:01 -08005213 if (summary != null) {
5214 mN.extras.putCharSequence(EXTRA_SUB_TEXT, summary);
5215 } else {
5216 mN.extras.remove(EXTRA_SUB_TEXT);
5217 }
5218 mN.color = color;
5219 return header;
5220 }
Selim Cinek624c02db2015-12-14 21:00:02 -08005221
Selim Cinek6743c0b2017-01-18 18:24:01 -08005222 private CharSequence createSummaryText() {
5223 CharSequence titleText = mN.extras.getCharSequence(Notification.EXTRA_TITLE);
5224 if (USE_ONLY_TITLE_IN_LOW_PRIORITY_SUMMARY) {
5225 return titleText;
5226 }
5227 SpannableStringBuilder summary = new SpannableStringBuilder();
5228 if (titleText == null) {
5229 titleText = mN.extras.getCharSequence(Notification.EXTRA_TITLE_BIG);
5230 }
5231 BidiFormatter bidi = BidiFormatter.getInstance();
5232 if (titleText != null) {
5233 summary.append(bidi.unicodeWrap(titleText));
5234 }
5235 CharSequence contentText = mN.extras.getCharSequence(Notification.EXTRA_TEXT);
5236 if (titleText != null && contentText != null) {
5237 summary.append(bidi.unicodeWrap(mContext.getText(
5238 R.string.notification_header_divider_symbol_with_spaces)));
5239 }
5240 if (contentText != null) {
5241 summary.append(bidi.unicodeWrap(contentText));
5242 }
5243 return summary;
5244 }
Chris Wren8fd39ec2014-02-27 17:43:26 -05005245
Selim Cinek06e9e1f2016-07-08 17:14:16 -07005246 private RemoteViews generateActionButton(Action action, boolean emphazisedMode,
Selim Cinek396caca2018-04-10 17:46:46 -07005247 boolean ambient) {
Daniel Sandler8680bf82012-05-15 16:52:52 -04005248 final boolean tombstone = (action.actionIntent == null);
Selim Cinekf33b1112015-07-15 17:45:11 -07005249 RemoteViews button = new BuilderRemoteViews(mContext.getApplicationInfo(),
Selim Cinek06e9e1f2016-07-08 17:14:16 -07005250 emphazisedMode ? getEmphasizedActionLayoutResource()
5251 : tombstone ? getActionTombstoneLayoutResource()
5252 : getActionLayoutResource());
Daniel Sandler8680bf82012-05-15 16:52:52 -04005253 if (!tombstone) {
Daniel Sandlere5518842012-05-10 16:20:40 -04005254 button.setOnClickPendingIntent(R.id.action0, action.actionIntent);
Daniel Sandlere5518842012-05-10 16:20:40 -04005255 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04005256 button.setContentDescription(R.id.action0, action.title);
Adrian Roosfe84e1f2015-11-04 15:55:39 -08005257 if (action.mRemoteInputs != null) {
5258 button.setRemoteInputs(R.id.action0, action.mRemoteInputs);
5259 }
Selim Cinek06e9e1f2016-07-08 17:14:16 -07005260 if (emphazisedMode) {
Selim Cinek981962e2016-07-20 20:41:58 -07005261 // change the background bgColor
Selim Cinek981962e2016-07-20 20:41:58 -07005262 CharSequence title = action.title;
5263 ColorStateList[] outResultColor = null;
Selim Cinek396caca2018-04-10 17:46:46 -07005264 int background = resolveBackgroundColor();
Selim Cinek981962e2016-07-20 20:41:58 -07005265 if (isLegacy()) {
Lucas Dupina291d192018-06-07 13:59:42 -07005266 title = ContrastColorUtil.clearColorSpans(title);
Selim Cinek981962e2016-07-20 20:41:58 -07005267 } else {
5268 outResultColor = new ColorStateList[1];
Selim Cinek396caca2018-04-10 17:46:46 -07005269 title = ensureColorSpanContrast(title, background, outResultColor);
Selim Cinek981962e2016-07-20 20:41:58 -07005270 }
Selim Cinek48f66b72017-08-18 16:17:51 -07005271 button.setTextViewText(R.id.action0, processTextSpans(title));
Selim Cinek7b9605b2017-01-19 17:36:00 -08005272 setTextViewColorPrimary(button, R.id.action0);
Selim Cinek396caca2018-04-10 17:46:46 -07005273 int rippleColor;
5274 boolean hasColorOverride = outResultColor != null && outResultColor[0] != null;
5275 if (hasColorOverride) {
5276 // There's a span spanning the full text, let's take it and use it as the
5277 // background color
5278 background = outResultColor[0].getDefaultColor();
Lucas Dupina291d192018-06-07 13:59:42 -07005279 int textColor = ContrastColorUtil.resolvePrimaryColor(mContext,
Lucas Dupinf03e7522018-06-25 16:21:13 -07005280 background, mInNightMode);
Selim Cinek396caca2018-04-10 17:46:46 -07005281 button.setTextColor(R.id.action0, textColor);
5282 rippleColor = textColor;
Anthony Chenad4d1582017-04-10 16:07:58 -07005283 } else if (mN.color != COLOR_DEFAULT && !isColorized() && mTintActionButtons) {
Selim Cinek396caca2018-04-10 17:46:46 -07005284 rippleColor = resolveContrastColor();
5285 button.setTextColor(R.id.action0, rippleColor);
5286 } else {
5287 rippleColor = getPrimaryTextColor();
Selim Cinek981962e2016-07-20 20:41:58 -07005288 }
Selim Cinek396caca2018-04-10 17:46:46 -07005289 // We only want about 20% alpha for the ripple
5290 rippleColor = (rippleColor & 0x00ffffff) | 0x33000000;
5291 button.setColorStateList(R.id.action0, "setRippleColor",
5292 ColorStateList.valueOf(rippleColor));
5293 button.setColorStateList(R.id.action0, "setButtonBackground",
5294 ColorStateList.valueOf(background));
5295 button.setBoolean(R.id.action0, "setHasStroke", !hasColorOverride);
Selim Cinek06e9e1f2016-07-08 17:14:16 -07005296 } else {
Selim Cinek48f66b72017-08-18 16:17:51 -07005297 button.setTextViewText(R.id.action0, processTextSpans(
5298 processLegacyText(action.title)));
Adrian Roos72171622017-01-27 10:32:06 -08005299 if (isColorized() && !ambient) {
Selim Cinek7b9605b2017-01-19 17:36:00 -08005300 setTextViewColorPrimary(button, R.id.action0);
Anthony Chenad4d1582017-04-10 16:07:58 -07005301 } else if (mN.color != COLOR_DEFAULT && mTintActionButtons) {
Adrian Roos487374f2017-01-11 15:48:14 -08005302 button.setTextColor(R.id.action0,
5303 ambient ? resolveAmbientColor() : resolveContrastColor());
Selim Cinek06e9e1f2016-07-08 17:14:16 -07005304 }
Adrian Roosfe84e1f2015-11-04 15:55:39 -08005305 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04005306 return button;
5307 }
5308
Joe Onoratocb109a02011-01-18 17:57:41 -08005309 /**
Selim Cinek981962e2016-07-20 20:41:58 -07005310 * Ensures contrast on color spans against a background color. also returns the color of the
5311 * text if a span was found that spans over the whole text.
5312 *
5313 * @param charSequence the charSequence on which the spans are
5314 * @param background the background color to ensure the contrast against
5315 * @param outResultColor an array in which a color will be returned as the first element if
5316 * there exists a full length color span.
5317 * @return the contrasted charSequence
5318 */
5319 private CharSequence ensureColorSpanContrast(CharSequence charSequence, int background,
5320 ColorStateList[] outResultColor) {
5321 if (charSequence instanceof Spanned) {
5322 Spanned ss = (Spanned) charSequence;
5323 Object[] spans = ss.getSpans(0, ss.length(), Object.class);
5324 SpannableStringBuilder builder = new SpannableStringBuilder(ss.toString());
5325 for (Object span : spans) {
5326 Object resultSpan = span;
5327 int spanStart = ss.getSpanStart(span);
5328 int spanEnd = ss.getSpanEnd(span);
5329 boolean fullLength = (spanEnd - spanStart) == charSequence.length();
5330 if (resultSpan instanceof CharacterStyle) {
5331 resultSpan = ((CharacterStyle) span).getUnderlying();
5332 }
5333 if (resultSpan instanceof TextAppearanceSpan) {
5334 TextAppearanceSpan originalSpan = (TextAppearanceSpan) resultSpan;
5335 ColorStateList textColor = originalSpan.getTextColor();
5336 if (textColor != null) {
5337 int[] colors = textColor.getColors();
5338 int[] newColors = new int[colors.length];
5339 for (int i = 0; i < newColors.length; i++) {
Lucas Dupina291d192018-06-07 13:59:42 -07005340 newColors[i] = ContrastColorUtil.ensureLargeTextContrast(
Anthony Chenad4d1582017-04-10 16:07:58 -07005341 colors[i], background, mInNightMode);
Selim Cinek981962e2016-07-20 20:41:58 -07005342 }
5343 textColor = new ColorStateList(textColor.getStates().clone(),
5344 newColors);
Selim Cinek396caca2018-04-10 17:46:46 -07005345 if (fullLength) {
5346 outResultColor[0] = textColor;
5347 // Let's drop the color from the span
5348 textColor = null;
5349 }
Selim Cinek981962e2016-07-20 20:41:58 -07005350 resultSpan = new TextAppearanceSpan(
5351 originalSpan.getFamily(),
5352 originalSpan.getTextStyle(),
5353 originalSpan.getTextSize(),
5354 textColor,
5355 originalSpan.getLinkTextColor());
Selim Cinek981962e2016-07-20 20:41:58 -07005356 }
5357 } else if (resultSpan instanceof ForegroundColorSpan) {
5358 ForegroundColorSpan originalSpan = (ForegroundColorSpan) resultSpan;
5359 int foregroundColor = originalSpan.getForegroundColor();
Lucas Dupina291d192018-06-07 13:59:42 -07005360 foregroundColor = ContrastColorUtil.ensureLargeTextContrast(
Anthony Chenad4d1582017-04-10 16:07:58 -07005361 foregroundColor, background, mInNightMode);
Selim Cinek981962e2016-07-20 20:41:58 -07005362 if (fullLength) {
5363 outResultColor[0] = ColorStateList.valueOf(foregroundColor);
Selim Cinek396caca2018-04-10 17:46:46 -07005364 resultSpan = null;
5365 } else {
5366 resultSpan = new ForegroundColorSpan(foregroundColor);
Selim Cinek981962e2016-07-20 20:41:58 -07005367 }
5368 } else {
5369 resultSpan = span;
5370 }
Selim Cinek396caca2018-04-10 17:46:46 -07005371 if (resultSpan != null) {
5372 builder.setSpan(resultSpan, spanStart, spanEnd, ss.getSpanFlags(span));
5373 }
Selim Cinek981962e2016-07-20 20:41:58 -07005374 }
5375 return builder;
5376 }
5377 return charSequence;
5378 }
5379
5380 /**
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01005381 * @return Whether we are currently building a notification from a legacy (an app that
Alan Viverette3cb07a462014-06-06 14:19:53 -07005382 * doesn't create material notifications by itself) app.
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01005383 */
5384 private boolean isLegacy() {
Selim Cinek7b9605b2017-01-19 17:36:00 -08005385 if (!mIsLegacyInitialized) {
5386 mIsLegacy = mContext.getApplicationInfo().targetSdkVersion
5387 < Build.VERSION_CODES.LOLLIPOP;
5388 mIsLegacyInitialized = true;
5389 }
5390 return mIsLegacy;
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01005391 }
5392
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01005393 private CharSequence processLegacyText(CharSequence charSequence) {
Adrian Roos0bc3f6a2017-03-06 11:54:05 -08005394 return processLegacyText(charSequence, false /* ambient */);
5395 }
5396
5397 private CharSequence processLegacyText(CharSequence charSequence, boolean ambient) {
5398 boolean isAlreadyLightText = isLegacy() || textColorsNeedInversion();
5399 boolean wantLightText = ambient;
5400 if (isAlreadyLightText != wantLightText) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005401 return getColorUtil().invertCharSequenceColors(charSequence);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01005402 } else {
5403 return charSequence;
5404 }
5405 }
5406
Dan Sandler26e81cf2014-05-06 10:01:27 -04005407 /**
Selim Cinek65b2e7c2015-10-26 14:11:31 -07005408 * Apply any necessariy colors to the small icon
Dan Sandler26e81cf2014-05-06 10:01:27 -04005409 */
Adrian Roos487374f2017-01-11 15:48:14 -08005410 private void processSmallIconColor(Icon smallIcon, RemoteViews contentView,
5411 boolean ambient) {
Selim Cinekea4bef72015-12-02 15:51:10 -08005412 boolean colorable = !isLegacy() || getColorUtil().isGrayscaleIcon(mContext, smallIcon);
Selim Cinekc7f5a822018-03-20 19:32:06 -07005413 int color;
5414 if (ambient) {
5415 color = resolveAmbientColor();
5416 } else if (isColorized()) {
5417 color = getPrimaryTextColor();
5418 } else {
5419 color = resolveContrastColor();
5420 }
Selim Cinekea4bef72015-12-02 15:51:10 -08005421 if (colorable) {
Sunny Goyal5b153922017-09-21 21:00:36 -07005422 contentView.setDrawableTint(R.id.icon, false, color,
5423 PorterDuff.Mode.SRC_ATOP);
Selim Cinekea4bef72015-12-02 15:51:10 -08005424
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01005425 }
Selim Cinekea4bef72015-12-02 15:51:10 -08005426 contentView.setInt(R.id.notification_header, "setOriginalIconColor",
Adrian Roos487374f2017-01-11 15:48:14 -08005427 colorable ? color : NotificationHeaderView.NO_COLOR);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01005428 }
5429
Dan Sandler26e81cf2014-05-06 10:01:27 -04005430 /**
Selim Cinek65b2e7c2015-10-26 14:11:31 -07005431 * Make the largeIcon dark if it's a fake smallIcon (that is,
Dan Sandler26e81cf2014-05-06 10:01:27 -04005432 * if it's grayscale).
5433 */
5434 // TODO: also check bounds, transparency, that sort of thing.
Dan Sandlerd63f9322015-05-06 15:18:49 -04005435 private void processLargeLegacyIcon(Icon largeIcon, RemoteViews contentView) {
5436 if (largeIcon != null && isLegacy()
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005437 && getColorUtil().isGrayscaleIcon(mContext, largeIcon)) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07005438 // resolve color will fall back to the default when legacy
Sunny Goyal5b153922017-09-21 21:00:36 -07005439 contentView.setDrawableTint(R.id.icon, false, resolveContrastColor(),
5440 PorterDuff.Mode.SRC_ATOP);
Jorim Jaggi92df1f22014-12-16 19:44:41 +01005441 }
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01005442 }
5443
Julia Reynolds10ee1fc2015-11-09 11:04:55 -05005444 private void sanitizeColor() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005445 if (mN.color != COLOR_DEFAULT) {
5446 mN.color |= 0xFF000000; // no alpha for custom colors
Jorim Jaggi74419312014-06-10 20:57:21 +02005447 }
Jorim Jaggi74419312014-06-10 20:57:21 +02005448 }
5449
Adrian Roos4ff3b122016-02-01 12:26:13 -08005450 int resolveContrastColor() {
5451 if (mCachedContrastColorIsFor == mN.color && mCachedContrastColor != COLOR_INVALID) {
5452 return mCachedContrastColor;
Dan Sandler26e81cf2014-05-06 10:01:27 -04005453 }
Adrian Roos4ff3b122016-02-01 12:26:13 -08005454
Selim Cinekac5f0272017-05-02 16:05:41 -07005455 int color;
Selim Cinekc7f5a822018-03-20 19:32:06 -07005456 int background = mContext.getColor(
5457 com.android.internal.R.color.notification_material_background_color);
Selim Cinekac5f0272017-05-02 16:05:41 -07005458 if (mN.color == COLOR_DEFAULT) {
5459 ensureColors();
Lucas Dupinf03e7522018-06-25 16:21:13 -07005460 color = ContrastColorUtil.resolveDefaultColor(mContext, background, mInNightMode);
Selim Cinekac5f0272017-05-02 16:05:41 -07005461 } else {
Lucas Dupina291d192018-06-07 13:59:42 -07005462 color = ContrastColorUtil.resolveContrastColor(mContext, mN.color,
Anthony Chenad4d1582017-04-10 16:07:58 -07005463 background, mInNightMode);
Selim Cinekac5f0272017-05-02 16:05:41 -07005464 }
5465 if (Color.alpha(color) < 255) {
5466 // alpha doesn't go well for color filters, so let's blend it manually
Lucas Dupina291d192018-06-07 13:59:42 -07005467 color = ContrastColorUtil.compositeColors(color, background);
Selim Cinekac5f0272017-05-02 16:05:41 -07005468 }
Adrian Roos4ff3b122016-02-01 12:26:13 -08005469 mCachedContrastColorIsFor = mN.color;
Selim Cinekac5f0272017-05-02 16:05:41 -07005470 return mCachedContrastColor = color;
Dan Sandler26e81cf2014-05-06 10:01:27 -04005471 }
5472
Selim Cinek4717d862018-04-19 09:19:15 +08005473 int resolveNeutralColor() {
5474 if (mNeutralColor != COLOR_INVALID) {
5475 return mNeutralColor;
5476 }
5477 int background = mContext.getColor(
5478 com.android.internal.R.color.notification_material_background_color);
Lucas Dupinf03e7522018-06-25 16:21:13 -07005479 mNeutralColor = ContrastColorUtil.resolveDefaultColor(mContext, background,
5480 mInNightMode);
Selim Cinek4717d862018-04-19 09:19:15 +08005481 if (Color.alpha(mNeutralColor) < 255) {
5482 // alpha doesn't go well for color filters, so let's blend it manually
Lucas Dupina291d192018-06-07 13:59:42 -07005483 mNeutralColor = ContrastColorUtil.compositeColors(mNeutralColor, background);
Selim Cinek4717d862018-04-19 09:19:15 +08005484 }
5485 return mNeutralColor;
5486 }
5487
Adrian Roos487374f2017-01-11 15:48:14 -08005488 int resolveAmbientColor() {
5489 if (mCachedAmbientColorIsFor == mN.color && mCachedAmbientColorIsFor != COLOR_INVALID) {
5490 return mCachedAmbientColor;
5491 }
Lucas Dupina291d192018-06-07 13:59:42 -07005492 final int contrasted = ContrastColorUtil.resolveAmbientColor(mContext, mN.color);
Adrian Roos487374f2017-01-11 15:48:14 -08005493
5494 mCachedAmbientColorIsFor = mN.color;
5495 return mCachedAmbientColor = contrasted;
5496 }
5497
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01005498 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005499 * Apply the unstyled operations and return a new {@link Notification} object.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04005500 * @hide
Joe Onoratocb109a02011-01-18 17:57:41 -08005501 */
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04005502 public Notification buildUnstyled() {
Daniel Sandlera0a938c2012-03-15 08:42:37 -04005503 if (mActions.size() > 0) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005504 mN.actions = new Action[mActions.size()];
5505 mActions.toArray(mN.actions);
Daniel Sandlera0a938c2012-03-15 08:42:37 -04005506 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005507 if (!mPersonList.isEmpty()) {
Selim Cineke7238dd2017-12-14 17:48:32 -08005508 mN.extras.putParcelableArrayList(EXTRA_PEOPLE_LIST, mPersonList);
Dan Sandler0bf2ed82013-12-21 23:33:41 -06005509 }
Selim Cinek247fa012016-02-18 09:50:48 -08005510 if (mN.bigContentView != null || mN.contentView != null
5511 || mN.headsUpContentView != null) {
5512 mN.extras.putBoolean(EXTRA_CONTAINS_CUSTOM_VIEW, true);
5513 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005514 return mN;
Joe Onorato46439ce2010-11-19 13:56:21 -08005515 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005516
Julia Reynolds3b848122016-02-26 10:45:32 -05005517 /**
5518 * Creates a Builder from an existing notification so further changes can be made.
5519 * @param context The context for your application / activity.
5520 * @param n The notification to create a Builder from.
5521 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005522 public static Notification.Builder recoverBuilder(Context context, Notification n) {
Christoph Studer4600f9b2014-07-22 22:44:43 +02005523 // Re-create notification context so we can access app resources.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005524 ApplicationInfo applicationInfo = n.extras.getParcelable(
5525 EXTRA_BUILDER_APPLICATION_INFO);
Christoph Studer4600f9b2014-07-22 22:44:43 +02005526 Context builderContext;
Julia Reynoldsda303542015-11-23 14:00:20 -05005527 if (applicationInfo != null) {
5528 try {
5529 builderContext = context.createApplicationContext(applicationInfo,
5530 Context.CONTEXT_RESTRICTED);
5531 } catch (NameNotFoundException e) {
5532 Log.e(TAG, "ApplicationInfo " + applicationInfo + " not found");
5533 builderContext = context; // try with our context
5534 }
5535 } else {
5536 builderContext = context; // try with given context
Christoph Studer4600f9b2014-07-22 22:44:43 +02005537 }
5538
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005539 return new Builder(builderContext, n);
Christoph Studer4600f9b2014-07-22 22:44:43 +02005540 }
5541
Daniel Sandlerf45564e2013-04-15 15:05:08 -04005542 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005543 * @deprecated Use {@link #build()} instead.
5544 */
5545 @Deprecated
5546 public Notification getNotification() {
5547 return build();
5548 }
5549
5550 /**
5551 * Combine all of the options that have been set and return a new {@link Notification}
5552 * object.
5553 */
5554 public Notification build() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005555 // first, add any extras from the calling code
5556 if (mUserExtras != null) {
5557 mN.extras = getAllExtras();
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07005558 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005559
Selim Cinekb85f36fd2016-04-20 18:46:36 -07005560 mN.creationTime = System.currentTimeMillis();
5561
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005562 // lazy stuff from mContext; see comment in Builder(Context, Notification)
Julia Reynoldsda303542015-11-23 14:00:20 -05005563 Notification.addFieldsFromContext(mContext, mN);
Christoph Studer943aa672014-08-03 20:31:16 +02005564
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005565 buildUnstyled();
Daniel Sandlerf45564e2013-04-15 15:05:08 -04005566
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005567 if (mStyle != null) {
Selim Cinekd0426622017-07-11 13:19:59 +02005568 mStyle.reduceImageSizes(mContext);
5569 mStyle.purgeResources();
Selim Cinek90343862018-02-01 11:07:11 -08005570 mStyle.validate(mContext);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005571 mStyle.buildStyled(mN);
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005572 }
Selim Cinekd0426622017-07-11 13:19:59 +02005573 mN.reduceImageSizes(mContext);
5574
Adrian Roos5081c0d2016-02-26 16:04:19 -08005575 if (mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N
Selim Cineka7679b62017-05-10 16:33:25 -07005576 && (useExistingRemoteView())) {
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05005577 if (mN.contentView == null) {
Julia Reynolds3b848122016-02-26 10:45:32 -05005578 mN.contentView = createContentView();
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05005579 mN.extras.putInt(EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT,
5580 mN.contentView.getSequenceNumber());
5581 }
5582 if (mN.bigContentView == null) {
Julia Reynolds3b848122016-02-26 10:45:32 -05005583 mN.bigContentView = createBigContentView();
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05005584 if (mN.bigContentView != null) {
5585 mN.extras.putInt(EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT,
5586 mN.bigContentView.getSequenceNumber());
5587 }
5588 }
5589 if (mN.headsUpContentView == null) {
Julia Reynolds3b848122016-02-26 10:45:32 -05005590 mN.headsUpContentView = createHeadsUpContentView();
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05005591 if (mN.headsUpContentView != null) {
5592 mN.extras.putInt(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT,
5593 mN.headsUpContentView.getSequenceNumber());
5594 }
5595 }
5596 }
5597
Julia Reynolds4c0c2022016-02-02 15:11:59 -05005598 if ((mN.defaults & DEFAULT_LIGHTS) != 0) {
5599 mN.flags |= FLAG_SHOW_LIGHTS;
5600 }
5601
Adrian Roosfb921842017-10-26 14:49:56 +02005602 mN.allPendingIntents = null;
5603
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005604 return mN;
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005605 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05005606
5607 /**
5608 * Apply this Builder to an existing {@link Notification} object.
5609 *
5610 * @hide
5611 */
5612 public Notification buildInto(Notification n) {
Daniel Sandler1a497d32013-04-18 14:52:45 -04005613 build().cloneInto(n, true);
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05005614 return n;
5615 }
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01005616
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05005617 /**
Adrian Roos184bfe022016-03-03 13:41:44 -08005618 * Removes RemoteViews that were created for compatibility from {@param n}, if they did not
Julia Reynolds8a3b4592017-06-26 17:15:14 -04005619 * change. Also removes extenders on low ram devices, as
5620 * {@link android.service.notification.NotificationListenerService} services are disabled.
Adrian Roos184bfe022016-03-03 13:41:44 -08005621 *
5622 * @return {@param n}, if no stripping is needed, otherwise a stripped clone of {@param n}.
5623 *
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05005624 * @hide
5625 */
Kristian Monsen05f34792018-04-09 10:27:16 +02005626 public static Notification maybeCloneStrippedForDelivery(Notification n, boolean isLowRam,
5627 Context context) {
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05005628 String templateClass = n.extras.getString(EXTRA_TEMPLATE);
Adrian Roos184bfe022016-03-03 13:41:44 -08005629
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05005630 // Only strip views for known Styles because we won't know how to
5631 // re-create them otherwise.
Julia Reynolds8a3b4592017-06-26 17:15:14 -04005632 if (!isLowRam
5633 && !TextUtils.isEmpty(templateClass)
Adrian Roos184bfe022016-03-03 13:41:44 -08005634 && getNotificationStyleClass(templateClass) == null) {
5635 return n;
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05005636 }
Adrian Roos184bfe022016-03-03 13:41:44 -08005637
5638 // Only strip unmodified BuilderRemoteViews.
5639 boolean stripContentView = n.contentView instanceof BuilderRemoteViews &&
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05005640 n.extras.getInt(EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT, -1) ==
Adrian Roos184bfe022016-03-03 13:41:44 -08005641 n.contentView.getSequenceNumber();
5642 boolean stripBigContentView = n.bigContentView instanceof BuilderRemoteViews &&
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05005643 n.extras.getInt(EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT, -1) ==
Adrian Roos184bfe022016-03-03 13:41:44 -08005644 n.bigContentView.getSequenceNumber();
5645 boolean stripHeadsUpContentView = n.headsUpContentView instanceof BuilderRemoteViews &&
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05005646 n.extras.getInt(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT, -1) ==
Adrian Roos184bfe022016-03-03 13:41:44 -08005647 n.headsUpContentView.getSequenceNumber();
5648
5649 // Nothing to do here, no need to clone.
Julia Reynolds8a3b4592017-06-26 17:15:14 -04005650 if (!isLowRam
5651 && !stripContentView && !stripBigContentView && !stripHeadsUpContentView) {
Adrian Roos184bfe022016-03-03 13:41:44 -08005652 return n;
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05005653 }
Adrian Roos184bfe022016-03-03 13:41:44 -08005654
5655 Notification clone = n.clone();
5656 if (stripContentView) {
5657 clone.contentView = null;
5658 clone.extras.remove(EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT);
5659 }
5660 if (stripBigContentView) {
5661 clone.bigContentView = null;
5662 clone.extras.remove(EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT);
5663 }
5664 if (stripHeadsUpContentView) {
5665 clone.headsUpContentView = null;
5666 clone.extras.remove(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT);
5667 }
Julia Reynolds8a3b4592017-06-26 17:15:14 -04005668 if (isLowRam) {
Kristian Monsen05f34792018-04-09 10:27:16 +02005669 String[] allowedServices = context.getResources().getStringArray(
5670 R.array.config_allowedManagedServicesOnLowRamDevices);
5671 if (allowedServices.length == 0) {
5672 clone.extras.remove(Notification.TvExtender.EXTRA_TV_EXTENDER);
5673 clone.extras.remove(WearableExtender.EXTRA_WEARABLE_EXTENSIONS);
5674 clone.extras.remove(CarExtender.EXTRA_CAR_EXTENDER);
5675 }
Julia Reynolds8a3b4592017-06-26 17:15:14 -04005676 }
Adrian Roos184bfe022016-03-03 13:41:44 -08005677 return clone;
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05005678 }
5679
Mathew Inwood61e8ae62018-08-14 14:17:44 +01005680 @UnsupportedAppUsage
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01005681 private int getBaseLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07005682 return R.layout.notification_template_material_base;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01005683 }
5684
5685 private int getBigBaseLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07005686 return R.layout.notification_template_material_big_base;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01005687 }
5688
5689 private int getBigPictureLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07005690 return R.layout.notification_template_material_big_picture;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01005691 }
5692
5693 private int getBigTextLayoutResource() {
Jorim Jaggi445d3c02014-08-19 22:33:42 +02005694 return R.layout.notification_template_material_big_text;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01005695 }
5696
5697 private int getInboxLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07005698 return R.layout.notification_template_material_inbox;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01005699 }
5700
Adrian Roosc1a80b02016-04-05 14:54:55 -07005701 private int getMessagingLayoutResource() {
5702 return R.layout.notification_template_material_messaging;
5703 }
5704
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01005705 private int getActionLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07005706 return R.layout.notification_material_action;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01005707 }
5708
Selim Cinek06e9e1f2016-07-08 17:14:16 -07005709 private int getEmphasizedActionLayoutResource() {
5710 return R.layout.notification_material_action_emphasized;
5711 }
5712
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01005713 private int getActionTombstoneLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07005714 return R.layout.notification_material_action_tombstone;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01005715 }
Selim Cinek7b9605b2017-01-19 17:36:00 -08005716
5717 private int getBackgroundColor() {
5718 if (isColorized()) {
Selim Cinek5fb73f82017-04-20 16:55:38 -07005719 return mBackgroundColor != COLOR_INVALID ? mBackgroundColor : mN.color;
Selim Cinek7b9605b2017-01-19 17:36:00 -08005720 } else {
Selim Cinekc7f5a822018-03-20 19:32:06 -07005721 return COLOR_DEFAULT;
Selim Cinek7b9605b2017-01-19 17:36:00 -08005722 }
5723 }
5724
Selim Cinek396caca2018-04-10 17:46:46 -07005725 /**
Selim Cinek4717d862018-04-19 09:19:15 +08005726 * Gets a neutral color that can be used for icons or similar that should not stand out.
5727 */
5728 private int getNeutralColor() {
5729 if (isColorized()) {
5730 return getSecondaryTextColor();
5731 } else {
5732 return resolveNeutralColor();
5733 }
5734 }
5735
5736 /**
Selim Cinek396caca2018-04-10 17:46:46 -07005737 * Same as getBackgroundColor but also resolved the default color to the background.
5738 */
5739 private int resolveBackgroundColor() {
5740 int backgroundColor = getBackgroundColor();
5741 if (backgroundColor == COLOR_DEFAULT) {
5742 backgroundColor = mContext.getColor(
5743 com.android.internal.R.color.notification_material_background_color);
5744 }
5745 return backgroundColor;
5746 }
5747
Selim Cinek7b9605b2017-01-19 17:36:00 -08005748 private boolean isColorized() {
5749 return mN.isColorized();
5750 }
Selim Cinek99104832017-01-25 14:47:33 -08005751
Anthony Chenad4d1582017-04-10 16:07:58 -07005752 private boolean shouldTintActionButtons() {
5753 return mTintActionButtons;
5754 }
5755
Selim Cinek99104832017-01-25 14:47:33 -08005756 private boolean textColorsNeedInversion() {
5757 if (mStyle == null || !MediaStyle.class.equals(mStyle.getClass())) {
5758 return false;
5759 }
5760 int targetSdkVersion = mContext.getApplicationInfo().targetSdkVersion;
5761 return targetSdkVersion > Build.VERSION_CODES.M
5762 && targetSdkVersion < Build.VERSION_CODES.O;
5763 }
Selim Cinek5fb73f82017-04-20 16:55:38 -07005764
5765 /**
5766 * Set a color palette to be used as the background and textColors
5767 *
5768 * @param backgroundColor the color to be used as the background
5769 * @param foregroundColor the color to be used as the foreground
5770 *
5771 * @hide
5772 */
5773 public void setColorPalette(int backgroundColor, int foregroundColor) {
5774 mBackgroundColor = backgroundColor;
5775 mForegroundColor = foregroundColor;
5776 mTextColorsAreForBackground = COLOR_INVALID;
5777 ensureColors();
5778 }
Selim Cinekac5f0272017-05-02 16:05:41 -07005779
5780 /**
Selim Cineka7679b62017-05-10 16:33:25 -07005781 * Forces all styled remoteViews to be built from scratch and not use any cached
5782 * RemoteViews.
5783 * This is needed for legacy apps that are baking in their remoteviews into the
5784 * notification.
5785 *
5786 * @hide
5787 */
5788 public void setRebuildStyledRemoteViews(boolean rebuild) {
5789 mRebuildStyledRemoteViews = rebuild;
5790 }
Selim Cinekaa9db1f2018-02-27 17:35:47 -08005791
5792 /**
5793 * Get the text that should be displayed in the statusBar when heads upped. This is
5794 * usually just the app name, but may be different depending on the style.
5795 *
5796 * @param publicMode If true, return a text that is safe to display in public.
5797 *
5798 * @hide
5799 */
5800 public CharSequence getHeadsUpStatusBarText(boolean publicMode) {
5801 if (mStyle != null && !publicMode) {
5802 CharSequence text = mStyle.getHeadsUpStatusBarText();
5803 if (!TextUtils.isEmpty(text)) {
5804 return text;
5805 }
5806 }
5807 return loadHeaderAppName();
5808 }
Selim Cinek7b9605b2017-01-19 17:36:00 -08005809 }
5810
5811 /**
Selim Cinekd0426622017-07-11 13:19:59 +02005812 * Reduces the image sizes to conform to a maximum allowed size. This also processes all custom
5813 * remote views.
5814 *
5815 * @hide
5816 */
5817 void reduceImageSizes(Context context) {
5818 if (extras.getBoolean(EXTRA_REDUCED_IMAGES)) {
5819 return;
5820 }
Selim Cineka8cb1262017-08-15 16:53:44 -07005821 boolean isLowRam = ActivityManager.isLowRamDeviceStatic();
Selim Cinekd0426622017-07-11 13:19:59 +02005822 if (mLargeIcon != null || largeIcon != null) {
5823 Resources resources = context.getResources();
5824 Class<? extends Style> style = getNotificationStyle();
Selim Cineka8cb1262017-08-15 16:53:44 -07005825 int maxWidth = resources.getDimensionPixelSize(isLowRam
5826 ? R.dimen.notification_right_icon_size_low_ram
5827 : R.dimen.notification_right_icon_size);
Selim Cinekd0426622017-07-11 13:19:59 +02005828 int maxHeight = maxWidth;
5829 if (MediaStyle.class.equals(style)
5830 || DecoratedMediaCustomViewStyle.class.equals(style)) {
Selim Cineka8cb1262017-08-15 16:53:44 -07005831 maxHeight = resources.getDimensionPixelSize(isLowRam
5832 ? R.dimen.notification_media_image_max_height_low_ram
5833 : R.dimen.notification_media_image_max_height);
5834 maxWidth = resources.getDimensionPixelSize(isLowRam
5835 ? R.dimen.notification_media_image_max_width_low_ram
5836 : R.dimen.notification_media_image_max_width);
Selim Cinekd0426622017-07-11 13:19:59 +02005837 }
5838 if (mLargeIcon != null) {
5839 mLargeIcon.scaleDownIfNecessary(maxWidth, maxHeight);
5840 }
5841 if (largeIcon != null) {
5842 largeIcon = Icon.scaleDownIfNecessary(largeIcon, maxWidth, maxHeight);
5843 }
5844 }
Selim Cineka8cb1262017-08-15 16:53:44 -07005845 reduceImageSizesForRemoteView(contentView, context, isLowRam);
5846 reduceImageSizesForRemoteView(headsUpContentView, context, isLowRam);
5847 reduceImageSizesForRemoteView(bigContentView, context, isLowRam);
Selim Cinekd0426622017-07-11 13:19:59 +02005848 extras.putBoolean(EXTRA_REDUCED_IMAGES, true);
5849 }
5850
Selim Cineka8cb1262017-08-15 16:53:44 -07005851 private void reduceImageSizesForRemoteView(RemoteViews remoteView, Context context,
5852 boolean isLowRam) {
Selim Cinekd0426622017-07-11 13:19:59 +02005853 if (remoteView != null) {
5854 Resources resources = context.getResources();
Selim Cineka8cb1262017-08-15 16:53:44 -07005855 int maxWidth = resources.getDimensionPixelSize(isLowRam
5856 ? R.dimen.notification_custom_view_max_image_width_low_ram
5857 : R.dimen.notification_custom_view_max_image_width);
5858 int maxHeight = resources.getDimensionPixelSize(isLowRam
5859 ? R.dimen.notification_custom_view_max_image_height_low_ram
5860 : R.dimen.notification_custom_view_max_image_height);
Selim Cinekd0426622017-07-11 13:19:59 +02005861 remoteView.reduceImageSizes(maxWidth, maxHeight);
5862 }
5863 }
5864
5865 /**
Selim Cinek22714f12017-04-13 16:23:53 -07005866 * @return whether this notification is a foreground service notification
Selim Cinek7b9605b2017-01-19 17:36:00 -08005867 */
Selim Cinek22714f12017-04-13 16:23:53 -07005868 private boolean isForegroundService() {
5869 return (flags & Notification.FLAG_FOREGROUND_SERVICE) != 0;
Selim Cinek7b9605b2017-01-19 17:36:00 -08005870 }
5871
5872 /**
Selim Cinek99104832017-01-25 14:47:33 -08005873 * @return whether this notification has a media session attached
5874 * @hide
5875 */
5876 public boolean hasMediaSession() {
5877 return extras.getParcelable(Notification.EXTRA_MEDIA_SESSION) != null;
5878 }
5879
5880 /**
Selim Cinek7d1009b2017-01-25 15:28:28 -08005881 * @return the style class of this notification
5882 * @hide
5883 */
5884 public Class<? extends Notification.Style> getNotificationStyle() {
5885 String templateClass = extras.getString(Notification.EXTRA_TEMPLATE);
5886
5887 if (!TextUtils.isEmpty(templateClass)) {
5888 return Notification.getNotificationStyleClass(templateClass);
5889 }
5890 return null;
5891 }
5892
5893 /**
Selim Cinek22714f12017-04-13 16:23:53 -07005894 * @return true if this notification is colorized.
Selim Cinek7b9605b2017-01-19 17:36:00 -08005895 *
5896 * @hide
5897 */
5898 public boolean isColorized() {
Selim Cinek5fb73f82017-04-20 16:55:38 -07005899 if (isColorizedMedia()) {
5900 return true;
5901 }
Julia Reynolds4db59552017-06-30 13:34:01 -04005902 return extras.getBoolean(EXTRA_COLORIZED)
5903 && (hasColorizedPermission() || isForegroundService());
5904 }
5905
5906 /**
5907 * Returns whether an app can colorize due to the android.permission.USE_COLORIZED_NOTIFICATIONS
5908 * permission. The permission is checked when a notification is enqueued.
5909 */
5910 private boolean hasColorizedPermission() {
5911 return (flags & Notification.FLAG_CAN_COLORIZE) != 0;
Selim Cinek5fb73f82017-04-20 16:55:38 -07005912 }
5913
5914 /**
5915 * @return true if this notification is colorized and it is a media notification
5916 *
5917 * @hide
5918 */
5919 public boolean isColorizedMedia() {
Selim Cinek99104832017-01-25 14:47:33 -08005920 Class<? extends Style> style = getNotificationStyle();
5921 if (MediaStyle.class.equals(style)) {
5922 Boolean colorized = (Boolean) extras.get(EXTRA_COLORIZED);
5923 if ((colorized == null || colorized) && hasMediaSession()) {
5924 return true;
5925 }
5926 } else if (DecoratedMediaCustomViewStyle.class.equals(style)) {
5927 if (extras.getBoolean(EXTRA_COLORIZED) && hasMediaSession()) {
5928 return true;
5929 }
5930 }
Selim Cinek5fb73f82017-04-20 16:55:38 -07005931 return false;
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005932 }
5933
Selim Cinek0847acd2017-04-24 19:48:29 -07005934
5935 /**
5936 * @return true if this is a media notification
5937 *
5938 * @hide
5939 */
5940 public boolean isMediaNotification() {
5941 Class<? extends Style> style = getNotificationStyle();
5942 if (MediaStyle.class.equals(style)) {
5943 return true;
5944 } else if (DecoratedMediaCustomViewStyle.class.equals(style)) {
5945 return true;
5946 }
5947 return false;
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005948 }
5949
Selim Cinek279fa862016-06-14 10:57:25 -07005950 private boolean hasLargeIcon() {
5951 return mLargeIcon != null || largeIcon != null;
5952 }
5953
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005954 /**
Selim Cinekc2c0b042016-05-18 17:13:46 -07005955 * @return true if the notification will show the time; false otherwise
Selim Cinekb85f36fd2016-04-20 18:46:36 -07005956 * @hide
5957 */
Selim Cinekc2c0b042016-05-18 17:13:46 -07005958 public boolean showsTime() {
Selim Cinekb85f36fd2016-04-20 18:46:36 -07005959 return when != 0 && extras.getBoolean(EXTRA_SHOW_WHEN);
5960 }
5961
5962 /**
Selim Cinekc2c0b042016-05-18 17:13:46 -07005963 * @return true if the notification will show a chronometer; false otherwise
5964 * @hide
5965 */
5966 public boolean showsChronometer() {
5967 return when != 0 && extras.getBoolean(EXTRA_SHOW_CHRONOMETER);
5968 }
5969
5970 /**
Julia Reynolds7ca33072017-06-29 13:58:24 -04005971 * @removed
Julia Reynolds4a02afb2016-12-13 13:39:52 -05005972 */
5973 @SystemApi
5974 public static Class<? extends Style> getNotificationStyleClass(String templateClass) {
5975 Class<? extends Style>[] classes = new Class[] {
5976 BigTextStyle.class, BigPictureStyle.class, InboxStyle.class, MediaStyle.class,
5977 DecoratedCustomViewStyle.class, DecoratedMediaCustomViewStyle.class,
5978 MessagingStyle.class };
5979 for (Class<? extends Style> innerClass : classes) {
5980 if (templateClass.equals(innerClass.getName())) {
5981 return innerClass;
5982 }
5983 }
5984 return null;
5985 }
5986
5987 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005988 * An object that can apply a rich notification style to a {@link Notification.Builder}
5989 * object.
5990 */
Griff Hazendfcb0802014-02-11 12:00:00 -08005991 public static abstract class Style {
Selim Cinekbee4e072018-05-21 22:06:43 -07005992
5993 /**
5994 * The number of items allowed simulatanously in the remote input history.
5995 * @hide
5996 */
5997 static final int MAX_REMOTE_INPUT_HISTORY_LINES = 3;
Chris Wrend6297db2012-05-03 16:20:13 -04005998 private CharSequence mBigContentTitle;
Jorim Jaggi457a10d2014-09-08 16:18:23 +02005999
6000 /**
6001 * @hide
6002 */
6003 protected CharSequence mSummaryText = null;
6004
6005 /**
6006 * @hide
6007 */
6008 protected boolean mSummaryTextSet = false;
Chris Wrend6297db2012-05-03 16:20:13 -04006009
Chris Wrenfbd96ba2012-05-01 12:03:58 -04006010 protected Builder mBuilder;
6011
Chris Wrend6297db2012-05-03 16:20:13 -04006012 /**
6013 * Overrides ContentTitle in the big form of the template.
6014 * This defaults to the value passed to setContentTitle().
6015 */
6016 protected void internalSetBigContentTitle(CharSequence title) {
6017 mBigContentTitle = title;
6018 }
6019
6020 /**
6021 * Set the first line of text after the detail section in the big form of the template.
6022 */
6023 protected void internalSetSummaryText(CharSequence cs) {
6024 mSummaryText = cs;
Daniel Sandler619738c2012-06-07 16:33:08 -04006025 mSummaryTextSet = true;
Chris Wrend6297db2012-05-03 16:20:13 -04006026 }
6027
Chris Wrenfbd96ba2012-05-01 12:03:58 -04006028 public void setBuilder(Builder builder) {
6029 if (mBuilder != builder) {
6030 mBuilder = builder;
Daniel Sandlerc08dea22012-06-28 08:35:24 -07006031 if (mBuilder != null) {
6032 mBuilder.setStyle(this);
6033 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04006034 }
6035 }
6036
Chris Wrend6297db2012-05-03 16:20:13 -04006037 protected void checkBuilder() {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04006038 if (mBuilder == null) {
6039 throw new IllegalArgumentException("Style requires a valid Builder object");
6040 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04006041 }
Chris Wrend6297db2012-05-03 16:20:13 -04006042
6043 protected RemoteViews getStandardView(int layoutId) {
Selim Cinek384804b2018-04-18 14:31:07 +08006044 return getStandardView(layoutId, null);
6045 }
6046
6047 /**
6048 * Get the standard view for this style.
6049 *
6050 * @param layoutId The layout id to use
6051 * @param result The result where template bind information is saved.
6052 * @return A remoteView for this style.
6053 * @hide
6054 */
6055 protected RemoteViews getStandardView(int layoutId, TemplateBindResult result) {
Chris Wrend6297db2012-05-03 16:20:13 -04006056 checkBuilder();
6057
Christoph Studer4600f9b2014-07-22 22:44:43 +02006058 // Nasty.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04006059 CharSequence oldBuilderContentTitle =
6060 mBuilder.getAllExtras().getCharSequence(EXTRA_TITLE);
Chris Wrend6297db2012-05-03 16:20:13 -04006061 if (mBigContentTitle != null) {
6062 mBuilder.setContentTitle(mBigContentTitle);
6063 }
6064
Selim Cinek384804b2018-04-18 14:31:07 +08006065 RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(layoutId, result);
Chris Wrend6297db2012-05-03 16:20:13 -04006066
Julia Reynoldsd9228f12015-10-20 10:37:27 -04006067 mBuilder.getAllExtras().putCharSequence(EXTRA_TITLE, oldBuilderContentTitle);
Christoph Studer4600f9b2014-07-22 22:44:43 +02006068
Chris Wrend6297db2012-05-03 16:20:13 -04006069 if (mBigContentTitle != null && mBigContentTitle.equals("")) {
6070 contentView.setViewVisibility(R.id.line1, View.GONE);
Chris Wren67dc9a02012-05-16 01:03:20 -04006071 } else {
6072 contentView.setViewVisibility(R.id.line1, View.VISIBLE);
Chris Wrend6297db2012-05-03 16:20:13 -04006073 }
6074
Chris Wrend6297db2012-05-03 16:20:13 -04006075 return contentView;
6076 }
6077
Daniel Sandlerf45564e2013-04-15 15:05:08 -04006078 /**
Selim Cinek7d1009b2017-01-25 15:28:28 -08006079 * Construct a Style-specific RemoteViews for the collapsed notification layout.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04006080 * The default implementation has nothing additional to add.
Selim Cinek7d1009b2017-01-25 15:28:28 -08006081 *
6082 * @param increasedHeight true if this layout be created with an increased height.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04006083 * @hide
6084 */
Selim Cinek7d1009b2017-01-25 15:28:28 -08006085 public RemoteViews makeContentView(boolean increasedHeight) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04006086 return null;
6087 }
6088
6089 /**
6090 * Construct a Style-specific RemoteViews for the final big notification layout.
6091 * @hide
6092 */
6093 public RemoteViews makeBigContentView() {
6094 return null;
6095 }
6096
6097 /**
6098 * Construct a Style-specific RemoteViews for the final HUN layout.
Selim Cinek87ed69b2017-02-09 15:59:43 -08006099 *
6100 * @param increasedHeight true if this layout be created with an increased height.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04006101 * @hide
6102 */
Selim Cinek87ed69b2017-02-09 15:59:43 -08006103 public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04006104 return null;
6105 }
6106
6107 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04006108 * Apply any style-specific extras to this notification before shipping it out.
Daniel Sandlerf45564e2013-04-15 15:05:08 -04006109 * @hide
6110 */
6111 public void addExtras(Bundle extras) {
6112 if (mSummaryTextSet) {
6113 extras.putCharSequence(EXTRA_SUMMARY_TEXT, mSummaryText);
6114 }
6115 if (mBigContentTitle != null) {
6116 extras.putCharSequence(EXTRA_TITLE_BIG, mBigContentTitle);
6117 }
Chris Wren91ad5632013-06-05 15:05:57 -04006118 extras.putString(EXTRA_TEMPLATE, this.getClass().getName());
Daniel Sandlerf45564e2013-04-15 15:05:08 -04006119 }
6120
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04006121 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04006122 * Reconstruct the internal state of this Style object from extras.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04006123 * @hide
6124 */
Christoph Studer4600f9b2014-07-22 22:44:43 +02006125 protected void restoreFromExtras(Bundle extras) {
6126 if (extras.containsKey(EXTRA_SUMMARY_TEXT)) {
6127 mSummaryText = extras.getCharSequence(EXTRA_SUMMARY_TEXT);
6128 mSummaryTextSet = true;
6129 }
6130 if (extras.containsKey(EXTRA_TITLE_BIG)) {
6131 mBigContentTitle = extras.getCharSequence(EXTRA_TITLE_BIG);
6132 }
6133 }
6134
6135
6136 /**
6137 * @hide
6138 */
6139 public Notification buildStyled(Notification wip) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04006140 addExtras(wip.extras);
Christoph Studer4600f9b2014-07-22 22:44:43 +02006141 return wip;
6142 }
6143
Daniel Sandler0ec46202015-06-24 01:27:05 -04006144 /**
6145 * @hide
6146 */
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07006147 public void purgeResources() {}
6148
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04006149 /**
6150 * Calls {@link android.app.Notification.Builder#build()} on the Builder this Style is
6151 * attached to.
6152 *
6153 * @return the fully constructed Notification.
6154 */
6155 public Notification build() {
6156 checkBuilder();
6157 return mBuilder.build();
6158 }
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006159
6160 /**
6161 * @hide
6162 * @return true if the style positions the progress bar on the second line; false if the
6163 * style hides the progress bar
6164 */
6165 protected boolean hasProgress() {
6166 return true;
6167 }
Selim Cinek03d0d652015-11-13 13:18:09 -05006168
6169 /**
6170 * @hide
6171 * @return Whether we should put the summary be put into the notification header
6172 */
6173 public boolean hasSummaryInHeader() {
6174 return true;
6175 }
Selim Cinek593610c2016-02-16 18:42:57 -08006176
6177 /**
6178 * @hide
6179 * @return Whether custom content views are displayed inline in the style
6180 */
6181 public boolean displayCustomViewInline() {
6182 return false;
6183 }
Selim Cinekd0426622017-07-11 13:19:59 +02006184
6185 /**
6186 * Reduces the image sizes contained in this style.
6187 *
6188 * @hide
6189 */
6190 public void reduceImageSizes(Context context) {
6191 }
Selim Cinek90343862018-02-01 11:07:11 -08006192
6193 /**
6194 * Validate that this style was properly composed. This is called at build time.
6195 * @hide
6196 */
6197 public void validate(Context context) {
6198 }
Julia Reynolds7217dc92018-03-07 12:12:09 -05006199
6200 /**
6201 * @hide
6202 */
6203 public abstract boolean areNotificationsVisiblyDifferent(Style other);
Selim Cinekc7f5a822018-03-20 19:32:06 -07006204
Selim Cinekaa9db1f2018-02-27 17:35:47 -08006205 /**
6206 * @return the the text that should be displayed in the statusBar when heads-upped.
6207 * If {@code null} is returned, the default implementation will be used.
6208 *
6209 * @hide
6210 */
6211 public CharSequence getHeadsUpStatusBarText() {
6212 return null;
6213 }
Joe Onorato46439ce2010-11-19 13:56:21 -08006214 }
Daniel Sandlerf3b73432012-03-27 15:01:25 -04006215
6216 /**
Daniel Sandler4dfbe832012-04-11 14:51:46 -04006217 * Helper class for generating large-format notifications that include a large image attachment.
Joe Malin8d40d042012-11-05 11:36:40 -08006218 *
Robert Ly91c5ce32014-06-08 15:37:00 -07006219 * Here's how you'd set the <code>BigPictureStyle</code> on a notification:
Daniel Sandlerf3b73432012-03-27 15:01:25 -04006220 * <pre class="prettyprint">
Robert Ly91c5ce32014-06-08 15:37:00 -07006221 * Notification notif = new Notification.Builder(mContext)
6222 * .setContentTitle(&quot;New photo from &quot; + sender.toString())
6223 * .setContentText(subject)
6224 * .setSmallIcon(R.drawable.new_post)
6225 * .setLargeIcon(aBitmap)
6226 * .setStyle(new Notification.BigPictureStyle()
6227 * .bigPicture(aBigBitmap))
6228 * .build();
Daniel Sandlerf3b73432012-03-27 15:01:25 -04006229 * </pre>
Joe Malin8d40d042012-11-05 11:36:40 -08006230 *
Daniel Sandler4dfbe832012-04-11 14:51:46 -04006231 * @see Notification#bigContentView
Daniel Sandlerf3b73432012-03-27 15:01:25 -04006232 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04006233 public static class BigPictureStyle extends Style {
Daniel Sandlerf3b73432012-03-27 15:01:25 -04006234 private Bitmap mPicture;
Dan Sandlerd63f9322015-05-06 15:18:49 -04006235 private Icon mBigLargeIcon;
Chris Wren3745a3d2012-05-22 15:11:52 -04006236 private boolean mBigLargeIconSet = false;
Daniel Sandlerf3b73432012-03-27 15:01:25 -04006237
Chris Wrenfbd96ba2012-05-01 12:03:58 -04006238 public BigPictureStyle() {
6239 }
6240
Adrian Roosf5faf9d2016-05-23 13:56:15 -07006241 /**
6242 * @deprecated use {@code BigPictureStyle()}.
6243 */
6244 @Deprecated
Daniel Sandlerf3b73432012-03-27 15:01:25 -04006245 public BigPictureStyle(Builder builder) {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04006246 setBuilder(builder);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04006247 }
6248
Chris Wrend6297db2012-05-03 16:20:13 -04006249 /**
6250 * Overrides ContentTitle in the big form of the template.
6251 * This defaults to the value passed to setContentTitle().
6252 */
6253 public BigPictureStyle setBigContentTitle(CharSequence title) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04006254 internalSetBigContentTitle(safeCharSequence(title));
Chris Wrend6297db2012-05-03 16:20:13 -04006255 return this;
6256 }
6257
6258 /**
6259 * Set the first line of text after the detail section in the big form of the template.
6260 */
6261 public BigPictureStyle setSummaryText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04006262 internalSetSummaryText(safeCharSequence(cs));
Chris Wrend6297db2012-05-03 16:20:13 -04006263 return this;
6264 }
6265
Chris Wren0bd664d2012-08-01 13:56:56 -04006266 /**
Julia Reynolds7217dc92018-03-07 12:12:09 -05006267 * @hide
6268 */
6269 public Bitmap getBigPicture() {
6270 return mPicture;
6271 }
6272
6273 /**
Chris Wren0bd664d2012-08-01 13:56:56 -04006274 * Provide the bitmap to be used as the payload for the BigPicture notification.
6275 */
Daniel Sandlerf3b73432012-03-27 15:01:25 -04006276 public BigPictureStyle bigPicture(Bitmap b) {
6277 mPicture = b;
6278 return this;
6279 }
6280
Chris Wren3745a3d2012-05-22 15:11:52 -04006281 /**
Chris Wren3745a3d2012-05-22 15:11:52 -04006282 * Override the large icon when the big notification is shown.
6283 */
6284 public BigPictureStyle bigLargeIcon(Bitmap b) {
Dan Sandlerd63f9322015-05-06 15:18:49 -04006285 return bigLargeIcon(b != null ? Icon.createWithBitmap(b) : null);
6286 }
6287
6288 /**
6289 * Override the large icon when the big notification is shown.
6290 */
6291 public BigPictureStyle bigLargeIcon(Icon icon) {
Chris Wren3745a3d2012-05-22 15:11:52 -04006292 mBigLargeIconSet = true;
Dan Sandlerd63f9322015-05-06 15:18:49 -04006293 mBigLargeIcon = icon;
Chris Wren3745a3d2012-05-22 15:11:52 -04006294 return this;
6295 }
6296
Riley Andrews0394a0c2015-11-03 23:36:52 -08006297 /** @hide */
6298 public static final int MIN_ASHMEM_BITMAP_SIZE = 128 * (1 << 10);
6299
Daniel Sandler0ec46202015-06-24 01:27:05 -04006300 /**
6301 * @hide
6302 */
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07006303 @Override
6304 public void purgeResources() {
6305 super.purgeResources();
Riley Andrews8cee7c12015-11-01 23:36:04 -08006306 if (mPicture != null &&
6307 mPicture.isMutable() &&
Riley Andrews0394a0c2015-11-03 23:36:52 -08006308 mPicture.getAllocationByteCount() >= MIN_ASHMEM_BITMAP_SIZE) {
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07006309 mPicture = mPicture.createAshmemBitmap();
6310 }
6311 if (mBigLargeIcon != null) {
6312 mBigLargeIcon.convertToAshmem();
6313 }
6314 }
Christoph Studer5c510ee2014-12-15 16:32:27 +01006315
Julia Reynoldsd9228f12015-10-20 10:37:27 -04006316 /**
6317 * @hide
6318 */
Selim Cinekd0426622017-07-11 13:19:59 +02006319 @Override
6320 public void reduceImageSizes(Context context) {
6321 super.reduceImageSizes(context);
6322 Resources resources = context.getResources();
Selim Cineka8cb1262017-08-15 16:53:44 -07006323 boolean isLowRam = ActivityManager.isLowRamDeviceStatic();
Selim Cinekd0426622017-07-11 13:19:59 +02006324 if (mPicture != null) {
Selim Cineka8cb1262017-08-15 16:53:44 -07006325 int maxPictureWidth = resources.getDimensionPixelSize(isLowRam
6326 ? R.dimen.notification_big_picture_max_height_low_ram
6327 : R.dimen.notification_big_picture_max_height);
6328 int maxPictureHeight = resources.getDimensionPixelSize(isLowRam
6329 ? R.dimen.notification_big_picture_max_width_low_ram
6330 : R.dimen.notification_big_picture_max_width);
Selim Cinekd0426622017-07-11 13:19:59 +02006331 mPicture = Icon.scaleDownIfNecessary(mPicture, maxPictureWidth, maxPictureHeight);
6332 }
6333 if (mBigLargeIcon != null) {
Selim Cineka8cb1262017-08-15 16:53:44 -07006334 int rightIconSize = resources.getDimensionPixelSize(isLowRam
6335 ? R.dimen.notification_right_icon_size_low_ram
6336 : R.dimen.notification_right_icon_size);
Selim Cinekd0426622017-07-11 13:19:59 +02006337 mBigLargeIcon.scaleDownIfNecessary(rightIconSize, rightIconSize);
6338 }
6339 }
6340
6341 /**
6342 * @hide
6343 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -04006344 public RemoteViews makeBigContentView() {
6345 // Replace mN.mLargeIcon with mBigLargeIcon if mBigLargeIconSet
Christoph Studer5c510ee2014-12-15 16:32:27 +01006346 // This covers the following cases:
6347 // 1. mBigLargeIconSet -> mBigLargeIcon (null or non-null) applies, overrides
Julia Reynoldsd9228f12015-10-20 10:37:27 -04006348 // mN.mLargeIcon
6349 // 2. !mBigLargeIconSet -> mN.mLargeIcon applies
Dan Sandlerd63f9322015-05-06 15:18:49 -04006350 Icon oldLargeIcon = null;
Selim Cineke99acb22016-08-04 12:55:48 -07006351 Bitmap largeIconLegacy = null;
Christoph Studer5c510ee2014-12-15 16:32:27 +01006352 if (mBigLargeIconSet) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04006353 oldLargeIcon = mBuilder.mN.mLargeIcon;
6354 mBuilder.mN.mLargeIcon = mBigLargeIcon;
Selim Cineke99acb22016-08-04 12:55:48 -07006355 // The legacy largeIcon might not allow us to clear the image, as it's taken in
6356 // replacement if the other one is null. Because we're restoring these legacy icons
6357 // for old listeners, this is in general non-null.
6358 largeIconLegacy = mBuilder.mN.largeIcon;
6359 mBuilder.mN.largeIcon = null;
Christoph Studer5c510ee2014-12-15 16:32:27 +01006360 }
6361
Selim Cinek384804b2018-04-18 14:31:07 +08006362 RemoteViews contentView = getStandardView(mBuilder.getBigPictureLayoutResource(),
6363 null /* result */);
Selim Cinek03d0d652015-11-13 13:18:09 -05006364 if (mSummaryTextSet) {
Selim Cinek48f66b72017-08-18 16:17:51 -07006365 contentView.setTextViewText(R.id.text, mBuilder.processTextSpans(
6366 mBuilder.processLegacyText(mSummaryText)));
Selim Cinek7b9605b2017-01-19 17:36:00 -08006367 mBuilder.setTextViewColorSecondary(contentView, R.id.text);
Selim Cinekc848c3a2016-01-13 15:27:30 -08006368 contentView.setViewVisibility(R.id.text, View.VISIBLE);
Selim Cinek03d0d652015-11-13 13:18:09 -05006369 }
Selim Cinek279fa862016-06-14 10:57:25 -07006370 mBuilder.setContentMinHeight(contentView, mBuilder.mN.hasLargeIcon());
Selim Cinek53e64a42015-11-16 10:40:56 -08006371
Christoph Studer5c510ee2014-12-15 16:32:27 +01006372 if (mBigLargeIconSet) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04006373 mBuilder.mN.mLargeIcon = oldLargeIcon;
Selim Cineke99acb22016-08-04 12:55:48 -07006374 mBuilder.mN.largeIcon = largeIconLegacy;
Christoph Studer5c510ee2014-12-15 16:32:27 +01006375 }
6376
Daniel Sandlerf3b73432012-03-27 15:01:25 -04006377 contentView.setImageViewBitmap(R.id.big_picture, mPicture);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04006378 return contentView;
6379 }
6380
Daniel Sandlerf45564e2013-04-15 15:05:08 -04006381 /**
6382 * @hide
6383 */
6384 public void addExtras(Bundle extras) {
6385 super.addExtras(extras);
6386
6387 if (mBigLargeIconSet) {
6388 extras.putParcelable(EXTRA_LARGE_ICON_BIG, mBigLargeIcon);
6389 }
6390 extras.putParcelable(EXTRA_PICTURE, mPicture);
6391 }
6392
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04006393 /**
6394 * @hide
6395 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04006396 @Override
Christoph Studer4600f9b2014-07-22 22:44:43 +02006397 protected void restoreFromExtras(Bundle extras) {
6398 super.restoreFromExtras(extras);
6399
6400 if (extras.containsKey(EXTRA_LARGE_ICON_BIG)) {
Christoph Studer5c510ee2014-12-15 16:32:27 +01006401 mBigLargeIconSet = true;
Christoph Studer4600f9b2014-07-22 22:44:43 +02006402 mBigLargeIcon = extras.getParcelable(EXTRA_LARGE_ICON_BIG);
Chris Wren3745a3d2012-05-22 15:11:52 -04006403 }
Christoph Studer4600f9b2014-07-22 22:44:43 +02006404 mPicture = extras.getParcelable(EXTRA_PICTURE);
6405 }
Selim Cinek03d0d652015-11-13 13:18:09 -05006406
6407 /**
6408 * @hide
6409 */
6410 @Override
6411 public boolean hasSummaryInHeader() {
6412 return false;
6413 }
Julia Reynolds7217dc92018-03-07 12:12:09 -05006414
6415 /**
6416 * @hide
Dan Sandler7d67bd42018-05-15 14:06:38 -04006417 * Note that we aren't actually comparing the contents of the bitmaps here, so this
6418 * is only doing a cursory inspection. Bitmaps of equal size will appear the same.
Julia Reynolds7217dc92018-03-07 12:12:09 -05006419 */
6420 @Override
6421 public boolean areNotificationsVisiblyDifferent(Style other) {
6422 if (other == null || getClass() != other.getClass()) {
6423 return true;
6424 }
6425 BigPictureStyle otherS = (BigPictureStyle) other;
Dan Sandler7d67bd42018-05-15 14:06:38 -04006426 return areBitmapsObviouslyDifferent(getBigPicture(), otherS.getBigPicture());
6427 }
6428
6429 private static boolean areBitmapsObviouslyDifferent(Bitmap a, Bitmap b) {
6430 if (a == b) {
6431 return false;
6432 }
6433 if (a == null || b == null) {
6434 return true;
6435 }
6436 return a.getWidth() != b.getWidth()
6437 || a.getHeight() != b.getHeight()
6438 || a.getConfig() != b.getConfig()
6439 || a.getGenerationId() != b.getGenerationId();
Julia Reynolds7217dc92018-03-07 12:12:09 -05006440 }
Daniel Sandlerf3b73432012-03-27 15:01:25 -04006441 }
6442
6443 /**
Daniel Sandler4dfbe832012-04-11 14:51:46 -04006444 * Helper class for generating large-format notifications that include a lot of text.
Joe Malin8d40d042012-11-05 11:36:40 -08006445 *
Robert Ly91c5ce32014-06-08 15:37:00 -07006446 * Here's how you'd set the <code>BigTextStyle</code> on a notification:
Daniel Sandlerf3b73432012-03-27 15:01:25 -04006447 * <pre class="prettyprint">
Robert Ly91c5ce32014-06-08 15:37:00 -07006448 * Notification notif = new Notification.Builder(mContext)
6449 * .setContentTitle(&quot;New mail from &quot; + sender.toString())
6450 * .setContentText(subject)
6451 * .setSmallIcon(R.drawable.new_mail)
6452 * .setLargeIcon(aBitmap)
6453 * .setStyle(new Notification.BigTextStyle()
6454 * .bigText(aVeryLongString))
6455 * .build();
Daniel Sandlerf3b73432012-03-27 15:01:25 -04006456 * </pre>
Joe Malin8d40d042012-11-05 11:36:40 -08006457 *
Daniel Sandler4dfbe832012-04-11 14:51:46 -04006458 * @see Notification#bigContentView
Daniel Sandlerf3b73432012-03-27 15:01:25 -04006459 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04006460 public static class BigTextStyle extends Style {
Jorim Jaggi457a10d2014-09-08 16:18:23 +02006461
Daniel Sandlerf3b73432012-03-27 15:01:25 -04006462 private CharSequence mBigText;
6463
Chris Wrenfbd96ba2012-05-01 12:03:58 -04006464 public BigTextStyle() {
6465 }
6466
Adrian Roosf5faf9d2016-05-23 13:56:15 -07006467 /**
6468 * @deprecated use {@code BigTextStyle()}.
6469 */
6470 @Deprecated
Daniel Sandlerf3b73432012-03-27 15:01:25 -04006471 public BigTextStyle(Builder builder) {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04006472 setBuilder(builder);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04006473 }
6474
Chris Wrend6297db2012-05-03 16:20:13 -04006475 /**
6476 * Overrides ContentTitle in the big form of the template.
6477 * This defaults to the value passed to setContentTitle().
6478 */
6479 public BigTextStyle setBigContentTitle(CharSequence title) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04006480 internalSetBigContentTitle(safeCharSequence(title));
Chris Wrend6297db2012-05-03 16:20:13 -04006481 return this;
6482 }
6483
6484 /**
6485 * Set the first line of text after the detail section in the big form of the template.
6486 */
6487 public BigTextStyle setSummaryText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04006488 internalSetSummaryText(safeCharSequence(cs));
Chris Wrend6297db2012-05-03 16:20:13 -04006489 return this;
6490 }
6491
Chris Wren0bd664d2012-08-01 13:56:56 -04006492 /**
6493 * Provide the longer text to be displayed in the big form of the
6494 * template in place of the content text.
6495 */
Daniel Sandlerf3b73432012-03-27 15:01:25 -04006496 public BigTextStyle bigText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04006497 mBigText = safeCharSequence(cs);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04006498 return this;
6499 }
6500
Daniel Sandlerf45564e2013-04-15 15:05:08 -04006501 /**
6502 * @hide
6503 */
Julia Reynolds7217dc92018-03-07 12:12:09 -05006504 public CharSequence getBigText() {
6505 return mBigText;
6506 }
6507
6508 /**
6509 * @hide
6510 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -04006511 public void addExtras(Bundle extras) {
6512 super.addExtras(extras);
6513
Christoph Studer4600f9b2014-07-22 22:44:43 +02006514 extras.putCharSequence(EXTRA_BIG_TEXT, mBigText);
6515 }
6516
6517 /**
6518 * @hide
6519 */
6520 @Override
6521 protected void restoreFromExtras(Bundle extras) {
6522 super.restoreFromExtras(extras);
6523
6524 mBigText = extras.getCharSequence(EXTRA_BIG_TEXT);
Daniel Sandlerf45564e2013-04-15 15:05:08 -04006525 }
6526
Julia Reynoldsd9228f12015-10-20 10:37:27 -04006527 /**
Selim Cinek7d1009b2017-01-25 15:28:28 -08006528 * @param increasedHeight true if this layout be created with an increased height.
6529 *
6530 * @hide
6531 */
6532 @Override
6533 public RemoteViews makeContentView(boolean increasedHeight) {
6534 if (increasedHeight) {
Selim Cinek5d6ef8d2017-05-18 22:16:00 -07006535 mBuilder.mOriginalActions = mBuilder.mActions;
Selim Cinek7d1009b2017-01-25 15:28:28 -08006536 mBuilder.mActions = new ArrayList<>();
6537 RemoteViews remoteViews = makeBigContentView();
Selim Cinek5d6ef8d2017-05-18 22:16:00 -07006538 mBuilder.mActions = mBuilder.mOriginalActions;
6539 mBuilder.mOriginalActions = null;
Selim Cinek7d1009b2017-01-25 15:28:28 -08006540 return remoteViews;
6541 }
6542 return super.makeContentView(increasedHeight);
6543 }
6544
6545 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04006546 * @hide
6547 */
Selim Cinek87ed69b2017-02-09 15:59:43 -08006548 @Override
6549 public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
6550 if (increasedHeight && mBuilder.mActions.size() > 0) {
6551 return makeBigContentView();
6552 }
6553 return super.makeHeadsUpContentView(increasedHeight);
6554 }
6555
6556 /**
6557 * @hide
6558 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -04006559 public RemoteViews makeBigContentView() {
Christoph Studer4600f9b2014-07-22 22:44:43 +02006560
6561 // Nasty
Selim Cinek75998782016-04-26 10:39:17 -07006562 CharSequence text = mBuilder.getAllExtras().getCharSequence(EXTRA_TEXT);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04006563 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, null);
Daniel Sandler916ad912012-06-13 12:17:07 -04006564
Selim Cinek384804b2018-04-18 14:31:07 +08006565 TemplateBindResult result = new TemplateBindResult();
6566 RemoteViews contentView = getStandardView(mBuilder.getBigTextLayoutResource(), result);
Selim Cinek1c72fa02018-04-23 18:00:54 +08006567 contentView.setInt(R.id.big_text, "setImageEndMargin", result.getIconMarginEnd());
Joe Malin8d40d042012-11-05 11:36:40 -08006568
Selim Cinek75998782016-04-26 10:39:17 -07006569 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, text);
Christoph Studer4600f9b2014-07-22 22:44:43 +02006570
Selim Cinek3a2c4b92015-12-17 17:01:17 -08006571 CharSequence bigTextText = mBuilder.processLegacyText(mBigText);
Selim Cinek75998782016-04-26 10:39:17 -07006572 if (TextUtils.isEmpty(bigTextText)) {
6573 // In case the bigtext is null / empty fall back to the normal text to avoid a weird
6574 // experience
6575 bigTextText = mBuilder.processLegacyText(text);
6576 }
Adrian Roosb1f427c2016-05-26 12:27:15 -07006577 applyBigTextContentView(mBuilder, contentView, bigTextText);
Selim Cinek4fb12d32015-11-19 18:10:48 -08006578
Daniel Sandlerf3b73432012-03-27 15:01:25 -04006579 return contentView;
6580 }
6581
Julia Reynolds7217dc92018-03-07 12:12:09 -05006582 /**
6583 * @hide
Dan Sandler7d67bd42018-05-15 14:06:38 -04006584 * Spans are ignored when comparing text for visual difference.
Julia Reynolds7217dc92018-03-07 12:12:09 -05006585 */
6586 @Override
6587 public boolean areNotificationsVisiblyDifferent(Style other) {
6588 if (other == null || getClass() != other.getClass()) {
6589 return true;
6590 }
6591 BigTextStyle newS = (BigTextStyle) other;
Dan Sandler7d67bd42018-05-15 14:06:38 -04006592 return !Objects.equals(String.valueOf(getBigText()), String.valueOf(newS.getBigText()));
Julia Reynolds7217dc92018-03-07 12:12:09 -05006593 }
6594
Adrian Roosb1f427c2016-05-26 12:27:15 -07006595 static void applyBigTextContentView(Builder builder,
6596 RemoteViews contentView, CharSequence bigTextText) {
Selim Cinek48f66b72017-08-18 16:17:51 -07006597 contentView.setTextViewText(R.id.big_text, builder.processTextSpans(bigTextText));
Selim Cinek7b9605b2017-01-19 17:36:00 -08006598 builder.setTextViewColorSecondary(contentView, R.id.big_text);
Adrian Roosb1f427c2016-05-26 12:27:15 -07006599 contentView.setViewVisibility(R.id.big_text,
6600 TextUtils.isEmpty(bigTextText) ? View.GONE : View.VISIBLE);
Selim Cinek279fa862016-06-14 10:57:25 -07006601 contentView.setBoolean(R.id.big_text, "setHasImage", builder.mN.hasLargeIcon());
Adrian Roosb1f427c2016-05-26 12:27:15 -07006602 }
Daniel Sandlerf3b73432012-03-27 15:01:25 -04006603 }
Daniel Sandler879c5e02012-04-17 16:46:51 -04006604
6605 /**
Alex Hillsfc737de2016-03-23 17:33:02 -04006606 * Helper class for generating large-format notifications that include multiple back-and-forth
6607 * messages of varying types between any number of people.
6608 *
Selim Cinekce8794f2018-05-23 16:46:05 -07006609 * <p>
Alex Hillsfc737de2016-03-23 17:33:02 -04006610 * If the platform does not provide large-format notifications, this method has no effect. The
6611 * user will always see the normal notification view.
Selim Cinekce8794f2018-05-23 16:46:05 -07006612 *
6613 * <p>
6614 * If the app is targeting Android P and above, it is required to use the {@link Person}
6615 * class in order to get an optimal rendering of the notification and its avatars. For
6616 * conversations involving multiple people, the app should also make sure that it marks the
6617 * conversation as a group with {@link #setGroupConversation(boolean)}.
6618 *
6619 * <p>
6620 * This class is a "rebuilder": It attaches to a Builder object and modifies its behavior.
6621 * Here's an example of how this may be used:
Alex Hillsfc737de2016-03-23 17:33:02 -04006622 * <pre class="prettyprint">
6623 *
Selim Cinekce8794f2018-05-23 16:46:05 -07006624 * Person user = new Person.Builder().setIcon(userIcon).setName(userName).build();
6625 * MessagingStyle style = new MessagingStyle(user)
6626 * .addMessage(messages[1].getText(), messages[1].getTime(), messages[1].getPerson())
6627 * .addMessage(messages[2].getText(), messages[2].getTime(), messages[2].getPerson())
6628 * .setGroupConversation(hasMultiplePeople());
6629 *
Alex Hillsfc737de2016-03-23 17:33:02 -04006630 * Notification noti = new Notification.Builder()
Selim Cinekce8794f2018-05-23 16:46:05 -07006631 * .setContentTitle(&quot;2 new messages with &quot; + sender.toString())
Alex Hillsfc737de2016-03-23 17:33:02 -04006632 * .setContentText(subject)
6633 * .setSmallIcon(R.drawable.new_message)
6634 * .setLargeIcon(aBitmap)
Selim Cinekce8794f2018-05-23 16:46:05 -07006635 * .setStyle(style)
Alex Hillsfc737de2016-03-23 17:33:02 -04006636 * .build();
6637 * </pre>
6638 */
6639 public static class MessagingStyle extends Style {
6640
6641 /**
6642 * The maximum number of messages that will be retained in the Notification itself (the
6643 * number displayed is up to the platform).
6644 */
6645 public static final int MAXIMUM_RETAINED_MESSAGES = 25;
6646
Selim Cinekcb8b9852017-12-15 18:01:52 -08006647 @NonNull Person mUser;
Kodlee Yin9ac617c2017-12-19 11:20:50 -08006648 @Nullable CharSequence mConversationTitle;
Alex Hillsd9b04d92016-04-11 16:38:16 -04006649 List<Message> mMessages = new ArrayList<>();
Adrian Roos437cd562017-01-18 15:47:03 -08006650 List<Message> mHistoricMessages = new ArrayList<>();
Kodlee Yin9ac617c2017-12-19 11:20:50 -08006651 boolean mIsGroupConversation;
Alex Hillsfc737de2016-03-23 17:33:02 -04006652
6653 MessagingStyle() {
6654 }
6655
6656 /**
Alex Hillsfd590442016-10-07 09:52:44 -04006657 * @param userDisplayName Required - the name to be displayed for any replies sent by the
6658 * user before the posting app reposts the notification with those messages after they've
6659 * been actually sent and in previous messages sent by the user added in
Alex Hillsfc737de2016-03-23 17:33:02 -04006660 * {@link #addMessage(Notification.MessagingStyle.Message)}
Selim Cinekcb8b9852017-12-15 18:01:52 -08006661 *
6662 * @deprecated use {@code MessagingStyle(Person)}
Alex Hillsfc737de2016-03-23 17:33:02 -04006663 */
Alex Hillsfd590442016-10-07 09:52:44 -04006664 public MessagingStyle(@NonNull CharSequence userDisplayName) {
Selim Cinek9acd6732018-03-23 16:39:02 -07006665 this(new Person.Builder().setName(userDisplayName).build());
Selim Cinekcb8b9852017-12-15 18:01:52 -08006666 }
6667
6668 /**
6669 * @param user Required - The person displayed for any messages that are sent by the
6670 * user. Any messages added with {@link #addMessage(Notification.MessagingStyle.Message)}
6671 * who don't have a Person associated with it will be displayed as if they were sent
Selim Cinek90343862018-02-01 11:07:11 -08006672 * by this user. The user also needs to have a valid name associated with it, which will
6673 * be enforced starting in Android P.
Selim Cinekcb8b9852017-12-15 18:01:52 -08006674 */
6675 public MessagingStyle(@NonNull Person user) {
6676 mUser = user;
Selim Cinek90343862018-02-01 11:07:11 -08006677 }
6678
6679 /**
6680 * Validate that this style was properly composed. This is called at build time.
6681 * @hide
6682 */
6683 @Override
6684 public void validate(Context context) {
6685 super.validate(context);
6686 if (context.getApplicationInfo().targetSdkVersion
6687 >= Build.VERSION_CODES.P && (mUser == null || mUser.getName() == null)) {
6688 throw new RuntimeException("User must be valid and have a name.");
Selim Cinekcb8b9852017-12-15 18:01:52 -08006689 }
6690 }
6691
6692 /**
Selim Cinekaa9db1f2018-02-27 17:35:47 -08006693 * @return the the text that should be displayed in the statusBar when heads upped.
6694 * If {@code null} is returned, the default implementation will be used.
6695 *
6696 * @hide
6697 */
6698 @Override
6699 public CharSequence getHeadsUpStatusBarText() {
6700 CharSequence conversationTitle = !TextUtils.isEmpty(super.mBigContentTitle)
6701 ? super.mBigContentTitle
6702 : mConversationTitle;
6703 if (!TextUtils.isEmpty(conversationTitle) && !hasOnlyWhiteSpaceSenders()) {
6704 return conversationTitle;
6705 }
6706 return null;
6707 }
6708
6709 /**
Selim Cinekcb8b9852017-12-15 18:01:52 -08006710 * @return the user to be displayed for any replies sent by the user
6711 */
Selim Cinekeb53c222018-04-06 12:56:09 -07006712 @NonNull
Selim Cinekcb8b9852017-12-15 18:01:52 -08006713 public Person getUser() {
6714 return mUser;
Alex Hillsfc737de2016-03-23 17:33:02 -04006715 }
6716
6717 /**
6718 * Returns the name to be displayed for any replies sent by the user
Selim Cinekcb8b9852017-12-15 18:01:52 -08006719 *
6720 * @deprecated use {@link #getUser()} instead
Alex Hillsfc737de2016-03-23 17:33:02 -04006721 */
6722 public CharSequence getUserDisplayName() {
Selim Cinekcb8b9852017-12-15 18:01:52 -08006723 return mUser.getName();
Alex Hillsfc737de2016-03-23 17:33:02 -04006724 }
6725
6726 /**
Kodlee Yin9ac617c2017-12-19 11:20:50 -08006727 * Sets the title to be displayed on this conversation. May be set to {@code null}.
6728 *
Kodlee Yin14656422017-12-22 17:00:46 -08006729 * <p>This API's behavior was changed in SDK version {@link Build.VERSION_CODES#P}. If your
6730 * application's target version is less than {@link Build.VERSION_CODES#P}, setting a
6731 * conversation title to a non-null value will make {@link #isGroupConversation()} return
6732 * {@code true} and passing {@code null} will make it return {@code false}. In
6733 * {@link Build.VERSION_CODES#P} and beyond, use {@link #setGroupConversation(boolean)}
6734 * to set group conversation status.
6735 *
6736 * @param conversationTitle Title displayed for this conversation
6737 * @return this object for method chaining
Alex Hillsfc737de2016-03-23 17:33:02 -04006738 */
Kodlee Yin9ac617c2017-12-19 11:20:50 -08006739 public MessagingStyle setConversationTitle(@Nullable CharSequence conversationTitle) {
Alex Hillsfc737de2016-03-23 17:33:02 -04006740 mConversationTitle = conversationTitle;
6741 return this;
6742 }
6743
6744 /**
Kodlee Yin9ac617c2017-12-19 11:20:50 -08006745 * Return the title to be displayed on this conversation. May return {@code null}.
Alex Hillsfc737de2016-03-23 17:33:02 -04006746 */
Kodlee Yin9ac617c2017-12-19 11:20:50 -08006747 @Nullable
Alex Hillsfc737de2016-03-23 17:33:02 -04006748 public CharSequence getConversationTitle() {
6749 return mConversationTitle;
6750 }
6751
6752 /**
6753 * Adds a message for display by this notification. Convenience call for a simple
6754 * {@link Message} in {@link #addMessage(Notification.MessagingStyle.Message)}.
6755 * @param text A {@link CharSequence} to be displayed as the message content
6756 * @param timestamp Time at which the message arrived
6757 * @param sender A {@link CharSequence} to be used for displaying the name of the
6758 * sender. Should be <code>null</code> for messages by the current user, in which case
6759 * the platform will insert {@link #getUserDisplayName()}.
6760 * Should be unique amongst all individuals in the conversation, and should be
6761 * consistent during re-posts of the notification.
6762 *
Aurimas Liutikase701dc12018-06-01 16:04:37 -07006763 * @see Message#Notification.MessagingStyle.Message(CharSequence, long, CharSequence)
Alex Hillsfc737de2016-03-23 17:33:02 -04006764 *
6765 * @return this object for method chaining
Selim Cinekcb8b9852017-12-15 18:01:52 -08006766 *
6767 * @deprecated use {@link #addMessage(CharSequence, long, Person)}
Alex Hillsfc737de2016-03-23 17:33:02 -04006768 */
6769 public MessagingStyle addMessage(CharSequence text, long timestamp, CharSequence sender) {
Selim Cinekcb8b9852017-12-15 18:01:52 -08006770 return addMessage(text, timestamp,
Selim Cinek9acd6732018-03-23 16:39:02 -07006771 sender == null ? null : new Person.Builder().setName(sender).build());
Selim Cinekcb8b9852017-12-15 18:01:52 -08006772 }
6773
6774 /**
6775 * Adds a message for display by this notification. Convenience call for a simple
6776 * {@link Message} in {@link #addMessage(Notification.MessagingStyle.Message)}.
6777 * @param text A {@link CharSequence} to be displayed as the message content
6778 * @param timestamp Time at which the message arrived
6779 * @param sender The {@link Person} who sent the message.
6780 * Should be <code>null</code> for messages by the current user, in which case
6781 * the platform will insert the user set in {@code MessagingStyle(Person)}.
6782 *
Aurimas Liutikase701dc12018-06-01 16:04:37 -07006783 * @see Message#Notification.MessagingStyle.Message(CharSequence, long, CharSequence)
Selim Cinekcb8b9852017-12-15 18:01:52 -08006784 *
6785 * @return this object for method chaining
6786 */
Selim Cinekeb53c222018-04-06 12:56:09 -07006787 public MessagingStyle addMessage(@NonNull CharSequence text, long timestamp,
6788 @Nullable Person sender) {
Adrian Roos437cd562017-01-18 15:47:03 -08006789 return addMessage(new Message(text, timestamp, sender));
Alex Hillsfc737de2016-03-23 17:33:02 -04006790 }
6791
6792 /**
6793 * Adds a {@link Message} for display in this notification.
Adrian Roos437cd562017-01-18 15:47:03 -08006794 *
6795 * <p>The messages should be added in chronologic order, i.e. the oldest first,
6796 * the newest last.
6797 *
Alex Hillsfc737de2016-03-23 17:33:02 -04006798 * @param message The {@link Message} to be displayed
6799 * @return this object for method chaining
6800 */
6801 public MessagingStyle addMessage(Message message) {
6802 mMessages.add(message);
6803 if (mMessages.size() > MAXIMUM_RETAINED_MESSAGES) {
6804 mMessages.remove(0);
6805 }
6806 return this;
6807 }
6808
6809 /**
Adrian Roos437cd562017-01-18 15:47:03 -08006810 * Adds a {@link Message} for historic context in this notification.
6811 *
6812 * <p>Messages should be added as historic if they are not the main subject of the
6813 * notification but may give context to a conversation. The system may choose to present
6814 * them only when relevant, e.g. when replying to a message through a {@link RemoteInput}.
6815 *
6816 * <p>The messages should be added in chronologic order, i.e. the oldest first,
6817 * the newest last.
6818 *
6819 * @param message The historic {@link Message} to be added
6820 * @return this object for method chaining
6821 */
6822 public MessagingStyle addHistoricMessage(Message message) {
6823 mHistoricMessages.add(message);
6824 if (mHistoricMessages.size() > MAXIMUM_RETAINED_MESSAGES) {
6825 mHistoricMessages.remove(0);
6826 }
6827 return this;
6828 }
6829
6830 /**
Alex Hillsfc737de2016-03-23 17:33:02 -04006831 * Gets the list of {@code Message} objects that represent the notification
6832 */
6833 public List<Message> getMessages() {
6834 return mMessages;
6835 }
6836
6837 /**
Adrian Roos437cd562017-01-18 15:47:03 -08006838 * Gets the list of historic {@code Message}s in the notification.
6839 */
6840 public List<Message> getHistoricMessages() {
6841 return mHistoricMessages;
6842 }
6843
6844 /**
Selim Cinekce8794f2018-05-23 16:46:05 -07006845 * Sets whether this conversation notification represents a group. If the app is targeting
6846 * Android P, this is required if the app wants to display the largeIcon set with
6847 * {@link Notification.Builder#setLargeIcon(Bitmap)}, otherwise it will be hidden.
Kodlee Yin14656422017-12-22 17:00:46 -08006848 *
Kodlee Yin9ac617c2017-12-19 11:20:50 -08006849 * @param isGroupConversation {@code true} if the conversation represents a group,
6850 * {@code false} otherwise.
6851 * @return this object for method chaining
6852 */
6853 public MessagingStyle setGroupConversation(boolean isGroupConversation) {
6854 mIsGroupConversation = isGroupConversation;
6855 return this;
6856 }
6857
6858 /**
Kodlee Yin14656422017-12-22 17:00:46 -08006859 * Returns {@code true} if this notification represents a group conversation, otherwise
6860 * {@code false}.
6861 *
6862 * <p> If the application that generated this {@link MessagingStyle} targets an SDK version
6863 * less than {@link Build.VERSION_CODES#P}, this method becomes dependent on whether or
6864 * not the conversation title is set; returning {@code true} if the conversation title is
6865 * a non-null value, or {@code false} otherwise. From {@link Build.VERSION_CODES#P} forward,
6866 * this method returns what's set by {@link #setGroupConversation(boolean)} allowing for
6867 * named, non-group conversations.
6868 *
6869 * @see #setConversationTitle(CharSequence)
Kodlee Yin9ac617c2017-12-19 11:20:50 -08006870 */
6871 public boolean isGroupConversation() {
Kodlee Yin14656422017-12-22 17:00:46 -08006872 // When target SDK version is < P, a non-null conversation title dictates if this is
6873 // as group conversation.
6874 if (mBuilder != null
6875 && mBuilder.mContext.getApplicationInfo().targetSdkVersion
6876 < Build.VERSION_CODES.P) {
6877 return mConversationTitle != null;
6878 }
6879
Kodlee Yin9ac617c2017-12-19 11:20:50 -08006880 return mIsGroupConversation;
6881 }
6882
6883 /**
Alex Hillsfc737de2016-03-23 17:33:02 -04006884 * @hide
6885 */
6886 @Override
6887 public void addExtras(Bundle extras) {
6888 super.addExtras(extras);
Selim Cinekcb8b9852017-12-15 18:01:52 -08006889 if (mUser != null) {
6890 // For legacy usages
6891 extras.putCharSequence(EXTRA_SELF_DISPLAY_NAME, mUser.getName());
6892 extras.putParcelable(EXTRA_MESSAGING_PERSON, mUser);
Alex Hillsfc737de2016-03-23 17:33:02 -04006893 }
6894 if (mConversationTitle != null) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04006895 extras.putCharSequence(EXTRA_CONVERSATION_TITLE, mConversationTitle);
Alex Hillsfc737de2016-03-23 17:33:02 -04006896 }
Alex Hillsd9b04d92016-04-11 16:38:16 -04006897 if (!mMessages.isEmpty()) { extras.putParcelableArray(EXTRA_MESSAGES,
6898 Message.getBundleArrayForMessages(mMessages));
Alex Hillsfc737de2016-03-23 17:33:02 -04006899 }
Adrian Roos437cd562017-01-18 15:47:03 -08006900 if (!mHistoricMessages.isEmpty()) { extras.putParcelableArray(EXTRA_HISTORIC_MESSAGES,
6901 Message.getBundleArrayForMessages(mHistoricMessages));
6902 }
Adrian Roos33fbd2c2016-05-27 15:35:28 -07006903
6904 fixTitleAndTextExtras(extras);
Kodlee Yin9ac617c2017-12-19 11:20:50 -08006905 extras.putBoolean(EXTRA_IS_GROUP_CONVERSATION, mIsGroupConversation);
Adrian Roos33fbd2c2016-05-27 15:35:28 -07006906 }
6907
6908 private void fixTitleAndTextExtras(Bundle extras) {
6909 Message m = findLatestIncomingMessage();
6910 CharSequence text = (m == null) ? null : m.mText;
6911 CharSequence sender = m == null ? null
Selim Cinekcb8b9852017-12-15 18:01:52 -08006912 : m.mSender == null || TextUtils.isEmpty(m.mSender.getName())
6913 ? mUser.getName() : m.mSender.getName();
Adrian Roos33fbd2c2016-05-27 15:35:28 -07006914 CharSequence title;
6915 if (!TextUtils.isEmpty(mConversationTitle)) {
6916 if (!TextUtils.isEmpty(sender)) {
6917 BidiFormatter bidi = BidiFormatter.getInstance();
6918 title = mBuilder.mContext.getString(
6919 com.android.internal.R.string.notification_messaging_title_template,
Selim Cinekcb8b9852017-12-15 18:01:52 -08006920 bidi.unicodeWrap(mConversationTitle), bidi.unicodeWrap(sender));
Adrian Roos33fbd2c2016-05-27 15:35:28 -07006921 } else {
6922 title = mConversationTitle;
6923 }
6924 } else {
6925 title = sender;
6926 }
6927
6928 if (title != null) {
6929 extras.putCharSequence(EXTRA_TITLE, title);
6930 }
6931 if (text != null) {
6932 extras.putCharSequence(EXTRA_TEXT, text);
6933 }
Alex Hillsfc737de2016-03-23 17:33:02 -04006934 }
6935
6936 /**
6937 * @hide
6938 */
6939 @Override
6940 protected void restoreFromExtras(Bundle extras) {
6941 super.restoreFromExtras(extras);
6942
Selim Cinekcb8b9852017-12-15 18:01:52 -08006943 mUser = extras.getParcelable(EXTRA_MESSAGING_PERSON);
6944 if (mUser == null) {
6945 CharSequence displayName = extras.getCharSequence(EXTRA_SELF_DISPLAY_NAME);
Selim Cinek9acd6732018-03-23 16:39:02 -07006946 mUser = new Person.Builder().setName(displayName).build();
Selim Cinekcb8b9852017-12-15 18:01:52 -08006947 }
Adrian Roos96b7e202016-05-17 13:50:38 -07006948 mConversationTitle = extras.getCharSequence(EXTRA_CONVERSATION_TITLE);
Adrian Roos437cd562017-01-18 15:47:03 -08006949 Parcelable[] messages = extras.getParcelableArray(EXTRA_MESSAGES);
Selim Cinek88188f22017-09-19 16:46:56 -07006950 mMessages = Message.getMessagesFromBundleArray(messages);
Adrian Roos437cd562017-01-18 15:47:03 -08006951 Parcelable[] histMessages = extras.getParcelableArray(EXTRA_HISTORIC_MESSAGES);
Selim Cinek88188f22017-09-19 16:46:56 -07006952 mHistoricMessages = Message.getMessagesFromBundleArray(histMessages);
Kodlee Yin9ac617c2017-12-19 11:20:50 -08006953 mIsGroupConversation = extras.getBoolean(EXTRA_IS_GROUP_CONVERSATION);
Alex Hillsfc737de2016-03-23 17:33:02 -04006954 }
6955
6956 /**
6957 * @hide
6958 */
Adrian Roosc1a80b02016-04-05 14:54:55 -07006959 @Override
Selim Cinek7d1009b2017-01-25 15:28:28 -08006960 public RemoteViews makeContentView(boolean increasedHeight) {
Selim Cineke62255c2017-09-28 18:23:23 -07006961 mBuilder.mOriginalActions = mBuilder.mActions;
6962 mBuilder.mActions = new ArrayList<>();
Selim Cinek85d0e6e2018-03-23 18:08:32 -07006963 RemoteViews remoteViews = makeMessagingView(true /* displayImagesAtEnd */,
Selim Cinek384804b2018-04-18 14:31:07 +08006964 false /* hideLargeIcon */);
Selim Cineke62255c2017-09-28 18:23:23 -07006965 mBuilder.mActions = mBuilder.mOriginalActions;
6966 mBuilder.mOriginalActions = null;
6967 return remoteViews;
Adrian Roosc1a80b02016-04-05 14:54:55 -07006968 }
6969
Julia Reynolds7217dc92018-03-07 12:12:09 -05006970 /**
6971 * @hide
Dan Sandler7d67bd42018-05-15 14:06:38 -04006972 * Spans are ignored when comparing text for visual difference.
Julia Reynolds7217dc92018-03-07 12:12:09 -05006973 */
6974 @Override
6975 public boolean areNotificationsVisiblyDifferent(Style other) {
6976 if (other == null || getClass() != other.getClass()) {
6977 return true;
6978 }
6979 MessagingStyle newS = (MessagingStyle) other;
6980 List<MessagingStyle.Message> oldMs = getMessages();
6981 List<MessagingStyle.Message> newMs = newS.getMessages();
6982
Dan Sandler7d67bd42018-05-15 14:06:38 -04006983 if (oldMs == null || newMs == null) {
Julia Reynolds7217dc92018-03-07 12:12:09 -05006984 newMs = new ArrayList<>();
6985 }
6986
6987 int n = oldMs.size();
6988 if (n != newMs.size()) {
6989 return true;
6990 }
6991 for (int i = 0; i < n; i++) {
6992 MessagingStyle.Message oldM = oldMs.get(i);
6993 MessagingStyle.Message newM = newMs.get(i);
Dan Sandler7d67bd42018-05-15 14:06:38 -04006994 if (!Objects.equals(
6995 String.valueOf(oldM.getText()),
6996 String.valueOf(newM.getText()))) {
Julia Reynolds7217dc92018-03-07 12:12:09 -05006997 return true;
6998 }
6999 if (!Objects.equals(oldM.getDataUri(), newM.getDataUri())) {
7000 return true;
7001 }
Dan Sandler7d67bd42018-05-15 14:06:38 -04007002 String oldSender = String.valueOf(oldM.getSenderPerson() == null
7003 ? oldM.getSender()
7004 : oldM.getSenderPerson().getName());
7005 String newSender = String.valueOf(newM.getSenderPerson() == null
7006 ? newM.getSender()
7007 : newM.getSenderPerson().getName());
Julia Reynolds7217dc92018-03-07 12:12:09 -05007008 if (!Objects.equals(oldSender, newSender)) {
7009 return true;
7010 }
7011
7012 String oldKey = oldM.getSenderPerson() == null
7013 ? null : oldM.getSenderPerson().getKey();
7014 String newKey = newM.getSenderPerson() == null
7015 ? null : newM.getSenderPerson().getKey();
7016 if (!Objects.equals(oldKey, newKey)) {
7017 return true;
7018 }
7019 // Other fields (like timestamp) intentionally excluded
7020 }
7021 return false;
7022 }
7023
Adrian Roosc1a80b02016-04-05 14:54:55 -07007024 private Message findLatestIncomingMessage() {
Selim Cinek88188f22017-09-19 16:46:56 -07007025 return findLatestIncomingMessage(mMessages);
7026 }
7027
7028 /**
7029 * @hide
7030 */
7031 @Nullable
7032 public static Message findLatestIncomingMessage(
7033 List<Message> messages) {
7034 for (int i = messages.size() - 1; i >= 0; i--) {
7035 Message m = messages.get(i);
Adrian Roosc1a80b02016-04-05 14:54:55 -07007036 // Incoming messages have a non-empty sender.
Selim Cinekcb8b9852017-12-15 18:01:52 -08007037 if (m.mSender != null && !TextUtils.isEmpty(m.mSender.getName())) {
Adrian Roosc1a80b02016-04-05 14:54:55 -07007038 return m;
7039 }
7040 }
Selim Cinek88188f22017-09-19 16:46:56 -07007041 if (!messages.isEmpty()) {
Adrian Roos33fbd2c2016-05-27 15:35:28 -07007042 // No incoming messages, fall back to outgoing message
Selim Cinek88188f22017-09-19 16:46:56 -07007043 return messages.get(messages.size() - 1);
Adrian Roos33fbd2c2016-05-27 15:35:28 -07007044 }
Adrian Roosc1a80b02016-04-05 14:54:55 -07007045 return null;
7046 }
7047
7048 /**
7049 * @hide
7050 */
7051 @Override
7052 public RemoteViews makeBigContentView() {
Selim Cinek384804b2018-04-18 14:31:07 +08007053 return makeMessagingView(false /* displayImagesAtEnd */, true /* hideLargeIcon */);
Selim Cinekafeed292017-12-12 17:32:44 -08007054 }
7055
Selim Cinek85d0e6e2018-03-23 18:08:32 -07007056 /**
7057 * Create a messaging layout.
7058 *
7059 * @param displayImagesAtEnd should images be displayed at the end of the content instead
7060 * of inline.
Selim Cinek384804b2018-04-18 14:31:07 +08007061 * @param hideRightIcons Should the reply affordance be shown at the end of the notification
Selim Cinek85d0e6e2018-03-23 18:08:32 -07007062 * @return the created remoteView.
7063 */
Selim Cinekafeed292017-12-12 17:32:44 -08007064 @NonNull
Selim Cinek384804b2018-04-18 14:31:07 +08007065 private RemoteViews makeMessagingView(boolean displayImagesAtEnd, boolean hideRightIcons) {
Selim Cinek88188f22017-09-19 16:46:56 -07007066 CharSequence conversationTitle = !TextUtils.isEmpty(super.mBigContentTitle)
Adrian Roosc1a80b02016-04-05 14:54:55 -07007067 ? super.mBigContentTitle
7068 : mConversationTitle;
Selim Cinekce8794f2018-05-23 16:46:05 -07007069 boolean atLeastP = mBuilder.mContext.getApplicationInfo().targetSdkVersion
7070 >= Build.VERSION_CODES.P;
7071 boolean isOneToOne;
Selim Cinek2dd3e722018-01-19 11:06:06 -08007072 CharSequence nameReplacement = null;
Selim Cinekce8794f2018-05-23 16:46:05 -07007073 Icon avatarReplacement = null;
7074 if (!atLeastP) {
7075 isOneToOne = TextUtils.isEmpty(conversationTitle);
7076 avatarReplacement = mBuilder.mN.mLargeIcon;
7077 if (hasOnlyWhiteSpaceSenders()) {
7078 isOneToOne = true;
7079 nameReplacement = conversationTitle;
7080 conversationTitle = null;
7081 }
7082 } else {
7083 isOneToOne = !isGroupConversation();
Adrian Roosb1f427c2016-05-26 12:27:15 -07007084 }
Selim Cinek384804b2018-04-18 14:31:07 +08007085 TemplateBindResult bindResult = new TemplateBindResult();
Adrian Roos48d746a2016-04-12 14:57:28 -07007086 RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(
Adrian Roosc1a80b02016-04-05 14:54:55 -07007087 mBuilder.getMessagingLayoutResource(),
Selim Cinek88188f22017-09-19 16:46:56 -07007088 mBuilder.mParams.reset().hasProgress(false).title(conversationTitle).text(null)
Selim Cinek384804b2018-04-18 14:31:07 +08007089 .hideLargeIcon(hideRightIcons || isOneToOne)
7090 .hideReplyIcon(hideRightIcons)
7091 .headerTextSecondary(conversationTitle),
7092 bindResult);
Selim Cinek88188f22017-09-19 16:46:56 -07007093 addExtras(mBuilder.mN.extras);
Selim Cinekafeed292017-12-12 17:32:44 -08007094 // also update the end margin if there is an image
Selim Cinek1c72fa02018-04-23 18:00:54 +08007095 contentView.setViewLayoutMarginEnd(R.id.notification_messaging,
7096 bindResult.getIconMarginEnd());
Selim Cinek88188f22017-09-19 16:46:56 -07007097 contentView.setInt(R.id.status_bar_latest_event_content, "setLayoutColor",
Kenny Guy14d035c2018-05-02 19:10:36 +01007098 mBuilder.isColorized() ? mBuilder.getPrimaryTextColor()
7099 : mBuilder.resolveContrastColor());
7100 contentView.setInt(R.id.status_bar_latest_event_content, "setSenderTextColor",
7101 mBuilder.getPrimaryTextColor());
7102 contentView.setInt(R.id.status_bar_latest_event_content, "setMessageTextColor",
7103 mBuilder.getSecondaryTextColor());
Selim Cinek85d0e6e2018-03-23 18:08:32 -07007104 contentView.setBoolean(R.id.status_bar_latest_event_content, "setDisplayImagesAtEnd",
7105 displayImagesAtEnd);
Selim Cinekce8794f2018-05-23 16:46:05 -07007106 contentView.setIcon(R.id.status_bar_latest_event_content, "setAvatarReplacement",
7107 avatarReplacement);
Selim Cinek2dd3e722018-01-19 11:06:06 -08007108 contentView.setCharSequence(R.id.status_bar_latest_event_content, "setNameReplacement",
7109 nameReplacement);
Selim Cinek88188f22017-09-19 16:46:56 -07007110 contentView.setBoolean(R.id.status_bar_latest_event_content, "setIsOneToOne",
7111 isOneToOne);
7112 contentView.setBundle(R.id.status_bar_latest_event_content, "setData",
7113 mBuilder.mN.extras);
Alex Hillsfc737de2016-03-23 17:33:02 -04007114 return contentView;
7115 }
7116
Selim Cinekf7409db2017-10-24 16:17:14 -07007117 private boolean hasOnlyWhiteSpaceSenders() {
7118 for (int i = 0; i < mMessages.size(); i++) {
7119 Message m = mMessages.get(i);
Selim Cinekcb8b9852017-12-15 18:01:52 -08007120 Person sender = m.getSenderPerson();
7121 if (sender != null && !isWhiteSpace(sender.getName())) {
Selim Cinekf7409db2017-10-24 16:17:14 -07007122 return false;
7123 }
7124 }
7125 return true;
7126 }
7127
7128 private boolean isWhiteSpace(CharSequence sender) {
7129 if (TextUtils.isEmpty(sender)) {
7130 return true;
7131 }
7132 if (sender.toString().matches("^\\s*$")) {
7133 return true;
7134 }
7135 // Let's check if we only have 0 whitespace chars. Some apps did this as a workaround
7136 // For the presentation that we had.
7137 for (int i = 0; i < sender.length(); i++) {
7138 char c = sender.charAt(i);
7139 if (c != '\u200B') {
7140 return false;
7141 }
7142 }
7143 return true;
7144 }
7145
Selim Cinek88188f22017-09-19 16:46:56 -07007146 private CharSequence createConversationTitleFromMessages() {
7147 ArraySet<CharSequence> names = new ArraySet<>();
7148 for (int i = 0; i < mMessages.size(); i++) {
7149 Message m = mMessages.get(i);
Selim Cinekcb8b9852017-12-15 18:01:52 -08007150 Person sender = m.getSenderPerson();
Selim Cinek88188f22017-09-19 16:46:56 -07007151 if (sender != null) {
Selim Cinekcb8b9852017-12-15 18:01:52 -08007152 names.add(sender.getName());
Selim Cinek88188f22017-09-19 16:46:56 -07007153 }
7154 }
7155 SpannableStringBuilder title = new SpannableStringBuilder();
7156 int size = names.size();
7157 for (int i = 0; i < size; i++) {
7158 CharSequence name = names.valueAt(i);
7159 if (!TextUtils.isEmpty(title)) {
7160 title.append(", ");
7161 }
7162 title.append(BidiFormatter.getInstance().unicodeWrap(name));
7163 }
7164 return title;
7165 }
7166
Adrian Roosdedd1df2016-04-26 16:38:47 -07007167 /**
7168 * @hide
7169 */
7170 @Override
Selim Cinek87ed69b2017-02-09 15:59:43 -08007171 public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
Selim Cinek85d0e6e2018-03-23 18:08:32 -07007172 RemoteViews remoteViews = makeMessagingView(true /* displayImagesAtEnd */,
Selim Cinek384804b2018-04-18 14:31:07 +08007173 true /* hideLargeIcon */);
Selim Cineke62255c2017-09-28 18:23:23 -07007174 remoteViews.setInt(R.id.notification_messaging, "setMaxDisplayedLines", 1);
7175 return remoteViews;
Adrian Roosdedd1df2016-04-26 16:38:47 -07007176 }
7177
Adrian Roosc1a80b02016-04-05 14:54:55 -07007178 private static TextAppearanceSpan makeFontColorSpan(int color) {
7179 return new TextAppearanceSpan(null, 0, 0,
7180 ColorStateList.valueOf(color), null);
7181 }
7182
Alex Hillsd9b04d92016-04-11 16:38:16 -04007183 public static final class Message {
Tony Mak09db2ea2018-06-27 18:12:48 +01007184 /** @hide */
7185 public static final String KEY_TEXT = "text";
Alex Hillsd9b04d92016-04-11 16:38:16 -04007186 static final String KEY_TIMESTAMP = "time";
7187 static final String KEY_SENDER = "sender";
Selim Cinekcb8b9852017-12-15 18:01:52 -08007188 static final String KEY_SENDER_PERSON = "sender_person";
Alex Hillsd9b04d92016-04-11 16:38:16 -04007189 static final String KEY_DATA_MIME_TYPE = "type";
7190 static final String KEY_DATA_URI= "uri";
Shane Brennan5a871862017-03-11 13:14:17 -08007191 static final String KEY_EXTRAS_BUNDLE = "extras";
Kenny Guya0f6de82018-04-06 16:20:16 +01007192 static final String KEY_REMOTE_INPUT_HISTORY = "remote_input_history";
Alex Hillsfc737de2016-03-23 17:33:02 -04007193
7194 private final CharSequence mText;
7195 private final long mTimestamp;
Selim Cinekcb8b9852017-12-15 18:01:52 -08007196 @Nullable
7197 private final Person mSender;
Kenny Guya0f6de82018-04-06 16:20:16 +01007198 /** True if this message was generated from the extra
7199 * {@link Notification#EXTRA_REMOTE_INPUT_HISTORY}
7200 */
7201 private final boolean mRemoteInputHistory;
Alex Hillsfc737de2016-03-23 17:33:02 -04007202
Shane Brennan5a871862017-03-11 13:14:17 -08007203 private Bundle mExtras = new Bundle();
Alex Hillsfc737de2016-03-23 17:33:02 -04007204 private String mDataMimeType;
7205 private Uri mDataUri;
7206
7207 /**
7208 * Constructor
7209 * @param text A {@link CharSequence} to be displayed as the message content
7210 * @param timestamp Time at which the message arrived
7211 * @param sender A {@link CharSequence} to be used for displaying the name of the
7212 * sender. Should be <code>null</code> for messages by the current user, in which case
7213 * the platform will insert {@link MessagingStyle#getUserDisplayName()}.
7214 * Should be unique amongst all individuals in the conversation, and should be
7215 * consistent during re-posts of the notification.
Selim Cinekcb8b9852017-12-15 18:01:52 -08007216 *
7217 * @deprecated use {@code Message(CharSequence, long, Person)}
Alex Hillsfc737de2016-03-23 17:33:02 -04007218 */
7219 public Message(CharSequence text, long timestamp, CharSequence sender){
Selim Cinek9acd6732018-03-23 16:39:02 -07007220 this(text, timestamp, sender == null ? null
7221 : new Person.Builder().setName(sender).build());
Selim Cinekcb8b9852017-12-15 18:01:52 -08007222 }
7223
7224 /**
7225 * Constructor
7226 * @param text A {@link CharSequence} to be displayed as the message content
7227 * @param timestamp Time at which the message arrived
7228 * @param sender The {@link Person} who sent the message.
7229 * Should be <code>null</code> for messages by the current user, in which case
7230 * the platform will insert the user set in {@code MessagingStyle(Person)}.
7231 * <p>
Selim Cinek9acd6732018-03-23 16:39:02 -07007232 * The person provided should contain an Icon, set with
7233 * {@link Person.Builder#setIcon(Icon)} and also have a name provided
7234 * with {@link Person.Builder#setName(CharSequence)}. If multiple users have the same
7235 * name, consider providing a key with {@link Person.Builder#setKey(String)} in order
7236 * to differentiate between the different users.
Selim Cinekcb8b9852017-12-15 18:01:52 -08007237 * </p>
7238 */
Selim Cinekeb53c222018-04-06 12:56:09 -07007239 public Message(@NonNull CharSequence text, long timestamp, @Nullable Person sender) {
Kenny Guya0f6de82018-04-06 16:20:16 +01007240 this(text, timestamp, sender, false /* remoteHistory */);
7241 }
7242
7243 /**
7244 * Constructor
7245 * @param text A {@link CharSequence} to be displayed as the message content
7246 * @param timestamp Time at which the message arrived
7247 * @param sender The {@link Person} who sent the message.
7248 * Should be <code>null</code> for messages by the current user, in which case
7249 * the platform will insert the user set in {@code MessagingStyle(Person)}.
7250 * @param remoteInputHistory True if the messages was generated from the extra
7251 * {@link Notification#EXTRA_REMOTE_INPUT_HISTORY}.
7252 * <p>
7253 * The person provided should contain an Icon, set with
7254 * {@link Person.Builder#setIcon(Icon)} and also have a name provided
7255 * with {@link Person.Builder#setName(CharSequence)}. If multiple users have the same
7256 * name, consider providing a key with {@link Person.Builder#setKey(String)} in order
7257 * to differentiate between the different users.
7258 * </p>
7259 * @hide
7260 */
7261 public Message(@NonNull CharSequence text, long timestamp, @Nullable Person sender,
7262 boolean remoteInputHistory) {
Alex Hillsfc737de2016-03-23 17:33:02 -04007263 mText = text;
7264 mTimestamp = timestamp;
7265 mSender = sender;
Kenny Guya0f6de82018-04-06 16:20:16 +01007266 mRemoteInputHistory = remoteInputHistory;
Alex Hillsfc737de2016-03-23 17:33:02 -04007267 }
7268
7269 /**
7270 * Sets a binary blob of data and an associated MIME type for a message. In the case
7271 * where the platform doesn't support the MIME type, the original text provided in the
7272 * constructor will be used.
7273 * @param dataMimeType The MIME type of the content. See
7274 * <a href="{@docRoot}notifications/messaging.html"> for the list of supported MIME
7275 * types on Android and Android Wear.
7276 * @param dataUri The uri containing the content whose type is given by the MIME type.
7277 * <p class="note">
7278 * <ol>
7279 * <li>Notification Listeners including the System UI need permission to access the
7280 * data the Uri points to. The recommended ways to do this are:</li>
7281 * <li>Store the data in your own ContentProvider, making sure that other apps have
7282 * the correct permission to access your provider. The preferred mechanism for
7283 * providing access is to use per-URI permissions which are temporary and only
7284 * grant access to the receiving application. An easy way to create a
7285 * ContentProvider like this is to use the FileProvider helper class.</li>
7286 * <li>Use the system MediaStore. The MediaStore is primarily aimed at video, audio
7287 * and image MIME types, however beginning with Android 3.0 (API level 11) it can
7288 * also store non-media types (see MediaStore.Files for more info). Files can be
7289 * inserted into the MediaStore using scanFile() after which a content:// style
7290 * Uri suitable for sharing is passed to the provided onScanCompleted() callback.
7291 * Note that once added to the system MediaStore the content is accessible to any
7292 * app on the device.</li>
7293 * </ol>
7294 * @return this object for method chaining
7295 */
7296 public Message setData(String dataMimeType, Uri dataUri) {
7297 mDataMimeType = dataMimeType;
7298 mDataUri = dataUri;
7299 return this;
7300 }
7301
Alex Hillsfc737de2016-03-23 17:33:02 -04007302 /**
7303 * Get the text to be used for this message, or the fallback text if a type and content
7304 * Uri have been set
7305 */
7306 public CharSequence getText() {
7307 return mText;
7308 }
7309
7310 /**
7311 * Get the time at which this message arrived
7312 */
7313 public long getTimestamp() {
7314 return mTimestamp;
7315 }
7316
7317 /**
Shane Brennan5a871862017-03-11 13:14:17 -08007318 * Get the extras Bundle for this message.
7319 */
7320 public Bundle getExtras() {
7321 return mExtras;
7322 }
7323
7324 /**
Alex Hillsfc737de2016-03-23 17:33:02 -04007325 * Get the text used to display the contact's name in the messaging experience
Selim Cinekcb8b9852017-12-15 18:01:52 -08007326 *
7327 * @deprecated use {@link #getSenderPerson()}
Alex Hillsfc737de2016-03-23 17:33:02 -04007328 */
7329 public CharSequence getSender() {
Selim Cinekcb8b9852017-12-15 18:01:52 -08007330 return mSender == null ? null : mSender.getName();
7331 }
7332
7333 /**
7334 * Get the sender associated with this message.
7335 */
7336 @Nullable
7337 public Person getSenderPerson() {
Alex Hillsfc737de2016-03-23 17:33:02 -04007338 return mSender;
7339 }
7340
7341 /**
7342 * Get the MIME type of the data pointed to by the Uri
7343 */
7344 public String getDataMimeType() {
7345 return mDataMimeType;
7346 }
7347
7348 /**
7349 * Get the the Uri pointing to the content of the message. Can be null, in which case
7350 * {@see #getText()} is used.
7351 */
7352 public Uri getDataUri() {
7353 return mDataUri;
7354 }
7355
Kenny Guya0f6de82018-04-06 16:20:16 +01007356 /**
7357 * @return True if the message was generated from
7358 * {@link Notification#EXTRA_REMOTE_INPUT_HISTORY}.
7359 * @hide
7360 */
7361 public boolean isRemoteInputHistory() {
7362 return mRemoteInputHistory;
7363 }
7364
Alex Hillsd9b04d92016-04-11 16:38:16 -04007365 private Bundle toBundle() {
7366 Bundle bundle = new Bundle();
Alex Hillsfc737de2016-03-23 17:33:02 -04007367 if (mText != null) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04007368 bundle.putCharSequence(KEY_TEXT, mText);
Alex Hillsfc737de2016-03-23 17:33:02 -04007369 }
Alex Hillsd9b04d92016-04-11 16:38:16 -04007370 bundle.putLong(KEY_TIMESTAMP, mTimestamp);
Alex Hillsfc737de2016-03-23 17:33:02 -04007371 if (mSender != null) {
Selim Cinekcb8b9852017-12-15 18:01:52 -08007372 // Legacy listeners need this
7373 bundle.putCharSequence(KEY_SENDER, mSender.getName());
7374 bundle.putParcelable(KEY_SENDER_PERSON, mSender);
Alex Hillsfc737de2016-03-23 17:33:02 -04007375 }
7376 if (mDataMimeType != null) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04007377 bundle.putString(KEY_DATA_MIME_TYPE, mDataMimeType);
Alex Hillsfc737de2016-03-23 17:33:02 -04007378 }
7379 if (mDataUri != null) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04007380 bundle.putParcelable(KEY_DATA_URI, mDataUri);
Alex Hillsfc737de2016-03-23 17:33:02 -04007381 }
Shane Brennan5a871862017-03-11 13:14:17 -08007382 if (mExtras != null) {
7383 bundle.putBundle(KEY_EXTRAS_BUNDLE, mExtras);
7384 }
Kenny Guya0f6de82018-04-06 16:20:16 +01007385 if (mRemoteInputHistory) {
7386 bundle.putBoolean(KEY_REMOTE_INPUT_HISTORY, mRemoteInputHistory);
7387 }
Alex Hillsd9b04d92016-04-11 16:38:16 -04007388 return bundle;
Alex Hillsfc737de2016-03-23 17:33:02 -04007389 }
7390
Alex Hillsd9b04d92016-04-11 16:38:16 -04007391 static Bundle[] getBundleArrayForMessages(List<Message> messages) {
7392 Bundle[] bundles = new Bundle[messages.size()];
7393 final int N = messages.size();
7394 for (int i = 0; i < N; i++) {
7395 bundles[i] = messages.get(i).toBundle();
7396 }
7397 return bundles;
7398 }
7399
Selim Cinek88188f22017-09-19 16:46:56 -07007400 /**
7401 * @return A list of messages read from the bundles.
7402 *
7403 * @hide
7404 */
7405 public static List<Message> getMessagesFromBundleArray(Parcelable[] bundles) {
7406 if (bundles == null) {
7407 return new ArrayList<>();
7408 }
Alex Hillsd9b04d92016-04-11 16:38:16 -04007409 List<Message> messages = new ArrayList<>(bundles.length);
7410 for (int i = 0; i < bundles.length; i++) {
Adrian Roosdedd1df2016-04-26 16:38:47 -07007411 if (bundles[i] instanceof Bundle) {
7412 Message message = getMessageFromBundle((Bundle)bundles[i]);
7413 if (message != null) {
7414 messages.add(message);
7415 }
Alex Hillsd9b04d92016-04-11 16:38:16 -04007416 }
7417 }
7418 return messages;
7419 }
7420
Selim Cinekb0dc61b2018-05-22 18:49:36 -07007421 /**
7422 * @return The message that is stored in the bundle or null if the message couldn't be
7423 * resolved.
7424 *
7425 * @hide
7426 */
7427 @Nullable
7428 public static Message getMessageFromBundle(Bundle bundle) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04007429 try {
Adrian Roosfbddd2c2016-05-13 12:57:20 -07007430 if (!bundle.containsKey(KEY_TEXT) || !bundle.containsKey(KEY_TIMESTAMP)) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04007431 return null;
7432 } else {
Selim Cinekcb8b9852017-12-15 18:01:52 -08007433
7434 Person senderPerson = bundle.getParcelable(KEY_SENDER_PERSON);
7435 if (senderPerson == null) {
7436 // Legacy apps that use compat don't actually provide the sender objects
7437 // We need to fix the compat version to provide people / use
7438 // the native api instead
7439 CharSequence senderName = bundle.getCharSequence(KEY_SENDER);
7440 if (senderName != null) {
Selim Cinek9acd6732018-03-23 16:39:02 -07007441 senderPerson = new Person.Builder().setName(senderName).build();
Selim Cinekcb8b9852017-12-15 18:01:52 -08007442 }
7443 }
Alex Hillsd9b04d92016-04-11 16:38:16 -04007444 Message message = new Message(bundle.getCharSequence(KEY_TEXT),
Selim Cinekcb8b9852017-12-15 18:01:52 -08007445 bundle.getLong(KEY_TIMESTAMP),
Kenny Guya0f6de82018-04-06 16:20:16 +01007446 senderPerson,
7447 bundle.getBoolean(KEY_REMOTE_INPUT_HISTORY, false));
Alex Hillsd9b04d92016-04-11 16:38:16 -04007448 if (bundle.containsKey(KEY_DATA_MIME_TYPE) &&
7449 bundle.containsKey(KEY_DATA_URI)) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04007450 message.setData(bundle.getString(KEY_DATA_MIME_TYPE),
7451 (Uri) bundle.getParcelable(KEY_DATA_URI));
Alex Hillsfc737de2016-03-23 17:33:02 -04007452 }
Shane Brennan5a871862017-03-11 13:14:17 -08007453 if (bundle.containsKey(KEY_EXTRAS_BUNDLE)) {
7454 message.getExtras().putAll(bundle.getBundle(KEY_EXTRAS_BUNDLE));
7455 }
Alex Hillsd9b04d92016-04-11 16:38:16 -04007456 return message;
7457 }
7458 } catch (ClassCastException e) {
7459 return null;
7460 }
7461 }
Alex Hillsfc737de2016-03-23 17:33:02 -04007462 }
7463 }
7464
7465 /**
Daniel Sandler879c5e02012-04-17 16:46:51 -04007466 * Helper class for generating large-format notifications that include a list of (up to 5) strings.
Joe Malin8d40d042012-11-05 11:36:40 -08007467 *
Robert Ly91c5ce32014-06-08 15:37:00 -07007468 * Here's how you'd set the <code>InboxStyle</code> on a notification:
Daniel Sandler879c5e02012-04-17 16:46:51 -04007469 * <pre class="prettyprint">
Robert Ly91c5ce32014-06-08 15:37:00 -07007470 * Notification notif = new Notification.Builder(mContext)
7471 * .setContentTitle(&quot;5 New mails from &quot; + sender.toString())
7472 * .setContentText(subject)
7473 * .setSmallIcon(R.drawable.new_mail)
7474 * .setLargeIcon(aBitmap)
7475 * .setStyle(new Notification.InboxStyle()
7476 * .addLine(str1)
7477 * .addLine(str2)
7478 * .setContentTitle(&quot;&quot;)
7479 * .setSummaryText(&quot;+3 more&quot;))
7480 * .build();
Daniel Sandler879c5e02012-04-17 16:46:51 -04007481 * </pre>
Joe Malin8d40d042012-11-05 11:36:40 -08007482 *
Daniel Sandler879c5e02012-04-17 16:46:51 -04007483 * @see Notification#bigContentView
7484 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04007485 public static class InboxStyle extends Style {
Selim Cinekbee4e072018-05-21 22:06:43 -07007486
7487 /**
7488 * The number of lines of remote input history allowed until we start reducing lines.
7489 */
7490 private static final int NUMBER_OF_HISTORY_ALLOWED_UNTIL_REDUCTION = 1;
Daniel Sandler879c5e02012-04-17 16:46:51 -04007491 private ArrayList<CharSequence> mTexts = new ArrayList<CharSequence>(5);
7492
Chris Wrenfbd96ba2012-05-01 12:03:58 -04007493 public InboxStyle() {
7494 }
7495
Adrian Roosf5faf9d2016-05-23 13:56:15 -07007496 /**
7497 * @deprecated use {@code InboxStyle()}.
7498 */
7499 @Deprecated
Daniel Sandler879c5e02012-04-17 16:46:51 -04007500 public InboxStyle(Builder builder) {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04007501 setBuilder(builder);
Daniel Sandler879c5e02012-04-17 16:46:51 -04007502 }
7503
Chris Wrend6297db2012-05-03 16:20:13 -04007504 /**
7505 * Overrides ContentTitle in the big form of the template.
7506 * This defaults to the value passed to setContentTitle().
7507 */
7508 public InboxStyle setBigContentTitle(CharSequence title) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04007509 internalSetBigContentTitle(safeCharSequence(title));
Chris Wrend6297db2012-05-03 16:20:13 -04007510 return this;
7511 }
7512
7513 /**
7514 * Set the first line of text after the detail section in the big form of the template.
7515 */
7516 public InboxStyle setSummaryText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04007517 internalSetSummaryText(safeCharSequence(cs));
Chris Wrend6297db2012-05-03 16:20:13 -04007518 return this;
7519 }
7520
Chris Wren0bd664d2012-08-01 13:56:56 -04007521 /**
7522 * Append a line to the digest section of the Inbox notification.
7523 */
Daniel Sandler879c5e02012-04-17 16:46:51 -04007524 public InboxStyle addLine(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04007525 mTexts.add(safeCharSequence(cs));
Daniel Sandler879c5e02012-04-17 16:46:51 -04007526 return this;
7527 }
7528
Daniel Sandlerf45564e2013-04-15 15:05:08 -04007529 /**
7530 * @hide
7531 */
Julia Reynolds7217dc92018-03-07 12:12:09 -05007532 public ArrayList<CharSequence> getLines() {
7533 return mTexts;
7534 }
7535
7536 /**
7537 * @hide
7538 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -04007539 public void addExtras(Bundle extras) {
7540 super.addExtras(extras);
Christoph Studer4600f9b2014-07-22 22:44:43 +02007541
Daniel Sandlerf45564e2013-04-15 15:05:08 -04007542 CharSequence[] a = new CharSequence[mTexts.size()];
7543 extras.putCharSequenceArray(EXTRA_TEXT_LINES, mTexts.toArray(a));
7544 }
7545
Christoph Studer4600f9b2014-07-22 22:44:43 +02007546 /**
7547 * @hide
7548 */
7549 @Override
7550 protected void restoreFromExtras(Bundle extras) {
7551 super.restoreFromExtras(extras);
7552
7553 mTexts.clear();
7554 if (extras.containsKey(EXTRA_TEXT_LINES)) {
7555 Collections.addAll(mTexts, extras.getCharSequenceArray(EXTRA_TEXT_LINES));
7556 }
7557 }
7558
Julia Reynoldsd9228f12015-10-20 10:37:27 -04007559 /**
7560 * @hide
7561 */
7562 public RemoteViews makeBigContentView() {
Selim Cinekc848c3a2016-01-13 15:27:30 -08007563 // Remove the content text so it disappears unless you have a summary
Christoph Studer4600f9b2014-07-22 22:44:43 +02007564 // Nasty
Julia Reynoldsd9228f12015-10-20 10:37:27 -04007565 CharSequence oldBuilderContentText = mBuilder.mN.extras.getCharSequence(EXTRA_TEXT);
7566 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, null);
Christoph Studer4600f9b2014-07-22 22:44:43 +02007567
Selim Cinek384804b2018-04-18 14:31:07 +08007568 TemplateBindResult result = new TemplateBindResult();
7569 RemoteViews contentView = getStandardView(mBuilder.getInboxLayoutResource(), result);
Daniel Sandler619738c2012-06-07 16:33:08 -04007570
Julia Reynoldsd9228f12015-10-20 10:37:27 -04007571 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, oldBuilderContentText);
Christoph Studer4600f9b2014-07-22 22:44:43 +02007572
Chris Wrend6297db2012-05-03 16:20:13 -04007573 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 -04007574 R.id.inbox_text4, R.id.inbox_text5, R.id.inbox_text6};
Chris Wrend6297db2012-05-03 16:20:13 -04007575
Chris Wren4ed80d52012-05-17 09:30:03 -04007576 // Make sure all rows are gone in case we reuse a view.
7577 for (int rowId : rowIds) {
7578 contentView.setViewVisibility(rowId, View.GONE);
7579 }
7580
Daniel Sandler879c5e02012-04-17 16:46:51 -04007581 int i=0;
Selim Cinek07c80172016-04-21 16:40:47 -07007582 int topPadding = mBuilder.mContext.getResources().getDimensionPixelSize(
7583 R.dimen.notification_inbox_item_top_padding);
Selim Cinek247fa012016-02-18 09:50:48 -08007584 boolean first = true;
Selim Cinek07c80172016-04-21 16:40:47 -07007585 int onlyViewId = 0;
7586 int maxRows = rowIds.length;
7587 if (mBuilder.mActions.size() > 0) {
7588 maxRows--;
7589 }
Selim Cinekbee4e072018-05-21 22:06:43 -07007590 CharSequence[] remoteInputHistory = mBuilder.mN.extras.getCharSequenceArray(
7591 EXTRA_REMOTE_INPUT_HISTORY);
7592 if (remoteInputHistory != null
7593 && remoteInputHistory.length > NUMBER_OF_HISTORY_ALLOWED_UNTIL_REDUCTION) {
7594 // Let's remove some messages to make room for the remote input history.
7595 // 1 is always able to fit, but let's remove them if they are 2 or 3
7596 int numRemoteInputs = Math.min(remoteInputHistory.length,
7597 MAX_REMOTE_INPUT_HISTORY_LINES);
7598 int totalNumRows = mTexts.size() + numRemoteInputs
7599 - NUMBER_OF_HISTORY_ALLOWED_UNTIL_REDUCTION;
7600 if (totalNumRows > maxRows) {
7601 int overflow = totalNumRows - maxRows;
7602 if (mTexts.size() > maxRows) {
7603 // Heuristic: if the Texts don't fit anyway, we'll rather drop the last
7604 // few messages, even with the remote input
7605 maxRows -= overflow;
7606 } else {
7607 // otherwise we drop the first messages
7608 i = overflow;
7609 }
7610 }
7611 }
Selim Cinek07c80172016-04-21 16:40:47 -07007612 while (i < mTexts.size() && i < maxRows) {
Daniel Sandler879c5e02012-04-17 16:46:51 -04007613 CharSequence str = mTexts.get(i);
Selim Cinek07c80172016-04-21 16:40:47 -07007614 if (!TextUtils.isEmpty(str)) {
Daniel Sandler879c5e02012-04-17 16:46:51 -04007615 contentView.setViewVisibility(rowIds[i], View.VISIBLE);
Selim Cinek48f66b72017-08-18 16:17:51 -07007616 contentView.setTextViewText(rowIds[i],
7617 mBuilder.processTextSpans(mBuilder.processLegacyText(str)));
Selim Cinek7b9605b2017-01-19 17:36:00 -08007618 mBuilder.setTextViewColorSecondary(contentView, rowIds[i]);
Selim Cinek07c80172016-04-21 16:40:47 -07007619 contentView.setViewPadding(rowIds[i], 0, topPadding, 0, 0);
Selim Cinek384804b2018-04-18 14:31:07 +08007620 handleInboxImageMargin(contentView, rowIds[i], first,
Selim Cinek1c72fa02018-04-23 18:00:54 +08007621 result.getIconMarginEnd());
Selim Cinek07c80172016-04-21 16:40:47 -07007622 if (first) {
7623 onlyViewId = rowIds[i];
7624 } else {
7625 onlyViewId = 0;
7626 }
Selim Cinek247fa012016-02-18 09:50:48 -08007627 first = false;
Daniel Sandler879c5e02012-04-17 16:46:51 -04007628 }
7629 i++;
7630 }
Selim Cinek07c80172016-04-21 16:40:47 -07007631 if (onlyViewId != 0) {
7632 // We only have 1 entry, lets make it look like the normal Text of a Bigtext
7633 topPadding = mBuilder.mContext.getResources().getDimensionPixelSize(
7634 R.dimen.notification_text_margin_top);
7635 contentView.setViewPadding(onlyViewId, 0, topPadding, 0, 0);
7636 }
Selim Cinek1e0bf612015-11-20 15:57:26 -08007637
Daniel Sandler879c5e02012-04-17 16:46:51 -04007638 return contentView;
7639 }
Selim Cinek1e0bf612015-11-20 15:57:26 -08007640
Julia Reynolds7217dc92018-03-07 12:12:09 -05007641 /**
7642 * @hide
7643 */
7644 @Override
7645 public boolean areNotificationsVisiblyDifferent(Style other) {
7646 if (other == null || getClass() != other.getClass()) {
7647 return true;
7648 }
7649 InboxStyle newS = (InboxStyle) other;
Dan Sandler7d67bd42018-05-15 14:06:38 -04007650
7651 final ArrayList<CharSequence> myLines = getLines();
7652 final ArrayList<CharSequence> newLines = newS.getLines();
7653 final int n = myLines.size();
7654 if (n != newLines.size()) {
7655 return true;
7656 }
7657
7658 for (int i = 0; i < n; i++) {
7659 if (!Objects.equals(
7660 String.valueOf(myLines.get(i)),
7661 String.valueOf(newLines.get(i)))) {
7662 return true;
7663 }
7664 }
7665 return false;
Julia Reynolds7217dc92018-03-07 12:12:09 -05007666 }
7667
Selim Cinek384804b2018-04-18 14:31:07 +08007668 private void handleInboxImageMargin(RemoteViews contentView, int id, boolean first,
Selim Cinek1c72fa02018-04-23 18:00:54 +08007669 int marginEndValue) {
Selim Cinek1e0bf612015-11-20 15:57:26 -08007670 int endMargin = 0;
Selim Cinek247fa012016-02-18 09:50:48 -08007671 if (first) {
7672 final int max = mBuilder.mN.extras.getInt(EXTRA_PROGRESS_MAX, 0);
7673 final boolean ind = mBuilder.mN.extras.getBoolean(EXTRA_PROGRESS_INDETERMINATE);
7674 boolean hasProgress = max != 0 || ind;
Selim Cinek384804b2018-04-18 14:31:07 +08007675 if (!hasProgress) {
Selim Cinek1c72fa02018-04-23 18:00:54 +08007676 endMargin = marginEndValue;
Selim Cinek247fa012016-02-18 09:50:48 -08007677 }
Selim Cinek1e0bf612015-11-20 15:57:26 -08007678 }
Selim Cinek1c72fa02018-04-23 18:00:54 +08007679 contentView.setViewLayoutMarginEnd(id, endMargin);
Selim Cinek1e0bf612015-11-20 15:57:26 -08007680 }
Daniel Sandler879c5e02012-04-17 16:46:51 -04007681 }
Dan Sandler842dd772014-05-15 09:36:47 -04007682
7683 /**
7684 * Notification style for media playback notifications.
7685 *
7686 * In the expanded form, {@link Notification#bigContentView}, up to 5
7687 * {@link Notification.Action}s specified with
Dan Sandler86647982015-05-13 23:41:13 -04007688 * {@link Notification.Builder#addAction(Action) addAction} will be
Dan Sandler842dd772014-05-15 09:36:47 -04007689 * shown as icon-only pushbuttons, suitable for transport controls. The Bitmap given to
7690 * {@link Notification.Builder#setLargeIcon(android.graphics.Bitmap) setLargeIcon()} will be
7691 * treated as album artwork.
Selim Cinek99104832017-01-25 14:47:33 -08007692 * <p>
Dan Sandler842dd772014-05-15 09:36:47 -04007693 * Unlike the other styles provided here, MediaStyle can also modify the standard-size
7694 * {@link Notification#contentView}; by providing action indices to
Christoph Studerfde6f4d2014-12-12 13:23:26 +01007695 * {@link #setShowActionsInCompactView(int...)} you can promote up to 3 actions to be displayed
Dan Sandler842dd772014-05-15 09:36:47 -04007696 * in the standard view alongside the usual content.
Selim Cinek99104832017-01-25 14:47:33 -08007697 * <p>
Bryan Mawhinney6be8de32014-07-18 10:35:12 +01007698 * Notifications created with MediaStyle will have their category set to
7699 * {@link Notification#CATEGORY_TRANSPORT CATEGORY_TRANSPORT} unless you set a different
7700 * category using {@link Notification.Builder#setCategory(String) setCategory()}.
Selim Cinek99104832017-01-25 14:47:33 -08007701 * <p>
Jeff Browndba34ba2014-06-24 20:46:03 -07007702 * Finally, if you attach a {@link android.media.session.MediaSession.Token} using
7703 * {@link android.app.Notification.MediaStyle#setMediaSession(MediaSession.Token)},
Dan Sandler842dd772014-05-15 09:36:47 -04007704 * the System UI can identify this as a notification representing an active media session
7705 * and respond accordingly (by showing album artwork in the lockscreen, for example).
7706 *
Selim Cinek99104832017-01-25 14:47:33 -08007707 * <p>
7708 * Starting at {@link android.os.Build.VERSION_CODES#O Android O} any notification that has a
7709 * media session attached with {@link #setMediaSession(MediaSession.Token)} will be colorized.
7710 * You can opt-out of this behavior by using {@link Notification.Builder#setColorized(boolean)}.
7711 * <p>
7712 *
Dan Sandler842dd772014-05-15 09:36:47 -04007713 * To use this style with your Notification, feed it to
7714 * {@link Notification.Builder#setStyle(android.app.Notification.Style)} like so:
7715 * <pre class="prettyprint">
7716 * Notification noti = new Notification.Builder()
7717 * .setSmallIcon(R.drawable.ic_stat_player)
Christoph Studere935fe92014-11-24 14:18:06 +01007718 * .setContentTitle(&quot;Track title&quot;)
7719 * .setContentText(&quot;Artist - Album&quot;)
7720 * .setLargeIcon(albumArtBitmap))
Bryan Mawhinney6be8de32014-07-18 10:35:12 +01007721 * .setStyle(<b>new Notification.MediaStyle()</b>
7722 * .setMediaSession(mySession))
Dan Sandler842dd772014-05-15 09:36:47 -04007723 * .build();
7724 * </pre>
7725 *
7726 * @see Notification#bigContentView
Selim Cinek99104832017-01-25 14:47:33 -08007727 * @see Notification.Builder#setColorized(boolean)
Dan Sandler842dd772014-05-15 09:36:47 -04007728 */
7729 public static class MediaStyle extends Style {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02007730 static final int MAX_MEDIA_BUTTONS_IN_COMPACT = 3;
Dan Sandler842dd772014-05-15 09:36:47 -04007731 static final int MAX_MEDIA_BUTTONS = 5;
7732
7733 private int[] mActionsToShowInCompact = null;
Jeff Browndba34ba2014-06-24 20:46:03 -07007734 private MediaSession.Token mToken;
Dan Sandler842dd772014-05-15 09:36:47 -04007735
7736 public MediaStyle() {
7737 }
7738
Adrian Roosf5faf9d2016-05-23 13:56:15 -07007739 /**
7740 * @deprecated use {@code MediaStyle()}.
7741 */
7742 @Deprecated
Dan Sandler842dd772014-05-15 09:36:47 -04007743 public MediaStyle(Builder builder) {
7744 setBuilder(builder);
7745 }
7746
7747 /**
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02007748 * 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 -04007749 * notification view.
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02007750 *
7751 * @param actions the indices of the actions to show in the compact notification view
Dan Sandler842dd772014-05-15 09:36:47 -04007752 */
7753 public MediaStyle setShowActionsInCompactView(int...actions) {
7754 mActionsToShowInCompact = actions;
7755 return this;
7756 }
7757
7758 /**
Jeff Browndba34ba2014-06-24 20:46:03 -07007759 * Attach a {@link android.media.session.MediaSession.Token} to this Notification
7760 * to provide additional playback information and control to the SystemUI.
Dan Sandler842dd772014-05-15 09:36:47 -04007761 */
Jeff Browndba34ba2014-06-24 20:46:03 -07007762 public MediaStyle setMediaSession(MediaSession.Token token) {
Dan Sandler842dd772014-05-15 09:36:47 -04007763 mToken = token;
7764 return this;
7765 }
7766
Christoph Studer4600f9b2014-07-22 22:44:43 +02007767 /**
7768 * @hide
7769 */
Dan Sandler842dd772014-05-15 09:36:47 -04007770 @Override
Mathew Inwood61e8ae62018-08-14 14:17:44 +01007771 @UnsupportedAppUsage
Dan Sandler842dd772014-05-15 09:36:47 -04007772 public Notification buildStyled(Notification wip) {
Christoph Studer4600f9b2014-07-22 22:44:43 +02007773 super.buildStyled(wip);
Bryan Mawhinney6be8de32014-07-18 10:35:12 +01007774 if (wip.category == null) {
7775 wip.category = Notification.CATEGORY_TRANSPORT;
7776 }
Dan Sandler842dd772014-05-15 09:36:47 -04007777 return wip;
7778 }
7779
Christoph Studer4600f9b2014-07-22 22:44:43 +02007780 /**
7781 * @hide
7782 */
7783 @Override
Selim Cinek7d1009b2017-01-25 15:28:28 -08007784 public RemoteViews makeContentView(boolean increasedHeight) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04007785 return makeMediaContentView();
Christoph Studer4600f9b2014-07-22 22:44:43 +02007786 }
7787
7788 /**
7789 * @hide
7790 */
7791 @Override
Julia Reynoldsd9228f12015-10-20 10:37:27 -04007792 public RemoteViews makeBigContentView() {
7793 return makeMediaBigContentView();
Christoph Studer4600f9b2014-07-22 22:44:43 +02007794 }
7795
Selim Cinekcc10bfb2016-02-10 16:24:21 -08007796 /**
7797 * @hide
7798 */
7799 @Override
Selim Cinek87ed69b2017-02-09 15:59:43 -08007800 public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
Selim Cinekcc10bfb2016-02-10 16:24:21 -08007801 RemoteViews expanded = makeMediaBigContentView();
7802 return expanded != null ? expanded : makeMediaContentView();
7803 }
7804
Dan Sandler842dd772014-05-15 09:36:47 -04007805 /** @hide */
7806 @Override
7807 public void addExtras(Bundle extras) {
7808 super.addExtras(extras);
7809
7810 if (mToken != null) {
7811 extras.putParcelable(EXTRA_MEDIA_SESSION, mToken);
7812 }
Bryan Mawhinneye191f902014-07-22 12:50:09 +01007813 if (mActionsToShowInCompact != null) {
7814 extras.putIntArray(EXTRA_COMPACT_ACTIONS, mActionsToShowInCompact);
7815 }
Dan Sandler842dd772014-05-15 09:36:47 -04007816 }
7817
Christoph Studer4600f9b2014-07-22 22:44:43 +02007818 /**
7819 * @hide
7820 */
7821 @Override
7822 protected void restoreFromExtras(Bundle extras) {
7823 super.restoreFromExtras(extras);
7824
7825 if (extras.containsKey(EXTRA_MEDIA_SESSION)) {
7826 mToken = extras.getParcelable(EXTRA_MEDIA_SESSION);
7827 }
7828 if (extras.containsKey(EXTRA_COMPACT_ACTIONS)) {
7829 mActionsToShowInCompact = extras.getIntArray(EXTRA_COMPACT_ACTIONS);
7830 }
7831 }
7832
Julia Reynolds7217dc92018-03-07 12:12:09 -05007833 /**
7834 * @hide
7835 */
7836 @Override
7837 public boolean areNotificationsVisiblyDifferent(Style other) {
7838 if (other == null || getClass() != other.getClass()) {
7839 return true;
7840 }
7841 // All fields to compare are on the Notification object
7842 return false;
7843 }
7844
Selim Cinek5bf069a2015-11-10 19:14:27 -05007845 private RemoteViews generateMediaActionButton(Action action, int color) {
Dan Sandler842dd772014-05-15 09:36:47 -04007846 final boolean tombstone = (action.actionIntent == null);
Selim Cinekf33b1112015-07-15 17:45:11 -07007847 RemoteViews button = new BuilderRemoteViews(mBuilder.mContext.getApplicationInfo(),
Alan Viverette3cb07a462014-06-06 14:19:53 -07007848 R.layout.notification_material_media_action);
Dan Sandler68079d52015-07-22 10:45:30 -04007849 button.setImageViewIcon(R.id.action0, action.getIcon());
Anthony Chenad4d1582017-04-10 16:07:58 -07007850
7851 // If the action buttons should not be tinted, then just use the default
7852 // notification color. Otherwise, just use the passed-in color.
Lucas Dupinf03e7522018-06-25 16:21:13 -07007853 Configuration currentConfig = mBuilder.mContext.getResources().getConfiguration();
7854 boolean inNightMode = (currentConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK)
7855 == Configuration.UI_MODE_NIGHT_YES;
Anthony Chenad4d1582017-04-10 16:07:58 -07007856 int tintColor = mBuilder.shouldTintActionButtons() || mBuilder.isColorized()
7857 ? color
Lucas Dupina291d192018-06-07 13:59:42 -07007858 : ContrastColorUtil.resolveColor(mBuilder.mContext,
Lucas Dupinf03e7522018-06-25 16:21:13 -07007859 Notification.COLOR_DEFAULT, inNightMode);
Anthony Chenad4d1582017-04-10 16:07:58 -07007860
Sunny Goyal5b153922017-09-21 21:00:36 -07007861 button.setDrawableTint(R.id.action0, false, tintColor,
7862 PorterDuff.Mode.SRC_ATOP);
Dan Sandler842dd772014-05-15 09:36:47 -04007863 if (!tombstone) {
7864 button.setOnClickPendingIntent(R.id.action0, action.actionIntent);
7865 }
7866 button.setContentDescription(R.id.action0, action.title);
7867 return button;
7868 }
7869
7870 private RemoteViews makeMediaContentView() {
7871 RemoteViews view = mBuilder.applyStandardTemplate(
Selim Cinek384804b2018-04-18 14:31:07 +08007872 R.layout.notification_template_material_media, false, /* hasProgress */
7873 null /* result */);
Dan Sandler842dd772014-05-15 09:36:47 -04007874
7875 final int numActions = mBuilder.mActions.size();
7876 final int N = mActionsToShowInCompact == null
7877 ? 0
7878 : Math.min(mActionsToShowInCompact.length, MAX_MEDIA_BUTTONS_IN_COMPACT);
Selim Cinek5da4a6b2018-07-20 14:00:24 -07007879 view.removeAllViews(com.android.internal.R.id.media_actions);
Dan Sandler842dd772014-05-15 09:36:47 -04007880 if (N > 0) {
Dan Sandler842dd772014-05-15 09:36:47 -04007881 for (int i = 0; i < N; i++) {
7882 if (i >= numActions) {
7883 throw new IllegalArgumentException(String.format(
7884 "setShowActionsInCompactView: action %d out of bounds (max %d)",
7885 i, numActions - 1));
7886 }
7887
7888 final Action action = mBuilder.mActions.get(mActionsToShowInCompact[i]);
Selim Cinekc7f5a822018-03-20 19:32:06 -07007889 final RemoteViews button = generateMediaActionButton(action, getActionColor());
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02007890 view.addView(com.android.internal.R.id.media_actions, button);
Dan Sandler842dd772014-05-15 09:36:47 -04007891 }
7892 }
Selim Cinekfdc738f2016-01-27 20:04:27 -08007893 handleImage(view);
7894 // handle the content margin
Adrian Roos2d5dbba2016-06-08 17:11:53 -07007895 int endMargin = R.dimen.notification_content_margin_end;
Selim Cinek279fa862016-06-14 10:57:25 -07007896 if (mBuilder.mN.hasLargeIcon()) {
Selim Cinek384804b2018-04-18 14:31:07 +08007897 endMargin = R.dimen.notification_media_image_margin_end;
Selim Cinekfdc738f2016-01-27 20:04:27 -08007898 }
Adrian Roos2d5dbba2016-06-08 17:11:53 -07007899 view.setViewLayoutMarginEndDimen(R.id.notification_main_column, endMargin);
Dan Sandler842dd772014-05-15 09:36:47 -04007900 return view;
7901 }
7902
Selim Cinekc7f5a822018-03-20 19:32:06 -07007903 private int getActionColor() {
7904 return mBuilder.isColorized() ? mBuilder.getPrimaryTextColor()
7905 : mBuilder.resolveContrastColor();
Selim Cinek99104832017-01-25 14:47:33 -08007906 }
7907
Dan Sandler842dd772014-05-15 09:36:47 -04007908 private RemoteViews makeMediaBigContentView() {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02007909 final int actionCount = Math.min(mBuilder.mActions.size(), MAX_MEDIA_BUTTONS);
Selim Cinekcc10bfb2016-02-10 16:24:21 -08007910 // Dont add an expanded view if there is no more content to be revealed
7911 int actionsInCompact = mActionsToShowInCompact == null
7912 ? 0
7913 : Math.min(mActionsToShowInCompact.length, MAX_MEDIA_BUTTONS_IN_COMPACT);
Selim Cinek279fa862016-06-14 10:57:25 -07007914 if (!mBuilder.mN.hasLargeIcon() && actionCount <= actionsInCompact) {
Selim Cinekcc10bfb2016-02-10 16:24:21 -08007915 return null;
7916 }
Selim Cinek5bf069a2015-11-10 19:14:27 -05007917 RemoteViews big = mBuilder.applyStandardTemplate(
Selim Cinek384804b2018-04-18 14:31:07 +08007918 R.layout.notification_template_material_big_media, false, null /* result */);
Dan Sandler842dd772014-05-15 09:36:47 -04007919
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02007920 if (actionCount > 0) {
7921 big.removeAllViews(com.android.internal.R.id.media_actions);
7922 for (int i = 0; i < actionCount; i++) {
Selim Cinek5bf069a2015-11-10 19:14:27 -05007923 final RemoteViews button = generateMediaActionButton(mBuilder.mActions.get(i),
Selim Cinekc7f5a822018-03-20 19:32:06 -07007924 getActionColor());
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02007925 big.addView(com.android.internal.R.id.media_actions, button);
Dan Sandler842dd772014-05-15 09:36:47 -04007926 }
7927 }
Selim Cinek5bf069a2015-11-10 19:14:27 -05007928 handleImage(big);
Dan Sandler842dd772014-05-15 09:36:47 -04007929 return big;
7930 }
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02007931
Selim Cinek5bf069a2015-11-10 19:14:27 -05007932 private void handleImage(RemoteViews contentView) {
Selim Cinek279fa862016-06-14 10:57:25 -07007933 if (mBuilder.mN.hasLargeIcon()) {
Adrian Roos2d5dbba2016-06-08 17:11:53 -07007934 contentView.setViewLayoutMarginEndDimen(R.id.line1, 0);
7935 contentView.setViewLayoutMarginEndDimen(R.id.text, 0);
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02007936 }
7937 }
7938
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02007939 /**
7940 * @hide
7941 */
7942 @Override
7943 protected boolean hasProgress() {
7944 return false;
7945 }
Dan Sandler842dd772014-05-15 09:36:47 -04007946 }
Griff Hazen61a9e862014-05-22 16:05:19 -07007947
Selim Cinek593610c2016-02-16 18:42:57 -08007948 /**
7949 * Notification style for custom views that are decorated by the system
7950 *
7951 * <p>Instead of providing a notification that is completely custom, a developer can set this
7952 * style and still obtain system decorations like the notification header with the expand
7953 * affordance and actions.
7954 *
7955 * <p>Use {@link android.app.Notification.Builder#setCustomContentView(RemoteViews)},
7956 * {@link android.app.Notification.Builder#setCustomBigContentView(RemoteViews)} and
7957 * {@link android.app.Notification.Builder#setCustomHeadsUpContentView(RemoteViews)} to set the
7958 * corresponding custom views to display.
7959 *
7960 * To use this style with your Notification, feed it to
7961 * {@link Notification.Builder#setStyle(android.app.Notification.Style)} like so:
7962 * <pre class="prettyprint">
7963 * Notification noti = new Notification.Builder()
7964 * .setSmallIcon(R.drawable.ic_stat_player)
7965 * .setLargeIcon(albumArtBitmap))
7966 * .setCustomContentView(contentView);
7967 * .setStyle(<b>new Notification.DecoratedCustomViewStyle()</b>)
7968 * .build();
7969 * </pre>
7970 */
7971 public static class DecoratedCustomViewStyle extends Style {
7972
7973 public DecoratedCustomViewStyle() {
7974 }
7975
Selim Cinek593610c2016-02-16 18:42:57 -08007976 /**
7977 * @hide
7978 */
7979 public boolean displayCustomViewInline() {
7980 return true;
7981 }
7982
7983 /**
7984 * @hide
7985 */
7986 @Override
Selim Cinek7d1009b2017-01-25 15:28:28 -08007987 public RemoteViews makeContentView(boolean increasedHeight) {
Selim Cinek593610c2016-02-16 18:42:57 -08007988 return makeStandardTemplateWithCustomContent(mBuilder.mN.contentView);
7989 }
7990
7991 /**
7992 * @hide
7993 */
7994 @Override
7995 public RemoteViews makeBigContentView() {
7996 return makeDecoratedBigContentView();
7997 }
7998
7999 /**
8000 * @hide
8001 */
8002 @Override
Selim Cinek87ed69b2017-02-09 15:59:43 -08008003 public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
Selim Cinek593610c2016-02-16 18:42:57 -08008004 return makeDecoratedHeadsUpContentView();
8005 }
8006
Selim Cinek593610c2016-02-16 18:42:57 -08008007 private RemoteViews makeDecoratedHeadsUpContentView() {
8008 RemoteViews headsUpContentView = mBuilder.mN.headsUpContentView == null
8009 ? mBuilder.mN.contentView
8010 : mBuilder.mN.headsUpContentView;
8011 if (mBuilder.mActions.size() == 0) {
8012 return makeStandardTemplateWithCustomContent(headsUpContentView);
8013 }
Selim Cinek384804b2018-04-18 14:31:07 +08008014 TemplateBindResult result = new TemplateBindResult();
Selim Cinek593610c2016-02-16 18:42:57 -08008015 RemoteViews remoteViews = mBuilder.applyStandardTemplateWithActions(
Selim Cinek384804b2018-04-18 14:31:07 +08008016 mBuilder.getBigBaseLayoutResource(), result);
8017 buildIntoRemoteViewContent(remoteViews, headsUpContentView, result);
Selim Cinek593610c2016-02-16 18:42:57 -08008018 return remoteViews;
8019 }
8020
Selim Cinek593610c2016-02-16 18:42:57 -08008021 private RemoteViews makeStandardTemplateWithCustomContent(RemoteViews customContent) {
Selim Cinek384804b2018-04-18 14:31:07 +08008022 TemplateBindResult result = new TemplateBindResult();
Selim Cinek593610c2016-02-16 18:42:57 -08008023 RemoteViews remoteViews = mBuilder.applyStandardTemplate(
Selim Cinek384804b2018-04-18 14:31:07 +08008024 mBuilder.getBaseLayoutResource(), result);
8025 buildIntoRemoteViewContent(remoteViews, customContent, result);
Selim Cinek593610c2016-02-16 18:42:57 -08008026 return remoteViews;
8027 }
8028
Selim Cinek593610c2016-02-16 18:42:57 -08008029 private RemoteViews makeDecoratedBigContentView() {
8030 RemoteViews bigContentView = mBuilder.mN.bigContentView == null
8031 ? mBuilder.mN.contentView
8032 : mBuilder.mN.bigContentView;
8033 if (mBuilder.mActions.size() == 0) {
8034 return makeStandardTemplateWithCustomContent(bigContentView);
8035 }
Selim Cinek384804b2018-04-18 14:31:07 +08008036 TemplateBindResult result = new TemplateBindResult();
Selim Cinek593610c2016-02-16 18:42:57 -08008037 RemoteViews remoteViews = mBuilder.applyStandardTemplateWithActions(
Selim Cinek384804b2018-04-18 14:31:07 +08008038 mBuilder.getBigBaseLayoutResource(), result);
8039 buildIntoRemoteViewContent(remoteViews, bigContentView, result);
Selim Cinek593610c2016-02-16 18:42:57 -08008040 return remoteViews;
8041 }
Selim Cinek247fa012016-02-18 09:50:48 -08008042
8043 private void buildIntoRemoteViewContent(RemoteViews remoteViews,
Selim Cinek384804b2018-04-18 14:31:07 +08008044 RemoteViews customContent, TemplateBindResult result) {
Adrian Roos5081c0d2016-02-26 16:04:19 -08008045 if (customContent != null) {
Selim Cinekf91017e2016-03-14 12:25:09 -07008046 // Need to clone customContent before adding, because otherwise it can no longer be
8047 // parceled independently of remoteViews.
Adrian Roos5081c0d2016-02-26 16:04:19 -08008048 customContent = customContent.clone();
Anthony Chen8f5f3582017-04-11 11:18:37 -07008049 remoteViews.removeAllViewsExceptId(R.id.notification_main_column, R.id.progress);
8050 remoteViews.addView(R.id.notification_main_column, customContent, 0 /* index */);
Selim Cinekfc8073c2017-08-16 17:50:20 -07008051 remoteViews.setReapplyDisallowed();
Adrian Roos5081c0d2016-02-26 16:04:19 -08008052 }
Selim Cinek247fa012016-02-18 09:50:48 -08008053 // also update the end margin if there is an image
Selim Cinek384804b2018-04-18 14:31:07 +08008054 Resources resources = mBuilder.mContext.getResources();
8055 int endMargin = resources.getDimensionPixelSize(
Selim Cinek1c72fa02018-04-23 18:00:54 +08008056 R.dimen.notification_content_margin_end) + result.getIconMarginEnd();
Selim Cinek384804b2018-04-18 14:31:07 +08008057 remoteViews.setViewLayoutMarginEnd(R.id.notification_main_column, endMargin);
Selim Cinek247fa012016-02-18 09:50:48 -08008058 }
Julia Reynolds7217dc92018-03-07 12:12:09 -05008059
8060 /**
8061 * @hide
8062 */
8063 @Override
8064 public boolean areNotificationsVisiblyDifferent(Style other) {
8065 if (other == null || getClass() != other.getClass()) {
8066 return true;
8067 }
8068 // Comparison done for all custom RemoteViews, independent of style
8069 return false;
8070 }
Selim Cinek593610c2016-02-16 18:42:57 -08008071 }
8072
Selim Cinek03eb3b72016-02-18 10:39:45 -08008073 /**
8074 * Notification style for media custom views that are decorated by the system
8075 *
8076 * <p>Instead of providing a media notification that is completely custom, a developer can set
8077 * this style and still obtain system decorations like the notification header with the expand
8078 * affordance and actions.
8079 *
8080 * <p>Use {@link android.app.Notification.Builder#setCustomContentView(RemoteViews)},
8081 * {@link android.app.Notification.Builder#setCustomBigContentView(RemoteViews)} and
8082 * {@link android.app.Notification.Builder#setCustomHeadsUpContentView(RemoteViews)} to set the
8083 * corresponding custom views to display.
Selim Cinek99104832017-01-25 14:47:33 -08008084 * <p>
8085 * Contrary to {@link MediaStyle} a developer has to opt-in to the colorizing of the
8086 * notification by using {@link Notification.Builder#setColorized(boolean)}.
8087 * <p>
Selim Cinek03eb3b72016-02-18 10:39:45 -08008088 * To use this style with your Notification, feed it to
8089 * {@link Notification.Builder#setStyle(android.app.Notification.Style)} like so:
8090 * <pre class="prettyprint">
8091 * Notification noti = new Notification.Builder()
8092 * .setSmallIcon(R.drawable.ic_stat_player)
8093 * .setLargeIcon(albumArtBitmap))
8094 * .setCustomContentView(contentView);
8095 * .setStyle(<b>new Notification.DecoratedMediaCustomViewStyle()</b>
8096 * .setMediaSession(mySession))
8097 * .build();
8098 * </pre>
8099 *
8100 * @see android.app.Notification.DecoratedCustomViewStyle
8101 * @see android.app.Notification.MediaStyle
8102 */
8103 public static class DecoratedMediaCustomViewStyle extends MediaStyle {
8104
8105 public DecoratedMediaCustomViewStyle() {
8106 }
8107
Selim Cinek03eb3b72016-02-18 10:39:45 -08008108 /**
8109 * @hide
8110 */
8111 public boolean displayCustomViewInline() {
8112 return true;
8113 }
8114
8115 /**
8116 * @hide
8117 */
8118 @Override
Selim Cinek7d1009b2017-01-25 15:28:28 -08008119 public RemoteViews makeContentView(boolean increasedHeight) {
8120 RemoteViews remoteViews = super.makeContentView(false /* increasedHeight */);
Selim Cinek03eb3b72016-02-18 10:39:45 -08008121 return buildIntoRemoteView(remoteViews, R.id.notification_content_container,
8122 mBuilder.mN.contentView);
8123 }
8124
8125 /**
8126 * @hide
8127 */
8128 @Override
8129 public RemoteViews makeBigContentView() {
8130 RemoteViews customRemoteView = mBuilder.mN.bigContentView != null
8131 ? mBuilder.mN.bigContentView
8132 : mBuilder.mN.contentView;
8133 return makeBigContentViewWithCustomContent(customRemoteView);
8134 }
8135
8136 private RemoteViews makeBigContentViewWithCustomContent(RemoteViews customRemoteView) {
8137 RemoteViews remoteViews = super.makeBigContentView();
8138 if (remoteViews != null) {
8139 return buildIntoRemoteView(remoteViews, R.id.notification_main_column,
8140 customRemoteView);
8141 } else if (customRemoteView != mBuilder.mN.contentView){
Selim Cinek7d1009b2017-01-25 15:28:28 -08008142 remoteViews = super.makeContentView(false /* increasedHeight */);
Selim Cinek03eb3b72016-02-18 10:39:45 -08008143 return buildIntoRemoteView(remoteViews, R.id.notification_content_container,
8144 customRemoteView);
8145 } else {
8146 return null;
8147 }
8148 }
8149
8150 /**
8151 * @hide
8152 */
8153 @Override
Selim Cinek87ed69b2017-02-09 15:59:43 -08008154 public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
Selim Cinek03eb3b72016-02-18 10:39:45 -08008155 RemoteViews customRemoteView = mBuilder.mN.headsUpContentView != null
8156 ? mBuilder.mN.headsUpContentView
8157 : mBuilder.mN.contentView;
8158 return makeBigContentViewWithCustomContent(customRemoteView);
8159 }
8160
Julia Reynolds7217dc92018-03-07 12:12:09 -05008161 /**
8162 * @hide
8163 */
8164 @Override
8165 public boolean areNotificationsVisiblyDifferent(Style other) {
8166 if (other == null || getClass() != other.getClass()) {
8167 return true;
8168 }
8169 // Comparison done for all custom RemoteViews, independent of style
8170 return false;
8171 }
8172
Selim Cinek03eb3b72016-02-18 10:39:45 -08008173 private RemoteViews buildIntoRemoteView(RemoteViews remoteViews, int id,
8174 RemoteViews customContent) {
Adrian Roos5081c0d2016-02-26 16:04:19 -08008175 if (customContent != null) {
Selim Cinekf91017e2016-03-14 12:25:09 -07008176 // Need to clone customContent before adding, because otherwise it can no longer be
8177 // parceled independently of remoteViews.
Adrian Roos5081c0d2016-02-26 16:04:19 -08008178 customContent = customContent.clone();
Selim Cinek87c31532017-08-18 18:53:44 -07008179 customContent.overrideTextColors(mBuilder.getPrimaryTextColor());
Selim Cinekf91017e2016-03-14 12:25:09 -07008180 remoteViews.removeAllViews(id);
8181 remoteViews.addView(id, customContent);
Selim Cinekfc8073c2017-08-16 17:50:20 -07008182 remoteViews.setReapplyDisallowed();
Adrian Roos5081c0d2016-02-26 16:04:19 -08008183 }
Selim Cinek03eb3b72016-02-18 10:39:45 -08008184 return remoteViews;
8185 }
8186 }
8187
Christoph Studer4600f9b2014-07-22 22:44:43 +02008188 // When adding a new Style subclass here, don't forget to update
8189 // Builder.getNotificationStyleClass.
8190
Griff Hazen61a9e862014-05-22 16:05:19 -07008191 /**
8192 * Extender interface for use with {@link Builder#extend}. Extenders may be used to add
8193 * metadata or change options on a notification builder.
8194 */
8195 public interface Extender {
8196 /**
8197 * Apply this extender to a notification builder.
8198 * @param builder the builder to be modified.
8199 * @return the build object for chaining.
8200 */
8201 public Builder extend(Builder builder);
8202 }
8203
8204 /**
8205 * Helper class to add wearable extensions to notifications.
8206 * <p class="note"> See
8207 * <a href="{@docRoot}wear/notifications/creating.html">Creating Notifications
8208 * for Android Wear</a> for more information on how to use this class.
8209 * <p>
8210 * To create a notification with wearable extensions:
8211 * <ol>
8212 * <li>Create a {@link android.app.Notification.Builder}, setting any desired
8213 * properties.
8214 * <li>Create a {@link android.app.Notification.WearableExtender}.
8215 * <li>Set wearable-specific properties using the
8216 * {@code add} and {@code set} methods of {@link android.app.Notification.WearableExtender}.
8217 * <li>Call {@link android.app.Notification.Builder#extend} to apply the extensions to a
8218 * notification.
8219 * <li>Post the notification to the notification system with the
8220 * {@code NotificationManager.notify(...)} methods.
8221 * </ol>
8222 *
8223 * <pre class="prettyprint">
8224 * Notification notif = new Notification.Builder(mContext)
8225 * .setContentTitle(&quot;New mail from &quot; + sender.toString())
8226 * .setContentText(subject)
8227 * .setSmallIcon(R.drawable.new_mail)
8228 * .extend(new Notification.WearableExtender()
8229 * .setContentIcon(R.drawable.new_mail))
8230 * .build();
8231 * NotificationManager notificationManger =
8232 * (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
8233 * notificationManger.notify(0, notif);</pre>
8234 *
8235 * <p>Wearable extensions can be accessed on an existing notification by using the
8236 * {@code WearableExtender(Notification)} constructor,
8237 * and then using the {@code get} methods to access values.
8238 *
8239 * <pre class="prettyprint">
8240 * Notification.WearableExtender wearableExtender = new Notification.WearableExtender(
8241 * notification);
Griff Hazen14f57992014-05-26 09:07:14 -07008242 * List&lt;Notification&gt; pages = wearableExtender.getPages();</pre>
Griff Hazen61a9e862014-05-22 16:05:19 -07008243 */
8244 public static final class WearableExtender implements Extender {
8245 /**
8246 * Sentinel value for an action index that is unset.
8247 */
8248 public static final int UNSET_ACTION_INDEX = -1;
8249
8250 /**
8251 * Size value for use with {@link #setCustomSizePreset} to show this notification with
8252 * default sizing.
8253 * <p>For custom display notifications created using {@link #setDisplayIntent},
Paul Soulosaa4f4bf2015-08-04 11:59:45 -07008254 * the default is {@link #SIZE_MEDIUM}. All other notifications size automatically based
Griff Hazen61a9e862014-05-22 16:05:19 -07008255 * on their content.
Gus Prevasd7363752018-09-18 14:35:15 -04008256 *
8257 * @deprecated Display intents are no longer supported.
Griff Hazen61a9e862014-05-22 16:05:19 -07008258 */
Gus Prevasd7363752018-09-18 14:35:15 -04008259 @Deprecated
Griff Hazen61a9e862014-05-22 16:05:19 -07008260 public static final int SIZE_DEFAULT = 0;
8261
8262 /**
8263 * Size value for use with {@link #setCustomSizePreset} to show this notification
8264 * with an extra small size.
8265 * <p>This value is only applicable for custom display notifications created using
8266 * {@link #setDisplayIntent}.
Gus Prevasd7363752018-09-18 14:35:15 -04008267 *
8268 * @deprecated Display intents are no longer supported.
Griff Hazen61a9e862014-05-22 16:05:19 -07008269 */
Gus Prevasd7363752018-09-18 14:35:15 -04008270 @Deprecated
Griff Hazen61a9e862014-05-22 16:05:19 -07008271 public static final int SIZE_XSMALL = 1;
8272
8273 /**
8274 * Size value for use with {@link #setCustomSizePreset} to show this notification
8275 * with a small size.
8276 * <p>This value is only applicable for custom display notifications created using
8277 * {@link #setDisplayIntent}.
Gus Prevasd7363752018-09-18 14:35:15 -04008278 *
8279 * @deprecated Display intents are no longer supported.
Griff Hazen61a9e862014-05-22 16:05:19 -07008280 */
Gus Prevasd7363752018-09-18 14:35:15 -04008281 @Deprecated
Griff Hazen61a9e862014-05-22 16:05:19 -07008282 public static final int SIZE_SMALL = 2;
8283
8284 /**
8285 * Size value for use with {@link #setCustomSizePreset} to show this notification
8286 * with a medium size.
8287 * <p>This value is only applicable for custom display notifications created using
8288 * {@link #setDisplayIntent}.
Gus Prevasd7363752018-09-18 14:35:15 -04008289 *
8290 * @deprecated Display intents are no longer supported.
Griff Hazen61a9e862014-05-22 16:05:19 -07008291 */
Gus Prevasd7363752018-09-18 14:35:15 -04008292 @Deprecated
Griff Hazen61a9e862014-05-22 16:05:19 -07008293 public static final int SIZE_MEDIUM = 3;
8294
8295 /**
8296 * Size value for use with {@link #setCustomSizePreset} to show this notification
8297 * with a large size.
8298 * <p>This value is only applicable for custom display notifications created using
8299 * {@link #setDisplayIntent}.
Gus Prevasd7363752018-09-18 14:35:15 -04008300 *
8301 * @deprecated Display intents are no longer supported.
Griff Hazen61a9e862014-05-22 16:05:19 -07008302 */
Gus Prevasd7363752018-09-18 14:35:15 -04008303 @Deprecated
Griff Hazen61a9e862014-05-22 16:05:19 -07008304 public static final int SIZE_LARGE = 4;
8305
Griff Hazend5f11f92014-05-27 15:40:09 -07008306 /**
8307 * Size value for use with {@link #setCustomSizePreset} to show this notification
8308 * full screen.
8309 * <p>This value is only applicable for custom display notifications created using
8310 * {@link #setDisplayIntent}.
Gus Prevasd7363752018-09-18 14:35:15 -04008311 *
8312 * @deprecated Display intents are no longer supported.
Griff Hazend5f11f92014-05-27 15:40:09 -07008313 */
Gus Prevasd7363752018-09-18 14:35:15 -04008314 @Deprecated
Griff Hazend5f11f92014-05-27 15:40:09 -07008315 public static final int SIZE_FULL_SCREEN = 5;
8316
Griff Hazen5f2edfc2014-09-29 16:28:44 -07008317 /**
8318 * Sentinel value for use with {@link #setHintScreenTimeout} to keep the screen on for a
8319 * short amount of time when this notification is displayed on the screen. This
8320 * is the default value.
Gus Prevasd7363752018-09-18 14:35:15 -04008321 *
8322 * @deprecated This feature is no longer supported.
Griff Hazen5f2edfc2014-09-29 16:28:44 -07008323 */
Gus Prevasd7363752018-09-18 14:35:15 -04008324 @Deprecated
Griff Hazen5f2edfc2014-09-29 16:28:44 -07008325 public static final int SCREEN_TIMEOUT_SHORT = 0;
8326
8327 /**
8328 * Sentinel value for use with {@link #setHintScreenTimeout} to keep the screen on
8329 * for a longer amount of time when this notification is displayed on the screen.
Gus Prevasd7363752018-09-18 14:35:15 -04008330 *
8331 * @deprecated This feature is no longer supported.
Griff Hazen5f2edfc2014-09-29 16:28:44 -07008332 */
Gus Prevasd7363752018-09-18 14:35:15 -04008333 @Deprecated
Griff Hazen5f2edfc2014-09-29 16:28:44 -07008334 public static final int SCREEN_TIMEOUT_LONG = -1;
8335
Griff Hazen61a9e862014-05-22 16:05:19 -07008336 /** Notification extra which contains wearable extensions */
8337 private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS";
8338
Pete Gastaf6781d2014-10-07 15:17:05 -04008339 // Keys within EXTRA_WEARABLE_EXTENSIONS for wearable options.
Griff Hazen61a9e862014-05-22 16:05:19 -07008340 private static final String KEY_ACTIONS = "actions";
8341 private static final String KEY_FLAGS = "flags";
8342 private static final String KEY_DISPLAY_INTENT = "displayIntent";
8343 private static final String KEY_PAGES = "pages";
8344 private static final String KEY_BACKGROUND = "background";
8345 private static final String KEY_CONTENT_ICON = "contentIcon";
8346 private static final String KEY_CONTENT_ICON_GRAVITY = "contentIconGravity";
8347 private static final String KEY_CONTENT_ACTION_INDEX = "contentActionIndex";
8348 private static final String KEY_CUSTOM_SIZE_PRESET = "customSizePreset";
8349 private static final String KEY_CUSTOM_CONTENT_HEIGHT = "customContentHeight";
8350 private static final String KEY_GRAVITY = "gravity";
Griff Hazen5f2edfc2014-09-29 16:28:44 -07008351 private static final String KEY_HINT_SCREEN_TIMEOUT = "hintScreenTimeout";
Nadia Benbernou948627e2016-04-14 14:41:08 -04008352 private static final String KEY_DISMISSAL_ID = "dismissalId";
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04008353 private static final String KEY_BRIDGE_TAG = "bridgeTag";
Griff Hazen61a9e862014-05-22 16:05:19 -07008354
8355 // Flags bitwise-ored to mFlags
8356 private static final int FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE = 0x1;
8357 private static final int FLAG_HINT_HIDE_ICON = 1 << 1;
8358 private static final int FLAG_HINT_SHOW_BACKGROUND_ONLY = 1 << 2;
8359 private static final int FLAG_START_SCROLL_BOTTOM = 1 << 3;
Griff Hazen5f2edfc2014-09-29 16:28:44 -07008360 private static final int FLAG_HINT_AVOID_BACKGROUND_CLIPPING = 1 << 4;
Alex Hills4bcb06b2016-04-05 14:26:25 -04008361 private static final int FLAG_BIG_PICTURE_AMBIENT = 1 << 5;
Alex Hills9ab3a232016-04-05 14:54:56 -04008362 private static final int FLAG_HINT_CONTENT_INTENT_LAUNCHES_ACTIVITY = 1 << 6;
Griff Hazen61a9e862014-05-22 16:05:19 -07008363
8364 // Default value for flags integer
8365 private static final int DEFAULT_FLAGS = FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE;
8366
8367 private static final int DEFAULT_CONTENT_ICON_GRAVITY = Gravity.END;
8368 private static final int DEFAULT_GRAVITY = Gravity.BOTTOM;
8369
8370 private ArrayList<Action> mActions = new ArrayList<Action>();
8371 private int mFlags = DEFAULT_FLAGS;
8372 private PendingIntent mDisplayIntent;
8373 private ArrayList<Notification> mPages = new ArrayList<Notification>();
8374 private Bitmap mBackground;
8375 private int mContentIcon;
8376 private int mContentIconGravity = DEFAULT_CONTENT_ICON_GRAVITY;
8377 private int mContentActionIndex = UNSET_ACTION_INDEX;
8378 private int mCustomSizePreset = SIZE_DEFAULT;
8379 private int mCustomContentHeight;
8380 private int mGravity = DEFAULT_GRAVITY;
Griff Hazen5f2edfc2014-09-29 16:28:44 -07008381 private int mHintScreenTimeout;
Nadia Benbernou948627e2016-04-14 14:41:08 -04008382 private String mDismissalId;
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04008383 private String mBridgeTag;
Griff Hazen61a9e862014-05-22 16:05:19 -07008384
8385 /**
8386 * Create a {@link android.app.Notification.WearableExtender} with default
8387 * options.
8388 */
8389 public WearableExtender() {
8390 }
8391
8392 public WearableExtender(Notification notif) {
8393 Bundle wearableBundle = notif.extras.getBundle(EXTRA_WEARABLE_EXTENSIONS);
8394 if (wearableBundle != null) {
8395 List<Action> actions = wearableBundle.getParcelableArrayList(KEY_ACTIONS);
8396 if (actions != null) {
8397 mActions.addAll(actions);
8398 }
8399
8400 mFlags = wearableBundle.getInt(KEY_FLAGS, DEFAULT_FLAGS);
8401 mDisplayIntent = wearableBundle.getParcelable(KEY_DISPLAY_INTENT);
8402
8403 Notification[] pages = getNotificationArrayFromBundle(
8404 wearableBundle, KEY_PAGES);
8405 if (pages != null) {
8406 Collections.addAll(mPages, pages);
8407 }
8408
8409 mBackground = wearableBundle.getParcelable(KEY_BACKGROUND);
8410 mContentIcon = wearableBundle.getInt(KEY_CONTENT_ICON);
8411 mContentIconGravity = wearableBundle.getInt(KEY_CONTENT_ICON_GRAVITY,
8412 DEFAULT_CONTENT_ICON_GRAVITY);
8413 mContentActionIndex = wearableBundle.getInt(KEY_CONTENT_ACTION_INDEX,
8414 UNSET_ACTION_INDEX);
8415 mCustomSizePreset = wearableBundle.getInt(KEY_CUSTOM_SIZE_PRESET,
8416 SIZE_DEFAULT);
8417 mCustomContentHeight = wearableBundle.getInt(KEY_CUSTOM_CONTENT_HEIGHT);
8418 mGravity = wearableBundle.getInt(KEY_GRAVITY, DEFAULT_GRAVITY);
Griff Hazen5f2edfc2014-09-29 16:28:44 -07008419 mHintScreenTimeout = wearableBundle.getInt(KEY_HINT_SCREEN_TIMEOUT);
Nadia Benbernou948627e2016-04-14 14:41:08 -04008420 mDismissalId = wearableBundle.getString(KEY_DISMISSAL_ID);
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04008421 mBridgeTag = wearableBundle.getString(KEY_BRIDGE_TAG);
Griff Hazen61a9e862014-05-22 16:05:19 -07008422 }
8423 }
8424
8425 /**
8426 * Apply wearable extensions to a notification that is being built. This is typically
8427 * called by the {@link android.app.Notification.Builder#extend} method of
8428 * {@link android.app.Notification.Builder}.
8429 */
8430 @Override
8431 public Notification.Builder extend(Notification.Builder builder) {
8432 Bundle wearableBundle = new Bundle();
8433
8434 if (!mActions.isEmpty()) {
8435 wearableBundle.putParcelableArrayList(KEY_ACTIONS, mActions);
8436 }
8437 if (mFlags != DEFAULT_FLAGS) {
8438 wearableBundle.putInt(KEY_FLAGS, mFlags);
8439 }
8440 if (mDisplayIntent != null) {
8441 wearableBundle.putParcelable(KEY_DISPLAY_INTENT, mDisplayIntent);
8442 }
8443 if (!mPages.isEmpty()) {
8444 wearableBundle.putParcelableArray(KEY_PAGES, mPages.toArray(
8445 new Notification[mPages.size()]));
8446 }
8447 if (mBackground != null) {
8448 wearableBundle.putParcelable(KEY_BACKGROUND, mBackground);
8449 }
8450 if (mContentIcon != 0) {
8451 wearableBundle.putInt(KEY_CONTENT_ICON, mContentIcon);
8452 }
8453 if (mContentIconGravity != DEFAULT_CONTENT_ICON_GRAVITY) {
8454 wearableBundle.putInt(KEY_CONTENT_ICON_GRAVITY, mContentIconGravity);
8455 }
8456 if (mContentActionIndex != UNSET_ACTION_INDEX) {
8457 wearableBundle.putInt(KEY_CONTENT_ACTION_INDEX,
8458 mContentActionIndex);
8459 }
8460 if (mCustomSizePreset != SIZE_DEFAULT) {
8461 wearableBundle.putInt(KEY_CUSTOM_SIZE_PRESET, mCustomSizePreset);
8462 }
8463 if (mCustomContentHeight != 0) {
8464 wearableBundle.putInt(KEY_CUSTOM_CONTENT_HEIGHT, mCustomContentHeight);
8465 }
8466 if (mGravity != DEFAULT_GRAVITY) {
8467 wearableBundle.putInt(KEY_GRAVITY, mGravity);
8468 }
Griff Hazen5f2edfc2014-09-29 16:28:44 -07008469 if (mHintScreenTimeout != 0) {
8470 wearableBundle.putInt(KEY_HINT_SCREEN_TIMEOUT, mHintScreenTimeout);
8471 }
Nadia Benbernou948627e2016-04-14 14:41:08 -04008472 if (mDismissalId != null) {
8473 wearableBundle.putString(KEY_DISMISSAL_ID, mDismissalId);
8474 }
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04008475 if (mBridgeTag != null) {
8476 wearableBundle.putString(KEY_BRIDGE_TAG, mBridgeTag);
8477 }
Griff Hazen61a9e862014-05-22 16:05:19 -07008478
8479 builder.getExtras().putBundle(EXTRA_WEARABLE_EXTENSIONS, wearableBundle);
8480 return builder;
8481 }
8482
8483 @Override
8484 public WearableExtender clone() {
8485 WearableExtender that = new WearableExtender();
8486 that.mActions = new ArrayList<Action>(this.mActions);
8487 that.mFlags = this.mFlags;
8488 that.mDisplayIntent = this.mDisplayIntent;
8489 that.mPages = new ArrayList<Notification>(this.mPages);
8490 that.mBackground = this.mBackground;
8491 that.mContentIcon = this.mContentIcon;
8492 that.mContentIconGravity = this.mContentIconGravity;
8493 that.mContentActionIndex = this.mContentActionIndex;
8494 that.mCustomSizePreset = this.mCustomSizePreset;
8495 that.mCustomContentHeight = this.mCustomContentHeight;
8496 that.mGravity = this.mGravity;
Griff Hazen5f2edfc2014-09-29 16:28:44 -07008497 that.mHintScreenTimeout = this.mHintScreenTimeout;
Nadia Benbernou948627e2016-04-14 14:41:08 -04008498 that.mDismissalId = this.mDismissalId;
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04008499 that.mBridgeTag = this.mBridgeTag;
Griff Hazen61a9e862014-05-22 16:05:19 -07008500 return that;
8501 }
8502
8503 /**
8504 * Add a wearable action to this notification.
8505 *
8506 * <p>When wearable actions are added using this method, the set of actions that
8507 * show on a wearable device splits from devices that only show actions added
8508 * using {@link android.app.Notification.Builder#addAction}. This allows for customization
8509 * of which actions display on different devices.
8510 *
8511 * @param action the action to add to this notification
8512 * @return this object for method chaining
8513 * @see android.app.Notification.Action
8514 */
8515 public WearableExtender addAction(Action action) {
8516 mActions.add(action);
8517 return this;
8518 }
8519
8520 /**
8521 * Adds wearable actions to this notification.
8522 *
8523 * <p>When wearable actions are added using this method, the set of actions that
8524 * show on a wearable device splits from devices that only show actions added
8525 * using {@link android.app.Notification.Builder#addAction}. This allows for customization
8526 * of which actions display on different devices.
8527 *
8528 * @param actions the actions to add to this notification
8529 * @return this object for method chaining
8530 * @see android.app.Notification.Action
8531 */
8532 public WearableExtender addActions(List<Action> actions) {
8533 mActions.addAll(actions);
8534 return this;
8535 }
8536
8537 /**
8538 * Clear all wearable actions present on this builder.
8539 * @return this object for method chaining.
8540 * @see #addAction
8541 */
8542 public WearableExtender clearActions() {
8543 mActions.clear();
8544 return this;
8545 }
8546
8547 /**
8548 * Get the wearable actions present on this notification.
8549 */
8550 public List<Action> getActions() {
8551 return mActions;
8552 }
8553
8554 /**
8555 * Set an intent to launch inside of an activity view when displaying
Griff Hazen14f57992014-05-26 09:07:14 -07008556 * this notification. The {@link PendingIntent} provided should be for an activity.
8557 *
8558 * <pre class="prettyprint">
8559 * Intent displayIntent = new Intent(context, MyDisplayActivity.class);
8560 * PendingIntent displayPendingIntent = PendingIntent.getActivity(context,
8561 * 0, displayIntent, PendingIntent.FLAG_UPDATE_CURRENT);
8562 * Notification notif = new Notification.Builder(context)
8563 * .extend(new Notification.WearableExtender()
8564 * .setDisplayIntent(displayPendingIntent)
8565 * .setCustomSizePreset(Notification.WearableExtender.SIZE_MEDIUM))
8566 * .build();</pre>
8567 *
8568 * <p>The activity to launch needs to allow embedding, must be exported, and
Griff Hazen831ca9d2014-06-17 00:38:38 -07008569 * should have an empty task affinity. It is also recommended to use the device
8570 * default light theme.
Griff Hazen14f57992014-05-26 09:07:14 -07008571 *
8572 * <p>Example AndroidManifest.xml entry:
8573 * <pre class="prettyprint">
8574 * &lt;activity android:name=&quot;com.example.MyDisplayActivity&quot;
8575 * android:exported=&quot;true&quot;
8576 * android:allowEmbedded=&quot;true&quot;
Griff Hazen831ca9d2014-06-17 00:38:38 -07008577 * android:taskAffinity=&quot;&quot;
8578 * android:theme=&quot;@android:style/Theme.DeviceDefault.Light&quot; /&gt;</pre>
Griff Hazen61a9e862014-05-22 16:05:19 -07008579 *
8580 * @param intent the {@link PendingIntent} for an activity
8581 * @return this object for method chaining
8582 * @see android.app.Notification.WearableExtender#getDisplayIntent
Gus Prevasd7363752018-09-18 14:35:15 -04008583 * @deprecated Display intents are no longer supported.
Griff Hazen61a9e862014-05-22 16:05:19 -07008584 */
Gus Prevasd7363752018-09-18 14:35:15 -04008585 @Deprecated
Griff Hazen61a9e862014-05-22 16:05:19 -07008586 public WearableExtender setDisplayIntent(PendingIntent intent) {
8587 mDisplayIntent = intent;
8588 return this;
8589 }
8590
8591 /**
8592 * Get the intent to launch inside of an activity view when displaying this
8593 * notification. This {@code PendingIntent} should be for an activity.
Gus Prevasd7363752018-09-18 14:35:15 -04008594 *
8595 * @deprecated Display intents are no longer supported.
Griff Hazen61a9e862014-05-22 16:05:19 -07008596 */
Gus Prevasd7363752018-09-18 14:35:15 -04008597 @Deprecated
Griff Hazen61a9e862014-05-22 16:05:19 -07008598 public PendingIntent getDisplayIntent() {
8599 return mDisplayIntent;
8600 }
8601
8602 /**
8603 * Add an additional page of content to display with this notification. The current
8604 * notification forms the first page, and pages added using this function form
8605 * subsequent pages. This field can be used to separate a notification into multiple
8606 * sections.
8607 *
8608 * @param page the notification to add as another page
8609 * @return this object for method chaining
8610 * @see android.app.Notification.WearableExtender#getPages
Gus Prevasd7363752018-09-18 14:35:15 -04008611 * @deprecated Multiple content pages are no longer supported.
Griff Hazen61a9e862014-05-22 16:05:19 -07008612 */
Gus Prevasd7363752018-09-18 14:35:15 -04008613 @Deprecated
Griff Hazen61a9e862014-05-22 16:05:19 -07008614 public WearableExtender addPage(Notification page) {
8615 mPages.add(page);
8616 return this;
8617 }
8618
8619 /**
8620 * Add additional pages of content to display with this notification. The current
8621 * notification forms the first page, and pages added using this function form
8622 * subsequent pages. This field can be used to separate a notification into multiple
8623 * sections.
8624 *
8625 * @param pages a list of notifications
8626 * @return this object for method chaining
8627 * @see android.app.Notification.WearableExtender#getPages
Gus Prevasd7363752018-09-18 14:35:15 -04008628 * @deprecated Multiple content pages are no longer supported.
Griff Hazen61a9e862014-05-22 16:05:19 -07008629 */
Gus Prevasd7363752018-09-18 14:35:15 -04008630 @Deprecated
Griff Hazen61a9e862014-05-22 16:05:19 -07008631 public WearableExtender addPages(List<Notification> pages) {
8632 mPages.addAll(pages);
8633 return this;
8634 }
8635
8636 /**
8637 * Clear all additional pages present on this builder.
8638 * @return this object for method chaining.
8639 * @see #addPage
Gus Prevasd7363752018-09-18 14:35:15 -04008640 * @deprecated Multiple content pages are no longer supported.
Griff Hazen61a9e862014-05-22 16:05:19 -07008641 */
Gus Prevasd7363752018-09-18 14:35:15 -04008642 @Deprecated
Griff Hazen61a9e862014-05-22 16:05:19 -07008643 public WearableExtender clearPages() {
8644 mPages.clear();
8645 return this;
8646 }
8647
8648 /**
8649 * Get the array of additional pages of content for displaying this notification. The
8650 * current notification forms the first page, and elements within this array form
8651 * subsequent pages. This field can be used to separate a notification into multiple
8652 * sections.
8653 * @return the pages for this notification
Gus Prevasd7363752018-09-18 14:35:15 -04008654 * @deprecated Multiple content pages are no longer supported.
Griff Hazen61a9e862014-05-22 16:05:19 -07008655 */
Gus Prevasd7363752018-09-18 14:35:15 -04008656 @Deprecated
Griff Hazen61a9e862014-05-22 16:05:19 -07008657 public List<Notification> getPages() {
8658 return mPages;
8659 }
8660
8661 /**
8662 * Set a background image to be displayed behind the notification content.
8663 * Contrary to the {@link android.app.Notification.BigPictureStyle}, this background
8664 * will work with any notification style.
8665 *
8666 * @param background the background bitmap
8667 * @return this object for method chaining
8668 * @see android.app.Notification.WearableExtender#getBackground
Gus Prevasd7363752018-09-18 14:35:15 -04008669 * @deprecated Background images are no longer supported.
Griff Hazen61a9e862014-05-22 16:05:19 -07008670 */
Gus Prevasd7363752018-09-18 14:35:15 -04008671 @Deprecated
Griff Hazen61a9e862014-05-22 16:05:19 -07008672 public WearableExtender setBackground(Bitmap background) {
8673 mBackground = background;
8674 return this;
8675 }
8676
8677 /**
8678 * Get a background image to be displayed behind the notification content.
8679 * Contrary to the {@link android.app.Notification.BigPictureStyle}, this background
8680 * will work with any notification style.
8681 *
8682 * @return the background image
8683 * @see android.app.Notification.WearableExtender#setBackground
Gus Prevasd7363752018-09-18 14:35:15 -04008684 * @deprecated Background images are no longer supported.
Griff Hazen61a9e862014-05-22 16:05:19 -07008685 */
Gus Prevasd7363752018-09-18 14:35:15 -04008686 @Deprecated
Griff Hazen61a9e862014-05-22 16:05:19 -07008687 public Bitmap getBackground() {
8688 return mBackground;
8689 }
8690
8691 /**
8692 * Set an icon that goes with the content of this notification.
8693 */
Gus Prevasb5f9cf52018-02-21 14:21:50 -05008694 @Deprecated
Griff Hazen61a9e862014-05-22 16:05:19 -07008695 public WearableExtender setContentIcon(int icon) {
8696 mContentIcon = icon;
8697 return this;
8698 }
8699
8700 /**
8701 * Get an icon that goes with the content of this notification.
8702 */
Gus Prevasb5f9cf52018-02-21 14:21:50 -05008703 @Deprecated
Griff Hazen61a9e862014-05-22 16:05:19 -07008704 public int getContentIcon() {
8705 return mContentIcon;
8706 }
8707
8708 /**
8709 * Set the gravity that the content icon should have within the notification display.
8710 * Supported values include {@link android.view.Gravity#START} and
8711 * {@link android.view.Gravity#END}. The default value is {@link android.view.Gravity#END}.
8712 * @see #setContentIcon
8713 */
Gus Prevasb5f9cf52018-02-21 14:21:50 -05008714 @Deprecated
Griff Hazen61a9e862014-05-22 16:05:19 -07008715 public WearableExtender setContentIconGravity(int contentIconGravity) {
8716 mContentIconGravity = contentIconGravity;
8717 return this;
8718 }
8719
8720 /**
8721 * Get the gravity that the content icon should have within the notification display.
8722 * Supported values include {@link android.view.Gravity#START} and
8723 * {@link android.view.Gravity#END}. The default value is {@link android.view.Gravity#END}.
8724 * @see #getContentIcon
8725 */
Gus Prevasb5f9cf52018-02-21 14:21:50 -05008726 @Deprecated
Griff Hazen61a9e862014-05-22 16:05:19 -07008727 public int getContentIconGravity() {
8728 return mContentIconGravity;
8729 }
8730
8731 /**
Gus Prevasd7363752018-09-18 14:35:15 -04008732 * Set an action from this notification's actions as the primary action. If the action has a
8733 * {@link RemoteInput} associated with it, shortcuts to the options for that input are shown
8734 * directly on the notification.
Griff Hazen14f57992014-05-26 09:07:14 -07008735 *
Gus Prevasd7363752018-09-18 14:35:15 -04008736 * @param actionIndex The index of the primary action.
Griff Hazen14f57992014-05-26 09:07:14 -07008737 * If wearable actions were added to the main notification, this index
8738 * will apply to that list, otherwise it will apply to the regular
8739 * actions list.
Griff Hazen61a9e862014-05-22 16:05:19 -07008740 */
8741 public WearableExtender setContentAction(int actionIndex) {
8742 mContentActionIndex = actionIndex;
8743 return this;
8744 }
8745
8746 /**
Gus Prevasd7363752018-09-18 14:35:15 -04008747 * Get the index of the notification action, if any, that was specified as the primary
8748 * action.
Griff Hazen14f57992014-05-26 09:07:14 -07008749 *
8750 * <p>If wearable specific actions were added to the main notification, this index will
8751 * apply to that list, otherwise it will apply to the regular actions list.
Griff Hazenca48d352014-05-28 22:37:13 -07008752 *
8753 * @return the action index or {@link #UNSET_ACTION_INDEX} if no action was selected.
Griff Hazen61a9e862014-05-22 16:05:19 -07008754 */
8755 public int getContentAction() {
8756 return mContentActionIndex;
8757 }
8758
8759 /**
8760 * Set the gravity that this notification should have within the available viewport space.
8761 * Supported values include {@link android.view.Gravity#TOP},
8762 * {@link android.view.Gravity#CENTER_VERTICAL} and {@link android.view.Gravity#BOTTOM}.
8763 * The default value is {@link android.view.Gravity#BOTTOM}.
8764 */
Gus Prevasb5f9cf52018-02-21 14:21:50 -05008765 @Deprecated
Griff Hazen61a9e862014-05-22 16:05:19 -07008766 public WearableExtender setGravity(int gravity) {
8767 mGravity = gravity;
8768 return this;
8769 }
8770
8771 /**
8772 * Get the gravity that this notification should have within the available viewport space.
8773 * Supported values include {@link android.view.Gravity#TOP},
8774 * {@link android.view.Gravity#CENTER_VERTICAL} and {@link android.view.Gravity#BOTTOM}.
8775 * The default value is {@link android.view.Gravity#BOTTOM}.
8776 */
Gus Prevasb5f9cf52018-02-21 14:21:50 -05008777 @Deprecated
Griff Hazen61a9e862014-05-22 16:05:19 -07008778 public int getGravity() {
8779 return mGravity;
8780 }
8781
8782 /**
8783 * Set the custom size preset for the display of this notification out of the available
8784 * presets found in {@link android.app.Notification.WearableExtender}, e.g.
8785 * {@link #SIZE_LARGE}.
8786 * <p>Some custom size presets are only applicable for custom display notifications created
8787 * using {@link android.app.Notification.WearableExtender#setDisplayIntent}. Check the
8788 * documentation for the preset in question. See also
8789 * {@link #setCustomContentHeight} and {@link #getCustomSizePreset}.
8790 */
Gus Prevasb5f9cf52018-02-21 14:21:50 -05008791 @Deprecated
Griff Hazen61a9e862014-05-22 16:05:19 -07008792 public WearableExtender setCustomSizePreset(int sizePreset) {
8793 mCustomSizePreset = sizePreset;
8794 return this;
8795 }
8796
8797 /**
8798 * Get the custom size preset for the display of this notification out of the available
8799 * presets found in {@link android.app.Notification.WearableExtender}, e.g.
8800 * {@link #SIZE_LARGE}.
8801 * <p>Some custom size presets are only applicable for custom display notifications created
8802 * using {@link #setDisplayIntent}. Check the documentation for the preset in question.
8803 * See also {@link #setCustomContentHeight} and {@link #setCustomSizePreset}.
8804 */
Gus Prevasb5f9cf52018-02-21 14:21:50 -05008805 @Deprecated
Griff Hazen61a9e862014-05-22 16:05:19 -07008806 public int getCustomSizePreset() {
8807 return mCustomSizePreset;
8808 }
8809
8810 /**
8811 * Set the custom height in pixels for the display of this notification's content.
8812 * <p>This option is only available for custom display notifications created
8813 * using {@link android.app.Notification.WearableExtender#setDisplayIntent}. See also
8814 * {@link android.app.Notification.WearableExtender#setCustomSizePreset} and
8815 * {@link #getCustomContentHeight}.
8816 */
Gus Prevasb5f9cf52018-02-21 14:21:50 -05008817 @Deprecated
Griff Hazen61a9e862014-05-22 16:05:19 -07008818 public WearableExtender setCustomContentHeight(int height) {
8819 mCustomContentHeight = height;
8820 return this;
8821 }
8822
8823 /**
8824 * Get the custom height in pixels for the display of this notification's content.
8825 * <p>This option is only available for custom display notifications created
8826 * using {@link #setDisplayIntent}. See also {@link #setCustomSizePreset} and
8827 * {@link #setCustomContentHeight}.
8828 */
Gus Prevasb5f9cf52018-02-21 14:21:50 -05008829 @Deprecated
Griff Hazen61a9e862014-05-22 16:05:19 -07008830 public int getCustomContentHeight() {
8831 return mCustomContentHeight;
8832 }
8833
8834 /**
8835 * Set whether the scrolling position for the contents of this notification should start
8836 * at the bottom of the contents instead of the top when the contents are too long to
8837 * display within the screen. Default is false (start scroll at the top).
8838 */
8839 public WearableExtender setStartScrollBottom(boolean startScrollBottom) {
8840 setFlag(FLAG_START_SCROLL_BOTTOM, startScrollBottom);
8841 return this;
8842 }
8843
8844 /**
8845 * Get whether the scrolling position for the contents of this notification should start
8846 * at the bottom of the contents instead of the top when the contents are too long to
8847 * display within the screen. Default is false (start scroll at the top).
8848 */
8849 public boolean getStartScrollBottom() {
8850 return (mFlags & FLAG_START_SCROLL_BOTTOM) != 0;
8851 }
8852
8853 /**
8854 * Set whether the content intent is available when the wearable device is not connected
8855 * to a companion device. The user can still trigger this intent when the wearable device
8856 * is offline, but a visual hint will indicate that the content intent may not be available.
8857 * Defaults to true.
8858 */
8859 public WearableExtender setContentIntentAvailableOffline(
8860 boolean contentIntentAvailableOffline) {
8861 setFlag(FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE, contentIntentAvailableOffline);
8862 return this;
8863 }
8864
8865 /**
8866 * Get whether the content intent is available when the wearable device is not connected
8867 * to a companion device. The user can still trigger this intent when the wearable device
8868 * is offline, but a visual hint will indicate that the content intent may not be available.
8869 * Defaults to true.
8870 */
8871 public boolean getContentIntentAvailableOffline() {
8872 return (mFlags & FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE) != 0;
8873 }
8874
8875 /**
8876 * Set a hint that this notification's icon should not be displayed.
8877 * @param hintHideIcon {@code true} to hide the icon, {@code false} otherwise.
8878 * @return this object for method chaining
8879 */
Gus Prevasb5f9cf52018-02-21 14:21:50 -05008880 @Deprecated
Griff Hazen61a9e862014-05-22 16:05:19 -07008881 public WearableExtender setHintHideIcon(boolean hintHideIcon) {
8882 setFlag(FLAG_HINT_HIDE_ICON, hintHideIcon);
8883 return this;
8884 }
8885
8886 /**
8887 * Get a hint that this notification's icon should not be displayed.
8888 * @return {@code true} if this icon should not be displayed, false otherwise.
8889 * The default value is {@code false} if this was never set.
8890 */
Gus Prevasb5f9cf52018-02-21 14:21:50 -05008891 @Deprecated
Griff Hazen61a9e862014-05-22 16:05:19 -07008892 public boolean getHintHideIcon() {
8893 return (mFlags & FLAG_HINT_HIDE_ICON) != 0;
8894 }
8895
8896 /**
8897 * Set a visual hint that only the background image of this notification should be
8898 * displayed, and other semantic content should be hidden. This hint is only applicable
8899 * to sub-pages added using {@link #addPage}.
8900 */
Gus Prevasb5f9cf52018-02-21 14:21:50 -05008901 @Deprecated
Griff Hazen61a9e862014-05-22 16:05:19 -07008902 public WearableExtender setHintShowBackgroundOnly(boolean hintShowBackgroundOnly) {
8903 setFlag(FLAG_HINT_SHOW_BACKGROUND_ONLY, hintShowBackgroundOnly);
8904 return this;
8905 }
8906
8907 /**
8908 * Get a visual hint that only the background image of this notification should be
8909 * displayed, and other semantic content should be hidden. This hint is only applicable
8910 * to sub-pages added using {@link android.app.Notification.WearableExtender#addPage}.
8911 */
Gus Prevasb5f9cf52018-02-21 14:21:50 -05008912 @Deprecated
Griff Hazen61a9e862014-05-22 16:05:19 -07008913 public boolean getHintShowBackgroundOnly() {
8914 return (mFlags & FLAG_HINT_SHOW_BACKGROUND_ONLY) != 0;
8915 }
8916
Griff Hazen5f2edfc2014-09-29 16:28:44 -07008917 /**
Griff Hazen9c5be4e2014-11-17 17:47:50 -08008918 * Set a hint that this notification's background should not be clipped if possible,
8919 * and should instead be resized to fully display on the screen, retaining the aspect
8920 * ratio of the image. This can be useful for images like barcodes or qr codes.
Griff Hazen5f2edfc2014-09-29 16:28:44 -07008921 * @param hintAvoidBackgroundClipping {@code true} to avoid clipping if possible.
8922 * @return this object for method chaining
8923 */
Gus Prevasb5f9cf52018-02-21 14:21:50 -05008924 @Deprecated
Griff Hazen5f2edfc2014-09-29 16:28:44 -07008925 public WearableExtender setHintAvoidBackgroundClipping(
8926 boolean hintAvoidBackgroundClipping) {
8927 setFlag(FLAG_HINT_AVOID_BACKGROUND_CLIPPING, hintAvoidBackgroundClipping);
8928 return this;
8929 }
8930
8931 /**
Griff Hazen9c5be4e2014-11-17 17:47:50 -08008932 * Get a hint that this notification's background should not be clipped if possible,
8933 * and should instead be resized to fully display on the screen, retaining the aspect
8934 * ratio of the image. This can be useful for images like barcodes or qr codes.
Griff Hazen5f2edfc2014-09-29 16:28:44 -07008935 * @return {@code true} if it's ok if the background is clipped on the screen, false
8936 * otherwise. The default value is {@code false} if this was never set.
8937 */
Gus Prevasb5f9cf52018-02-21 14:21:50 -05008938 @Deprecated
Griff Hazen5f2edfc2014-09-29 16:28:44 -07008939 public boolean getHintAvoidBackgroundClipping() {
8940 return (mFlags & FLAG_HINT_AVOID_BACKGROUND_CLIPPING) != 0;
8941 }
8942
8943 /**
8944 * Set a hint that the screen should remain on for at least this duration when
8945 * this notification is displayed on the screen.
8946 * @param timeout The requested screen timeout in milliseconds. Can also be either
8947 * {@link #SCREEN_TIMEOUT_SHORT} or {@link #SCREEN_TIMEOUT_LONG}.
8948 * @return this object for method chaining
8949 */
Gus Prevasb5f9cf52018-02-21 14:21:50 -05008950 @Deprecated
Griff Hazen5f2edfc2014-09-29 16:28:44 -07008951 public WearableExtender setHintScreenTimeout(int timeout) {
8952 mHintScreenTimeout = timeout;
8953 return this;
8954 }
8955
8956 /**
8957 * Get the duration, in milliseconds, that the screen should remain on for
8958 * when this notification is displayed.
8959 * @return the duration in milliseconds if > 0, or either one of the sentinel values
8960 * {@link #SCREEN_TIMEOUT_SHORT} or {@link #SCREEN_TIMEOUT_LONG}.
8961 */
Gus Prevasb5f9cf52018-02-21 14:21:50 -05008962 @Deprecated
Griff Hazen5f2edfc2014-09-29 16:28:44 -07008963 public int getHintScreenTimeout() {
8964 return mHintScreenTimeout;
8965 }
8966
Alex Hills9ab3a232016-04-05 14:54:56 -04008967 /**
Alex Hills4bcb06b2016-04-05 14:26:25 -04008968 * Set a hint that this notification's {@link BigPictureStyle} (if present) should be
8969 * converted to low-bit and displayed in ambient mode, especially useful for barcodes and
8970 * qr codes, as well as other simple black-and-white tickets.
8971 * @param hintAmbientBigPicture {@code true} to enable converstion and ambient.
8972 * @return this object for method chaining
Gus Prevasd7363752018-09-18 14:35:15 -04008973 * @deprecated This feature is no longer supported.
Alex Hills4bcb06b2016-04-05 14:26:25 -04008974 */
Gus Prevasd7363752018-09-18 14:35:15 -04008975 @Deprecated
Alex Hills4bcb06b2016-04-05 14:26:25 -04008976 public WearableExtender setHintAmbientBigPicture(boolean hintAmbientBigPicture) {
8977 setFlag(FLAG_BIG_PICTURE_AMBIENT, hintAmbientBigPicture);
8978 return this;
8979 }
8980
8981 /**
8982 * Get a hint that this notification's {@link BigPictureStyle} (if present) should be
8983 * converted to low-bit and displayed in ambient mode, especially useful for barcodes and
8984 * qr codes, as well as other simple black-and-white tickets.
8985 * @return {@code true} if it should be displayed in ambient, false otherwise
8986 * otherwise. The default value is {@code false} if this was never set.
Gus Prevasd7363752018-09-18 14:35:15 -04008987 * @deprecated This feature is no longer supported.
Alex Hills4bcb06b2016-04-05 14:26:25 -04008988 */
Gus Prevasd7363752018-09-18 14:35:15 -04008989 @Deprecated
Alex Hills4bcb06b2016-04-05 14:26:25 -04008990 public boolean getHintAmbientBigPicture() {
8991 return (mFlags & FLAG_BIG_PICTURE_AMBIENT) != 0;
8992 }
8993
8994 /**
Alex Hills9ab3a232016-04-05 14:54:56 -04008995 * Set a hint that this notification's content intent will launch an {@link Activity}
8996 * directly, telling the platform that it can generate the appropriate transitions.
8997 * @param hintContentIntentLaunchesActivity {@code true} if the content intent will launch
8998 * an activity and transitions should be generated, false otherwise.
8999 * @return this object for method chaining
9000 */
9001 public WearableExtender setHintContentIntentLaunchesActivity(
9002 boolean hintContentIntentLaunchesActivity) {
9003 setFlag(FLAG_HINT_CONTENT_INTENT_LAUNCHES_ACTIVITY, hintContentIntentLaunchesActivity);
9004 return this;
9005 }
9006
9007 /**
9008 * Get a hint that this notification's content intent will launch an {@link Activity}
9009 * directly, telling the platform that it can generate the appropriate transitions
9010 * @return {@code true} if the content intent will launch an activity and transitions should
9011 * be generated, false otherwise. The default value is {@code false} if this was never set.
9012 */
9013 public boolean getHintContentIntentLaunchesActivity() {
9014 return (mFlags & FLAG_HINT_CONTENT_INTENT_LAUNCHES_ACTIVITY) != 0;
9015 }
9016
Nadia Benbernou948627e2016-04-14 14:41:08 -04009017 /**
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04009018 * Sets the dismissal id for this notification. If a notification is posted with a
9019 * dismissal id, then when that notification is canceled, notifications on other wearables
9020 * and the paired Android phone having that same dismissal id will also be canceled. See
Nadia Benbernou948627e2016-04-14 14:41:08 -04009021 * <a href="{@docRoot}wear/notifications/index.html">Adding Wearable Features to
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04009022 * Notifications</a> for more information.
Nadia Benbernou948627e2016-04-14 14:41:08 -04009023 * @param dismissalId the dismissal id of the notification.
9024 * @return this object for method chaining
9025 */
9026 public WearableExtender setDismissalId(String dismissalId) {
9027 mDismissalId = dismissalId;
9028 return this;
9029 }
9030
9031 /**
9032 * Returns the dismissal id of the notification.
9033 * @return the dismissal id of the notification or null if it has not been set.
9034 */
9035 public String getDismissalId() {
9036 return mDismissalId;
9037 }
9038
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04009039 /**
9040 * Sets a bridge tag for this notification. A bridge tag can be set for notifications
9041 * posted from a phone to provide finer-grained control on what notifications are bridged
9042 * to wearables. See <a href="{@docRoot}wear/notifications/index.html">Adding Wearable
9043 * Features to Notifications</a> for more information.
9044 * @param bridgeTag the bridge tag of the notification.
9045 * @return this object for method chaining
9046 */
9047 public WearableExtender setBridgeTag(String bridgeTag) {
9048 mBridgeTag = bridgeTag;
9049 return this;
9050 }
9051
9052 /**
9053 * Returns the bridge tag of the notification.
9054 * @return the bridge tag or null if not present.
9055 */
9056 public String getBridgeTag() {
9057 return mBridgeTag;
9058 }
9059
Griff Hazen61a9e862014-05-22 16:05:19 -07009060 private void setFlag(int mask, boolean value) {
9061 if (value) {
9062 mFlags |= mask;
9063 } else {
9064 mFlags &= ~mask;
9065 }
9066 }
9067 }
9068
9069 /**
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08009070 * <p>Helper class to add Android Auto extensions to notifications. To create a notification
9071 * with car extensions:
9072 *
9073 * <ol>
9074 * <li>Create an {@link Notification.Builder}, setting any desired
9075 * properties.
9076 * <li>Create a {@link CarExtender}.
9077 * <li>Set car-specific properties using the {@code add} and {@code set} methods of
9078 * {@link CarExtender}.
9079 * <li>Call {@link Notification.Builder#extend(Notification.Extender)}
9080 * to apply the extensions to a notification.
9081 * </ol>
9082 *
9083 * <pre class="prettyprint">
9084 * Notification notification = new Notification.Builder(context)
9085 * ...
9086 * .extend(new CarExtender()
9087 * .set*(...))
9088 * .build();
9089 * </pre>
9090 *
9091 * <p>Car extensions can be accessed on an existing notification by using the
9092 * {@code CarExtender(Notification)} constructor, and then using the {@code get} methods
9093 * to access values.
9094 */
9095 public static final class CarExtender implements Extender {
9096 private static final String TAG = "CarExtender";
9097
9098 private static final String EXTRA_CAR_EXTENDER = "android.car.EXTENSIONS";
9099 private static final String EXTRA_LARGE_ICON = "large_icon";
9100 private static final String EXTRA_CONVERSATION = "car_conversation";
9101 private static final String EXTRA_COLOR = "app_color";
9102
9103 private Bitmap mLargeIcon;
9104 private UnreadConversation mUnreadConversation;
9105 private int mColor = Notification.COLOR_DEFAULT;
9106
9107 /**
9108 * Create a {@link CarExtender} with default options.
9109 */
9110 public CarExtender() {
9111 }
9112
9113 /**
9114 * Create a {@link CarExtender} from the CarExtender options of an existing Notification.
9115 *
9116 * @param notif The notification from which to copy options.
9117 */
9118 public CarExtender(Notification notif) {
9119 Bundle carBundle = notif.extras == null ?
9120 null : notif.extras.getBundle(EXTRA_CAR_EXTENDER);
9121 if (carBundle != null) {
9122 mLargeIcon = carBundle.getParcelable(EXTRA_LARGE_ICON);
9123 mColor = carBundle.getInt(EXTRA_COLOR, Notification.COLOR_DEFAULT);
9124
9125 Bundle b = carBundle.getBundle(EXTRA_CONVERSATION);
9126 mUnreadConversation = UnreadConversation.getUnreadConversationFromBundle(b);
9127 }
9128 }
9129
9130 /**
9131 * Apply car extensions to a notification that is being built. This is typically called by
9132 * the {@link Notification.Builder#extend(Notification.Extender)}
9133 * method of {@link Notification.Builder}.
9134 */
9135 @Override
9136 public Notification.Builder extend(Notification.Builder builder) {
9137 Bundle carExtensions = new Bundle();
9138
9139 if (mLargeIcon != null) {
9140 carExtensions.putParcelable(EXTRA_LARGE_ICON, mLargeIcon);
9141 }
9142 if (mColor != Notification.COLOR_DEFAULT) {
9143 carExtensions.putInt(EXTRA_COLOR, mColor);
9144 }
9145
9146 if (mUnreadConversation != null) {
9147 Bundle b = mUnreadConversation.getBundleForUnreadConversation();
9148 carExtensions.putBundle(EXTRA_CONVERSATION, b);
9149 }
9150
9151 builder.getExtras().putBundle(EXTRA_CAR_EXTENDER, carExtensions);
9152 return builder;
9153 }
9154
9155 /**
9156 * Sets the accent color to use when Android Auto presents the notification.
9157 *
9158 * Android Auto uses the color set with {@link Notification.Builder#setColor(int)}
9159 * to accent the displayed notification. However, not all colors are acceptable in an
9160 * automotive setting. This method can be used to override the color provided in the
9161 * notification in such a situation.
9162 */
Tor Norbye80756e32015-03-02 09:39:27 -08009163 public CarExtender setColor(@ColorInt int color) {
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08009164 mColor = color;
9165 return this;
9166 }
9167
9168 /**
9169 * Gets the accent color.
9170 *
Julia Reynoldsd9228f12015-10-20 10:37:27 -04009171 * @see #setColor
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08009172 */
Tor Norbye80756e32015-03-02 09:39:27 -08009173 @ColorInt
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08009174 public int getColor() {
9175 return mColor;
9176 }
9177
9178 /**
9179 * Sets the large icon of the car notification.
9180 *
9181 * If no large icon is set in the extender, Android Auto will display the icon
9182 * specified by {@link Notification.Builder#setLargeIcon(android.graphics.Bitmap)}
9183 *
9184 * @param largeIcon The large icon to use in the car notification.
9185 * @return This object for method chaining.
9186 */
9187 public CarExtender setLargeIcon(Bitmap largeIcon) {
9188 mLargeIcon = largeIcon;
9189 return this;
9190 }
9191
9192 /**
9193 * Gets the large icon used in this car notification, or null if no icon has been set.
9194 *
9195 * @return The large icon for the car notification.
9196 * @see CarExtender#setLargeIcon
9197 */
9198 public Bitmap getLargeIcon() {
9199 return mLargeIcon;
9200 }
9201
9202 /**
9203 * Sets the unread conversation in a message notification.
9204 *
9205 * @param unreadConversation The unread part of the conversation this notification conveys.
9206 * @return This object for method chaining.
9207 */
9208 public CarExtender setUnreadConversation(UnreadConversation unreadConversation) {
9209 mUnreadConversation = unreadConversation;
9210 return this;
9211 }
9212
9213 /**
9214 * Returns the unread conversation conveyed by this notification.
9215 * @see #setUnreadConversation(UnreadConversation)
9216 */
9217 public UnreadConversation getUnreadConversation() {
9218 return mUnreadConversation;
9219 }
9220
9221 /**
9222 * A class which holds the unread messages from a conversation.
9223 */
9224 public static class UnreadConversation {
9225 private static final String KEY_AUTHOR = "author";
9226 private static final String KEY_TEXT = "text";
9227 private static final String KEY_MESSAGES = "messages";
9228 private static final String KEY_REMOTE_INPUT = "remote_input";
9229 private static final String KEY_ON_REPLY = "on_reply";
9230 private static final String KEY_ON_READ = "on_read";
9231 private static final String KEY_PARTICIPANTS = "participants";
9232 private static final String KEY_TIMESTAMP = "timestamp";
9233
9234 private final String[] mMessages;
9235 private final RemoteInput mRemoteInput;
9236 private final PendingIntent mReplyPendingIntent;
9237 private final PendingIntent mReadPendingIntent;
9238 private final String[] mParticipants;
9239 private final long mLatestTimestamp;
9240
9241 UnreadConversation(String[] messages, RemoteInput remoteInput,
9242 PendingIntent replyPendingIntent, PendingIntent readPendingIntent,
9243 String[] participants, long latestTimestamp) {
9244 mMessages = messages;
9245 mRemoteInput = remoteInput;
9246 mReadPendingIntent = readPendingIntent;
9247 mReplyPendingIntent = replyPendingIntent;
9248 mParticipants = participants;
9249 mLatestTimestamp = latestTimestamp;
9250 }
9251
9252 /**
9253 * Gets the list of messages conveyed by this notification.
9254 */
9255 public String[] getMessages() {
9256 return mMessages;
9257 }
9258
9259 /**
9260 * Gets the remote input that will be used to convey the response to a message list, or
9261 * null if no such remote input exists.
9262 */
9263 public RemoteInput getRemoteInput() {
9264 return mRemoteInput;
9265 }
9266
9267 /**
9268 * Gets the pending intent that will be triggered when the user replies to this
9269 * notification.
9270 */
9271 public PendingIntent getReplyPendingIntent() {
9272 return mReplyPendingIntent;
9273 }
9274
9275 /**
9276 * Gets the pending intent that Android Auto will send after it reads aloud all messages
9277 * in this object's message list.
9278 */
9279 public PendingIntent getReadPendingIntent() {
9280 return mReadPendingIntent;
9281 }
9282
9283 /**
9284 * Gets the participants in the conversation.
9285 */
9286 public String[] getParticipants() {
9287 return mParticipants;
9288 }
9289
9290 /**
9291 * Gets the firs participant in the conversation.
9292 */
9293 public String getParticipant() {
9294 return mParticipants.length > 0 ? mParticipants[0] : null;
9295 }
9296
9297 /**
9298 * Gets the timestamp of the conversation.
9299 */
9300 public long getLatestTimestamp() {
9301 return mLatestTimestamp;
9302 }
9303
9304 Bundle getBundleForUnreadConversation() {
9305 Bundle b = new Bundle();
9306 String author = null;
9307 if (mParticipants != null && mParticipants.length > 1) {
9308 author = mParticipants[0];
9309 }
9310 Parcelable[] messages = new Parcelable[mMessages.length];
9311 for (int i = 0; i < messages.length; i++) {
9312 Bundle m = new Bundle();
9313 m.putString(KEY_TEXT, mMessages[i]);
9314 m.putString(KEY_AUTHOR, author);
9315 messages[i] = m;
9316 }
9317 b.putParcelableArray(KEY_MESSAGES, messages);
9318 if (mRemoteInput != null) {
9319 b.putParcelable(KEY_REMOTE_INPUT, mRemoteInput);
9320 }
9321 b.putParcelable(KEY_ON_REPLY, mReplyPendingIntent);
9322 b.putParcelable(KEY_ON_READ, mReadPendingIntent);
9323 b.putStringArray(KEY_PARTICIPANTS, mParticipants);
9324 b.putLong(KEY_TIMESTAMP, mLatestTimestamp);
9325 return b;
9326 }
9327
9328 static UnreadConversation getUnreadConversationFromBundle(Bundle b) {
9329 if (b == null) {
9330 return null;
9331 }
9332 Parcelable[] parcelableMessages = b.getParcelableArray(KEY_MESSAGES);
9333 String[] messages = null;
9334 if (parcelableMessages != null) {
9335 String[] tmp = new String[parcelableMessages.length];
9336 boolean success = true;
9337 for (int i = 0; i < tmp.length; i++) {
9338 if (!(parcelableMessages[i] instanceof Bundle)) {
9339 success = false;
9340 break;
9341 }
9342 tmp[i] = ((Bundle) parcelableMessages[i]).getString(KEY_TEXT);
9343 if (tmp[i] == null) {
9344 success = false;
9345 break;
9346 }
9347 }
9348 if (success) {
9349 messages = tmp;
9350 } else {
9351 return null;
9352 }
9353 }
9354
9355 PendingIntent onRead = b.getParcelable(KEY_ON_READ);
9356 PendingIntent onReply = b.getParcelable(KEY_ON_REPLY);
9357
9358 RemoteInput remoteInput = b.getParcelable(KEY_REMOTE_INPUT);
9359
9360 String[] participants = b.getStringArray(KEY_PARTICIPANTS);
9361 if (participants == null || participants.length != 1) {
9362 return null;
9363 }
9364
9365 return new UnreadConversation(messages,
9366 remoteInput,
9367 onReply,
9368 onRead,
9369 participants, b.getLong(KEY_TIMESTAMP));
9370 }
9371 };
9372
9373 /**
9374 * Builder class for {@link CarExtender.UnreadConversation} objects.
9375 */
9376 public static class Builder {
9377 private final List<String> mMessages = new ArrayList<String>();
9378 private final String mParticipant;
9379 private RemoteInput mRemoteInput;
9380 private PendingIntent mReadPendingIntent;
9381 private PendingIntent mReplyPendingIntent;
9382 private long mLatestTimestamp;
9383
9384 /**
9385 * Constructs a new builder for {@link CarExtender.UnreadConversation}.
9386 *
9387 * @param name The name of the other participant in the conversation.
9388 */
9389 public Builder(String name) {
9390 mParticipant = name;
9391 }
9392
9393 /**
9394 * Appends a new unread message to the list of messages for this conversation.
9395 *
9396 * The messages should be added from oldest to newest.
9397 *
9398 * @param message The text of the new unread message.
9399 * @return This object for method chaining.
9400 */
9401 public Builder addMessage(String message) {
9402 mMessages.add(message);
9403 return this;
9404 }
9405
9406 /**
9407 * Sets the pending intent and remote input which will convey the reply to this
9408 * notification.
9409 *
9410 * @param pendingIntent The pending intent which will be triggered on a reply.
9411 * @param remoteInput The remote input parcelable which will carry the reply.
9412 * @return This object for method chaining.
9413 *
9414 * @see CarExtender.UnreadConversation#getRemoteInput
9415 * @see CarExtender.UnreadConversation#getReplyPendingIntent
9416 */
9417 public Builder setReplyAction(
9418 PendingIntent pendingIntent, RemoteInput remoteInput) {
9419 mRemoteInput = remoteInput;
9420 mReplyPendingIntent = pendingIntent;
9421
9422 return this;
9423 }
9424
9425 /**
9426 * Sets the pending intent that will be sent once the messages in this notification
9427 * are read.
9428 *
9429 * @param pendingIntent The pending intent to use.
9430 * @return This object for method chaining.
9431 */
9432 public Builder setReadPendingIntent(PendingIntent pendingIntent) {
9433 mReadPendingIntent = pendingIntent;
9434 return this;
9435 }
9436
9437 /**
9438 * Sets the timestamp of the most recent message in an unread conversation.
9439 *
9440 * If a messaging notification has been posted by your application and has not
9441 * yet been cancelled, posting a later notification with the same id and tag
9442 * but without a newer timestamp may result in Android Auto not displaying a
9443 * heads up notification for the later notification.
9444 *
9445 * @param timestamp The timestamp of the most recent message in the conversation.
9446 * @return This object for method chaining.
9447 */
9448 public Builder setLatestTimestamp(long timestamp) {
9449 mLatestTimestamp = timestamp;
9450 return this;
9451 }
9452
9453 /**
9454 * Builds a new unread conversation object.
9455 *
9456 * @return The new unread conversation object.
9457 */
9458 public UnreadConversation build() {
9459 String[] messages = mMessages.toArray(new String[mMessages.size()]);
9460 String[] participants = { mParticipant };
9461 return new UnreadConversation(messages, mRemoteInput, mReplyPendingIntent,
9462 mReadPendingIntent, participants, mLatestTimestamp);
9463 }
9464 }
9465 }
9466
9467 /**
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08009468 * <p>Helper class to add Android TV extensions to notifications. To create a notification
9469 * with a TV extension:
9470 *
9471 * <ol>
9472 * <li>Create an {@link Notification.Builder}, setting any desired properties.
9473 * <li>Create a {@link TvExtender}.
9474 * <li>Set TV-specific properties using the {@code set} methods of
9475 * {@link TvExtender}.
9476 * <li>Call {@link Notification.Builder#extend(Notification.Extender)}
9477 * to apply the extension to a notification.
9478 * </ol>
9479 *
9480 * <pre class="prettyprint">
9481 * Notification notification = new Notification.Builder(context)
9482 * ...
9483 * .extend(new TvExtender()
9484 * .set*(...))
9485 * .build();
9486 * </pre>
9487 *
9488 * <p>TV extensions can be accessed on an existing notification by using the
9489 * {@code TvExtender(Notification)} constructor, and then using the {@code get} methods
9490 * to access values.
9491 *
9492 * @hide
9493 */
9494 @SystemApi
9495 public static final class TvExtender implements Extender {
9496 private static final String TAG = "TvExtender";
9497
9498 private static final String EXTRA_TV_EXTENDER = "android.tv.EXTENSIONS";
9499 private static final String EXTRA_FLAGS = "flags";
9500 private static final String EXTRA_CONTENT_INTENT = "content_intent";
9501 private static final String EXTRA_DELETE_INTENT = "delete_intent";
Dmitri Plotnikovb8a04ee2017-01-20 10:39:22 -08009502 private static final String EXTRA_CHANNEL_ID = "channel_id";
Rhiannon Malia1a083932018-01-24 15:02:30 -08009503 private static final String EXTRA_SUPPRESS_SHOW_OVER_APPS = "suppressShowOverApps";
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08009504
9505 // Flags bitwise-ored to mFlags
9506 private static final int FLAG_AVAILABLE_ON_TV = 0x1;
9507
9508 private int mFlags;
Dmitri Plotnikovb8a04ee2017-01-20 10:39:22 -08009509 private String mChannelId;
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08009510 private PendingIntent mContentIntent;
9511 private PendingIntent mDeleteIntent;
Rhiannon Malia1a083932018-01-24 15:02:30 -08009512 private boolean mSuppressShowOverApps;
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08009513
9514 /**
9515 * Create a {@link TvExtender} with default options.
9516 */
9517 public TvExtender() {
9518 mFlags = FLAG_AVAILABLE_ON_TV;
9519 }
9520
9521 /**
9522 * Create a {@link TvExtender} from the TvExtender options of an existing Notification.
9523 *
9524 * @param notif The notification from which to copy options.
9525 */
9526 public TvExtender(Notification notif) {
9527 Bundle bundle = notif.extras == null ?
9528 null : notif.extras.getBundle(EXTRA_TV_EXTENDER);
9529 if (bundle != null) {
9530 mFlags = bundle.getInt(EXTRA_FLAGS);
Dmitri Plotnikovb8a04ee2017-01-20 10:39:22 -08009531 mChannelId = bundle.getString(EXTRA_CHANNEL_ID);
Rhiannon Malia1a083932018-01-24 15:02:30 -08009532 mSuppressShowOverApps = bundle.getBoolean(EXTRA_SUPPRESS_SHOW_OVER_APPS);
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08009533 mContentIntent = bundle.getParcelable(EXTRA_CONTENT_INTENT);
9534 mDeleteIntent = bundle.getParcelable(EXTRA_DELETE_INTENT);
9535 }
9536 }
9537
9538 /**
9539 * Apply a TV extension to a notification that is being built. This is typically called by
9540 * the {@link Notification.Builder#extend(Notification.Extender)}
9541 * method of {@link Notification.Builder}.
9542 */
9543 @Override
9544 public Notification.Builder extend(Notification.Builder builder) {
9545 Bundle bundle = new Bundle();
9546
9547 bundle.putInt(EXTRA_FLAGS, mFlags);
Dmitri Plotnikovb8a04ee2017-01-20 10:39:22 -08009548 bundle.putString(EXTRA_CHANNEL_ID, mChannelId);
Rhiannon Malia1a083932018-01-24 15:02:30 -08009549 bundle.putBoolean(EXTRA_SUPPRESS_SHOW_OVER_APPS, mSuppressShowOverApps);
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08009550 if (mContentIntent != null) {
9551 bundle.putParcelable(EXTRA_CONTENT_INTENT, mContentIntent);
9552 }
9553
9554 if (mDeleteIntent != null) {
9555 bundle.putParcelable(EXTRA_DELETE_INTENT, mDeleteIntent);
9556 }
9557
9558 builder.getExtras().putBundle(EXTRA_TV_EXTENDER, bundle);
9559 return builder;
9560 }
9561
9562 /**
9563 * Returns true if this notification should be shown on TV. This method return true
9564 * if the notification was extended with a TvExtender.
9565 */
9566 public boolean isAvailableOnTv() {
9567 return (mFlags & FLAG_AVAILABLE_ON_TV) != 0;
9568 }
9569
9570 /**
Dmitri Plotnikovb8a04ee2017-01-20 10:39:22 -08009571 * Specifies the channel the notification should be delivered on when shown on TV.
9572 * It can be different from the channel that the notification is delivered to when
9573 * posting on a non-TV device.
9574 */
9575 public TvExtender setChannel(String channelId) {
9576 mChannelId = channelId;
9577 return this;
9578 }
9579
9580 /**
Julia Reynoldsbad42972017-04-25 13:52:49 -04009581 * Specifies the channel the notification should be delivered on when shown on TV.
9582 * It can be different from the channel that the notification is delivered to when
9583 * posting on a non-TV device.
9584 */
9585 public TvExtender setChannelId(String channelId) {
9586 mChannelId = channelId;
9587 return this;
9588 }
9589
Jeff Sharkey000ce802017-04-29 13:13:27 -06009590 /** @removed */
9591 @Deprecated
Dmitri Plotnikovb8a04ee2017-01-20 10:39:22 -08009592 public String getChannel() {
9593 return mChannelId;
9594 }
9595
9596 /**
Julia Reynoldsbad42972017-04-25 13:52:49 -04009597 * Returns the id of the channel this notification posts to on TV.
9598 */
9599 public String getChannelId() {
9600 return mChannelId;
9601 }
9602
9603 /**
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08009604 * Supplies a {@link PendingIntent} to be sent when the notification is selected on TV.
9605 * If provided, it is used instead of the content intent specified
9606 * at the level of Notification.
9607 */
9608 public TvExtender setContentIntent(PendingIntent intent) {
9609 mContentIntent = intent;
9610 return this;
9611 }
9612
9613 /**
9614 * Returns the TV-specific content intent. If this method returns null, the
9615 * main content intent on the notification should be used.
9616 *
9617 * @see {@link Notification#contentIntent}
9618 */
9619 public PendingIntent getContentIntent() {
9620 return mContentIntent;
9621 }
9622
9623 /**
9624 * Supplies a {@link PendingIntent} to send when the notification is cleared explicitly
9625 * by the user on TV. If provided, it is used instead of the delete intent specified
9626 * at the level of Notification.
9627 */
9628 public TvExtender setDeleteIntent(PendingIntent intent) {
9629 mDeleteIntent = intent;
9630 return this;
9631 }
9632
9633 /**
9634 * Returns the TV-specific delete intent. If this method returns null, the
9635 * main delete intent on the notification should be used.
9636 *
9637 * @see {@link Notification#deleteIntent}
9638 */
9639 public PendingIntent getDeleteIntent() {
9640 return mDeleteIntent;
9641 }
Rhiannon Malia1a083932018-01-24 15:02:30 -08009642
9643 /**
9644 * Specifies whether this notification should suppress showing a message over top of apps
9645 * outside of the launcher.
9646 */
9647 public TvExtender setSuppressShowOverApps(boolean suppress) {
9648 mSuppressShowOverApps = suppress;
9649 return this;
9650 }
9651
9652 /**
9653 * Returns true if this notification should not show messages over top of apps
9654 * outside of the launcher.
9655 */
9656 public boolean getSuppressShowOverApps() {
9657 return mSuppressShowOverApps;
9658 }
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08009659 }
9660
9661 /**
Griff Hazen61a9e862014-05-22 16:05:19 -07009662 * Get an array of Notification objects from a parcelable array bundle field.
9663 * Update the bundle to have a typed array so fetches in the future don't need
9664 * to do an array copy.
9665 */
9666 private static Notification[] getNotificationArrayFromBundle(Bundle bundle, String key) {
9667 Parcelable[] array = bundle.getParcelableArray(key);
9668 if (array instanceof Notification[] || array == null) {
9669 return (Notification[]) array;
9670 }
9671 Notification[] typedArray = Arrays.copyOf(array, array.length,
9672 Notification[].class);
9673 bundle.putParcelableArray(key, typedArray);
9674 return typedArray;
9675 }
Christoph Studer4600f9b2014-07-22 22:44:43 +02009676
9677 private static class BuilderRemoteViews extends RemoteViews {
9678 public BuilderRemoteViews(Parcel parcel) {
9679 super(parcel);
9680 }
9681
Kenny Guy77320062014-08-27 21:37:15 +01009682 public BuilderRemoteViews(ApplicationInfo appInfo, int layoutId) {
9683 super(appInfo, layoutId);
Christoph Studer4600f9b2014-07-22 22:44:43 +02009684 }
9685
9686 @Override
9687 public BuilderRemoteViews clone() {
9688 Parcel p = Parcel.obtain();
9689 writeToParcel(p, 0);
9690 p.setDataPosition(0);
9691 BuilderRemoteViews brv = new BuilderRemoteViews(p);
9692 p.recycle();
9693 return brv;
9694 }
9695 }
Adrian Roos70d7aa32017-01-11 15:39:06 -08009696
Selim Cinek384804b2018-04-18 14:31:07 +08009697 /**
9698 * A result object where information about the template that was created is saved.
9699 */
9700 private static class TemplateBindResult {
Selim Cinek1c72fa02018-04-23 18:00:54 +08009701 int mIconMarginEnd;
Selim Cinek384804b2018-04-18 14:31:07 +08009702
9703 /**
Selim Cinek1c72fa02018-04-23 18:00:54 +08009704 * Get the margin end that needs to be added to any fields that may overlap
Selim Cinek384804b2018-04-18 14:31:07 +08009705 * with the right actions.
9706 */
Selim Cinek1c72fa02018-04-23 18:00:54 +08009707 public int getIconMarginEnd() {
9708 return mIconMarginEnd;
Selim Cinek384804b2018-04-18 14:31:07 +08009709 }
9710
Selim Cinek1c72fa02018-04-23 18:00:54 +08009711 public void setIconMarginEnd(int iconMarginEnd) {
9712 this.mIconMarginEnd = iconMarginEnd;
Selim Cinek384804b2018-04-18 14:31:07 +08009713 }
9714 }
9715
Adrian Roos70d7aa32017-01-11 15:39:06 -08009716 private static class StandardTemplateParams {
9717 boolean hasProgress = true;
9718 boolean ambient = false;
9719 CharSequence title;
9720 CharSequence text;
Selim Cinekafeed292017-12-12 17:32:44 -08009721 CharSequence headerTextSecondary;
Selim Cinekbee4e072018-05-21 22:06:43 -07009722 int maxRemoteInputHistory = Style.MAX_REMOTE_INPUT_HISTORY_LINES;
Selim Cinek88188f22017-09-19 16:46:56 -07009723 boolean hideLargeIcon;
Selim Cinek384804b2018-04-18 14:31:07 +08009724 boolean hideReplyIcon;
Adrian Roos70d7aa32017-01-11 15:39:06 -08009725
9726 final StandardTemplateParams reset() {
9727 hasProgress = true;
9728 ambient = false;
9729 title = null;
9730 text = null;
Selim Cinekafeed292017-12-12 17:32:44 -08009731 headerTextSecondary = null;
Selim Cinekbee4e072018-05-21 22:06:43 -07009732 maxRemoteInputHistory = Style.MAX_REMOTE_INPUT_HISTORY_LINES;
Adrian Roos70d7aa32017-01-11 15:39:06 -08009733 return this;
9734 }
9735
9736 final StandardTemplateParams hasProgress(boolean hasProgress) {
9737 this.hasProgress = hasProgress;
9738 return this;
9739 }
9740
9741 final StandardTemplateParams title(CharSequence title) {
9742 this.title = title;
9743 return this;
9744 }
9745
9746 final StandardTemplateParams text(CharSequence text) {
9747 this.text = text;
9748 return this;
9749 }
9750
Selim Cinekafeed292017-12-12 17:32:44 -08009751 final StandardTemplateParams headerTextSecondary(CharSequence text) {
9752 this.headerTextSecondary = text;
9753 return this;
9754 }
9755
Selim Cinek384804b2018-04-18 14:31:07 +08009756 final StandardTemplateParams hideLargeIcon(boolean hideLargeIcon) {
9757 this.hideLargeIcon = hideLargeIcon;
Selim Cinek88188f22017-09-19 16:46:56 -07009758 return this;
9759 }
9760
Selim Cinek384804b2018-04-18 14:31:07 +08009761 final StandardTemplateParams hideReplyIcon(boolean hideReplyIcon) {
9762 this.hideReplyIcon = hideReplyIcon;
Selim Cinek88188f22017-09-19 16:46:56 -07009763 return this;
9764 }
9765
Adrian Roos70d7aa32017-01-11 15:39:06 -08009766 final StandardTemplateParams ambient(boolean ambient) {
Adrian Roos0bc3f6a2017-03-06 11:54:05 -08009767 Preconditions.checkState(title == null && text == null, "must set ambient before text");
Adrian Roos70d7aa32017-01-11 15:39:06 -08009768 this.ambient = ambient;
9769 return this;
9770 }
9771
9772 final StandardTemplateParams fillTextsFrom(Builder b) {
9773 Bundle extras = b.mN.extras;
Lucas Dupin06c5e642017-09-13 16:34:58 -07009774 this.title = b.processLegacyText(extras.getCharSequence(EXTRA_TITLE), ambient);
9775
9776 // Big text notifications should contain their content when viewed in ambient mode.
9777 CharSequence text = extras.getCharSequence(EXTRA_BIG_TEXT);
9778 if (!ambient || TextUtils.isEmpty(text)) {
9779 text = extras.getCharSequence(EXTRA_TEXT);
9780 }
9781 this.text = b.processLegacyText(text, ambient);
Adrian Roos70d7aa32017-01-11 15:39:06 -08009782 return this;
9783 }
Selim Cinekbee4e072018-05-21 22:06:43 -07009784
9785 /**
9786 * Set the maximum lines of remote input history lines allowed.
9787 * @param maxRemoteInputHistory The number of lines.
9788 * @return The builder for method chaining.
9789 */
9790 public StandardTemplateParams setMaxRemoteInputHistory(int maxRemoteInputHistory) {
9791 this.maxRemoteInputHistory = maxRemoteInputHistory;
9792 return this;
9793 }
Adrian Roos70d7aa32017-01-11 15:39:06 -08009794 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08009795}