blob: 4294eab222aa2da1500ebc250961fc8e931c15ef [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
Tor Norbye80756e32015-03-02 09:39:27 -080019import android.annotation.ColorInt;
Tor Norbye7b9c9122013-05-30 16:48:33 -070020import android.annotation.DrawableRes;
Tor Norbyed9273d62013-05-30 15:59:53 -070021import android.annotation.IntDef;
Alex Hillsfd590442016-10-07 09:52:44 -040022import android.annotation.NonNull;
Daniel Sandler01df1c62014-06-09 10:54:01 -040023import android.annotation.SdkConstant;
24import android.annotation.SdkConstant.SdkConstantType;
Julia Reynoldse46bb372016-03-17 11:05:58 -040025import android.annotation.SystemApi;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080026import android.content.Context;
27import android.content.Intent;
Kenny Guy77320062014-08-27 21:37:15 +010028import android.content.pm.ApplicationInfo;
Dan Sandler732bd6c2016-04-12 14:20:32 -040029import android.content.pm.PackageManager;
Christoph Studer4600f9b2014-07-22 22:44:43 +020030import android.content.pm.PackageManager.NameNotFoundException;
Julia Reynolds13d898c2017-02-02 12:22:05 -050031import android.content.pm.ShortcutInfo;
Jorim Jaggief72a192014-08-26 21:57:46 +020032import android.content.res.ColorStateList;
Joe Onoratoef1e7762010-09-17 18:38:38 -040033import android.graphics.Bitmap;
Kenny Guy8a0101b2014-05-08 23:34:12 +010034import android.graphics.Canvas;
Adrian Roosc1a80b02016-04-05 14:54:55 -070035import android.graphics.Color;
Jorim Jaggi5c2d8462014-03-21 17:37:00 +010036import android.graphics.PorterDuff;
Kenny Guy8a0101b2014-05-08 23:34:12 +010037import android.graphics.drawable.Drawable;
Dan Sandlerd63f9322015-05-06 15:18:49 -040038import android.graphics.drawable.Icon;
John Spurlockc0650f022014-07-19 13:22:39 -040039import android.media.AudioAttributes;
Jeff Sharkey098d5802012-04-26 17:30:34 -070040import android.media.AudioManager;
Jean-Michel Trivi2f7511f2016-11-28 15:40:27 -080041import android.media.PlayerBase;
Jeff Browndba34ba2014-06-24 20:46:03 -070042import android.media.session.MediaSession;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080043import android.net.Uri;
Daniel Sandlerdcbaf662013-04-26 16:23:09 -040044import android.os.BadParcelableException;
Dianne Hackborn98305522017-05-05 17:53:53 -070045import android.os.Binder;
Christoph Studer239f8352014-08-25 15:13:18 +020046import android.os.Build;
Daniel Sandler2561b0b2012-02-13 21:04:12 -050047import android.os.Bundle;
Dianne Hackborn98305522017-05-05 17:53:53 -070048import android.os.IBinder;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080049import android.os.Parcel;
50import android.os.Parcelable;
Daniel Sandlera2985ed2012-04-03 16:42:00 -040051import android.os.SystemClock;
Selim Cinek6743c0b2017-01-18 18:24:01 -080052import android.os.SystemProperties;
Jeff Sharkey6d515712012-09-20 16:06:08 -070053import android.os.UserHandle;
Adrian Roosc1a80b02016-04-05 14:54:55 -070054import android.text.BidiFormatter;
Selim Cinek60a54252016-02-26 17:03:25 -080055import android.text.SpannableStringBuilder;
56import android.text.Spanned;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080057import android.text.TextUtils;
Selim Cinek60a54252016-02-26 17:03:25 -080058import android.text.style.AbsoluteSizeSpan;
Selim Cinek981962e2016-07-20 20:41:58 -070059import android.text.style.BackgroundColorSpan;
Selim Cinek89991a22016-03-07 19:51:54 -080060import android.text.style.CharacterStyle;
Selim Cinek981962e2016-07-20 20:41:58 -070061import android.text.style.ForegroundColorSpan;
Selim Cinek60a54252016-02-26 17:03:25 -080062import android.text.style.RelativeSizeSpan;
63import android.text.style.TextAppearanceSpan;
Svet Ganovddb94882016-06-23 19:55:24 -070064import android.util.ArraySet;
Daniel Sandlerdcbaf662013-04-26 16:23:09 -040065import android.util.Log;
Dan Sandler50128532015-12-08 15:42:41 -050066import android.util.SparseArray;
Griff Hazen61a9e862014-05-22 16:05:19 -070067import android.view.Gravity;
Selim Cinekea4bef72015-12-02 15:51:10 -080068import android.view.NotificationHeaderView;
Joe Onorato8595a3d2010-11-19 18:12:07 -080069import android.view.View;
Selim Cinek954cc232016-05-20 13:29:23 -070070import android.view.ViewGroup;
Jeff Sharkey1c400132011-08-05 14:50:13 -070071import android.widget.ProgressBar;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080072import android.widget.RemoteViews;
73
Griff Hazen959591e2014-05-15 22:26:18 -070074import com.android.internal.R;
Svet Ganovddb94882016-06-23 19:55:24 -070075import com.android.internal.util.ArrayUtils;
Griff Hazenc091ba82014-05-16 10:13:26 -070076import com.android.internal.util.NotificationColorUtil;
Adrian Roos0bc3f6a2017-03-06 11:54:05 -080077import com.android.internal.util.Preconditions;
Griff Hazen959591e2014-05-15 22:26:18 -070078
Tor Norbyed9273d62013-05-30 15:59:53 -070079import java.lang.annotation.Retention;
80import java.lang.annotation.RetentionPolicy;
Christoph Studer4600f9b2014-07-22 22:44:43 +020081import java.lang.reflect.Constructor;
Daniel Sandler2561b0b2012-02-13 21:04:12 -050082import java.util.ArrayList;
Griff Hazen61a9e862014-05-22 16:05:19 -070083import java.util.Arrays;
Griff Hazen5cadc3b2014-05-20 09:55:39 -070084import java.util.Collections;
Griff Hazen61a9e862014-05-22 16:05:19 -070085import java.util.List;
Dan Sandler50128532015-12-08 15:42:41 -050086import java.util.Set;
Joe Onorato561d3852010-11-20 18:09:34 -080087
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080088/**
89 * A class that represents how a persistent notification is to be presented to
90 * the user using the {@link android.app.NotificationManager}.
91 *
Joe Onoratocb109a02011-01-18 17:57:41 -080092 * <p>The {@link Notification.Builder Notification.Builder} has been added to make it
93 * easier to construct Notifications.</p>
94 *
Joe Fernandez558459f2011-10-13 16:47:36 -070095 * <div class="special reference">
96 * <h3>Developer Guides</h3>
97 * <p>For a guide to creating notifications, read the
98 * <a href="{@docRoot}guide/topics/ui/notifiers/notifications.html">Status Bar Notifications</a>
99 * developer guide.</p>
100 * </div>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800101 */
102public class Notification implements Parcelable
103{
Daniel Sandlerdcbaf662013-04-26 16:23:09 -0400104 private static final String TAG = "Notification";
105
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800106 /**
Daniel Sandler01df1c62014-06-09 10:54:01 -0400107 * An activity that provides a user interface for adjusting notification preferences for its
Julia Reynolds3aedded2017-03-31 14:42:09 -0400108 * containing application.
Daniel Sandler01df1c62014-06-09 10:54:01 -0400109 */
110 @SdkConstant(SdkConstantType.INTENT_CATEGORY)
111 public static final String INTENT_CATEGORY_NOTIFICATION_PREFERENCES
112 = "android.intent.category.NOTIFICATION_PREFERENCES";
113
114 /**
Julia Reynolds2619b5e2017-02-09 09:58:15 -0500115 * Optional extra for {@link #INTENT_CATEGORY_NOTIFICATION_PREFERENCES}. If provided, will
116 * contain a {@link NotificationChannel#getId() channel id} that can be used to narrow down
Julia Reynolds3aedded2017-03-31 14:42:09 -0400117 * what settings should be shown in the target app.
Julia Reynolds2619b5e2017-02-09 09:58:15 -0500118 */
119 public static final String EXTRA_CHANNEL_ID = "android.intent.extra.CHANNEL_ID";
120
121 /**
Julia Reynolds3aedded2017-03-31 14:42:09 -0400122 * Optional extra for {@link #INTENT_CATEGORY_NOTIFICATION_PREFERENCES}. If provided, will
123 * contain the tag provided to {@link NotificationManager#notify(String, int, Notification)}
124 * that can be used to narrow down what settings should be shown in the target app.
125 */
126 public static final String EXTRA_NOTIFICATION_TAG = "android.intent.extra.NOTIFICATION_TAG";
127
128 /**
129 * Optional extra for {@link #INTENT_CATEGORY_NOTIFICATION_PREFERENCES}. If provided, will
130 * contain the id provided to {@link NotificationManager#notify(String, int, Notification)}
131 * that can be used to narrow down what settings should be shown in the target app.
132 */
133 public static final String EXTRA_NOTIFICATION_ID = "android.intent.extra.NOTIFICATION_ID";
134
135 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800136 * Use all default values (where applicable).
137 */
138 public static final int DEFAULT_ALL = ~0;
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500139
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800140 /**
141 * Use the default notification sound. This will ignore any given
142 * {@link #sound}.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500143 *
Chris Wren47c20a12014-06-18 17:27:29 -0400144 * <p>
145 * A notification that is noisy is more likely to be presented as a heads-up notification.
146 * </p>
147 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800148 * @see #defaults
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500149 */
150
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800151 public static final int DEFAULT_SOUND = 1;
152
153 /**
154 * Use the default notification vibrate. This will ignore any given
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500155 * {@link #vibrate}. Using phone vibration requires the
Scott Mainb8b36452009-04-26 15:50:49 -0700156 * {@link android.Manifest.permission#VIBRATE VIBRATE} permission.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500157 *
Chris Wren47c20a12014-06-18 17:27:29 -0400158 * <p>
159 * A notification that vibrates is more likely to be presented as a heads-up notification.
160 * </p>
161 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800162 * @see #defaults
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500163 */
164
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800165 public static final int DEFAULT_VIBRATE = 2;
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500166
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800167 /**
168 * Use the default notification lights. This will ignore the
169 * {@link #FLAG_SHOW_LIGHTS} bit, and {@link #ledARGB}, {@link #ledOffMS}, or
170 * {@link #ledOnMS}.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500171 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800172 * @see #defaults
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500173 */
174
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800175 public static final int DEFAULT_LIGHTS = 4;
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500176
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800177 /**
Christoph Studer535ec612014-09-03 15:47:47 +0200178 * Maximum length of CharSequences accepted by Builder and friends.
179 *
180 * <p>
181 * Avoids spamming the system with overly large strings such as full e-mails.
182 */
183 private static final int MAX_CHARSEQUENCE_LENGTH = 5 * 1024;
184
185 /**
Adrian Roose458aa82015-12-08 16:17:19 -0800186 * Maximum entries of reply text that are accepted by Builder and friends.
187 */
188 private static final int MAX_REPLY_HISTORY = 5;
189
190 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500191 * A timestamp related to this notification, in milliseconds since the epoch.
Joe Malin8d40d042012-11-05 11:36:40 -0800192 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500193 * Default value: {@link System#currentTimeMillis() Now}.
194 *
195 * Choose a timestamp that will be most relevant to the user. For most finite events, this
196 * corresponds to the time the event happened (or will happen, in the case of events that have
197 * yet to occur but about which the user is being informed). Indefinite events should be
Joe Malin8d40d042012-11-05 11:36:40 -0800198 * timestamped according to when the activity began.
199 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500200 * Some examples:
Joe Malin8d40d042012-11-05 11:36:40 -0800201 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500202 * <ul>
203 * <li>Notification of a new chat message should be stamped when the message was received.</li>
204 * <li>Notification of an ongoing file download (with a progress bar, for example) should be stamped when the download started.</li>
205 * <li>Notification of a completed file download should be stamped when the download finished.</li>
206 * <li>Notification of an upcoming meeting should be stamped with the time the meeting will begin (that is, in the future).</li>
207 * <li>Notification of an ongoing stopwatch (increasing timer) should be stamped with the watch's start time.
208 * <li>Notification of an ongoing countdown timer should be stamped with the timer's end time.
Joe Malin8d40d042012-11-05 11:36:40 -0800209 * </ul>
210 *
Selim Cinek0ff1ce602016-04-05 18:27:16 -0700211 * For apps targeting {@link android.os.Build.VERSION_CODES#N} and above, this time is not shown
212 * anymore by default and must be opted into by using
213 * {@link android.app.Notification.Builder#setShowWhen(boolean)}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800214 */
215 public long when;
216
217 /**
Selim Cinekb85f36fd2016-04-20 18:46:36 -0700218 * The creation time of the notification
219 */
220 private long creationTime;
221
222 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800223 * The resource id of a drawable to use as the icon in the status bar.
Dan Sandler86647982015-05-13 23:41:13 -0400224 *
225 * @deprecated Use {@link Builder#setSmallIcon(Icon)} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800226 */
Dan Sandler86647982015-05-13 23:41:13 -0400227 @Deprecated
Tor Norbye7b9c9122013-05-30 16:48:33 -0700228 @DrawableRes
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800229 public int icon;
230
231 /**
Joe Onorato46439ce2010-11-19 13:56:21 -0800232 * If the icon in the status bar is to have more than one level, you can set this. Otherwise,
233 * leave it at its default value of 0.
234 *
235 * @see android.widget.ImageView#setImageLevel
Griff Hazen959591e2014-05-15 22:26:18 -0700236 * @see android.graphics.drawable.Drawable#setLevel
Joe Onorato46439ce2010-11-19 13:56:21 -0800237 */
238 public int iconLevel;
239
240 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500241 * The number of events that this notification represents. For example, in a new mail
242 * notification, this could be the number of unread messages.
Joe Malin8d40d042012-11-05 11:36:40 -0800243 *
Julia Reynolds30331982017-04-27 10:12:50 -0400244 * The system may or may not use this field to modify the appearance of the notification.
Julia Reynolds13d898c2017-02-02 12:22:05 -0500245 * Starting with {@link android.os.Build.VERSION_CODES#O}, the number may be displayed as a
246 * badge icon in Launchers that support badging.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800247 */
Julia Reynolds30331982017-04-27 10:12:50 -0400248 public int number = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800249
250 /**
251 * The intent to execute when the expanded status entry is clicked. If
252 * this is an activity, it must include the
253 * {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} flag, which requires
Scott Main7aee61f2011-02-08 11:25:01 -0800254 * that you take care of task management as described in the
255 * <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back
Dianne Hackborn6ceca582012-01-10 15:24:26 -0800256 * Stack</a> document. In particular, make sure to read the notification section
257 * <a href="{@docRoot}guide/topics/ui/notifiers/notifications.html#HandlingNotifications">Handling
258 * Notifications</a> for the correct ways to launch an application from a
259 * notification.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800260 */
261 public PendingIntent contentIntent;
262
263 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500264 * The intent to execute when the notification is explicitly dismissed by the user, either with
265 * the "Clear All" button or by swiping it away individually.
266 *
267 * This probably shouldn't be launching an activity since several of those will be sent
268 * at the same time.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800269 */
270 public PendingIntent deleteIntent;
271
272 /**
Dianne Hackborn170bae72010-09-03 15:14:28 -0700273 * An intent to launch instead of posting the notification to the status bar.
Joe Onoratocb109a02011-01-18 17:57:41 -0800274 *
Chris Wren47c20a12014-06-18 17:27:29 -0400275 * <p>
276 * The system UI may choose to display a heads-up notification, instead of
277 * launching this intent, while the user is using the device.
278 * </p>
279 *
Joe Onoratocb109a02011-01-18 17:57:41 -0800280 * @see Notification.Builder#setFullScreenIntent
Daniel Sandlere46cbd32010-06-17 10:35:26 -0400281 */
282 public PendingIntent fullScreenIntent;
283
284 /**
Dan Sandler5fcdf6e2014-07-18 11:31:15 -0400285 * Text that summarizes this notification for accessibility services.
286 *
287 * As of the L release, this text is no longer shown on screen, but it is still useful to
288 * accessibility services (where it serves as an audible announcement of the notification's
289 * appearance).
Joe Onoratoef1e7762010-09-17 18:38:38 -0400290 *
Joe Onorato46439ce2010-11-19 13:56:21 -0800291 * @see #tickerView
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800292 */
293 public CharSequence tickerText;
294
295 /**
Dan Sandler5fcdf6e2014-07-18 11:31:15 -0400296 * Formerly, a view showing the {@link #tickerText}.
297 *
298 * No longer displayed in the status bar as of API 21.
Joe Onoratoef1e7762010-09-17 18:38:38 -0400299 */
Dan Sandler5fcdf6e2014-07-18 11:31:15 -0400300 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -0800301 public RemoteViews tickerView;
Joe Onoratoef1e7762010-09-17 18:38:38 -0400302
303 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400304 * The view that will represent this notification in the notification list (which is pulled
305 * down from the status bar).
306 *
Julia Reynoldsd4ea7412016-02-17 14:00:56 -0500307 * As of N, this field may be null. The notification view is determined by the inputs
308 * to {@link Notification.Builder}; a custom RemoteViews can optionally be
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400309 * supplied with {@link Notification.Builder#setCustomContentView(RemoteViews)}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800310 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400311 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800312 public RemoteViews contentView;
313
Daniel Sandlera0a938c2012-03-15 08:42:37 -0400314 /**
Daniel Sandler4dfbe832012-04-11 14:51:46 -0400315 * A large-format version of {@link #contentView}, giving the Notification an
Daniel Sandlerf3b73432012-03-27 15:01:25 -0400316 * opportunity to show more detail. The system UI may choose to show this
317 * instead of the normal content view at its discretion.
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400318 *
Julia Reynoldsd4ea7412016-02-17 14:00:56 -0500319 * As of N, this field may be null. The expanded notification view is determined by the
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400320 * inputs to {@link Notification.Builder}; a custom RemoteViews can optionally be
321 * supplied with {@link Notification.Builder#setCustomBigContentView(RemoteViews)}.
Daniel Sandlerf3b73432012-03-27 15:01:25 -0400322 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400323 @Deprecated
Daniel Sandlerf3b73432012-03-27 15:01:25 -0400324 public RemoteViews bigContentView;
325
Chris Wren8fd39ec2014-02-27 17:43:26 -0500326
327 /**
Chris Wren47c20a12014-06-18 17:27:29 -0400328 * A medium-format version of {@link #contentView}, providing the Notification an
329 * opportunity to add action buttons to contentView. At its discretion, the system UI may
330 * choose to show this as a heads-up notification, which will pop up so the user can see
331 * it without leaving their current activity.
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400332 *
Julia Reynoldsd4ea7412016-02-17 14:00:56 -0500333 * As of N, this field may be null. The heads-up notification view is determined by the
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400334 * inputs to {@link Notification.Builder}; a custom RemoteViews can optionally be
335 * supplied with {@link Notification.Builder#setCustomHeadsUpContentView(RemoteViews)}.
Chris Wren8fd39ec2014-02-27 17:43:26 -0500336 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400337 @Deprecated
Chris Wren8fd39ec2014-02-27 17:43:26 -0500338 public RemoteViews headsUpContentView;
339
Daniel Sandlerf3b73432012-03-27 15:01:25 -0400340 /**
Dan Sandler86647982015-05-13 23:41:13 -0400341 * A large bitmap to be shown in the notification content area.
342 *
343 * @deprecated Use {@link Builder#setLargeIcon(Icon)} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800344 */
Dan Sandler86647982015-05-13 23:41:13 -0400345 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -0800346 public Bitmap largeIcon;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800347
348 /**
349 * The sound to play.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500350 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800351 * <p>
Chris Wren47c20a12014-06-18 17:27:29 -0400352 * A notification that is noisy is more likely to be presented as a heads-up notification.
353 * </p>
354 *
355 * <p>
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500356 * To play the default notification sound, see {@link #defaults}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800357 * </p>
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500358 * @deprecated use {@link NotificationChannel#getSound()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800359 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500360 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800361 public Uri sound;
362
363 /**
364 * Use this constant as the value for audioStreamType to request that
365 * the default stream type for notifications be used. Currently the
Jeff Sharkey098d5802012-04-26 17:30:34 -0700366 * default stream type is {@link AudioManager#STREAM_NOTIFICATION}.
John Spurlockc0650f022014-07-19 13:22:39 -0400367 *
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500368 * @deprecated Use {@link NotificationChannel#getAudioAttributes()} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800369 */
Jean-Michel Trivi81f871e2014-08-06 16:32:38 -0700370 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800371 public static final int STREAM_DEFAULT = -1;
372
373 /**
374 * The audio stream type to use when playing the sound.
375 * Should be one of the STREAM_ constants from
376 * {@link android.media.AudioManager}.
John Spurlockc0650f022014-07-19 13:22:39 -0400377 *
378 * @deprecated Use {@link #audioAttributes} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800379 */
Jean-Michel Trivi81f871e2014-08-06 16:32:38 -0700380 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800381 public int audioStreamType = STREAM_DEFAULT;
382
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800383 /**
John Spurlockc0650f022014-07-19 13:22:39 -0400384 * The default value of {@link #audioAttributes}.
385 */
386 public static final AudioAttributes AUDIO_ATTRIBUTES_DEFAULT = new AudioAttributes.Builder()
387 .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
388 .setUsage(AudioAttributes.USAGE_NOTIFICATION)
389 .build();
390
391 /**
392 * The {@link AudioAttributes audio attributes} to use when playing the sound.
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500393 *
394 * @deprecated use {@link NotificationChannel#getAudioAttributes()} instead.
John Spurlockc0650f022014-07-19 13:22:39 -0400395 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500396 @Deprecated
John Spurlockc0650f022014-07-19 13:22:39 -0400397 public AudioAttributes audioAttributes = AUDIO_ATTRIBUTES_DEFAULT;
398
399 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500400 * The pattern with which to vibrate.
401 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800402 * <p>
403 * To vibrate the default pattern, see {@link #defaults}.
404 * </p>
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500405 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800406 * @see android.os.Vibrator#vibrate(long[],int)
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500407 * @deprecated use {@link NotificationChannel#getVibrationPattern()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800408 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500409 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800410 public long[] vibrate;
411
412 /**
413 * The color of the led. The hardware will do its best approximation.
414 *
415 * @see #FLAG_SHOW_LIGHTS
416 * @see #flags
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500417 * @deprecated use {@link NotificationChannel#shouldShowLights()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800418 */
Tor Norbye80756e32015-03-02 09:39:27 -0800419 @ColorInt
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500420 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800421 public int ledARGB;
422
423 /**
424 * The number of milliseconds for the LED to be on while it's flashing.
425 * The hardware will do its best approximation.
426 *
427 * @see #FLAG_SHOW_LIGHTS
428 * @see #flags
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500429 * @deprecated use {@link NotificationChannel#shouldShowLights()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800430 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500431 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800432 public int ledOnMS;
433
434 /**
435 * The number of milliseconds for the LED to be off while it's flashing.
436 * The hardware will do its best approximation.
437 *
438 * @see #FLAG_SHOW_LIGHTS
439 * @see #flags
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500440 *
441 * @deprecated use {@link NotificationChannel#shouldShowLights()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800442 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500443 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800444 public int ledOffMS;
445
446 /**
447 * Specifies which values should be taken from the defaults.
448 * <p>
449 * To set, OR the desired from {@link #DEFAULT_SOUND},
450 * {@link #DEFAULT_VIBRATE}, {@link #DEFAULT_LIGHTS}. For all default
451 * values, use {@link #DEFAULT_ALL}.
452 * </p>
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500453 *
454 * @deprecated use {@link NotificationChannel#getSound()} and
455 * {@link NotificationChannel#shouldShowLights()} and
456 * {@link NotificationChannel#shouldVibrate()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800457 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500458 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800459 public int defaults;
460
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800461 /**
462 * Bit to be bitwise-ored into the {@link #flags} field that should be
463 * set if you want the LED on for this notification.
464 * <ul>
465 * <li>To turn the LED off, pass 0 in the alpha channel for colorARGB
466 * or 0 for both ledOnMS and ledOffMS.</li>
467 * <li>To turn the LED on, pass 1 for ledOnMS and 0 for ledOffMS.</li>
468 * <li>To flash the LED, pass the number of milliseconds that it should
469 * be on and off to ledOnMS and ledOffMS.</li>
470 * </ul>
471 * <p>
472 * Since hardware varies, you are not guaranteed that any of the values
473 * you pass are honored exactly. Use the system defaults (TODO) if possible
474 * because they will be set to values that work on any given hardware.
475 * <p>
476 * The alpha channel must be set for forward compatibility.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500477 *
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500478 * @deprecated use {@link NotificationChannel#shouldShowLights()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800479 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500480 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800481 public static final int FLAG_SHOW_LIGHTS = 0x00000001;
482
483 /**
484 * Bit to be bitwise-ored into the {@link #flags} field that should be
485 * set if this notification is in reference to something that is ongoing,
486 * like a phone call. It should not be set if this notification is in
487 * reference to something that happened at a particular point in time,
488 * like a missed phone call.
489 */
490 public static final int FLAG_ONGOING_EVENT = 0x00000002;
491
492 /**
493 * Bit to be bitwise-ored into the {@link #flags} field that if set,
Scott Mainb8b36452009-04-26 15:50:49 -0700494 * the audio will be repeated until the notification is
495 * cancelled or the notification window is opened.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800496 */
497 public static final int FLAG_INSISTENT = 0x00000004;
498
499 /**
500 * Bit to be bitwise-ored into the {@link #flags} field that should be
Griff Hazen293977b2014-04-28 08:37:20 -0700501 * set if you would only like the sound, vibrate and ticker to be played
502 * if the notification was not already showing.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800503 */
504 public static final int FLAG_ONLY_ALERT_ONCE = 0x00000008;
505
506 /**
507 * Bit to be bitwise-ored into the {@link #flags} field that should be
508 * set if the notification should be canceled when it is clicked by the
Daniel Sandler8aa9ae62012-12-04 23:31:47 -0500509 * user.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800510 */
511 public static final int FLAG_AUTO_CANCEL = 0x00000010;
512
513 /**
514 * Bit to be bitwise-ored into the {@link #flags} field that should be
515 * set if the notification should not be canceled when the user clicks
516 * the Clear all button.
517 */
518 public static final int FLAG_NO_CLEAR = 0x00000020;
519
Dianne Hackbornd8a43f62009-08-17 23:33:56 -0700520 /**
521 * Bit to be bitwise-ored into the {@link #flags} field that should be
522 * set if this notification represents a currently running service. This
523 * will normally be set for you by {@link Service#startForeground}.
524 */
525 public static final int FLAG_FOREGROUND_SERVICE = 0x00000040;
526
Daniel Sandlere46cbd32010-06-17 10:35:26 -0400527 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500528 * Obsolete flag indicating high-priority notifications; use the priority field instead.
Joe Malin8d40d042012-11-05 11:36:40 -0800529 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500530 * @deprecated Use {@link #priority} with a positive value.
Daniel Sandlere46cbd32010-06-17 10:35:26 -0400531 */
Aurimas Liutikas514c5ef2016-05-24 15:22:55 -0700532 @Deprecated
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500533 public static final int FLAG_HIGH_PRIORITY = 0x00000080;
Daniel Sandlere46cbd32010-06-17 10:35:26 -0400534
Griff Hazendfcb0802014-02-11 12:00:00 -0800535 /**
536 * Bit to be bitswise-ored into the {@link #flags} field that should be
537 * set if this notification is relevant to the current device only
538 * and it is not recommended that it bridge to other devices.
539 */
540 public static final int FLAG_LOCAL_ONLY = 0x00000100;
541
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700542 /**
543 * Bit to be bitswise-ored into the {@link #flags} field that should be
544 * set if this notification is the group summary for a group of notifications.
545 * Grouped notifications may display in a cluster or stack on devices which
546 * support such rendering. Requires a group key also be set using {@link Builder#setGroup}.
547 */
548 public static final int FLAG_GROUP_SUMMARY = 0x00000200;
549
Julia Reynoldse46bb372016-03-17 11:05:58 -0400550 /**
551 * Bit to be bitswise-ored into the {@link #flags} field that should be
552 * set if this notification is the group summary for an auto-group of notifications.
553 *
554 * @hide
555 */
556 @SystemApi
557 public static final int FLAG_AUTOGROUP_SUMMARY = 0x00000400;
558
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800559 public int flags;
560
Tor Norbyed9273d62013-05-30 15:59:53 -0700561 /** @hide */
562 @IntDef({PRIORITY_DEFAULT,PRIORITY_LOW,PRIORITY_MIN,PRIORITY_HIGH,PRIORITY_MAX})
563 @Retention(RetentionPolicy.SOURCE)
564 public @interface Priority {}
565
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800566 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500567 * Default notification {@link #priority}. If your application does not prioritize its own
568 * notifications, use this value for all notifications.
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500569 *
570 * @deprecated use {@link NotificationManager#IMPORTANCE_DEFAULT} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500571 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500572 @Deprecated
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500573 public static final int PRIORITY_DEFAULT = 0;
574
575 /**
576 * Lower {@link #priority}, for items that are less important. The UI may choose to show these
577 * items smaller, or at a different position in the list, compared with your app's
578 * {@link #PRIORITY_DEFAULT} items.
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500579 *
580 * @deprecated use {@link NotificationManager#IMPORTANCE_LOW} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500581 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500582 @Deprecated
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500583 public static final int PRIORITY_LOW = -1;
584
585 /**
586 * Lowest {@link #priority}; these items might not be shown to the user except under special
587 * circumstances, such as detailed notification logs.
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500588 *
589 * @deprecated use {@link NotificationManager#IMPORTANCE_MIN} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500590 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500591 @Deprecated
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500592 public static final int PRIORITY_MIN = -2;
593
594 /**
595 * Higher {@link #priority}, for more important notifications or alerts. The UI may choose to
596 * show these items larger, or at a different position in notification lists, compared with
597 * your app's {@link #PRIORITY_DEFAULT} items.
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500598 *
599 * @deprecated use {@link NotificationManager#IMPORTANCE_HIGH} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500600 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500601 @Deprecated
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500602 public static final int PRIORITY_HIGH = 1;
603
604 /**
605 * Highest {@link #priority}, for your application's most important items that require the
606 * user's prompt attention or input.
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500607 *
608 * @deprecated use {@link NotificationManager#IMPORTANCE_HIGH} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500609 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500610 @Deprecated
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500611 public static final int PRIORITY_MAX = 2;
612
613 /**
614 * Relative priority for this notification.
Joe Malin8d40d042012-11-05 11:36:40 -0800615 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500616 * Priority is an indication of how much of the user's valuable attention should be consumed by
617 * this notification. Low-priority notifications may be hidden from the user in certain
618 * situations, while the user might be interrupted for a higher-priority notification. The
Daniel Sandler6738eee2012-11-16 12:03:32 -0500619 * system will make a determination about how to interpret this priority when presenting
620 * the notification.
Chris Wren47c20a12014-06-18 17:27:29 -0400621 *
622 * <p>
623 * A notification that is at least {@link #PRIORITY_HIGH} is more likely to be presented
624 * as a heads-up notification.
625 * </p>
626 *
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500627 * @deprecated use {@link NotificationChannel#getImportance()} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500628 */
Tor Norbyed9273d62013-05-30 15:59:53 -0700629 @Priority
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500630 @Deprecated
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500631 public int priority;
Joe Malin8d40d042012-11-05 11:36:40 -0800632
Dan Sandler26e81cf2014-05-06 10:01:27 -0400633 /**
634 * Accent color (an ARGB integer like the constants in {@link android.graphics.Color})
635 * to be applied by the standard Style templates when presenting this notification.
636 *
637 * The current template design constructs a colorful header image by overlaying the
638 * {@link #icon} image (stenciled in white) atop a field of this color. Alpha components are
639 * ignored.
640 */
Tor Norbye80756e32015-03-02 09:39:27 -0800641 @ColorInt
Dan Sandler26e81cf2014-05-06 10:01:27 -0400642 public int color = COLOR_DEFAULT;
643
644 /**
645 * Special value of {@link #color} telling the system not to decorate this notification with
646 * any special color but instead use default colors when presenting this notification.
647 */
Tor Norbye80756e32015-03-02 09:39:27 -0800648 @ColorInt
Dan Sandler26e81cf2014-05-06 10:01:27 -0400649 public static final int COLOR_DEFAULT = 0; // AKA Color.TRANSPARENT
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600650
651 /**
Adrian Roos4ff3b122016-02-01 12:26:13 -0800652 * Special value of {@link #color} used as a place holder for an invalid color.
Selim Cinek99104832017-01-25 14:47:33 -0800653 * @hide
Adrian Roos4ff3b122016-02-01 12:26:13 -0800654 */
655 @ColorInt
Selim Cinek99104832017-01-25 14:47:33 -0800656 public static final int COLOR_INVALID = 1;
Adrian Roos4ff3b122016-02-01 12:26:13 -0800657
658 /**
Adrian Roosc1a80b02016-04-05 14:54:55 -0700659 * Sphere of visibility of this notification, which affects how and when the SystemUI reveals
660 * the notification's presence and contents in untrusted situations (namely, on the secure
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600661 * lockscreen).
662 *
663 * The default level, {@link #VISIBILITY_PRIVATE}, behaves exactly as notifications have always
664 * done on Android: The notification's {@link #icon} and {@link #tickerText} (if available) are
665 * shown in all situations, but the contents are only available if the device is unlocked for
666 * the appropriate user.
667 *
668 * A more permissive policy can be expressed by {@link #VISIBILITY_PUBLIC}; such a notification
669 * can be read even in an "insecure" context (that is, above a secure lockscreen).
670 * To modify the public version of this notification—for example, to redact some portions—see
671 * {@link Builder#setPublicVersion(Notification)}.
672 *
673 * Finally, a notification can be made {@link #VISIBILITY_SECRET}, which will suppress its icon
674 * and ticker until the user has bypassed the lockscreen.
675 */
Jeff Sharkey30e06bb2017-04-24 11:18:03 -0600676 public @Visibility int visibility;
677
678 /** @hide */
679 @IntDef(prefix = { "VISIBILITY_" }, value = {
680 VISIBILITY_PUBLIC,
681 VISIBILITY_PRIVATE,
682 VISIBILITY_SECRET,
683 })
684 @Retention(RetentionPolicy.SOURCE)
685 public @interface Visibility {}
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600686
Griff Hazenfc3922d2014-08-20 11:56:44 -0700687 /**
688 * Notification visibility: Show this notification in its entirety on all lockscreens.
689 *
690 * {@see #visibility}
691 */
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600692 public static final int VISIBILITY_PUBLIC = 1;
Griff Hazenfc3922d2014-08-20 11:56:44 -0700693
694 /**
695 * Notification visibility: Show this notification on all lockscreens, but conceal sensitive or
696 * private information on secure lockscreens.
697 *
698 * {@see #visibility}
699 */
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600700 public static final int VISIBILITY_PRIVATE = 0;
Griff Hazenfc3922d2014-08-20 11:56:44 -0700701
702 /**
703 * Notification visibility: Do not reveal any part of this notification on a secure lockscreen.
704 *
705 * {@see #visibility}
706 */
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600707 public static final int VISIBILITY_SECRET = -1;
708
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500709 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400710 * Notification category: incoming call (voice or video) or similar synchronous communication request.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500711 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400712 public static final String CATEGORY_CALL = "call";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500713
714 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400715 * Notification category: incoming direct message (SMS, instant message, etc.).
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500716 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400717 public static final String CATEGORY_MESSAGE = "msg";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500718
719 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400720 * Notification category: asynchronous bulk message (email).
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500721 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400722 public static final String CATEGORY_EMAIL = "email";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500723
724 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400725 * Notification category: calendar event.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500726 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400727 public static final String CATEGORY_EVENT = "event";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500728
729 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400730 * Notification category: promotion or advertisement.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500731 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400732 public static final String CATEGORY_PROMO = "promo";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500733
734 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400735 * Notification category: alarm or timer.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500736 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400737 public static final String CATEGORY_ALARM = "alarm";
738
739 /**
740 * Notification category: progress of a long-running background operation.
741 */
742 public static final String CATEGORY_PROGRESS = "progress";
743
744 /**
745 * Notification category: social network or sharing update.
746 */
747 public static final String CATEGORY_SOCIAL = "social";
748
749 /**
750 * Notification category: error in background operation or authentication status.
751 */
752 public static final String CATEGORY_ERROR = "err";
753
754 /**
755 * Notification category: media transport control for playback.
756 */
757 public static final String CATEGORY_TRANSPORT = "transport";
758
759 /**
760 * Notification category: system or device status update. Reserved for system use.
761 */
762 public static final String CATEGORY_SYSTEM = "sys";
763
764 /**
765 * Notification category: indication of running background service.
766 */
767 public static final String CATEGORY_SERVICE = "service";
768
769 /**
John Spurlock0a69c8c2014-03-21 13:30:57 -0400770 * Notification category: a specific, timely recommendation for a single thing.
771 * For example, a news app might want to recommend a news story it believes the user will
772 * want to read next.
773 */
774 public static final String CATEGORY_RECOMMENDATION = "recommendation";
775
776 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400777 * Notification category: ongoing information about device or contextual status.
778 */
779 public static final String CATEGORY_STATUS = "status";
780
781 /**
John Spurlock24d3dad2015-04-02 12:24:02 -0400782 * Notification category: user-scheduled reminder.
783 */
784 public static final String CATEGORY_REMINDER = "reminder";
785
786 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400787 * One of the predefined notification categories (see the <code>CATEGORY_*</code> constants)
788 * that best describes this Notification. May be used by the system for ranking and filtering.
789 */
790 public String category;
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500791
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700792 private String mGroupKey;
793
794 /**
795 * Get the key used to group this notification into a cluster or stack
796 * with other notifications on devices which support such rendering.
797 */
798 public String getGroup() {
799 return mGroupKey;
800 }
801
802 private String mSortKey;
803
804 /**
805 * Get a sort key that orders this notification among other notifications from the
806 * same package. This can be useful if an external sort was already applied and an app
807 * would like to preserve this. Notifications will be sorted lexicographically using this
808 * value, although providing different priorities in addition to providing sort key may
809 * cause this value to be ignored.
810 *
811 * <p>This sort key can also be used to order members of a notification group. See
812 * {@link Builder#setGroup}.
813 *
814 * @see String#compareTo(String)
815 */
816 public String getSortKey() {
817 return mSortKey;
818 }
819
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500820 /**
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400821 * Additional semantic data to be carried around with this Notification.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400822 * <p>
823 * The extras keys defined here are intended to capture the original inputs to {@link Builder}
824 * APIs, and are intended to be used by
825 * {@link android.service.notification.NotificationListenerService} implementations to extract
826 * detailed information from notification objects.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500827 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400828 public Bundle extras = new Bundle();
829
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400830 /**
Felipe Lemedd85da62016-06-28 11:29:54 -0700831 * All pending intents in the notification as the system needs to be able to access them but
832 * touching the extras bundle in the system process is not safe because the bundle may contain
Svet Ganovddb94882016-06-23 19:55:24 -0700833 * custom parcelable objects.
834 *
835 * @hide
836 */
Felipe Lemedd85da62016-06-28 11:29:54 -0700837 public ArraySet<PendingIntent> allPendingIntents;
Svet Ganovddb94882016-06-23 19:55:24 -0700838
839 /**
Dianne Hackborn98305522017-05-05 17:53:53 -0700840 * Token identifying the notification that is applying doze/bgcheck whitelisting to the
841 * pending intents inside of it, so only those will get the behavior.
842 *
843 * @hide
844 */
845 static public IBinder whitelistToken;
846
847 /**
848 * Must be set by a process to start associating tokens with Notification objects
849 * coming in to it. This is set by NotificationManagerService.
850 *
851 * @hide
852 */
853 static public IBinder processWhitelistToken;
854
855 /**
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400856 * {@link #extras} key: this is the title of the notification,
857 * as supplied to {@link Builder#setContentTitle(CharSequence)}.
858 */
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -0500859 public static final String EXTRA_TITLE = "android.title";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400860
861 /**
862 * {@link #extras} key: this is the title of the notification when shown in expanded form,
863 * e.g. as supplied to {@link BigTextStyle#setBigContentTitle(CharSequence)}.
864 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400865 public static final String EXTRA_TITLE_BIG = EXTRA_TITLE + ".big";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400866
867 /**
868 * {@link #extras} key: this is the main text payload, as supplied to
869 * {@link Builder#setContentText(CharSequence)}.
870 */
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -0500871 public static final String EXTRA_TEXT = "android.text";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400872
873 /**
874 * {@link #extras} key: this is a third line of text, as supplied to
875 * {@link Builder#setSubText(CharSequence)}.
876 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400877 public static final String EXTRA_SUB_TEXT = "android.subText";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400878
879 /**
Adrian Roose458aa82015-12-08 16:17:19 -0800880 * {@link #extras} key: this is the remote input history, as supplied to
881 * {@link Builder#setRemoteInputHistory(CharSequence[])}.
Adrian Roos005e7742016-04-13 15:02:20 -0700882 *
883 * Apps can fill this through {@link Builder#setRemoteInputHistory(CharSequence[])}
884 * with the most recent inputs that have been sent through a {@link RemoteInput} of this
885 * Notification and are expected to clear it once the it is no longer relevant (e.g. for chat
886 * notifications once the other party has responded).
887 *
888 * The extra with this key is of type CharSequence[] and contains the most recent entry at
889 * the 0 index, the second most recent at the 1 index, etc.
890 *
891 * @see Builder#setRemoteInputHistory(CharSequence[])
Adrian Roose458aa82015-12-08 16:17:19 -0800892 */
893 public static final String EXTRA_REMOTE_INPUT_HISTORY = "android.remoteInputHistory";
894
895 /**
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400896 * {@link #extras} key: this is a small piece of additional text as supplied to
897 * {@link Builder#setContentInfo(CharSequence)}.
898 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400899 public static final String EXTRA_INFO_TEXT = "android.infoText";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400900
901 /**
902 * {@link #extras} key: this is a line of summary information intended to be shown
903 * alongside expanded notifications, as supplied to (e.g.)
904 * {@link BigTextStyle#setSummaryText(CharSequence)}.
905 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400906 public static final String EXTRA_SUMMARY_TEXT = "android.summaryText";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400907
908 /**
Christoph Studer4600f9b2014-07-22 22:44:43 +0200909 * {@link #extras} key: this is the longer text shown in the big form of a
910 * {@link BigTextStyle} notification, as supplied to
911 * {@link BigTextStyle#bigText(CharSequence)}.
912 */
913 public static final String EXTRA_BIG_TEXT = "android.bigText";
914
915 /**
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400916 * {@link #extras} key: this is the resource ID of the notification's main small icon, as
917 * supplied to {@link Builder#setSmallIcon(int)}.
Julia Reynoldse071abd2017-03-22 10:52:11 -0400918 *
919 * @deprecated Use {@link #getSmallIcon()}, which supports a wider variety of icon sources.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400920 */
Julia Reynoldse071abd2017-03-22 10:52:11 -0400921 @Deprecated
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -0500922 public static final String EXTRA_SMALL_ICON = "android.icon";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400923
924 /**
925 * {@link #extras} key: this is a bitmap to be used instead of the small icon when showing the
926 * notification payload, as
927 * supplied to {@link Builder#setLargeIcon(android.graphics.Bitmap)}.
Julia Reynoldse071abd2017-03-22 10:52:11 -0400928 *
929 * @deprecated Use {@link #getLargeIcon()}, which supports a wider variety of icon sources.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400930 */
Julia Reynoldse071abd2017-03-22 10:52:11 -0400931 @Deprecated
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400932 public static final String EXTRA_LARGE_ICON = "android.largeIcon";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400933
934 /**
935 * {@link #extras} key: this is a bitmap to be used instead of the one from
936 * {@link Builder#setLargeIcon(android.graphics.Bitmap)} when the notification is
937 * shown in its expanded form, as supplied to
938 * {@link BigPictureStyle#bigLargeIcon(android.graphics.Bitmap)}.
939 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400940 public static final String EXTRA_LARGE_ICON_BIG = EXTRA_LARGE_ICON + ".big";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400941
942 /**
943 * {@link #extras} key: this is the progress value supplied to
944 * {@link Builder#setProgress(int, int, boolean)}.
945 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400946 public static final String EXTRA_PROGRESS = "android.progress";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400947
948 /**
949 * {@link #extras} key: this is the maximum value supplied to
950 * {@link Builder#setProgress(int, int, boolean)}.
951 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400952 public static final String EXTRA_PROGRESS_MAX = "android.progressMax";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400953
954 /**
955 * {@link #extras} key: whether the progress bar is indeterminate, supplied to
956 * {@link Builder#setProgress(int, int, boolean)}.
957 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400958 public static final String EXTRA_PROGRESS_INDETERMINATE = "android.progressIndeterminate";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400959
960 /**
961 * {@link #extras} key: whether {@link #when} should be shown as a count-up timer (specifically
962 * a {@link android.widget.Chronometer}) instead of a timestamp, as supplied to
963 * {@link Builder#setUsesChronometer(boolean)}.
964 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400965 public static final String EXTRA_SHOW_CHRONOMETER = "android.showChronometer";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400966
967 /**
Selim Cinek81c23aa2016-02-25 16:23:13 -0800968 * {@link #extras} key: whether the chronometer set on the notification should count down
969 * instead of counting up. Is only relevant if key {@link #EXTRA_SHOW_CHRONOMETER} is present.
Adrian Roos96b7e202016-05-17 13:50:38 -0700970 * This extra is a boolean. The default is false.
Selim Cinek81c23aa2016-02-25 16:23:13 -0800971 */
Adrian Roos96b7e202016-05-17 13:50:38 -0700972 public static final String EXTRA_CHRONOMETER_COUNT_DOWN = "android.chronometerCountDown";
Selim Cinek81c23aa2016-02-25 16:23:13 -0800973
974 /**
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400975 * {@link #extras} key: whether {@link #when} should be shown,
976 * as supplied to {@link Builder#setShowWhen(boolean)}.
977 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400978 public static final String EXTRA_SHOW_WHEN = "android.showWhen";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400979
980 /**
981 * {@link #extras} key: this is a bitmap to be shown in {@link BigPictureStyle} expanded
982 * notifications, supplied to {@link BigPictureStyle#bigPicture(android.graphics.Bitmap)}.
983 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400984 public static final String EXTRA_PICTURE = "android.picture";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400985
986 /**
987 * {@link #extras} key: An array of CharSequences to show in {@link InboxStyle} expanded
988 * notifications, each of which was supplied to {@link InboxStyle#addLine(CharSequence)}.
989 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400990 public static final String EXTRA_TEXT_LINES = "android.textLines";
Dan Sandler842dd772014-05-15 09:36:47 -0400991
992 /**
993 * {@link #extras} key: A string representing the name of the specific
994 * {@link android.app.Notification.Style} used to create this notification.
995 */
Chris Wren91ad5632013-06-05 15:05:57 -0400996 public static final String EXTRA_TEMPLATE = "android.template";
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400997
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400998 /**
Chris Wrene6c48932014-09-29 17:19:27 -0400999 * {@link #extras} key: A String array containing the people that this notification relates to,
1000 * each of which was supplied to {@link Builder#addPerson(String)}.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001001 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -04001002 public static final String EXTRA_PEOPLE = "android.people";
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001003
1004 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -04001005 * Allow certain system-generated notifications to appear before the device is provisioned.
1006 * Only available to notifications coming from the android package.
1007 * @hide
1008 */
Maurice Lam96c10032017-03-29 15:42:38 -07001009 @SystemApi
John Spurlockfd7f1e02014-03-18 16:41:57 -04001010 public static final String EXTRA_ALLOW_DURING_SETUP = "android.allowDuringSetup";
1011
1012 /**
Jose Limae9e3b3b2014-05-18 23:44:50 -07001013 * {@link #extras} key: A
1014 * {@link android.content.ContentUris content URI} pointing to an image that can be displayed
1015 * in the background when the notification is selected. The URI must point to an image stream
1016 * suitable for passing into
1017 * {@link android.graphics.BitmapFactory#decodeStream(java.io.InputStream)
1018 * BitmapFactory.decodeStream}; all other content types will be ignored. The content provider
1019 * URI used for this purpose must require no permissions to read the image data.
1020 */
1021 public static final String EXTRA_BACKGROUND_IMAGE_URI = "android.backgroundImageUri";
1022
1023 /**
Dan Sandler842dd772014-05-15 09:36:47 -04001024 * {@link #extras} key: A
Jeff Browndba34ba2014-06-24 20:46:03 -07001025 * {@link android.media.session.MediaSession.Token} associated with a
Dan Sandler842dd772014-05-15 09:36:47 -04001026 * {@link android.app.Notification.MediaStyle} notification.
1027 */
1028 public static final String EXTRA_MEDIA_SESSION = "android.mediaSession";
1029
1030 /**
Bryan Mawhinneye191f902014-07-22 12:50:09 +01001031 * {@link #extras} key: the indices of actions to be shown in the compact view,
1032 * as supplied to (e.g.) {@link MediaStyle#setShowActionsInCompactView(int...)}.
1033 */
1034 public static final String EXTRA_COMPACT_ACTIONS = "android.compactActions";
1035
Christoph Studer943aa672014-08-03 20:31:16 +02001036 /**
Alex Hillsfc737de2016-03-23 17:33:02 -04001037 * {@link #extras} key: the username to be displayed for all messages sent by the user including
1038 * direct replies
Adrian Roos96b7e202016-05-17 13:50:38 -07001039 * {@link android.app.Notification.MessagingStyle} notification. This extra is a
1040 * {@link CharSequence}
Alex Hillsfc737de2016-03-23 17:33:02 -04001041 */
1042 public static final String EXTRA_SELF_DISPLAY_NAME = "android.selfDisplayName";
1043
1044 /**
Adrian Roos96b7e202016-05-17 13:50:38 -07001045 * {@link #extras} key: a {@link CharSequence} to be displayed as the title to a conversation
Alex Hillsd9b04d92016-04-11 16:38:16 -04001046 * represented by a {@link android.app.Notification.MessagingStyle}
Alex Hillsfc737de2016-03-23 17:33:02 -04001047 */
Alex Hillsd9b04d92016-04-11 16:38:16 -04001048 public static final String EXTRA_CONVERSATION_TITLE = "android.conversationTitle";
Alex Hillsfc737de2016-03-23 17:33:02 -04001049
1050 /**
1051 * {@link #extras} key: an array of {@link android.app.Notification.MessagingStyle.Message}
1052 * bundles provided by a
Adrian Roos96b7e202016-05-17 13:50:38 -07001053 * {@link android.app.Notification.MessagingStyle} notification. This extra is a parcelable
1054 * array of bundles.
Alex Hillsfc737de2016-03-23 17:33:02 -04001055 */
1056 public static final String EXTRA_MESSAGES = "android.messages";
1057
1058 /**
Adrian Roos437cd562017-01-18 15:47:03 -08001059 * {@link #extras} key: an array of
1060 * {@link android.app.Notification.MessagingStyle#addHistoricMessage historic}
1061 * {@link android.app.Notification.MessagingStyle.Message} bundles provided by a
1062 * {@link android.app.Notification.MessagingStyle} notification. This extra is a parcelable
1063 * array of bundles.
1064 */
1065 public static final String EXTRA_HISTORIC_MESSAGES = "android.messages.historic";
1066
1067 /**
Selim Cinek7b9605b2017-01-19 17:36:00 -08001068 * {@link #extras} key: whether the notification should be colorized as
1069 * supplied to {@link Builder#setColorized(boolean)}}.
1070 */
1071 public static final String EXTRA_COLORIZED = "android.colorized";
1072
1073 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04001074 * @hide
1075 */
1076 public static final String EXTRA_BUILDER_APPLICATION_INFO = "android.appInfo";
1077
Selim Cinek247fa012016-02-18 09:50:48 -08001078 /**
1079 * @hide
1080 */
1081 public static final String EXTRA_CONTAINS_CUSTOM_VIEW = "android.contains.customView";
1082
Shane Brennan472a3b32016-12-12 15:28:10 -08001083 /**
1084 * {@link #extras} key: the audio contents of this notification.
1085 *
1086 * This is for use when rendering the notification on an audio-focused interface;
1087 * the audio contents are a complete sound sample that contains the contents/body of the
1088 * notification. This may be used in substitute of a Text-to-Speech reading of the
1089 * notification. For example if the notification represents a voice message this should point
1090 * to the audio of that message.
1091 *
1092 * The data stored under this key should be a String representation of a Uri that contains the
1093 * audio contents in one of the following formats: WAV, PCM 16-bit, AMR-WB.
1094 *
1095 * This extra is unnecessary if you are using {@code MessagingStyle} since each {@code Message}
1096 * has a field for holding data URI. That field can be used for audio.
1097 * See {@code Message#setData}.
1098 *
1099 * Example usage:
1100 * <pre>
1101 * {@code
1102 * Notification.Builder myBuilder = (build your Notification as normal);
1103 * myBuilder.getExtras().putString(EXTRA_AUDIO_CONTENTS_URI, myAudioUri.toString());
1104 * }
1105 * </pre>
1106 */
1107 public static final String EXTRA_AUDIO_CONTENTS_URI = "android.audioContents";
1108
Dan Sandler80eaa592016-04-14 23:34:54 -04001109 /** @hide */
1110 @SystemApi
Dan Sandler732bd6c2016-04-12 14:20:32 -04001111 public static final String EXTRA_SUBSTITUTE_APP_NAME = "android.substName";
1112
Dan Sandlerd63f9322015-05-06 15:18:49 -04001113 private Icon mSmallIcon;
1114 private Icon mLargeIcon;
1115
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04001116 private String mChannelId;
Julia Reynolds2a128742016-11-28 14:29:25 -05001117 private long mTimeout;
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04001118
Julia Reynolds13d898c2017-02-02 12:22:05 -05001119 private String mShortcutId;
Julia Reynolds3aedded2017-03-31 14:42:09 -04001120 private CharSequence mSettingsText;
Julia Reynolds13d898c2017-02-02 12:22:05 -05001121
Julia Reynoldsa79c3712017-04-21 10:29:57 -04001122 /** @hide */
1123 @IntDef(prefix = { "GROUP_ALERT_" }, value = {
1124 GROUP_ALERT_ALL, GROUP_ALERT_CHILDREN, GROUP_ALERT_SUMMARY
1125 })
1126 @Retention(RetentionPolicy.SOURCE)
1127 public @interface GroupAlertBehavior {}
1128
1129 /**
1130 * Constant for {@link Builder#setGroupAlertBehavior(int)}, meaning that all notifications in a
1131 * group with sound or vibration ought to make sound or vibrate (respectively), so this
1132 * notification will not be muted when it is in a group.
1133 */
1134 public static final int GROUP_ALERT_ALL = 0;
1135
1136 /**
1137 * Constant for {@link Builder#setGroupAlertBehavior(int)}, meaning that all children
1138 * notification in a group should be silenced (no sound or vibration) even if they are posted
1139 * to a {@link NotificationChannel} that has sound and/or vibration. Use this constant to
1140 * mute this notification if this notification is a group child.
1141 *
1142 * <p> For example, you might want to use this constant if you post a number of children
1143 * notifications at once (say, after a periodic sync), and only need to notify the user
1144 * audibly once.
1145 */
1146 public static final int GROUP_ALERT_SUMMARY = 1;
1147
1148 /**
1149 * Constant for {@link Builder#setGroupAlertBehavior(int)}, meaning that the summary
1150 * notification in a group should be silenced (no sound or vibration) even if they are
1151 * posted to a {@link NotificationChannel} that has sound and/or vibration. Use this constant
1152 * to mute this notification if this notification is a group summary.
1153 *
1154 * <p>For example, you might want to use this constant if only the children notifications
1155 * in your group have content and the summary is only used to visually group notifications.
1156 */
1157 public static final int GROUP_ALERT_CHILDREN = 2;
1158
1159 private int mGroupAlertBehavior = GROUP_ALERT_ALL;
1160
Julia Reynolds13d898c2017-02-02 12:22:05 -05001161 /**
1162 * If this notification is being shown as a badge, always show as a number.
1163 */
1164 public static final int BADGE_ICON_NONE = 0;
1165
1166 /**
1167 * If this notification is being shown as a badge, use the {@link #getSmallIcon()} to
1168 * represent this notification.
1169 */
1170 public static final int BADGE_ICON_SMALL = 1;
1171
1172 /**
1173 * If this notification is being shown as a badge, use the {@link #getLargeIcon()} to
1174 * represent this notification.
1175 */
1176 public static final int BADGE_ICON_LARGE = 2;
Julia Reynolds7d5b4da2017-03-20 10:18:49 -04001177 private int mBadgeIcon = BADGE_ICON_NONE;
Julia Reynolds13d898c2017-02-02 12:22:05 -05001178
Chris Wren51c75102013-07-16 20:49:17 -04001179 /**
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001180 * Structure to encapsulate a named action that can be shown as part of this notification.
1181 * It must include an icon, a label, and a {@link PendingIntent} to be fired when the action is
1182 * selected by the user.
1183 * <p>
Griff Hazen959591e2014-05-15 22:26:18 -07001184 * Apps should use {@link Notification.Builder#addAction(int, CharSequence, PendingIntent)}
1185 * or {@link Notification.Builder#addAction(Notification.Action)}
1186 * to attach actions.
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001187 */
Daniel Sandlerea2a3172013-02-20 22:24:20 -05001188 public static class Action implements Parcelable {
Shane Brennan472a3b32016-12-12 15:28:10 -08001189 /**
1190 * {@link #extras} key: Keys to a {@link Parcelable} {@link ArrayList} of
1191 * {@link RemoteInput}s.
1192 *
1193 * This is intended for {@link RemoteInput}s that only accept data, meaning
1194 * {@link RemoteInput#getAllowFreeFormInput} is false, {@link RemoteInput#getChoices}
1195 * is null or empty, and {@link RemoteInput#getAllowedDataTypes is non-null and not
1196 * empty. These {@link RemoteInput}s will be ignored by devices that do not
1197 * support non-text-based {@link RemoteInput}s. See {@link Builder#build}.
1198 *
1199 * You can test if a RemoteInput matches these constraints using
1200 * {@link RemoteInput#isDataOnly}.
1201 */
1202 private static final String EXTRA_DATA_ONLY_INPUTS = "android.extra.DATA_ONLY_INPUTS";
1203
Griff Hazen959591e2014-05-15 22:26:18 -07001204 private final Bundle mExtras;
Dan Sandler86647982015-05-13 23:41:13 -04001205 private Icon mIcon;
Griff Hazen61a9e862014-05-22 16:05:19 -07001206 private final RemoteInput[] mRemoteInputs;
Alex Hills1f27f882017-01-12 11:24:46 -05001207 private boolean mAllowGeneratedReplies = true;
Griff Hazen959591e2014-05-15 22:26:18 -07001208
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001209 /**
1210 * Small icon representing the action.
Dan Sandler86647982015-05-13 23:41:13 -04001211 *
1212 * @deprecated Use {@link Action#getIcon()} instead.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001213 */
Dan Sandler86647982015-05-13 23:41:13 -04001214 @Deprecated
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001215 public int icon;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001216
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001217 /**
1218 * Title of the action.
1219 */
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001220 public CharSequence title;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001221
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001222 /**
1223 * Intent to send when the user invokes this action. May be null, in which case the action
1224 * may be rendered in a disabled presentation by the system UI.
1225 */
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001226 public PendingIntent actionIntent;
Griff Hazen959591e2014-05-15 22:26:18 -07001227
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001228 private Action(Parcel in) {
Dan Sandler86647982015-05-13 23:41:13 -04001229 if (in.readInt() != 0) {
1230 mIcon = Icon.CREATOR.createFromParcel(in);
Dan Sandler68079d52015-07-22 10:45:30 -04001231 if (mIcon.getType() == Icon.TYPE_RESOURCE) {
1232 icon = mIcon.getResId();
1233 }
Dan Sandler86647982015-05-13 23:41:13 -04001234 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001235 title = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
1236 if (in.readInt() == 1) {
1237 actionIntent = PendingIntent.CREATOR.createFromParcel(in);
1238 }
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001239 mExtras = Bundle.setDefusable(in.readBundle(), true);
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001240 mRemoteInputs = in.createTypedArray(RemoteInput.CREATOR);
Alex Hills42b0c4d2016-04-26 13:35:36 -04001241 mAllowGeneratedReplies = in.readInt() == 1;
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001242 }
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001243
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001244 /**
Dan Sandler86647982015-05-13 23:41:13 -04001245 * @deprecated Use {@link android.app.Notification.Action.Builder}.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001246 */
Dan Sandler86647982015-05-13 23:41:13 -04001247 @Deprecated
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001248 public Action(int icon, CharSequence title, PendingIntent intent) {
Alex Hills1f27f882017-01-12 11:24:46 -05001249 this(Icon.createWithResource("", icon), title, intent, new Bundle(), null, true);
Griff Hazen959591e2014-05-15 22:26:18 -07001250 }
1251
Adrian Roos7af53622016-10-12 13:44:05 -07001252 /** Keep in sync with {@link Notification.Action.Builder#Builder(Action)}! */
Dan Sandler86647982015-05-13 23:41:13 -04001253 private Action(Icon icon, CharSequence title, PendingIntent intent, Bundle extras,
Alex Hills42b0c4d2016-04-26 13:35:36 -04001254 RemoteInput[] remoteInputs, boolean allowGeneratedReplies) {
Dan Sandler86647982015-05-13 23:41:13 -04001255 this.mIcon = icon;
Gus Prevasf5bff46f2015-08-24 10:34:28 -04001256 if (icon != null && icon.getType() == Icon.TYPE_RESOURCE) {
1257 this.icon = icon.getResId();
1258 }
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001259 this.title = title;
1260 this.actionIntent = intent;
Griff Hazen959591e2014-05-15 22:26:18 -07001261 this.mExtras = extras != null ? extras : new Bundle();
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001262 this.mRemoteInputs = remoteInputs;
Alex Hills42b0c4d2016-04-26 13:35:36 -04001263 this.mAllowGeneratedReplies = allowGeneratedReplies;
Griff Hazen959591e2014-05-15 22:26:18 -07001264 }
1265
1266 /**
Dan Sandler86647982015-05-13 23:41:13 -04001267 * Return an icon representing the action.
1268 */
1269 public Icon getIcon() {
1270 if (mIcon == null && icon != 0) {
1271 // you snuck an icon in here without using the builder; let's try to keep it
1272 mIcon = Icon.createWithResource("", icon);
1273 }
1274 return mIcon;
1275 }
1276
1277 /**
Griff Hazen959591e2014-05-15 22:26:18 -07001278 * Get additional metadata carried around with this Action.
1279 */
1280 public Bundle getExtras() {
1281 return mExtras;
1282 }
1283
1284 /**
Alex Hills42b0c4d2016-04-26 13:35:36 -04001285 * Return whether the platform should automatically generate possible replies for this
1286 * {@link Action}
1287 */
1288 public boolean getAllowGeneratedReplies() {
1289 return mAllowGeneratedReplies;
1290 }
1291
1292 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001293 * Get the list of inputs to be collected from the user when this action is sent.
Shane Brennan472a3b32016-12-12 15:28:10 -08001294 * May return null if no remote inputs were added. Only returns inputs which accept
1295 * a text input. For inputs which only accept data use {@link #getDataOnlyRemoteInputs}.
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001296 */
1297 public RemoteInput[] getRemoteInputs() {
1298 return mRemoteInputs;
1299 }
1300
1301 /**
Shane Brennan472a3b32016-12-12 15:28:10 -08001302 * Get the list of inputs to be collected from the user that ONLY accept data when this
1303 * action is sent. These remote inputs are guaranteed to return true on a call to
1304 * {@link RemoteInput#isDataOnly}.
1305 *
Shane Brennanf670d6c2017-04-25 13:05:45 -07001306 * Returns null if there are no data-only remote inputs.
Shane Brennan472a3b32016-12-12 15:28:10 -08001307 *
1308 * This method exists so that legacy RemoteInput collectors that pre-date the addition
1309 * of non-textual RemoteInputs do not access these remote inputs.
1310 */
1311 public RemoteInput[] getDataOnlyRemoteInputs() {
1312 return (RemoteInput[]) mExtras.getParcelableArray(EXTRA_DATA_ONLY_INPUTS);
1313 }
1314
1315 /**
Griff Hazen959591e2014-05-15 22:26:18 -07001316 * Builder class for {@link Action} objects.
1317 */
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001318 public static final class Builder {
Dan Sandler86647982015-05-13 23:41:13 -04001319 private final Icon mIcon;
Griff Hazen959591e2014-05-15 22:26:18 -07001320 private final CharSequence mTitle;
1321 private final PendingIntent mIntent;
Alex Hills1f27f882017-01-12 11:24:46 -05001322 private boolean mAllowGeneratedReplies = true;
Griff Hazen959591e2014-05-15 22:26:18 -07001323 private final Bundle mExtras;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001324 private ArrayList<RemoteInput> mRemoteInputs;
Griff Hazen959591e2014-05-15 22:26:18 -07001325
1326 /**
1327 * Construct a new builder for {@link Action} object.
1328 * @param icon icon to show for this action
1329 * @param title the title of the action
1330 * @param intent the {@link PendingIntent} to fire when users trigger this action
1331 */
Dan Sandler86647982015-05-13 23:41:13 -04001332 @Deprecated
Griff Hazen959591e2014-05-15 22:26:18 -07001333 public Builder(int icon, CharSequence title, PendingIntent intent) {
Adrian Roos7af53622016-10-12 13:44:05 -07001334 this(Icon.createWithResource("", icon), title, intent);
Dan Sandler86647982015-05-13 23:41:13 -04001335 }
1336
1337 /**
1338 * Construct a new builder for {@link Action} object.
1339 * @param icon icon to show for this action
1340 * @param title the title of the action
1341 * @param intent the {@link PendingIntent} to fire when users trigger this action
1342 */
1343 public Builder(Icon icon, CharSequence title, PendingIntent intent) {
Alex Hills1f27f882017-01-12 11:24:46 -05001344 this(icon, title, intent, new Bundle(), null, true);
Griff Hazen959591e2014-05-15 22:26:18 -07001345 }
1346
1347 /**
1348 * Construct a new builder for {@link Action} object using the fields from an
1349 * {@link Action}.
1350 * @param action the action to read fields from.
1351 */
1352 public Builder(Action action) {
Adrian Roos7af53622016-10-12 13:44:05 -07001353 this(action.getIcon(), action.title, action.actionIntent,
1354 new Bundle(action.mExtras), action.getRemoteInputs(),
1355 action.getAllowGeneratedReplies());
Griff Hazen959591e2014-05-15 22:26:18 -07001356 }
1357
Dan Sandler86647982015-05-13 23:41:13 -04001358 private Builder(Icon icon, CharSequence title, PendingIntent intent, Bundle extras,
Adrian Roos7af53622016-10-12 13:44:05 -07001359 RemoteInput[] remoteInputs, boolean allowGeneratedReplies) {
Griff Hazen959591e2014-05-15 22:26:18 -07001360 mIcon = icon;
1361 mTitle = title;
1362 mIntent = intent;
1363 mExtras = extras;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001364 if (remoteInputs != null) {
1365 mRemoteInputs = new ArrayList<RemoteInput>(remoteInputs.length);
1366 Collections.addAll(mRemoteInputs, remoteInputs);
1367 }
Adrian Roos7af53622016-10-12 13:44:05 -07001368 mAllowGeneratedReplies = allowGeneratedReplies;
Griff Hazen959591e2014-05-15 22:26:18 -07001369 }
1370
1371 /**
1372 * Merge additional metadata into this builder.
1373 *
1374 * <p>Values within the Bundle will replace existing extras values in this Builder.
1375 *
1376 * @see Notification.Action#extras
1377 */
1378 public Builder addExtras(Bundle extras) {
1379 if (extras != null) {
1380 mExtras.putAll(extras);
1381 }
1382 return this;
1383 }
1384
1385 /**
1386 * Get the metadata Bundle used by this Builder.
1387 *
1388 * <p>The returned Bundle is shared with this Builder.
1389 */
1390 public Bundle getExtras() {
1391 return mExtras;
1392 }
1393
1394 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001395 * Add an input to be collected from the user when this action is sent.
1396 * Response values can be retrieved from the fired intent by using the
1397 * {@link RemoteInput#getResultsFromIntent} function.
1398 * @param remoteInput a {@link RemoteInput} to add to the action
1399 * @return this object for method chaining
1400 */
1401 public Builder addRemoteInput(RemoteInput remoteInput) {
1402 if (mRemoteInputs == null) {
1403 mRemoteInputs = new ArrayList<RemoteInput>();
1404 }
1405 mRemoteInputs.add(remoteInput);
1406 return this;
1407 }
1408
1409 /**
Alex Hills42b0c4d2016-04-26 13:35:36 -04001410 * Set whether the platform should automatically generate possible replies to add to
1411 * {@link RemoteInput#getChoices()}. If the {@link Action} doesn't have a
1412 * {@link RemoteInput}, this has no effect.
1413 * @param allowGeneratedReplies {@code true} to allow generated replies, {@code false}
1414 * otherwise
1415 * @return this object for method chaining
Alex Hills1f27f882017-01-12 11:24:46 -05001416 * The default value is {@code true}
Alex Hills42b0c4d2016-04-26 13:35:36 -04001417 */
1418 public Builder setAllowGeneratedReplies(boolean allowGeneratedReplies) {
1419 mAllowGeneratedReplies = allowGeneratedReplies;
1420 return this;
1421 }
1422
1423 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001424 * Apply an extender to this action builder. Extenders may be used to add
1425 * metadata or change options on this builder.
1426 */
Griff Hazen61a9e862014-05-22 16:05:19 -07001427 public Builder extend(Extender extender) {
1428 extender.extend(this);
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001429 return this;
1430 }
1431
1432 /**
Griff Hazen959591e2014-05-15 22:26:18 -07001433 * Combine all of the options that have been set and return a new {@link Action}
1434 * object.
1435 * @return the built action
1436 */
1437 public Action build() {
Shane Brennan472a3b32016-12-12 15:28:10 -08001438 ArrayList<RemoteInput> dataOnlyInputs = new ArrayList<>();
1439 RemoteInput[] previousDataInputs =
1440 (RemoteInput[]) mExtras.getParcelableArray(EXTRA_DATA_ONLY_INPUTS);
Felipe Lemedfd11962017-01-18 18:38:46 -08001441 if (previousDataInputs != null) {
Shane Brennan472a3b32016-12-12 15:28:10 -08001442 for (RemoteInput input : previousDataInputs) {
1443 dataOnlyInputs.add(input);
1444 }
1445 }
1446 List<RemoteInput> textInputs = new ArrayList<>();
1447 if (mRemoteInputs != null) {
1448 for (RemoteInput input : mRemoteInputs) {
1449 if (input.isDataOnly()) {
1450 dataOnlyInputs.add(input);
1451 } else {
1452 textInputs.add(input);
1453 }
1454 }
1455 }
1456 if (!dataOnlyInputs.isEmpty()) {
1457 RemoteInput[] dataInputsArr =
1458 dataOnlyInputs.toArray(new RemoteInput[dataOnlyInputs.size()]);
1459 mExtras.putParcelableArray(EXTRA_DATA_ONLY_INPUTS, dataInputsArr);
1460 }
1461 RemoteInput[] textInputsArr = textInputs.isEmpty()
1462 ? null : textInputs.toArray(new RemoteInput[textInputs.size()]);
1463 return new Action(mIcon, mTitle, mIntent, mExtras, textInputsArr,
Alex Hills42b0c4d2016-04-26 13:35:36 -04001464 mAllowGeneratedReplies);
Griff Hazen959591e2014-05-15 22:26:18 -07001465 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001466 }
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001467
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001468 @Override
1469 public Action clone() {
1470 return new Action(
Dan Sandler86647982015-05-13 23:41:13 -04001471 getIcon(),
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001472 title,
1473 actionIntent, // safe to alias
Julia Reynolds53c934c2016-09-16 14:03:43 -04001474 mExtras == null ? new Bundle() : new Bundle(mExtras),
Alex Hills42b0c4d2016-04-26 13:35:36 -04001475 getRemoteInputs(),
1476 getAllowGeneratedReplies());
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001477 }
1478 @Override
1479 public int describeContents() {
1480 return 0;
1481 }
1482 @Override
1483 public void writeToParcel(Parcel out, int flags) {
Dan Sandler86647982015-05-13 23:41:13 -04001484 final Icon ic = getIcon();
1485 if (ic != null) {
1486 out.writeInt(1);
1487 ic.writeToParcel(out, 0);
1488 } else {
1489 out.writeInt(0);
1490 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001491 TextUtils.writeToParcel(title, out, flags);
1492 if (actionIntent != null) {
1493 out.writeInt(1);
1494 actionIntent.writeToParcel(out, flags);
1495 } else {
1496 out.writeInt(0);
1497 }
Griff Hazen959591e2014-05-15 22:26:18 -07001498 out.writeBundle(mExtras);
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001499 out.writeTypedArray(mRemoteInputs, flags);
Alex Hills42b0c4d2016-04-26 13:35:36 -04001500 out.writeInt(mAllowGeneratedReplies ? 1 : 0);
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001501 }
Griff Hazen959591e2014-05-15 22:26:18 -07001502 public static final Parcelable.Creator<Action> CREATOR =
1503 new Parcelable.Creator<Action>() {
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001504 public Action createFromParcel(Parcel in) {
1505 return new Action(in);
1506 }
1507 public Action[] newArray(int size) {
1508 return new Action[size];
1509 }
1510 };
Griff Hazen61a9e862014-05-22 16:05:19 -07001511
1512 /**
1513 * Extender interface for use with {@link Builder#extend}. Extenders may be used to add
1514 * metadata or change options on an action builder.
1515 */
1516 public interface Extender {
1517 /**
1518 * Apply this extender to a notification action builder.
1519 * @param builder the builder to be modified.
1520 * @return the build object for chaining.
1521 */
1522 public Builder extend(Builder builder);
1523 }
1524
1525 /**
1526 * Wearable extender for notification actions. To add extensions to an action,
1527 * create a new {@link android.app.Notification.Action.WearableExtender} object using
1528 * the {@code WearableExtender()} constructor and apply it to a
1529 * {@link android.app.Notification.Action.Builder} using
1530 * {@link android.app.Notification.Action.Builder#extend}.
1531 *
1532 * <pre class="prettyprint">
1533 * Notification.Action action = new Notification.Action.Builder(
1534 * R.drawable.archive_all, "Archive all", actionIntent)
Griff Hazen14f57992014-05-26 09:07:14 -07001535 * .extend(new Notification.Action.WearableExtender()
Griff Hazen61a9e862014-05-22 16:05:19 -07001536 * .setAvailableOffline(false))
Griff Hazen14f57992014-05-26 09:07:14 -07001537 * .build();</pre>
Griff Hazen61a9e862014-05-22 16:05:19 -07001538 */
1539 public static final class WearableExtender implements Extender {
1540 /** Notification action extra which contains wearable extensions */
1541 private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS";
1542
Pete Gastaf6781d2014-10-07 15:17:05 -04001543 // Keys within EXTRA_WEARABLE_EXTENSIONS for wearable options.
Griff Hazen61a9e862014-05-22 16:05:19 -07001544 private static final String KEY_FLAGS = "flags";
Pete Gastaf6781d2014-10-07 15:17:05 -04001545 private static final String KEY_IN_PROGRESS_LABEL = "inProgressLabel";
1546 private static final String KEY_CONFIRM_LABEL = "confirmLabel";
1547 private static final String KEY_CANCEL_LABEL = "cancelLabel";
Griff Hazen61a9e862014-05-22 16:05:19 -07001548
1549 // Flags bitwise-ored to mFlags
1550 private static final int FLAG_AVAILABLE_OFFLINE = 0x1;
Alex Hills9ab3a232016-04-05 14:54:56 -04001551 private static final int FLAG_HINT_LAUNCHES_ACTIVITY = 1 << 1;
Alex Hills9f087612016-06-07 09:08:59 -04001552 private static final int FLAG_HINT_DISPLAY_INLINE = 1 << 2;
Griff Hazen61a9e862014-05-22 16:05:19 -07001553
1554 // Default value for flags integer
1555 private static final int DEFAULT_FLAGS = FLAG_AVAILABLE_OFFLINE;
1556
1557 private int mFlags = DEFAULT_FLAGS;
1558
Pete Gastaf6781d2014-10-07 15:17:05 -04001559 private CharSequence mInProgressLabel;
1560 private CharSequence mConfirmLabel;
1561 private CharSequence mCancelLabel;
1562
Griff Hazen61a9e862014-05-22 16:05:19 -07001563 /**
1564 * Create a {@link android.app.Notification.Action.WearableExtender} with default
1565 * options.
1566 */
1567 public WearableExtender() {
1568 }
1569
1570 /**
1571 * Create a {@link android.app.Notification.Action.WearableExtender} by reading
1572 * wearable options present in an existing notification action.
1573 * @param action the notification action to inspect.
1574 */
1575 public WearableExtender(Action action) {
1576 Bundle wearableBundle = action.getExtras().getBundle(EXTRA_WEARABLE_EXTENSIONS);
1577 if (wearableBundle != null) {
1578 mFlags = wearableBundle.getInt(KEY_FLAGS, DEFAULT_FLAGS);
Pete Gastaf6781d2014-10-07 15:17:05 -04001579 mInProgressLabel = wearableBundle.getCharSequence(KEY_IN_PROGRESS_LABEL);
1580 mConfirmLabel = wearableBundle.getCharSequence(KEY_CONFIRM_LABEL);
1581 mCancelLabel = wearableBundle.getCharSequence(KEY_CANCEL_LABEL);
Griff Hazen61a9e862014-05-22 16:05:19 -07001582 }
1583 }
1584
1585 /**
1586 * Apply wearable extensions to a notification action that is being built. This is
1587 * typically called by the {@link android.app.Notification.Action.Builder#extend}
1588 * method of {@link android.app.Notification.Action.Builder}.
1589 */
1590 @Override
1591 public Action.Builder extend(Action.Builder builder) {
1592 Bundle wearableBundle = new Bundle();
1593
1594 if (mFlags != DEFAULT_FLAGS) {
1595 wearableBundle.putInt(KEY_FLAGS, mFlags);
1596 }
Pete Gastaf6781d2014-10-07 15:17:05 -04001597 if (mInProgressLabel != null) {
1598 wearableBundle.putCharSequence(KEY_IN_PROGRESS_LABEL, mInProgressLabel);
1599 }
1600 if (mConfirmLabel != null) {
1601 wearableBundle.putCharSequence(KEY_CONFIRM_LABEL, mConfirmLabel);
1602 }
1603 if (mCancelLabel != null) {
1604 wearableBundle.putCharSequence(KEY_CANCEL_LABEL, mCancelLabel);
1605 }
Griff Hazen61a9e862014-05-22 16:05:19 -07001606
1607 builder.getExtras().putBundle(EXTRA_WEARABLE_EXTENSIONS, wearableBundle);
1608 return builder;
1609 }
1610
1611 @Override
1612 public WearableExtender clone() {
1613 WearableExtender that = new WearableExtender();
1614 that.mFlags = this.mFlags;
Pete Gastaf6781d2014-10-07 15:17:05 -04001615 that.mInProgressLabel = this.mInProgressLabel;
1616 that.mConfirmLabel = this.mConfirmLabel;
1617 that.mCancelLabel = this.mCancelLabel;
Griff Hazen61a9e862014-05-22 16:05:19 -07001618 return that;
1619 }
1620
1621 /**
1622 * Set whether this action is available when the wearable device is not connected to
1623 * a companion device. The user can still trigger this action when the wearable device is
1624 * offline, but a visual hint will indicate that the action may not be available.
1625 * Defaults to true.
1626 */
1627 public WearableExtender setAvailableOffline(boolean availableOffline) {
1628 setFlag(FLAG_AVAILABLE_OFFLINE, availableOffline);
1629 return this;
1630 }
1631
1632 /**
1633 * Get whether this action is available when the wearable device is not connected to
1634 * a companion device. The user can still trigger this action when the wearable device is
1635 * offline, but a visual hint will indicate that the action may not be available.
1636 * Defaults to true.
1637 */
1638 public boolean isAvailableOffline() {
1639 return (mFlags & FLAG_AVAILABLE_OFFLINE) != 0;
1640 }
1641
1642 private void setFlag(int mask, boolean value) {
1643 if (value) {
1644 mFlags |= mask;
1645 } else {
1646 mFlags &= ~mask;
1647 }
1648 }
Pete Gastaf6781d2014-10-07 15:17:05 -04001649
1650 /**
1651 * Set a label to display while the wearable is preparing to automatically execute the
1652 * action. This is usually a 'ing' verb ending in ellipsis like "Sending..."
1653 *
1654 * @param label the label to display while the action is being prepared to execute
1655 * @return this object for method chaining
1656 */
1657 public WearableExtender setInProgressLabel(CharSequence label) {
1658 mInProgressLabel = label;
1659 return this;
1660 }
1661
1662 /**
1663 * Get the label to display while the wearable is preparing to automatically execute
1664 * the action. This is usually a 'ing' verb ending in ellipsis like "Sending..."
1665 *
1666 * @return the label to display while the action is being prepared to execute
1667 */
1668 public CharSequence getInProgressLabel() {
1669 return mInProgressLabel;
1670 }
1671
1672 /**
1673 * Set a label to display to confirm that the action should be executed.
1674 * This is usually an imperative verb like "Send".
1675 *
1676 * @param label the label to confirm the action should be executed
1677 * @return this object for method chaining
1678 */
1679 public WearableExtender setConfirmLabel(CharSequence label) {
1680 mConfirmLabel = label;
1681 return this;
1682 }
1683
1684 /**
1685 * Get the label to display to confirm that the action should be executed.
1686 * This is usually an imperative verb like "Send".
1687 *
1688 * @return the label to confirm the action should be executed
1689 */
1690 public CharSequence getConfirmLabel() {
1691 return mConfirmLabel;
1692 }
1693
1694 /**
1695 * Set a label to display to cancel the action.
1696 * This is usually an imperative verb, like "Cancel".
1697 *
1698 * @param label the label to display to cancel the action
1699 * @return this object for method chaining
1700 */
1701 public WearableExtender setCancelLabel(CharSequence label) {
1702 mCancelLabel = label;
1703 return this;
1704 }
1705
1706 /**
1707 * Get the label to display to cancel the action.
1708 * This is usually an imperative verb like "Cancel".
1709 *
1710 * @return the label to display to cancel the action
1711 */
1712 public CharSequence getCancelLabel() {
1713 return mCancelLabel;
1714 }
Alex Hills9ab3a232016-04-05 14:54:56 -04001715
1716 /**
1717 * Set a hint that this Action will launch an {@link Activity} directly, telling the
1718 * platform that it can generate the appropriate transitions.
1719 * @param hintLaunchesActivity {@code true} if the content intent will launch
1720 * an activity and transitions should be generated, false otherwise.
1721 * @return this object for method chaining
1722 */
Alex Hills4ec3ff42016-04-12 11:36:18 -04001723 public WearableExtender setHintLaunchesActivity(
Alex Hills9ab3a232016-04-05 14:54:56 -04001724 boolean hintLaunchesActivity) {
1725 setFlag(FLAG_HINT_LAUNCHES_ACTIVITY, hintLaunchesActivity);
1726 return this;
1727 }
1728
1729 /**
1730 * Get a hint that this Action will launch an {@link Activity} directly, telling the
1731 * platform that it can generate the appropriate transitions
1732 * @return {@code true} if the content intent will launch an activity and transitions
1733 * should be generated, false otherwise. The default value is {@code false} if this was
1734 * never set.
1735 */
Alex Hills4ec3ff42016-04-12 11:36:18 -04001736 public boolean getHintLaunchesActivity() {
Alex Hills9ab3a232016-04-05 14:54:56 -04001737 return (mFlags & FLAG_HINT_LAUNCHES_ACTIVITY) != 0;
1738 }
Alex Hills9f087612016-06-07 09:08:59 -04001739
1740 /**
1741 * Set a hint that this Action should be displayed inline.
1742 *
1743 * @param hintDisplayInline {@code true} if action should be displayed inline, false
1744 * otherwise
1745 * @return this object for method chaining
1746 */
1747 public WearableExtender setHintDisplayActionInline(
1748 boolean hintDisplayInline) {
1749 setFlag(FLAG_HINT_DISPLAY_INLINE, hintDisplayInline);
1750 return this;
1751 }
1752
1753 /**
1754 * Get a hint that this Action should be displayed inline.
1755 *
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08001756 * @return {@code true} if the Action should be displayed inline, {@code false}
Alex Hills9f087612016-06-07 09:08:59 -04001757 * otherwise. The default value is {@code false} if this was never set.
1758 */
1759 public boolean getHintDisplayActionInline() {
1760 return (mFlags & FLAG_HINT_DISPLAY_INLINE) != 0;
1761 }
Griff Hazen61a9e862014-05-22 16:05:19 -07001762 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001763 }
1764
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001765 /**
1766 * Array of all {@link Action} structures attached to this notification by
1767 * {@link Builder#addAction(int, CharSequence, PendingIntent)}. Mostly useful for instances of
1768 * {@link android.service.notification.NotificationListenerService} that provide an alternative
1769 * interface for invoking actions.
1770 */
Daniel Sandlerea2a3172013-02-20 22:24:20 -05001771 public Action[] actions;
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001772
1773 /**
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001774 * Replacement version of this notification whose content will be shown
1775 * in an insecure context such as atop a secure keyguard. See {@link #visibility}
1776 * and {@link #VISIBILITY_PUBLIC}.
1777 */
1778 public Notification publicVersion;
1779
1780 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001781 * Constructs a Notification object with default values.
Joe Onorato46439ce2010-11-19 13:56:21 -08001782 * You might want to consider using {@link Builder} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001783 */
1784 public Notification()
1785 {
1786 this.when = System.currentTimeMillis();
Selim Cinekb85f36fd2016-04-20 18:46:36 -07001787 this.creationTime = System.currentTimeMillis();
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001788 this.priority = PRIORITY_DEFAULT;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001789 }
1790
1791 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001792 * @hide
1793 */
1794 public Notification(Context context, int icon, CharSequence tickerText, long when,
1795 CharSequence contentTitle, CharSequence contentText, Intent contentIntent)
1796 {
Chris Wren1ce4b6d2015-06-11 10:19:43 -04001797 new Builder(context)
1798 .setWhen(when)
1799 .setSmallIcon(icon)
1800 .setTicker(tickerText)
1801 .setContentTitle(contentTitle)
1802 .setContentText(contentText)
1803 .setContentIntent(PendingIntent.getActivity(context, 0, contentIntent, 0))
1804 .buildInto(this);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001805 }
1806
1807 /**
1808 * Constructs a Notification object with the information needed to
1809 * have a status bar icon without the standard expanded view.
1810 *
1811 * @param icon The resource id of the icon to put in the status bar.
1812 * @param tickerText The text that flows by in the status bar when the notification first
1813 * activates.
1814 * @param when The time to show in the time field. In the System.currentTimeMillis
1815 * timebase.
Joe Onorato46439ce2010-11-19 13:56:21 -08001816 *
1817 * @deprecated Use {@link Builder} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001818 */
Joe Onorato46439ce2010-11-19 13:56:21 -08001819 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001820 public Notification(int icon, CharSequence tickerText, long when)
1821 {
1822 this.icon = icon;
1823 this.tickerText = tickerText;
1824 this.when = when;
Selim Cinekb85f36fd2016-04-20 18:46:36 -07001825 this.creationTime = System.currentTimeMillis();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001826 }
1827
1828 /**
1829 * Unflatten the notification from a parcel.
1830 */
Svet Ganovddb94882016-06-23 19:55:24 -07001831 @SuppressWarnings("unchecked")
1832 public Notification(Parcel parcel) {
1833 // IMPORTANT: Add unmarshaling code in readFromParcel as the pending
1834 // intents in extras are always written as the last entry.
1835 readFromParcelImpl(parcel);
1836 // Must be read last!
Felipe Lemedd85da62016-06-28 11:29:54 -07001837 allPendingIntents = (ArraySet<PendingIntent>) parcel.readArraySet(null);
Svet Ganovddb94882016-06-23 19:55:24 -07001838 }
1839
1840 private void readFromParcelImpl(Parcel parcel)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001841 {
1842 int version = parcel.readInt();
1843
Dianne Hackborn98305522017-05-05 17:53:53 -07001844 whitelistToken = parcel.readStrongBinder();
1845 if (whitelistToken == null) {
1846 whitelistToken = processWhitelistToken;
1847 }
1848 // Propagate this token to all pending intents that are unmarshalled from the parcel.
1849 parcel.setClassCookie(PendingIntent.class, whitelistToken);
1850
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001851 when = parcel.readLong();
Selim Cinekb85f36fd2016-04-20 18:46:36 -07001852 creationTime = parcel.readLong();
Dan Sandler3936e7a2015-05-19 20:59:12 -04001853 if (parcel.readInt() != 0) {
1854 mSmallIcon = Icon.CREATOR.createFromParcel(parcel);
Dan Sandler4e787062015-06-17 15:09:48 -04001855 if (mSmallIcon.getType() == Icon.TYPE_RESOURCE) {
1856 icon = mSmallIcon.getResId();
1857 }
Dan Sandler3936e7a2015-05-19 20:59:12 -04001858 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001859 number = parcel.readInt();
1860 if (parcel.readInt() != 0) {
1861 contentIntent = PendingIntent.CREATOR.createFromParcel(parcel);
1862 }
1863 if (parcel.readInt() != 0) {
1864 deleteIntent = PendingIntent.CREATOR.createFromParcel(parcel);
1865 }
1866 if (parcel.readInt() != 0) {
1867 tickerText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
1868 }
1869 if (parcel.readInt() != 0) {
Joe Onorato46439ce2010-11-19 13:56:21 -08001870 tickerView = RemoteViews.CREATOR.createFromParcel(parcel);
Joe Onoratoef1e7762010-09-17 18:38:38 -04001871 }
1872 if (parcel.readInt() != 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001873 contentView = RemoteViews.CREATOR.createFromParcel(parcel);
1874 }
Joe Onorato561d3852010-11-20 18:09:34 -08001875 if (parcel.readInt() != 0) {
Dan Sandlerd63f9322015-05-06 15:18:49 -04001876 mLargeIcon = Icon.CREATOR.createFromParcel(parcel);
Joe Onorato561d3852010-11-20 18:09:34 -08001877 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001878 defaults = parcel.readInt();
1879 flags = parcel.readInt();
1880 if (parcel.readInt() != 0) {
1881 sound = Uri.CREATOR.createFromParcel(parcel);
1882 }
1883
1884 audioStreamType = parcel.readInt();
John Spurlockc0650f022014-07-19 13:22:39 -04001885 if (parcel.readInt() != 0) {
1886 audioAttributes = AudioAttributes.CREATOR.createFromParcel(parcel);
1887 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001888 vibrate = parcel.createLongArray();
1889 ledARGB = parcel.readInt();
1890 ledOnMS = parcel.readInt();
1891 ledOffMS = parcel.readInt();
1892 iconLevel = parcel.readInt();
Daniel Sandlere46cbd32010-06-17 10:35:26 -04001893
1894 if (parcel.readInt() != 0) {
1895 fullScreenIntent = PendingIntent.CREATOR.createFromParcel(parcel);
1896 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001897
1898 priority = parcel.readInt();
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001899
John Spurlockfd7f1e02014-03-18 16:41:57 -04001900 category = parcel.readString();
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001901
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001902 mGroupKey = parcel.readString();
1903
1904 mSortKey = parcel.readString();
1905
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001906 extras = Bundle.setDefusable(parcel.readBundle(), true); // may be null
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001907
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001908 actions = parcel.createTypedArray(Action.CREATOR); // may be null
1909
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001910 if (parcel.readInt() != 0) {
Daniel Sandlerf3b73432012-03-27 15:01:25 -04001911 bigContentView = RemoteViews.CREATOR.createFromParcel(parcel);
1912 }
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001913
Chris Wren8fd39ec2014-02-27 17:43:26 -05001914 if (parcel.readInt() != 0) {
1915 headsUpContentView = RemoteViews.CREATOR.createFromParcel(parcel);
1916 }
1917
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001918 visibility = parcel.readInt();
1919
1920 if (parcel.readInt() != 0) {
1921 publicVersion = Notification.CREATOR.createFromParcel(parcel);
1922 }
Dan Sandler26e81cf2014-05-06 10:01:27 -04001923
1924 color = parcel.readInt();
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04001925
1926 if (parcel.readInt() != 0) {
1927 mChannelId = parcel.readString();
1928 }
Julia Reynolds2a128742016-11-28 14:29:25 -05001929 mTimeout = parcel.readLong();
Julia Reynolds13d898c2017-02-02 12:22:05 -05001930
1931 if (parcel.readInt() != 0) {
1932 mShortcutId = parcel.readString();
1933 }
1934
1935 mBadgeIcon = parcel.readInt();
Julia Reynolds3aedded2017-03-31 14:42:09 -04001936
1937 if (parcel.readInt() != 0) {
1938 mSettingsText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
1939 }
Julia Reynoldsa79c3712017-04-21 10:29:57 -04001940
1941 mGroupAlertBehavior = parcel.readInt();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001942 }
1943
Andy Stadler110988c2010-12-03 14:29:16 -08001944 @Override
Joe Onorato18e69df2010-05-17 22:26:12 -07001945 public Notification clone() {
1946 Notification that = new Notification();
Daniel Sandler1a497d32013-04-18 14:52:45 -04001947 cloneInto(that, true);
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001948 return that;
1949 }
Joe Onorato18e69df2010-05-17 22:26:12 -07001950
Daniel Sandler1a497d32013-04-18 14:52:45 -04001951 /**
1952 * Copy all (or if heavy is false, all except Bitmaps and RemoteViews) members
1953 * of this into that.
1954 * @hide
1955 */
1956 public void cloneInto(Notification that, boolean heavy) {
Dianne Hackborn98305522017-05-05 17:53:53 -07001957 that.whitelistToken = this.whitelistToken;
Joe Onorato18e69df2010-05-17 22:26:12 -07001958 that.when = this.when;
Selim Cinekb85f36fd2016-04-20 18:46:36 -07001959 that.creationTime = this.creationTime;
Dan Sandlerd63f9322015-05-06 15:18:49 -04001960 that.mSmallIcon = this.mSmallIcon;
Joe Onorato18e69df2010-05-17 22:26:12 -07001961 that.number = this.number;
1962
1963 // PendingIntents are global, so there's no reason (or way) to clone them.
1964 that.contentIntent = this.contentIntent;
1965 that.deleteIntent = this.deleteIntent;
Daniel Sandlere46cbd32010-06-17 10:35:26 -04001966 that.fullScreenIntent = this.fullScreenIntent;
Joe Onorato18e69df2010-05-17 22:26:12 -07001967
1968 if (this.tickerText != null) {
1969 that.tickerText = this.tickerText.toString();
1970 }
Daniel Sandler1a497d32013-04-18 14:52:45 -04001971 if (heavy && this.tickerView != null) {
Joe Onorato46439ce2010-11-19 13:56:21 -08001972 that.tickerView = this.tickerView.clone();
Joe Onoratoef1e7762010-09-17 18:38:38 -04001973 }
Daniel Sandler1a497d32013-04-18 14:52:45 -04001974 if (heavy && this.contentView != null) {
Joe Onorato18e69df2010-05-17 22:26:12 -07001975 that.contentView = this.contentView.clone();
1976 }
Dan Sandlerd63f9322015-05-06 15:18:49 -04001977 if (heavy && this.mLargeIcon != null) {
1978 that.mLargeIcon = this.mLargeIcon;
Joe Onorato561d3852010-11-20 18:09:34 -08001979 }
Jozef BABJAKa8b91832011-02-22 08:05:08 +01001980 that.iconLevel = this.iconLevel;
Joe Onorato18e69df2010-05-17 22:26:12 -07001981 that.sound = this.sound; // android.net.Uri is immutable
1982 that.audioStreamType = this.audioStreamType;
John Spurlockc0650f022014-07-19 13:22:39 -04001983 if (this.audioAttributes != null) {
1984 that.audioAttributes = new AudioAttributes.Builder(this.audioAttributes).build();
1985 }
Joe Onorato18e69df2010-05-17 22:26:12 -07001986
1987 final long[] vibrate = this.vibrate;
1988 if (vibrate != null) {
1989 final int N = vibrate.length;
1990 final long[] vib = that.vibrate = new long[N];
1991 System.arraycopy(vibrate, 0, vib, 0, N);
1992 }
1993
1994 that.ledARGB = this.ledARGB;
1995 that.ledOnMS = this.ledOnMS;
1996 that.ledOffMS = this.ledOffMS;
1997 that.defaults = this.defaults;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001998
Joe Onorato18e69df2010-05-17 22:26:12 -07001999 that.flags = this.flags;
2000
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002001 that.priority = this.priority;
Joe Malin8d40d042012-11-05 11:36:40 -08002002
John Spurlockfd7f1e02014-03-18 16:41:57 -04002003 that.category = this.category;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002004
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002005 that.mGroupKey = this.mGroupKey;
2006
2007 that.mSortKey = this.mSortKey;
2008
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002009 if (this.extras != null) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04002010 try {
2011 that.extras = new Bundle(this.extras);
2012 // will unparcel
2013 that.extras.size();
2014 } catch (BadParcelableException e) {
2015 Log.e(TAG, "could not unparcel extras from notification: " + this, e);
2016 that.extras = null;
2017 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002018 }
2019
Felipe Lemedd85da62016-06-28 11:29:54 -07002020 if (!ArrayUtils.isEmpty(allPendingIntents)) {
2021 that.allPendingIntents = new ArraySet<>(allPendingIntents);
Svet Ganovddb94882016-06-23 19:55:24 -07002022 }
2023
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002024 if (this.actions != null) {
2025 that.actions = new Action[this.actions.length];
2026 for(int i=0; i<this.actions.length; i++) {
liangweikang63b03b52017-03-16 19:22:15 +08002027 if ( this.actions[i] != null) {
2028 that.actions[i] = this.actions[i].clone();
2029 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002030 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002031 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002032
Daniel Sandler1a497d32013-04-18 14:52:45 -04002033 if (heavy && this.bigContentView != null) {
Daniel Sandlerf3b73432012-03-27 15:01:25 -04002034 that.bigContentView = this.bigContentView.clone();
2035 }
Daniel Sandler1a497d32013-04-18 14:52:45 -04002036
Chris Wren8fd39ec2014-02-27 17:43:26 -05002037 if (heavy && this.headsUpContentView != null) {
2038 that.headsUpContentView = this.headsUpContentView.clone();
2039 }
2040
Dan Sandler0bf2ed82013-12-21 23:33:41 -06002041 that.visibility = this.visibility;
2042
2043 if (this.publicVersion != null) {
2044 that.publicVersion = new Notification();
2045 this.publicVersion.cloneInto(that.publicVersion, heavy);
2046 }
2047
Dan Sandler26e81cf2014-05-06 10:01:27 -04002048 that.color = this.color;
2049
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04002050 that.mChannelId = this.mChannelId;
Julia Reynolds2a128742016-11-28 14:29:25 -05002051 that.mTimeout = this.mTimeout;
Julia Reynolds3aedded2017-03-31 14:42:09 -04002052 that.mShortcutId = this.mShortcutId;
2053 that.mBadgeIcon = this.mBadgeIcon;
2054 that.mSettingsText = this.mSettingsText;
Julia Reynoldsa79c3712017-04-21 10:29:57 -04002055 that.mGroupAlertBehavior = this.mGroupAlertBehavior;
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04002056
Daniel Sandler1a497d32013-04-18 14:52:45 -04002057 if (!heavy) {
2058 that.lightenPayload(); // will clean out extras
2059 }
2060 }
2061
2062 /**
2063 * Removes heavyweight parts of the Notification object for archival or for sending to
2064 * listeners when the full contents are not necessary.
2065 * @hide
2066 */
2067 public final void lightenPayload() {
2068 tickerView = null;
2069 contentView = null;
2070 bigContentView = null;
Chris Wren8fd39ec2014-02-27 17:43:26 -05002071 headsUpContentView = null;
Dan Sandlerd63f9322015-05-06 15:18:49 -04002072 mLargeIcon = null;
Dan Sandler50128532015-12-08 15:42:41 -05002073 if (extras != null && !extras.isEmpty()) {
2074 final Set<String> keyset = extras.keySet();
2075 final int N = keyset.size();
2076 final String[] keys = keyset.toArray(new String[N]);
2077 for (int i=0; i<N; i++) {
2078 final String key = keys[i];
Dmitri Plotnikov22281362017-01-30 11:16:21 -08002079 if (TvExtender.EXTRA_TV_EXTENDER.equals(key)) {
2080 continue;
2081 }
Dan Sandler50128532015-12-08 15:42:41 -05002082 final Object obj = extras.get(key);
2083 if (obj != null &&
2084 ( obj instanceof Parcelable
2085 || obj instanceof Parcelable[]
2086 || obj instanceof SparseArray
2087 || obj instanceof ArrayList)) {
2088 extras.remove(key);
2089 }
2090 }
Daniel Sandler1a497d32013-04-18 14:52:45 -04002091 }
Joe Onorato18e69df2010-05-17 22:26:12 -07002092 }
2093
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04002094 /**
2095 * Make sure this CharSequence is safe to put into a bundle, which basically
2096 * means it had better not be some custom Parcelable implementation.
2097 * @hide
2098 */
2099 public static CharSequence safeCharSequence(CharSequence cs) {
Christoph Studer535ec612014-09-03 15:47:47 +02002100 if (cs == null) return cs;
2101 if (cs.length() > MAX_CHARSEQUENCE_LENGTH) {
2102 cs = cs.subSequence(0, MAX_CHARSEQUENCE_LENGTH);
2103 }
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04002104 if (cs instanceof Parcelable) {
2105 Log.e(TAG, "warning: " + cs.getClass().getCanonicalName()
2106 + " instance is a custom Parcelable and not allowed in Notification");
2107 return cs.toString();
2108 }
Selim Cinek60a54252016-02-26 17:03:25 -08002109 return removeTextSizeSpans(cs);
2110 }
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04002111
Selim Cinek60a54252016-02-26 17:03:25 -08002112 private static CharSequence removeTextSizeSpans(CharSequence charSequence) {
2113 if (charSequence instanceof Spanned) {
2114 Spanned ss = (Spanned) charSequence;
2115 Object[] spans = ss.getSpans(0, ss.length(), Object.class);
2116 SpannableStringBuilder builder = new SpannableStringBuilder(ss.toString());
2117 for (Object span : spans) {
2118 Object resultSpan = span;
Selim Cinek89991a22016-03-07 19:51:54 -08002119 if (resultSpan instanceof CharacterStyle) {
2120 resultSpan = ((CharacterStyle) span).getUnderlying();
2121 }
2122 if (resultSpan instanceof TextAppearanceSpan) {
2123 TextAppearanceSpan originalSpan = (TextAppearanceSpan) resultSpan;
Selim Cinek60a54252016-02-26 17:03:25 -08002124 resultSpan = new TextAppearanceSpan(
2125 originalSpan.getFamily(),
2126 originalSpan.getTextStyle(),
2127 -1,
2128 originalSpan.getTextColor(),
2129 originalSpan.getLinkTextColor());
Selim Cinek89991a22016-03-07 19:51:54 -08002130 } else if (resultSpan instanceof RelativeSizeSpan
2131 || resultSpan instanceof AbsoluteSizeSpan) {
Selim Cinek60a54252016-02-26 17:03:25 -08002132 continue;
Selim Cinek89991a22016-03-07 19:51:54 -08002133 } else {
2134 resultSpan = span;
Selim Cinek60a54252016-02-26 17:03:25 -08002135 }
2136 builder.setSpan(resultSpan, ss.getSpanStart(span), ss.getSpanEnd(span),
2137 ss.getSpanFlags(span));
2138 }
2139 return builder;
2140 }
2141 return charSequence;
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04002142 }
2143
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002144 public int describeContents() {
2145 return 0;
2146 }
2147
2148 /**
Dan Sandler4e787062015-06-17 15:09:48 -04002149 * Flatten this notification into a parcel.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002150 */
Svet Ganovddb94882016-06-23 19:55:24 -07002151 public void writeToParcel(Parcel parcel, int flags) {
2152 // We need to mark all pending intents getting into the notification
2153 // system as being put there to later allow the notification ranker
2154 // to launch them and by doing so add the app to the battery saver white
2155 // list for a short period of time. The problem is that the system
2156 // cannot look into the extras as there may be parcelables there that
2157 // the platform does not know how to handle. To go around that we have
2158 // an explicit list of the pending intents in the extras bundle.
Felipe Lemedd85da62016-06-28 11:29:54 -07002159 final boolean collectPendingIntents = (allPendingIntents == null);
Svet Ganovddb94882016-06-23 19:55:24 -07002160 if (collectPendingIntents) {
2161 PendingIntent.setOnMarshaledListener(
2162 (PendingIntent intent, Parcel out, int outFlags) -> {
2163 if (parcel == out) {
Felipe Lemedd85da62016-06-28 11:29:54 -07002164 if (allPendingIntents == null) {
2165 allPendingIntents = new ArraySet<>();
Svet Ganovddb94882016-06-23 19:55:24 -07002166 }
Felipe Lemedd85da62016-06-28 11:29:54 -07002167 allPendingIntents.add(intent);
Svet Ganovddb94882016-06-23 19:55:24 -07002168 }
2169 });
2170 }
2171 try {
2172 // IMPORTANT: Add marshaling code in writeToParcelImpl as we
Julia Reynolds13d898c2017-02-02 12:22:05 -05002173 // want to intercept all pending events written to the parcel.
Svet Ganovddb94882016-06-23 19:55:24 -07002174 writeToParcelImpl(parcel, flags);
2175 // Must be written last!
Felipe Lemedd85da62016-06-28 11:29:54 -07002176 parcel.writeArraySet(allPendingIntents);
Svet Ganovddb94882016-06-23 19:55:24 -07002177 } finally {
2178 if (collectPendingIntents) {
2179 PendingIntent.setOnMarshaledListener(null);
2180 }
2181 }
2182 }
2183
2184 private void writeToParcelImpl(Parcel parcel, int flags) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002185 parcel.writeInt(1);
2186
Dianne Hackborn98305522017-05-05 17:53:53 -07002187 parcel.writeStrongBinder(whitelistToken);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002188 parcel.writeLong(when);
Selim Cinekb85f36fd2016-04-20 18:46:36 -07002189 parcel.writeLong(creationTime);
Dan Sandler4e787062015-06-17 15:09:48 -04002190 if (mSmallIcon == null && icon != 0) {
2191 // you snuck an icon in here without using the builder; let's try to keep it
2192 mSmallIcon = Icon.createWithResource("", icon);
2193 }
Dan Sandler3936e7a2015-05-19 20:59:12 -04002194 if (mSmallIcon != null) {
2195 parcel.writeInt(1);
2196 mSmallIcon.writeToParcel(parcel, 0);
2197 } else {
2198 parcel.writeInt(0);
2199 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002200 parcel.writeInt(number);
2201 if (contentIntent != null) {
2202 parcel.writeInt(1);
2203 contentIntent.writeToParcel(parcel, 0);
2204 } else {
2205 parcel.writeInt(0);
2206 }
2207 if (deleteIntent != null) {
2208 parcel.writeInt(1);
2209 deleteIntent.writeToParcel(parcel, 0);
2210 } else {
2211 parcel.writeInt(0);
2212 }
2213 if (tickerText != null) {
2214 parcel.writeInt(1);
2215 TextUtils.writeToParcel(tickerText, parcel, flags);
2216 } else {
2217 parcel.writeInt(0);
2218 }
Joe Onorato46439ce2010-11-19 13:56:21 -08002219 if (tickerView != null) {
Joe Onoratoef1e7762010-09-17 18:38:38 -04002220 parcel.writeInt(1);
Joe Onorato46439ce2010-11-19 13:56:21 -08002221 tickerView.writeToParcel(parcel, 0);
Joe Onoratoef1e7762010-09-17 18:38:38 -04002222 } else {
2223 parcel.writeInt(0);
2224 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002225 if (contentView != null) {
2226 parcel.writeInt(1);
2227 contentView.writeToParcel(parcel, 0);
2228 } else {
2229 parcel.writeInt(0);
2230 }
Selim Cinek279fa862016-06-14 10:57:25 -07002231 if (mLargeIcon == null && largeIcon != null) {
2232 // you snuck an icon in here without using the builder; let's try to keep it
2233 mLargeIcon = Icon.createWithBitmap(largeIcon);
2234 }
Dan Sandlerd63f9322015-05-06 15:18:49 -04002235 if (mLargeIcon != null) {
Joe Onorato561d3852010-11-20 18:09:34 -08002236 parcel.writeInt(1);
Dan Sandlerd63f9322015-05-06 15:18:49 -04002237 mLargeIcon.writeToParcel(parcel, 0);
Joe Onorato561d3852010-11-20 18:09:34 -08002238 } else {
2239 parcel.writeInt(0);
2240 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002241
2242 parcel.writeInt(defaults);
2243 parcel.writeInt(this.flags);
2244
2245 if (sound != null) {
2246 parcel.writeInt(1);
2247 sound.writeToParcel(parcel, 0);
2248 } else {
2249 parcel.writeInt(0);
2250 }
2251 parcel.writeInt(audioStreamType);
John Spurlockc0650f022014-07-19 13:22:39 -04002252
2253 if (audioAttributes != null) {
2254 parcel.writeInt(1);
2255 audioAttributes.writeToParcel(parcel, 0);
2256 } else {
2257 parcel.writeInt(0);
2258 }
2259
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002260 parcel.writeLongArray(vibrate);
2261 parcel.writeInt(ledARGB);
2262 parcel.writeInt(ledOnMS);
2263 parcel.writeInt(ledOffMS);
2264 parcel.writeInt(iconLevel);
Daniel Sandlere46cbd32010-06-17 10:35:26 -04002265
2266 if (fullScreenIntent != null) {
2267 parcel.writeInt(1);
2268 fullScreenIntent.writeToParcel(parcel, 0);
2269 } else {
2270 parcel.writeInt(0);
2271 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002272
2273 parcel.writeInt(priority);
Joe Malin8d40d042012-11-05 11:36:40 -08002274
John Spurlockfd7f1e02014-03-18 16:41:57 -04002275 parcel.writeString(category);
Joe Malin8d40d042012-11-05 11:36:40 -08002276
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002277 parcel.writeString(mGroupKey);
2278
2279 parcel.writeString(mSortKey);
2280
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002281 parcel.writeBundle(extras); // null ok
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002282
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002283 parcel.writeTypedArray(actions, 0); // null ok
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002284
Daniel Sandlerf3b73432012-03-27 15:01:25 -04002285 if (bigContentView != null) {
2286 parcel.writeInt(1);
2287 bigContentView.writeToParcel(parcel, 0);
2288 } else {
2289 parcel.writeInt(0);
2290 }
Dan Sandler0bf2ed82013-12-21 23:33:41 -06002291
Chris Wren8fd39ec2014-02-27 17:43:26 -05002292 if (headsUpContentView != null) {
2293 parcel.writeInt(1);
2294 headsUpContentView.writeToParcel(parcel, 0);
2295 } else {
2296 parcel.writeInt(0);
2297 }
2298
Dan Sandler0bf2ed82013-12-21 23:33:41 -06002299 parcel.writeInt(visibility);
2300
2301 if (publicVersion != null) {
2302 parcel.writeInt(1);
2303 publicVersion.writeToParcel(parcel, 0);
2304 } else {
2305 parcel.writeInt(0);
2306 }
Dan Sandler26e81cf2014-05-06 10:01:27 -04002307
2308 parcel.writeInt(color);
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04002309
2310 if (mChannelId != null) {
2311 parcel.writeInt(1);
2312 parcel.writeString(mChannelId);
2313 } else {
2314 parcel.writeInt(0);
2315 }
Julia Reynolds2a128742016-11-28 14:29:25 -05002316 parcel.writeLong(mTimeout);
Julia Reynolds13d898c2017-02-02 12:22:05 -05002317
2318 if (mShortcutId != null) {
2319 parcel.writeInt(1);
2320 parcel.writeString(mShortcutId);
2321 } else {
2322 parcel.writeInt(0);
2323 }
2324
2325 parcel.writeInt(mBadgeIcon);
Julia Reynolds3aedded2017-03-31 14:42:09 -04002326
2327 if (mSettingsText != null) {
2328 parcel.writeInt(1);
2329 TextUtils.writeToParcel(mSettingsText, parcel, flags);
2330 } else {
2331 parcel.writeInt(0);
2332 }
Julia Reynoldsa79c3712017-04-21 10:29:57 -04002333
2334 parcel.writeInt(mGroupAlertBehavior);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002335 }
2336
2337 /**
2338 * Parcelable.Creator that instantiates Notification objects
2339 */
2340 public static final Parcelable.Creator<Notification> CREATOR
2341 = new Parcelable.Creator<Notification>()
2342 {
2343 public Notification createFromParcel(Parcel parcel)
2344 {
2345 return new Notification(parcel);
2346 }
2347
2348 public Notification[] newArray(int size)
2349 {
2350 return new Notification[size];
2351 }
2352 };
2353
2354 /**
2355 * Sets the {@link #contentView} field to be a view with the standard "Latest Event"
2356 * layout.
2357 *
2358 * <p>Uses the {@link #icon} and {@link #when} fields to set the icon and time fields
2359 * in the view.</p>
2360 * @param context The context for your application / activity.
2361 * @param contentTitle The title that goes in the expanded entry.
2362 * @param contentText The text that goes in the expanded entry.
2363 * @param contentIntent The intent to launch when the user clicks the expanded notification.
2364 * If this is an activity, it must include the
2365 * {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} flag, which requires
Scott Main7aee61f2011-02-08 11:25:01 -08002366 * that you take care of task management as described in the
2367 * <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back
2368 * Stack</a> document.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002369 *
Joe Onorato46439ce2010-11-19 13:56:21 -08002370 * @deprecated Use {@link Builder} instead.
Chris Wrena05db382015-06-24 15:18:34 -04002371 * @removed
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002372 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002373 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002374 public void setLatestEventInfo(Context context,
2375 CharSequence contentTitle, CharSequence contentText, PendingIntent contentIntent) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002376 if (context.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1){
2377 Log.e(TAG, "setLatestEventInfo() is deprecated and you should feel deprecated.",
2378 new Throwable());
2379 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002380
Selim Cinek4ac6f602016-06-13 15:47:03 -07002381 if (context.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N) {
2382 extras.putBoolean(EXTRA_SHOW_WHEN, true);
2383 }
2384
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002385 // ensure that any information already set directly is preserved
2386 final Notification.Builder builder = new Notification.Builder(context, this);
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002387
2388 // now apply the latestEventInfo fields
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002389 if (contentTitle != null) {
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002390 builder.setContentTitle(contentTitle);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002391 }
2392 if (contentText != null) {
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002393 builder.setContentText(contentText);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002394 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002395 builder.setContentIntent(contentIntent);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002396
2397 builder.build(); // callers expect this notification to be ready to use
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002398 }
2399
Julia Reynoldsda303542015-11-23 14:00:20 -05002400 /**
2401 * @hide
2402 */
2403 public static void addFieldsFromContext(Context context, Notification notification) {
Julia Reynoldse071abd2017-03-22 10:52:11 -04002404 addFieldsFromContext(context.getApplicationInfo(), notification);
Jeff Sharkey012bc7b2016-04-11 16:30:27 -06002405 }
2406
2407 /**
2408 * @hide
2409 */
Julia Reynoldse071abd2017-03-22 10:52:11 -04002410 public static void addFieldsFromContext(ApplicationInfo ai, Notification notification) {
Jeff Sharkey012bc7b2016-04-11 16:30:27 -06002411 notification.extras.putParcelable(EXTRA_BUILDER_APPLICATION_INFO, ai);
Julia Reynoldsda303542015-11-23 14:00:20 -05002412 }
2413
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002414 @Override
2415 public String toString() {
2416 StringBuilder sb = new StringBuilder();
Julia Reynoldsb9e712e2017-04-17 10:31:03 -04002417 sb.append("Notification(channel=");
Julia Reynoldsbad42972017-04-25 13:52:49 -04002418 sb.append(getChannelId());
Julia Reynoldsb9e712e2017-04-17 10:31:03 -04002419 sb.append(" pri=");
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002420 sb.append(priority);
2421 sb.append(" contentView=");
Joe Onoratoc9596d62011-01-12 17:03:11 -08002422 if (contentView != null) {
2423 sb.append(contentView.getPackage());
2424 sb.append("/0x");
2425 sb.append(Integer.toHexString(contentView.getLayoutId()));
2426 } else {
2427 sb.append("null");
2428 }
2429 sb.append(" vibrate=");
Daniel Sandler6738eee2012-11-16 12:03:32 -05002430 if ((this.defaults & DEFAULT_VIBRATE) != 0) {
2431 sb.append("default");
2432 } else if (this.vibrate != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002433 int N = this.vibrate.length-1;
2434 sb.append("[");
2435 for (int i=0; i<N; i++) {
2436 sb.append(this.vibrate[i]);
2437 sb.append(',');
2438 }
Simon Schoar8cf97d92009-06-10 22:08:37 +02002439 if (N != -1) {
2440 sb.append(this.vibrate[N]);
2441 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002442 sb.append("]");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002443 } else {
2444 sb.append("null");
2445 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002446 sb.append(" sound=");
Daniel Sandler6738eee2012-11-16 12:03:32 -05002447 if ((this.defaults & DEFAULT_SOUND) != 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002448 sb.append("default");
Daniel Sandler6738eee2012-11-16 12:03:32 -05002449 } else if (this.sound != null) {
2450 sb.append(this.sound.toString());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002451 } else {
2452 sb.append("null");
2453 }
Chris Wren365b6d32015-07-16 10:39:26 -04002454 if (this.tickerText != null) {
2455 sb.append(" tick");
2456 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002457 sb.append(" defaults=0x");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002458 sb.append(Integer.toHexString(this.defaults));
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002459 sb.append(" flags=0x");
Daniel Sandlere46cbd32010-06-17 10:35:26 -04002460 sb.append(Integer.toHexString(this.flags));
Dan Sandler26e81cf2014-05-06 10:01:27 -04002461 sb.append(String.format(" color=0x%08x", this.color));
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002462 if (this.category != null) {
2463 sb.append(" category=");
2464 sb.append(this.category);
2465 }
2466 if (this.mGroupKey != null) {
2467 sb.append(" groupKey=");
2468 sb.append(this.mGroupKey);
2469 }
2470 if (this.mSortKey != null) {
2471 sb.append(" sortKey=");
2472 sb.append(this.mSortKey);
2473 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002474 if (actions != null) {
Dan Sandler1b718782014-07-18 12:43:45 -04002475 sb.append(" actions=");
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002476 sb.append(actions.length);
Dan Sandler1b718782014-07-18 12:43:45 -04002477 }
2478 sb.append(" vis=");
2479 sb.append(visibilityToString(this.visibility));
2480 if (this.publicVersion != null) {
2481 sb.append(" publicVersion=");
2482 sb.append(publicVersion.toString());
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002483 }
2484 sb.append(")");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002485 return sb.toString();
2486 }
Joe Onorato46439ce2010-11-19 13:56:21 -08002487
Dan Sandler1b718782014-07-18 12:43:45 -04002488 /**
2489 * {@hide}
2490 */
2491 public static String visibilityToString(int vis) {
2492 switch (vis) {
2493 case VISIBILITY_PRIVATE:
2494 return "PRIVATE";
2495 case VISIBILITY_PUBLIC:
2496 return "PUBLIC";
2497 case VISIBILITY_SECRET:
2498 return "SECRET";
2499 default:
2500 return "UNKNOWN(" + String.valueOf(vis) + ")";
2501 }
2502 }
2503
Joe Onoratocb109a02011-01-18 17:57:41 -08002504 /**
John Spurlock1d881a12015-03-18 19:21:54 -04002505 * {@hide}
2506 */
2507 public static String priorityToString(@Priority int pri) {
2508 switch (pri) {
2509 case PRIORITY_MIN:
2510 return "MIN";
2511 case PRIORITY_LOW:
2512 return "LOW";
2513 case PRIORITY_DEFAULT:
2514 return "DEFAULT";
2515 case PRIORITY_HIGH:
2516 return "HIGH";
2517 case PRIORITY_MAX:
2518 return "MAX";
2519 default:
2520 return "UNKNOWN(" + String.valueOf(pri) + ")";
2521 }
2522 }
2523
Jeff Sharkey000ce802017-04-29 13:13:27 -06002524 /** @removed */
2525 @Deprecated
Julia Reynolds37856052016-11-11 09:20:07 -05002526 public String getChannel() {
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04002527 return mChannelId;
2528 }
2529
2530 /**
Julia Reynoldsbad42972017-04-25 13:52:49 -04002531 * Returns the id of the channel this notification posts to.
2532 */
2533 public String getChannelId() {
2534 return mChannelId;
2535 }
2536
Jeff Sharkey000ce802017-04-29 13:13:27 -06002537 /** @removed */
2538 @Deprecated
Julia Reynolds2a128742016-11-28 14:29:25 -05002539 public long getTimeout() {
2540 return mTimeout;
2541 }
2542
2543 /**
Julia Reynoldsbad42972017-04-25 13:52:49 -04002544 * Returns the duration from posting after which this notification should be canceled by the
2545 * system, if it's not canceled already.
2546 */
2547 public long getTimeoutAfter() {
2548 return mTimeout;
2549 }
2550
2551 /**
Julia Reynoldse071abd2017-03-22 10:52:11 -04002552 * Returns what icon should be shown for this notification if it is being displayed in a
2553 * Launcher that supports badging. Will be one of {@link #BADGE_ICON_NONE},
2554 * {@link #BADGE_ICON_SMALL}, or {@link #BADGE_ICON_LARGE}.
2555 */
2556 public int getBadgeIconType() {
2557 return mBadgeIcon;
2558 }
2559
2560 /**
Julia Reynolds13d898c2017-02-02 12:22:05 -05002561 * Returns the {@link ShortcutInfo#getId() id} that this notification supersedes, if any.
Julia Reynoldsbad42972017-04-25 13:52:49 -04002562 *
2563 * <p>Used by some Launchers that display notification content to hide shortcuts that duplicate
2564 * notifications.
Julia Reynolds13d898c2017-02-02 12:22:05 -05002565 */
2566 public String getShortcutId() {
2567 return mShortcutId;
2568 }
2569
Julia Reynolds3aedded2017-03-31 14:42:09 -04002570
2571 /**
2572 * Returns the settings text provided to {@link Builder#setSettingsText(CharSequence)}.
2573 */
2574 public CharSequence getSettingsText() {
2575 return mSettingsText;
2576 }
2577
Julia Reynolds13d898c2017-02-02 12:22:05 -05002578 /**
Julia Reynoldsa79c3712017-04-21 10:29:57 -04002579 * Returns which type of notifications in a group are responsible for audibly alerting the
2580 * user. See {@link #GROUP_ALERT_ALL}, {@link #GROUP_ALERT_CHILDREN},
2581 * {@link #GROUP_ALERT_SUMMARY}.
2582 */
2583 public @GroupAlertBehavior int getGroupAlertBehavior() {
2584 return mGroupAlertBehavior;
2585 }
2586
2587 /**
Dan Sandlerd63f9322015-05-06 15:18:49 -04002588 * The small icon representing this notification in the status bar and content view.
2589 *
2590 * @return the small icon representing this notification.
2591 *
2592 * @see Builder#getSmallIcon()
2593 * @see Builder#setSmallIcon(Icon)
2594 */
2595 public Icon getSmallIcon() {
2596 return mSmallIcon;
2597 }
2598
2599 /**
2600 * Used when notifying to clean up legacy small icons.
2601 * @hide
2602 */
2603 public void setSmallIcon(Icon icon) {
2604 mSmallIcon = icon;
2605 }
2606
2607 /**
2608 * The large icon shown in this notification's content view.
2609 * @see Builder#getLargeIcon()
2610 * @see Builder#setLargeIcon(Icon)
2611 */
2612 public Icon getLargeIcon() {
2613 return mLargeIcon;
2614 }
2615
2616 /**
Christoph Studer4600f9b2014-07-22 22:44:43 +02002617 * @hide
2618 */
Christoph Studerc8db24b2014-07-25 17:50:30 +02002619 public boolean isGroupSummary() {
2620 return mGroupKey != null && (flags & FLAG_GROUP_SUMMARY) != 0;
2621 }
2622
2623 /**
2624 * @hide
2625 */
2626 public boolean isGroupChild() {
2627 return mGroupKey != null && (flags & FLAG_GROUP_SUMMARY) == 0;
2628 }
2629
2630 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002631 * Builder class for {@link Notification} objects.
Joe Malin8d40d042012-11-05 11:36:40 -08002632 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002633 * Provides a convenient way to set the various fields of a {@link Notification} and generate
Scott Main183bf112012-08-13 19:12:13 -07002634 * content views using the platform's notification layout template. If your app supports
2635 * versions of Android as old as API level 4, you can instead use
2636 * {@link android.support.v4.app.NotificationCompat.Builder NotificationCompat.Builder},
2637 * available in the <a href="{@docRoot}tools/extras/support-library.html">Android Support
2638 * library</a>.
Joe Malin8d40d042012-11-05 11:36:40 -08002639 *
Scott Main183bf112012-08-13 19:12:13 -07002640 * <p>Example:
Joe Malin8d40d042012-11-05 11:36:40 -08002641 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002642 * <pre class="prettyprint">
Scott Main183bf112012-08-13 19:12:13 -07002643 * Notification noti = new Notification.Builder(mContext)
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002644 * .setContentTitle(&quot;New mail from &quot; + sender.toString())
2645 * .setContentText(subject)
2646 * .setSmallIcon(R.drawable.new_mail)
2647 * .setLargeIcon(aBitmap)
Chris Wrenfbd96ba2012-05-01 12:03:58 -04002648 * .build();
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002649 * </pre>
Joe Onoratocb109a02011-01-18 17:57:41 -08002650 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002651 public static class Builder {
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05002652 /**
2653 * @hide
2654 */
2655 public static final String EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT =
2656 "android.rebuild.contentViewActionCount";
2657 /**
2658 * @hide
2659 */
2660 public static final String EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT
2661 = "android.rebuild.bigViewActionCount";
2662 /**
2663 * @hide
2664 */
2665 public static final String EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT
2666 = "android.rebuild.hudViewActionCount";
2667
Daniel Sandler602ad1c2012-06-12 16:06:27 -04002668 private static final int MAX_ACTION_BUTTONS = 3;
Daniel Sandler8680bf82012-05-15 16:52:52 -04002669
Selim Cinek6743c0b2017-01-18 18:24:01 -08002670 private static final boolean USE_ONLY_TITLE_IN_LOW_PRIORITY_SUMMARY =
2671 SystemProperties.getBoolean("notifications.only_title", true);
2672
Joe Onorato46439ce2010-11-19 13:56:21 -08002673 private Context mContext;
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002674 private Notification mN;
2675 private Bundle mUserExtras = new Bundle();
Chris Wrenfbd96ba2012-05-01 12:03:58 -04002676 private Style mStyle;
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002677 private ArrayList<Action> mActions = new ArrayList<Action>(MAX_ACTION_BUTTONS);
2678 private ArrayList<String> mPersonList = new ArrayList<String>();
2679 private NotificationColorUtil mColorUtil;
Selim Cinek7b9605b2017-01-19 17:36:00 -08002680 private boolean mIsLegacy;
2681 private boolean mIsLegacyInitialized;
Christoph Studer7ac80e62014-08-04 16:01:57 +02002682
2683 /**
Adrian Roos4ff3b122016-02-01 12:26:13 -08002684 * Caches a contrast-enhanced version of {@link #mCachedContrastColorIsFor}.
2685 */
2686 private int mCachedContrastColor = COLOR_INVALID;
2687 private int mCachedContrastColorIsFor = COLOR_INVALID;
Adrian Roos487374f2017-01-11 15:48:14 -08002688 /**
2689 * Caches a ambient version of {@link #mCachedContrastColorIsFor}.
2690 */
2691 private int mCachedAmbientColor = COLOR_INVALID;
2692 private int mCachedAmbientColorIsFor = COLOR_INVALID;
Adrian Roos4ff3b122016-02-01 12:26:13 -08002693
2694 /**
Adrian Roos70d7aa32017-01-11 15:39:06 -08002695 * Caches an instance of StandardTemplateParams. Note that this may have been used before,
2696 * so make sure to call {@link StandardTemplateParams#reset()} before using it.
2697 */
2698 StandardTemplateParams mParams = new StandardTemplateParams();
Selim Cinek7b9605b2017-01-19 17:36:00 -08002699 private int mTextColorsAreForBackground = COLOR_INVALID;
2700 private int mPrimaryTextColor = COLOR_INVALID;
2701 private int mSecondaryTextColor = COLOR_INVALID;
2702 private int mActionBarColor = COLOR_INVALID;
Selim Cinek5fb73f82017-04-20 16:55:38 -07002703 private int mBackgroundColor = COLOR_INVALID;
2704 private int mForegroundColor = COLOR_INVALID;
Selim Cinekac5f0272017-05-02 16:05:41 -07002705 private int mBackgroundColorHint = COLOR_INVALID;
Adrian Roos70d7aa32017-01-11 15:39:06 -08002706
2707 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002708 * Constructs a new Builder with the defaults:
Joe Onoratocb109a02011-01-18 17:57:41 -08002709 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002710 * @param context
2711 * A {@link Context} that will be used by the Builder to construct the
2712 * RemoteViews. The Context will not be held past the lifetime of this Builder
2713 * object.
Geoffrey Pitsch5caa2762017-01-12 09:35:54 -05002714 * @param channelId
2715 * The constructed Notification will be posted on this
2716 * {@link NotificationChannel}. To use a NotificationChannel, it must first be
2717 * created using {@link NotificationManager#createNotificationChannel}.
Joe Onoratocb109a02011-01-18 17:57:41 -08002718 */
Geoffrey Pitsch5caa2762017-01-12 09:35:54 -05002719 public Builder(Context context, String channelId) {
2720 this(context, (Notification) null);
2721 mN.mChannelId = channelId;
2722 }
2723
2724 /**
2725 * @deprecated use {@link Notification.Builder#Notification.Builder(Context, String)}
2726 * instead. All posted Notifications must specify a NotificationChannel Id.
2727 */
2728 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08002729 public Builder(Context context) {
Geoffrey Pitsch5caa2762017-01-12 09:35:54 -05002730 this(context, (Notification) null);
Joe Onorato46439ce2010-11-19 13:56:21 -08002731 }
2732
Joe Onoratocb109a02011-01-18 17:57:41 -08002733 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002734 * @hide
Christoph Studer4600f9b2014-07-22 22:44:43 +02002735 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002736 public Builder(Context context, Notification toAdopt) {
2737 mContext = context;
Christoph Studer4600f9b2014-07-22 22:44:43 +02002738
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002739 if (toAdopt == null) {
2740 mN = new Notification();
Selim Cinek0ff1ce602016-04-05 18:27:16 -07002741 if (context.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N) {
2742 mN.extras.putBoolean(EXTRA_SHOW_WHEN, true);
2743 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002744 mN.priority = PRIORITY_DEFAULT;
2745 mN.visibility = VISIBILITY_PRIVATE;
2746 } else {
2747 mN = toAdopt;
2748 if (mN.actions != null) {
2749 Collections.addAll(mActions, mN.actions);
Christoph Studer4600f9b2014-07-22 22:44:43 +02002750 }
2751
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002752 if (mN.extras.containsKey(EXTRA_PEOPLE)) {
2753 Collections.addAll(mPersonList, mN.extras.getStringArray(EXTRA_PEOPLE));
2754 }
2755
Selim Cinek4ac6f602016-06-13 15:47:03 -07002756 if (mN.getSmallIcon() == null && mN.icon != 0) {
2757 setSmallIcon(mN.icon);
2758 }
2759
2760 if (mN.getLargeIcon() == null && mN.largeIcon != null) {
2761 setLargeIcon(mN.largeIcon);
2762 }
2763
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002764 String templateClass = mN.extras.getString(EXTRA_TEMPLATE);
2765 if (!TextUtils.isEmpty(templateClass)) {
2766 final Class<? extends Style> styleClass
2767 = getNotificationStyleClass(templateClass);
2768 if (styleClass == null) {
2769 Log.d(TAG, "Unknown style class: " + templateClass);
2770 } else {
2771 try {
Adrian Roosc1a80b02016-04-05 14:54:55 -07002772 final Constructor<? extends Style> ctor =
2773 styleClass.getDeclaredConstructor();
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002774 ctor.setAccessible(true);
2775 final Style style = ctor.newInstance();
2776 style.restoreFromExtras(mN.extras);
2777
2778 if (style != null) {
2779 setStyle(style);
2780 }
2781 } catch (Throwable t) {
2782 Log.e(TAG, "Could not create Style", t);
2783 }
2784 }
2785 }
2786
2787 }
2788 }
2789
2790 private NotificationColorUtil getColorUtil() {
Selim Cinek99104832017-01-25 14:47:33 -08002791 if (mColorUtil == null) {
2792 mColorUtil = NotificationColorUtil.getInstance(mContext);
Christoph Studer4600f9b2014-07-22 22:44:43 +02002793 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002794 return mColorUtil;
Christoph Studer4600f9b2014-07-22 22:44:43 +02002795 }
2796
2797 /**
Julia Reynolds13d898c2017-02-02 12:22:05 -05002798 * If this notification is duplicative of a Launcher shortcut, sets the
2799 * {@link ShortcutInfo#getId() id} of the shortcut, in case the Launcher wants to hide
2800 * the shortcut.
2801 *
Julia Reynoldsbad42972017-04-25 13:52:49 -04002802 * This field will be ignored by Launchers that don't support badging, don't show
2803 * notification content, or don't show {@link android.content.pm.ShortcutManager shortcuts}.
Julia Reynolds13d898c2017-02-02 12:22:05 -05002804 *
2805 * @param shortcutId the {@link ShortcutInfo#getId() id} of the shortcut this notification
2806 * supersedes
2807 */
2808 public Builder setShortcutId(String shortcutId) {
2809 mN.mShortcutId = shortcutId;
2810 return this;
2811 }
2812
2813 /**
Julia Reynoldse071abd2017-03-22 10:52:11 -04002814 * Sets which icon to display as a badge for this notification.
2815 *
2816 * Must be one of {@link #BADGE_ICON_NONE}, {@link #BADGE_ICON_SMALL},
2817 * {@link #BADGE_ICON_LARGE}.
2818 *
2819 * Note: This value might be ignored, for launchers that don't support badge icons.
2820 */
Julia Reynolds612beb22017-03-30 10:48:30 -04002821 public Builder setBadgeIconType(int icon) {
Julia Reynoldse071abd2017-03-22 10:52:11 -04002822 mN.mBadgeIcon = icon;
2823 return this;
2824 }
2825
2826 /**
Julia Reynoldsa79c3712017-04-21 10:29:57 -04002827 * Sets the group alert behavior for this notification. Use this method to mute this
2828 * notification if alerts for this notification's group should be handled by a different
2829 * notification. This is only applicable for notifications that belong to a
2830 * {@link #setGroup(String) group}.
2831 *
2832 * <p> The default value is {@link #GROUP_ALERT_ALL}.</p>
2833 */
2834 public Builder setGroupAlertBehavior(@GroupAlertBehavior int groupAlertBehavior) {
2835 mN.mGroupAlertBehavior = groupAlertBehavior;
2836 return this;
2837 }
2838
Jeff Sharkey000ce802017-04-29 13:13:27 -06002839 /** @removed */
2840 @Deprecated
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04002841 public Builder setChannel(String channelId) {
2842 mN.mChannelId = channelId;
2843 return this;
2844 }
2845
2846 /**
Julia Reynoldsbad42972017-04-25 13:52:49 -04002847 * Specifies the channel the notification should be delivered on.
2848 */
2849 public Builder setChannelId(String channelId) {
2850 mN.mChannelId = channelId;
2851 return this;
2852 }
2853
Jeff Sharkey000ce802017-04-29 13:13:27 -06002854 /** @removed */
2855 @Deprecated
Julia Reynolds50989772017-02-23 14:32:16 -05002856 public Builder setTimeout(long durationMs) {
2857 mN.mTimeout = durationMs;
Julia Reynolds2a128742016-11-28 14:29:25 -05002858 return this;
2859 }
2860
2861 /**
Julia Reynoldsbad42972017-04-25 13:52:49 -04002862 * Specifies a duration in milliseconds after which this notification should be canceled,
2863 * if it is not already canceled.
2864 */
2865 public Builder setTimeoutAfter(long durationMs) {
2866 mN.mTimeout = durationMs;
2867 return this;
2868 }
2869
2870 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002871 * Add a timestamp pertaining to the notification (usually the time the event occurred).
Selim Cinek0ff1ce602016-04-05 18:27:16 -07002872 *
2873 * For apps targeting {@link android.os.Build.VERSION_CODES#N} and above, this time is not
2874 * shown anymore by default and must be opted into by using
2875 * {@link android.app.Notification.Builder#setShowWhen(boolean)}
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002876 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002877 * @see Notification#when
Joe Onoratocb109a02011-01-18 17:57:41 -08002878 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002879 public Builder setWhen(long when) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002880 mN.when = when;
Joe Onorato46439ce2010-11-19 13:56:21 -08002881 return this;
2882 }
2883
Joe Onoratocb109a02011-01-18 17:57:41 -08002884 /**
Griff Hazen50c11652014-05-16 09:46:31 -07002885 * Control whether the timestamp set with {@link #setWhen(long) setWhen} is shown
Daniel Sandler0c890492012-09-12 17:23:10 -07002886 * in the content view.
Selim Cinek0ff1ce602016-04-05 18:27:16 -07002887 * For apps targeting {@link android.os.Build.VERSION_CODES#N} and above, this defaults to
2888 * {@code false}. For earlier apps, the default is {@code true}.
Daniel Sandler0c890492012-09-12 17:23:10 -07002889 */
2890 public Builder setShowWhen(boolean show) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002891 mN.extras.putBoolean(EXTRA_SHOW_WHEN, show);
Daniel Sandler0c890492012-09-12 17:23:10 -07002892 return this;
2893 }
2894
2895 /**
Daniel Sandlerd33b8032012-05-10 11:41:48 -04002896 * Show the {@link Notification#when} field as a stopwatch.
Joe Malin8d40d042012-11-05 11:36:40 -08002897 *
2898 * Instead of presenting <code>when</code> as a timestamp, the notification will show an
Daniel Sandlerd33b8032012-05-10 11:41:48 -04002899 * automatically updating display of the minutes and seconds since <code>when</code>.
Daniel Sandlera2985ed2012-04-03 16:42:00 -04002900 *
Daniel Sandlerd33b8032012-05-10 11:41:48 -04002901 * Useful when showing an elapsed time (like an ongoing phone call).
2902 *
Selim Cinek81c23aa2016-02-25 16:23:13 -08002903 * The counter can also be set to count down to <code>when</code> when using
Adrian Roos96b7e202016-05-17 13:50:38 -07002904 * {@link #setChronometerCountDown(boolean)}.
Selim Cinek81c23aa2016-02-25 16:23:13 -08002905 *
Daniel Sandlerd33b8032012-05-10 11:41:48 -04002906 * @see android.widget.Chronometer
Daniel Sandlera2985ed2012-04-03 16:42:00 -04002907 * @see Notification#when
Adrian Roos96b7e202016-05-17 13:50:38 -07002908 * @see #setChronometerCountDown(boolean)
Daniel Sandlera2985ed2012-04-03 16:42:00 -04002909 */
2910 public Builder setUsesChronometer(boolean b) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002911 mN.extras.putBoolean(EXTRA_SHOW_CHRONOMETER, b);
Daniel Sandlera2985ed2012-04-03 16:42:00 -04002912 return this;
2913 }
2914
2915 /**
Selim Cinek81c23aa2016-02-25 16:23:13 -08002916 * Sets the Chronometer to count down instead of counting up.
2917 *
2918 * <p>This is only relevant if {@link #setUsesChronometer(boolean)} has been set to true.
2919 * If it isn't set the chronometer will count up.
2920 *
2921 * @see #setUsesChronometer(boolean)
2922 */
Adrian Roos96b7e202016-05-17 13:50:38 -07002923 public Builder setChronometerCountDown(boolean countDown) {
2924 mN.extras.putBoolean(EXTRA_CHRONOMETER_COUNT_DOWN, countDown);
Selim Cinek81c23aa2016-02-25 16:23:13 -08002925 return this;
2926 }
2927
2928 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002929 * Set the small icon resource, which will be used to represent the notification in the
2930 * status bar.
Joe Onoratocb109a02011-01-18 17:57:41 -08002931 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002932
2933 * The platform template for the expanded view will draw this icon in the left, unless a
2934 * {@link #setLargeIcon(Bitmap) large icon} has also been specified, in which case the small
2935 * icon will be moved to the right-hand side.
2936 *
2937
2938 * @param icon
2939 * A resource ID in the application's package of the drawable to use.
2940 * @see Notification#icon
Joe Onoratocb109a02011-01-18 17:57:41 -08002941 */
Tor Norbye7b9c9122013-05-30 16:48:33 -07002942 public Builder setSmallIcon(@DrawableRes int icon) {
Dan Sandlerd63f9322015-05-06 15:18:49 -04002943 return setSmallIcon(icon != 0
2944 ? Icon.createWithResource(mContext, icon)
2945 : null);
Joe Onorato46439ce2010-11-19 13:56:21 -08002946 }
2947
Joe Onoratocb109a02011-01-18 17:57:41 -08002948 /**
2949 * A variant of {@link #setSmallIcon(int) setSmallIcon(int)} that takes an additional
2950 * level parameter for when the icon is a {@link android.graphics.drawable.LevelListDrawable
2951 * LevelListDrawable}.
2952 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002953 * @param icon A resource ID in the application's package of the drawable to use.
Joe Onoratocb109a02011-01-18 17:57:41 -08002954 * @param level The level to use for the icon.
2955 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002956 * @see Notification#icon
2957 * @see Notification#iconLevel
Joe Onoratocb109a02011-01-18 17:57:41 -08002958 */
Tor Norbye7b9c9122013-05-30 16:48:33 -07002959 public Builder setSmallIcon(@DrawableRes int icon, int level) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002960 mN.iconLevel = level;
Dan Sandlerd63f9322015-05-06 15:18:49 -04002961 return setSmallIcon(icon);
2962 }
2963
2964 /**
2965 * Set the small icon, which will be used to represent the notification in the
2966 * status bar and content view (unless overriden there by a
2967 * {@link #setLargeIcon(Bitmap) large icon}).
2968 *
2969 * @param icon An Icon object to use.
2970 * @see Notification#icon
2971 */
2972 public Builder setSmallIcon(Icon icon) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002973 mN.setSmallIcon(icon);
2974 if (icon != null && icon.getType() == Icon.TYPE_RESOURCE) {
2975 mN.icon = icon.getResId();
2976 }
Joe Onorato46439ce2010-11-19 13:56:21 -08002977 return this;
2978 }
2979
Joe Onoratocb109a02011-01-18 17:57:41 -08002980 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002981 * Set the first line of text in the platform notification template.
Joe Onoratocb109a02011-01-18 17:57:41 -08002982 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002983 public Builder setContentTitle(CharSequence title) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002984 mN.extras.putCharSequence(EXTRA_TITLE, safeCharSequence(title));
Joe Onorato46439ce2010-11-19 13:56:21 -08002985 return this;
2986 }
2987
Joe Onoratocb109a02011-01-18 17:57:41 -08002988 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002989 * Set the second line of text in the platform notification template.
Joe Onoratocb109a02011-01-18 17:57:41 -08002990 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002991 public Builder setContentText(CharSequence text) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002992 mN.extras.putCharSequence(EXTRA_TEXT, safeCharSequence(text));
Joe Onorato46439ce2010-11-19 13:56:21 -08002993 return this;
2994 }
2995
Joe Onoratocb109a02011-01-18 17:57:41 -08002996 /**
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07002997 * This provides some additional information that is displayed in the notification. No
2998 * guarantees are given where exactly it is displayed.
2999 *
3000 * <p>This information should only be provided if it provides an essential
3001 * benefit to the understanding of the notification. The more text you provide the
3002 * less readable it becomes. For example, an email client should only provide the account
3003 * name here if more than one email account has been added.</p>
3004 *
3005 * <p>As of {@link android.os.Build.VERSION_CODES#N} this information is displayed in the
3006 * notification header area.
3007 *
3008 * On Android versions before {@link android.os.Build.VERSION_CODES#N}
3009 * this will be shown in the third line of text in the platform notification template.
3010 * You should not be using {@link #setProgress(int, int, boolean)} at the
3011 * same time on those versions; they occupy the same place.
3012 * </p>
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003013 */
3014 public Builder setSubText(CharSequence text) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003015 mN.extras.putCharSequence(EXTRA_SUB_TEXT, safeCharSequence(text));
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003016 return this;
3017 }
3018
3019 /**
Julia Reynolds3aedded2017-03-31 14:42:09 -04003020 * Provides text that will appear as a link to your application's settings.
3021 *
3022 * <p>This text does not appear within notification {@link Style templates} but may
3023 * appear when the user uses an affordance to learn more about the notification.
3024 * Additionally, this text will not appear unless you provide a valid link target by
3025 * handling {@link #INTENT_CATEGORY_NOTIFICATION_PREFERENCES}.
3026 *
3027 * <p>This text is meant to be concise description about what the user can customize
3028 * when they click on this link. The recommended maximum length is 40 characters.
3029 * @param text
3030 * @return
3031 */
3032 public Builder setSettingsText(CharSequence text) {
3033 mN.mSettingsText = safeCharSequence(text);
3034 return this;
3035 }
3036
3037 /**
Adrian Roose458aa82015-12-08 16:17:19 -08003038 * Set the remote input history.
3039 *
3040 * This should be set to the most recent inputs that have been sent
3041 * through a {@link RemoteInput} of this Notification and cleared once the it is no
3042 * longer relevant (e.g. for chat notifications once the other party has responded).
3043 *
3044 * The most recent input must be stored at the 0 index, the second most recent at the
3045 * 1 index, etc. Note that the system will limit both how far back the inputs will be shown
3046 * and how much of each individual input is shown.
3047 *
3048 * <p>Note: The reply text will only be shown on notifications that have least one action
3049 * with a {@code RemoteInput}.</p>
3050 */
3051 public Builder setRemoteInputHistory(CharSequence[] text) {
3052 if (text == null) {
3053 mN.extras.putCharSequenceArray(EXTRA_REMOTE_INPUT_HISTORY, null);
3054 } else {
3055 final int N = Math.min(MAX_REPLY_HISTORY, text.length);
3056 CharSequence[] safe = new CharSequence[N];
3057 for (int i = 0; i < N; i++) {
3058 safe[i] = safeCharSequence(text[i]);
3059 }
3060 mN.extras.putCharSequenceArray(EXTRA_REMOTE_INPUT_HISTORY, safe);
3061 }
3062 return this;
3063 }
3064
3065 /**
Julia Reynolds13d898c2017-02-02 12:22:05 -05003066 * Sets the number of items this notification represents. May be displayed as a badge count
3067 * for Launchers that support badging.
Joe Onoratocb109a02011-01-18 17:57:41 -08003068 */
Joe Onorato8595a3d2010-11-19 18:12:07 -08003069 public Builder setNumber(int number) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003070 mN.number = number;
Joe Onorato8595a3d2010-11-19 18:12:07 -08003071 return this;
3072 }
3073
Joe Onoratocb109a02011-01-18 17:57:41 -08003074 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003075 * A small piece of additional information pertaining to this notification.
3076 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003077 * The platform template will draw this on the last line of the notification, at the far
3078 * right (to the right of a smallIcon if it has been placed there).
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07003079 *
3080 * @deprecated use {@link #setSubText(CharSequence)} instead to set a text in the header.
3081 * For legacy apps targeting a version below {@link android.os.Build.VERSION_CODES#N} this
3082 * field will still show up, but the subtext will take precedence.
Joe Onoratocb109a02011-01-18 17:57:41 -08003083 */
Aurimas Liutikas514c5ef2016-05-24 15:22:55 -07003084 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08003085 public Builder setContentInfo(CharSequence info) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003086 mN.extras.putCharSequence(EXTRA_INFO_TEXT, safeCharSequence(info));
Joe Onorato46439ce2010-11-19 13:56:21 -08003087 return this;
3088 }
3089
Joe Onoratocb109a02011-01-18 17:57:41 -08003090 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003091 * Set the progress this notification represents.
3092 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003093 * The platform template will represent this using a {@link ProgressBar}.
Jeff Sharkey1c400132011-08-05 14:50:13 -07003094 */
3095 public Builder setProgress(int max, int progress, boolean indeterminate) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003096 mN.extras.putInt(EXTRA_PROGRESS, progress);
3097 mN.extras.putInt(EXTRA_PROGRESS_MAX, max);
3098 mN.extras.putBoolean(EXTRA_PROGRESS_INDETERMINATE, indeterminate);
Jeff Sharkey1c400132011-08-05 14:50:13 -07003099 return this;
3100 }
3101
3102 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003103 * Supply a custom RemoteViews to use instead of the platform template.
3104 *
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003105 * Use {@link #setCustomContentView(RemoteViews)} instead.
Joe Onoratocb109a02011-01-18 17:57:41 -08003106 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003107 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08003108 public Builder setContent(RemoteViews views) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003109 return setCustomContentView(views);
3110 }
3111
3112 /**
3113 * Supply custom RemoteViews to use instead of the platform template.
3114 *
3115 * This will override the layout that would otherwise be constructed by this Builder
3116 * object.
3117 */
3118 public Builder setCustomContentView(RemoteViews contentView) {
3119 mN.contentView = contentView;
3120 return this;
3121 }
3122
3123 /**
3124 * Supply custom RemoteViews to use instead of the platform template in the expanded form.
3125 *
3126 * This will override the expanded layout that would otherwise be constructed by this
3127 * Builder object.
3128 */
3129 public Builder setCustomBigContentView(RemoteViews contentView) {
3130 mN.bigContentView = contentView;
3131 return this;
3132 }
3133
3134 /**
3135 * Supply custom RemoteViews to use instead of the platform template in the heads up dialog.
3136 *
3137 * This will override the heads-up layout that would otherwise be constructed by this
3138 * Builder object.
3139 */
3140 public Builder setCustomHeadsUpContentView(RemoteViews contentView) {
3141 mN.headsUpContentView = contentView;
Joe Onorato46439ce2010-11-19 13:56:21 -08003142 return this;
3143 }
3144
Joe Onoratocb109a02011-01-18 17:57:41 -08003145 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003146 * Supply a {@link PendingIntent} to be sent when the notification is clicked.
3147 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003148 * As of {@link android.os.Build.VERSION_CODES#HONEYCOMB}, if this field is unset and you
3149 * have specified a custom RemoteViews with {@link #setContent(RemoteViews)}, you can use
3150 * {@link RemoteViews#setOnClickPendingIntent RemoteViews.setOnClickPendingIntent(int,PendingIntent)}
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003151 * to assign PendingIntents to individual views in that custom layout (i.e., to create
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003152 * clickable buttons inside the notification view).
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003153 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003154 * @see Notification#contentIntent Notification.contentIntent
Joe Onoratocb109a02011-01-18 17:57:41 -08003155 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003156 public Builder setContentIntent(PendingIntent intent) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003157 mN.contentIntent = intent;
Joe Onorato46439ce2010-11-19 13:56:21 -08003158 return this;
3159 }
3160
Joe Onoratocb109a02011-01-18 17:57:41 -08003161 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003162 * Supply a {@link PendingIntent} to send when the notification is cleared explicitly by the user.
3163 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003164 * @see Notification#deleteIntent
Joe Onoratocb109a02011-01-18 17:57:41 -08003165 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003166 public Builder setDeleteIntent(PendingIntent intent) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003167 mN.deleteIntent = intent;
Joe Onorato46439ce2010-11-19 13:56:21 -08003168 return this;
3169 }
3170
Joe Onoratocb109a02011-01-18 17:57:41 -08003171 /**
3172 * An intent to launch instead of posting the notification to the status bar.
3173 * Only for use with extremely high-priority notifications demanding the user's
3174 * <strong>immediate</strong> attention, such as an incoming phone call or
3175 * alarm clock that the user has explicitly set to a particular time.
3176 * If this facility is used for something else, please give the user an option
3177 * to turn it off and use a normal notification, as this can be extremely
3178 * disruptive.
3179 *
Chris Wren47c20a12014-06-18 17:27:29 -04003180 * <p>
3181 * The system UI may choose to display a heads-up notification, instead of
3182 * launching this intent, while the user is using the device.
3183 * </p>
3184 *
Joe Onoratocb109a02011-01-18 17:57:41 -08003185 * @param intent The pending intent to launch.
3186 * @param highPriority Passing true will cause this notification to be sent
3187 * even if other notifications are suppressed.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003188 *
3189 * @see Notification#fullScreenIntent
Joe Onoratocb109a02011-01-18 17:57:41 -08003190 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003191 public Builder setFullScreenIntent(PendingIntent intent, boolean highPriority) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003192 mN.fullScreenIntent = intent;
Joe Onorato46439ce2010-11-19 13:56:21 -08003193 setFlag(FLAG_HIGH_PRIORITY, highPriority);
3194 return this;
3195 }
3196
Joe Onoratocb109a02011-01-18 17:57:41 -08003197 /**
Dan Sandler5fcdf6e2014-07-18 11:31:15 -04003198 * Set the "ticker" text which is sent to accessibility services.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003199 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003200 * @see Notification#tickerText
Joe Onoratocb109a02011-01-18 17:57:41 -08003201 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003202 public Builder setTicker(CharSequence tickerText) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003203 mN.tickerText = safeCharSequence(tickerText);
Joe Onorato46439ce2010-11-19 13:56:21 -08003204 return this;
3205 }
3206
Joe Onoratocb109a02011-01-18 17:57:41 -08003207 /**
Dan Sandler5fcdf6e2014-07-18 11:31:15 -04003208 * Obsolete version of {@link #setTicker(CharSequence)}.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003209 *
Joe Onoratocb109a02011-01-18 17:57:41 -08003210 */
Dan Sandler5fcdf6e2014-07-18 11:31:15 -04003211 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08003212 public Builder setTicker(CharSequence tickerText, RemoteViews views) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003213 setTicker(tickerText);
3214 // views is ignored
Joe Onorato46439ce2010-11-19 13:56:21 -08003215 return this;
3216 }
3217
Joe Onoratocb109a02011-01-18 17:57:41 -08003218 /**
Dan Sandlerd63f9322015-05-06 15:18:49 -04003219 * Add a large icon to the notification content view.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003220 *
3221 * In the platform template, this image will be shown on the left of the notification view
Dan Sandlerd63f9322015-05-06 15:18:49 -04003222 * in place of the {@link #setSmallIcon(Icon) small icon} (which will be placed in a small
3223 * badge atop the large icon).
Dan Sandler08a04c12015-05-06 15:18:49 -04003224 */
Dan Sandlerd63f9322015-05-06 15:18:49 -04003225 public Builder setLargeIcon(Bitmap b) {
3226 return setLargeIcon(b != null ? Icon.createWithBitmap(b) : null);
3227 }
3228
3229 /**
3230 * Add a large icon to the notification content view.
3231 *
3232 * In the platform template, this image will be shown on the left of the notification view
3233 * in place of the {@link #setSmallIcon(Icon) small icon} (which will be placed in a small
3234 * badge atop the large icon).
3235 */
3236 public Builder setLargeIcon(Icon icon) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003237 mN.mLargeIcon = icon;
3238 mN.extras.putParcelable(EXTRA_LARGE_ICON, icon);
Joe Onorato46439ce2010-11-19 13:56:21 -08003239 return this;
3240 }
3241
Joe Onoratocb109a02011-01-18 17:57:41 -08003242 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003243 * Set the sound to play.
3244 *
John Spurlockc0650f022014-07-19 13:22:39 -04003245 * It will be played using the {@link #AUDIO_ATTRIBUTES_DEFAULT default audio attributes}
3246 * for notifications.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003247 *
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003248 * @deprecated use {@link NotificationChannel#setSound(Uri, AudioAttributes)} instead.
Joe Onoratocb109a02011-01-18 17:57:41 -08003249 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003250 @Deprecated
Joe Onorato52f80cd2010-11-21 15:34:48 -08003251 public Builder setSound(Uri sound) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003252 mN.sound = sound;
3253 mN.audioAttributes = AUDIO_ATTRIBUTES_DEFAULT;
Joe Onorato52f80cd2010-11-21 15:34:48 -08003254 return this;
3255 }
3256
Joe Onoratocb109a02011-01-18 17:57:41 -08003257 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003258 * Set the sound to play, along with a specific stream on which to play it.
Joe Onoratocb109a02011-01-18 17:57:41 -08003259 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003260 * See {@link android.media.AudioManager} for the <code>STREAM_</code> constants.
3261 *
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003262 * @deprecated use {@link NotificationChannel#setSound(Uri, AudioAttributes)}.
Joe Onoratocb109a02011-01-18 17:57:41 -08003263 */
Jean-Michel Trivi81f871e2014-08-06 16:32:38 -07003264 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08003265 public Builder setSound(Uri sound, int streamType) {
Jean-Michel Trivi2f7511f2016-11-28 15:40:27 -08003266 PlayerBase.deprecateStreamTypeForPlayback(streamType, "Notification", "setSound()");
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003267 mN.sound = sound;
3268 mN.audioStreamType = streamType;
Joe Onorato46439ce2010-11-19 13:56:21 -08003269 return this;
3270 }
3271
Joe Onoratocb109a02011-01-18 17:57:41 -08003272 /**
John Spurlockc0650f022014-07-19 13:22:39 -04003273 * Set the sound to play, along with specific {@link AudioAttributes audio attributes} to
3274 * use during playback.
3275 *
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003276 * @deprecated use {@link NotificationChannel#setSound(Uri, AudioAttributes)} instead.
John Spurlockc0650f022014-07-19 13:22:39 -04003277 * @see Notification#sound
3278 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003279 @Deprecated
John Spurlockc0650f022014-07-19 13:22:39 -04003280 public Builder setSound(Uri sound, AudioAttributes audioAttributes) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003281 mN.sound = sound;
3282 mN.audioAttributes = audioAttributes;
John Spurlockc0650f022014-07-19 13:22:39 -04003283 return this;
3284 }
3285
3286 /**
Joe Onoratocb109a02011-01-18 17:57:41 -08003287 * Set the vibration pattern to use.
3288 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003289 * See {@link android.os.Vibrator#vibrate(long[], int)} for a discussion of the
3290 * <code>pattern</code> parameter.
3291 *
Chris Wren47c20a12014-06-18 17:27:29 -04003292 * <p>
3293 * A notification that vibrates is more likely to be presented as a heads-up notification.
3294 * </p>
3295 *
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003296 * @deprecated use {@link NotificationChannel#setVibrationPattern(long[])} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003297 * @see Notification#vibrate
Joe Onoratocb109a02011-01-18 17:57:41 -08003298 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003299 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08003300 public Builder setVibrate(long[] pattern) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003301 mN.vibrate = pattern;
Joe Onorato46439ce2010-11-19 13:56:21 -08003302 return this;
3303 }
3304
Joe Onoratocb109a02011-01-18 17:57:41 -08003305 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003306 * Set the desired color for the indicator LED on the device, as well as the
3307 * blink duty cycle (specified in milliseconds).
3308 *
3309
3310 * Not all devices will honor all (or even any) of these values.
3311 *
Julia Reynolds529e3322017-02-06 08:33:01 -05003312 * @deprecated use {@link NotificationChannel#enableLights(boolean)} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003313 * @see Notification#ledARGB
3314 * @see Notification#ledOnMS
3315 * @see Notification#ledOffMS
Joe Onoratocb109a02011-01-18 17:57:41 -08003316 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003317 @Deprecated
Tor Norbye80756e32015-03-02 09:39:27 -08003318 public Builder setLights(@ColorInt int argb, int onMs, int offMs) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003319 mN.ledARGB = argb;
3320 mN.ledOnMS = onMs;
3321 mN.ledOffMS = offMs;
Julia Reynolds10ee1fc2015-11-09 11:04:55 -05003322 if (onMs != 0 || offMs != 0) {
3323 mN.flags |= FLAG_SHOW_LIGHTS;
3324 }
Joe Onorato46439ce2010-11-19 13:56:21 -08003325 return this;
3326 }
3327
Joe Onoratocb109a02011-01-18 17:57:41 -08003328 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003329 * Set whether this is an "ongoing" notification.
Joe Onoratocb109a02011-01-18 17:57:41 -08003330 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003331
3332 * Ongoing notifications cannot be dismissed by the user, so your application or service
3333 * must take care of canceling them.
3334 *
3335
3336 * They are typically used to indicate a background task that the user is actively engaged
3337 * with (e.g., playing music) or is pending in some way and therefore occupying the device
3338 * (e.g., a file download, sync operation, active network connection).
3339 *
3340
3341 * @see Notification#FLAG_ONGOING_EVENT
3342 * @see Service#setForeground(boolean)
Joe Onoratocb109a02011-01-18 17:57:41 -08003343 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003344 public Builder setOngoing(boolean ongoing) {
3345 setFlag(FLAG_ONGOING_EVENT, ongoing);
3346 return this;
3347 }
3348
Joe Onoratocb109a02011-01-18 17:57:41 -08003349 /**
Selim Cinek7b9605b2017-01-19 17:36:00 -08003350 * Set whether this notification should be colorized. When set, the color set with
3351 * {@link #setColor(int)} will be used as the background color of this notification.
3352 * <p>
Selim Cinek7b9605b2017-01-19 17:36:00 -08003353 * This should only be used for high priority ongoing tasks like navigation, an ongoing
3354 * call, or other similarly high-priority events for the user.
Selim Cinek99104832017-01-25 14:47:33 -08003355 * <p>
Selim Cinek22714f12017-04-13 16:23:53 -07003356 * For most styles, the coloring will only be applied if the notification is for a
3357 * foreground service notification.
Selim Cinek99104832017-01-25 14:47:33 -08003358 * However, for {@link MediaStyle} and {@link DecoratedMediaCustomViewStyle} notifications
Selim Cinek22714f12017-04-13 16:23:53 -07003359 * that have a media session attached there is no such requirement.
Selim Cinek7b9605b2017-01-19 17:36:00 -08003360 *
Selim Cinek7b9605b2017-01-19 17:36:00 -08003361 * @see Builder#setColor(int)
Selim Cinek99104832017-01-25 14:47:33 -08003362 * @see MediaStyle#setMediaSession(MediaSession.Token)
Selim Cinek7b9605b2017-01-19 17:36:00 -08003363 */
3364 public Builder setColorized(boolean colorize) {
3365 mN.extras.putBoolean(EXTRA_COLORIZED, colorize);
3366 return this;
3367 }
3368
3369 /**
Joe Onoratocb109a02011-01-18 17:57:41 -08003370 * Set this flag if you would only like the sound, vibrate
3371 * and ticker to be played if the notification is not already showing.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003372 *
3373 * @see Notification#FLAG_ONLY_ALERT_ONCE
Joe Onoratocb109a02011-01-18 17:57:41 -08003374 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003375 public Builder setOnlyAlertOnce(boolean onlyAlertOnce) {
3376 setFlag(FLAG_ONLY_ALERT_ONCE, onlyAlertOnce);
3377 return this;
3378 }
3379
Joe Onoratocb109a02011-01-18 17:57:41 -08003380 /**
Julia Reynolds04499532016-09-13 14:04:53 -04003381 * Make this notification automatically dismissed when the user touches it.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003382 *
3383 * @see Notification#FLAG_AUTO_CANCEL
Joe Onoratocb109a02011-01-18 17:57:41 -08003384 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003385 public Builder setAutoCancel(boolean autoCancel) {
Joe Onorato281d83f2011-01-04 17:13:10 -08003386 setFlag(FLAG_AUTO_CANCEL, autoCancel);
Joe Onorato46439ce2010-11-19 13:56:21 -08003387 return this;
3388 }
3389
Joe Onoratocb109a02011-01-18 17:57:41 -08003390 /**
Griff Hazendfcb0802014-02-11 12:00:00 -08003391 * Set whether or not this notification should not bridge to other devices.
3392 *
3393 * <p>Some notifications can be bridged to other devices for remote display.
3394 * This hint can be set to recommend this notification not be bridged.
3395 */
3396 public Builder setLocalOnly(boolean localOnly) {
3397 setFlag(FLAG_LOCAL_ONLY, localOnly);
3398 return this;
3399 }
3400
3401 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003402 * Set which notification properties will be inherited from system defaults.
Joe Onoratocb109a02011-01-18 17:57:41 -08003403 * <p>
3404 * The value should be one or more of the following fields combined with
3405 * bitwise-or:
3406 * {@link #DEFAULT_SOUND}, {@link #DEFAULT_VIBRATE}, {@link #DEFAULT_LIGHTS}.
3407 * <p>
3408 * For all default values, use {@link #DEFAULT_ALL}.
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003409 *
3410 * @deprecated use {@link NotificationChannel#enableVibration(boolean)} and
Julia Reynolds529e3322017-02-06 08:33:01 -05003411 * {@link NotificationChannel#enableLights(boolean)} and
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003412 * {@link NotificationChannel#setSound(Uri, AudioAttributes)} instead.
Joe Onoratocb109a02011-01-18 17:57:41 -08003413 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003414 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08003415 public Builder setDefaults(int defaults) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003416 mN.defaults = defaults;
Joe Onorato46439ce2010-11-19 13:56:21 -08003417 return this;
3418 }
3419
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003420 /**
3421 * Set the priority of this notification.
3422 *
3423 * @see Notification#priority
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003424 * @deprecated use {@link NotificationChannel#setImportance(int)} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003425 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003426 @Deprecated
Tor Norbyed9273d62013-05-30 15:59:53 -07003427 public Builder setPriority(@Priority int pri) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003428 mN.priority = pri;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003429 return this;
3430 }
Joe Malin8d40d042012-11-05 11:36:40 -08003431
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003432 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -04003433 * Set the notification category.
Joe Malin8d40d042012-11-05 11:36:40 -08003434 *
John Spurlockfd7f1e02014-03-18 16:41:57 -04003435 * @see Notification#category
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003436 */
John Spurlockfd7f1e02014-03-18 16:41:57 -04003437 public Builder setCategory(String category) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003438 mN.category = category;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003439 return this;
3440 }
3441
3442 /**
Chris Wrendde75302014-03-26 17:24:15 -04003443 * Add a person that is relevant to this notification.
3444 *
Chris Wrene6c48932014-09-29 17:19:27 -04003445 * <P>
3446 * Depending on user preferences, this annotation may allow the notification to pass
Julia Reynoldse071abd2017-03-22 10:52:11 -04003447 * through interruption filters, if this notification is of category {@link #CATEGORY_CALL}
3448 * or {@link #CATEGORY_MESSAGE}. The addition of people may also cause this notification to
3449 * appear more prominently in the user interface.
Chris Wrene6c48932014-09-29 17:19:27 -04003450 * </P>
3451 *
3452 * <P>
3453 * The person should be specified by the {@code String} representation of a
3454 * {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI}.
3455 * </P>
3456 *
3457 * <P>The system will also attempt to resolve {@code mailto:} and {@code tel:} schema
3458 * URIs. The path part of these URIs must exist in the contacts database, in the
3459 * appropriate column, or the reference will be discarded as invalid. Telephone schema
3460 * URIs will be resolved by {@link android.provider.ContactsContract.PhoneLookup}.
3461 * </P>
3462 *
3463 * @param uri A URI for the person.
Chris Wrendde75302014-03-26 17:24:15 -04003464 * @see Notification#EXTRA_PEOPLE
3465 */
Chris Wrene6c48932014-09-29 17:19:27 -04003466 public Builder addPerson(String uri) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003467 mPersonList.add(uri);
Chris Wrendde75302014-03-26 17:24:15 -04003468 return this;
3469 }
3470
3471 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003472 * Set this notification to be part of a group of notifications sharing the same key.
3473 * Grouped notifications may display in a cluster or stack on devices which
3474 * support such rendering.
3475 *
3476 * <p>To make this notification the summary for its group, also call
3477 * {@link #setGroupSummary}. A sort order can be specified for group members by using
3478 * {@link #setSortKey}.
3479 * @param groupKey The group key of the group.
3480 * @return this object for method chaining
3481 */
3482 public Builder setGroup(String groupKey) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003483 mN.mGroupKey = groupKey;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003484 return this;
3485 }
3486
3487 /**
3488 * Set this notification to be the group summary for a group of notifications.
3489 * Grouped notifications may display in a cluster or stack on devices which
Julia Reynolds04499532016-09-13 14:04:53 -04003490 * support such rendering. If thereRequires a group key also be set using {@link #setGroup}.
3491 * The group summary may be suppressed if too few notifications are included in the group.
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003492 * @param isGroupSummary Whether this notification should be a group summary.
3493 * @return this object for method chaining
3494 */
3495 public Builder setGroupSummary(boolean isGroupSummary) {
3496 setFlag(FLAG_GROUP_SUMMARY, isGroupSummary);
3497 return this;
3498 }
3499
3500 /**
3501 * Set a sort key that orders this notification among other notifications from the
3502 * same package. This can be useful if an external sort was already applied and an app
3503 * would like to preserve this. Notifications will be sorted lexicographically using this
3504 * value, although providing different priorities in addition to providing sort key may
3505 * cause this value to be ignored.
3506 *
3507 * <p>This sort key can also be used to order members of a notification group. See
Griff Hazen9e1379f2014-05-20 12:50:51 -07003508 * {@link #setGroup}.
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003509 *
3510 * @see String#compareTo(String)
3511 */
3512 public Builder setSortKey(String sortKey) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003513 mN.mSortKey = sortKey;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003514 return this;
3515 }
3516
3517 /**
Griff Hazen720042b2014-02-24 15:46:56 -08003518 * Merge additional metadata into this notification.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003519 *
Griff Hazen720042b2014-02-24 15:46:56 -08003520 * <p>Values within the Bundle will replace existing extras values in this Builder.
3521 *
3522 * @see Notification#extras
3523 */
Griff Hazen959591e2014-05-15 22:26:18 -07003524 public Builder addExtras(Bundle extras) {
3525 if (extras != null) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003526 mUserExtras.putAll(extras);
Griff Hazen720042b2014-02-24 15:46:56 -08003527 }
3528 return this;
3529 }
3530
3531 /**
3532 * Set metadata for this notification.
3533 *
3534 * <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 -04003535 * current contents are copied into the Notification each time {@link #build()} is
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003536 * called.
3537 *
Griff Hazen720042b2014-02-24 15:46:56 -08003538 * <p>Replaces any existing extras values with those from the provided Bundle.
3539 * Use {@link #addExtras} to merge in metadata instead.
3540 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003541 * @see Notification#extras
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003542 */
Griff Hazen959591e2014-05-15 22:26:18 -07003543 public Builder setExtras(Bundle extras) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003544 if (extras != null) {
3545 mUserExtras = extras;
3546 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003547 return this;
3548 }
3549
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003550 /**
Griff Hazen720042b2014-02-24 15:46:56 -08003551 * Get the current metadata Bundle used by this notification Builder.
3552 *
3553 * <p>The returned Bundle is shared with this Builder.
3554 *
3555 * <p>The current contents of this Bundle are copied into the Notification each time
3556 * {@link #build()} is called.
3557 *
3558 * @see Notification#extras
3559 */
3560 public Bundle getExtras() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003561 return mUserExtras;
3562 }
3563
3564 private Bundle getAllExtras() {
3565 final Bundle saveExtras = (Bundle) mUserExtras.clone();
3566 saveExtras.putAll(mN.extras);
3567 return saveExtras;
Griff Hazen720042b2014-02-24 15:46:56 -08003568 }
3569
3570 /**
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003571 * Add an action to this notification. Actions are typically displayed by
3572 * the system as a button adjacent to the notification content.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04003573 * <p>
3574 * Every action must have an icon (32dp square and matching the
3575 * <a href="{@docRoot}design/style/iconography.html#action-bar">Holo
3576 * Dark action bar</a> visual style), a textual label, and a {@link PendingIntent}.
3577 * <p>
3578 * A notification in its expanded form can display up to 3 actions, from left to right in
3579 * the order they were added. Actions will not be displayed when the notification is
3580 * collapsed, however, so be sure that any essential functions may be accessed by the user
3581 * in some other way (for example, in the Activity pointed to by {@link #contentIntent}).
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003582 *
3583 * @param icon Resource ID of a drawable that represents the action.
3584 * @param title Text describing the action.
3585 * @param intent PendingIntent to be fired when the action is invoked.
Dan Sandler86647982015-05-13 23:41:13 -04003586 *
3587 * @deprecated Use {@link #addAction(Action)} instead.
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003588 */
Dan Sandler86647982015-05-13 23:41:13 -04003589 @Deprecated
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003590 public Builder addAction(int icon, CharSequence title, PendingIntent intent) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04003591 mActions.add(new Action(icon, safeCharSequence(title), intent));
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003592 return this;
3593 }
3594
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003595 /**
Griff Hazen959591e2014-05-15 22:26:18 -07003596 * Add an action to this notification. Actions are typically displayed by
3597 * the system as a button adjacent to the notification content.
3598 * <p>
3599 * Every action must have an icon (32dp square and matching the
3600 * <a href="{@docRoot}design/style/iconography.html#action-bar">Holo
3601 * Dark action bar</a> visual style), a textual label, and a {@link PendingIntent}.
3602 * <p>
3603 * A notification in its expanded form can display up to 3 actions, from left to right in
3604 * the order they were added. Actions will not be displayed when the notification is
3605 * collapsed, however, so be sure that any essential functions may be accessed by the user
3606 * in some other way (for example, in the Activity pointed to by {@link #contentIntent}).
3607 *
3608 * @param action The action to add.
3609 */
3610 public Builder addAction(Action action) {
liangweikang63b03b52017-03-16 19:22:15 +08003611 if (action != null) {
3612 mActions.add(action);
3613 }
Griff Hazen959591e2014-05-15 22:26:18 -07003614 return this;
3615 }
3616
3617 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003618 * Alter the complete list of actions attached to this notification.
3619 * @see #addAction(Action).
3620 *
3621 * @param actions
3622 * @return
3623 */
3624 public Builder setActions(Action... actions) {
3625 mActions.clear();
3626 for (int i = 0; i < actions.length; i++) {
liangweikang63b03b52017-03-16 19:22:15 +08003627 if (actions[i] != null) {
3628 mActions.add(actions[i]);
3629 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003630 }
3631 return this;
3632 }
3633
3634 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003635 * Add a rich notification style to be applied at build time.
3636 *
3637 * @param style Object responsible for modifying the notification style.
3638 */
3639 public Builder setStyle(Style style) {
3640 if (mStyle != style) {
3641 mStyle = style;
Daniel Sandlerc08dea22012-06-28 08:35:24 -07003642 if (mStyle != null) {
3643 mStyle.setBuilder(this);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003644 mN.extras.putString(EXTRA_TEMPLATE, style.getClass().getName());
3645 } else {
3646 mN.extras.remove(EXTRA_TEMPLATE);
Daniel Sandlerc08dea22012-06-28 08:35:24 -07003647 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003648 }
3649 return this;
3650 }
3651
Dan Sandler0bf2ed82013-12-21 23:33:41 -06003652 /**
3653 * Specify the value of {@link #visibility}.
Griff Hazenb720abe2014-05-20 13:15:30 -07003654 *
Dan Sandler0bf2ed82013-12-21 23:33:41 -06003655 * @return The same Builder.
3656 */
Jeff Sharkey30e06bb2017-04-24 11:18:03 -06003657 public Builder setVisibility(@Visibility int visibility) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003658 mN.visibility = visibility;
Dan Sandler0bf2ed82013-12-21 23:33:41 -06003659 return this;
3660 }
3661
3662 /**
3663 * Supply a replacement Notification whose contents should be shown in insecure contexts
3664 * (i.e. atop the secure lockscreen). See {@link #visibility} and {@link #VISIBILITY_PUBLIC}.
3665 * @param n A replacement notification, presumably with some or all info redacted.
3666 * @return The same Builder.
3667 */
3668 public Builder setPublicVersion(Notification n) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003669 if (n != null) {
3670 mN.publicVersion = new Notification();
3671 n.cloneInto(mN.publicVersion, /*heavy=*/ true);
3672 } else {
3673 mN.publicVersion = null;
3674 }
Dan Sandler0bf2ed82013-12-21 23:33:41 -06003675 return this;
3676 }
3677
Griff Hazenb720abe2014-05-20 13:15:30 -07003678 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003679 * Apply an extender to this notification builder. Extenders may be used to add
3680 * metadata or change options on this builder.
3681 */
Griff Hazen61a9e862014-05-22 16:05:19 -07003682 public Builder extend(Extender extender) {
3683 extender.extend(this);
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003684 return this;
3685 }
3686
Dan Sandler4e787062015-06-17 15:09:48 -04003687 /**
3688 * @hide
3689 */
Julia Reynoldse46bb372016-03-17 11:05:58 -04003690 public Builder setFlag(int mask, boolean value) {
Joe Onorato46439ce2010-11-19 13:56:21 -08003691 if (value) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003692 mN.flags |= mask;
Joe Onorato46439ce2010-11-19 13:56:21 -08003693 } else {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003694 mN.flags &= ~mask;
Joe Onorato46439ce2010-11-19 13:56:21 -08003695 }
Julia Reynoldse46bb372016-03-17 11:05:58 -04003696 return this;
Joe Onorato46439ce2010-11-19 13:56:21 -08003697 }
3698
Dan Sandler26e81cf2014-05-06 10:01:27 -04003699 /**
3700 * Sets {@link Notification#color}.
3701 *
3702 * @param argb The accent color to use
3703 *
3704 * @return The same Builder.
3705 */
Tor Norbye80756e32015-03-02 09:39:27 -08003706 public Builder setColor(@ColorInt int argb) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003707 mN.color = argb;
Julia Reynolds10ee1fc2015-11-09 11:04:55 -05003708 sanitizeColor();
Dan Sandler26e81cf2014-05-06 10:01:27 -04003709 return this;
3710 }
3711
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003712 private Drawable getProfileBadgeDrawable() {
Chris Wren66619a22016-05-12 16:42:37 -04003713 if (mContext.getUserId() == UserHandle.USER_SYSTEM) {
3714 // This user can never be a badged profile,
3715 // and also includes USER_ALL system notifications.
3716 return null;
3717 }
Christoph Studer7ac80e62014-08-04 16:01:57 +02003718 // Note: This assumes that the current user can read the profile badge of the
3719 // originating user.
Selim Cineke6ff9462016-01-15 15:07:06 -08003720 return mContext.getPackageManager().getUserBadgeForDensityNoBackground(
Julia Reynoldsda303542015-11-23 14:00:20 -05003721 new UserHandle(mContext.getUserId()), 0);
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003722 }
3723
3724 private Bitmap getProfileBadge() {
3725 Drawable badge = getProfileBadgeDrawable();
Kenny Guy8a0101b2014-05-08 23:34:12 +01003726 if (badge == null) {
3727 return null;
3728 }
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003729 final int size = mContext.getResources().getDimensionPixelSize(
3730 R.dimen.notification_badge_size);
3731 Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
Kenny Guy8a0101b2014-05-08 23:34:12 +01003732 Canvas canvas = new Canvas(bitmap);
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003733 badge.setBounds(0, 0, size, size);
Kenny Guy8a0101b2014-05-08 23:34:12 +01003734 badge.draw(canvas);
3735 return bitmap;
3736 }
3737
Selim Cinekc848c3a2016-01-13 15:27:30 -08003738 private void bindProfileBadge(RemoteViews contentView) {
Kenny Guy98193ea2014-07-24 19:54:37 +01003739 Bitmap profileBadge = getProfileBadge();
3740
Kenny Guy98193ea2014-07-24 19:54:37 +01003741 if (profileBadge != null) {
Selim Cinekc848c3a2016-01-13 15:27:30 -08003742 contentView.setImageViewBitmap(R.id.profile_badge, profileBadge);
3743 contentView.setViewVisibility(R.id.profile_badge, View.VISIBLE);
Selim Cinek7b9605b2017-01-19 17:36:00 -08003744 if (isColorized()) {
3745 contentView.setDrawableParameters(R.id.profile_badge, false, -1,
3746 getPrimaryTextColor(), PorterDuff.Mode.SRC_ATOP, -1);
3747 }
Kenny Guy98193ea2014-07-24 19:54:37 +01003748 }
Kenny Guy98193ea2014-07-24 19:54:37 +01003749 }
3750
Christoph Studerfe718432014-09-01 18:21:18 +02003751 private void resetStandardTemplate(RemoteViews contentView) {
Selim Cinekeaa29ca2015-11-23 13:51:13 -08003752 resetNotificationHeader(contentView);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003753 resetContentMargins(contentView);
Christoph Studerfe718432014-09-01 18:21:18 +02003754 contentView.setViewVisibility(R.id.right_icon, View.GONE);
Selim Cinek860b6da2015-12-16 19:02:19 -08003755 contentView.setViewVisibility(R.id.title, View.GONE);
Christoph Studerfe718432014-09-01 18:21:18 +02003756 contentView.setTextViewText(R.id.title, null);
Selim Cinek41598732016-01-11 16:58:37 -08003757 contentView.setViewVisibility(R.id.text, View.GONE);
Christoph Studerfe718432014-09-01 18:21:18 +02003758 contentView.setTextViewText(R.id.text, null);
Selim Cinek29603462015-11-17 19:04:39 -08003759 contentView.setViewVisibility(R.id.text_line_1, View.GONE);
Selim Cinek41598732016-01-11 16:58:37 -08003760 contentView.setTextViewText(R.id.text_line_1, null);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003761 }
3762
Selim Cinekeaa29ca2015-11-23 13:51:13 -08003763 /**
3764 * Resets the notification header to its original state
3765 */
3766 private void resetNotificationHeader(RemoteViews contentView) {
Adrian Roosc4337a32016-08-02 18:30:34 -07003767 // Small icon doesn't need to be reset, as it's always set. Resetting would prevent
3768 // re-using the drawable when the notification is updated.
Selim Cinek7b836392015-12-04 20:02:59 -08003769 contentView.setBoolean(R.id.notification_header, "setExpanded", false);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003770 contentView.setTextViewText(R.id.app_name_text, null);
Christoph Studerca1db712014-09-10 17:31:33 +02003771 contentView.setViewVisibility(R.id.chronometer, View.GONE);
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07003772 contentView.setViewVisibility(R.id.header_text, View.GONE);
Adrian Roos9dfb78f2016-06-30 15:43:44 -07003773 contentView.setTextViewText(R.id.header_text, null);
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07003774 contentView.setViewVisibility(R.id.header_text_divider, View.GONE);
Selim Cinek29603462015-11-17 19:04:39 -08003775 contentView.setViewVisibility(R.id.time_divider, View.GONE);
Selim Cinekb85f36fd2016-04-20 18:46:36 -07003776 contentView.setViewVisibility(R.id.time, View.GONE);
Selim Cinekc848c3a2016-01-13 15:27:30 -08003777 contentView.setImageViewIcon(R.id.profile_badge, null);
3778 contentView.setViewVisibility(R.id.profile_badge, View.GONE);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003779 }
3780
3781 private void resetContentMargins(RemoteViews contentView) {
Adrian Roos2d5dbba2016-06-08 17:11:53 -07003782 contentView.setViewLayoutMarginEndDimen(R.id.line1, 0);
3783 contentView.setViewLayoutMarginEndDimen(R.id.text, 0);
Christoph Studerfe718432014-09-01 18:21:18 +02003784 }
3785
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003786 private RemoteViews applyStandardTemplate(int resId) {
Adrian Roos70d7aa32017-01-11 15:39:06 -08003787 return applyStandardTemplate(resId, mParams.reset().fillTextsFrom(this));
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02003788 }
3789
3790 /**
3791 * @param hasProgress whether the progress bar should be shown and set
3792 */
3793 private RemoteViews applyStandardTemplate(int resId, boolean hasProgress) {
Adrian Roos70d7aa32017-01-11 15:39:06 -08003794 return applyStandardTemplate(resId, mParams.reset().hasProgress(hasProgress)
3795 .fillTextsFrom(this));
Adrian Roosc1a80b02016-04-05 14:54:55 -07003796 }
3797
Adrian Roos70d7aa32017-01-11 15:39:06 -08003798 private RemoteViews applyStandardTemplate(int resId, StandardTemplateParams p) {
Kenny Guy77320062014-08-27 21:37:15 +01003799 RemoteViews contentView = new BuilderRemoteViews(mContext.getApplicationInfo(), resId);
Dan Sandler539aad42014-08-04 00:43:39 -04003800
Christoph Studerfe718432014-09-01 18:21:18 +02003801 resetStandardTemplate(contentView);
3802
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003803 final Bundle ex = mN.extras;
Selim Cinek7b9605b2017-01-19 17:36:00 -08003804 updateBackgroundColor(contentView);
Adrian Roos487374f2017-01-11 15:48:14 -08003805 bindNotificationHeader(contentView, p.ambient);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003806 bindLargeIcon(contentView);
Adrian Roos70d7aa32017-01-11 15:39:06 -08003807 boolean showProgress = handleProgressBar(p.hasProgress, contentView, ex);
3808 if (p.title != null) {
Selim Cinek860b6da2015-12-16 19:02:19 -08003809 contentView.setViewVisibility(R.id.title, View.VISIBLE);
Adrian Roos70d7aa32017-01-11 15:39:06 -08003810 contentView.setTextViewText(R.id.title, p.title);
Adrian Roos72171622017-01-27 10:32:06 -08003811 if (!p.ambient) {
3812 setTextViewColorPrimary(contentView, R.id.title);
3813 }
Selim Cinek954cc232016-05-20 13:29:23 -07003814 contentView.setViewLayoutWidth(R.id.title, showProgress
3815 ? ViewGroup.LayoutParams.WRAP_CONTENT
3816 : ViewGroup.LayoutParams.MATCH_PARENT);
Joe Onorato561d3852010-11-20 18:09:34 -08003817 }
Adrian Roos70d7aa32017-01-11 15:39:06 -08003818 if (p.text != null) {
Selim Cinek41598732016-01-11 16:58:37 -08003819 int textId = showProgress ? com.android.internal.R.id.text_line_1
3820 : com.android.internal.R.id.text;
Adrian Roos70d7aa32017-01-11 15:39:06 -08003821 contentView.setTextViewText(textId, p.text);
Adrian Roos72171622017-01-27 10:32:06 -08003822 if (!p.ambient) {
3823 setTextViewColorSecondary(contentView, textId);
3824 }
Selim Cinek41598732016-01-11 16:58:37 -08003825 contentView.setViewVisibility(textId, View.VISIBLE);
Joe Onorato561d3852010-11-20 18:09:34 -08003826 }
Selim Cinekc848c3a2016-01-13 15:27:30 -08003827
Selim Cinek279fa862016-06-14 10:57:25 -07003828 setContentMinHeight(contentView, showProgress || mN.hasLargeIcon());
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003829
Selim Cinek29603462015-11-17 19:04:39 -08003830 return contentView;
3831 }
3832
Selim Cinek7b9605b2017-01-19 17:36:00 -08003833 private void setTextViewColorPrimary(RemoteViews contentView, int id) {
3834 ensureColors();
3835 contentView.setTextColor(id, mPrimaryTextColor);
3836 }
3837
3838 private int getPrimaryTextColor() {
3839 ensureColors();
3840 return mPrimaryTextColor;
3841 }
3842
3843 private int getActionBarColor() {
3844 ensureColors();
3845 return mActionBarColor;
3846 }
3847
Selim Cinek622c64a2017-04-17 17:10:05 -07003848 private int getActionBarColorDeEmphasized() {
3849 int backgroundColor = getBackgroundColor();
3850 return NotificationColorUtil.getShiftedColor(backgroundColor, 12);
3851 }
3852
Selim Cinek7b9605b2017-01-19 17:36:00 -08003853 private void setTextViewColorSecondary(RemoteViews contentView, int id) {
3854 ensureColors();
3855 contentView.setTextColor(id, mSecondaryTextColor);
3856 }
3857
3858 private void ensureColors() {
3859 int backgroundColor = getBackgroundColor();
3860 if (mPrimaryTextColor == COLOR_INVALID
3861 || mSecondaryTextColor == COLOR_INVALID
3862 || mActionBarColor == COLOR_INVALID
3863 || mTextColorsAreForBackground != backgroundColor) {
3864 mTextColorsAreForBackground = backgroundColor;
Selim Cinek5fb73f82017-04-20 16:55:38 -07003865 if (mForegroundColor == COLOR_INVALID || !isColorized()) {
3866 mPrimaryTextColor = NotificationColorUtil.resolvePrimaryColor(mContext,
3867 backgroundColor);
3868 mSecondaryTextColor = NotificationColorUtil.resolveSecondaryColor(mContext,
3869 backgroundColor);
Selim Cinekac5f0272017-05-02 16:05:41 -07003870 if (backgroundColor != COLOR_DEFAULT
3871 && (mBackgroundColorHint != COLOR_INVALID || isColorized())) {
3872 mPrimaryTextColor = NotificationColorUtil.findAlphaToMeetContrast(
3873 mPrimaryTextColor, backgroundColor, 4.5);
3874 mSecondaryTextColor = NotificationColorUtil.findAlphaToMeetContrast(
3875 mSecondaryTextColor, backgroundColor, 4.5);
3876 }
Selim Cinek5fb73f82017-04-20 16:55:38 -07003877 } else {
3878 double backLum = NotificationColorUtil.calculateLuminance(backgroundColor);
3879 double textLum = NotificationColorUtil.calculateLuminance(mForegroundColor);
3880 double contrast = NotificationColorUtil.calculateContrast(mForegroundColor,
3881 backgroundColor);
3882 boolean textDark = backLum > textLum;
3883 if (contrast < 4.5f) {
3884 if (textDark) {
3885 mSecondaryTextColor = NotificationColorUtil.findContrastColor(
3886 mForegroundColor,
3887 backgroundColor,
3888 true /* findFG */,
3889 4.5f);
3890 mPrimaryTextColor = NotificationColorUtil.changeColorLightness(
3891 mSecondaryTextColor, -20);
3892 } else {
3893 mSecondaryTextColor =
3894 NotificationColorUtil.findContrastColorAgainstDark(
3895 mForegroundColor,
3896 backgroundColor,
3897 true /* findFG */,
3898 4.5f);
3899 mPrimaryTextColor = NotificationColorUtil.changeColorLightness(
3900 mSecondaryTextColor, 10);
3901 }
3902 } else {
3903 mPrimaryTextColor = mForegroundColor;
3904 mSecondaryTextColor = NotificationColorUtil.changeColorLightness(
3905 mPrimaryTextColor, textDark ? 10 : -20);
3906 if (NotificationColorUtil.calculateContrast(mSecondaryTextColor,
3907 backgroundColor) < 4.5f) {
3908 // oh well the secondary is not good enough
3909 if (textDark) {
3910 mSecondaryTextColor = NotificationColorUtil.findContrastColor(
3911 mSecondaryTextColor,
3912 backgroundColor,
3913 true /* findFG */,
3914 4.5f);
3915 } else {
3916 mSecondaryTextColor
3917 = NotificationColorUtil.findContrastColorAgainstDark(
3918 mSecondaryTextColor,
3919 backgroundColor,
3920 true /* findFG */,
3921 4.5f);
3922 }
3923 mPrimaryTextColor = NotificationColorUtil.changeColorLightness(
3924 mSecondaryTextColor, textDark ? -20 : 10);
3925 }
3926 }
3927 }
Selim Cinek875ba9b2017-02-13 16:20:17 -08003928 mActionBarColor = NotificationColorUtil.resolveActionBarColor(mContext,
3929 backgroundColor);
Selim Cinek7b9605b2017-01-19 17:36:00 -08003930 }
3931 }
3932
3933 private void updateBackgroundColor(RemoteViews contentView) {
3934 if (isColorized()) {
3935 contentView.setInt(R.id.status_bar_latest_event_content, "setBackgroundColor",
3936 getBackgroundColor());
3937 } else {
3938 // Clear it!
3939 contentView.setInt(R.id.status_bar_latest_event_content, "setBackgroundResource",
3940 0);
3941 }
3942 }
3943
Selim Cinek860b6da2015-12-16 19:02:19 -08003944 /**
3945 * @param remoteView the remote view to update the minheight in
3946 * @param hasMinHeight does it have a mimHeight
3947 * @hide
3948 */
3949 void setContentMinHeight(RemoteViews remoteView, boolean hasMinHeight) {
3950 int minHeight = 0;
3951 if (hasMinHeight) {
3952 // we need to set the minHeight of the notification
3953 minHeight = mContext.getResources().getDimensionPixelSize(
3954 com.android.internal.R.dimen.notification_min_content_height);
3955 }
3956 remoteView.setInt(R.id.notification_main_column, "setMinimumHeight", minHeight);
3957 }
3958
Selim Cinek29603462015-11-17 19:04:39 -08003959 private boolean handleProgressBar(boolean hasProgress, RemoteViews contentView, Bundle ex) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003960 final int max = ex.getInt(EXTRA_PROGRESS_MAX, 0);
3961 final int progress = ex.getInt(EXTRA_PROGRESS, 0);
3962 final boolean ind = ex.getBoolean(EXTRA_PROGRESS_INDETERMINATE);
3963 if (hasProgress && (max != 0 || ind)) {
Selim Cinek29603462015-11-17 19:04:39 -08003964 contentView.setViewVisibility(com.android.internal.R.id.progress, View.VISIBLE);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003965 contentView.setProgressBar(
Selim Cinek29603462015-11-17 19:04:39 -08003966 R.id.progress, max, progress, ind);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003967 contentView.setProgressBackgroundTintList(
3968 R.id.progress, ColorStateList.valueOf(mContext.getColor(
3969 R.color.notification_progress_background_color)));
Selim Cinek29603462015-11-17 19:04:39 -08003970 if (mN.color != COLOR_DEFAULT) {
Adrian Roos4ff3b122016-02-01 12:26:13 -08003971 ColorStateList colorStateList = ColorStateList.valueOf(resolveContrastColor());
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003972 contentView.setProgressTintList(R.id.progress, colorStateList);
3973 contentView.setProgressIndeterminateTintList(R.id.progress, colorStateList);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003974 }
Selim Cinek29603462015-11-17 19:04:39 -08003975 return true;
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003976 } else {
3977 contentView.setViewVisibility(R.id.progress, View.GONE);
Selim Cinek29603462015-11-17 19:04:39 -08003978 return false;
Jeff Sharkey1c400132011-08-05 14:50:13 -07003979 }
Joe Onorato561d3852010-11-20 18:09:34 -08003980 }
3981
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003982 private void bindLargeIcon(RemoteViews contentView) {
Selim Cinek279fa862016-06-14 10:57:25 -07003983 if (mN.mLargeIcon == null && mN.largeIcon != null) {
3984 mN.mLargeIcon = Icon.createWithBitmap(mN.largeIcon);
3985 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003986 if (mN.mLargeIcon != null) {
3987 contentView.setViewVisibility(R.id.right_icon, View.VISIBLE);
3988 contentView.setImageViewIcon(R.id.right_icon, mN.mLargeIcon);
3989 processLargeLegacyIcon(mN.mLargeIcon, contentView);
Adrian Roos2d5dbba2016-06-08 17:11:53 -07003990 int endMargin = R.dimen.notification_content_picture_margin;
3991 contentView.setViewLayoutMarginEndDimen(R.id.line1, endMargin);
3992 contentView.setViewLayoutMarginEndDimen(R.id.text, endMargin);
3993 contentView.setViewLayoutMarginEndDimen(R.id.progress, endMargin);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003994 }
3995 }
3996
Adrian Roos487374f2017-01-11 15:48:14 -08003997 private void bindNotificationHeader(RemoteViews contentView, boolean ambient) {
3998 bindSmallIcon(contentView, ambient);
3999 bindHeaderAppName(contentView, ambient);
4000 if (!ambient) {
4001 // Ambient view does not have these
4002 bindHeaderText(contentView);
4003 bindHeaderChronometerAndTime(contentView);
Adrian Roos487374f2017-01-11 15:48:14 -08004004 bindProfileBadge(contentView);
4005 }
Adrian Roosd83e9992017-03-16 15:17:57 -07004006 bindExpandButton(contentView);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004007 }
4008
4009 private void bindExpandButton(RemoteViews contentView) {
Selim Cinek99104832017-01-25 14:47:33 -08004010 int color = getPrimaryHighlightColor();
Selim Cinek7b9605b2017-01-19 17:36:00 -08004011 contentView.setDrawableParameters(R.id.expand_button, false, -1, color,
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004012 PorterDuff.Mode.SRC_ATOP, -1);
Selim Cinekea4bef72015-12-02 15:51:10 -08004013 contentView.setInt(R.id.notification_header, "setOriginalNotificationColor",
Selim Cinek7b9605b2017-01-19 17:36:00 -08004014 color);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004015 }
4016
Selim Cinek99104832017-01-25 14:47:33 -08004017 /**
4018 * @return the color that is used as the first primary highlight color. This is applied
4019 * in several places like the action buttons or the app name in the header.
4020 */
4021 private int getPrimaryHighlightColor() {
4022 return isColorized() ? getPrimaryTextColor() : resolveContrastColor();
4023 }
4024
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004025 private void bindHeaderChronometerAndTime(RemoteViews contentView) {
4026 if (showsTimeOrChronometer()) {
Selim Cinek29603462015-11-17 19:04:39 -08004027 contentView.setViewVisibility(R.id.time_divider, View.VISIBLE);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004028 setTextViewColorSecondary(contentView, R.id.time_divider);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004029 if (mN.extras.getBoolean(EXTRA_SHOW_CHRONOMETER)) {
4030 contentView.setViewVisibility(R.id.chronometer, View.VISIBLE);
4031 contentView.setLong(R.id.chronometer, "setBase",
4032 mN.when + (SystemClock.elapsedRealtime() - System.currentTimeMillis()));
4033 contentView.setBoolean(R.id.chronometer, "setStarted", true);
Adrian Roos96b7e202016-05-17 13:50:38 -07004034 boolean countsDown = mN.extras.getBoolean(EXTRA_CHRONOMETER_COUNT_DOWN);
Selim Cinekc3b752e2016-04-20 16:13:59 -07004035 contentView.setChronometerCountDown(R.id.chronometer, countsDown);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004036 setTextViewColorSecondary(contentView, R.id.chronometer);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004037 } else {
4038 contentView.setViewVisibility(R.id.time, View.VISIBLE);
4039 contentView.setLong(R.id.time, "setTime", mN.when);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004040 setTextViewColorSecondary(contentView, R.id.time);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004041 }
Selim Cinekb85f36fd2016-04-20 18:46:36 -07004042 } else {
4043 // We still want a time to be set but gone, such that we can show and hide it
4044 // on demand in case it's a child notification without anything in the header
4045 contentView.setLong(R.id.time, "setTime", mN.when != 0 ? mN.when : mN.creationTime);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004046 }
4047 }
4048
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07004049 private void bindHeaderText(RemoteViews contentView) {
4050 CharSequence headerText = mN.extras.getCharSequence(EXTRA_SUB_TEXT);
4051 if (headerText == null && mStyle != null && mStyle.mSummaryTextSet
Selim Cinek03d0d652015-11-13 13:18:09 -05004052 && mStyle.hasSummaryInHeader()) {
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07004053 headerText = mStyle.mSummaryText;
Selim Cinek03d0d652015-11-13 13:18:09 -05004054 }
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07004055 if (headerText == null
4056 && mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N
4057 && mN.extras.getCharSequence(EXTRA_INFO_TEXT) != null) {
4058 headerText = mN.extras.getCharSequence(EXTRA_INFO_TEXT);
4059 }
4060 if (headerText != null) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004061 // TODO: Remove the span entirely to only have the string with propper formating.
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07004062 contentView.setTextViewText(R.id.header_text, processLegacyText(headerText));
Selim Cinek7b9605b2017-01-19 17:36:00 -08004063 setTextViewColorSecondary(contentView, R.id.header_text);
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07004064 contentView.setViewVisibility(R.id.header_text, View.VISIBLE);
4065 contentView.setViewVisibility(R.id.header_text_divider, View.VISIBLE);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004066 setTextViewColorSecondary(contentView, R.id.header_text_divider);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004067 }
4068 }
4069
Adrian Rooseba05822016-04-22 17:09:27 -07004070 /**
4071 * @hide
4072 */
4073 public String loadHeaderAppName() {
Dan Sandler732bd6c2016-04-12 14:20:32 -04004074 CharSequence name = null;
4075 final PackageManager pm = mContext.getPackageManager();
4076 if (mN.extras.containsKey(EXTRA_SUBSTITUTE_APP_NAME)) {
4077 // only system packages which lump together a bunch of unrelated stuff
4078 // may substitute a different name to make the purpose of the
4079 // notification more clear. the correct package label should always
4080 // be accessible via SystemUI.
4081 final String pkg = mContext.getPackageName();
4082 final String subName = mN.extras.getString(EXTRA_SUBSTITUTE_APP_NAME);
4083 if (PackageManager.PERMISSION_GRANTED == pm.checkPermission(
4084 android.Manifest.permission.SUBSTITUTE_NOTIFICATION_APP_NAME, pkg)) {
4085 name = subName;
4086 } else {
4087 Log.w(TAG, "warning: pkg "
4088 + pkg + " attempting to substitute app name '" + subName
4089 + "' without holding perm "
4090 + android.Manifest.permission.SUBSTITUTE_NOTIFICATION_APP_NAME);
4091 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004092 }
Dan Sandler732bd6c2016-04-12 14:20:32 -04004093 if (TextUtils.isEmpty(name)) {
4094 name = pm.getApplicationLabel(mContext.getApplicationInfo());
4095 }
4096 if (TextUtils.isEmpty(name)) {
4097 // still nothing?
4098 return null;
4099 }
4100
4101 return String.valueOf(name);
4102 }
Adrian Roos487374f2017-01-11 15:48:14 -08004103 private void bindHeaderAppName(RemoteViews contentView, boolean ambient) {
Dan Sandler732bd6c2016-04-12 14:20:32 -04004104 contentView.setTextViewText(R.id.app_name_text, loadHeaderAppName());
Adrian Roos72171622017-01-27 10:32:06 -08004105 if (isColorized() && !ambient) {
Selim Cinek7b9605b2017-01-19 17:36:00 -08004106 setTextViewColorPrimary(contentView, R.id.app_name_text);
4107 } else {
4108 contentView.setTextColor(R.id.app_name_text,
4109 ambient ? resolveAmbientColor() : resolveContrastColor());
4110 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004111 }
4112
Adrian Roos487374f2017-01-11 15:48:14 -08004113 private void bindSmallIcon(RemoteViews contentView, boolean ambient) {
Selim Cinek279fa862016-06-14 10:57:25 -07004114 if (mN.mSmallIcon == null && mN.icon != 0) {
4115 mN.mSmallIcon = Icon.createWithResource(mContext, mN.icon);
4116 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004117 contentView.setImageViewIcon(R.id.icon, mN.mSmallIcon);
Adrian Roos9b45a152016-06-28 13:32:29 -07004118 contentView.setDrawableParameters(R.id.icon, false /* targetBackground */,
4119 -1 /* alpha */, -1 /* colorFilter */, null /* mode */, mN.iconLevel);
Adrian Roos487374f2017-01-11 15:48:14 -08004120 processSmallIconColor(mN.mSmallIcon, contentView, ambient);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004121 }
4122
Jorim Jaggi445d3c02014-08-19 22:33:42 +02004123 /**
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004124 * @return true if the built notification will show the time or the chronometer; false
4125 * otherwise
4126 */
4127 private boolean showsTimeOrChronometer() {
Selim Cinekc2c0b042016-05-18 17:13:46 -07004128 return mN.showsTime() || mN.showsChronometer();
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004129 }
4130
Christoph Studerfe718432014-09-01 18:21:18 +02004131 private void resetStandardTemplateWithActions(RemoteViews big) {
Adrian Roos4c1fcc82016-03-31 14:39:39 -07004132 // actions_container is only reset when there are no actions to avoid focus issues with
4133 // remote inputs.
Christoph Studerfe718432014-09-01 18:21:18 +02004134 big.setViewVisibility(R.id.actions, View.GONE);
Christoph Studerfe718432014-09-01 18:21:18 +02004135 big.removeAllViews(R.id.actions);
Adrian Roose458aa82015-12-08 16:17:19 -08004136
4137 big.setViewVisibility(R.id.notification_material_reply_container, View.GONE);
4138 big.setTextViewText(R.id.notification_material_reply_text_1, null);
4139
4140 big.setViewVisibility(R.id.notification_material_reply_text_2, View.GONE);
4141 big.setTextViewText(R.id.notification_material_reply_text_2, null);
4142 big.setViewVisibility(R.id.notification_material_reply_text_3, View.GONE);
4143 big.setTextViewText(R.id.notification_material_reply_text_3, null);
Adrian Roosf852a422016-06-03 13:33:43 -07004144
4145 big.setViewLayoutMarginBottomDimen(R.id.notification_action_list_margin_target, 0);
Christoph Studerfe718432014-09-01 18:21:18 +02004146 }
4147
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004148 private RemoteViews applyStandardTemplateWithActions(int layoutId) {
Adrian Roos70d7aa32017-01-11 15:39:06 -08004149 return applyStandardTemplateWithActions(layoutId, mParams.reset().fillTextsFrom(this));
Adrian Roos48d746a2016-04-12 14:57:28 -07004150 }
4151
Adrian Roos70d7aa32017-01-11 15:39:06 -08004152 private RemoteViews applyStandardTemplateWithActions(int layoutId,
4153 StandardTemplateParams p) {
4154 RemoteViews big = applyStandardTemplate(layoutId, p);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004155
Christoph Studerfe718432014-09-01 18:21:18 +02004156 resetStandardTemplateWithActions(big);
4157
Adrian Roose458aa82015-12-08 16:17:19 -08004158 boolean validRemoteInput = false;
4159
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004160 int N = mActions.size();
Adrian Roos487374f2017-01-11 15:48:14 -08004161 boolean emphazisedMode = mN.fullScreenIntent != null && !p.ambient;
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004162 big.setBoolean(R.id.actions, "setEmphasizedMode", emphazisedMode);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004163 if (N > 0) {
Adrian Roos7052de52016-03-03 15:53:34 -08004164 big.setViewVisibility(R.id.actions_container, View.VISIBLE);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004165 big.setViewVisibility(R.id.actions, View.VISIBLE);
Adrian Roos487374f2017-01-11 15:48:14 -08004166 if (p.ambient) {
4167 big.setInt(R.id.actions, "setBackgroundColor", Color.TRANSPARENT);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004168 } else if (isColorized()) {
4169 big.setInt(R.id.actions, "setBackgroundColor", getActionBarColor());
4170 } else {
4171 big.setInt(R.id.actions, "setBackgroundColor", mContext.getColor(
4172 R.color.notification_action_list));
Adrian Roos487374f2017-01-11 15:48:14 -08004173 }
Adrian Roosf852a422016-06-03 13:33:43 -07004174 big.setViewLayoutMarginBottomDimen(R.id.notification_action_list_margin_target,
4175 R.dimen.notification_action_list_height);
Daniel Sandler8680bf82012-05-15 16:52:52 -04004176 if (N>MAX_ACTION_BUTTONS) N=MAX_ACTION_BUTTONS;
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004177 for (int i=0; i<N; i++) {
Adrian Roose458aa82015-12-08 16:17:19 -08004178 Action action = mActions.get(i);
4179 validRemoteInput |= hasValidRemoteInput(action);
4180
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004181 final RemoteViews button = generateActionButton(action, emphazisedMode,
Adrian Roos487374f2017-01-11 15:48:14 -08004182 i % 2 != 0, p.ambient);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004183 big.addView(R.id.actions, button);
4184 }
Adrian Roos4c1fcc82016-03-31 14:39:39 -07004185 } else {
4186 big.setViewVisibility(R.id.actions_container, View.GONE);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004187 }
Adrian Roose458aa82015-12-08 16:17:19 -08004188
4189 CharSequence[] replyText = mN.extras.getCharSequenceArray(EXTRA_REMOTE_INPUT_HISTORY);
Adrian Roos487374f2017-01-11 15:48:14 -08004190 if (!p.ambient && validRemoteInput && replyText != null
Adrian Roose458aa82015-12-08 16:17:19 -08004191 && replyText.length > 0 && !TextUtils.isEmpty(replyText[0])) {
4192 big.setViewVisibility(R.id.notification_material_reply_container, View.VISIBLE);
4193 big.setTextViewText(R.id.notification_material_reply_text_1, replyText[0]);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004194 setTextViewColorSecondary(big, R.id.notification_material_reply_text_1);
Adrian Roose458aa82015-12-08 16:17:19 -08004195
4196 if (replyText.length > 1 && !TextUtils.isEmpty(replyText[1])) {
4197 big.setViewVisibility(R.id.notification_material_reply_text_2, View.VISIBLE);
4198 big.setTextViewText(R.id.notification_material_reply_text_2, replyText[1]);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004199 setTextViewColorSecondary(big, R.id.notification_material_reply_text_2);
Adrian Roose458aa82015-12-08 16:17:19 -08004200
4201 if (replyText.length > 2 && !TextUtils.isEmpty(replyText[2])) {
4202 big.setViewVisibility(
4203 R.id.notification_material_reply_text_3, View.VISIBLE);
4204 big.setTextViewText(R.id.notification_material_reply_text_3, replyText[2]);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004205 setTextViewColorSecondary(big, R.id.notification_material_reply_text_3);
Adrian Roose458aa82015-12-08 16:17:19 -08004206 }
4207 }
4208 }
4209
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004210 return big;
4211 }
4212
Adrian Roose458aa82015-12-08 16:17:19 -08004213 private boolean hasValidRemoteInput(Action action) {
4214 if (TextUtils.isEmpty(action.title) || action.actionIntent == null) {
4215 // Weird actions
4216 return false;
4217 }
4218
4219 RemoteInput[] remoteInputs = action.getRemoteInputs();
4220 if (remoteInputs == null) {
4221 return false;
4222 }
4223
4224 for (RemoteInput r : remoteInputs) {
4225 CharSequence[] choices = r.getChoices();
4226 if (r.getAllowFreeFormInput() || (choices != null && choices.length != 0)) {
4227 return true;
4228 }
4229 }
4230 return false;
4231 }
4232
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004233 /**
4234 * Construct a RemoteViews for the final 1U notification layout. In order:
4235 * 1. Custom contentView from the caller
4236 * 2. Style's proposed content view
4237 * 3. Standard template view
4238 */
Julia Reynolds3b848122016-02-26 10:45:32 -05004239 public RemoteViews createContentView() {
Selim Cinek7d1009b2017-01-25 15:28:28 -08004240 return createContentView(false /* increasedheight */ );
4241 }
4242
4243 /**
4244 * Construct a RemoteViews for the smaller content view.
4245 *
4246 * @param increasedHeight true if this layout be created with an increased height. Some
4247 * styles may support showing more then just that basic 1U size
4248 * and the system may decide to render important notifications
4249 * slightly bigger even when collapsed.
4250 *
4251 * @hide
4252 */
4253 public RemoteViews createContentView(boolean increasedHeight) {
Selim Cinek593610c2016-02-16 18:42:57 -08004254 if (mN.contentView != null && (mStyle == null || !mStyle.displayCustomViewInline())) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004255 return mN.contentView;
4256 } else if (mStyle != null) {
Selim Cinek7d1009b2017-01-25 15:28:28 -08004257 final RemoteViews styleView = mStyle.makeContentView(increasedHeight);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004258 if (styleView != null) {
4259 return styleView;
4260 }
Joe Onorato46439ce2010-11-19 13:56:21 -08004261 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004262 return applyStandardTemplate(getBaseLayoutResource());
Joe Onorato46439ce2010-11-19 13:56:21 -08004263 }
4264
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004265 /**
4266 * Construct a RemoteViews for the final big notification layout.
4267 */
Julia Reynolds3b848122016-02-26 10:45:32 -05004268 public RemoteViews createBigContentView() {
Selim Cinek850a8542015-11-11 11:48:36 -05004269 RemoteViews result = null;
Selim Cinek593610c2016-02-16 18:42:57 -08004270 if (mN.bigContentView != null
4271 && (mStyle == null || !mStyle.displayCustomViewInline())) {
Julia Reynolds089e3e42015-11-18 09:59:57 -05004272 return mN.bigContentView;
4273 } else if (mStyle != null) {
Selim Cinek850a8542015-11-11 11:48:36 -05004274 result = mStyle.makeBigContentView();
Selim Cinek90dcf6d2015-11-18 20:24:13 -08004275 hideLine1Text(result);
Selim Cinekcc10bfb2016-02-10 16:24:21 -08004276 } else if (mActions.size() != 0) {
4277 result = applyStandardTemplateWithActions(getBigBaseLayoutResource());
Selim Cinek850a8542015-11-11 11:48:36 -05004278 }
Selim Cinek6743c0b2017-01-18 18:24:01 -08004279 makeHeaderExpanded(result);
Selim Cinek850a8542015-11-11 11:48:36 -05004280 return result;
4281 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004282
Selim Cinekeaa29ca2015-11-23 13:51:13 -08004283 /**
Selim Cinek414ad332017-02-24 19:06:12 -08004284 * Construct a RemoteViews for the final notification header only. This will not be
4285 * colorized.
Selim Cinekeaa29ca2015-11-23 13:51:13 -08004286 *
Adrian Roos6f6e1592017-05-02 16:22:53 -07004287 * @param ambient if true, generate the header for the ambient display layout.
Selim Cinekeaa29ca2015-11-23 13:51:13 -08004288 * @hide
4289 */
Adrian Roos6f6e1592017-05-02 16:22:53 -07004290 public RemoteViews makeNotificationHeader(boolean ambient) {
Selim Cinek414ad332017-02-24 19:06:12 -08004291 Boolean colorized = (Boolean) mN.extras.get(EXTRA_COLORIZED);
4292 mN.extras.putBoolean(EXTRA_COLORIZED, false);
Selim Cinekeaa29ca2015-11-23 13:51:13 -08004293 RemoteViews header = new BuilderRemoteViews(mContext.getApplicationInfo(),
Adrian Roos6f6e1592017-05-02 16:22:53 -07004294 ambient ? R.layout.notification_template_ambient_header
4295 : R.layout.notification_template_header);
Selim Cinekeaa29ca2015-11-23 13:51:13 -08004296 resetNotificationHeader(header);
Adrian Roos6f6e1592017-05-02 16:22:53 -07004297 bindNotificationHeader(header, ambient);
Selim Cinek414ad332017-02-24 19:06:12 -08004298 if (colorized != null) {
4299 mN.extras.putBoolean(EXTRA_COLORIZED, colorized);
4300 } else {
4301 mN.extras.remove(EXTRA_COLORIZED);
4302 }
Selim Cinekeaa29ca2015-11-23 13:51:13 -08004303 return header;
4304 }
4305
Adrian Roos487374f2017-01-11 15:48:14 -08004306 /**
4307 * Construct a RemoteViews for the ambient version of the notification.
4308 *
4309 * @hide
4310 */
4311 public RemoteViews makeAmbientNotification() {
4312 RemoteViews ambient = applyStandardTemplateWithActions(
4313 R.layout.notification_template_material_ambient,
Adrian Roos0bc3f6a2017-03-06 11:54:05 -08004314 mParams.reset().ambient(true).fillTextsFrom(this).hasProgress(false));
Adrian Roos487374f2017-01-11 15:48:14 -08004315 return ambient;
4316 }
4317
Selim Cinek29603462015-11-17 19:04:39 -08004318 private void hideLine1Text(RemoteViews result) {
Selim Cinekcc10bfb2016-02-10 16:24:21 -08004319 if (result != null) {
4320 result.setViewVisibility(R.id.text_line_1, View.GONE);
4321 }
Selim Cinek29603462015-11-17 19:04:39 -08004322 }
4323
Selim Cinek6743c0b2017-01-18 18:24:01 -08004324 /**
4325 * Adapt the Notification header if this view is used as an expanded view.
4326 *
4327 * @hide
4328 */
4329 public static void makeHeaderExpanded(RemoteViews result) {
Selim Cinekcc10bfb2016-02-10 16:24:21 -08004330 if (result != null) {
4331 result.setBoolean(R.id.notification_header, "setExpanded", true);
4332 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004333 }
4334
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004335 /**
4336 * Construct a RemoteViews for the final heads-up notification layout.
Selim Cinek87ed69b2017-02-09 15:59:43 -08004337 *
4338 * @param increasedHeight true if this layout be created with an increased height. Some
4339 * styles may support showing more then just that basic 1U size
4340 * and the system may decide to render important notifications
4341 * slightly bigger even when collapsed.
4342 *
4343 * @hide
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004344 */
Selim Cinek87ed69b2017-02-09 15:59:43 -08004345 public RemoteViews createHeadsUpContentView(boolean increasedHeight) {
Selim Cinek593610c2016-02-16 18:42:57 -08004346 if (mN.headsUpContentView != null
4347 && (mStyle == null || !mStyle.displayCustomViewInline())) {
Julia Reynolds089e3e42015-11-18 09:59:57 -05004348 return mN.headsUpContentView;
4349 } else if (mStyle != null) {
Selim Cinek87ed69b2017-02-09 15:59:43 -08004350 final RemoteViews styleView = mStyle.makeHeadsUpContentView(increasedHeight);
4351 if (styleView != null) {
4352 return styleView;
4353 }
Julia Reynolds089e3e42015-11-18 09:59:57 -05004354 } else if (mActions.size() == 0) {
4355 return null;
4356 }
4357
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004358 return applyStandardTemplateWithActions(getBigBaseLayoutResource());
Chris Wren8fd39ec2014-02-27 17:43:26 -05004359 }
4360
Selim Cinek624c02db2015-12-14 21:00:02 -08004361 /**
Selim Cinek87ed69b2017-02-09 15:59:43 -08004362 * Construct a RemoteViews for the final heads-up notification layout.
4363 */
4364 public RemoteViews createHeadsUpContentView() {
4365 return createHeadsUpContentView(false /* useIncreasedHeight */);
4366 }
4367
4368 /**
Selim Cinek624c02db2015-12-14 21:00:02 -08004369 * Construct a RemoteViews for the display in public contexts like on the lockscreen.
4370 *
4371 * @hide
4372 */
4373 public RemoteViews makePublicContentView() {
Adrian Roos1a1ecfc2017-04-17 11:17:59 -07004374 return makePublicView(false /* ambient */);
4375 }
4376
4377 /**
4378 * Construct a RemoteViews for the display in public contexts like on the lockscreen.
4379 *
4380 * @hide
4381 */
4382 public RemoteViews makePublicAmbientNotification() {
4383 return makePublicView(true /* ambient */);
4384 }
4385
4386 private RemoteViews makePublicView(boolean ambient) {
Selim Cinek624c02db2015-12-14 21:00:02 -08004387 if (mN.publicVersion != null) {
4388 final Builder builder = recoverBuilder(mContext, mN.publicVersion);
Adrian Roos1a1ecfc2017-04-17 11:17:59 -07004389 return ambient ? builder.makeAmbientNotification() : builder.createContentView();
Selim Cinek624c02db2015-12-14 21:00:02 -08004390 }
4391 Bundle savedBundle = mN.extras;
4392 Style style = mStyle;
4393 mStyle = null;
4394 Icon largeIcon = mN.mLargeIcon;
4395 mN.mLargeIcon = null;
Selim Cinek279fa862016-06-14 10:57:25 -07004396 Bitmap largeIconLegacy = mN.largeIcon;
4397 mN.largeIcon = null;
Adrian Roosb19b06b2017-05-02 18:54:40 -07004398 ArrayList<Action> actions = mActions;
4399 mActions = new ArrayList<>();
Selim Cinek624c02db2015-12-14 21:00:02 -08004400 Bundle publicExtras = new Bundle();
4401 publicExtras.putBoolean(EXTRA_SHOW_WHEN,
4402 savedBundle.getBoolean(EXTRA_SHOW_WHEN));
4403 publicExtras.putBoolean(EXTRA_SHOW_CHRONOMETER,
4404 savedBundle.getBoolean(EXTRA_SHOW_CHRONOMETER));
Adrian Roos96b7e202016-05-17 13:50:38 -07004405 publicExtras.putBoolean(EXTRA_CHRONOMETER_COUNT_DOWN,
4406 savedBundle.getBoolean(EXTRA_CHRONOMETER_COUNT_DOWN));
Selim Cinek624c02db2015-12-14 21:00:02 -08004407 publicExtras.putCharSequence(EXTRA_TITLE,
Adrian Roos1a1ecfc2017-04-17 11:17:59 -07004408 mContext.getString(com.android.internal.R.string.notification_hidden_text));
Selim Cinek624c02db2015-12-14 21:00:02 -08004409 mN.extras = publicExtras;
Adrian Roos1a1ecfc2017-04-17 11:17:59 -07004410 final RemoteViews view = ambient ? makeAmbientNotification()
4411 : applyStandardTemplate(getBaseLayoutResource());
Selim Cinek624c02db2015-12-14 21:00:02 -08004412 mN.extras = savedBundle;
4413 mN.mLargeIcon = largeIcon;
Selim Cinek279fa862016-06-14 10:57:25 -07004414 mN.largeIcon = largeIconLegacy;
Adrian Roosb19b06b2017-05-02 18:54:40 -07004415 mActions = actions;
Selim Cinek624c02db2015-12-14 21:00:02 -08004416 mStyle = style;
Adrian Roos1a1ecfc2017-04-17 11:17:59 -07004417 return view;
Selim Cinek624c02db2015-12-14 21:00:02 -08004418 }
4419
Selim Cinek6743c0b2017-01-18 18:24:01 -08004420 /**
4421 * Construct a content view for the display when low - priority
4422 *
4423 * @param useRegularSubtext uses the normal subtext set if there is one available. Otherwise
4424 * a new subtext is created consisting of the content of the
4425 * notification.
4426 * @hide
4427 */
4428 public RemoteViews makeLowPriorityContentView(boolean useRegularSubtext) {
4429 int color = mN.color;
4430 mN.color = COLOR_DEFAULT;
4431 CharSequence summary = mN.extras.getCharSequence(EXTRA_SUB_TEXT);
4432 if (!useRegularSubtext || TextUtils.isEmpty(summary)) {
4433 CharSequence newSummary = createSummaryText();
4434 if (!TextUtils.isEmpty(newSummary)) {
4435 mN.extras.putCharSequence(EXTRA_SUB_TEXT, newSummary);
4436 }
4437 }
Selim Cinek875ba9b2017-02-13 16:20:17 -08004438
Adrian Roos6f6e1592017-05-02 16:22:53 -07004439 RemoteViews header = makeNotificationHeader(false /* ambient */);
Selim Cinek1b554392017-02-28 17:22:49 -08004440 header.setBoolean(R.id.notification_header, "setAcceptAllTouches", true);
Selim Cinek6743c0b2017-01-18 18:24:01 -08004441 if (summary != null) {
4442 mN.extras.putCharSequence(EXTRA_SUB_TEXT, summary);
4443 } else {
4444 mN.extras.remove(EXTRA_SUB_TEXT);
4445 }
4446 mN.color = color;
4447 return header;
4448 }
Selim Cinek624c02db2015-12-14 21:00:02 -08004449
Selim Cinek6743c0b2017-01-18 18:24:01 -08004450 private CharSequence createSummaryText() {
4451 CharSequence titleText = mN.extras.getCharSequence(Notification.EXTRA_TITLE);
4452 if (USE_ONLY_TITLE_IN_LOW_PRIORITY_SUMMARY) {
4453 return titleText;
4454 }
4455 SpannableStringBuilder summary = new SpannableStringBuilder();
4456 if (titleText == null) {
4457 titleText = mN.extras.getCharSequence(Notification.EXTRA_TITLE_BIG);
4458 }
4459 BidiFormatter bidi = BidiFormatter.getInstance();
4460 if (titleText != null) {
4461 summary.append(bidi.unicodeWrap(titleText));
4462 }
4463 CharSequence contentText = mN.extras.getCharSequence(Notification.EXTRA_TEXT);
4464 if (titleText != null && contentText != null) {
4465 summary.append(bidi.unicodeWrap(mContext.getText(
4466 R.string.notification_header_divider_symbol_with_spaces)));
4467 }
4468 if (contentText != null) {
4469 summary.append(bidi.unicodeWrap(contentText));
4470 }
4471 return summary;
4472 }
Chris Wren8fd39ec2014-02-27 17:43:26 -05004473
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004474 private RemoteViews generateActionButton(Action action, boolean emphazisedMode,
Adrian Roos487374f2017-01-11 15:48:14 -08004475 boolean oddAction, boolean ambient) {
Daniel Sandler8680bf82012-05-15 16:52:52 -04004476 final boolean tombstone = (action.actionIntent == null);
Selim Cinekf33b1112015-07-15 17:45:11 -07004477 RemoteViews button = new BuilderRemoteViews(mContext.getApplicationInfo(),
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004478 emphazisedMode ? getEmphasizedActionLayoutResource()
4479 : tombstone ? getActionTombstoneLayoutResource()
4480 : getActionLayoutResource());
Daniel Sandler8680bf82012-05-15 16:52:52 -04004481 if (!tombstone) {
Daniel Sandlere5518842012-05-10 16:20:40 -04004482 button.setOnClickPendingIntent(R.id.action0, action.actionIntent);
Daniel Sandlere5518842012-05-10 16:20:40 -04004483 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004484 button.setContentDescription(R.id.action0, action.title);
Adrian Roosfe84e1f2015-11-04 15:55:39 -08004485 if (action.mRemoteInputs != null) {
4486 button.setRemoteInputs(R.id.action0, action.mRemoteInputs);
4487 }
Selim Cinek7b9605b2017-01-19 17:36:00 -08004488 // TODO: handle emphasized mode / actions right
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004489 if (emphazisedMode) {
Selim Cinek981962e2016-07-20 20:41:58 -07004490 // change the background bgColor
Selim Cinek622c64a2017-04-17 17:10:05 -07004491 int bgColor;
4492 if (isColorized()) {
4493 bgColor = oddAction ? getActionBarColor() : getActionBarColorDeEmphasized();
4494 } else {
4495 bgColor = mContext.getColor(oddAction ? R.color.notification_action_list
4496 : R.color.notification_action_list_dark);
4497 }
Selim Cinek981962e2016-07-20 20:41:58 -07004498 button.setDrawableParameters(R.id.button_holder, true, -1, bgColor,
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004499 PorterDuff.Mode.SRC_ATOP, -1);
Selim Cinek981962e2016-07-20 20:41:58 -07004500 CharSequence title = action.title;
4501 ColorStateList[] outResultColor = null;
4502 if (isLegacy()) {
4503 title = clearColorSpans(title);
4504 } else {
4505 outResultColor = new ColorStateList[1];
4506 title = ensureColorSpanContrast(title, bgColor, outResultColor);
4507 }
4508 button.setTextViewText(R.id.action0, title);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004509 setTextViewColorPrimary(button, R.id.action0);
Selim Cinek981962e2016-07-20 20:41:58 -07004510 if (outResultColor != null && outResultColor[0] != null) {
4511 // We need to set the text color as well since changing a text to uppercase
4512 // clears its spans.
4513 button.setTextColor(R.id.action0, outResultColor[0]);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004514 } else if (mN.color != COLOR_DEFAULT && !isColorized()) {
Selim Cinek981962e2016-07-20 20:41:58 -07004515 button.setTextColor(R.id.action0,resolveContrastColor());
4516 }
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004517 } else {
Selim Cinek981962e2016-07-20 20:41:58 -07004518 button.setTextViewText(R.id.action0, processLegacyText(action.title));
Adrian Roos72171622017-01-27 10:32:06 -08004519 if (isColorized() && !ambient) {
Selim Cinek7b9605b2017-01-19 17:36:00 -08004520 setTextViewColorPrimary(button, R.id.action0);
4521 } else if (mN.color != COLOR_DEFAULT) {
Adrian Roos487374f2017-01-11 15:48:14 -08004522 button.setTextColor(R.id.action0,
4523 ambient ? resolveAmbientColor() : resolveContrastColor());
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004524 }
Adrian Roosfe84e1f2015-11-04 15:55:39 -08004525 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004526 return button;
4527 }
4528
Joe Onoratocb109a02011-01-18 17:57:41 -08004529 /**
Selim Cinek981962e2016-07-20 20:41:58 -07004530 * Clears all color spans of a text
4531 * @param charSequence the input text
4532 * @return the same text but without color spans
4533 */
4534 private CharSequence clearColorSpans(CharSequence charSequence) {
4535 if (charSequence instanceof Spanned) {
4536 Spanned ss = (Spanned) charSequence;
4537 Object[] spans = ss.getSpans(0, ss.length(), Object.class);
4538 SpannableStringBuilder builder = new SpannableStringBuilder(ss.toString());
4539 for (Object span : spans) {
4540 Object resultSpan = span;
4541 if (resultSpan instanceof CharacterStyle) {
4542 resultSpan = ((CharacterStyle) span).getUnderlying();
4543 }
4544 if (resultSpan instanceof TextAppearanceSpan) {
4545 TextAppearanceSpan originalSpan = (TextAppearanceSpan) resultSpan;
4546 if (originalSpan.getTextColor() != null) {
4547 resultSpan = new TextAppearanceSpan(
4548 originalSpan.getFamily(),
4549 originalSpan.getTextStyle(),
4550 originalSpan.getTextSize(),
4551 null,
4552 originalSpan.getLinkTextColor());
4553 }
4554 } else if (resultSpan instanceof ForegroundColorSpan
4555 || (resultSpan instanceof BackgroundColorSpan)) {
4556 continue;
4557 } else {
4558 resultSpan = span;
4559 }
4560 builder.setSpan(resultSpan, ss.getSpanStart(span), ss.getSpanEnd(span),
4561 ss.getSpanFlags(span));
4562 }
4563 return builder;
4564 }
4565 return charSequence;
4566 }
4567
4568 /**
4569 * Ensures contrast on color spans against a background color. also returns the color of the
4570 * text if a span was found that spans over the whole text.
4571 *
4572 * @param charSequence the charSequence on which the spans are
4573 * @param background the background color to ensure the contrast against
4574 * @param outResultColor an array in which a color will be returned as the first element if
4575 * there exists a full length color span.
4576 * @return the contrasted charSequence
4577 */
4578 private CharSequence ensureColorSpanContrast(CharSequence charSequence, int background,
4579 ColorStateList[] outResultColor) {
4580 if (charSequence instanceof Spanned) {
4581 Spanned ss = (Spanned) charSequence;
4582 Object[] spans = ss.getSpans(0, ss.length(), Object.class);
4583 SpannableStringBuilder builder = new SpannableStringBuilder(ss.toString());
4584 for (Object span : spans) {
4585 Object resultSpan = span;
4586 int spanStart = ss.getSpanStart(span);
4587 int spanEnd = ss.getSpanEnd(span);
4588 boolean fullLength = (spanEnd - spanStart) == charSequence.length();
4589 if (resultSpan instanceof CharacterStyle) {
4590 resultSpan = ((CharacterStyle) span).getUnderlying();
4591 }
4592 if (resultSpan instanceof TextAppearanceSpan) {
4593 TextAppearanceSpan originalSpan = (TextAppearanceSpan) resultSpan;
4594 ColorStateList textColor = originalSpan.getTextColor();
4595 if (textColor != null) {
4596 int[] colors = textColor.getColors();
4597 int[] newColors = new int[colors.length];
4598 for (int i = 0; i < newColors.length; i++) {
4599 newColors[i] = NotificationColorUtil.ensureLargeTextContrast(
4600 colors[i], background);
4601 }
4602 textColor = new ColorStateList(textColor.getStates().clone(),
4603 newColors);
4604 resultSpan = new TextAppearanceSpan(
4605 originalSpan.getFamily(),
4606 originalSpan.getTextStyle(),
4607 originalSpan.getTextSize(),
4608 textColor,
4609 originalSpan.getLinkTextColor());
4610 if (fullLength) {
4611 outResultColor[0] = new ColorStateList(
4612 textColor.getStates().clone(), newColors);
4613 }
4614 }
4615 } else if (resultSpan instanceof ForegroundColorSpan) {
4616 ForegroundColorSpan originalSpan = (ForegroundColorSpan) resultSpan;
4617 int foregroundColor = originalSpan.getForegroundColor();
4618 foregroundColor = NotificationColorUtil.ensureLargeTextContrast(
4619 foregroundColor, background);
4620 resultSpan = new ForegroundColorSpan(foregroundColor);
4621 if (fullLength) {
4622 outResultColor[0] = ColorStateList.valueOf(foregroundColor);
4623 }
4624 } else {
4625 resultSpan = span;
4626 }
4627
4628 builder.setSpan(resultSpan, spanStart, spanEnd, ss.getSpanFlags(span));
4629 }
4630 return builder;
4631 }
4632 return charSequence;
4633 }
4634
4635 /**
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004636 * @return Whether we are currently building a notification from a legacy (an app that
Alan Viverette3cb07a462014-06-06 14:19:53 -07004637 * doesn't create material notifications by itself) app.
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004638 */
4639 private boolean isLegacy() {
Selim Cinek7b9605b2017-01-19 17:36:00 -08004640 if (!mIsLegacyInitialized) {
4641 mIsLegacy = mContext.getApplicationInfo().targetSdkVersion
4642 < Build.VERSION_CODES.LOLLIPOP;
4643 mIsLegacyInitialized = true;
4644 }
4645 return mIsLegacy;
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004646 }
4647
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004648 private CharSequence processLegacyText(CharSequence charSequence) {
Adrian Roos0bc3f6a2017-03-06 11:54:05 -08004649 return processLegacyText(charSequence, false /* ambient */);
4650 }
4651
4652 private CharSequence processLegacyText(CharSequence charSequence, boolean ambient) {
4653 boolean isAlreadyLightText = isLegacy() || textColorsNeedInversion();
4654 boolean wantLightText = ambient;
4655 if (isAlreadyLightText != wantLightText) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004656 return getColorUtil().invertCharSequenceColors(charSequence);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004657 } else {
4658 return charSequence;
4659 }
4660 }
4661
Dan Sandler26e81cf2014-05-06 10:01:27 -04004662 /**
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004663 * Apply any necessariy colors to the small icon
Dan Sandler26e81cf2014-05-06 10:01:27 -04004664 */
Adrian Roos487374f2017-01-11 15:48:14 -08004665 private void processSmallIconColor(Icon smallIcon, RemoteViews contentView,
4666 boolean ambient) {
Selim Cinekea4bef72015-12-02 15:51:10 -08004667 boolean colorable = !isLegacy() || getColorUtil().isGrayscaleIcon(mContext, smallIcon);
Selim Cinek99104832017-01-25 14:47:33 -08004668 int color = ambient ? resolveAmbientColor() : getPrimaryHighlightColor();
Selim Cinekea4bef72015-12-02 15:51:10 -08004669 if (colorable) {
Adrian Roos487374f2017-01-11 15:48:14 -08004670 contentView.setDrawableParameters(R.id.icon, false, -1, color,
Jorim Jaggi92df1f22014-12-16 19:44:41 +01004671 PorterDuff.Mode.SRC_ATOP, -1);
Selim Cinekea4bef72015-12-02 15:51:10 -08004672
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004673 }
Selim Cinekea4bef72015-12-02 15:51:10 -08004674 contentView.setInt(R.id.notification_header, "setOriginalIconColor",
Adrian Roos487374f2017-01-11 15:48:14 -08004675 colorable ? color : NotificationHeaderView.NO_COLOR);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004676 }
4677
Dan Sandler26e81cf2014-05-06 10:01:27 -04004678 /**
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004679 * Make the largeIcon dark if it's a fake smallIcon (that is,
Dan Sandler26e81cf2014-05-06 10:01:27 -04004680 * if it's grayscale).
4681 */
4682 // TODO: also check bounds, transparency, that sort of thing.
Dan Sandlerd63f9322015-05-06 15:18:49 -04004683 private void processLargeLegacyIcon(Icon largeIcon, RemoteViews contentView) {
4684 if (largeIcon != null && isLegacy()
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004685 && getColorUtil().isGrayscaleIcon(mContext, largeIcon)) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004686 // resolve color will fall back to the default when legacy
Adrian Roos4ff3b122016-02-01 12:26:13 -08004687 contentView.setDrawableParameters(R.id.icon, false, -1, resolveContrastColor(),
Dan Sandler190d58d2014-05-15 09:33:39 -04004688 PorterDuff.Mode.SRC_ATOP, -1);
Jorim Jaggi92df1f22014-12-16 19:44:41 +01004689 }
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004690 }
4691
Julia Reynolds10ee1fc2015-11-09 11:04:55 -05004692 private void sanitizeColor() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004693 if (mN.color != COLOR_DEFAULT) {
4694 mN.color |= 0xFF000000; // no alpha for custom colors
Jorim Jaggi74419312014-06-10 20:57:21 +02004695 }
Jorim Jaggi74419312014-06-10 20:57:21 +02004696 }
4697
Adrian Roos4ff3b122016-02-01 12:26:13 -08004698 int resolveContrastColor() {
4699 if (mCachedContrastColorIsFor == mN.color && mCachedContrastColor != COLOR_INVALID) {
4700 return mCachedContrastColor;
Dan Sandler26e81cf2014-05-06 10:01:27 -04004701 }
Adrian Roos4ff3b122016-02-01 12:26:13 -08004702
Selim Cinekac5f0272017-05-02 16:05:41 -07004703 int color;
4704 int background = mBackgroundColorHint;
4705 if (mBackgroundColorHint == COLOR_INVALID) {
4706 background = mContext.getColor(
4707 com.android.internal.R.color.notification_material_background_color);
4708 }
4709 if (mN.color == COLOR_DEFAULT) {
4710 ensureColors();
4711 color = mSecondaryTextColor;
4712 } else {
4713 color = NotificationColorUtil.resolveContrastColor(mContext, mN.color,
4714 background);
4715 }
4716 if (Color.alpha(color) < 255) {
4717 // alpha doesn't go well for color filters, so let's blend it manually
4718 color = NotificationColorUtil.compositeColors(color, background);
4719 }
Adrian Roos4ff3b122016-02-01 12:26:13 -08004720 mCachedContrastColorIsFor = mN.color;
Selim Cinekac5f0272017-05-02 16:05:41 -07004721 return mCachedContrastColor = color;
Dan Sandler26e81cf2014-05-06 10:01:27 -04004722 }
4723
Adrian Roos487374f2017-01-11 15:48:14 -08004724 int resolveAmbientColor() {
4725 if (mCachedAmbientColorIsFor == mN.color && mCachedAmbientColorIsFor != COLOR_INVALID) {
4726 return mCachedAmbientColor;
4727 }
4728 final int contrasted = NotificationColorUtil.resolveAmbientColor(mContext, mN.color);
4729
4730 mCachedAmbientColorIsFor = mN.color;
4731 return mCachedAmbientColor = contrasted;
4732 }
4733
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004734 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004735 * Apply the unstyled operations and return a new {@link Notification} object.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04004736 * @hide
Joe Onoratocb109a02011-01-18 17:57:41 -08004737 */
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04004738 public Notification buildUnstyled() {
Daniel Sandlera0a938c2012-03-15 08:42:37 -04004739 if (mActions.size() > 0) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004740 mN.actions = new Action[mActions.size()];
4741 mActions.toArray(mN.actions);
Daniel Sandlera0a938c2012-03-15 08:42:37 -04004742 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004743 if (!mPersonList.isEmpty()) {
4744 mN.extras.putStringArray(EXTRA_PEOPLE,
4745 mPersonList.toArray(new String[mPersonList.size()]));
Dan Sandler0bf2ed82013-12-21 23:33:41 -06004746 }
Selim Cinek247fa012016-02-18 09:50:48 -08004747 if (mN.bigContentView != null || mN.contentView != null
4748 || mN.headsUpContentView != null) {
4749 mN.extras.putBoolean(EXTRA_CONTAINS_CUSTOM_VIEW, true);
4750 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004751 return mN;
Joe Onorato46439ce2010-11-19 13:56:21 -08004752 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004753
Julia Reynolds3b848122016-02-26 10:45:32 -05004754 /**
4755 * Creates a Builder from an existing notification so further changes can be made.
4756 * @param context The context for your application / activity.
4757 * @param n The notification to create a Builder from.
4758 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004759 public static Notification.Builder recoverBuilder(Context context, Notification n) {
Christoph Studer4600f9b2014-07-22 22:44:43 +02004760 // Re-create notification context so we can access app resources.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004761 ApplicationInfo applicationInfo = n.extras.getParcelable(
4762 EXTRA_BUILDER_APPLICATION_INFO);
Christoph Studer4600f9b2014-07-22 22:44:43 +02004763 Context builderContext;
Julia Reynoldsda303542015-11-23 14:00:20 -05004764 if (applicationInfo != null) {
4765 try {
4766 builderContext = context.createApplicationContext(applicationInfo,
4767 Context.CONTEXT_RESTRICTED);
4768 } catch (NameNotFoundException e) {
4769 Log.e(TAG, "ApplicationInfo " + applicationInfo + " not found");
4770 builderContext = context; // try with our context
4771 }
4772 } else {
4773 builderContext = context; // try with given context
Christoph Studer4600f9b2014-07-22 22:44:43 +02004774 }
4775
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004776 return new Builder(builderContext, n);
Christoph Studer4600f9b2014-07-22 22:44:43 +02004777 }
4778
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004779 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004780 * @deprecated Use {@link #build()} instead.
4781 */
4782 @Deprecated
4783 public Notification getNotification() {
4784 return build();
4785 }
4786
4787 /**
4788 * Combine all of the options that have been set and return a new {@link Notification}
4789 * object.
4790 */
4791 public Notification build() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004792 // first, add any extras from the calling code
4793 if (mUserExtras != null) {
4794 mN.extras = getAllExtras();
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07004795 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004796
Selim Cinekb85f36fd2016-04-20 18:46:36 -07004797 mN.creationTime = System.currentTimeMillis();
4798
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004799 // lazy stuff from mContext; see comment in Builder(Context, Notification)
Julia Reynoldsda303542015-11-23 14:00:20 -05004800 Notification.addFieldsFromContext(mContext, mN);
Christoph Studer943aa672014-08-03 20:31:16 +02004801
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004802 buildUnstyled();
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004803
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004804 if (mStyle != null) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004805 mStyle.buildStyled(mN);
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004806 }
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004807
Adrian Roos5081c0d2016-02-26 16:04:19 -08004808 if (mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N
4809 && (mStyle == null || !mStyle.displayCustomViewInline())) {
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004810 if (mN.contentView == null) {
Julia Reynolds3b848122016-02-26 10:45:32 -05004811 mN.contentView = createContentView();
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004812 mN.extras.putInt(EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT,
4813 mN.contentView.getSequenceNumber());
4814 }
4815 if (mN.bigContentView == null) {
Julia Reynolds3b848122016-02-26 10:45:32 -05004816 mN.bigContentView = createBigContentView();
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004817 if (mN.bigContentView != null) {
4818 mN.extras.putInt(EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT,
4819 mN.bigContentView.getSequenceNumber());
4820 }
4821 }
4822 if (mN.headsUpContentView == null) {
Julia Reynolds3b848122016-02-26 10:45:32 -05004823 mN.headsUpContentView = createHeadsUpContentView();
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004824 if (mN.headsUpContentView != null) {
4825 mN.extras.putInt(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT,
4826 mN.headsUpContentView.getSequenceNumber());
4827 }
4828 }
4829 }
4830
Julia Reynolds4c0c2022016-02-02 15:11:59 -05004831 if ((mN.defaults & DEFAULT_LIGHTS) != 0) {
4832 mN.flags |= FLAG_SHOW_LIGHTS;
4833 }
4834
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004835 return mN;
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004836 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05004837
4838 /**
4839 * Apply this Builder to an existing {@link Notification} object.
4840 *
4841 * @hide
4842 */
4843 public Notification buildInto(Notification n) {
Daniel Sandler1a497d32013-04-18 14:52:45 -04004844 build().cloneInto(n, true);
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05004845 return n;
4846 }
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004847
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004848 /**
Adrian Roos184bfe022016-03-03 13:41:44 -08004849 * Removes RemoteViews that were created for compatibility from {@param n}, if they did not
4850 * change.
4851 *
4852 * @return {@param n}, if no stripping is needed, otherwise a stripped clone of {@param n}.
4853 *
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004854 * @hide
4855 */
Adrian Roos184bfe022016-03-03 13:41:44 -08004856 public static Notification maybeCloneStrippedForDelivery(Notification n) {
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004857 String templateClass = n.extras.getString(EXTRA_TEMPLATE);
Adrian Roos184bfe022016-03-03 13:41:44 -08004858
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004859 // Only strip views for known Styles because we won't know how to
4860 // re-create them otherwise.
Adrian Roos184bfe022016-03-03 13:41:44 -08004861 if (!TextUtils.isEmpty(templateClass)
4862 && getNotificationStyleClass(templateClass) == null) {
4863 return n;
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004864 }
Adrian Roos184bfe022016-03-03 13:41:44 -08004865
4866 // Only strip unmodified BuilderRemoteViews.
4867 boolean stripContentView = n.contentView instanceof BuilderRemoteViews &&
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004868 n.extras.getInt(EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT, -1) ==
Adrian Roos184bfe022016-03-03 13:41:44 -08004869 n.contentView.getSequenceNumber();
4870 boolean stripBigContentView = n.bigContentView instanceof BuilderRemoteViews &&
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004871 n.extras.getInt(EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT, -1) ==
Adrian Roos184bfe022016-03-03 13:41:44 -08004872 n.bigContentView.getSequenceNumber();
4873 boolean stripHeadsUpContentView = n.headsUpContentView instanceof BuilderRemoteViews &&
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004874 n.extras.getInt(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT, -1) ==
Adrian Roos184bfe022016-03-03 13:41:44 -08004875 n.headsUpContentView.getSequenceNumber();
4876
4877 // Nothing to do here, no need to clone.
4878 if (!stripContentView && !stripBigContentView && !stripHeadsUpContentView) {
4879 return n;
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004880 }
Adrian Roos184bfe022016-03-03 13:41:44 -08004881
4882 Notification clone = n.clone();
4883 if (stripContentView) {
4884 clone.contentView = null;
4885 clone.extras.remove(EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT);
4886 }
4887 if (stripBigContentView) {
4888 clone.bigContentView = null;
4889 clone.extras.remove(EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT);
4890 }
4891 if (stripHeadsUpContentView) {
4892 clone.headsUpContentView = null;
4893 clone.extras.remove(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT);
4894 }
4895 return clone;
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004896 }
4897
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004898 private int getBaseLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07004899 return R.layout.notification_template_material_base;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004900 }
4901
4902 private int getBigBaseLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07004903 return R.layout.notification_template_material_big_base;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004904 }
4905
4906 private int getBigPictureLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07004907 return R.layout.notification_template_material_big_picture;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004908 }
4909
4910 private int getBigTextLayoutResource() {
Jorim Jaggi445d3c02014-08-19 22:33:42 +02004911 return R.layout.notification_template_material_big_text;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004912 }
4913
4914 private int getInboxLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07004915 return R.layout.notification_template_material_inbox;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004916 }
4917
Adrian Roosc1a80b02016-04-05 14:54:55 -07004918 private int getMessagingLayoutResource() {
4919 return R.layout.notification_template_material_messaging;
4920 }
4921
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004922 private int getActionLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07004923 return R.layout.notification_material_action;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004924 }
4925
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004926 private int getEmphasizedActionLayoutResource() {
4927 return R.layout.notification_material_action_emphasized;
4928 }
4929
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004930 private int getActionTombstoneLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07004931 return R.layout.notification_material_action_tombstone;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004932 }
Selim Cinek7b9605b2017-01-19 17:36:00 -08004933
4934 private int getBackgroundColor() {
4935 if (isColorized()) {
Selim Cinek5fb73f82017-04-20 16:55:38 -07004936 return mBackgroundColor != COLOR_INVALID ? mBackgroundColor : mN.color;
Selim Cinek7b9605b2017-01-19 17:36:00 -08004937 } else {
Selim Cinekac5f0272017-05-02 16:05:41 -07004938 return mBackgroundColorHint != COLOR_INVALID ? mBackgroundColorHint
4939 : COLOR_DEFAULT;
Selim Cinek7b9605b2017-01-19 17:36:00 -08004940 }
4941 }
4942
4943 private boolean isColorized() {
4944 return mN.isColorized();
4945 }
Selim Cinek99104832017-01-25 14:47:33 -08004946
4947 private boolean textColorsNeedInversion() {
4948 if (mStyle == null || !MediaStyle.class.equals(mStyle.getClass())) {
4949 return false;
4950 }
4951 int targetSdkVersion = mContext.getApplicationInfo().targetSdkVersion;
4952 return targetSdkVersion > Build.VERSION_CODES.M
4953 && targetSdkVersion < Build.VERSION_CODES.O;
4954 }
Selim Cinek5fb73f82017-04-20 16:55:38 -07004955
4956 /**
4957 * Set a color palette to be used as the background and textColors
4958 *
4959 * @param backgroundColor the color to be used as the background
4960 * @param foregroundColor the color to be used as the foreground
4961 *
4962 * @hide
4963 */
4964 public void setColorPalette(int backgroundColor, int foregroundColor) {
4965 mBackgroundColor = backgroundColor;
4966 mForegroundColor = foregroundColor;
4967 mTextColorsAreForBackground = COLOR_INVALID;
4968 ensureColors();
4969 }
Selim Cinekac5f0272017-05-02 16:05:41 -07004970
4971 /**
4972 * Sets the background color for this notification to be a different one then the default.
4973 * This is mainly used to calculate contrast and won't necessarily be applied to the
4974 * background.
4975 *
4976 * @hide
4977 */
4978 public void setBackgroundColorHint(int backgroundColor) {
4979 mBackgroundColorHint = backgroundColor;
4980 }
Selim Cinek7b9605b2017-01-19 17:36:00 -08004981 }
4982
4983 /**
Selim Cinek22714f12017-04-13 16:23:53 -07004984 * @return whether this notification is a foreground service notification
Selim Cinek7b9605b2017-01-19 17:36:00 -08004985 */
Selim Cinek22714f12017-04-13 16:23:53 -07004986 private boolean isForegroundService() {
4987 return (flags & Notification.FLAG_FOREGROUND_SERVICE) != 0;
Selim Cinek7b9605b2017-01-19 17:36:00 -08004988 }
4989
4990 /**
Selim Cinek99104832017-01-25 14:47:33 -08004991 * @return whether this notification has a media session attached
4992 * @hide
4993 */
4994 public boolean hasMediaSession() {
4995 return extras.getParcelable(Notification.EXTRA_MEDIA_SESSION) != null;
4996 }
4997
4998 /**
Selim Cinek7d1009b2017-01-25 15:28:28 -08004999 * @return the style class of this notification
5000 * @hide
5001 */
5002 public Class<? extends Notification.Style> getNotificationStyle() {
5003 String templateClass = extras.getString(Notification.EXTRA_TEMPLATE);
5004
5005 if (!TextUtils.isEmpty(templateClass)) {
5006 return Notification.getNotificationStyleClass(templateClass);
5007 }
5008 return null;
5009 }
5010
5011 /**
Selim Cinek22714f12017-04-13 16:23:53 -07005012 * @return true if this notification is colorized.
Selim Cinek7b9605b2017-01-19 17:36:00 -08005013 *
5014 * @hide
5015 */
5016 public boolean isColorized() {
Selim Cinek5fb73f82017-04-20 16:55:38 -07005017 if (isColorizedMedia()) {
5018 return true;
5019 }
5020 return extras.getBoolean(EXTRA_COLORIZED) && isForegroundService();
5021 }
5022
5023 /**
5024 * @return true if this notification is colorized and it is a media notification
5025 *
5026 * @hide
5027 */
5028 public boolean isColorizedMedia() {
Selim Cinek99104832017-01-25 14:47:33 -08005029 Class<? extends Style> style = getNotificationStyle();
5030 if (MediaStyle.class.equals(style)) {
5031 Boolean colorized = (Boolean) extras.get(EXTRA_COLORIZED);
5032 if ((colorized == null || colorized) && hasMediaSession()) {
5033 return true;
5034 }
5035 } else if (DecoratedMediaCustomViewStyle.class.equals(style)) {
5036 if (extras.getBoolean(EXTRA_COLORIZED) && hasMediaSession()) {
5037 return true;
5038 }
5039 }
Selim Cinek5fb73f82017-04-20 16:55:38 -07005040 return false;
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005041 }
5042
Selim Cinek0847acd2017-04-24 19:48:29 -07005043
5044 /**
5045 * @return true if this is a media notification
5046 *
5047 * @hide
5048 */
5049 public boolean isMediaNotification() {
5050 Class<? extends Style> style = getNotificationStyle();
5051 if (MediaStyle.class.equals(style)) {
5052 return true;
5053 } else if (DecoratedMediaCustomViewStyle.class.equals(style)) {
5054 return true;
5055 }
5056 return false;
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005057 }
5058
Selim Cinek279fa862016-06-14 10:57:25 -07005059 private boolean hasLargeIcon() {
5060 return mLargeIcon != null || largeIcon != null;
5061 }
5062
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005063 /**
Selim Cinekc2c0b042016-05-18 17:13:46 -07005064 * @return true if the notification will show the time; false otherwise
Selim Cinekb85f36fd2016-04-20 18:46:36 -07005065 * @hide
5066 */
Selim Cinekc2c0b042016-05-18 17:13:46 -07005067 public boolean showsTime() {
Selim Cinekb85f36fd2016-04-20 18:46:36 -07005068 return when != 0 && extras.getBoolean(EXTRA_SHOW_WHEN);
5069 }
5070
5071 /**
Selim Cinekc2c0b042016-05-18 17:13:46 -07005072 * @return true if the notification will show a chronometer; false otherwise
5073 * @hide
5074 */
5075 public boolean showsChronometer() {
5076 return when != 0 && extras.getBoolean(EXTRA_SHOW_CHRONOMETER);
5077 }
5078
5079 /**
Julia Reynolds4a02afb2016-12-13 13:39:52 -05005080 * @hide
5081 */
5082 @SystemApi
5083 public static Class<? extends Style> getNotificationStyleClass(String templateClass) {
5084 Class<? extends Style>[] classes = new Class[] {
5085 BigTextStyle.class, BigPictureStyle.class, InboxStyle.class, MediaStyle.class,
5086 DecoratedCustomViewStyle.class, DecoratedMediaCustomViewStyle.class,
5087 MessagingStyle.class };
5088 for (Class<? extends Style> innerClass : classes) {
5089 if (templateClass.equals(innerClass.getName())) {
5090 return innerClass;
5091 }
5092 }
5093 return null;
5094 }
5095
5096 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005097 * An object that can apply a rich notification style to a {@link Notification.Builder}
5098 * object.
5099 */
Griff Hazendfcb0802014-02-11 12:00:00 -08005100 public static abstract class Style {
Chris Wrend6297db2012-05-03 16:20:13 -04005101 private CharSequence mBigContentTitle;
Jorim Jaggi457a10d2014-09-08 16:18:23 +02005102
5103 /**
5104 * @hide
5105 */
5106 protected CharSequence mSummaryText = null;
5107
5108 /**
5109 * @hide
5110 */
5111 protected boolean mSummaryTextSet = false;
Chris Wrend6297db2012-05-03 16:20:13 -04005112
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005113 protected Builder mBuilder;
5114
Chris Wrend6297db2012-05-03 16:20:13 -04005115 /**
5116 * Overrides ContentTitle in the big form of the template.
5117 * This defaults to the value passed to setContentTitle().
5118 */
5119 protected void internalSetBigContentTitle(CharSequence title) {
5120 mBigContentTitle = title;
5121 }
5122
5123 /**
5124 * Set the first line of text after the detail section in the big form of the template.
5125 */
5126 protected void internalSetSummaryText(CharSequence cs) {
5127 mSummaryText = cs;
Daniel Sandler619738c2012-06-07 16:33:08 -04005128 mSummaryTextSet = true;
Chris Wrend6297db2012-05-03 16:20:13 -04005129 }
5130
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005131 public void setBuilder(Builder builder) {
5132 if (mBuilder != builder) {
5133 mBuilder = builder;
Daniel Sandlerc08dea22012-06-28 08:35:24 -07005134 if (mBuilder != null) {
5135 mBuilder.setStyle(this);
5136 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005137 }
5138 }
5139
Chris Wrend6297db2012-05-03 16:20:13 -04005140 protected void checkBuilder() {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005141 if (mBuilder == null) {
5142 throw new IllegalArgumentException("Style requires a valid Builder object");
5143 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005144 }
Chris Wrend6297db2012-05-03 16:20:13 -04005145
5146 protected RemoteViews getStandardView(int layoutId) {
5147 checkBuilder();
5148
Christoph Studer4600f9b2014-07-22 22:44:43 +02005149 // Nasty.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005150 CharSequence oldBuilderContentTitle =
5151 mBuilder.getAllExtras().getCharSequence(EXTRA_TITLE);
Chris Wrend6297db2012-05-03 16:20:13 -04005152 if (mBigContentTitle != null) {
5153 mBuilder.setContentTitle(mBigContentTitle);
5154 }
5155
Chris Wrend6297db2012-05-03 16:20:13 -04005156 RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(layoutId);
5157
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005158 mBuilder.getAllExtras().putCharSequence(EXTRA_TITLE, oldBuilderContentTitle);
Christoph Studer4600f9b2014-07-22 22:44:43 +02005159
Chris Wrend6297db2012-05-03 16:20:13 -04005160 if (mBigContentTitle != null && mBigContentTitle.equals("")) {
5161 contentView.setViewVisibility(R.id.line1, View.GONE);
Chris Wren67dc9a02012-05-16 01:03:20 -04005162 } else {
5163 contentView.setViewVisibility(R.id.line1, View.VISIBLE);
Chris Wrend6297db2012-05-03 16:20:13 -04005164 }
5165
Chris Wrend6297db2012-05-03 16:20:13 -04005166 return contentView;
5167 }
5168
Daniel Sandlerf45564e2013-04-15 15:05:08 -04005169 /**
Selim Cinek7d1009b2017-01-25 15:28:28 -08005170 * Construct a Style-specific RemoteViews for the collapsed notification layout.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005171 * The default implementation has nothing additional to add.
Selim Cinek7d1009b2017-01-25 15:28:28 -08005172 *
5173 * @param increasedHeight true if this layout be created with an increased height.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005174 * @hide
5175 */
Selim Cinek7d1009b2017-01-25 15:28:28 -08005176 public RemoteViews makeContentView(boolean increasedHeight) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005177 return null;
5178 }
5179
5180 /**
5181 * Construct a Style-specific RemoteViews for the final big notification layout.
5182 * @hide
5183 */
5184 public RemoteViews makeBigContentView() {
5185 return null;
5186 }
5187
5188 /**
5189 * Construct a Style-specific RemoteViews for the final HUN layout.
Selim Cinek87ed69b2017-02-09 15:59:43 -08005190 *
5191 * @param increasedHeight true if this layout be created with an increased height.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005192 * @hide
5193 */
Selim Cinek87ed69b2017-02-09 15:59:43 -08005194 public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005195 return null;
5196 }
5197
5198 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005199 * Apply any style-specific extras to this notification before shipping it out.
Daniel Sandlerf45564e2013-04-15 15:05:08 -04005200 * @hide
5201 */
5202 public void addExtras(Bundle extras) {
5203 if (mSummaryTextSet) {
5204 extras.putCharSequence(EXTRA_SUMMARY_TEXT, mSummaryText);
5205 }
5206 if (mBigContentTitle != null) {
5207 extras.putCharSequence(EXTRA_TITLE_BIG, mBigContentTitle);
5208 }
Chris Wren91ad5632013-06-05 15:05:57 -04005209 extras.putString(EXTRA_TEMPLATE, this.getClass().getName());
Daniel Sandlerf45564e2013-04-15 15:05:08 -04005210 }
5211
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04005212 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005213 * Reconstruct the internal state of this Style object from extras.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04005214 * @hide
5215 */
Christoph Studer4600f9b2014-07-22 22:44:43 +02005216 protected void restoreFromExtras(Bundle extras) {
5217 if (extras.containsKey(EXTRA_SUMMARY_TEXT)) {
5218 mSummaryText = extras.getCharSequence(EXTRA_SUMMARY_TEXT);
5219 mSummaryTextSet = true;
5220 }
5221 if (extras.containsKey(EXTRA_TITLE_BIG)) {
5222 mBigContentTitle = extras.getCharSequence(EXTRA_TITLE_BIG);
5223 }
5224 }
5225
5226
5227 /**
5228 * @hide
5229 */
5230 public Notification buildStyled(Notification wip) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005231 addExtras(wip.extras);
Christoph Studer4600f9b2014-07-22 22:44:43 +02005232 return wip;
5233 }
5234
Daniel Sandler0ec46202015-06-24 01:27:05 -04005235 /**
5236 * @hide
5237 */
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07005238 public void purgeResources() {}
5239
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04005240 /**
5241 * Calls {@link android.app.Notification.Builder#build()} on the Builder this Style is
5242 * attached to.
5243 *
5244 * @return the fully constructed Notification.
5245 */
5246 public Notification build() {
5247 checkBuilder();
5248 return mBuilder.build();
5249 }
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02005250
5251 /**
5252 * @hide
5253 * @return true if the style positions the progress bar on the second line; false if the
5254 * style hides the progress bar
5255 */
5256 protected boolean hasProgress() {
5257 return true;
5258 }
Selim Cinek03d0d652015-11-13 13:18:09 -05005259
5260 /**
5261 * @hide
5262 * @return Whether we should put the summary be put into the notification header
5263 */
5264 public boolean hasSummaryInHeader() {
5265 return true;
5266 }
Selim Cinek593610c2016-02-16 18:42:57 -08005267
5268 /**
5269 * @hide
5270 * @return Whether custom content views are displayed inline in the style
5271 */
5272 public boolean displayCustomViewInline() {
5273 return false;
5274 }
Joe Onorato46439ce2010-11-19 13:56:21 -08005275 }
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005276
5277 /**
Daniel Sandler4dfbe832012-04-11 14:51:46 -04005278 * Helper class for generating large-format notifications that include a large image attachment.
Joe Malin8d40d042012-11-05 11:36:40 -08005279 *
Robert Ly91c5ce32014-06-08 15:37:00 -07005280 * Here's how you'd set the <code>BigPictureStyle</code> on a notification:
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005281 * <pre class="prettyprint">
Robert Ly91c5ce32014-06-08 15:37:00 -07005282 * Notification notif = new Notification.Builder(mContext)
5283 * .setContentTitle(&quot;New photo from &quot; + sender.toString())
5284 * .setContentText(subject)
5285 * .setSmallIcon(R.drawable.new_post)
5286 * .setLargeIcon(aBitmap)
5287 * .setStyle(new Notification.BigPictureStyle()
5288 * .bigPicture(aBigBitmap))
5289 * .build();
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005290 * </pre>
Joe Malin8d40d042012-11-05 11:36:40 -08005291 *
Daniel Sandler4dfbe832012-04-11 14:51:46 -04005292 * @see Notification#bigContentView
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005293 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005294 public static class BigPictureStyle extends Style {
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005295 private Bitmap mPicture;
Dan Sandlerd63f9322015-05-06 15:18:49 -04005296 private Icon mBigLargeIcon;
Chris Wren3745a3d2012-05-22 15:11:52 -04005297 private boolean mBigLargeIconSet = false;
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005298
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005299 public BigPictureStyle() {
5300 }
5301
Adrian Roosf5faf9d2016-05-23 13:56:15 -07005302 /**
5303 * @deprecated use {@code BigPictureStyle()}.
5304 */
5305 @Deprecated
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005306 public BigPictureStyle(Builder builder) {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005307 setBuilder(builder);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005308 }
5309
Chris Wrend6297db2012-05-03 16:20:13 -04005310 /**
5311 * Overrides ContentTitle in the big form of the template.
5312 * This defaults to the value passed to setContentTitle().
5313 */
5314 public BigPictureStyle setBigContentTitle(CharSequence title) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04005315 internalSetBigContentTitle(safeCharSequence(title));
Chris Wrend6297db2012-05-03 16:20:13 -04005316 return this;
5317 }
5318
5319 /**
5320 * Set the first line of text after the detail section in the big form of the template.
5321 */
5322 public BigPictureStyle setSummaryText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04005323 internalSetSummaryText(safeCharSequence(cs));
Chris Wrend6297db2012-05-03 16:20:13 -04005324 return this;
5325 }
5326
Chris Wren0bd664d2012-08-01 13:56:56 -04005327 /**
5328 * Provide the bitmap to be used as the payload for the BigPicture notification.
5329 */
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005330 public BigPictureStyle bigPicture(Bitmap b) {
5331 mPicture = b;
5332 return this;
5333 }
5334
Chris Wren3745a3d2012-05-22 15:11:52 -04005335 /**
Chris Wren3745a3d2012-05-22 15:11:52 -04005336 * Override the large icon when the big notification is shown.
5337 */
5338 public BigPictureStyle bigLargeIcon(Bitmap b) {
Dan Sandlerd63f9322015-05-06 15:18:49 -04005339 return bigLargeIcon(b != null ? Icon.createWithBitmap(b) : null);
5340 }
5341
5342 /**
5343 * Override the large icon when the big notification is shown.
5344 */
5345 public BigPictureStyle bigLargeIcon(Icon icon) {
Chris Wren3745a3d2012-05-22 15:11:52 -04005346 mBigLargeIconSet = true;
Dan Sandlerd63f9322015-05-06 15:18:49 -04005347 mBigLargeIcon = icon;
Chris Wren3745a3d2012-05-22 15:11:52 -04005348 return this;
5349 }
5350
Riley Andrews0394a0c2015-11-03 23:36:52 -08005351 /** @hide */
5352 public static final int MIN_ASHMEM_BITMAP_SIZE = 128 * (1 << 10);
5353
Daniel Sandler0ec46202015-06-24 01:27:05 -04005354 /**
5355 * @hide
5356 */
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07005357 @Override
5358 public void purgeResources() {
5359 super.purgeResources();
Riley Andrews8cee7c12015-11-01 23:36:04 -08005360 if (mPicture != null &&
5361 mPicture.isMutable() &&
Riley Andrews0394a0c2015-11-03 23:36:52 -08005362 mPicture.getAllocationByteCount() >= MIN_ASHMEM_BITMAP_SIZE) {
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07005363 mPicture = mPicture.createAshmemBitmap();
5364 }
5365 if (mBigLargeIcon != null) {
5366 mBigLargeIcon.convertToAshmem();
5367 }
5368 }
Christoph Studer5c510ee2014-12-15 16:32:27 +01005369
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005370 /**
5371 * @hide
5372 */
5373 public RemoteViews makeBigContentView() {
5374 // Replace mN.mLargeIcon with mBigLargeIcon if mBigLargeIconSet
Christoph Studer5c510ee2014-12-15 16:32:27 +01005375 // This covers the following cases:
5376 // 1. mBigLargeIconSet -> mBigLargeIcon (null or non-null) applies, overrides
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005377 // mN.mLargeIcon
5378 // 2. !mBigLargeIconSet -> mN.mLargeIcon applies
Dan Sandlerd63f9322015-05-06 15:18:49 -04005379 Icon oldLargeIcon = null;
Selim Cineke99acb22016-08-04 12:55:48 -07005380 Bitmap largeIconLegacy = null;
Christoph Studer5c510ee2014-12-15 16:32:27 +01005381 if (mBigLargeIconSet) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005382 oldLargeIcon = mBuilder.mN.mLargeIcon;
5383 mBuilder.mN.mLargeIcon = mBigLargeIcon;
Selim Cineke99acb22016-08-04 12:55:48 -07005384 // The legacy largeIcon might not allow us to clear the image, as it's taken in
5385 // replacement if the other one is null. Because we're restoring these legacy icons
5386 // for old listeners, this is in general non-null.
5387 largeIconLegacy = mBuilder.mN.largeIcon;
5388 mBuilder.mN.largeIcon = null;
Christoph Studer5c510ee2014-12-15 16:32:27 +01005389 }
5390
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01005391 RemoteViews contentView = getStandardView(mBuilder.getBigPictureLayoutResource());
Selim Cinek03d0d652015-11-13 13:18:09 -05005392 if (mSummaryTextSet) {
5393 contentView.setTextViewText(R.id.text, mBuilder.processLegacyText(mSummaryText));
Selim Cinek7b9605b2017-01-19 17:36:00 -08005394 mBuilder.setTextViewColorSecondary(contentView, R.id.text);
Selim Cinekc848c3a2016-01-13 15:27:30 -08005395 contentView.setViewVisibility(R.id.text, View.VISIBLE);
Selim Cinek03d0d652015-11-13 13:18:09 -05005396 }
Selim Cinek279fa862016-06-14 10:57:25 -07005397 mBuilder.setContentMinHeight(contentView, mBuilder.mN.hasLargeIcon());
Selim Cinek53e64a42015-11-16 10:40:56 -08005398
Christoph Studer5c510ee2014-12-15 16:32:27 +01005399 if (mBigLargeIconSet) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005400 mBuilder.mN.mLargeIcon = oldLargeIcon;
Selim Cineke99acb22016-08-04 12:55:48 -07005401 mBuilder.mN.largeIcon = largeIconLegacy;
Christoph Studer5c510ee2014-12-15 16:32:27 +01005402 }
5403
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005404 contentView.setImageViewBitmap(R.id.big_picture, mPicture);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005405 return contentView;
5406 }
5407
Daniel Sandlerf45564e2013-04-15 15:05:08 -04005408 /**
5409 * @hide
5410 */
5411 public void addExtras(Bundle extras) {
5412 super.addExtras(extras);
5413
5414 if (mBigLargeIconSet) {
5415 extras.putParcelable(EXTRA_LARGE_ICON_BIG, mBigLargeIcon);
5416 }
5417 extras.putParcelable(EXTRA_PICTURE, mPicture);
5418 }
5419
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04005420 /**
5421 * @hide
5422 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005423 @Override
Christoph Studer4600f9b2014-07-22 22:44:43 +02005424 protected void restoreFromExtras(Bundle extras) {
5425 super.restoreFromExtras(extras);
5426
5427 if (extras.containsKey(EXTRA_LARGE_ICON_BIG)) {
Christoph Studer5c510ee2014-12-15 16:32:27 +01005428 mBigLargeIconSet = true;
Christoph Studer4600f9b2014-07-22 22:44:43 +02005429 mBigLargeIcon = extras.getParcelable(EXTRA_LARGE_ICON_BIG);
Chris Wren3745a3d2012-05-22 15:11:52 -04005430 }
Christoph Studer4600f9b2014-07-22 22:44:43 +02005431 mPicture = extras.getParcelable(EXTRA_PICTURE);
5432 }
Selim Cinek03d0d652015-11-13 13:18:09 -05005433
5434 /**
5435 * @hide
5436 */
5437 @Override
5438 public boolean hasSummaryInHeader() {
5439 return false;
5440 }
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005441 }
5442
5443 /**
Daniel Sandler4dfbe832012-04-11 14:51:46 -04005444 * Helper class for generating large-format notifications that include a lot of text.
Joe Malin8d40d042012-11-05 11:36:40 -08005445 *
Robert Ly91c5ce32014-06-08 15:37:00 -07005446 * Here's how you'd set the <code>BigTextStyle</code> on a notification:
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005447 * <pre class="prettyprint">
Robert Ly91c5ce32014-06-08 15:37:00 -07005448 * Notification notif = new Notification.Builder(mContext)
5449 * .setContentTitle(&quot;New mail from &quot; + sender.toString())
5450 * .setContentText(subject)
5451 * .setSmallIcon(R.drawable.new_mail)
5452 * .setLargeIcon(aBitmap)
5453 * .setStyle(new Notification.BigTextStyle()
5454 * .bigText(aVeryLongString))
5455 * .build();
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005456 * </pre>
Joe Malin8d40d042012-11-05 11:36:40 -08005457 *
Daniel Sandler4dfbe832012-04-11 14:51:46 -04005458 * @see Notification#bigContentView
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005459 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005460 public static class BigTextStyle extends Style {
Jorim Jaggi457a10d2014-09-08 16:18:23 +02005461
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005462 private CharSequence mBigText;
5463
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005464 public BigTextStyle() {
5465 }
5466
Adrian Roosf5faf9d2016-05-23 13:56:15 -07005467 /**
5468 * @deprecated use {@code BigTextStyle()}.
5469 */
5470 @Deprecated
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005471 public BigTextStyle(Builder builder) {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005472 setBuilder(builder);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005473 }
5474
Chris Wrend6297db2012-05-03 16:20:13 -04005475 /**
5476 * Overrides ContentTitle in the big form of the template.
5477 * This defaults to the value passed to setContentTitle().
5478 */
5479 public BigTextStyle setBigContentTitle(CharSequence title) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04005480 internalSetBigContentTitle(safeCharSequence(title));
Chris Wrend6297db2012-05-03 16:20:13 -04005481 return this;
5482 }
5483
5484 /**
5485 * Set the first line of text after the detail section in the big form of the template.
5486 */
5487 public BigTextStyle setSummaryText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04005488 internalSetSummaryText(safeCharSequence(cs));
Chris Wrend6297db2012-05-03 16:20:13 -04005489 return this;
5490 }
5491
Chris Wren0bd664d2012-08-01 13:56:56 -04005492 /**
5493 * Provide the longer text to be displayed in the big form of the
5494 * template in place of the content text.
5495 */
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005496 public BigTextStyle bigText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04005497 mBigText = safeCharSequence(cs);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005498 return this;
5499 }
5500
Daniel Sandlerf45564e2013-04-15 15:05:08 -04005501 /**
5502 * @hide
5503 */
5504 public void addExtras(Bundle extras) {
5505 super.addExtras(extras);
5506
Christoph Studer4600f9b2014-07-22 22:44:43 +02005507 extras.putCharSequence(EXTRA_BIG_TEXT, mBigText);
5508 }
5509
5510 /**
5511 * @hide
5512 */
5513 @Override
5514 protected void restoreFromExtras(Bundle extras) {
5515 super.restoreFromExtras(extras);
5516
5517 mBigText = extras.getCharSequence(EXTRA_BIG_TEXT);
Daniel Sandlerf45564e2013-04-15 15:05:08 -04005518 }
5519
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005520 /**
Selim Cinek7d1009b2017-01-25 15:28:28 -08005521 * @param increasedHeight true if this layout be created with an increased height.
5522 *
5523 * @hide
5524 */
5525 @Override
5526 public RemoteViews makeContentView(boolean increasedHeight) {
5527 if (increasedHeight) {
5528 ArrayList<Action> actions = mBuilder.mActions;
5529 mBuilder.mActions = new ArrayList<>();
5530 RemoteViews remoteViews = makeBigContentView();
5531 mBuilder.mActions = actions;
5532 return remoteViews;
5533 }
5534 return super.makeContentView(increasedHeight);
5535 }
5536
5537 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005538 * @hide
5539 */
Selim Cinek87ed69b2017-02-09 15:59:43 -08005540 @Override
5541 public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
5542 if (increasedHeight && mBuilder.mActions.size() > 0) {
5543 return makeBigContentView();
5544 }
5545 return super.makeHeadsUpContentView(increasedHeight);
5546 }
5547
5548 /**
5549 * @hide
5550 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005551 public RemoteViews makeBigContentView() {
Christoph Studer4600f9b2014-07-22 22:44:43 +02005552
5553 // Nasty
Selim Cinek75998782016-04-26 10:39:17 -07005554 CharSequence text = mBuilder.getAllExtras().getCharSequence(EXTRA_TEXT);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005555 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, null);
Daniel Sandler916ad912012-06-13 12:17:07 -04005556
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01005557 RemoteViews contentView = getStandardView(mBuilder.getBigTextLayoutResource());
Joe Malin8d40d042012-11-05 11:36:40 -08005558
Selim Cinek75998782016-04-26 10:39:17 -07005559 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, text);
Christoph Studer4600f9b2014-07-22 22:44:43 +02005560
Selim Cinek3a2c4b92015-12-17 17:01:17 -08005561 CharSequence bigTextText = mBuilder.processLegacyText(mBigText);
Selim Cinek75998782016-04-26 10:39:17 -07005562 if (TextUtils.isEmpty(bigTextText)) {
5563 // In case the bigtext is null / empty fall back to the normal text to avoid a weird
5564 // experience
5565 bigTextText = mBuilder.processLegacyText(text);
5566 }
Adrian Roosb1f427c2016-05-26 12:27:15 -07005567 applyBigTextContentView(mBuilder, contentView, bigTextText);
Selim Cinek4fb12d32015-11-19 18:10:48 -08005568
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005569 return contentView;
5570 }
5571
Adrian Roosb1f427c2016-05-26 12:27:15 -07005572 static void applyBigTextContentView(Builder builder,
5573 RemoteViews contentView, CharSequence bigTextText) {
5574 contentView.setTextViewText(R.id.big_text, bigTextText);
Selim Cinek7b9605b2017-01-19 17:36:00 -08005575 builder.setTextViewColorSecondary(contentView, R.id.big_text);
Adrian Roosb1f427c2016-05-26 12:27:15 -07005576 contentView.setViewVisibility(R.id.big_text,
5577 TextUtils.isEmpty(bigTextText) ? View.GONE : View.VISIBLE);
Selim Cinek279fa862016-06-14 10:57:25 -07005578 contentView.setBoolean(R.id.big_text, "setHasImage", builder.mN.hasLargeIcon());
Adrian Roosb1f427c2016-05-26 12:27:15 -07005579 }
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005580 }
Daniel Sandler879c5e02012-04-17 16:46:51 -04005581
5582 /**
Alex Hillsfc737de2016-03-23 17:33:02 -04005583 * Helper class for generating large-format notifications that include multiple back-and-forth
5584 * messages of varying types between any number of people.
5585 *
5586 * <br>
5587 * If the platform does not provide large-format notifications, this method has no effect. The
5588 * user will always see the normal notification view.
5589 * <br>
5590 * This class is a "rebuilder": It attaches to a Builder object and modifies its behavior, like
5591 * so:
5592 * <pre class="prettyprint">
5593 *
5594 * Notification noti = new Notification.Builder()
5595 * .setContentTitle(&quot;2 new messages wtih &quot; + sender.toString())
5596 * .setContentText(subject)
5597 * .setSmallIcon(R.drawable.new_message)
5598 * .setLargeIcon(aBitmap)
5599 * .setStyle(new Notification.MessagingStyle(resources.getString(R.string.reply_name))
5600 * .addMessage(messages[0].getText(), messages[0].getTime(), messages[0].getSender())
5601 * .addMessage(messages[1].getText(), messages[1].getTime(), messages[1].getSender()))
5602 * .build();
5603 * </pre>
5604 */
5605 public static class MessagingStyle extends Style {
5606
5607 /**
5608 * The maximum number of messages that will be retained in the Notification itself (the
5609 * number displayed is up to the platform).
5610 */
5611 public static final int MAXIMUM_RETAINED_MESSAGES = 25;
5612
5613 CharSequence mUserDisplayName;
5614 CharSequence mConversationTitle;
Alex Hillsd9b04d92016-04-11 16:38:16 -04005615 List<Message> mMessages = new ArrayList<>();
Adrian Roos437cd562017-01-18 15:47:03 -08005616 List<Message> mHistoricMessages = new ArrayList<>();
Alex Hillsfc737de2016-03-23 17:33:02 -04005617
5618 MessagingStyle() {
5619 }
5620
5621 /**
Alex Hillsfd590442016-10-07 09:52:44 -04005622 * @param userDisplayName Required - the name to be displayed for any replies sent by the
5623 * user before the posting app reposts the notification with those messages after they've
5624 * been actually sent and in previous messages sent by the user added in
Alex Hillsfc737de2016-03-23 17:33:02 -04005625 * {@link #addMessage(Notification.MessagingStyle.Message)}
5626 */
Alex Hillsfd590442016-10-07 09:52:44 -04005627 public MessagingStyle(@NonNull CharSequence userDisplayName) {
Alex Hillsfc737de2016-03-23 17:33:02 -04005628 mUserDisplayName = userDisplayName;
5629 }
5630
5631 /**
5632 * Returns the name to be displayed for any replies sent by the user
5633 */
5634 public CharSequence getUserDisplayName() {
5635 return mUserDisplayName;
5636 }
5637
5638 /**
Alex Hillsfc737de2016-03-23 17:33:02 -04005639 * Sets the title to be displayed on this conversation. This should only be used for
5640 * group messaging and left unset for one-on-one conversations.
5641 * @param conversationTitle
5642 * @return this object for method chaining.
5643 */
5644 public MessagingStyle setConversationTitle(CharSequence conversationTitle) {
5645 mConversationTitle = conversationTitle;
5646 return this;
5647 }
5648
5649 /**
5650 * Return the title to be displayed on this conversation. Can be <code>null</code> and
5651 * should be for one-on-one conversations
5652 */
5653 public CharSequence getConversationTitle() {
5654 return mConversationTitle;
5655 }
5656
5657 /**
5658 * Adds a message for display by this notification. Convenience call for a simple
5659 * {@link Message} in {@link #addMessage(Notification.MessagingStyle.Message)}.
5660 * @param text A {@link CharSequence} to be displayed as the message content
5661 * @param timestamp Time at which the message arrived
5662 * @param sender A {@link CharSequence} to be used for displaying the name of the
5663 * sender. Should be <code>null</code> for messages by the current user, in which case
5664 * the platform will insert {@link #getUserDisplayName()}.
5665 * Should be unique amongst all individuals in the conversation, and should be
5666 * consistent during re-posts of the notification.
5667 *
5668 * @see Message#Message(CharSequence, long, CharSequence)
5669 *
5670 * @return this object for method chaining
5671 */
5672 public MessagingStyle addMessage(CharSequence text, long timestamp, CharSequence sender) {
Adrian Roos437cd562017-01-18 15:47:03 -08005673 return addMessage(new Message(text, timestamp, sender));
Alex Hillsfc737de2016-03-23 17:33:02 -04005674 }
5675
5676 /**
5677 * Adds a {@link Message} for display in this notification.
Adrian Roos437cd562017-01-18 15:47:03 -08005678 *
5679 * <p>The messages should be added in chronologic order, i.e. the oldest first,
5680 * the newest last.
5681 *
Alex Hillsfc737de2016-03-23 17:33:02 -04005682 * @param message The {@link Message} to be displayed
5683 * @return this object for method chaining
5684 */
5685 public MessagingStyle addMessage(Message message) {
5686 mMessages.add(message);
5687 if (mMessages.size() > MAXIMUM_RETAINED_MESSAGES) {
5688 mMessages.remove(0);
5689 }
5690 return this;
5691 }
5692
5693 /**
Adrian Roos437cd562017-01-18 15:47:03 -08005694 * Adds a {@link Message} for historic context in this notification.
5695 *
5696 * <p>Messages should be added as historic if they are not the main subject of the
5697 * notification but may give context to a conversation. The system may choose to present
5698 * them only when relevant, e.g. when replying to a message through a {@link RemoteInput}.
5699 *
5700 * <p>The messages should be added in chronologic order, i.e. the oldest first,
5701 * the newest last.
5702 *
5703 * @param message The historic {@link Message} to be added
5704 * @return this object for method chaining
5705 */
5706 public MessagingStyle addHistoricMessage(Message message) {
5707 mHistoricMessages.add(message);
5708 if (mHistoricMessages.size() > MAXIMUM_RETAINED_MESSAGES) {
5709 mHistoricMessages.remove(0);
5710 }
5711 return this;
5712 }
5713
5714 /**
Alex Hillsfc737de2016-03-23 17:33:02 -04005715 * Gets the list of {@code Message} objects that represent the notification
5716 */
5717 public List<Message> getMessages() {
5718 return mMessages;
5719 }
5720
5721 /**
Adrian Roos437cd562017-01-18 15:47:03 -08005722 * Gets the list of historic {@code Message}s in the notification.
5723 */
5724 public List<Message> getHistoricMessages() {
5725 return mHistoricMessages;
5726 }
5727
5728 /**
Alex Hillsfc737de2016-03-23 17:33:02 -04005729 * @hide
5730 */
5731 @Override
5732 public void addExtras(Bundle extras) {
5733 super.addExtras(extras);
5734 if (mUserDisplayName != null) {
5735 extras.putCharSequence(EXTRA_SELF_DISPLAY_NAME, mUserDisplayName);
5736 }
5737 if (mConversationTitle != null) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04005738 extras.putCharSequence(EXTRA_CONVERSATION_TITLE, mConversationTitle);
Alex Hillsfc737de2016-03-23 17:33:02 -04005739 }
Alex Hillsd9b04d92016-04-11 16:38:16 -04005740 if (!mMessages.isEmpty()) { extras.putParcelableArray(EXTRA_MESSAGES,
5741 Message.getBundleArrayForMessages(mMessages));
Alex Hillsfc737de2016-03-23 17:33:02 -04005742 }
Adrian Roos437cd562017-01-18 15:47:03 -08005743 if (!mHistoricMessages.isEmpty()) { extras.putParcelableArray(EXTRA_HISTORIC_MESSAGES,
5744 Message.getBundleArrayForMessages(mHistoricMessages));
5745 }
Adrian Roos33fbd2c2016-05-27 15:35:28 -07005746
5747 fixTitleAndTextExtras(extras);
5748 }
5749
5750 private void fixTitleAndTextExtras(Bundle extras) {
5751 Message m = findLatestIncomingMessage();
5752 CharSequence text = (m == null) ? null : m.mText;
5753 CharSequence sender = m == null ? null
5754 : TextUtils.isEmpty(m.mSender) ? mUserDisplayName : m.mSender;
5755 CharSequence title;
5756 if (!TextUtils.isEmpty(mConversationTitle)) {
5757 if (!TextUtils.isEmpty(sender)) {
5758 BidiFormatter bidi = BidiFormatter.getInstance();
5759 title = mBuilder.mContext.getString(
5760 com.android.internal.R.string.notification_messaging_title_template,
5761 bidi.unicodeWrap(mConversationTitle), bidi.unicodeWrap(m.mSender));
5762 } else {
5763 title = mConversationTitle;
5764 }
5765 } else {
5766 title = sender;
5767 }
5768
5769 if (title != null) {
5770 extras.putCharSequence(EXTRA_TITLE, title);
5771 }
5772 if (text != null) {
5773 extras.putCharSequence(EXTRA_TEXT, text);
5774 }
Alex Hillsfc737de2016-03-23 17:33:02 -04005775 }
5776
5777 /**
5778 * @hide
5779 */
5780 @Override
5781 protected void restoreFromExtras(Bundle extras) {
5782 super.restoreFromExtras(extras);
5783
5784 mMessages.clear();
Adrian Roos437cd562017-01-18 15:47:03 -08005785 mHistoricMessages.clear();
Adrian Roos96b7e202016-05-17 13:50:38 -07005786 mUserDisplayName = extras.getCharSequence(EXTRA_SELF_DISPLAY_NAME);
5787 mConversationTitle = extras.getCharSequence(EXTRA_CONVERSATION_TITLE);
Adrian Roos437cd562017-01-18 15:47:03 -08005788 Parcelable[] messages = extras.getParcelableArray(EXTRA_MESSAGES);
5789 if (messages != null && messages instanceof Parcelable[]) {
5790 mMessages = Message.getMessagesFromBundleArray(messages);
5791 }
5792 Parcelable[] histMessages = extras.getParcelableArray(EXTRA_HISTORIC_MESSAGES);
5793 if (histMessages != null && histMessages instanceof Parcelable[]) {
5794 mHistoricMessages = Message.getMessagesFromBundleArray(histMessages);
Alex Hillsfc737de2016-03-23 17:33:02 -04005795 }
5796 }
5797
5798 /**
5799 * @hide
5800 */
Adrian Roosc1a80b02016-04-05 14:54:55 -07005801 @Override
Selim Cinek7d1009b2017-01-25 15:28:28 -08005802 public RemoteViews makeContentView(boolean increasedHeight) {
5803 if (!increasedHeight) {
5804 Message m = findLatestIncomingMessage();
5805 CharSequence title = mConversationTitle != null
5806 ? mConversationTitle
5807 : (m == null) ? null : m.mSender;
5808 CharSequence text = (m == null)
5809 ? null
5810 : mConversationTitle != null ? makeMessageLine(m, mBuilder) : m.mText;
Alex Hillsfc737de2016-03-23 17:33:02 -04005811
Selim Cinek7d1009b2017-01-25 15:28:28 -08005812 return mBuilder.applyStandardTemplate(mBuilder.getBaseLayoutResource(),
5813 mBuilder.mParams.reset().hasProgress(false).title(title).text(text));
5814 } else {
5815 ArrayList<Action> actions = mBuilder.mActions;
5816 mBuilder.mActions = new ArrayList<>();
5817 RemoteViews remoteViews = makeBigContentView();
5818 mBuilder.mActions = actions;
5819 return remoteViews;
5820 }
Adrian Roosc1a80b02016-04-05 14:54:55 -07005821 }
5822
5823 private Message findLatestIncomingMessage() {
5824 for (int i = mMessages.size() - 1; i >= 0; i--) {
5825 Message m = mMessages.get(i);
5826 // Incoming messages have a non-empty sender.
5827 if (!TextUtils.isEmpty(m.mSender)) {
5828 return m;
5829 }
5830 }
Adrian Roos33fbd2c2016-05-27 15:35:28 -07005831 if (!mMessages.isEmpty()) {
5832 // No incoming messages, fall back to outgoing message
5833 return mMessages.get(mMessages.size() - 1);
5834 }
Adrian Roosc1a80b02016-04-05 14:54:55 -07005835 return null;
5836 }
5837
5838 /**
5839 * @hide
5840 */
5841 @Override
5842 public RemoteViews makeBigContentView() {
5843 CharSequence title = !TextUtils.isEmpty(super.mBigContentTitle)
5844 ? super.mBigContentTitle
5845 : mConversationTitle;
5846 boolean hasTitle = !TextUtils.isEmpty(title);
5847
Adrian Roosfeafa052016-06-01 17:09:45 -07005848 if (mMessages.size() == 1) {
5849 // Special case for a single message: Use the big text style
5850 // so the collapsed and expanded versions match nicely.
5851 CharSequence bigTitle;
5852 CharSequence text;
5853 if (hasTitle) {
5854 bigTitle = title;
Selim Cinek7b9605b2017-01-19 17:36:00 -08005855 text = makeMessageLine(mMessages.get(0), mBuilder);
Adrian Roosfeafa052016-06-01 17:09:45 -07005856 } else {
5857 bigTitle = mMessages.get(0).mSender;
5858 text = mMessages.get(0).mText;
5859 }
Adrian Roosb1f427c2016-05-26 12:27:15 -07005860 RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(
5861 mBuilder.getBigTextLayoutResource(),
Adrian Roos70d7aa32017-01-11 15:39:06 -08005862 mBuilder.mParams.reset().hasProgress(false).title(bigTitle).text(null));
Adrian Roosb1f427c2016-05-26 12:27:15 -07005863 BigTextStyle.applyBigTextContentView(mBuilder, contentView, text);
5864 return contentView;
5865 }
5866
Adrian Roos48d746a2016-04-12 14:57:28 -07005867 RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(
Adrian Roosc1a80b02016-04-05 14:54:55 -07005868 mBuilder.getMessagingLayoutResource(),
Adrian Roos70d7aa32017-01-11 15:39:06 -08005869 mBuilder.mParams.reset().hasProgress(false).title(title).text(null));
Adrian Roosc1a80b02016-04-05 14:54:55 -07005870
5871 int[] rowIds = {R.id.inbox_text0, R.id.inbox_text1, R.id.inbox_text2, R.id.inbox_text3,
5872 R.id.inbox_text4, R.id.inbox_text5, R.id.inbox_text6};
5873
5874 // Make sure all rows are gone in case we reuse a view.
5875 for (int rowId : rowIds) {
5876 contentView.setViewVisibility(rowId, View.GONE);
5877 }
5878
5879 int i=0;
Adrian Roos2d5dbba2016-06-08 17:11:53 -07005880 contentView.setViewLayoutMarginBottomDimen(R.id.line1,
5881 hasTitle ? R.dimen.notification_messaging_spacing : 0);
Adrian Roosc1a80b02016-04-05 14:54:55 -07005882 contentView.setInt(R.id.notification_messaging, "setNumIndentLines",
Selim Cinek279fa862016-06-14 10:57:25 -07005883 !mBuilder.mN.hasLargeIcon() ? 0 : (hasTitle ? 1 : 2));
Adrian Roosc1a80b02016-04-05 14:54:55 -07005884
Adrian Roosfeafa052016-06-01 17:09:45 -07005885 int contractedChildId = View.NO_ID;
5886 Message contractedMessage = findLatestIncomingMessage();
Adrian Roos437cd562017-01-18 15:47:03 -08005887 int firstHistoricMessage = Math.max(0, mHistoricMessages.size()
5888 - (rowIds.length - mMessages.size()));
5889 while (firstHistoricMessage + i < mHistoricMessages.size() && i < rowIds.length) {
5890 Message m = mHistoricMessages.get(firstHistoricMessage + i);
5891 int rowId = rowIds[i];
5892
Selim Cinek7b9605b2017-01-19 17:36:00 -08005893 contentView.setTextViewText(rowId, makeMessageLine(m, mBuilder));
Adrian Roos437cd562017-01-18 15:47:03 -08005894
5895 if (contractedMessage == m) {
5896 contractedChildId = rowId;
5897 }
5898
5899 i++;
5900 }
5901
Adrian Roosc1a80b02016-04-05 14:54:55 -07005902 int firstMessage = Math.max(0, mMessages.size() - rowIds.length);
5903 while (firstMessage + i < mMessages.size() && i < rowIds.length) {
5904 Message m = mMessages.get(firstMessage + i);
5905 int rowId = rowIds[i];
5906
5907 contentView.setViewVisibility(rowId, View.VISIBLE);
Selim Cinek7b9605b2017-01-19 17:36:00 -08005908 contentView.setTextViewText(rowId, makeMessageLine(m, mBuilder));
5909 mBuilder.setTextViewColorSecondary(contentView, rowId);
Adrian Roosc1a80b02016-04-05 14:54:55 -07005910
Adrian Roosfeafa052016-06-01 17:09:45 -07005911 if (contractedMessage == m) {
5912 contractedChildId = rowId;
5913 }
5914
Adrian Roosc1a80b02016-04-05 14:54:55 -07005915 i++;
5916 }
Adrian Roos437cd562017-01-18 15:47:03 -08005917 // Clear the remaining views for reapply. Ensures that historic message views can
5918 // reliably be identified as being GONE and having non-null text.
5919 while (i < rowIds.length) {
5920 int rowId = rowIds[i];
5921 contentView.setTextViewText(rowId, null);
5922 i++;
5923 }
5924
Adrian Roosfeafa052016-06-01 17:09:45 -07005925 // Record this here to allow transformation between the contracted and expanded views.
5926 contentView.setInt(R.id.notification_messaging, "setContractedChildId",
5927 contractedChildId);
Alex Hillsfc737de2016-03-23 17:33:02 -04005928 return contentView;
5929 }
5930
Selim Cinek7b9605b2017-01-19 17:36:00 -08005931 private CharSequence makeMessageLine(Message m, Builder builder) {
Adrian Roosc1a80b02016-04-05 14:54:55 -07005932 BidiFormatter bidi = BidiFormatter.getInstance();
5933 SpannableStringBuilder sb = new SpannableStringBuilder();
Selim Cinek7b9605b2017-01-19 17:36:00 -08005934 boolean colorize = builder.isColorized();
Adrian Roosc1a80b02016-04-05 14:54:55 -07005935 if (TextUtils.isEmpty(m.mSender)) {
5936 CharSequence replyName = mUserDisplayName == null ? "" : mUserDisplayName;
5937 sb.append(bidi.unicodeWrap(replyName),
Selim Cinek7b9605b2017-01-19 17:36:00 -08005938 makeFontColorSpan(colorize
5939 ? builder.getPrimaryTextColor()
5940 : mBuilder.resolveContrastColor()),
Adrian Roosc1a80b02016-04-05 14:54:55 -07005941 0 /* flags */);
5942 } else {
5943 sb.append(bidi.unicodeWrap(m.mSender),
Selim Cinek7b9605b2017-01-19 17:36:00 -08005944 makeFontColorSpan(colorize
5945 ? builder.getPrimaryTextColor()
5946 : Color.BLACK),
Adrian Roosc1a80b02016-04-05 14:54:55 -07005947 0 /* flags */);
5948 }
5949 CharSequence text = m.mText == null ? "" : m.mText;
5950 sb.append(" ").append(bidi.unicodeWrap(text));
5951 return sb;
5952 }
5953
Adrian Roosdedd1df2016-04-26 16:38:47 -07005954 /**
5955 * @hide
5956 */
5957 @Override
Selim Cinek87ed69b2017-02-09 15:59:43 -08005958 public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
5959 if (increasedHeight) {
5960 return makeBigContentView();
5961 }
Adrian Roosdedd1df2016-04-26 16:38:47 -07005962 Message m = findLatestIncomingMessage();
5963 CharSequence title = mConversationTitle != null
5964 ? mConversationTitle
5965 : (m == null) ? null : m.mSender;
5966 CharSequence text = (m == null)
5967 ? null
Selim Cinek7b9605b2017-01-19 17:36:00 -08005968 : mConversationTitle != null ? makeMessageLine(m, mBuilder) : m.mText;
Adrian Roosdedd1df2016-04-26 16:38:47 -07005969
5970 return mBuilder.applyStandardTemplateWithActions(mBuilder.getBigBaseLayoutResource(),
Adrian Roos70d7aa32017-01-11 15:39:06 -08005971 mBuilder.mParams.reset().hasProgress(false).title(title).text(text));
Adrian Roosdedd1df2016-04-26 16:38:47 -07005972 }
5973
Adrian Roosc1a80b02016-04-05 14:54:55 -07005974 private static TextAppearanceSpan makeFontColorSpan(int color) {
5975 return new TextAppearanceSpan(null, 0, 0,
5976 ColorStateList.valueOf(color), null);
5977 }
5978
Alex Hillsd9b04d92016-04-11 16:38:16 -04005979 public static final class Message {
5980
5981 static final String KEY_TEXT = "text";
5982 static final String KEY_TIMESTAMP = "time";
5983 static final String KEY_SENDER = "sender";
5984 static final String KEY_DATA_MIME_TYPE = "type";
5985 static final String KEY_DATA_URI= "uri";
Shane Brennan5a871862017-03-11 13:14:17 -08005986 static final String KEY_EXTRAS_BUNDLE = "extras";
Alex Hillsfc737de2016-03-23 17:33:02 -04005987
5988 private final CharSequence mText;
5989 private final long mTimestamp;
5990 private final CharSequence mSender;
5991
Shane Brennan5a871862017-03-11 13:14:17 -08005992 private Bundle mExtras = new Bundle();
Alex Hillsfc737de2016-03-23 17:33:02 -04005993 private String mDataMimeType;
5994 private Uri mDataUri;
5995
5996 /**
5997 * Constructor
5998 * @param text A {@link CharSequence} to be displayed as the message content
5999 * @param timestamp Time at which the message arrived
6000 * @param sender A {@link CharSequence} to be used for displaying the name of the
6001 * sender. Should be <code>null</code> for messages by the current user, in which case
6002 * the platform will insert {@link MessagingStyle#getUserDisplayName()}.
6003 * Should be unique amongst all individuals in the conversation, and should be
6004 * consistent during re-posts of the notification.
6005 */
6006 public Message(CharSequence text, long timestamp, CharSequence sender){
6007 mText = text;
6008 mTimestamp = timestamp;
6009 mSender = sender;
6010 }
6011
6012 /**
6013 * Sets a binary blob of data and an associated MIME type for a message. In the case
6014 * where the platform doesn't support the MIME type, the original text provided in the
6015 * constructor will be used.
6016 * @param dataMimeType The MIME type of the content. See
6017 * <a href="{@docRoot}notifications/messaging.html"> for the list of supported MIME
6018 * types on Android and Android Wear.
6019 * @param dataUri The uri containing the content whose type is given by the MIME type.
6020 * <p class="note">
6021 * <ol>
6022 * <li>Notification Listeners including the System UI need permission to access the
6023 * data the Uri points to. The recommended ways to do this are:</li>
6024 * <li>Store the data in your own ContentProvider, making sure that other apps have
6025 * the correct permission to access your provider. The preferred mechanism for
6026 * providing access is to use per-URI permissions which are temporary and only
6027 * grant access to the receiving application. An easy way to create a
6028 * ContentProvider like this is to use the FileProvider helper class.</li>
6029 * <li>Use the system MediaStore. The MediaStore is primarily aimed at video, audio
6030 * and image MIME types, however beginning with Android 3.0 (API level 11) it can
6031 * also store non-media types (see MediaStore.Files for more info). Files can be
6032 * inserted into the MediaStore using scanFile() after which a content:// style
6033 * Uri suitable for sharing is passed to the provided onScanCompleted() callback.
6034 * Note that once added to the system MediaStore the content is accessible to any
6035 * app on the device.</li>
6036 * </ol>
6037 * @return this object for method chaining
6038 */
6039 public Message setData(String dataMimeType, Uri dataUri) {
6040 mDataMimeType = dataMimeType;
6041 mDataUri = dataUri;
6042 return this;
6043 }
6044
Alex Hillsfc737de2016-03-23 17:33:02 -04006045 /**
6046 * Get the text to be used for this message, or the fallback text if a type and content
6047 * Uri have been set
6048 */
6049 public CharSequence getText() {
6050 return mText;
6051 }
6052
6053 /**
6054 * Get the time at which this message arrived
6055 */
6056 public long getTimestamp() {
6057 return mTimestamp;
6058 }
6059
6060 /**
Shane Brennan5a871862017-03-11 13:14:17 -08006061 * Get the extras Bundle for this message.
6062 */
6063 public Bundle getExtras() {
6064 return mExtras;
6065 }
6066
6067 /**
Alex Hillsfc737de2016-03-23 17:33:02 -04006068 * Get the text used to display the contact's name in the messaging experience
6069 */
6070 public CharSequence getSender() {
6071 return mSender;
6072 }
6073
6074 /**
6075 * Get the MIME type of the data pointed to by the Uri
6076 */
6077 public String getDataMimeType() {
6078 return mDataMimeType;
6079 }
6080
6081 /**
6082 * Get the the Uri pointing to the content of the message. Can be null, in which case
6083 * {@see #getText()} is used.
6084 */
6085 public Uri getDataUri() {
6086 return mDataUri;
6087 }
6088
Alex Hillsd9b04d92016-04-11 16:38:16 -04006089 private Bundle toBundle() {
6090 Bundle bundle = new Bundle();
Alex Hillsfc737de2016-03-23 17:33:02 -04006091 if (mText != null) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04006092 bundle.putCharSequence(KEY_TEXT, mText);
Alex Hillsfc737de2016-03-23 17:33:02 -04006093 }
Alex Hillsd9b04d92016-04-11 16:38:16 -04006094 bundle.putLong(KEY_TIMESTAMP, mTimestamp);
Alex Hillsfc737de2016-03-23 17:33:02 -04006095 if (mSender != null) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04006096 bundle.putCharSequence(KEY_SENDER, mSender);
Alex Hillsfc737de2016-03-23 17:33:02 -04006097 }
6098 if (mDataMimeType != null) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04006099 bundle.putString(KEY_DATA_MIME_TYPE, mDataMimeType);
Alex Hillsfc737de2016-03-23 17:33:02 -04006100 }
6101 if (mDataUri != null) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04006102 bundle.putParcelable(KEY_DATA_URI, mDataUri);
Alex Hillsfc737de2016-03-23 17:33:02 -04006103 }
Shane Brennan5a871862017-03-11 13:14:17 -08006104 if (mExtras != null) {
6105 bundle.putBundle(KEY_EXTRAS_BUNDLE, mExtras);
6106 }
Alex Hillsd9b04d92016-04-11 16:38:16 -04006107 return bundle;
Alex Hillsfc737de2016-03-23 17:33:02 -04006108 }
6109
Alex Hillsd9b04d92016-04-11 16:38:16 -04006110 static Bundle[] getBundleArrayForMessages(List<Message> messages) {
6111 Bundle[] bundles = new Bundle[messages.size()];
6112 final int N = messages.size();
6113 for (int i = 0; i < N; i++) {
6114 bundles[i] = messages.get(i).toBundle();
6115 }
6116 return bundles;
6117 }
6118
Adrian Roosdedd1df2016-04-26 16:38:47 -07006119 static List<Message> getMessagesFromBundleArray(Parcelable[] bundles) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04006120 List<Message> messages = new ArrayList<>(bundles.length);
6121 for (int i = 0; i < bundles.length; i++) {
Adrian Roosdedd1df2016-04-26 16:38:47 -07006122 if (bundles[i] instanceof Bundle) {
6123 Message message = getMessageFromBundle((Bundle)bundles[i]);
6124 if (message != null) {
6125 messages.add(message);
6126 }
Alex Hillsd9b04d92016-04-11 16:38:16 -04006127 }
6128 }
6129 return messages;
6130 }
6131
6132 static Message getMessageFromBundle(Bundle bundle) {
6133 try {
Adrian Roosfbddd2c2016-05-13 12:57:20 -07006134 if (!bundle.containsKey(KEY_TEXT) || !bundle.containsKey(KEY_TIMESTAMP)) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04006135 return null;
6136 } else {
6137 Message message = new Message(bundle.getCharSequence(KEY_TEXT),
6138 bundle.getLong(KEY_TIMESTAMP), bundle.getCharSequence(KEY_SENDER));
6139 if (bundle.containsKey(KEY_DATA_MIME_TYPE) &&
6140 bundle.containsKey(KEY_DATA_URI)) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04006141 message.setData(bundle.getString(KEY_DATA_MIME_TYPE),
6142 (Uri) bundle.getParcelable(KEY_DATA_URI));
Alex Hillsfc737de2016-03-23 17:33:02 -04006143 }
Shane Brennan5a871862017-03-11 13:14:17 -08006144 if (bundle.containsKey(KEY_EXTRAS_BUNDLE)) {
6145 message.getExtras().putAll(bundle.getBundle(KEY_EXTRAS_BUNDLE));
6146 }
Alex Hillsd9b04d92016-04-11 16:38:16 -04006147 return message;
6148 }
6149 } catch (ClassCastException e) {
6150 return null;
6151 }
6152 }
Alex Hillsfc737de2016-03-23 17:33:02 -04006153 }
6154 }
6155
6156 /**
Daniel Sandler879c5e02012-04-17 16:46:51 -04006157 * Helper class for generating large-format notifications that include a list of (up to 5) strings.
Joe Malin8d40d042012-11-05 11:36:40 -08006158 *
Robert Ly91c5ce32014-06-08 15:37:00 -07006159 * Here's how you'd set the <code>InboxStyle</code> on a notification:
Daniel Sandler879c5e02012-04-17 16:46:51 -04006160 * <pre class="prettyprint">
Robert Ly91c5ce32014-06-08 15:37:00 -07006161 * Notification notif = new Notification.Builder(mContext)
6162 * .setContentTitle(&quot;5 New mails from &quot; + sender.toString())
6163 * .setContentText(subject)
6164 * .setSmallIcon(R.drawable.new_mail)
6165 * .setLargeIcon(aBitmap)
6166 * .setStyle(new Notification.InboxStyle()
6167 * .addLine(str1)
6168 * .addLine(str2)
6169 * .setContentTitle(&quot;&quot;)
6170 * .setSummaryText(&quot;+3 more&quot;))
6171 * .build();
Daniel Sandler879c5e02012-04-17 16:46:51 -04006172 * </pre>
Joe Malin8d40d042012-11-05 11:36:40 -08006173 *
Daniel Sandler879c5e02012-04-17 16:46:51 -04006174 * @see Notification#bigContentView
6175 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04006176 public static class InboxStyle extends Style {
Daniel Sandler879c5e02012-04-17 16:46:51 -04006177 private ArrayList<CharSequence> mTexts = new ArrayList<CharSequence>(5);
6178
Chris Wrenfbd96ba2012-05-01 12:03:58 -04006179 public InboxStyle() {
6180 }
6181
Adrian Roosf5faf9d2016-05-23 13:56:15 -07006182 /**
6183 * @deprecated use {@code InboxStyle()}.
6184 */
6185 @Deprecated
Daniel Sandler879c5e02012-04-17 16:46:51 -04006186 public InboxStyle(Builder builder) {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04006187 setBuilder(builder);
Daniel Sandler879c5e02012-04-17 16:46:51 -04006188 }
6189
Chris Wrend6297db2012-05-03 16:20:13 -04006190 /**
6191 * Overrides ContentTitle in the big form of the template.
6192 * This defaults to the value passed to setContentTitle().
6193 */
6194 public InboxStyle setBigContentTitle(CharSequence title) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04006195 internalSetBigContentTitle(safeCharSequence(title));
Chris Wrend6297db2012-05-03 16:20:13 -04006196 return this;
6197 }
6198
6199 /**
6200 * Set the first line of text after the detail section in the big form of the template.
6201 */
6202 public InboxStyle setSummaryText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04006203 internalSetSummaryText(safeCharSequence(cs));
Chris Wrend6297db2012-05-03 16:20:13 -04006204 return this;
6205 }
6206
Chris Wren0bd664d2012-08-01 13:56:56 -04006207 /**
6208 * Append a line to the digest section of the Inbox notification.
6209 */
Daniel Sandler879c5e02012-04-17 16:46:51 -04006210 public InboxStyle addLine(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04006211 mTexts.add(safeCharSequence(cs));
Daniel Sandler879c5e02012-04-17 16:46:51 -04006212 return this;
6213 }
6214
Daniel Sandlerf45564e2013-04-15 15:05:08 -04006215 /**
6216 * @hide
6217 */
6218 public void addExtras(Bundle extras) {
6219 super.addExtras(extras);
Christoph Studer4600f9b2014-07-22 22:44:43 +02006220
Daniel Sandlerf45564e2013-04-15 15:05:08 -04006221 CharSequence[] a = new CharSequence[mTexts.size()];
6222 extras.putCharSequenceArray(EXTRA_TEXT_LINES, mTexts.toArray(a));
6223 }
6224
Christoph Studer4600f9b2014-07-22 22:44:43 +02006225 /**
6226 * @hide
6227 */
6228 @Override
6229 protected void restoreFromExtras(Bundle extras) {
6230 super.restoreFromExtras(extras);
6231
6232 mTexts.clear();
6233 if (extras.containsKey(EXTRA_TEXT_LINES)) {
6234 Collections.addAll(mTexts, extras.getCharSequenceArray(EXTRA_TEXT_LINES));
6235 }
6236 }
6237
Julia Reynoldsd9228f12015-10-20 10:37:27 -04006238 /**
6239 * @hide
6240 */
6241 public RemoteViews makeBigContentView() {
Selim Cinekc848c3a2016-01-13 15:27:30 -08006242 // Remove the content text so it disappears unless you have a summary
Christoph Studer4600f9b2014-07-22 22:44:43 +02006243 // Nasty
Julia Reynoldsd9228f12015-10-20 10:37:27 -04006244 CharSequence oldBuilderContentText = mBuilder.mN.extras.getCharSequence(EXTRA_TEXT);
6245 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, null);
Christoph Studer4600f9b2014-07-22 22:44:43 +02006246
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01006247 RemoteViews contentView = getStandardView(mBuilder.getInboxLayoutResource());
Daniel Sandler619738c2012-06-07 16:33:08 -04006248
Julia Reynoldsd9228f12015-10-20 10:37:27 -04006249 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, oldBuilderContentText);
Christoph Studer4600f9b2014-07-22 22:44:43 +02006250
Chris Wrend6297db2012-05-03 16:20:13 -04006251 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 -04006252 R.id.inbox_text4, R.id.inbox_text5, R.id.inbox_text6};
Chris Wrend6297db2012-05-03 16:20:13 -04006253
Chris Wren4ed80d52012-05-17 09:30:03 -04006254 // Make sure all rows are gone in case we reuse a view.
6255 for (int rowId : rowIds) {
6256 contentView.setViewVisibility(rowId, View.GONE);
6257 }
6258
Daniel Sandler879c5e02012-04-17 16:46:51 -04006259 int i=0;
Selim Cinek07c80172016-04-21 16:40:47 -07006260 int topPadding = mBuilder.mContext.getResources().getDimensionPixelSize(
6261 R.dimen.notification_inbox_item_top_padding);
Selim Cinek247fa012016-02-18 09:50:48 -08006262 boolean first = true;
Selim Cinek07c80172016-04-21 16:40:47 -07006263 int onlyViewId = 0;
6264 int maxRows = rowIds.length;
6265 if (mBuilder.mActions.size() > 0) {
6266 maxRows--;
6267 }
6268 while (i < mTexts.size() && i < maxRows) {
Daniel Sandler879c5e02012-04-17 16:46:51 -04006269 CharSequence str = mTexts.get(i);
Selim Cinek07c80172016-04-21 16:40:47 -07006270 if (!TextUtils.isEmpty(str)) {
Daniel Sandler879c5e02012-04-17 16:46:51 -04006271 contentView.setViewVisibility(rowIds[i], View.VISIBLE);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01006272 contentView.setTextViewText(rowIds[i], mBuilder.processLegacyText(str));
Selim Cinek7b9605b2017-01-19 17:36:00 -08006273 mBuilder.setTextViewColorSecondary(contentView, rowIds[i]);
Selim Cinek07c80172016-04-21 16:40:47 -07006274 contentView.setViewPadding(rowIds[i], 0, topPadding, 0, 0);
Selim Cinek247fa012016-02-18 09:50:48 -08006275 handleInboxImageMargin(contentView, rowIds[i], first);
Selim Cinek07c80172016-04-21 16:40:47 -07006276 if (first) {
6277 onlyViewId = rowIds[i];
6278 } else {
6279 onlyViewId = 0;
6280 }
Selim Cinek247fa012016-02-18 09:50:48 -08006281 first = false;
Daniel Sandler879c5e02012-04-17 16:46:51 -04006282 }
6283 i++;
6284 }
Selim Cinek07c80172016-04-21 16:40:47 -07006285 if (onlyViewId != 0) {
6286 // We only have 1 entry, lets make it look like the normal Text of a Bigtext
6287 topPadding = mBuilder.mContext.getResources().getDimensionPixelSize(
6288 R.dimen.notification_text_margin_top);
6289 contentView.setViewPadding(onlyViewId, 0, topPadding, 0, 0);
6290 }
Selim Cinek1e0bf612015-11-20 15:57:26 -08006291
Daniel Sandler879c5e02012-04-17 16:46:51 -04006292 return contentView;
6293 }
Selim Cinek1e0bf612015-11-20 15:57:26 -08006294
Selim Cinek247fa012016-02-18 09:50:48 -08006295 private void handleInboxImageMargin(RemoteViews contentView, int id, boolean first) {
Selim Cinek1e0bf612015-11-20 15:57:26 -08006296 int endMargin = 0;
Selim Cinek247fa012016-02-18 09:50:48 -08006297 if (first) {
6298 final int max = mBuilder.mN.extras.getInt(EXTRA_PROGRESS_MAX, 0);
6299 final boolean ind = mBuilder.mN.extras.getBoolean(EXTRA_PROGRESS_INDETERMINATE);
6300 boolean hasProgress = max != 0 || ind;
Selim Cinek279fa862016-06-14 10:57:25 -07006301 if (mBuilder.mN.hasLargeIcon() && !hasProgress) {
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006302 endMargin = R.dimen.notification_content_picture_margin;
Selim Cinek247fa012016-02-18 09:50:48 -08006303 }
Selim Cinek1e0bf612015-11-20 15:57:26 -08006304 }
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006305 contentView.setViewLayoutMarginEndDimen(id, endMargin);
Selim Cinek1e0bf612015-11-20 15:57:26 -08006306 }
Daniel Sandler879c5e02012-04-17 16:46:51 -04006307 }
Dan Sandler842dd772014-05-15 09:36:47 -04006308
6309 /**
6310 * Notification style for media playback notifications.
6311 *
6312 * In the expanded form, {@link Notification#bigContentView}, up to 5
6313 * {@link Notification.Action}s specified with
Dan Sandler86647982015-05-13 23:41:13 -04006314 * {@link Notification.Builder#addAction(Action) addAction} will be
Dan Sandler842dd772014-05-15 09:36:47 -04006315 * shown as icon-only pushbuttons, suitable for transport controls. The Bitmap given to
6316 * {@link Notification.Builder#setLargeIcon(android.graphics.Bitmap) setLargeIcon()} will be
6317 * treated as album artwork.
Selim Cinek99104832017-01-25 14:47:33 -08006318 * <p>
Dan Sandler842dd772014-05-15 09:36:47 -04006319 * Unlike the other styles provided here, MediaStyle can also modify the standard-size
6320 * {@link Notification#contentView}; by providing action indices to
Christoph Studerfde6f4d2014-12-12 13:23:26 +01006321 * {@link #setShowActionsInCompactView(int...)} you can promote up to 3 actions to be displayed
Dan Sandler842dd772014-05-15 09:36:47 -04006322 * in the standard view alongside the usual content.
Selim Cinek99104832017-01-25 14:47:33 -08006323 * <p>
Bryan Mawhinney6be8de32014-07-18 10:35:12 +01006324 * Notifications created with MediaStyle will have their category set to
6325 * {@link Notification#CATEGORY_TRANSPORT CATEGORY_TRANSPORT} unless you set a different
6326 * category using {@link Notification.Builder#setCategory(String) setCategory()}.
Selim Cinek99104832017-01-25 14:47:33 -08006327 * <p>
Jeff Browndba34ba2014-06-24 20:46:03 -07006328 * Finally, if you attach a {@link android.media.session.MediaSession.Token} using
6329 * {@link android.app.Notification.MediaStyle#setMediaSession(MediaSession.Token)},
Dan Sandler842dd772014-05-15 09:36:47 -04006330 * the System UI can identify this as a notification representing an active media session
6331 * and respond accordingly (by showing album artwork in the lockscreen, for example).
6332 *
Selim Cinek99104832017-01-25 14:47:33 -08006333 * <p>
6334 * Starting at {@link android.os.Build.VERSION_CODES#O Android O} any notification that has a
6335 * media session attached with {@link #setMediaSession(MediaSession.Token)} will be colorized.
6336 * You can opt-out of this behavior by using {@link Notification.Builder#setColorized(boolean)}.
6337 * <p>
6338 *
Dan Sandler842dd772014-05-15 09:36:47 -04006339 * To use this style with your Notification, feed it to
6340 * {@link Notification.Builder#setStyle(android.app.Notification.Style)} like so:
6341 * <pre class="prettyprint">
6342 * Notification noti = new Notification.Builder()
6343 * .setSmallIcon(R.drawable.ic_stat_player)
Christoph Studere935fe92014-11-24 14:18:06 +01006344 * .setContentTitle(&quot;Track title&quot;)
6345 * .setContentText(&quot;Artist - Album&quot;)
6346 * .setLargeIcon(albumArtBitmap))
Bryan Mawhinney6be8de32014-07-18 10:35:12 +01006347 * .setStyle(<b>new Notification.MediaStyle()</b>
6348 * .setMediaSession(mySession))
Dan Sandler842dd772014-05-15 09:36:47 -04006349 * .build();
6350 * </pre>
6351 *
6352 * @see Notification#bigContentView
Selim Cinek99104832017-01-25 14:47:33 -08006353 * @see Notification.Builder#setColorized(boolean)
Dan Sandler842dd772014-05-15 09:36:47 -04006354 */
6355 public static class MediaStyle extends Style {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006356 static final int MAX_MEDIA_BUTTONS_IN_COMPACT = 3;
Dan Sandler842dd772014-05-15 09:36:47 -04006357 static final int MAX_MEDIA_BUTTONS = 5;
6358
6359 private int[] mActionsToShowInCompact = null;
Jeff Browndba34ba2014-06-24 20:46:03 -07006360 private MediaSession.Token mToken;
Dan Sandler842dd772014-05-15 09:36:47 -04006361
6362 public MediaStyle() {
6363 }
6364
Adrian Roosf5faf9d2016-05-23 13:56:15 -07006365 /**
6366 * @deprecated use {@code MediaStyle()}.
6367 */
6368 @Deprecated
Dan Sandler842dd772014-05-15 09:36:47 -04006369 public MediaStyle(Builder builder) {
6370 setBuilder(builder);
6371 }
6372
6373 /**
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006374 * 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 -04006375 * notification view.
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006376 *
6377 * @param actions the indices of the actions to show in the compact notification view
Dan Sandler842dd772014-05-15 09:36:47 -04006378 */
6379 public MediaStyle setShowActionsInCompactView(int...actions) {
6380 mActionsToShowInCompact = actions;
6381 return this;
6382 }
6383
6384 /**
Jeff Browndba34ba2014-06-24 20:46:03 -07006385 * Attach a {@link android.media.session.MediaSession.Token} to this Notification
6386 * to provide additional playback information and control to the SystemUI.
Dan Sandler842dd772014-05-15 09:36:47 -04006387 */
Jeff Browndba34ba2014-06-24 20:46:03 -07006388 public MediaStyle setMediaSession(MediaSession.Token token) {
Dan Sandler842dd772014-05-15 09:36:47 -04006389 mToken = token;
6390 return this;
6391 }
6392
Christoph Studer4600f9b2014-07-22 22:44:43 +02006393 /**
6394 * @hide
6395 */
Dan Sandler842dd772014-05-15 09:36:47 -04006396 @Override
6397 public Notification buildStyled(Notification wip) {
Christoph Studer4600f9b2014-07-22 22:44:43 +02006398 super.buildStyled(wip);
Bryan Mawhinney6be8de32014-07-18 10:35:12 +01006399 if (wip.category == null) {
6400 wip.category = Notification.CATEGORY_TRANSPORT;
6401 }
Dan Sandler842dd772014-05-15 09:36:47 -04006402 return wip;
6403 }
6404
Christoph Studer4600f9b2014-07-22 22:44:43 +02006405 /**
6406 * @hide
6407 */
6408 @Override
Selim Cinek7d1009b2017-01-25 15:28:28 -08006409 public RemoteViews makeContentView(boolean increasedHeight) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04006410 return makeMediaContentView();
Christoph Studer4600f9b2014-07-22 22:44:43 +02006411 }
6412
6413 /**
6414 * @hide
6415 */
6416 @Override
Julia Reynoldsd9228f12015-10-20 10:37:27 -04006417 public RemoteViews makeBigContentView() {
6418 return makeMediaBigContentView();
Christoph Studer4600f9b2014-07-22 22:44:43 +02006419 }
6420
Selim Cinekcc10bfb2016-02-10 16:24:21 -08006421 /**
6422 * @hide
6423 */
6424 @Override
Selim Cinek87ed69b2017-02-09 15:59:43 -08006425 public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
Selim Cinekcc10bfb2016-02-10 16:24:21 -08006426 RemoteViews expanded = makeMediaBigContentView();
6427 return expanded != null ? expanded : makeMediaContentView();
6428 }
6429
Dan Sandler842dd772014-05-15 09:36:47 -04006430 /** @hide */
6431 @Override
6432 public void addExtras(Bundle extras) {
6433 super.addExtras(extras);
6434
6435 if (mToken != null) {
6436 extras.putParcelable(EXTRA_MEDIA_SESSION, mToken);
6437 }
Bryan Mawhinneye191f902014-07-22 12:50:09 +01006438 if (mActionsToShowInCompact != null) {
6439 extras.putIntArray(EXTRA_COMPACT_ACTIONS, mActionsToShowInCompact);
6440 }
Dan Sandler842dd772014-05-15 09:36:47 -04006441 }
6442
Christoph Studer4600f9b2014-07-22 22:44:43 +02006443 /**
6444 * @hide
6445 */
6446 @Override
6447 protected void restoreFromExtras(Bundle extras) {
6448 super.restoreFromExtras(extras);
6449
6450 if (extras.containsKey(EXTRA_MEDIA_SESSION)) {
6451 mToken = extras.getParcelable(EXTRA_MEDIA_SESSION);
6452 }
6453 if (extras.containsKey(EXTRA_COMPACT_ACTIONS)) {
6454 mActionsToShowInCompact = extras.getIntArray(EXTRA_COMPACT_ACTIONS);
6455 }
6456 }
6457
Selim Cinek5bf069a2015-11-10 19:14:27 -05006458 private RemoteViews generateMediaActionButton(Action action, int color) {
Dan Sandler842dd772014-05-15 09:36:47 -04006459 final boolean tombstone = (action.actionIntent == null);
Selim Cinekf33b1112015-07-15 17:45:11 -07006460 RemoteViews button = new BuilderRemoteViews(mBuilder.mContext.getApplicationInfo(),
Alan Viverette3cb07a462014-06-06 14:19:53 -07006461 R.layout.notification_material_media_action);
Dan Sandler68079d52015-07-22 10:45:30 -04006462 button.setImageViewIcon(R.id.action0, action.getIcon());
Selim Cinek5bf069a2015-11-10 19:14:27 -05006463 button.setDrawableParameters(R.id.action0, false, -1, color, PorterDuff.Mode.SRC_ATOP,
6464 -1);
Dan Sandler842dd772014-05-15 09:36:47 -04006465 if (!tombstone) {
6466 button.setOnClickPendingIntent(R.id.action0, action.actionIntent);
6467 }
6468 button.setContentDescription(R.id.action0, action.title);
6469 return button;
6470 }
6471
6472 private RemoteViews makeMediaContentView() {
6473 RemoteViews view = mBuilder.applyStandardTemplate(
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006474 R.layout.notification_template_material_media, false /* hasProgress */);
Dan Sandler842dd772014-05-15 09:36:47 -04006475
6476 final int numActions = mBuilder.mActions.size();
6477 final int N = mActionsToShowInCompact == null
6478 ? 0
6479 : Math.min(mActionsToShowInCompact.length, MAX_MEDIA_BUTTONS_IN_COMPACT);
6480 if (N > 0) {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006481 view.removeAllViews(com.android.internal.R.id.media_actions);
Dan Sandler842dd772014-05-15 09:36:47 -04006482 for (int i = 0; i < N; i++) {
6483 if (i >= numActions) {
6484 throw new IllegalArgumentException(String.format(
6485 "setShowActionsInCompactView: action %d out of bounds (max %d)",
6486 i, numActions - 1));
6487 }
6488
6489 final Action action = mBuilder.mActions.get(mActionsToShowInCompact[i]);
Selim Cinek5bf069a2015-11-10 19:14:27 -05006490 final RemoteViews button = generateMediaActionButton(action,
Selim Cinek99104832017-01-25 14:47:33 -08006491 getPrimaryHighlightColor());
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006492 view.addView(com.android.internal.R.id.media_actions, button);
Dan Sandler842dd772014-05-15 09:36:47 -04006493 }
6494 }
Selim Cinekfdc738f2016-01-27 20:04:27 -08006495 handleImage(view);
6496 // handle the content margin
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006497 int endMargin = R.dimen.notification_content_margin_end;
Selim Cinek279fa862016-06-14 10:57:25 -07006498 if (mBuilder.mN.hasLargeIcon()) {
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006499 endMargin = R.dimen.notification_content_plus_picture_margin_end;
Selim Cinekfdc738f2016-01-27 20:04:27 -08006500 }
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006501 view.setViewLayoutMarginEndDimen(R.id.notification_main_column, endMargin);
Dan Sandler842dd772014-05-15 09:36:47 -04006502 return view;
6503 }
6504
Selim Cinek99104832017-01-25 14:47:33 -08006505 private int getPrimaryHighlightColor() {
6506 return mBuilder.getPrimaryHighlightColor();
6507 }
6508
Dan Sandler842dd772014-05-15 09:36:47 -04006509 private RemoteViews makeMediaBigContentView() {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006510 final int actionCount = Math.min(mBuilder.mActions.size(), MAX_MEDIA_BUTTONS);
Selim Cinekcc10bfb2016-02-10 16:24:21 -08006511 // Dont add an expanded view if there is no more content to be revealed
6512 int actionsInCompact = mActionsToShowInCompact == null
6513 ? 0
6514 : Math.min(mActionsToShowInCompact.length, MAX_MEDIA_BUTTONS_IN_COMPACT);
Selim Cinek279fa862016-06-14 10:57:25 -07006515 if (!mBuilder.mN.hasLargeIcon() && actionCount <= actionsInCompact) {
Selim Cinekcc10bfb2016-02-10 16:24:21 -08006516 return null;
6517 }
Selim Cinek5bf069a2015-11-10 19:14:27 -05006518 RemoteViews big = mBuilder.applyStandardTemplate(
6519 R.layout.notification_template_material_big_media,
6520 false);
Dan Sandler842dd772014-05-15 09:36:47 -04006521
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006522 if (actionCount > 0) {
6523 big.removeAllViews(com.android.internal.R.id.media_actions);
6524 for (int i = 0; i < actionCount; i++) {
Selim Cinek5bf069a2015-11-10 19:14:27 -05006525 final RemoteViews button = generateMediaActionButton(mBuilder.mActions.get(i),
Selim Cinek99104832017-01-25 14:47:33 -08006526 getPrimaryHighlightColor());
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006527 big.addView(com.android.internal.R.id.media_actions, button);
Dan Sandler842dd772014-05-15 09:36:47 -04006528 }
6529 }
Selim Cinek5bf069a2015-11-10 19:14:27 -05006530 handleImage(big);
Dan Sandler842dd772014-05-15 09:36:47 -04006531 return big;
6532 }
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006533
Selim Cinek5bf069a2015-11-10 19:14:27 -05006534 private void handleImage(RemoteViews contentView) {
Selim Cinek279fa862016-06-14 10:57:25 -07006535 if (mBuilder.mN.hasLargeIcon()) {
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006536 contentView.setViewLayoutMarginEndDimen(R.id.line1, 0);
6537 contentView.setViewLayoutMarginEndDimen(R.id.text, 0);
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006538 }
6539 }
6540
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006541 /**
6542 * @hide
6543 */
6544 @Override
6545 protected boolean hasProgress() {
6546 return false;
6547 }
Dan Sandler842dd772014-05-15 09:36:47 -04006548 }
Griff Hazen61a9e862014-05-22 16:05:19 -07006549
Selim Cinek593610c2016-02-16 18:42:57 -08006550 /**
6551 * Notification style for custom views that are decorated by the system
6552 *
6553 * <p>Instead of providing a notification that is completely custom, a developer can set this
6554 * style and still obtain system decorations like the notification header with the expand
6555 * affordance and actions.
6556 *
6557 * <p>Use {@link android.app.Notification.Builder#setCustomContentView(RemoteViews)},
6558 * {@link android.app.Notification.Builder#setCustomBigContentView(RemoteViews)} and
6559 * {@link android.app.Notification.Builder#setCustomHeadsUpContentView(RemoteViews)} to set the
6560 * corresponding custom views to display.
6561 *
6562 * To use this style with your Notification, feed it to
6563 * {@link Notification.Builder#setStyle(android.app.Notification.Style)} like so:
6564 * <pre class="prettyprint">
6565 * Notification noti = new Notification.Builder()
6566 * .setSmallIcon(R.drawable.ic_stat_player)
6567 * .setLargeIcon(albumArtBitmap))
6568 * .setCustomContentView(contentView);
6569 * .setStyle(<b>new Notification.DecoratedCustomViewStyle()</b>)
6570 * .build();
6571 * </pre>
6572 */
6573 public static class DecoratedCustomViewStyle extends Style {
6574
6575 public DecoratedCustomViewStyle() {
6576 }
6577
Selim Cinek593610c2016-02-16 18:42:57 -08006578 /**
6579 * @hide
6580 */
6581 public boolean displayCustomViewInline() {
6582 return true;
6583 }
6584
6585 /**
6586 * @hide
6587 */
6588 @Override
Selim Cinek7d1009b2017-01-25 15:28:28 -08006589 public RemoteViews makeContentView(boolean increasedHeight) {
Selim Cinek593610c2016-02-16 18:42:57 -08006590 return makeStandardTemplateWithCustomContent(mBuilder.mN.contentView);
6591 }
6592
6593 /**
6594 * @hide
6595 */
6596 @Override
6597 public RemoteViews makeBigContentView() {
6598 return makeDecoratedBigContentView();
6599 }
6600
6601 /**
6602 * @hide
6603 */
6604 @Override
Selim Cinek87ed69b2017-02-09 15:59:43 -08006605 public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
Selim Cinek593610c2016-02-16 18:42:57 -08006606 return makeDecoratedHeadsUpContentView();
6607 }
6608
Selim Cinek593610c2016-02-16 18:42:57 -08006609 private RemoteViews makeDecoratedHeadsUpContentView() {
6610 RemoteViews headsUpContentView = mBuilder.mN.headsUpContentView == null
6611 ? mBuilder.mN.contentView
6612 : mBuilder.mN.headsUpContentView;
6613 if (mBuilder.mActions.size() == 0) {
6614 return makeStandardTemplateWithCustomContent(headsUpContentView);
6615 }
6616 RemoteViews remoteViews = mBuilder.applyStandardTemplateWithActions(
6617 mBuilder.getBigBaseLayoutResource());
Selim Cinek247fa012016-02-18 09:50:48 -08006618 buildIntoRemoteViewContent(remoteViews, headsUpContentView);
Selim Cinek593610c2016-02-16 18:42:57 -08006619 return remoteViews;
6620 }
6621
Selim Cinek593610c2016-02-16 18:42:57 -08006622 private RemoteViews makeStandardTemplateWithCustomContent(RemoteViews customContent) {
6623 RemoteViews remoteViews = mBuilder.applyStandardTemplate(
6624 mBuilder.getBaseLayoutResource());
Selim Cinek247fa012016-02-18 09:50:48 -08006625 buildIntoRemoteViewContent(remoteViews, customContent);
Selim Cinek593610c2016-02-16 18:42:57 -08006626 return remoteViews;
6627 }
6628
Selim Cinek593610c2016-02-16 18:42:57 -08006629 private RemoteViews makeDecoratedBigContentView() {
6630 RemoteViews bigContentView = mBuilder.mN.bigContentView == null
6631 ? mBuilder.mN.contentView
6632 : mBuilder.mN.bigContentView;
6633 if (mBuilder.mActions.size() == 0) {
6634 return makeStandardTemplateWithCustomContent(bigContentView);
6635 }
6636 RemoteViews remoteViews = mBuilder.applyStandardTemplateWithActions(
6637 mBuilder.getBigBaseLayoutResource());
Selim Cinek247fa012016-02-18 09:50:48 -08006638 buildIntoRemoteViewContent(remoteViews, bigContentView);
Selim Cinek593610c2016-02-16 18:42:57 -08006639 return remoteViews;
6640 }
Selim Cinek247fa012016-02-18 09:50:48 -08006641
6642 private void buildIntoRemoteViewContent(RemoteViews remoteViews,
6643 RemoteViews customContent) {
Adrian Roos5081c0d2016-02-26 16:04:19 -08006644 if (customContent != null) {
Selim Cinekf91017e2016-03-14 12:25:09 -07006645 // Need to clone customContent before adding, because otherwise it can no longer be
6646 // parceled independently of remoteViews.
Adrian Roos5081c0d2016-02-26 16:04:19 -08006647 customContent = customContent.clone();
Selim Cinekf91017e2016-03-14 12:25:09 -07006648 remoteViews.removeAllViews(R.id.notification_main_column);
6649 remoteViews.addView(R.id.notification_main_column, customContent);
Adrian Roos5081c0d2016-02-26 16:04:19 -08006650 }
Selim Cinek247fa012016-02-18 09:50:48 -08006651 // also update the end margin if there is an image
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006652 int endMargin = R.dimen.notification_content_margin_end;
Selim Cinek279fa862016-06-14 10:57:25 -07006653 if (mBuilder.mN.hasLargeIcon()) {
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006654 endMargin = R.dimen.notification_content_plus_picture_margin_end;
Selim Cinek247fa012016-02-18 09:50:48 -08006655 }
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006656 remoteViews.setViewLayoutMarginEndDimen(R.id.notification_main_column, endMargin);
Selim Cinek247fa012016-02-18 09:50:48 -08006657 }
Selim Cinek593610c2016-02-16 18:42:57 -08006658 }
6659
Selim Cinek03eb3b72016-02-18 10:39:45 -08006660 /**
6661 * Notification style for media custom views that are decorated by the system
6662 *
6663 * <p>Instead of providing a media notification that is completely custom, a developer can set
6664 * this style and still obtain system decorations like the notification header with the expand
6665 * affordance and actions.
6666 *
6667 * <p>Use {@link android.app.Notification.Builder#setCustomContentView(RemoteViews)},
6668 * {@link android.app.Notification.Builder#setCustomBigContentView(RemoteViews)} and
6669 * {@link android.app.Notification.Builder#setCustomHeadsUpContentView(RemoteViews)} to set the
6670 * corresponding custom views to display.
Selim Cinek99104832017-01-25 14:47:33 -08006671 * <p>
6672 * Contrary to {@link MediaStyle} a developer has to opt-in to the colorizing of the
6673 * notification by using {@link Notification.Builder#setColorized(boolean)}.
6674 * <p>
Selim Cinek03eb3b72016-02-18 10:39:45 -08006675 * To use this style with your Notification, feed it to
6676 * {@link Notification.Builder#setStyle(android.app.Notification.Style)} like so:
6677 * <pre class="prettyprint">
6678 * Notification noti = new Notification.Builder()
6679 * .setSmallIcon(R.drawable.ic_stat_player)
6680 * .setLargeIcon(albumArtBitmap))
6681 * .setCustomContentView(contentView);
6682 * .setStyle(<b>new Notification.DecoratedMediaCustomViewStyle()</b>
6683 * .setMediaSession(mySession))
6684 * .build();
6685 * </pre>
6686 *
6687 * @see android.app.Notification.DecoratedCustomViewStyle
6688 * @see android.app.Notification.MediaStyle
6689 */
6690 public static class DecoratedMediaCustomViewStyle extends MediaStyle {
6691
6692 public DecoratedMediaCustomViewStyle() {
6693 }
6694
Selim Cinek03eb3b72016-02-18 10:39:45 -08006695 /**
6696 * @hide
6697 */
6698 public boolean displayCustomViewInline() {
6699 return true;
6700 }
6701
6702 /**
6703 * @hide
6704 */
6705 @Override
Selim Cinek7d1009b2017-01-25 15:28:28 -08006706 public RemoteViews makeContentView(boolean increasedHeight) {
6707 RemoteViews remoteViews = super.makeContentView(false /* increasedHeight */);
Selim Cinek03eb3b72016-02-18 10:39:45 -08006708 return buildIntoRemoteView(remoteViews, R.id.notification_content_container,
6709 mBuilder.mN.contentView);
6710 }
6711
6712 /**
6713 * @hide
6714 */
6715 @Override
6716 public RemoteViews makeBigContentView() {
6717 RemoteViews customRemoteView = mBuilder.mN.bigContentView != null
6718 ? mBuilder.mN.bigContentView
6719 : mBuilder.mN.contentView;
6720 return makeBigContentViewWithCustomContent(customRemoteView);
6721 }
6722
6723 private RemoteViews makeBigContentViewWithCustomContent(RemoteViews customRemoteView) {
6724 RemoteViews remoteViews = super.makeBigContentView();
6725 if (remoteViews != null) {
6726 return buildIntoRemoteView(remoteViews, R.id.notification_main_column,
6727 customRemoteView);
6728 } else if (customRemoteView != mBuilder.mN.contentView){
Selim Cinek7d1009b2017-01-25 15:28:28 -08006729 remoteViews = super.makeContentView(false /* increasedHeight */);
Selim Cinek03eb3b72016-02-18 10:39:45 -08006730 return buildIntoRemoteView(remoteViews, R.id.notification_content_container,
6731 customRemoteView);
6732 } else {
6733 return null;
6734 }
6735 }
6736
6737 /**
6738 * @hide
6739 */
6740 @Override
Selim Cinek87ed69b2017-02-09 15:59:43 -08006741 public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
Selim Cinek03eb3b72016-02-18 10:39:45 -08006742 RemoteViews customRemoteView = mBuilder.mN.headsUpContentView != null
6743 ? mBuilder.mN.headsUpContentView
6744 : mBuilder.mN.contentView;
6745 return makeBigContentViewWithCustomContent(customRemoteView);
6746 }
6747
6748 private RemoteViews buildIntoRemoteView(RemoteViews remoteViews, int id,
6749 RemoteViews customContent) {
Adrian Roos5081c0d2016-02-26 16:04:19 -08006750 if (customContent != null) {
Selim Cinekf91017e2016-03-14 12:25:09 -07006751 // Need to clone customContent before adding, because otherwise it can no longer be
6752 // parceled independently of remoteViews.
Adrian Roos5081c0d2016-02-26 16:04:19 -08006753 customContent = customContent.clone();
Selim Cinekf91017e2016-03-14 12:25:09 -07006754 remoteViews.removeAllViews(id);
6755 remoteViews.addView(id, customContent);
Adrian Roos5081c0d2016-02-26 16:04:19 -08006756 }
Selim Cinek03eb3b72016-02-18 10:39:45 -08006757 return remoteViews;
6758 }
6759 }
6760
Christoph Studer4600f9b2014-07-22 22:44:43 +02006761 // When adding a new Style subclass here, don't forget to update
6762 // Builder.getNotificationStyleClass.
6763
Griff Hazen61a9e862014-05-22 16:05:19 -07006764 /**
6765 * Extender interface for use with {@link Builder#extend}. Extenders may be used to add
6766 * metadata or change options on a notification builder.
6767 */
6768 public interface Extender {
6769 /**
6770 * Apply this extender to a notification builder.
6771 * @param builder the builder to be modified.
6772 * @return the build object for chaining.
6773 */
6774 public Builder extend(Builder builder);
6775 }
6776
6777 /**
6778 * Helper class to add wearable extensions to notifications.
6779 * <p class="note"> See
6780 * <a href="{@docRoot}wear/notifications/creating.html">Creating Notifications
6781 * for Android Wear</a> for more information on how to use this class.
6782 * <p>
6783 * To create a notification with wearable extensions:
6784 * <ol>
6785 * <li>Create a {@link android.app.Notification.Builder}, setting any desired
6786 * properties.
6787 * <li>Create a {@link android.app.Notification.WearableExtender}.
6788 * <li>Set wearable-specific properties using the
6789 * {@code add} and {@code set} methods of {@link android.app.Notification.WearableExtender}.
6790 * <li>Call {@link android.app.Notification.Builder#extend} to apply the extensions to a
6791 * notification.
6792 * <li>Post the notification to the notification system with the
6793 * {@code NotificationManager.notify(...)} methods.
6794 * </ol>
6795 *
6796 * <pre class="prettyprint">
6797 * Notification notif = new Notification.Builder(mContext)
6798 * .setContentTitle(&quot;New mail from &quot; + sender.toString())
6799 * .setContentText(subject)
6800 * .setSmallIcon(R.drawable.new_mail)
6801 * .extend(new Notification.WearableExtender()
6802 * .setContentIcon(R.drawable.new_mail))
6803 * .build();
6804 * NotificationManager notificationManger =
6805 * (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
6806 * notificationManger.notify(0, notif);</pre>
6807 *
6808 * <p>Wearable extensions can be accessed on an existing notification by using the
6809 * {@code WearableExtender(Notification)} constructor,
6810 * and then using the {@code get} methods to access values.
6811 *
6812 * <pre class="prettyprint">
6813 * Notification.WearableExtender wearableExtender = new Notification.WearableExtender(
6814 * notification);
Griff Hazen14f57992014-05-26 09:07:14 -07006815 * List&lt;Notification&gt; pages = wearableExtender.getPages();</pre>
Griff Hazen61a9e862014-05-22 16:05:19 -07006816 */
6817 public static final class WearableExtender implements Extender {
6818 /**
6819 * Sentinel value for an action index that is unset.
6820 */
6821 public static final int UNSET_ACTION_INDEX = -1;
6822
6823 /**
6824 * Size value for use with {@link #setCustomSizePreset} to show this notification with
6825 * default sizing.
6826 * <p>For custom display notifications created using {@link #setDisplayIntent},
Paul Soulosaa4f4bf2015-08-04 11:59:45 -07006827 * the default is {@link #SIZE_MEDIUM}. All other notifications size automatically based
Griff Hazen61a9e862014-05-22 16:05:19 -07006828 * on their content.
6829 */
6830 public static final int SIZE_DEFAULT = 0;
6831
6832 /**
6833 * Size value for use with {@link #setCustomSizePreset} to show this notification
6834 * with an extra small size.
6835 * <p>This value is only applicable for custom display notifications created using
6836 * {@link #setDisplayIntent}.
6837 */
6838 public static final int SIZE_XSMALL = 1;
6839
6840 /**
6841 * Size value for use with {@link #setCustomSizePreset} to show this notification
6842 * with a small size.
6843 * <p>This value is only applicable for custom display notifications created using
6844 * {@link #setDisplayIntent}.
6845 */
6846 public static final int SIZE_SMALL = 2;
6847
6848 /**
6849 * Size value for use with {@link #setCustomSizePreset} to show this notification
6850 * with a medium size.
6851 * <p>This value is only applicable for custom display notifications created using
6852 * {@link #setDisplayIntent}.
6853 */
6854 public static final int SIZE_MEDIUM = 3;
6855
6856 /**
6857 * Size value for use with {@link #setCustomSizePreset} to show this notification
6858 * with a large size.
6859 * <p>This value is only applicable for custom display notifications created using
6860 * {@link #setDisplayIntent}.
6861 */
6862 public static final int SIZE_LARGE = 4;
6863
Griff Hazend5f11f92014-05-27 15:40:09 -07006864 /**
6865 * Size value for use with {@link #setCustomSizePreset} to show this notification
6866 * full screen.
6867 * <p>This value is only applicable for custom display notifications created using
6868 * {@link #setDisplayIntent}.
6869 */
6870 public static final int SIZE_FULL_SCREEN = 5;
6871
Griff Hazen5f2edfc2014-09-29 16:28:44 -07006872 /**
6873 * Sentinel value for use with {@link #setHintScreenTimeout} to keep the screen on for a
6874 * short amount of time when this notification is displayed on the screen. This
6875 * is the default value.
6876 */
6877 public static final int SCREEN_TIMEOUT_SHORT = 0;
6878
6879 /**
6880 * Sentinel value for use with {@link #setHintScreenTimeout} to keep the screen on
6881 * for a longer amount of time when this notification is displayed on the screen.
6882 */
6883 public static final int SCREEN_TIMEOUT_LONG = -1;
6884
Griff Hazen61a9e862014-05-22 16:05:19 -07006885 /** Notification extra which contains wearable extensions */
6886 private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS";
6887
Pete Gastaf6781d2014-10-07 15:17:05 -04006888 // Keys within EXTRA_WEARABLE_EXTENSIONS for wearable options.
Griff Hazen61a9e862014-05-22 16:05:19 -07006889 private static final String KEY_ACTIONS = "actions";
6890 private static final String KEY_FLAGS = "flags";
6891 private static final String KEY_DISPLAY_INTENT = "displayIntent";
6892 private static final String KEY_PAGES = "pages";
6893 private static final String KEY_BACKGROUND = "background";
6894 private static final String KEY_CONTENT_ICON = "contentIcon";
6895 private static final String KEY_CONTENT_ICON_GRAVITY = "contentIconGravity";
6896 private static final String KEY_CONTENT_ACTION_INDEX = "contentActionIndex";
6897 private static final String KEY_CUSTOM_SIZE_PRESET = "customSizePreset";
6898 private static final String KEY_CUSTOM_CONTENT_HEIGHT = "customContentHeight";
6899 private static final String KEY_GRAVITY = "gravity";
Griff Hazen5f2edfc2014-09-29 16:28:44 -07006900 private static final String KEY_HINT_SCREEN_TIMEOUT = "hintScreenTimeout";
Nadia Benbernou948627e2016-04-14 14:41:08 -04006901 private static final String KEY_DISMISSAL_ID = "dismissalId";
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04006902 private static final String KEY_BRIDGE_TAG = "bridgeTag";
Griff Hazen61a9e862014-05-22 16:05:19 -07006903
6904 // Flags bitwise-ored to mFlags
6905 private static final int FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE = 0x1;
6906 private static final int FLAG_HINT_HIDE_ICON = 1 << 1;
6907 private static final int FLAG_HINT_SHOW_BACKGROUND_ONLY = 1 << 2;
6908 private static final int FLAG_START_SCROLL_BOTTOM = 1 << 3;
Griff Hazen5f2edfc2014-09-29 16:28:44 -07006909 private static final int FLAG_HINT_AVOID_BACKGROUND_CLIPPING = 1 << 4;
Alex Hills4bcb06b2016-04-05 14:26:25 -04006910 private static final int FLAG_BIG_PICTURE_AMBIENT = 1 << 5;
Alex Hills9ab3a232016-04-05 14:54:56 -04006911 private static final int FLAG_HINT_CONTENT_INTENT_LAUNCHES_ACTIVITY = 1 << 6;
Griff Hazen61a9e862014-05-22 16:05:19 -07006912
6913 // Default value for flags integer
6914 private static final int DEFAULT_FLAGS = FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE;
6915
6916 private static final int DEFAULT_CONTENT_ICON_GRAVITY = Gravity.END;
6917 private static final int DEFAULT_GRAVITY = Gravity.BOTTOM;
6918
6919 private ArrayList<Action> mActions = new ArrayList<Action>();
6920 private int mFlags = DEFAULT_FLAGS;
6921 private PendingIntent mDisplayIntent;
6922 private ArrayList<Notification> mPages = new ArrayList<Notification>();
6923 private Bitmap mBackground;
6924 private int mContentIcon;
6925 private int mContentIconGravity = DEFAULT_CONTENT_ICON_GRAVITY;
6926 private int mContentActionIndex = UNSET_ACTION_INDEX;
6927 private int mCustomSizePreset = SIZE_DEFAULT;
6928 private int mCustomContentHeight;
6929 private int mGravity = DEFAULT_GRAVITY;
Griff Hazen5f2edfc2014-09-29 16:28:44 -07006930 private int mHintScreenTimeout;
Nadia Benbernou948627e2016-04-14 14:41:08 -04006931 private String mDismissalId;
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04006932 private String mBridgeTag;
Griff Hazen61a9e862014-05-22 16:05:19 -07006933
6934 /**
6935 * Create a {@link android.app.Notification.WearableExtender} with default
6936 * options.
6937 */
6938 public WearableExtender() {
6939 }
6940
6941 public WearableExtender(Notification notif) {
6942 Bundle wearableBundle = notif.extras.getBundle(EXTRA_WEARABLE_EXTENSIONS);
6943 if (wearableBundle != null) {
6944 List<Action> actions = wearableBundle.getParcelableArrayList(KEY_ACTIONS);
6945 if (actions != null) {
6946 mActions.addAll(actions);
6947 }
6948
6949 mFlags = wearableBundle.getInt(KEY_FLAGS, DEFAULT_FLAGS);
6950 mDisplayIntent = wearableBundle.getParcelable(KEY_DISPLAY_INTENT);
6951
6952 Notification[] pages = getNotificationArrayFromBundle(
6953 wearableBundle, KEY_PAGES);
6954 if (pages != null) {
6955 Collections.addAll(mPages, pages);
6956 }
6957
6958 mBackground = wearableBundle.getParcelable(KEY_BACKGROUND);
6959 mContentIcon = wearableBundle.getInt(KEY_CONTENT_ICON);
6960 mContentIconGravity = wearableBundle.getInt(KEY_CONTENT_ICON_GRAVITY,
6961 DEFAULT_CONTENT_ICON_GRAVITY);
6962 mContentActionIndex = wearableBundle.getInt(KEY_CONTENT_ACTION_INDEX,
6963 UNSET_ACTION_INDEX);
6964 mCustomSizePreset = wearableBundle.getInt(KEY_CUSTOM_SIZE_PRESET,
6965 SIZE_DEFAULT);
6966 mCustomContentHeight = wearableBundle.getInt(KEY_CUSTOM_CONTENT_HEIGHT);
6967 mGravity = wearableBundle.getInt(KEY_GRAVITY, DEFAULT_GRAVITY);
Griff Hazen5f2edfc2014-09-29 16:28:44 -07006968 mHintScreenTimeout = wearableBundle.getInt(KEY_HINT_SCREEN_TIMEOUT);
Nadia Benbernou948627e2016-04-14 14:41:08 -04006969 mDismissalId = wearableBundle.getString(KEY_DISMISSAL_ID);
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04006970 mBridgeTag = wearableBundle.getString(KEY_BRIDGE_TAG);
Griff Hazen61a9e862014-05-22 16:05:19 -07006971 }
6972 }
6973
6974 /**
6975 * Apply wearable extensions to a notification that is being built. This is typically
6976 * called by the {@link android.app.Notification.Builder#extend} method of
6977 * {@link android.app.Notification.Builder}.
6978 */
6979 @Override
6980 public Notification.Builder extend(Notification.Builder builder) {
6981 Bundle wearableBundle = new Bundle();
6982
6983 if (!mActions.isEmpty()) {
6984 wearableBundle.putParcelableArrayList(KEY_ACTIONS, mActions);
6985 }
6986 if (mFlags != DEFAULT_FLAGS) {
6987 wearableBundle.putInt(KEY_FLAGS, mFlags);
6988 }
6989 if (mDisplayIntent != null) {
6990 wearableBundle.putParcelable(KEY_DISPLAY_INTENT, mDisplayIntent);
6991 }
6992 if (!mPages.isEmpty()) {
6993 wearableBundle.putParcelableArray(KEY_PAGES, mPages.toArray(
6994 new Notification[mPages.size()]));
6995 }
6996 if (mBackground != null) {
6997 wearableBundle.putParcelable(KEY_BACKGROUND, mBackground);
6998 }
6999 if (mContentIcon != 0) {
7000 wearableBundle.putInt(KEY_CONTENT_ICON, mContentIcon);
7001 }
7002 if (mContentIconGravity != DEFAULT_CONTENT_ICON_GRAVITY) {
7003 wearableBundle.putInt(KEY_CONTENT_ICON_GRAVITY, mContentIconGravity);
7004 }
7005 if (mContentActionIndex != UNSET_ACTION_INDEX) {
7006 wearableBundle.putInt(KEY_CONTENT_ACTION_INDEX,
7007 mContentActionIndex);
7008 }
7009 if (mCustomSizePreset != SIZE_DEFAULT) {
7010 wearableBundle.putInt(KEY_CUSTOM_SIZE_PRESET, mCustomSizePreset);
7011 }
7012 if (mCustomContentHeight != 0) {
7013 wearableBundle.putInt(KEY_CUSTOM_CONTENT_HEIGHT, mCustomContentHeight);
7014 }
7015 if (mGravity != DEFAULT_GRAVITY) {
7016 wearableBundle.putInt(KEY_GRAVITY, mGravity);
7017 }
Griff Hazen5f2edfc2014-09-29 16:28:44 -07007018 if (mHintScreenTimeout != 0) {
7019 wearableBundle.putInt(KEY_HINT_SCREEN_TIMEOUT, mHintScreenTimeout);
7020 }
Nadia Benbernou948627e2016-04-14 14:41:08 -04007021 if (mDismissalId != null) {
7022 wearableBundle.putString(KEY_DISMISSAL_ID, mDismissalId);
7023 }
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04007024 if (mBridgeTag != null) {
7025 wearableBundle.putString(KEY_BRIDGE_TAG, mBridgeTag);
7026 }
Griff Hazen61a9e862014-05-22 16:05:19 -07007027
7028 builder.getExtras().putBundle(EXTRA_WEARABLE_EXTENSIONS, wearableBundle);
7029 return builder;
7030 }
7031
7032 @Override
7033 public WearableExtender clone() {
7034 WearableExtender that = new WearableExtender();
7035 that.mActions = new ArrayList<Action>(this.mActions);
7036 that.mFlags = this.mFlags;
7037 that.mDisplayIntent = this.mDisplayIntent;
7038 that.mPages = new ArrayList<Notification>(this.mPages);
7039 that.mBackground = this.mBackground;
7040 that.mContentIcon = this.mContentIcon;
7041 that.mContentIconGravity = this.mContentIconGravity;
7042 that.mContentActionIndex = this.mContentActionIndex;
7043 that.mCustomSizePreset = this.mCustomSizePreset;
7044 that.mCustomContentHeight = this.mCustomContentHeight;
7045 that.mGravity = this.mGravity;
Griff Hazen5f2edfc2014-09-29 16:28:44 -07007046 that.mHintScreenTimeout = this.mHintScreenTimeout;
Nadia Benbernou948627e2016-04-14 14:41:08 -04007047 that.mDismissalId = this.mDismissalId;
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04007048 that.mBridgeTag = this.mBridgeTag;
Griff Hazen61a9e862014-05-22 16:05:19 -07007049 return that;
7050 }
7051
7052 /**
7053 * Add a wearable action to this notification.
7054 *
7055 * <p>When wearable actions are added using this method, the set of actions that
7056 * show on a wearable device splits from devices that only show actions added
7057 * using {@link android.app.Notification.Builder#addAction}. This allows for customization
7058 * of which actions display on different devices.
7059 *
7060 * @param action the action to add to this notification
7061 * @return this object for method chaining
7062 * @see android.app.Notification.Action
7063 */
7064 public WearableExtender addAction(Action action) {
7065 mActions.add(action);
7066 return this;
7067 }
7068
7069 /**
7070 * Adds wearable actions to this notification.
7071 *
7072 * <p>When wearable actions are added using this method, the set of actions that
7073 * show on a wearable device splits from devices that only show actions added
7074 * using {@link android.app.Notification.Builder#addAction}. This allows for customization
7075 * of which actions display on different devices.
7076 *
7077 * @param actions the actions to add to this notification
7078 * @return this object for method chaining
7079 * @see android.app.Notification.Action
7080 */
7081 public WearableExtender addActions(List<Action> actions) {
7082 mActions.addAll(actions);
7083 return this;
7084 }
7085
7086 /**
7087 * Clear all wearable actions present on this builder.
7088 * @return this object for method chaining.
7089 * @see #addAction
7090 */
7091 public WearableExtender clearActions() {
7092 mActions.clear();
7093 return this;
7094 }
7095
7096 /**
7097 * Get the wearable actions present on this notification.
7098 */
7099 public List<Action> getActions() {
7100 return mActions;
7101 }
7102
7103 /**
7104 * Set an intent to launch inside of an activity view when displaying
Griff Hazen14f57992014-05-26 09:07:14 -07007105 * this notification. The {@link PendingIntent} provided should be for an activity.
7106 *
7107 * <pre class="prettyprint">
7108 * Intent displayIntent = new Intent(context, MyDisplayActivity.class);
7109 * PendingIntent displayPendingIntent = PendingIntent.getActivity(context,
7110 * 0, displayIntent, PendingIntent.FLAG_UPDATE_CURRENT);
7111 * Notification notif = new Notification.Builder(context)
7112 * .extend(new Notification.WearableExtender()
7113 * .setDisplayIntent(displayPendingIntent)
7114 * .setCustomSizePreset(Notification.WearableExtender.SIZE_MEDIUM))
7115 * .build();</pre>
7116 *
7117 * <p>The activity to launch needs to allow embedding, must be exported, and
Griff Hazen831ca9d2014-06-17 00:38:38 -07007118 * should have an empty task affinity. It is also recommended to use the device
7119 * default light theme.
Griff Hazen14f57992014-05-26 09:07:14 -07007120 *
7121 * <p>Example AndroidManifest.xml entry:
7122 * <pre class="prettyprint">
7123 * &lt;activity android:name=&quot;com.example.MyDisplayActivity&quot;
7124 * android:exported=&quot;true&quot;
7125 * android:allowEmbedded=&quot;true&quot;
Griff Hazen831ca9d2014-06-17 00:38:38 -07007126 * android:taskAffinity=&quot;&quot;
7127 * android:theme=&quot;@android:style/Theme.DeviceDefault.Light&quot; /&gt;</pre>
Griff Hazen61a9e862014-05-22 16:05:19 -07007128 *
7129 * @param intent the {@link PendingIntent} for an activity
7130 * @return this object for method chaining
7131 * @see android.app.Notification.WearableExtender#getDisplayIntent
7132 */
7133 public WearableExtender setDisplayIntent(PendingIntent intent) {
7134 mDisplayIntent = intent;
7135 return this;
7136 }
7137
7138 /**
7139 * Get the intent to launch inside of an activity view when displaying this
7140 * notification. This {@code PendingIntent} should be for an activity.
7141 */
7142 public PendingIntent getDisplayIntent() {
7143 return mDisplayIntent;
7144 }
7145
7146 /**
7147 * Add an additional page of content to display with this notification. The current
7148 * notification forms the first page, and pages added using this function form
7149 * subsequent pages. This field can be used to separate a notification into multiple
7150 * sections.
7151 *
7152 * @param page the notification to add as another page
7153 * @return this object for method chaining
7154 * @see android.app.Notification.WearableExtender#getPages
7155 */
7156 public WearableExtender addPage(Notification page) {
7157 mPages.add(page);
7158 return this;
7159 }
7160
7161 /**
7162 * Add additional pages of content to display with this notification. The current
7163 * notification forms the first page, and pages added using this function form
7164 * subsequent pages. This field can be used to separate a notification into multiple
7165 * sections.
7166 *
7167 * @param pages a list of notifications
7168 * @return this object for method chaining
7169 * @see android.app.Notification.WearableExtender#getPages
7170 */
7171 public WearableExtender addPages(List<Notification> pages) {
7172 mPages.addAll(pages);
7173 return this;
7174 }
7175
7176 /**
7177 * Clear all additional pages present on this builder.
7178 * @return this object for method chaining.
7179 * @see #addPage
7180 */
7181 public WearableExtender clearPages() {
7182 mPages.clear();
7183 return this;
7184 }
7185
7186 /**
7187 * Get the array of additional pages of content for displaying this notification. The
7188 * current notification forms the first page, and elements within this array form
7189 * subsequent pages. This field can be used to separate a notification into multiple
7190 * sections.
7191 * @return the pages for this notification
7192 */
7193 public List<Notification> getPages() {
7194 return mPages;
7195 }
7196
7197 /**
7198 * Set a background image to be displayed behind the notification content.
7199 * Contrary to the {@link android.app.Notification.BigPictureStyle}, this background
7200 * will work with any notification style.
7201 *
7202 * @param background the background bitmap
7203 * @return this object for method chaining
7204 * @see android.app.Notification.WearableExtender#getBackground
7205 */
7206 public WearableExtender setBackground(Bitmap background) {
7207 mBackground = background;
7208 return this;
7209 }
7210
7211 /**
7212 * Get a background image to be displayed behind the notification content.
7213 * Contrary to the {@link android.app.Notification.BigPictureStyle}, this background
7214 * will work with any notification style.
7215 *
7216 * @return the background image
7217 * @see android.app.Notification.WearableExtender#setBackground
7218 */
7219 public Bitmap getBackground() {
7220 return mBackground;
7221 }
7222
7223 /**
7224 * Set an icon that goes with the content of this notification.
7225 */
7226 public WearableExtender setContentIcon(int icon) {
7227 mContentIcon = icon;
7228 return this;
7229 }
7230
7231 /**
7232 * Get an icon that goes with the content of this notification.
7233 */
7234 public int getContentIcon() {
7235 return mContentIcon;
7236 }
7237
7238 /**
7239 * Set the gravity that the content icon should have within the notification display.
7240 * Supported values include {@link android.view.Gravity#START} and
7241 * {@link android.view.Gravity#END}. The default value is {@link android.view.Gravity#END}.
7242 * @see #setContentIcon
7243 */
7244 public WearableExtender setContentIconGravity(int contentIconGravity) {
7245 mContentIconGravity = contentIconGravity;
7246 return this;
7247 }
7248
7249 /**
7250 * Get the gravity that the content icon should have within the notification display.
7251 * Supported values include {@link android.view.Gravity#START} and
7252 * {@link android.view.Gravity#END}. The default value is {@link android.view.Gravity#END}.
7253 * @see #getContentIcon
7254 */
7255 public int getContentIconGravity() {
7256 return mContentIconGravity;
7257 }
7258
7259 /**
7260 * Set an action from this notification's actions to be clickable with the content of
Griff Hazen14f57992014-05-26 09:07:14 -07007261 * this notification. This action will no longer display separately from the
7262 * notification's content.
7263 *
Griff Hazenca48d352014-05-28 22:37:13 -07007264 * <p>For notifications with multiple pages, child pages can also have content actions
Griff Hazen14f57992014-05-26 09:07:14 -07007265 * set, although the list of available actions comes from the main notification and not
7266 * from the child page's notification.
7267 *
7268 * @param actionIndex The index of the action to hoist onto the current notification page.
7269 * If wearable actions were added to the main notification, this index
7270 * will apply to that list, otherwise it will apply to the regular
7271 * actions list.
Griff Hazen61a9e862014-05-22 16:05:19 -07007272 */
7273 public WearableExtender setContentAction(int actionIndex) {
7274 mContentActionIndex = actionIndex;
7275 return this;
7276 }
7277
7278 /**
Griff Hazenca48d352014-05-28 22:37:13 -07007279 * Get the index of the notification action, if any, that was specified as being clickable
7280 * with the content of this notification. This action will no longer display separately
Griff Hazen14f57992014-05-26 09:07:14 -07007281 * from the notification's content.
Griff Hazen61a9e862014-05-22 16:05:19 -07007282 *
Griff Hazenca48d352014-05-28 22:37:13 -07007283 * <p>For notifications with multiple pages, child pages can also have content actions
Griff Hazen14f57992014-05-26 09:07:14 -07007284 * set, although the list of available actions comes from the main notification and not
7285 * from the child page's notification.
7286 *
7287 * <p>If wearable specific actions were added to the main notification, this index will
7288 * apply to that list, otherwise it will apply to the regular actions list.
Griff Hazenca48d352014-05-28 22:37:13 -07007289 *
7290 * @return the action index or {@link #UNSET_ACTION_INDEX} if no action was selected.
Griff Hazen61a9e862014-05-22 16:05:19 -07007291 */
7292 public int getContentAction() {
7293 return mContentActionIndex;
7294 }
7295
7296 /**
7297 * Set the gravity that this notification should have within the available viewport space.
7298 * Supported values include {@link android.view.Gravity#TOP},
7299 * {@link android.view.Gravity#CENTER_VERTICAL} and {@link android.view.Gravity#BOTTOM}.
7300 * The default value is {@link android.view.Gravity#BOTTOM}.
7301 */
7302 public WearableExtender setGravity(int gravity) {
7303 mGravity = gravity;
7304 return this;
7305 }
7306
7307 /**
7308 * Get the gravity that this notification should have within the available viewport space.
7309 * Supported values include {@link android.view.Gravity#TOP},
7310 * {@link android.view.Gravity#CENTER_VERTICAL} and {@link android.view.Gravity#BOTTOM}.
7311 * The default value is {@link android.view.Gravity#BOTTOM}.
7312 */
7313 public int getGravity() {
7314 return mGravity;
7315 }
7316
7317 /**
7318 * Set the custom size preset for the display of this notification out of the available
7319 * presets found in {@link android.app.Notification.WearableExtender}, e.g.
7320 * {@link #SIZE_LARGE}.
7321 * <p>Some custom size presets are only applicable for custom display notifications created
7322 * using {@link android.app.Notification.WearableExtender#setDisplayIntent}. Check the
7323 * documentation for the preset in question. See also
7324 * {@link #setCustomContentHeight} and {@link #getCustomSizePreset}.
7325 */
7326 public WearableExtender setCustomSizePreset(int sizePreset) {
7327 mCustomSizePreset = sizePreset;
7328 return this;
7329 }
7330
7331 /**
7332 * Get the custom size preset for the display of this notification out of the available
7333 * presets found in {@link android.app.Notification.WearableExtender}, e.g.
7334 * {@link #SIZE_LARGE}.
7335 * <p>Some custom size presets are only applicable for custom display notifications created
7336 * using {@link #setDisplayIntent}. Check the documentation for the preset in question.
7337 * See also {@link #setCustomContentHeight} and {@link #setCustomSizePreset}.
7338 */
7339 public int getCustomSizePreset() {
7340 return mCustomSizePreset;
7341 }
7342
7343 /**
7344 * Set the custom height in pixels for the display of this notification's content.
7345 * <p>This option is only available for custom display notifications created
7346 * using {@link android.app.Notification.WearableExtender#setDisplayIntent}. See also
7347 * {@link android.app.Notification.WearableExtender#setCustomSizePreset} and
7348 * {@link #getCustomContentHeight}.
7349 */
7350 public WearableExtender setCustomContentHeight(int height) {
7351 mCustomContentHeight = height;
7352 return this;
7353 }
7354
7355 /**
7356 * Get the custom height in pixels for the display of this notification's content.
7357 * <p>This option is only available for custom display notifications created
7358 * using {@link #setDisplayIntent}. See also {@link #setCustomSizePreset} and
7359 * {@link #setCustomContentHeight}.
7360 */
7361 public int getCustomContentHeight() {
7362 return mCustomContentHeight;
7363 }
7364
7365 /**
7366 * Set whether the scrolling position for the contents of this notification should start
7367 * at the bottom of the contents instead of the top when the contents are too long to
7368 * display within the screen. Default is false (start scroll at the top).
7369 */
7370 public WearableExtender setStartScrollBottom(boolean startScrollBottom) {
7371 setFlag(FLAG_START_SCROLL_BOTTOM, startScrollBottom);
7372 return this;
7373 }
7374
7375 /**
7376 * Get whether the scrolling position for the contents of this notification should start
7377 * at the bottom of the contents instead of the top when the contents are too long to
7378 * display within the screen. Default is false (start scroll at the top).
7379 */
7380 public boolean getStartScrollBottom() {
7381 return (mFlags & FLAG_START_SCROLL_BOTTOM) != 0;
7382 }
7383
7384 /**
7385 * Set whether the content intent is available when the wearable device is not connected
7386 * to a companion device. The user can still trigger this intent when the wearable device
7387 * is offline, but a visual hint will indicate that the content intent may not be available.
7388 * Defaults to true.
7389 */
7390 public WearableExtender setContentIntentAvailableOffline(
7391 boolean contentIntentAvailableOffline) {
7392 setFlag(FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE, contentIntentAvailableOffline);
7393 return this;
7394 }
7395
7396 /**
7397 * Get whether the content intent is available when the wearable device is not connected
7398 * to a companion device. The user can still trigger this intent when the wearable device
7399 * is offline, but a visual hint will indicate that the content intent may not be available.
7400 * Defaults to true.
7401 */
7402 public boolean getContentIntentAvailableOffline() {
7403 return (mFlags & FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE) != 0;
7404 }
7405
7406 /**
7407 * Set a hint that this notification's icon should not be displayed.
7408 * @param hintHideIcon {@code true} to hide the icon, {@code false} otherwise.
7409 * @return this object for method chaining
7410 */
7411 public WearableExtender setHintHideIcon(boolean hintHideIcon) {
7412 setFlag(FLAG_HINT_HIDE_ICON, hintHideIcon);
7413 return this;
7414 }
7415
7416 /**
7417 * Get a hint that this notification's icon should not be displayed.
7418 * @return {@code true} if this icon should not be displayed, false otherwise.
7419 * The default value is {@code false} if this was never set.
7420 */
7421 public boolean getHintHideIcon() {
7422 return (mFlags & FLAG_HINT_HIDE_ICON) != 0;
7423 }
7424
7425 /**
7426 * Set a visual hint that only the background image of this notification should be
7427 * displayed, and other semantic content should be hidden. This hint is only applicable
7428 * to sub-pages added using {@link #addPage}.
7429 */
7430 public WearableExtender setHintShowBackgroundOnly(boolean hintShowBackgroundOnly) {
7431 setFlag(FLAG_HINT_SHOW_BACKGROUND_ONLY, hintShowBackgroundOnly);
7432 return this;
7433 }
7434
7435 /**
7436 * Get a visual hint that only the background image of this notification should be
7437 * displayed, and other semantic content should be hidden. This hint is only applicable
7438 * to sub-pages added using {@link android.app.Notification.WearableExtender#addPage}.
7439 */
7440 public boolean getHintShowBackgroundOnly() {
7441 return (mFlags & FLAG_HINT_SHOW_BACKGROUND_ONLY) != 0;
7442 }
7443
Griff Hazen5f2edfc2014-09-29 16:28:44 -07007444 /**
Griff Hazen9c5be4e2014-11-17 17:47:50 -08007445 * Set a hint that this notification's background should not be clipped if possible,
7446 * and should instead be resized to fully display on the screen, retaining the aspect
7447 * ratio of the image. This can be useful for images like barcodes or qr codes.
Griff Hazen5f2edfc2014-09-29 16:28:44 -07007448 * @param hintAvoidBackgroundClipping {@code true} to avoid clipping if possible.
7449 * @return this object for method chaining
7450 */
7451 public WearableExtender setHintAvoidBackgroundClipping(
7452 boolean hintAvoidBackgroundClipping) {
7453 setFlag(FLAG_HINT_AVOID_BACKGROUND_CLIPPING, hintAvoidBackgroundClipping);
7454 return this;
7455 }
7456
7457 /**
Griff Hazen9c5be4e2014-11-17 17:47:50 -08007458 * Get a hint that this notification's background should not be clipped if possible,
7459 * and should instead be resized to fully display on the screen, retaining the aspect
7460 * ratio of the image. This can be useful for images like barcodes or qr codes.
Griff Hazen5f2edfc2014-09-29 16:28:44 -07007461 * @return {@code true} if it's ok if the background is clipped on the screen, false
7462 * otherwise. The default value is {@code false} if this was never set.
7463 */
7464 public boolean getHintAvoidBackgroundClipping() {
7465 return (mFlags & FLAG_HINT_AVOID_BACKGROUND_CLIPPING) != 0;
7466 }
7467
7468 /**
7469 * Set a hint that the screen should remain on for at least this duration when
7470 * this notification is displayed on the screen.
7471 * @param timeout The requested screen timeout in milliseconds. Can also be either
7472 * {@link #SCREEN_TIMEOUT_SHORT} or {@link #SCREEN_TIMEOUT_LONG}.
7473 * @return this object for method chaining
7474 */
7475 public WearableExtender setHintScreenTimeout(int timeout) {
7476 mHintScreenTimeout = timeout;
7477 return this;
7478 }
7479
7480 /**
7481 * Get the duration, in milliseconds, that the screen should remain on for
7482 * when this notification is displayed.
7483 * @return the duration in milliseconds if > 0, or either one of the sentinel values
7484 * {@link #SCREEN_TIMEOUT_SHORT} or {@link #SCREEN_TIMEOUT_LONG}.
7485 */
7486 public int getHintScreenTimeout() {
7487 return mHintScreenTimeout;
7488 }
7489
Alex Hills9ab3a232016-04-05 14:54:56 -04007490 /**
Alex Hills4bcb06b2016-04-05 14:26:25 -04007491 * Set a hint that this notification's {@link BigPictureStyle} (if present) should be
7492 * converted to low-bit and displayed in ambient mode, especially useful for barcodes and
7493 * qr codes, as well as other simple black-and-white tickets.
7494 * @param hintAmbientBigPicture {@code true} to enable converstion and ambient.
7495 * @return this object for method chaining
7496 */
7497 public WearableExtender setHintAmbientBigPicture(boolean hintAmbientBigPicture) {
7498 setFlag(FLAG_BIG_PICTURE_AMBIENT, hintAmbientBigPicture);
7499 return this;
7500 }
7501
7502 /**
7503 * Get a hint that this notification's {@link BigPictureStyle} (if present) should be
7504 * converted to low-bit and displayed in ambient mode, especially useful for barcodes and
7505 * qr codes, as well as other simple black-and-white tickets.
7506 * @return {@code true} if it should be displayed in ambient, false otherwise
7507 * otherwise. The default value is {@code false} if this was never set.
7508 */
7509 public boolean getHintAmbientBigPicture() {
7510 return (mFlags & FLAG_BIG_PICTURE_AMBIENT) != 0;
7511 }
7512
7513 /**
Alex Hills9ab3a232016-04-05 14:54:56 -04007514 * Set a hint that this notification's content intent will launch an {@link Activity}
7515 * directly, telling the platform that it can generate the appropriate transitions.
7516 * @param hintContentIntentLaunchesActivity {@code true} if the content intent will launch
7517 * an activity and transitions should be generated, false otherwise.
7518 * @return this object for method chaining
7519 */
7520 public WearableExtender setHintContentIntentLaunchesActivity(
7521 boolean hintContentIntentLaunchesActivity) {
7522 setFlag(FLAG_HINT_CONTENT_INTENT_LAUNCHES_ACTIVITY, hintContentIntentLaunchesActivity);
7523 return this;
7524 }
7525
7526 /**
7527 * Get a hint that this notification's content intent will launch an {@link Activity}
7528 * directly, telling the platform that it can generate the appropriate transitions
7529 * @return {@code true} if the content intent will launch an activity and transitions should
7530 * be generated, false otherwise. The default value is {@code false} if this was never set.
7531 */
7532 public boolean getHintContentIntentLaunchesActivity() {
7533 return (mFlags & FLAG_HINT_CONTENT_INTENT_LAUNCHES_ACTIVITY) != 0;
7534 }
7535
Nadia Benbernou948627e2016-04-14 14:41:08 -04007536 /**
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04007537 * Sets the dismissal id for this notification. If a notification is posted with a
7538 * dismissal id, then when that notification is canceled, notifications on other wearables
7539 * and the paired Android phone having that same dismissal id will also be canceled. See
Nadia Benbernou948627e2016-04-14 14:41:08 -04007540 * <a href="{@docRoot}wear/notifications/index.html">Adding Wearable Features to
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04007541 * Notifications</a> for more information.
Nadia Benbernou948627e2016-04-14 14:41:08 -04007542 * @param dismissalId the dismissal id of the notification.
7543 * @return this object for method chaining
7544 */
7545 public WearableExtender setDismissalId(String dismissalId) {
7546 mDismissalId = dismissalId;
7547 return this;
7548 }
7549
7550 /**
7551 * Returns the dismissal id of the notification.
7552 * @return the dismissal id of the notification or null if it has not been set.
7553 */
7554 public String getDismissalId() {
7555 return mDismissalId;
7556 }
7557
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04007558 /**
7559 * Sets a bridge tag for this notification. A bridge tag can be set for notifications
7560 * posted from a phone to provide finer-grained control on what notifications are bridged
7561 * to wearables. See <a href="{@docRoot}wear/notifications/index.html">Adding Wearable
7562 * Features to Notifications</a> for more information.
7563 * @param bridgeTag the bridge tag of the notification.
7564 * @return this object for method chaining
7565 */
7566 public WearableExtender setBridgeTag(String bridgeTag) {
7567 mBridgeTag = bridgeTag;
7568 return this;
7569 }
7570
7571 /**
7572 * Returns the bridge tag of the notification.
7573 * @return the bridge tag or null if not present.
7574 */
7575 public String getBridgeTag() {
7576 return mBridgeTag;
7577 }
7578
Griff Hazen61a9e862014-05-22 16:05:19 -07007579 private void setFlag(int mask, boolean value) {
7580 if (value) {
7581 mFlags |= mask;
7582 } else {
7583 mFlags &= ~mask;
7584 }
7585 }
7586 }
7587
7588 /**
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08007589 * <p>Helper class to add Android Auto extensions to notifications. To create a notification
7590 * with car extensions:
7591 *
7592 * <ol>
7593 * <li>Create an {@link Notification.Builder}, setting any desired
7594 * properties.
7595 * <li>Create a {@link CarExtender}.
7596 * <li>Set car-specific properties using the {@code add} and {@code set} methods of
7597 * {@link CarExtender}.
7598 * <li>Call {@link Notification.Builder#extend(Notification.Extender)}
7599 * to apply the extensions to a notification.
7600 * </ol>
7601 *
7602 * <pre class="prettyprint">
7603 * Notification notification = new Notification.Builder(context)
7604 * ...
7605 * .extend(new CarExtender()
7606 * .set*(...))
7607 * .build();
7608 * </pre>
7609 *
7610 * <p>Car extensions can be accessed on an existing notification by using the
7611 * {@code CarExtender(Notification)} constructor, and then using the {@code get} methods
7612 * to access values.
7613 */
7614 public static final class CarExtender implements Extender {
7615 private static final String TAG = "CarExtender";
7616
7617 private static final String EXTRA_CAR_EXTENDER = "android.car.EXTENSIONS";
7618 private static final String EXTRA_LARGE_ICON = "large_icon";
7619 private static final String EXTRA_CONVERSATION = "car_conversation";
7620 private static final String EXTRA_COLOR = "app_color";
7621
7622 private Bitmap mLargeIcon;
7623 private UnreadConversation mUnreadConversation;
7624 private int mColor = Notification.COLOR_DEFAULT;
7625
7626 /**
7627 * Create a {@link CarExtender} with default options.
7628 */
7629 public CarExtender() {
7630 }
7631
7632 /**
7633 * Create a {@link CarExtender} from the CarExtender options of an existing Notification.
7634 *
7635 * @param notif The notification from which to copy options.
7636 */
7637 public CarExtender(Notification notif) {
7638 Bundle carBundle = notif.extras == null ?
7639 null : notif.extras.getBundle(EXTRA_CAR_EXTENDER);
7640 if (carBundle != null) {
7641 mLargeIcon = carBundle.getParcelable(EXTRA_LARGE_ICON);
7642 mColor = carBundle.getInt(EXTRA_COLOR, Notification.COLOR_DEFAULT);
7643
7644 Bundle b = carBundle.getBundle(EXTRA_CONVERSATION);
7645 mUnreadConversation = UnreadConversation.getUnreadConversationFromBundle(b);
7646 }
7647 }
7648
7649 /**
7650 * Apply car extensions to a notification that is being built. This is typically called by
7651 * the {@link Notification.Builder#extend(Notification.Extender)}
7652 * method of {@link Notification.Builder}.
7653 */
7654 @Override
7655 public Notification.Builder extend(Notification.Builder builder) {
7656 Bundle carExtensions = new Bundle();
7657
7658 if (mLargeIcon != null) {
7659 carExtensions.putParcelable(EXTRA_LARGE_ICON, mLargeIcon);
7660 }
7661 if (mColor != Notification.COLOR_DEFAULT) {
7662 carExtensions.putInt(EXTRA_COLOR, mColor);
7663 }
7664
7665 if (mUnreadConversation != null) {
7666 Bundle b = mUnreadConversation.getBundleForUnreadConversation();
7667 carExtensions.putBundle(EXTRA_CONVERSATION, b);
7668 }
7669
7670 builder.getExtras().putBundle(EXTRA_CAR_EXTENDER, carExtensions);
7671 return builder;
7672 }
7673
7674 /**
7675 * Sets the accent color to use when Android Auto presents the notification.
7676 *
7677 * Android Auto uses the color set with {@link Notification.Builder#setColor(int)}
7678 * to accent the displayed notification. However, not all colors are acceptable in an
7679 * automotive setting. This method can be used to override the color provided in the
7680 * notification in such a situation.
7681 */
Tor Norbye80756e32015-03-02 09:39:27 -08007682 public CarExtender setColor(@ColorInt int color) {
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08007683 mColor = color;
7684 return this;
7685 }
7686
7687 /**
7688 * Gets the accent color.
7689 *
Julia Reynoldsd9228f12015-10-20 10:37:27 -04007690 * @see #setColor
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08007691 */
Tor Norbye80756e32015-03-02 09:39:27 -08007692 @ColorInt
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08007693 public int getColor() {
7694 return mColor;
7695 }
7696
7697 /**
7698 * Sets the large icon of the car notification.
7699 *
7700 * If no large icon is set in the extender, Android Auto will display the icon
7701 * specified by {@link Notification.Builder#setLargeIcon(android.graphics.Bitmap)}
7702 *
7703 * @param largeIcon The large icon to use in the car notification.
7704 * @return This object for method chaining.
7705 */
7706 public CarExtender setLargeIcon(Bitmap largeIcon) {
7707 mLargeIcon = largeIcon;
7708 return this;
7709 }
7710
7711 /**
7712 * Gets the large icon used in this car notification, or null if no icon has been set.
7713 *
7714 * @return The large icon for the car notification.
7715 * @see CarExtender#setLargeIcon
7716 */
7717 public Bitmap getLargeIcon() {
7718 return mLargeIcon;
7719 }
7720
7721 /**
7722 * Sets the unread conversation in a message notification.
7723 *
7724 * @param unreadConversation The unread part of the conversation this notification conveys.
7725 * @return This object for method chaining.
7726 */
7727 public CarExtender setUnreadConversation(UnreadConversation unreadConversation) {
7728 mUnreadConversation = unreadConversation;
7729 return this;
7730 }
7731
7732 /**
7733 * Returns the unread conversation conveyed by this notification.
7734 * @see #setUnreadConversation(UnreadConversation)
7735 */
7736 public UnreadConversation getUnreadConversation() {
7737 return mUnreadConversation;
7738 }
7739
7740 /**
7741 * A class which holds the unread messages from a conversation.
7742 */
7743 public static class UnreadConversation {
7744 private static final String KEY_AUTHOR = "author";
7745 private static final String KEY_TEXT = "text";
7746 private static final String KEY_MESSAGES = "messages";
7747 private static final String KEY_REMOTE_INPUT = "remote_input";
7748 private static final String KEY_ON_REPLY = "on_reply";
7749 private static final String KEY_ON_READ = "on_read";
7750 private static final String KEY_PARTICIPANTS = "participants";
7751 private static final String KEY_TIMESTAMP = "timestamp";
7752
7753 private final String[] mMessages;
7754 private final RemoteInput mRemoteInput;
7755 private final PendingIntent mReplyPendingIntent;
7756 private final PendingIntent mReadPendingIntent;
7757 private final String[] mParticipants;
7758 private final long mLatestTimestamp;
7759
7760 UnreadConversation(String[] messages, RemoteInput remoteInput,
7761 PendingIntent replyPendingIntent, PendingIntent readPendingIntent,
7762 String[] participants, long latestTimestamp) {
7763 mMessages = messages;
7764 mRemoteInput = remoteInput;
7765 mReadPendingIntent = readPendingIntent;
7766 mReplyPendingIntent = replyPendingIntent;
7767 mParticipants = participants;
7768 mLatestTimestamp = latestTimestamp;
7769 }
7770
7771 /**
7772 * Gets the list of messages conveyed by this notification.
7773 */
7774 public String[] getMessages() {
7775 return mMessages;
7776 }
7777
7778 /**
7779 * Gets the remote input that will be used to convey the response to a message list, or
7780 * null if no such remote input exists.
7781 */
7782 public RemoteInput getRemoteInput() {
7783 return mRemoteInput;
7784 }
7785
7786 /**
7787 * Gets the pending intent that will be triggered when the user replies to this
7788 * notification.
7789 */
7790 public PendingIntent getReplyPendingIntent() {
7791 return mReplyPendingIntent;
7792 }
7793
7794 /**
7795 * Gets the pending intent that Android Auto will send after it reads aloud all messages
7796 * in this object's message list.
7797 */
7798 public PendingIntent getReadPendingIntent() {
7799 return mReadPendingIntent;
7800 }
7801
7802 /**
7803 * Gets the participants in the conversation.
7804 */
7805 public String[] getParticipants() {
7806 return mParticipants;
7807 }
7808
7809 /**
7810 * Gets the firs participant in the conversation.
7811 */
7812 public String getParticipant() {
7813 return mParticipants.length > 0 ? mParticipants[0] : null;
7814 }
7815
7816 /**
7817 * Gets the timestamp of the conversation.
7818 */
7819 public long getLatestTimestamp() {
7820 return mLatestTimestamp;
7821 }
7822
7823 Bundle getBundleForUnreadConversation() {
7824 Bundle b = new Bundle();
7825 String author = null;
7826 if (mParticipants != null && mParticipants.length > 1) {
7827 author = mParticipants[0];
7828 }
7829 Parcelable[] messages = new Parcelable[mMessages.length];
7830 for (int i = 0; i < messages.length; i++) {
7831 Bundle m = new Bundle();
7832 m.putString(KEY_TEXT, mMessages[i]);
7833 m.putString(KEY_AUTHOR, author);
7834 messages[i] = m;
7835 }
7836 b.putParcelableArray(KEY_MESSAGES, messages);
7837 if (mRemoteInput != null) {
7838 b.putParcelable(KEY_REMOTE_INPUT, mRemoteInput);
7839 }
7840 b.putParcelable(KEY_ON_REPLY, mReplyPendingIntent);
7841 b.putParcelable(KEY_ON_READ, mReadPendingIntent);
7842 b.putStringArray(KEY_PARTICIPANTS, mParticipants);
7843 b.putLong(KEY_TIMESTAMP, mLatestTimestamp);
7844 return b;
7845 }
7846
7847 static UnreadConversation getUnreadConversationFromBundle(Bundle b) {
7848 if (b == null) {
7849 return null;
7850 }
7851 Parcelable[] parcelableMessages = b.getParcelableArray(KEY_MESSAGES);
7852 String[] messages = null;
7853 if (parcelableMessages != null) {
7854 String[] tmp = new String[parcelableMessages.length];
7855 boolean success = true;
7856 for (int i = 0; i < tmp.length; i++) {
7857 if (!(parcelableMessages[i] instanceof Bundle)) {
7858 success = false;
7859 break;
7860 }
7861 tmp[i] = ((Bundle) parcelableMessages[i]).getString(KEY_TEXT);
7862 if (tmp[i] == null) {
7863 success = false;
7864 break;
7865 }
7866 }
7867 if (success) {
7868 messages = tmp;
7869 } else {
7870 return null;
7871 }
7872 }
7873
7874 PendingIntent onRead = b.getParcelable(KEY_ON_READ);
7875 PendingIntent onReply = b.getParcelable(KEY_ON_REPLY);
7876
7877 RemoteInput remoteInput = b.getParcelable(KEY_REMOTE_INPUT);
7878
7879 String[] participants = b.getStringArray(KEY_PARTICIPANTS);
7880 if (participants == null || participants.length != 1) {
7881 return null;
7882 }
7883
7884 return new UnreadConversation(messages,
7885 remoteInput,
7886 onReply,
7887 onRead,
7888 participants, b.getLong(KEY_TIMESTAMP));
7889 }
7890 };
7891
7892 /**
7893 * Builder class for {@link CarExtender.UnreadConversation} objects.
7894 */
7895 public static class Builder {
7896 private final List<String> mMessages = new ArrayList<String>();
7897 private final String mParticipant;
7898 private RemoteInput mRemoteInput;
7899 private PendingIntent mReadPendingIntent;
7900 private PendingIntent mReplyPendingIntent;
7901 private long mLatestTimestamp;
7902
7903 /**
7904 * Constructs a new builder for {@link CarExtender.UnreadConversation}.
7905 *
7906 * @param name The name of the other participant in the conversation.
7907 */
7908 public Builder(String name) {
7909 mParticipant = name;
7910 }
7911
7912 /**
7913 * Appends a new unread message to the list of messages for this conversation.
7914 *
7915 * The messages should be added from oldest to newest.
7916 *
7917 * @param message The text of the new unread message.
7918 * @return This object for method chaining.
7919 */
7920 public Builder addMessage(String message) {
7921 mMessages.add(message);
7922 return this;
7923 }
7924
7925 /**
7926 * Sets the pending intent and remote input which will convey the reply to this
7927 * notification.
7928 *
7929 * @param pendingIntent The pending intent which will be triggered on a reply.
7930 * @param remoteInput The remote input parcelable which will carry the reply.
7931 * @return This object for method chaining.
7932 *
7933 * @see CarExtender.UnreadConversation#getRemoteInput
7934 * @see CarExtender.UnreadConversation#getReplyPendingIntent
7935 */
7936 public Builder setReplyAction(
7937 PendingIntent pendingIntent, RemoteInput remoteInput) {
7938 mRemoteInput = remoteInput;
7939 mReplyPendingIntent = pendingIntent;
7940
7941 return this;
7942 }
7943
7944 /**
7945 * Sets the pending intent that will be sent once the messages in this notification
7946 * are read.
7947 *
7948 * @param pendingIntent The pending intent to use.
7949 * @return This object for method chaining.
7950 */
7951 public Builder setReadPendingIntent(PendingIntent pendingIntent) {
7952 mReadPendingIntent = pendingIntent;
7953 return this;
7954 }
7955
7956 /**
7957 * Sets the timestamp of the most recent message in an unread conversation.
7958 *
7959 * If a messaging notification has been posted by your application and has not
7960 * yet been cancelled, posting a later notification with the same id and tag
7961 * but without a newer timestamp may result in Android Auto not displaying a
7962 * heads up notification for the later notification.
7963 *
7964 * @param timestamp The timestamp of the most recent message in the conversation.
7965 * @return This object for method chaining.
7966 */
7967 public Builder setLatestTimestamp(long timestamp) {
7968 mLatestTimestamp = timestamp;
7969 return this;
7970 }
7971
7972 /**
7973 * Builds a new unread conversation object.
7974 *
7975 * @return The new unread conversation object.
7976 */
7977 public UnreadConversation build() {
7978 String[] messages = mMessages.toArray(new String[mMessages.size()]);
7979 String[] participants = { mParticipant };
7980 return new UnreadConversation(messages, mRemoteInput, mReplyPendingIntent,
7981 mReadPendingIntent, participants, mLatestTimestamp);
7982 }
7983 }
7984 }
7985
7986 /**
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08007987 * <p>Helper class to add Android TV extensions to notifications. To create a notification
7988 * with a TV extension:
7989 *
7990 * <ol>
7991 * <li>Create an {@link Notification.Builder}, setting any desired properties.
7992 * <li>Create a {@link TvExtender}.
7993 * <li>Set TV-specific properties using the {@code set} methods of
7994 * {@link TvExtender}.
7995 * <li>Call {@link Notification.Builder#extend(Notification.Extender)}
7996 * to apply the extension to a notification.
7997 * </ol>
7998 *
7999 * <pre class="prettyprint">
8000 * Notification notification = new Notification.Builder(context)
8001 * ...
8002 * .extend(new TvExtender()
8003 * .set*(...))
8004 * .build();
8005 * </pre>
8006 *
8007 * <p>TV extensions can be accessed on an existing notification by using the
8008 * {@code TvExtender(Notification)} constructor, and then using the {@code get} methods
8009 * to access values.
8010 *
8011 * @hide
8012 */
8013 @SystemApi
8014 public static final class TvExtender implements Extender {
8015 private static final String TAG = "TvExtender";
8016
8017 private static final String EXTRA_TV_EXTENDER = "android.tv.EXTENSIONS";
8018 private static final String EXTRA_FLAGS = "flags";
8019 private static final String EXTRA_CONTENT_INTENT = "content_intent";
8020 private static final String EXTRA_DELETE_INTENT = "delete_intent";
Dmitri Plotnikovb8a04ee2017-01-20 10:39:22 -08008021 private static final String EXTRA_CHANNEL_ID = "channel_id";
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08008022
8023 // Flags bitwise-ored to mFlags
8024 private static final int FLAG_AVAILABLE_ON_TV = 0x1;
8025
8026 private int mFlags;
Dmitri Plotnikovb8a04ee2017-01-20 10:39:22 -08008027 private String mChannelId;
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08008028 private PendingIntent mContentIntent;
8029 private PendingIntent mDeleteIntent;
8030
8031 /**
8032 * Create a {@link TvExtender} with default options.
8033 */
8034 public TvExtender() {
8035 mFlags = FLAG_AVAILABLE_ON_TV;
8036 }
8037
8038 /**
8039 * Create a {@link TvExtender} from the TvExtender options of an existing Notification.
8040 *
8041 * @param notif The notification from which to copy options.
8042 */
8043 public TvExtender(Notification notif) {
8044 Bundle bundle = notif.extras == null ?
8045 null : notif.extras.getBundle(EXTRA_TV_EXTENDER);
8046 if (bundle != null) {
8047 mFlags = bundle.getInt(EXTRA_FLAGS);
Dmitri Plotnikovb8a04ee2017-01-20 10:39:22 -08008048 mChannelId = bundle.getString(EXTRA_CHANNEL_ID);
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08008049 mContentIntent = bundle.getParcelable(EXTRA_CONTENT_INTENT);
8050 mDeleteIntent = bundle.getParcelable(EXTRA_DELETE_INTENT);
8051 }
8052 }
8053
8054 /**
8055 * Apply a TV extension to a notification that is being built. This is typically called by
8056 * the {@link Notification.Builder#extend(Notification.Extender)}
8057 * method of {@link Notification.Builder}.
8058 */
8059 @Override
8060 public Notification.Builder extend(Notification.Builder builder) {
8061 Bundle bundle = new Bundle();
8062
8063 bundle.putInt(EXTRA_FLAGS, mFlags);
Dmitri Plotnikovb8a04ee2017-01-20 10:39:22 -08008064 bundle.putString(EXTRA_CHANNEL_ID, mChannelId);
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08008065 if (mContentIntent != null) {
8066 bundle.putParcelable(EXTRA_CONTENT_INTENT, mContentIntent);
8067 }
8068
8069 if (mDeleteIntent != null) {
8070 bundle.putParcelable(EXTRA_DELETE_INTENT, mDeleteIntent);
8071 }
8072
8073 builder.getExtras().putBundle(EXTRA_TV_EXTENDER, bundle);
8074 return builder;
8075 }
8076
8077 /**
8078 * Returns true if this notification should be shown on TV. This method return true
8079 * if the notification was extended with a TvExtender.
8080 */
8081 public boolean isAvailableOnTv() {
8082 return (mFlags & FLAG_AVAILABLE_ON_TV) != 0;
8083 }
8084
8085 /**
Dmitri Plotnikovb8a04ee2017-01-20 10:39:22 -08008086 * Specifies the channel the notification should be delivered on when shown on TV.
8087 * It can be different from the channel that the notification is delivered to when
8088 * posting on a non-TV device.
8089 */
8090 public TvExtender setChannel(String channelId) {
8091 mChannelId = channelId;
8092 return this;
8093 }
8094
8095 /**
Julia Reynoldsbad42972017-04-25 13:52:49 -04008096 * Specifies the channel the notification should be delivered on when shown on TV.
8097 * It can be different from the channel that the notification is delivered to when
8098 * posting on a non-TV device.
8099 */
8100 public TvExtender setChannelId(String channelId) {
8101 mChannelId = channelId;
8102 return this;
8103 }
8104
Jeff Sharkey000ce802017-04-29 13:13:27 -06008105 /** @removed */
8106 @Deprecated
Dmitri Plotnikovb8a04ee2017-01-20 10:39:22 -08008107 public String getChannel() {
8108 return mChannelId;
8109 }
8110
8111 /**
Julia Reynoldsbad42972017-04-25 13:52:49 -04008112 * Returns the id of the channel this notification posts to on TV.
8113 */
8114 public String getChannelId() {
8115 return mChannelId;
8116 }
8117
8118 /**
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08008119 * Supplies a {@link PendingIntent} to be sent when the notification is selected on TV.
8120 * If provided, it is used instead of the content intent specified
8121 * at the level of Notification.
8122 */
8123 public TvExtender setContentIntent(PendingIntent intent) {
8124 mContentIntent = intent;
8125 return this;
8126 }
8127
8128 /**
8129 * Returns the TV-specific content intent. If this method returns null, the
8130 * main content intent on the notification should be used.
8131 *
8132 * @see {@link Notification#contentIntent}
8133 */
8134 public PendingIntent getContentIntent() {
8135 return mContentIntent;
8136 }
8137
8138 /**
8139 * Supplies a {@link PendingIntent} to send when the notification is cleared explicitly
8140 * by the user on TV. If provided, it is used instead of the delete intent specified
8141 * at the level of Notification.
8142 */
8143 public TvExtender setDeleteIntent(PendingIntent intent) {
8144 mDeleteIntent = intent;
8145 return this;
8146 }
8147
8148 /**
8149 * Returns the TV-specific delete intent. If this method returns null, the
8150 * main delete intent on the notification should be used.
8151 *
8152 * @see {@link Notification#deleteIntent}
8153 */
8154 public PendingIntent getDeleteIntent() {
8155 return mDeleteIntent;
8156 }
8157 }
8158
8159 /**
Griff Hazen61a9e862014-05-22 16:05:19 -07008160 * Get an array of Notification objects from a parcelable array bundle field.
8161 * Update the bundle to have a typed array so fetches in the future don't need
8162 * to do an array copy.
8163 */
8164 private static Notification[] getNotificationArrayFromBundle(Bundle bundle, String key) {
8165 Parcelable[] array = bundle.getParcelableArray(key);
8166 if (array instanceof Notification[] || array == null) {
8167 return (Notification[]) array;
8168 }
8169 Notification[] typedArray = Arrays.copyOf(array, array.length,
8170 Notification[].class);
8171 bundle.putParcelableArray(key, typedArray);
8172 return typedArray;
8173 }
Christoph Studer4600f9b2014-07-22 22:44:43 +02008174
8175 private static class BuilderRemoteViews extends RemoteViews {
8176 public BuilderRemoteViews(Parcel parcel) {
8177 super(parcel);
8178 }
8179
Kenny Guy77320062014-08-27 21:37:15 +01008180 public BuilderRemoteViews(ApplicationInfo appInfo, int layoutId) {
8181 super(appInfo, layoutId);
Christoph Studer4600f9b2014-07-22 22:44:43 +02008182 }
8183
8184 @Override
8185 public BuilderRemoteViews clone() {
8186 Parcel p = Parcel.obtain();
8187 writeToParcel(p, 0);
8188 p.setDataPosition(0);
8189 BuilderRemoteViews brv = new BuilderRemoteViews(p);
8190 p.recycle();
8191 return brv;
8192 }
8193 }
Adrian Roos70d7aa32017-01-11 15:39:06 -08008194
8195 private static class StandardTemplateParams {
8196 boolean hasProgress = true;
8197 boolean ambient = false;
8198 CharSequence title;
8199 CharSequence text;
8200
8201 final StandardTemplateParams reset() {
8202 hasProgress = true;
8203 ambient = false;
8204 title = null;
8205 text = null;
8206 return this;
8207 }
8208
8209 final StandardTemplateParams hasProgress(boolean hasProgress) {
8210 this.hasProgress = hasProgress;
8211 return this;
8212 }
8213
8214 final StandardTemplateParams title(CharSequence title) {
8215 this.title = title;
8216 return this;
8217 }
8218
8219 final StandardTemplateParams text(CharSequence text) {
8220 this.text = text;
8221 return this;
8222 }
8223
8224 final StandardTemplateParams ambient(boolean ambient) {
Adrian Roos0bc3f6a2017-03-06 11:54:05 -08008225 Preconditions.checkState(title == null && text == null, "must set ambient before text");
Adrian Roos70d7aa32017-01-11 15:39:06 -08008226 this.ambient = ambient;
8227 return this;
8228 }
8229
8230 final StandardTemplateParams fillTextsFrom(Builder b) {
8231 Bundle extras = b.mN.extras;
Adrian Roos0bc3f6a2017-03-06 11:54:05 -08008232 title = b.processLegacyText(extras.getCharSequence(EXTRA_TITLE), ambient);
8233 text = b.processLegacyText(extras.getCharSequence(EXTRA_TEXT), ambient);
Adrian Roos70d7aa32017-01-11 15:39:06 -08008234 return this;
8235 }
8236 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08008237}