blob: ccbd5b41d34e4402e26c9ba73424b38535a65c57 [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;
Selim Cineka7679b62017-05-10 16:33:25 -07002706 private boolean mRebuildStyledRemoteViews;
Adrian Roos70d7aa32017-01-11 15:39:06 -08002707
2708 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002709 * Constructs a new Builder with the defaults:
Joe Onoratocb109a02011-01-18 17:57:41 -08002710 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002711 * @param context
2712 * A {@link Context} that will be used by the Builder to construct the
2713 * RemoteViews. The Context will not be held past the lifetime of this Builder
2714 * object.
Geoffrey Pitsch5caa2762017-01-12 09:35:54 -05002715 * @param channelId
2716 * The constructed Notification will be posted on this
2717 * {@link NotificationChannel}. To use a NotificationChannel, it must first be
2718 * created using {@link NotificationManager#createNotificationChannel}.
Joe Onoratocb109a02011-01-18 17:57:41 -08002719 */
Geoffrey Pitsch5caa2762017-01-12 09:35:54 -05002720 public Builder(Context context, String channelId) {
2721 this(context, (Notification) null);
2722 mN.mChannelId = channelId;
2723 }
2724
2725 /**
2726 * @deprecated use {@link Notification.Builder#Notification.Builder(Context, String)}
2727 * instead. All posted Notifications must specify a NotificationChannel Id.
2728 */
2729 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08002730 public Builder(Context context) {
Geoffrey Pitsch5caa2762017-01-12 09:35:54 -05002731 this(context, (Notification) null);
Joe Onorato46439ce2010-11-19 13:56:21 -08002732 }
2733
Joe Onoratocb109a02011-01-18 17:57:41 -08002734 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002735 * @hide
Christoph Studer4600f9b2014-07-22 22:44:43 +02002736 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002737 public Builder(Context context, Notification toAdopt) {
2738 mContext = context;
Christoph Studer4600f9b2014-07-22 22:44:43 +02002739
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002740 if (toAdopt == null) {
2741 mN = new Notification();
Selim Cinek0ff1ce602016-04-05 18:27:16 -07002742 if (context.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N) {
2743 mN.extras.putBoolean(EXTRA_SHOW_WHEN, true);
2744 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002745 mN.priority = PRIORITY_DEFAULT;
2746 mN.visibility = VISIBILITY_PRIVATE;
2747 } else {
2748 mN = toAdopt;
2749 if (mN.actions != null) {
2750 Collections.addAll(mActions, mN.actions);
Christoph Studer4600f9b2014-07-22 22:44:43 +02002751 }
2752
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002753 if (mN.extras.containsKey(EXTRA_PEOPLE)) {
2754 Collections.addAll(mPersonList, mN.extras.getStringArray(EXTRA_PEOPLE));
2755 }
2756
Selim Cinek4ac6f602016-06-13 15:47:03 -07002757 if (mN.getSmallIcon() == null && mN.icon != 0) {
2758 setSmallIcon(mN.icon);
2759 }
2760
2761 if (mN.getLargeIcon() == null && mN.largeIcon != null) {
2762 setLargeIcon(mN.largeIcon);
2763 }
2764
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002765 String templateClass = mN.extras.getString(EXTRA_TEMPLATE);
2766 if (!TextUtils.isEmpty(templateClass)) {
2767 final Class<? extends Style> styleClass
2768 = getNotificationStyleClass(templateClass);
2769 if (styleClass == null) {
2770 Log.d(TAG, "Unknown style class: " + templateClass);
2771 } else {
2772 try {
Adrian Roosc1a80b02016-04-05 14:54:55 -07002773 final Constructor<? extends Style> ctor =
2774 styleClass.getDeclaredConstructor();
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002775 ctor.setAccessible(true);
2776 final Style style = ctor.newInstance();
2777 style.restoreFromExtras(mN.extras);
2778
2779 if (style != null) {
2780 setStyle(style);
2781 }
2782 } catch (Throwable t) {
2783 Log.e(TAG, "Could not create Style", t);
2784 }
2785 }
2786 }
2787
2788 }
2789 }
2790
2791 private NotificationColorUtil getColorUtil() {
Selim Cinek99104832017-01-25 14:47:33 -08002792 if (mColorUtil == null) {
2793 mColorUtil = NotificationColorUtil.getInstance(mContext);
Christoph Studer4600f9b2014-07-22 22:44:43 +02002794 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002795 return mColorUtil;
Christoph Studer4600f9b2014-07-22 22:44:43 +02002796 }
2797
2798 /**
Julia Reynolds13d898c2017-02-02 12:22:05 -05002799 * If this notification is duplicative of a Launcher shortcut, sets the
2800 * {@link ShortcutInfo#getId() id} of the shortcut, in case the Launcher wants to hide
2801 * the shortcut.
2802 *
Julia Reynoldsbad42972017-04-25 13:52:49 -04002803 * This field will be ignored by Launchers that don't support badging, don't show
2804 * notification content, or don't show {@link android.content.pm.ShortcutManager shortcuts}.
Julia Reynolds13d898c2017-02-02 12:22:05 -05002805 *
2806 * @param shortcutId the {@link ShortcutInfo#getId() id} of the shortcut this notification
2807 * supersedes
2808 */
2809 public Builder setShortcutId(String shortcutId) {
2810 mN.mShortcutId = shortcutId;
2811 return this;
2812 }
2813
2814 /**
Julia Reynoldse071abd2017-03-22 10:52:11 -04002815 * Sets which icon to display as a badge for this notification.
2816 *
2817 * Must be one of {@link #BADGE_ICON_NONE}, {@link #BADGE_ICON_SMALL},
2818 * {@link #BADGE_ICON_LARGE}.
2819 *
2820 * Note: This value might be ignored, for launchers that don't support badge icons.
2821 */
Julia Reynolds612beb22017-03-30 10:48:30 -04002822 public Builder setBadgeIconType(int icon) {
Julia Reynoldse071abd2017-03-22 10:52:11 -04002823 mN.mBadgeIcon = icon;
2824 return this;
2825 }
2826
2827 /**
Julia Reynoldsa79c3712017-04-21 10:29:57 -04002828 * Sets the group alert behavior for this notification. Use this method to mute this
2829 * notification if alerts for this notification's group should be handled by a different
2830 * notification. This is only applicable for notifications that belong to a
2831 * {@link #setGroup(String) group}.
2832 *
2833 * <p> The default value is {@link #GROUP_ALERT_ALL}.</p>
2834 */
2835 public Builder setGroupAlertBehavior(@GroupAlertBehavior int groupAlertBehavior) {
2836 mN.mGroupAlertBehavior = groupAlertBehavior;
2837 return this;
2838 }
2839
Jeff Sharkey000ce802017-04-29 13:13:27 -06002840 /** @removed */
2841 @Deprecated
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04002842 public Builder setChannel(String channelId) {
2843 mN.mChannelId = channelId;
2844 return this;
2845 }
2846
2847 /**
Julia Reynoldsbad42972017-04-25 13:52:49 -04002848 * Specifies the channel the notification should be delivered on.
2849 */
2850 public Builder setChannelId(String channelId) {
2851 mN.mChannelId = channelId;
2852 return this;
2853 }
2854
Jeff Sharkey000ce802017-04-29 13:13:27 -06002855 /** @removed */
2856 @Deprecated
Julia Reynolds50989772017-02-23 14:32:16 -05002857 public Builder setTimeout(long durationMs) {
2858 mN.mTimeout = durationMs;
Julia Reynolds2a128742016-11-28 14:29:25 -05002859 return this;
2860 }
2861
2862 /**
Julia Reynoldsbad42972017-04-25 13:52:49 -04002863 * Specifies a duration in milliseconds after which this notification should be canceled,
2864 * if it is not already canceled.
2865 */
2866 public Builder setTimeoutAfter(long durationMs) {
2867 mN.mTimeout = durationMs;
2868 return this;
2869 }
2870
2871 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002872 * Add a timestamp pertaining to the notification (usually the time the event occurred).
Selim Cinek0ff1ce602016-04-05 18:27:16 -07002873 *
2874 * For apps targeting {@link android.os.Build.VERSION_CODES#N} and above, this time is not
2875 * shown anymore by default and must be opted into by using
2876 * {@link android.app.Notification.Builder#setShowWhen(boolean)}
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002877 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002878 * @see Notification#when
Joe Onoratocb109a02011-01-18 17:57:41 -08002879 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002880 public Builder setWhen(long when) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002881 mN.when = when;
Joe Onorato46439ce2010-11-19 13:56:21 -08002882 return this;
2883 }
2884
Joe Onoratocb109a02011-01-18 17:57:41 -08002885 /**
Griff Hazen50c11652014-05-16 09:46:31 -07002886 * Control whether the timestamp set with {@link #setWhen(long) setWhen} is shown
Daniel Sandler0c890492012-09-12 17:23:10 -07002887 * in the content view.
Selim Cinek0ff1ce602016-04-05 18:27:16 -07002888 * For apps targeting {@link android.os.Build.VERSION_CODES#N} and above, this defaults to
2889 * {@code false}. For earlier apps, the default is {@code true}.
Daniel Sandler0c890492012-09-12 17:23:10 -07002890 */
2891 public Builder setShowWhen(boolean show) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002892 mN.extras.putBoolean(EXTRA_SHOW_WHEN, show);
Daniel Sandler0c890492012-09-12 17:23:10 -07002893 return this;
2894 }
2895
2896 /**
Daniel Sandlerd33b8032012-05-10 11:41:48 -04002897 * Show the {@link Notification#when} field as a stopwatch.
Joe Malin8d40d042012-11-05 11:36:40 -08002898 *
2899 * Instead of presenting <code>when</code> as a timestamp, the notification will show an
Daniel Sandlerd33b8032012-05-10 11:41:48 -04002900 * automatically updating display of the minutes and seconds since <code>when</code>.
Daniel Sandlera2985ed2012-04-03 16:42:00 -04002901 *
Daniel Sandlerd33b8032012-05-10 11:41:48 -04002902 * Useful when showing an elapsed time (like an ongoing phone call).
2903 *
Selim Cinek81c23aa2016-02-25 16:23:13 -08002904 * The counter can also be set to count down to <code>when</code> when using
Adrian Roos96b7e202016-05-17 13:50:38 -07002905 * {@link #setChronometerCountDown(boolean)}.
Selim Cinek81c23aa2016-02-25 16:23:13 -08002906 *
Daniel Sandlerd33b8032012-05-10 11:41:48 -04002907 * @see android.widget.Chronometer
Daniel Sandlera2985ed2012-04-03 16:42:00 -04002908 * @see Notification#when
Adrian Roos96b7e202016-05-17 13:50:38 -07002909 * @see #setChronometerCountDown(boolean)
Daniel Sandlera2985ed2012-04-03 16:42:00 -04002910 */
2911 public Builder setUsesChronometer(boolean b) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002912 mN.extras.putBoolean(EXTRA_SHOW_CHRONOMETER, b);
Daniel Sandlera2985ed2012-04-03 16:42:00 -04002913 return this;
2914 }
2915
2916 /**
Selim Cinek81c23aa2016-02-25 16:23:13 -08002917 * Sets the Chronometer to count down instead of counting up.
2918 *
2919 * <p>This is only relevant if {@link #setUsesChronometer(boolean)} has been set to true.
2920 * If it isn't set the chronometer will count up.
2921 *
2922 * @see #setUsesChronometer(boolean)
2923 */
Adrian Roos96b7e202016-05-17 13:50:38 -07002924 public Builder setChronometerCountDown(boolean countDown) {
2925 mN.extras.putBoolean(EXTRA_CHRONOMETER_COUNT_DOWN, countDown);
Selim Cinek81c23aa2016-02-25 16:23:13 -08002926 return this;
2927 }
2928
2929 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002930 * Set the small icon resource, which will be used to represent the notification in the
2931 * status bar.
Joe Onoratocb109a02011-01-18 17:57:41 -08002932 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002933
2934 * The platform template for the expanded view will draw this icon in the left, unless a
2935 * {@link #setLargeIcon(Bitmap) large icon} has also been specified, in which case the small
2936 * icon will be moved to the right-hand side.
2937 *
2938
2939 * @param icon
2940 * A resource ID in the application's package of the drawable to use.
2941 * @see Notification#icon
Joe Onoratocb109a02011-01-18 17:57:41 -08002942 */
Tor Norbye7b9c9122013-05-30 16:48:33 -07002943 public Builder setSmallIcon(@DrawableRes int icon) {
Dan Sandlerd63f9322015-05-06 15:18:49 -04002944 return setSmallIcon(icon != 0
2945 ? Icon.createWithResource(mContext, icon)
2946 : null);
Joe Onorato46439ce2010-11-19 13:56:21 -08002947 }
2948
Joe Onoratocb109a02011-01-18 17:57:41 -08002949 /**
2950 * A variant of {@link #setSmallIcon(int) setSmallIcon(int)} that takes an additional
2951 * level parameter for when the icon is a {@link android.graphics.drawable.LevelListDrawable
2952 * LevelListDrawable}.
2953 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002954 * @param icon A resource ID in the application's package of the drawable to use.
Joe Onoratocb109a02011-01-18 17:57:41 -08002955 * @param level The level to use for the icon.
2956 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002957 * @see Notification#icon
2958 * @see Notification#iconLevel
Joe Onoratocb109a02011-01-18 17:57:41 -08002959 */
Tor Norbye7b9c9122013-05-30 16:48:33 -07002960 public Builder setSmallIcon(@DrawableRes int icon, int level) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002961 mN.iconLevel = level;
Dan Sandlerd63f9322015-05-06 15:18:49 -04002962 return setSmallIcon(icon);
2963 }
2964
2965 /**
2966 * Set the small icon, which will be used to represent the notification in the
2967 * status bar and content view (unless overriden there by a
2968 * {@link #setLargeIcon(Bitmap) large icon}).
2969 *
2970 * @param icon An Icon object to use.
2971 * @see Notification#icon
2972 */
2973 public Builder setSmallIcon(Icon icon) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002974 mN.setSmallIcon(icon);
2975 if (icon != null && icon.getType() == Icon.TYPE_RESOURCE) {
2976 mN.icon = icon.getResId();
2977 }
Joe Onorato46439ce2010-11-19 13:56:21 -08002978 return this;
2979 }
2980
Joe Onoratocb109a02011-01-18 17:57:41 -08002981 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002982 * Set the first line of text in the platform notification template.
Joe Onoratocb109a02011-01-18 17:57:41 -08002983 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002984 public Builder setContentTitle(CharSequence title) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002985 mN.extras.putCharSequence(EXTRA_TITLE, safeCharSequence(title));
Joe Onorato46439ce2010-11-19 13:56:21 -08002986 return this;
2987 }
2988
Joe Onoratocb109a02011-01-18 17:57:41 -08002989 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002990 * Set the second line of text in the platform notification template.
Joe Onoratocb109a02011-01-18 17:57:41 -08002991 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002992 public Builder setContentText(CharSequence text) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002993 mN.extras.putCharSequence(EXTRA_TEXT, safeCharSequence(text));
Joe Onorato46439ce2010-11-19 13:56:21 -08002994 return this;
2995 }
2996
Joe Onoratocb109a02011-01-18 17:57:41 -08002997 /**
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07002998 * This provides some additional information that is displayed in the notification. No
2999 * guarantees are given where exactly it is displayed.
3000 *
3001 * <p>This information should only be provided if it provides an essential
3002 * benefit to the understanding of the notification. The more text you provide the
3003 * less readable it becomes. For example, an email client should only provide the account
3004 * name here if more than one email account has been added.</p>
3005 *
3006 * <p>As of {@link android.os.Build.VERSION_CODES#N} this information is displayed in the
3007 * notification header area.
3008 *
3009 * On Android versions before {@link android.os.Build.VERSION_CODES#N}
3010 * this will be shown in the third line of text in the platform notification template.
3011 * You should not be using {@link #setProgress(int, int, boolean)} at the
3012 * same time on those versions; they occupy the same place.
3013 * </p>
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003014 */
3015 public Builder setSubText(CharSequence text) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003016 mN.extras.putCharSequence(EXTRA_SUB_TEXT, safeCharSequence(text));
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003017 return this;
3018 }
3019
3020 /**
Julia Reynolds3aedded2017-03-31 14:42:09 -04003021 * Provides text that will appear as a link to your application's settings.
3022 *
3023 * <p>This text does not appear within notification {@link Style templates} but may
3024 * appear when the user uses an affordance to learn more about the notification.
3025 * Additionally, this text will not appear unless you provide a valid link target by
3026 * handling {@link #INTENT_CATEGORY_NOTIFICATION_PREFERENCES}.
3027 *
3028 * <p>This text is meant to be concise description about what the user can customize
3029 * when they click on this link. The recommended maximum length is 40 characters.
3030 * @param text
3031 * @return
3032 */
3033 public Builder setSettingsText(CharSequence text) {
3034 mN.mSettingsText = safeCharSequence(text);
3035 return this;
3036 }
3037
3038 /**
Adrian Roose458aa82015-12-08 16:17:19 -08003039 * Set the remote input history.
3040 *
3041 * This should be set to the most recent inputs that have been sent
3042 * through a {@link RemoteInput} of this Notification and cleared once the it is no
3043 * longer relevant (e.g. for chat notifications once the other party has responded).
3044 *
3045 * The most recent input must be stored at the 0 index, the second most recent at the
3046 * 1 index, etc. Note that the system will limit both how far back the inputs will be shown
3047 * and how much of each individual input is shown.
3048 *
3049 * <p>Note: The reply text will only be shown on notifications that have least one action
3050 * with a {@code RemoteInput}.</p>
3051 */
3052 public Builder setRemoteInputHistory(CharSequence[] text) {
3053 if (text == null) {
3054 mN.extras.putCharSequenceArray(EXTRA_REMOTE_INPUT_HISTORY, null);
3055 } else {
3056 final int N = Math.min(MAX_REPLY_HISTORY, text.length);
3057 CharSequence[] safe = new CharSequence[N];
3058 for (int i = 0; i < N; i++) {
3059 safe[i] = safeCharSequence(text[i]);
3060 }
3061 mN.extras.putCharSequenceArray(EXTRA_REMOTE_INPUT_HISTORY, safe);
3062 }
3063 return this;
3064 }
3065
3066 /**
Julia Reynolds13d898c2017-02-02 12:22:05 -05003067 * Sets the number of items this notification represents. May be displayed as a badge count
3068 * for Launchers that support badging.
Joe Onoratocb109a02011-01-18 17:57:41 -08003069 */
Joe Onorato8595a3d2010-11-19 18:12:07 -08003070 public Builder setNumber(int number) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003071 mN.number = number;
Joe Onorato8595a3d2010-11-19 18:12:07 -08003072 return this;
3073 }
3074
Joe Onoratocb109a02011-01-18 17:57:41 -08003075 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003076 * A small piece of additional information pertaining to this notification.
3077 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003078 * The platform template will draw this on the last line of the notification, at the far
3079 * right (to the right of a smallIcon if it has been placed there).
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07003080 *
3081 * @deprecated use {@link #setSubText(CharSequence)} instead to set a text in the header.
3082 * For legacy apps targeting a version below {@link android.os.Build.VERSION_CODES#N} this
3083 * field will still show up, but the subtext will take precedence.
Joe Onoratocb109a02011-01-18 17:57:41 -08003084 */
Aurimas Liutikas514c5ef2016-05-24 15:22:55 -07003085 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08003086 public Builder setContentInfo(CharSequence info) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003087 mN.extras.putCharSequence(EXTRA_INFO_TEXT, safeCharSequence(info));
Joe Onorato46439ce2010-11-19 13:56:21 -08003088 return this;
3089 }
3090
Joe Onoratocb109a02011-01-18 17:57:41 -08003091 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003092 * Set the progress this notification represents.
3093 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003094 * The platform template will represent this using a {@link ProgressBar}.
Jeff Sharkey1c400132011-08-05 14:50:13 -07003095 */
3096 public Builder setProgress(int max, int progress, boolean indeterminate) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003097 mN.extras.putInt(EXTRA_PROGRESS, progress);
3098 mN.extras.putInt(EXTRA_PROGRESS_MAX, max);
3099 mN.extras.putBoolean(EXTRA_PROGRESS_INDETERMINATE, indeterminate);
Jeff Sharkey1c400132011-08-05 14:50:13 -07003100 return this;
3101 }
3102
3103 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003104 * Supply a custom RemoteViews to use instead of the platform template.
3105 *
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003106 * Use {@link #setCustomContentView(RemoteViews)} instead.
Joe Onoratocb109a02011-01-18 17:57:41 -08003107 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003108 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08003109 public Builder setContent(RemoteViews views) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003110 return setCustomContentView(views);
3111 }
3112
3113 /**
3114 * Supply custom RemoteViews to use instead of the platform template.
3115 *
3116 * This will override the layout that would otherwise be constructed by this Builder
3117 * object.
3118 */
3119 public Builder setCustomContentView(RemoteViews contentView) {
3120 mN.contentView = contentView;
3121 return this;
3122 }
3123
3124 /**
3125 * Supply custom RemoteViews to use instead of the platform template in the expanded form.
3126 *
3127 * This will override the expanded layout that would otherwise be constructed by this
3128 * Builder object.
3129 */
3130 public Builder setCustomBigContentView(RemoteViews contentView) {
3131 mN.bigContentView = contentView;
3132 return this;
3133 }
3134
3135 /**
3136 * Supply custom RemoteViews to use instead of the platform template in the heads up dialog.
3137 *
3138 * This will override the heads-up layout that would otherwise be constructed by this
3139 * Builder object.
3140 */
3141 public Builder setCustomHeadsUpContentView(RemoteViews contentView) {
3142 mN.headsUpContentView = contentView;
Joe Onorato46439ce2010-11-19 13:56:21 -08003143 return this;
3144 }
3145
Joe Onoratocb109a02011-01-18 17:57:41 -08003146 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003147 * Supply a {@link PendingIntent} to be sent when the notification is clicked.
3148 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003149 * As of {@link android.os.Build.VERSION_CODES#HONEYCOMB}, if this field is unset and you
3150 * have specified a custom RemoteViews with {@link #setContent(RemoteViews)}, you can use
3151 * {@link RemoteViews#setOnClickPendingIntent RemoteViews.setOnClickPendingIntent(int,PendingIntent)}
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003152 * to assign PendingIntents to individual views in that custom layout (i.e., to create
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003153 * clickable buttons inside the notification view).
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003154 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003155 * @see Notification#contentIntent Notification.contentIntent
Joe Onoratocb109a02011-01-18 17:57:41 -08003156 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003157 public Builder setContentIntent(PendingIntent intent) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003158 mN.contentIntent = intent;
Joe Onorato46439ce2010-11-19 13:56:21 -08003159 return this;
3160 }
3161
Joe Onoratocb109a02011-01-18 17:57:41 -08003162 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003163 * Supply a {@link PendingIntent} to send when the notification is cleared explicitly by the user.
3164 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003165 * @see Notification#deleteIntent
Joe Onoratocb109a02011-01-18 17:57:41 -08003166 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003167 public Builder setDeleteIntent(PendingIntent intent) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003168 mN.deleteIntent = intent;
Joe Onorato46439ce2010-11-19 13:56:21 -08003169 return this;
3170 }
3171
Joe Onoratocb109a02011-01-18 17:57:41 -08003172 /**
3173 * An intent to launch instead of posting the notification to the status bar.
3174 * Only for use with extremely high-priority notifications demanding the user's
3175 * <strong>immediate</strong> attention, such as an incoming phone call or
3176 * alarm clock that the user has explicitly set to a particular time.
3177 * If this facility is used for something else, please give the user an option
3178 * to turn it off and use a normal notification, as this can be extremely
3179 * disruptive.
3180 *
Chris Wren47c20a12014-06-18 17:27:29 -04003181 * <p>
3182 * The system UI may choose to display a heads-up notification, instead of
3183 * launching this intent, while the user is using the device.
3184 * </p>
3185 *
Joe Onoratocb109a02011-01-18 17:57:41 -08003186 * @param intent The pending intent to launch.
3187 * @param highPriority Passing true will cause this notification to be sent
3188 * even if other notifications are suppressed.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003189 *
3190 * @see Notification#fullScreenIntent
Joe Onoratocb109a02011-01-18 17:57:41 -08003191 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003192 public Builder setFullScreenIntent(PendingIntent intent, boolean highPriority) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003193 mN.fullScreenIntent = intent;
Joe Onorato46439ce2010-11-19 13:56:21 -08003194 setFlag(FLAG_HIGH_PRIORITY, highPriority);
3195 return this;
3196 }
3197
Joe Onoratocb109a02011-01-18 17:57:41 -08003198 /**
Dan Sandler5fcdf6e2014-07-18 11:31:15 -04003199 * Set the "ticker" text which is sent to accessibility services.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003200 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003201 * @see Notification#tickerText
Joe Onoratocb109a02011-01-18 17:57:41 -08003202 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003203 public Builder setTicker(CharSequence tickerText) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003204 mN.tickerText = safeCharSequence(tickerText);
Joe Onorato46439ce2010-11-19 13:56:21 -08003205 return this;
3206 }
3207
Joe Onoratocb109a02011-01-18 17:57:41 -08003208 /**
Dan Sandler5fcdf6e2014-07-18 11:31:15 -04003209 * Obsolete version of {@link #setTicker(CharSequence)}.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003210 *
Joe Onoratocb109a02011-01-18 17:57:41 -08003211 */
Dan Sandler5fcdf6e2014-07-18 11:31:15 -04003212 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08003213 public Builder setTicker(CharSequence tickerText, RemoteViews views) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003214 setTicker(tickerText);
3215 // views is ignored
Joe Onorato46439ce2010-11-19 13:56:21 -08003216 return this;
3217 }
3218
Joe Onoratocb109a02011-01-18 17:57:41 -08003219 /**
Dan Sandlerd63f9322015-05-06 15:18:49 -04003220 * Add a large icon to the notification content view.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003221 *
3222 * In the platform template, this image will be shown on the left of the notification view
Dan Sandlerd63f9322015-05-06 15:18:49 -04003223 * in place of the {@link #setSmallIcon(Icon) small icon} (which will be placed in a small
3224 * badge atop the large icon).
Dan Sandler08a04c12015-05-06 15:18:49 -04003225 */
Dan Sandlerd63f9322015-05-06 15:18:49 -04003226 public Builder setLargeIcon(Bitmap b) {
3227 return setLargeIcon(b != null ? Icon.createWithBitmap(b) : null);
3228 }
3229
3230 /**
3231 * Add a large icon to the notification content view.
3232 *
3233 * In the platform template, this image will be shown on the left of the notification view
3234 * in place of the {@link #setSmallIcon(Icon) small icon} (which will be placed in a small
3235 * badge atop the large icon).
3236 */
3237 public Builder setLargeIcon(Icon icon) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003238 mN.mLargeIcon = icon;
3239 mN.extras.putParcelable(EXTRA_LARGE_ICON, icon);
Joe Onorato46439ce2010-11-19 13:56:21 -08003240 return this;
3241 }
3242
Joe Onoratocb109a02011-01-18 17:57:41 -08003243 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003244 * Set the sound to play.
3245 *
John Spurlockc0650f022014-07-19 13:22:39 -04003246 * It will be played using the {@link #AUDIO_ATTRIBUTES_DEFAULT default audio attributes}
3247 * for notifications.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003248 *
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003249 * @deprecated use {@link NotificationChannel#setSound(Uri, AudioAttributes)} instead.
Joe Onoratocb109a02011-01-18 17:57:41 -08003250 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003251 @Deprecated
Joe Onorato52f80cd2010-11-21 15:34:48 -08003252 public Builder setSound(Uri sound) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003253 mN.sound = sound;
3254 mN.audioAttributes = AUDIO_ATTRIBUTES_DEFAULT;
Joe Onorato52f80cd2010-11-21 15:34:48 -08003255 return this;
3256 }
3257
Joe Onoratocb109a02011-01-18 17:57:41 -08003258 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003259 * Set the sound to play, along with a specific stream on which to play it.
Joe Onoratocb109a02011-01-18 17:57:41 -08003260 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003261 * See {@link android.media.AudioManager} for the <code>STREAM_</code> constants.
3262 *
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003263 * @deprecated use {@link NotificationChannel#setSound(Uri, AudioAttributes)}.
Joe Onoratocb109a02011-01-18 17:57:41 -08003264 */
Jean-Michel Trivi81f871e2014-08-06 16:32:38 -07003265 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08003266 public Builder setSound(Uri sound, int streamType) {
Jean-Michel Trivi2f7511f2016-11-28 15:40:27 -08003267 PlayerBase.deprecateStreamTypeForPlayback(streamType, "Notification", "setSound()");
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003268 mN.sound = sound;
3269 mN.audioStreamType = streamType;
Joe Onorato46439ce2010-11-19 13:56:21 -08003270 return this;
3271 }
3272
Joe Onoratocb109a02011-01-18 17:57:41 -08003273 /**
John Spurlockc0650f022014-07-19 13:22:39 -04003274 * Set the sound to play, along with specific {@link AudioAttributes audio attributes} to
3275 * use during playback.
3276 *
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003277 * @deprecated use {@link NotificationChannel#setSound(Uri, AudioAttributes)} instead.
John Spurlockc0650f022014-07-19 13:22:39 -04003278 * @see Notification#sound
3279 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003280 @Deprecated
John Spurlockc0650f022014-07-19 13:22:39 -04003281 public Builder setSound(Uri sound, AudioAttributes audioAttributes) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003282 mN.sound = sound;
3283 mN.audioAttributes = audioAttributes;
John Spurlockc0650f022014-07-19 13:22:39 -04003284 return this;
3285 }
3286
3287 /**
Joe Onoratocb109a02011-01-18 17:57:41 -08003288 * Set the vibration pattern to use.
3289 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003290 * See {@link android.os.Vibrator#vibrate(long[], int)} for a discussion of the
3291 * <code>pattern</code> parameter.
3292 *
Chris Wren47c20a12014-06-18 17:27:29 -04003293 * <p>
3294 * A notification that vibrates is more likely to be presented as a heads-up notification.
3295 * </p>
3296 *
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003297 * @deprecated use {@link NotificationChannel#setVibrationPattern(long[])} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003298 * @see Notification#vibrate
Joe Onoratocb109a02011-01-18 17:57:41 -08003299 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003300 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08003301 public Builder setVibrate(long[] pattern) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003302 mN.vibrate = pattern;
Joe Onorato46439ce2010-11-19 13:56:21 -08003303 return this;
3304 }
3305
Joe Onoratocb109a02011-01-18 17:57:41 -08003306 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003307 * Set the desired color for the indicator LED on the device, as well as the
3308 * blink duty cycle (specified in milliseconds).
3309 *
3310
3311 * Not all devices will honor all (or even any) of these values.
3312 *
Julia Reynolds529e3322017-02-06 08:33:01 -05003313 * @deprecated use {@link NotificationChannel#enableLights(boolean)} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003314 * @see Notification#ledARGB
3315 * @see Notification#ledOnMS
3316 * @see Notification#ledOffMS
Joe Onoratocb109a02011-01-18 17:57:41 -08003317 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003318 @Deprecated
Tor Norbye80756e32015-03-02 09:39:27 -08003319 public Builder setLights(@ColorInt int argb, int onMs, int offMs) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003320 mN.ledARGB = argb;
3321 mN.ledOnMS = onMs;
3322 mN.ledOffMS = offMs;
Julia Reynolds10ee1fc2015-11-09 11:04:55 -05003323 if (onMs != 0 || offMs != 0) {
3324 mN.flags |= FLAG_SHOW_LIGHTS;
3325 }
Joe Onorato46439ce2010-11-19 13:56:21 -08003326 return this;
3327 }
3328
Joe Onoratocb109a02011-01-18 17:57:41 -08003329 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003330 * Set whether this is an "ongoing" notification.
Joe Onoratocb109a02011-01-18 17:57:41 -08003331 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003332
3333 * Ongoing notifications cannot be dismissed by the user, so your application or service
3334 * must take care of canceling them.
3335 *
3336
3337 * They are typically used to indicate a background task that the user is actively engaged
3338 * with (e.g., playing music) or is pending in some way and therefore occupying the device
3339 * (e.g., a file download, sync operation, active network connection).
3340 *
3341
3342 * @see Notification#FLAG_ONGOING_EVENT
3343 * @see Service#setForeground(boolean)
Joe Onoratocb109a02011-01-18 17:57:41 -08003344 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003345 public Builder setOngoing(boolean ongoing) {
3346 setFlag(FLAG_ONGOING_EVENT, ongoing);
3347 return this;
3348 }
3349
Joe Onoratocb109a02011-01-18 17:57:41 -08003350 /**
Selim Cinek7b9605b2017-01-19 17:36:00 -08003351 * Set whether this notification should be colorized. When set, the color set with
3352 * {@link #setColor(int)} will be used as the background color of this notification.
3353 * <p>
Selim Cinek7b9605b2017-01-19 17:36:00 -08003354 * This should only be used for high priority ongoing tasks like navigation, an ongoing
3355 * call, or other similarly high-priority events for the user.
Selim Cinek99104832017-01-25 14:47:33 -08003356 * <p>
Selim Cinek22714f12017-04-13 16:23:53 -07003357 * For most styles, the coloring will only be applied if the notification is for a
3358 * foreground service notification.
Selim Cinek99104832017-01-25 14:47:33 -08003359 * However, for {@link MediaStyle} and {@link DecoratedMediaCustomViewStyle} notifications
Selim Cinek22714f12017-04-13 16:23:53 -07003360 * that have a media session attached there is no such requirement.
Selim Cinek7b9605b2017-01-19 17:36:00 -08003361 *
Selim Cinek7b9605b2017-01-19 17:36:00 -08003362 * @see Builder#setColor(int)
Selim Cinek99104832017-01-25 14:47:33 -08003363 * @see MediaStyle#setMediaSession(MediaSession.Token)
Selim Cinek7b9605b2017-01-19 17:36:00 -08003364 */
3365 public Builder setColorized(boolean colorize) {
3366 mN.extras.putBoolean(EXTRA_COLORIZED, colorize);
3367 return this;
3368 }
3369
3370 /**
Joe Onoratocb109a02011-01-18 17:57:41 -08003371 * Set this flag if you would only like the sound, vibrate
3372 * and ticker to be played if the notification is not already showing.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003373 *
3374 * @see Notification#FLAG_ONLY_ALERT_ONCE
Joe Onoratocb109a02011-01-18 17:57:41 -08003375 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003376 public Builder setOnlyAlertOnce(boolean onlyAlertOnce) {
3377 setFlag(FLAG_ONLY_ALERT_ONCE, onlyAlertOnce);
3378 return this;
3379 }
3380
Joe Onoratocb109a02011-01-18 17:57:41 -08003381 /**
Julia Reynolds04499532016-09-13 14:04:53 -04003382 * Make this notification automatically dismissed when the user touches it.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003383 *
3384 * @see Notification#FLAG_AUTO_CANCEL
Joe Onoratocb109a02011-01-18 17:57:41 -08003385 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003386 public Builder setAutoCancel(boolean autoCancel) {
Joe Onorato281d83f2011-01-04 17:13:10 -08003387 setFlag(FLAG_AUTO_CANCEL, autoCancel);
Joe Onorato46439ce2010-11-19 13:56:21 -08003388 return this;
3389 }
3390
Joe Onoratocb109a02011-01-18 17:57:41 -08003391 /**
Griff Hazendfcb0802014-02-11 12:00:00 -08003392 * Set whether or not this notification should not bridge to other devices.
3393 *
3394 * <p>Some notifications can be bridged to other devices for remote display.
3395 * This hint can be set to recommend this notification not be bridged.
3396 */
3397 public Builder setLocalOnly(boolean localOnly) {
3398 setFlag(FLAG_LOCAL_ONLY, localOnly);
3399 return this;
3400 }
3401
3402 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003403 * Set which notification properties will be inherited from system defaults.
Joe Onoratocb109a02011-01-18 17:57:41 -08003404 * <p>
3405 * The value should be one or more of the following fields combined with
3406 * bitwise-or:
3407 * {@link #DEFAULT_SOUND}, {@link #DEFAULT_VIBRATE}, {@link #DEFAULT_LIGHTS}.
3408 * <p>
3409 * For all default values, use {@link #DEFAULT_ALL}.
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003410 *
3411 * @deprecated use {@link NotificationChannel#enableVibration(boolean)} and
Julia Reynolds529e3322017-02-06 08:33:01 -05003412 * {@link NotificationChannel#enableLights(boolean)} and
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003413 * {@link NotificationChannel#setSound(Uri, AudioAttributes)} instead.
Joe Onoratocb109a02011-01-18 17:57:41 -08003414 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003415 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08003416 public Builder setDefaults(int defaults) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003417 mN.defaults = defaults;
Joe Onorato46439ce2010-11-19 13:56:21 -08003418 return this;
3419 }
3420
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003421 /**
3422 * Set the priority of this notification.
3423 *
3424 * @see Notification#priority
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003425 * @deprecated use {@link NotificationChannel#setImportance(int)} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003426 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003427 @Deprecated
Tor Norbyed9273d62013-05-30 15:59:53 -07003428 public Builder setPriority(@Priority int pri) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003429 mN.priority = pri;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003430 return this;
3431 }
Joe Malin8d40d042012-11-05 11:36:40 -08003432
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003433 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -04003434 * Set the notification category.
Joe Malin8d40d042012-11-05 11:36:40 -08003435 *
John Spurlockfd7f1e02014-03-18 16:41:57 -04003436 * @see Notification#category
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003437 */
John Spurlockfd7f1e02014-03-18 16:41:57 -04003438 public Builder setCategory(String category) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003439 mN.category = category;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003440 return this;
3441 }
3442
3443 /**
Chris Wrendde75302014-03-26 17:24:15 -04003444 * Add a person that is relevant to this notification.
3445 *
Chris Wrene6c48932014-09-29 17:19:27 -04003446 * <P>
3447 * Depending on user preferences, this annotation may allow the notification to pass
Julia Reynoldse071abd2017-03-22 10:52:11 -04003448 * through interruption filters, if this notification is of category {@link #CATEGORY_CALL}
3449 * or {@link #CATEGORY_MESSAGE}. The addition of people may also cause this notification to
3450 * appear more prominently in the user interface.
Chris Wrene6c48932014-09-29 17:19:27 -04003451 * </P>
3452 *
3453 * <P>
3454 * The person should be specified by the {@code String} representation of a
3455 * {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI}.
3456 * </P>
3457 *
3458 * <P>The system will also attempt to resolve {@code mailto:} and {@code tel:} schema
3459 * URIs. The path part of these URIs must exist in the contacts database, in the
3460 * appropriate column, or the reference will be discarded as invalid. Telephone schema
3461 * URIs will be resolved by {@link android.provider.ContactsContract.PhoneLookup}.
3462 * </P>
3463 *
3464 * @param uri A URI for the person.
Chris Wrendde75302014-03-26 17:24:15 -04003465 * @see Notification#EXTRA_PEOPLE
3466 */
Chris Wrene6c48932014-09-29 17:19:27 -04003467 public Builder addPerson(String uri) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003468 mPersonList.add(uri);
Chris Wrendde75302014-03-26 17:24:15 -04003469 return this;
3470 }
3471
3472 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003473 * Set this notification to be part of a group of notifications sharing the same key.
3474 * Grouped notifications may display in a cluster or stack on devices which
3475 * support such rendering.
3476 *
3477 * <p>To make this notification the summary for its group, also call
3478 * {@link #setGroupSummary}. A sort order can be specified for group members by using
3479 * {@link #setSortKey}.
3480 * @param groupKey The group key of the group.
3481 * @return this object for method chaining
3482 */
3483 public Builder setGroup(String groupKey) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003484 mN.mGroupKey = groupKey;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003485 return this;
3486 }
3487
3488 /**
3489 * Set this notification to be the group summary for a group of notifications.
3490 * Grouped notifications may display in a cluster or stack on devices which
Julia Reynolds04499532016-09-13 14:04:53 -04003491 * support such rendering. If thereRequires a group key also be set using {@link #setGroup}.
3492 * The group summary may be suppressed if too few notifications are included in the group.
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003493 * @param isGroupSummary Whether this notification should be a group summary.
3494 * @return this object for method chaining
3495 */
3496 public Builder setGroupSummary(boolean isGroupSummary) {
3497 setFlag(FLAG_GROUP_SUMMARY, isGroupSummary);
3498 return this;
3499 }
3500
3501 /**
3502 * Set a sort key that orders this notification among other notifications from the
3503 * same package. This can be useful if an external sort was already applied and an app
3504 * would like to preserve this. Notifications will be sorted lexicographically using this
3505 * value, although providing different priorities in addition to providing sort key may
3506 * cause this value to be ignored.
3507 *
3508 * <p>This sort key can also be used to order members of a notification group. See
Griff Hazen9e1379f2014-05-20 12:50:51 -07003509 * {@link #setGroup}.
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003510 *
3511 * @see String#compareTo(String)
3512 */
3513 public Builder setSortKey(String sortKey) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003514 mN.mSortKey = sortKey;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003515 return this;
3516 }
3517
3518 /**
Griff Hazen720042b2014-02-24 15:46:56 -08003519 * Merge additional metadata into this notification.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003520 *
Griff Hazen720042b2014-02-24 15:46:56 -08003521 * <p>Values within the Bundle will replace existing extras values in this Builder.
3522 *
3523 * @see Notification#extras
3524 */
Griff Hazen959591e2014-05-15 22:26:18 -07003525 public Builder addExtras(Bundle extras) {
3526 if (extras != null) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003527 mUserExtras.putAll(extras);
Griff Hazen720042b2014-02-24 15:46:56 -08003528 }
3529 return this;
3530 }
3531
3532 /**
3533 * Set metadata for this notification.
3534 *
3535 * <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 -04003536 * current contents are copied into the Notification each time {@link #build()} is
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003537 * called.
3538 *
Griff Hazen720042b2014-02-24 15:46:56 -08003539 * <p>Replaces any existing extras values with those from the provided Bundle.
3540 * Use {@link #addExtras} to merge in metadata instead.
3541 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003542 * @see Notification#extras
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003543 */
Griff Hazen959591e2014-05-15 22:26:18 -07003544 public Builder setExtras(Bundle extras) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003545 if (extras != null) {
3546 mUserExtras = extras;
3547 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003548 return this;
3549 }
3550
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003551 /**
Griff Hazen720042b2014-02-24 15:46:56 -08003552 * Get the current metadata Bundle used by this notification Builder.
3553 *
3554 * <p>The returned Bundle is shared with this Builder.
3555 *
3556 * <p>The current contents of this Bundle are copied into the Notification each time
3557 * {@link #build()} is called.
3558 *
3559 * @see Notification#extras
3560 */
3561 public Bundle getExtras() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003562 return mUserExtras;
3563 }
3564
3565 private Bundle getAllExtras() {
3566 final Bundle saveExtras = (Bundle) mUserExtras.clone();
3567 saveExtras.putAll(mN.extras);
3568 return saveExtras;
Griff Hazen720042b2014-02-24 15:46:56 -08003569 }
3570
3571 /**
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003572 * Add an action to this notification. Actions are typically displayed by
3573 * the system as a button adjacent to the notification content.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04003574 * <p>
3575 * Every action must have an icon (32dp square and matching the
3576 * <a href="{@docRoot}design/style/iconography.html#action-bar">Holo
3577 * Dark action bar</a> visual style), a textual label, and a {@link PendingIntent}.
3578 * <p>
3579 * A notification in its expanded form can display up to 3 actions, from left to right in
3580 * the order they were added. Actions will not be displayed when the notification is
3581 * collapsed, however, so be sure that any essential functions may be accessed by the user
3582 * in some other way (for example, in the Activity pointed to by {@link #contentIntent}).
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003583 *
3584 * @param icon Resource ID of a drawable that represents the action.
3585 * @param title Text describing the action.
3586 * @param intent PendingIntent to be fired when the action is invoked.
Dan Sandler86647982015-05-13 23:41:13 -04003587 *
3588 * @deprecated Use {@link #addAction(Action)} instead.
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003589 */
Dan Sandler86647982015-05-13 23:41:13 -04003590 @Deprecated
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003591 public Builder addAction(int icon, CharSequence title, PendingIntent intent) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04003592 mActions.add(new Action(icon, safeCharSequence(title), intent));
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003593 return this;
3594 }
3595
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003596 /**
Griff Hazen959591e2014-05-15 22:26:18 -07003597 * Add an action to this notification. Actions are typically displayed by
3598 * the system as a button adjacent to the notification content.
3599 * <p>
3600 * Every action must have an icon (32dp square and matching the
3601 * <a href="{@docRoot}design/style/iconography.html#action-bar">Holo
3602 * Dark action bar</a> visual style), a textual label, and a {@link PendingIntent}.
3603 * <p>
3604 * A notification in its expanded form can display up to 3 actions, from left to right in
3605 * the order they were added. Actions will not be displayed when the notification is
3606 * collapsed, however, so be sure that any essential functions may be accessed by the user
3607 * in some other way (for example, in the Activity pointed to by {@link #contentIntent}).
3608 *
3609 * @param action The action to add.
3610 */
3611 public Builder addAction(Action action) {
liangweikang63b03b52017-03-16 19:22:15 +08003612 if (action != null) {
3613 mActions.add(action);
3614 }
Griff Hazen959591e2014-05-15 22:26:18 -07003615 return this;
3616 }
3617
3618 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003619 * Alter the complete list of actions attached to this notification.
3620 * @see #addAction(Action).
3621 *
3622 * @param actions
3623 * @return
3624 */
3625 public Builder setActions(Action... actions) {
3626 mActions.clear();
3627 for (int i = 0; i < actions.length; i++) {
liangweikang63b03b52017-03-16 19:22:15 +08003628 if (actions[i] != null) {
3629 mActions.add(actions[i]);
3630 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003631 }
3632 return this;
3633 }
3634
3635 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003636 * Add a rich notification style to be applied at build time.
3637 *
3638 * @param style Object responsible for modifying the notification style.
3639 */
3640 public Builder setStyle(Style style) {
3641 if (mStyle != style) {
3642 mStyle = style;
Daniel Sandlerc08dea22012-06-28 08:35:24 -07003643 if (mStyle != null) {
3644 mStyle.setBuilder(this);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003645 mN.extras.putString(EXTRA_TEMPLATE, style.getClass().getName());
3646 } else {
3647 mN.extras.remove(EXTRA_TEMPLATE);
Daniel Sandlerc08dea22012-06-28 08:35:24 -07003648 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003649 }
3650 return this;
3651 }
3652
Dan Sandler0bf2ed82013-12-21 23:33:41 -06003653 /**
3654 * Specify the value of {@link #visibility}.
Griff Hazenb720abe2014-05-20 13:15:30 -07003655 *
Dan Sandler0bf2ed82013-12-21 23:33:41 -06003656 * @return The same Builder.
3657 */
Jeff Sharkey30e06bb2017-04-24 11:18:03 -06003658 public Builder setVisibility(@Visibility int visibility) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003659 mN.visibility = visibility;
Dan Sandler0bf2ed82013-12-21 23:33:41 -06003660 return this;
3661 }
3662
3663 /**
3664 * Supply a replacement Notification whose contents should be shown in insecure contexts
3665 * (i.e. atop the secure lockscreen). See {@link #visibility} and {@link #VISIBILITY_PUBLIC}.
3666 * @param n A replacement notification, presumably with some or all info redacted.
3667 * @return The same Builder.
3668 */
3669 public Builder setPublicVersion(Notification n) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003670 if (n != null) {
3671 mN.publicVersion = new Notification();
3672 n.cloneInto(mN.publicVersion, /*heavy=*/ true);
3673 } else {
3674 mN.publicVersion = null;
3675 }
Dan Sandler0bf2ed82013-12-21 23:33:41 -06003676 return this;
3677 }
3678
Griff Hazenb720abe2014-05-20 13:15:30 -07003679 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003680 * Apply an extender to this notification builder. Extenders may be used to add
3681 * metadata or change options on this builder.
3682 */
Griff Hazen61a9e862014-05-22 16:05:19 -07003683 public Builder extend(Extender extender) {
3684 extender.extend(this);
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003685 return this;
3686 }
3687
Dan Sandler4e787062015-06-17 15:09:48 -04003688 /**
3689 * @hide
3690 */
Julia Reynoldse46bb372016-03-17 11:05:58 -04003691 public Builder setFlag(int mask, boolean value) {
Joe Onorato46439ce2010-11-19 13:56:21 -08003692 if (value) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003693 mN.flags |= mask;
Joe Onorato46439ce2010-11-19 13:56:21 -08003694 } else {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003695 mN.flags &= ~mask;
Joe Onorato46439ce2010-11-19 13:56:21 -08003696 }
Julia Reynoldse46bb372016-03-17 11:05:58 -04003697 return this;
Joe Onorato46439ce2010-11-19 13:56:21 -08003698 }
3699
Dan Sandler26e81cf2014-05-06 10:01:27 -04003700 /**
3701 * Sets {@link Notification#color}.
3702 *
3703 * @param argb The accent color to use
3704 *
3705 * @return The same Builder.
3706 */
Tor Norbye80756e32015-03-02 09:39:27 -08003707 public Builder setColor(@ColorInt int argb) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003708 mN.color = argb;
Julia Reynolds10ee1fc2015-11-09 11:04:55 -05003709 sanitizeColor();
Dan Sandler26e81cf2014-05-06 10:01:27 -04003710 return this;
3711 }
3712
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003713 private Drawable getProfileBadgeDrawable() {
Chris Wren66619a22016-05-12 16:42:37 -04003714 if (mContext.getUserId() == UserHandle.USER_SYSTEM) {
3715 // This user can never be a badged profile,
3716 // and also includes USER_ALL system notifications.
3717 return null;
3718 }
Christoph Studer7ac80e62014-08-04 16:01:57 +02003719 // Note: This assumes that the current user can read the profile badge of the
3720 // originating user.
Selim Cineke6ff9462016-01-15 15:07:06 -08003721 return mContext.getPackageManager().getUserBadgeForDensityNoBackground(
Julia Reynoldsda303542015-11-23 14:00:20 -05003722 new UserHandle(mContext.getUserId()), 0);
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003723 }
3724
3725 private Bitmap getProfileBadge() {
3726 Drawable badge = getProfileBadgeDrawable();
Kenny Guy8a0101b2014-05-08 23:34:12 +01003727 if (badge == null) {
3728 return null;
3729 }
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003730 final int size = mContext.getResources().getDimensionPixelSize(
3731 R.dimen.notification_badge_size);
3732 Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
Kenny Guy8a0101b2014-05-08 23:34:12 +01003733 Canvas canvas = new Canvas(bitmap);
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003734 badge.setBounds(0, 0, size, size);
Kenny Guy8a0101b2014-05-08 23:34:12 +01003735 badge.draw(canvas);
3736 return bitmap;
3737 }
3738
Selim Cinekc848c3a2016-01-13 15:27:30 -08003739 private void bindProfileBadge(RemoteViews contentView) {
Kenny Guy98193ea2014-07-24 19:54:37 +01003740 Bitmap profileBadge = getProfileBadge();
3741
Kenny Guy98193ea2014-07-24 19:54:37 +01003742 if (profileBadge != null) {
Selim Cinekc848c3a2016-01-13 15:27:30 -08003743 contentView.setImageViewBitmap(R.id.profile_badge, profileBadge);
3744 contentView.setViewVisibility(R.id.profile_badge, View.VISIBLE);
Selim Cinek7b9605b2017-01-19 17:36:00 -08003745 if (isColorized()) {
3746 contentView.setDrawableParameters(R.id.profile_badge, false, -1,
3747 getPrimaryTextColor(), PorterDuff.Mode.SRC_ATOP, -1);
3748 }
Kenny Guy98193ea2014-07-24 19:54:37 +01003749 }
Kenny Guy98193ea2014-07-24 19:54:37 +01003750 }
3751
Christoph Studerfe718432014-09-01 18:21:18 +02003752 private void resetStandardTemplate(RemoteViews contentView) {
Selim Cinekeaa29ca2015-11-23 13:51:13 -08003753 resetNotificationHeader(contentView);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003754 resetContentMargins(contentView);
Christoph Studerfe718432014-09-01 18:21:18 +02003755 contentView.setViewVisibility(R.id.right_icon, View.GONE);
Selim Cinek860b6da2015-12-16 19:02:19 -08003756 contentView.setViewVisibility(R.id.title, View.GONE);
Christoph Studerfe718432014-09-01 18:21:18 +02003757 contentView.setTextViewText(R.id.title, null);
Selim Cinek41598732016-01-11 16:58:37 -08003758 contentView.setViewVisibility(R.id.text, View.GONE);
Christoph Studerfe718432014-09-01 18:21:18 +02003759 contentView.setTextViewText(R.id.text, null);
Selim Cinek29603462015-11-17 19:04:39 -08003760 contentView.setViewVisibility(R.id.text_line_1, View.GONE);
Selim Cinek41598732016-01-11 16:58:37 -08003761 contentView.setTextViewText(R.id.text_line_1, null);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003762 }
3763
Selim Cinekeaa29ca2015-11-23 13:51:13 -08003764 /**
3765 * Resets the notification header to its original state
3766 */
3767 private void resetNotificationHeader(RemoteViews contentView) {
Adrian Roosc4337a32016-08-02 18:30:34 -07003768 // Small icon doesn't need to be reset, as it's always set. Resetting would prevent
3769 // re-using the drawable when the notification is updated.
Selim Cinek7b836392015-12-04 20:02:59 -08003770 contentView.setBoolean(R.id.notification_header, "setExpanded", false);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003771 contentView.setTextViewText(R.id.app_name_text, null);
Christoph Studerca1db712014-09-10 17:31:33 +02003772 contentView.setViewVisibility(R.id.chronometer, View.GONE);
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07003773 contentView.setViewVisibility(R.id.header_text, View.GONE);
Adrian Roos9dfb78f2016-06-30 15:43:44 -07003774 contentView.setTextViewText(R.id.header_text, null);
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07003775 contentView.setViewVisibility(R.id.header_text_divider, View.GONE);
Selim Cinek29603462015-11-17 19:04:39 -08003776 contentView.setViewVisibility(R.id.time_divider, View.GONE);
Selim Cinekb85f36fd2016-04-20 18:46:36 -07003777 contentView.setViewVisibility(R.id.time, View.GONE);
Selim Cinekc848c3a2016-01-13 15:27:30 -08003778 contentView.setImageViewIcon(R.id.profile_badge, null);
3779 contentView.setViewVisibility(R.id.profile_badge, View.GONE);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003780 }
3781
3782 private void resetContentMargins(RemoteViews contentView) {
Adrian Roos2d5dbba2016-06-08 17:11:53 -07003783 contentView.setViewLayoutMarginEndDimen(R.id.line1, 0);
3784 contentView.setViewLayoutMarginEndDimen(R.id.text, 0);
Christoph Studerfe718432014-09-01 18:21:18 +02003785 }
3786
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003787 private RemoteViews applyStandardTemplate(int resId) {
Adrian Roos70d7aa32017-01-11 15:39:06 -08003788 return applyStandardTemplate(resId, mParams.reset().fillTextsFrom(this));
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02003789 }
3790
3791 /**
3792 * @param hasProgress whether the progress bar should be shown and set
3793 */
3794 private RemoteViews applyStandardTemplate(int resId, boolean hasProgress) {
Adrian Roos70d7aa32017-01-11 15:39:06 -08003795 return applyStandardTemplate(resId, mParams.reset().hasProgress(hasProgress)
3796 .fillTextsFrom(this));
Adrian Roosc1a80b02016-04-05 14:54:55 -07003797 }
3798
Adrian Roos70d7aa32017-01-11 15:39:06 -08003799 private RemoteViews applyStandardTemplate(int resId, StandardTemplateParams p) {
Kenny Guy77320062014-08-27 21:37:15 +01003800 RemoteViews contentView = new BuilderRemoteViews(mContext.getApplicationInfo(), resId);
Dan Sandler539aad42014-08-04 00:43:39 -04003801
Christoph Studerfe718432014-09-01 18:21:18 +02003802 resetStandardTemplate(contentView);
3803
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003804 final Bundle ex = mN.extras;
Selim Cinek7b9605b2017-01-19 17:36:00 -08003805 updateBackgroundColor(contentView);
Adrian Roos487374f2017-01-11 15:48:14 -08003806 bindNotificationHeader(contentView, p.ambient);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003807 bindLargeIcon(contentView);
Adrian Roos70d7aa32017-01-11 15:39:06 -08003808 boolean showProgress = handleProgressBar(p.hasProgress, contentView, ex);
3809 if (p.title != null) {
Selim Cinek860b6da2015-12-16 19:02:19 -08003810 contentView.setViewVisibility(R.id.title, View.VISIBLE);
Adrian Roos70d7aa32017-01-11 15:39:06 -08003811 contentView.setTextViewText(R.id.title, p.title);
Adrian Roos72171622017-01-27 10:32:06 -08003812 if (!p.ambient) {
3813 setTextViewColorPrimary(contentView, R.id.title);
3814 }
Selim Cinek954cc232016-05-20 13:29:23 -07003815 contentView.setViewLayoutWidth(R.id.title, showProgress
3816 ? ViewGroup.LayoutParams.WRAP_CONTENT
3817 : ViewGroup.LayoutParams.MATCH_PARENT);
Joe Onorato561d3852010-11-20 18:09:34 -08003818 }
Adrian Roos70d7aa32017-01-11 15:39:06 -08003819 if (p.text != null) {
Selim Cinek41598732016-01-11 16:58:37 -08003820 int textId = showProgress ? com.android.internal.R.id.text_line_1
3821 : com.android.internal.R.id.text;
Adrian Roos70d7aa32017-01-11 15:39:06 -08003822 contentView.setTextViewText(textId, p.text);
Adrian Roos72171622017-01-27 10:32:06 -08003823 if (!p.ambient) {
3824 setTextViewColorSecondary(contentView, textId);
3825 }
Selim Cinek41598732016-01-11 16:58:37 -08003826 contentView.setViewVisibility(textId, View.VISIBLE);
Joe Onorato561d3852010-11-20 18:09:34 -08003827 }
Selim Cinekc848c3a2016-01-13 15:27:30 -08003828
Selim Cinek279fa862016-06-14 10:57:25 -07003829 setContentMinHeight(contentView, showProgress || mN.hasLargeIcon());
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003830
Selim Cinek29603462015-11-17 19:04:39 -08003831 return contentView;
3832 }
3833
Selim Cinek7b9605b2017-01-19 17:36:00 -08003834 private void setTextViewColorPrimary(RemoteViews contentView, int id) {
3835 ensureColors();
3836 contentView.setTextColor(id, mPrimaryTextColor);
3837 }
3838
3839 private int getPrimaryTextColor() {
3840 ensureColors();
3841 return mPrimaryTextColor;
3842 }
3843
3844 private int getActionBarColor() {
3845 ensureColors();
3846 return mActionBarColor;
3847 }
3848
Selim Cinek622c64a2017-04-17 17:10:05 -07003849 private int getActionBarColorDeEmphasized() {
3850 int backgroundColor = getBackgroundColor();
3851 return NotificationColorUtil.getShiftedColor(backgroundColor, 12);
3852 }
3853
Selim Cinek7b9605b2017-01-19 17:36:00 -08003854 private void setTextViewColorSecondary(RemoteViews contentView, int id) {
3855 ensureColors();
3856 contentView.setTextColor(id, mSecondaryTextColor);
3857 }
3858
3859 private void ensureColors() {
3860 int backgroundColor = getBackgroundColor();
3861 if (mPrimaryTextColor == COLOR_INVALID
3862 || mSecondaryTextColor == COLOR_INVALID
3863 || mActionBarColor == COLOR_INVALID
3864 || mTextColorsAreForBackground != backgroundColor) {
3865 mTextColorsAreForBackground = backgroundColor;
Selim Cinek5fb73f82017-04-20 16:55:38 -07003866 if (mForegroundColor == COLOR_INVALID || !isColorized()) {
3867 mPrimaryTextColor = NotificationColorUtil.resolvePrimaryColor(mContext,
3868 backgroundColor);
3869 mSecondaryTextColor = NotificationColorUtil.resolveSecondaryColor(mContext,
3870 backgroundColor);
Selim Cinekac5f0272017-05-02 16:05:41 -07003871 if (backgroundColor != COLOR_DEFAULT
3872 && (mBackgroundColorHint != COLOR_INVALID || isColorized())) {
3873 mPrimaryTextColor = NotificationColorUtil.findAlphaToMeetContrast(
3874 mPrimaryTextColor, backgroundColor, 4.5);
3875 mSecondaryTextColor = NotificationColorUtil.findAlphaToMeetContrast(
3876 mSecondaryTextColor, backgroundColor, 4.5);
3877 }
Selim Cinek5fb73f82017-04-20 16:55:38 -07003878 } else {
3879 double backLum = NotificationColorUtil.calculateLuminance(backgroundColor);
3880 double textLum = NotificationColorUtil.calculateLuminance(mForegroundColor);
3881 double contrast = NotificationColorUtil.calculateContrast(mForegroundColor,
3882 backgroundColor);
3883 boolean textDark = backLum > textLum;
3884 if (contrast < 4.5f) {
3885 if (textDark) {
3886 mSecondaryTextColor = NotificationColorUtil.findContrastColor(
3887 mForegroundColor,
3888 backgroundColor,
3889 true /* findFG */,
3890 4.5f);
3891 mPrimaryTextColor = NotificationColorUtil.changeColorLightness(
3892 mSecondaryTextColor, -20);
3893 } else {
3894 mSecondaryTextColor =
3895 NotificationColorUtil.findContrastColorAgainstDark(
3896 mForegroundColor,
3897 backgroundColor,
3898 true /* findFG */,
3899 4.5f);
3900 mPrimaryTextColor = NotificationColorUtil.changeColorLightness(
3901 mSecondaryTextColor, 10);
3902 }
3903 } else {
3904 mPrimaryTextColor = mForegroundColor;
3905 mSecondaryTextColor = NotificationColorUtil.changeColorLightness(
3906 mPrimaryTextColor, textDark ? 10 : -20);
3907 if (NotificationColorUtil.calculateContrast(mSecondaryTextColor,
3908 backgroundColor) < 4.5f) {
3909 // oh well the secondary is not good enough
3910 if (textDark) {
3911 mSecondaryTextColor = NotificationColorUtil.findContrastColor(
3912 mSecondaryTextColor,
3913 backgroundColor,
3914 true /* findFG */,
3915 4.5f);
3916 } else {
3917 mSecondaryTextColor
3918 = NotificationColorUtil.findContrastColorAgainstDark(
3919 mSecondaryTextColor,
3920 backgroundColor,
3921 true /* findFG */,
3922 4.5f);
3923 }
3924 mPrimaryTextColor = NotificationColorUtil.changeColorLightness(
3925 mSecondaryTextColor, textDark ? -20 : 10);
3926 }
3927 }
3928 }
Selim Cinek875ba9b2017-02-13 16:20:17 -08003929 mActionBarColor = NotificationColorUtil.resolveActionBarColor(mContext,
3930 backgroundColor);
Selim Cinek7b9605b2017-01-19 17:36:00 -08003931 }
3932 }
3933
3934 private void updateBackgroundColor(RemoteViews contentView) {
3935 if (isColorized()) {
3936 contentView.setInt(R.id.status_bar_latest_event_content, "setBackgroundColor",
3937 getBackgroundColor());
3938 } else {
3939 // Clear it!
3940 contentView.setInt(R.id.status_bar_latest_event_content, "setBackgroundResource",
3941 0);
3942 }
3943 }
3944
Selim Cinek860b6da2015-12-16 19:02:19 -08003945 /**
3946 * @param remoteView the remote view to update the minheight in
3947 * @param hasMinHeight does it have a mimHeight
3948 * @hide
3949 */
3950 void setContentMinHeight(RemoteViews remoteView, boolean hasMinHeight) {
3951 int minHeight = 0;
3952 if (hasMinHeight) {
3953 // we need to set the minHeight of the notification
3954 minHeight = mContext.getResources().getDimensionPixelSize(
3955 com.android.internal.R.dimen.notification_min_content_height);
3956 }
3957 remoteView.setInt(R.id.notification_main_column, "setMinimumHeight", minHeight);
3958 }
3959
Selim Cinek29603462015-11-17 19:04:39 -08003960 private boolean handleProgressBar(boolean hasProgress, RemoteViews contentView, Bundle ex) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003961 final int max = ex.getInt(EXTRA_PROGRESS_MAX, 0);
3962 final int progress = ex.getInt(EXTRA_PROGRESS, 0);
3963 final boolean ind = ex.getBoolean(EXTRA_PROGRESS_INDETERMINATE);
3964 if (hasProgress && (max != 0 || ind)) {
Selim Cinek29603462015-11-17 19:04:39 -08003965 contentView.setViewVisibility(com.android.internal.R.id.progress, View.VISIBLE);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003966 contentView.setProgressBar(
Selim Cinek29603462015-11-17 19:04:39 -08003967 R.id.progress, max, progress, ind);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003968 contentView.setProgressBackgroundTintList(
3969 R.id.progress, ColorStateList.valueOf(mContext.getColor(
3970 R.color.notification_progress_background_color)));
Selim Cinek29603462015-11-17 19:04:39 -08003971 if (mN.color != COLOR_DEFAULT) {
Adrian Roos4ff3b122016-02-01 12:26:13 -08003972 ColorStateList colorStateList = ColorStateList.valueOf(resolveContrastColor());
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003973 contentView.setProgressTintList(R.id.progress, colorStateList);
3974 contentView.setProgressIndeterminateTintList(R.id.progress, colorStateList);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003975 }
Selim Cinek29603462015-11-17 19:04:39 -08003976 return true;
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003977 } else {
3978 contentView.setViewVisibility(R.id.progress, View.GONE);
Selim Cinek29603462015-11-17 19:04:39 -08003979 return false;
Jeff Sharkey1c400132011-08-05 14:50:13 -07003980 }
Joe Onorato561d3852010-11-20 18:09:34 -08003981 }
3982
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003983 private void bindLargeIcon(RemoteViews contentView) {
Selim Cinek279fa862016-06-14 10:57:25 -07003984 if (mN.mLargeIcon == null && mN.largeIcon != null) {
3985 mN.mLargeIcon = Icon.createWithBitmap(mN.largeIcon);
3986 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003987 if (mN.mLargeIcon != null) {
3988 contentView.setViewVisibility(R.id.right_icon, View.VISIBLE);
3989 contentView.setImageViewIcon(R.id.right_icon, mN.mLargeIcon);
3990 processLargeLegacyIcon(mN.mLargeIcon, contentView);
Adrian Roos2d5dbba2016-06-08 17:11:53 -07003991 int endMargin = R.dimen.notification_content_picture_margin;
3992 contentView.setViewLayoutMarginEndDimen(R.id.line1, endMargin);
3993 contentView.setViewLayoutMarginEndDimen(R.id.text, endMargin);
3994 contentView.setViewLayoutMarginEndDimen(R.id.progress, endMargin);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003995 }
3996 }
3997
Adrian Roos487374f2017-01-11 15:48:14 -08003998 private void bindNotificationHeader(RemoteViews contentView, boolean ambient) {
3999 bindSmallIcon(contentView, ambient);
4000 bindHeaderAppName(contentView, ambient);
4001 if (!ambient) {
4002 // Ambient view does not have these
4003 bindHeaderText(contentView);
4004 bindHeaderChronometerAndTime(contentView);
Adrian Roos487374f2017-01-11 15:48:14 -08004005 bindProfileBadge(contentView);
4006 }
Adrian Roosd83e9992017-03-16 15:17:57 -07004007 bindExpandButton(contentView);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004008 }
4009
4010 private void bindExpandButton(RemoteViews contentView) {
Selim Cinek99104832017-01-25 14:47:33 -08004011 int color = getPrimaryHighlightColor();
Selim Cinek7b9605b2017-01-19 17:36:00 -08004012 contentView.setDrawableParameters(R.id.expand_button, false, -1, color,
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004013 PorterDuff.Mode.SRC_ATOP, -1);
Selim Cinekea4bef72015-12-02 15:51:10 -08004014 contentView.setInt(R.id.notification_header, "setOriginalNotificationColor",
Selim Cinek7b9605b2017-01-19 17:36:00 -08004015 color);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004016 }
4017
Selim Cinek99104832017-01-25 14:47:33 -08004018 /**
4019 * @return the color that is used as the first primary highlight color. This is applied
4020 * in several places like the action buttons or the app name in the header.
4021 */
4022 private int getPrimaryHighlightColor() {
4023 return isColorized() ? getPrimaryTextColor() : resolveContrastColor();
4024 }
4025
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004026 private void bindHeaderChronometerAndTime(RemoteViews contentView) {
4027 if (showsTimeOrChronometer()) {
Selim Cinek29603462015-11-17 19:04:39 -08004028 contentView.setViewVisibility(R.id.time_divider, View.VISIBLE);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004029 setTextViewColorSecondary(contentView, R.id.time_divider);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004030 if (mN.extras.getBoolean(EXTRA_SHOW_CHRONOMETER)) {
4031 contentView.setViewVisibility(R.id.chronometer, View.VISIBLE);
4032 contentView.setLong(R.id.chronometer, "setBase",
4033 mN.when + (SystemClock.elapsedRealtime() - System.currentTimeMillis()));
4034 contentView.setBoolean(R.id.chronometer, "setStarted", true);
Adrian Roos96b7e202016-05-17 13:50:38 -07004035 boolean countsDown = mN.extras.getBoolean(EXTRA_CHRONOMETER_COUNT_DOWN);
Selim Cinekc3b752e2016-04-20 16:13:59 -07004036 contentView.setChronometerCountDown(R.id.chronometer, countsDown);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004037 setTextViewColorSecondary(contentView, R.id.chronometer);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004038 } else {
4039 contentView.setViewVisibility(R.id.time, View.VISIBLE);
4040 contentView.setLong(R.id.time, "setTime", mN.when);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004041 setTextViewColorSecondary(contentView, R.id.time);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004042 }
Selim Cinekb85f36fd2016-04-20 18:46:36 -07004043 } else {
4044 // We still want a time to be set but gone, such that we can show and hide it
4045 // on demand in case it's a child notification without anything in the header
4046 contentView.setLong(R.id.time, "setTime", mN.when != 0 ? mN.when : mN.creationTime);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004047 }
4048 }
4049
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07004050 private void bindHeaderText(RemoteViews contentView) {
4051 CharSequence headerText = mN.extras.getCharSequence(EXTRA_SUB_TEXT);
4052 if (headerText == null && mStyle != null && mStyle.mSummaryTextSet
Selim Cinek03d0d652015-11-13 13:18:09 -05004053 && mStyle.hasSummaryInHeader()) {
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07004054 headerText = mStyle.mSummaryText;
Selim Cinek03d0d652015-11-13 13:18:09 -05004055 }
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07004056 if (headerText == null
4057 && mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N
4058 && mN.extras.getCharSequence(EXTRA_INFO_TEXT) != null) {
4059 headerText = mN.extras.getCharSequence(EXTRA_INFO_TEXT);
4060 }
4061 if (headerText != null) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004062 // TODO: Remove the span entirely to only have the string with propper formating.
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07004063 contentView.setTextViewText(R.id.header_text, processLegacyText(headerText));
Selim Cinek7b9605b2017-01-19 17:36:00 -08004064 setTextViewColorSecondary(contentView, R.id.header_text);
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07004065 contentView.setViewVisibility(R.id.header_text, View.VISIBLE);
4066 contentView.setViewVisibility(R.id.header_text_divider, View.VISIBLE);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004067 setTextViewColorSecondary(contentView, R.id.header_text_divider);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004068 }
4069 }
4070
Adrian Rooseba05822016-04-22 17:09:27 -07004071 /**
4072 * @hide
4073 */
4074 public String loadHeaderAppName() {
Dan Sandler732bd6c2016-04-12 14:20:32 -04004075 CharSequence name = null;
4076 final PackageManager pm = mContext.getPackageManager();
4077 if (mN.extras.containsKey(EXTRA_SUBSTITUTE_APP_NAME)) {
4078 // only system packages which lump together a bunch of unrelated stuff
4079 // may substitute a different name to make the purpose of the
4080 // notification more clear. the correct package label should always
4081 // be accessible via SystemUI.
4082 final String pkg = mContext.getPackageName();
4083 final String subName = mN.extras.getString(EXTRA_SUBSTITUTE_APP_NAME);
4084 if (PackageManager.PERMISSION_GRANTED == pm.checkPermission(
4085 android.Manifest.permission.SUBSTITUTE_NOTIFICATION_APP_NAME, pkg)) {
4086 name = subName;
4087 } else {
4088 Log.w(TAG, "warning: pkg "
4089 + pkg + " attempting to substitute app name '" + subName
4090 + "' without holding perm "
4091 + android.Manifest.permission.SUBSTITUTE_NOTIFICATION_APP_NAME);
4092 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004093 }
Dan Sandler732bd6c2016-04-12 14:20:32 -04004094 if (TextUtils.isEmpty(name)) {
4095 name = pm.getApplicationLabel(mContext.getApplicationInfo());
4096 }
4097 if (TextUtils.isEmpty(name)) {
4098 // still nothing?
4099 return null;
4100 }
4101
4102 return String.valueOf(name);
4103 }
Adrian Roos487374f2017-01-11 15:48:14 -08004104 private void bindHeaderAppName(RemoteViews contentView, boolean ambient) {
Dan Sandler732bd6c2016-04-12 14:20:32 -04004105 contentView.setTextViewText(R.id.app_name_text, loadHeaderAppName());
Adrian Roos72171622017-01-27 10:32:06 -08004106 if (isColorized() && !ambient) {
Selim Cinek7b9605b2017-01-19 17:36:00 -08004107 setTextViewColorPrimary(contentView, R.id.app_name_text);
4108 } else {
4109 contentView.setTextColor(R.id.app_name_text,
4110 ambient ? resolveAmbientColor() : resolveContrastColor());
4111 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004112 }
4113
Adrian Roos487374f2017-01-11 15:48:14 -08004114 private void bindSmallIcon(RemoteViews contentView, boolean ambient) {
Selim Cinek279fa862016-06-14 10:57:25 -07004115 if (mN.mSmallIcon == null && mN.icon != 0) {
4116 mN.mSmallIcon = Icon.createWithResource(mContext, mN.icon);
4117 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004118 contentView.setImageViewIcon(R.id.icon, mN.mSmallIcon);
Adrian Roos9b45a152016-06-28 13:32:29 -07004119 contentView.setDrawableParameters(R.id.icon, false /* targetBackground */,
4120 -1 /* alpha */, -1 /* colorFilter */, null /* mode */, mN.iconLevel);
Adrian Roos487374f2017-01-11 15:48:14 -08004121 processSmallIconColor(mN.mSmallIcon, contentView, ambient);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004122 }
4123
Jorim Jaggi445d3c02014-08-19 22:33:42 +02004124 /**
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004125 * @return true if the built notification will show the time or the chronometer; false
4126 * otherwise
4127 */
4128 private boolean showsTimeOrChronometer() {
Selim Cinekc2c0b042016-05-18 17:13:46 -07004129 return mN.showsTime() || mN.showsChronometer();
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004130 }
4131
Christoph Studerfe718432014-09-01 18:21:18 +02004132 private void resetStandardTemplateWithActions(RemoteViews big) {
Adrian Roos4c1fcc82016-03-31 14:39:39 -07004133 // actions_container is only reset when there are no actions to avoid focus issues with
4134 // remote inputs.
Christoph Studerfe718432014-09-01 18:21:18 +02004135 big.setViewVisibility(R.id.actions, View.GONE);
Christoph Studerfe718432014-09-01 18:21:18 +02004136 big.removeAllViews(R.id.actions);
Adrian Roose458aa82015-12-08 16:17:19 -08004137
4138 big.setViewVisibility(R.id.notification_material_reply_container, View.GONE);
4139 big.setTextViewText(R.id.notification_material_reply_text_1, null);
4140
4141 big.setViewVisibility(R.id.notification_material_reply_text_2, View.GONE);
4142 big.setTextViewText(R.id.notification_material_reply_text_2, null);
4143 big.setViewVisibility(R.id.notification_material_reply_text_3, View.GONE);
4144 big.setTextViewText(R.id.notification_material_reply_text_3, null);
Adrian Roosf852a422016-06-03 13:33:43 -07004145
4146 big.setViewLayoutMarginBottomDimen(R.id.notification_action_list_margin_target, 0);
Christoph Studerfe718432014-09-01 18:21:18 +02004147 }
4148
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004149 private RemoteViews applyStandardTemplateWithActions(int layoutId) {
Adrian Roos70d7aa32017-01-11 15:39:06 -08004150 return applyStandardTemplateWithActions(layoutId, mParams.reset().fillTextsFrom(this));
Adrian Roos48d746a2016-04-12 14:57:28 -07004151 }
4152
Adrian Roos70d7aa32017-01-11 15:39:06 -08004153 private RemoteViews applyStandardTemplateWithActions(int layoutId,
4154 StandardTemplateParams p) {
4155 RemoteViews big = applyStandardTemplate(layoutId, p);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004156
Christoph Studerfe718432014-09-01 18:21:18 +02004157 resetStandardTemplateWithActions(big);
4158
Adrian Roose458aa82015-12-08 16:17:19 -08004159 boolean validRemoteInput = false;
4160
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004161 int N = mActions.size();
Adrian Roos487374f2017-01-11 15:48:14 -08004162 boolean emphazisedMode = mN.fullScreenIntent != null && !p.ambient;
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004163 big.setBoolean(R.id.actions, "setEmphasizedMode", emphazisedMode);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004164 if (N > 0) {
Adrian Roos7052de52016-03-03 15:53:34 -08004165 big.setViewVisibility(R.id.actions_container, View.VISIBLE);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004166 big.setViewVisibility(R.id.actions, View.VISIBLE);
Adrian Roos487374f2017-01-11 15:48:14 -08004167 if (p.ambient) {
4168 big.setInt(R.id.actions, "setBackgroundColor", Color.TRANSPARENT);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004169 } else if (isColorized()) {
4170 big.setInt(R.id.actions, "setBackgroundColor", getActionBarColor());
4171 } else {
4172 big.setInt(R.id.actions, "setBackgroundColor", mContext.getColor(
4173 R.color.notification_action_list));
Adrian Roos487374f2017-01-11 15:48:14 -08004174 }
Adrian Roosf852a422016-06-03 13:33:43 -07004175 big.setViewLayoutMarginBottomDimen(R.id.notification_action_list_margin_target,
4176 R.dimen.notification_action_list_height);
Daniel Sandler8680bf82012-05-15 16:52:52 -04004177 if (N>MAX_ACTION_BUTTONS) N=MAX_ACTION_BUTTONS;
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004178 for (int i=0; i<N; i++) {
Adrian Roose458aa82015-12-08 16:17:19 -08004179 Action action = mActions.get(i);
4180 validRemoteInput |= hasValidRemoteInput(action);
4181
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004182 final RemoteViews button = generateActionButton(action, emphazisedMode,
Adrian Roos487374f2017-01-11 15:48:14 -08004183 i % 2 != 0, p.ambient);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004184 big.addView(R.id.actions, button);
4185 }
Adrian Roos4c1fcc82016-03-31 14:39:39 -07004186 } else {
4187 big.setViewVisibility(R.id.actions_container, View.GONE);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004188 }
Adrian Roose458aa82015-12-08 16:17:19 -08004189
4190 CharSequence[] replyText = mN.extras.getCharSequenceArray(EXTRA_REMOTE_INPUT_HISTORY);
Adrian Roos487374f2017-01-11 15:48:14 -08004191 if (!p.ambient && validRemoteInput && replyText != null
Adrian Roose458aa82015-12-08 16:17:19 -08004192 && replyText.length > 0 && !TextUtils.isEmpty(replyText[0])) {
4193 big.setViewVisibility(R.id.notification_material_reply_container, View.VISIBLE);
4194 big.setTextViewText(R.id.notification_material_reply_text_1, replyText[0]);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004195 setTextViewColorSecondary(big, R.id.notification_material_reply_text_1);
Adrian Roose458aa82015-12-08 16:17:19 -08004196
4197 if (replyText.length > 1 && !TextUtils.isEmpty(replyText[1])) {
4198 big.setViewVisibility(R.id.notification_material_reply_text_2, View.VISIBLE);
4199 big.setTextViewText(R.id.notification_material_reply_text_2, replyText[1]);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004200 setTextViewColorSecondary(big, R.id.notification_material_reply_text_2);
Adrian Roose458aa82015-12-08 16:17:19 -08004201
4202 if (replyText.length > 2 && !TextUtils.isEmpty(replyText[2])) {
4203 big.setViewVisibility(
4204 R.id.notification_material_reply_text_3, View.VISIBLE);
4205 big.setTextViewText(R.id.notification_material_reply_text_3, replyText[2]);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004206 setTextViewColorSecondary(big, R.id.notification_material_reply_text_3);
Adrian Roose458aa82015-12-08 16:17:19 -08004207 }
4208 }
4209 }
4210
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004211 return big;
4212 }
4213
Adrian Roose458aa82015-12-08 16:17:19 -08004214 private boolean hasValidRemoteInput(Action action) {
4215 if (TextUtils.isEmpty(action.title) || action.actionIntent == null) {
4216 // Weird actions
4217 return false;
4218 }
4219
4220 RemoteInput[] remoteInputs = action.getRemoteInputs();
4221 if (remoteInputs == null) {
4222 return false;
4223 }
4224
4225 for (RemoteInput r : remoteInputs) {
4226 CharSequence[] choices = r.getChoices();
4227 if (r.getAllowFreeFormInput() || (choices != null && choices.length != 0)) {
4228 return true;
4229 }
4230 }
4231 return false;
4232 }
4233
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004234 /**
4235 * Construct a RemoteViews for the final 1U notification layout. In order:
4236 * 1. Custom contentView from the caller
4237 * 2. Style's proposed content view
4238 * 3. Standard template view
4239 */
Julia Reynolds3b848122016-02-26 10:45:32 -05004240 public RemoteViews createContentView() {
Selim Cinek7d1009b2017-01-25 15:28:28 -08004241 return createContentView(false /* increasedheight */ );
4242 }
4243
4244 /**
4245 * Construct a RemoteViews for the smaller content view.
4246 *
4247 * @param increasedHeight true if this layout be created with an increased height. Some
4248 * styles may support showing more then just that basic 1U size
4249 * and the system may decide to render important notifications
4250 * slightly bigger even when collapsed.
4251 *
4252 * @hide
4253 */
4254 public RemoteViews createContentView(boolean increasedHeight) {
Selim Cineka7679b62017-05-10 16:33:25 -07004255 if (mN.contentView != null && useExistingRemoteView()) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004256 return mN.contentView;
4257 } else if (mStyle != null) {
Selim Cinek7d1009b2017-01-25 15:28:28 -08004258 final RemoteViews styleView = mStyle.makeContentView(increasedHeight);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004259 if (styleView != null) {
4260 return styleView;
4261 }
Joe Onorato46439ce2010-11-19 13:56:21 -08004262 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004263 return applyStandardTemplate(getBaseLayoutResource());
Joe Onorato46439ce2010-11-19 13:56:21 -08004264 }
4265
Selim Cineka7679b62017-05-10 16:33:25 -07004266 private boolean useExistingRemoteView() {
4267 return mStyle == null || (!mStyle.displayCustomViewInline()
4268 && !mRebuildStyledRemoteViews);
4269 }
4270
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004271 /**
4272 * Construct a RemoteViews for the final big notification layout.
4273 */
Julia Reynolds3b848122016-02-26 10:45:32 -05004274 public RemoteViews createBigContentView() {
Selim Cinek850a8542015-11-11 11:48:36 -05004275 RemoteViews result = null;
Selim Cineka7679b62017-05-10 16:33:25 -07004276 if (mN.bigContentView != null && useExistingRemoteView()) {
Julia Reynolds089e3e42015-11-18 09:59:57 -05004277 return mN.bigContentView;
4278 } else if (mStyle != null) {
Selim Cinek850a8542015-11-11 11:48:36 -05004279 result = mStyle.makeBigContentView();
Selim Cinek90dcf6d2015-11-18 20:24:13 -08004280 hideLine1Text(result);
Selim Cinekcc10bfb2016-02-10 16:24:21 -08004281 } else if (mActions.size() != 0) {
4282 result = applyStandardTemplateWithActions(getBigBaseLayoutResource());
Selim Cinek850a8542015-11-11 11:48:36 -05004283 }
Selim Cinek6743c0b2017-01-18 18:24:01 -08004284 makeHeaderExpanded(result);
Selim Cinek850a8542015-11-11 11:48:36 -05004285 return result;
4286 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004287
Selim Cinekeaa29ca2015-11-23 13:51:13 -08004288 /**
Selim Cinek414ad332017-02-24 19:06:12 -08004289 * Construct a RemoteViews for the final notification header only. This will not be
4290 * colorized.
Selim Cinekeaa29ca2015-11-23 13:51:13 -08004291 *
Adrian Roos6f6e1592017-05-02 16:22:53 -07004292 * @param ambient if true, generate the header for the ambient display layout.
Selim Cinekeaa29ca2015-11-23 13:51:13 -08004293 * @hide
4294 */
Adrian Roos6f6e1592017-05-02 16:22:53 -07004295 public RemoteViews makeNotificationHeader(boolean ambient) {
Selim Cinek414ad332017-02-24 19:06:12 -08004296 Boolean colorized = (Boolean) mN.extras.get(EXTRA_COLORIZED);
4297 mN.extras.putBoolean(EXTRA_COLORIZED, false);
Selim Cinekeaa29ca2015-11-23 13:51:13 -08004298 RemoteViews header = new BuilderRemoteViews(mContext.getApplicationInfo(),
Adrian Roos6f6e1592017-05-02 16:22:53 -07004299 ambient ? R.layout.notification_template_ambient_header
4300 : R.layout.notification_template_header);
Selim Cinekeaa29ca2015-11-23 13:51:13 -08004301 resetNotificationHeader(header);
Adrian Roos6f6e1592017-05-02 16:22:53 -07004302 bindNotificationHeader(header, ambient);
Selim Cinek414ad332017-02-24 19:06:12 -08004303 if (colorized != null) {
4304 mN.extras.putBoolean(EXTRA_COLORIZED, colorized);
4305 } else {
4306 mN.extras.remove(EXTRA_COLORIZED);
4307 }
Selim Cinekeaa29ca2015-11-23 13:51:13 -08004308 return header;
4309 }
4310
Adrian Roos487374f2017-01-11 15:48:14 -08004311 /**
4312 * Construct a RemoteViews for the ambient version of the notification.
4313 *
4314 * @hide
4315 */
4316 public RemoteViews makeAmbientNotification() {
4317 RemoteViews ambient = applyStandardTemplateWithActions(
4318 R.layout.notification_template_material_ambient,
Adrian Roos0bc3f6a2017-03-06 11:54:05 -08004319 mParams.reset().ambient(true).fillTextsFrom(this).hasProgress(false));
Adrian Roos487374f2017-01-11 15:48:14 -08004320 return ambient;
4321 }
4322
Selim Cinek29603462015-11-17 19:04:39 -08004323 private void hideLine1Text(RemoteViews result) {
Selim Cinekcc10bfb2016-02-10 16:24:21 -08004324 if (result != null) {
4325 result.setViewVisibility(R.id.text_line_1, View.GONE);
4326 }
Selim Cinek29603462015-11-17 19:04:39 -08004327 }
4328
Selim Cinek6743c0b2017-01-18 18:24:01 -08004329 /**
4330 * Adapt the Notification header if this view is used as an expanded view.
4331 *
4332 * @hide
4333 */
4334 public static void makeHeaderExpanded(RemoteViews result) {
Selim Cinekcc10bfb2016-02-10 16:24:21 -08004335 if (result != null) {
4336 result.setBoolean(R.id.notification_header, "setExpanded", true);
4337 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004338 }
4339
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004340 /**
4341 * Construct a RemoteViews for the final heads-up notification layout.
Selim Cinek87ed69b2017-02-09 15:59:43 -08004342 *
4343 * @param increasedHeight true if this layout be created with an increased height. Some
4344 * styles may support showing more then just that basic 1U size
4345 * and the system may decide to render important notifications
4346 * slightly bigger even when collapsed.
4347 *
4348 * @hide
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004349 */
Selim Cinek87ed69b2017-02-09 15:59:43 -08004350 public RemoteViews createHeadsUpContentView(boolean increasedHeight) {
Selim Cineka7679b62017-05-10 16:33:25 -07004351 if (mN.headsUpContentView != null && useExistingRemoteView()) {
Julia Reynolds089e3e42015-11-18 09:59:57 -05004352 return mN.headsUpContentView;
4353 } else if (mStyle != null) {
Selim Cinek87ed69b2017-02-09 15:59:43 -08004354 final RemoteViews styleView = mStyle.makeHeadsUpContentView(increasedHeight);
4355 if (styleView != null) {
4356 return styleView;
4357 }
Julia Reynolds089e3e42015-11-18 09:59:57 -05004358 } else if (mActions.size() == 0) {
4359 return null;
4360 }
4361
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004362 return applyStandardTemplateWithActions(getBigBaseLayoutResource());
Chris Wren8fd39ec2014-02-27 17:43:26 -05004363 }
4364
Selim Cinek624c02db2015-12-14 21:00:02 -08004365 /**
Selim Cinek87ed69b2017-02-09 15:59:43 -08004366 * Construct a RemoteViews for the final heads-up notification layout.
4367 */
4368 public RemoteViews createHeadsUpContentView() {
4369 return createHeadsUpContentView(false /* useIncreasedHeight */);
4370 }
4371
4372 /**
Selim Cinek624c02db2015-12-14 21:00:02 -08004373 * Construct a RemoteViews for the display in public contexts like on the lockscreen.
4374 *
4375 * @hide
4376 */
4377 public RemoteViews makePublicContentView() {
Adrian Roos1a1ecfc2017-04-17 11:17:59 -07004378 return makePublicView(false /* ambient */);
4379 }
4380
4381 /**
4382 * Construct a RemoteViews for the display in public contexts like on the lockscreen.
4383 *
4384 * @hide
4385 */
4386 public RemoteViews makePublicAmbientNotification() {
4387 return makePublicView(true /* ambient */);
4388 }
4389
4390 private RemoteViews makePublicView(boolean ambient) {
Selim Cinek624c02db2015-12-14 21:00:02 -08004391 if (mN.publicVersion != null) {
4392 final Builder builder = recoverBuilder(mContext, mN.publicVersion);
Adrian Roos1a1ecfc2017-04-17 11:17:59 -07004393 return ambient ? builder.makeAmbientNotification() : builder.createContentView();
Selim Cinek624c02db2015-12-14 21:00:02 -08004394 }
4395 Bundle savedBundle = mN.extras;
4396 Style style = mStyle;
4397 mStyle = null;
4398 Icon largeIcon = mN.mLargeIcon;
4399 mN.mLargeIcon = null;
Selim Cinek279fa862016-06-14 10:57:25 -07004400 Bitmap largeIconLegacy = mN.largeIcon;
4401 mN.largeIcon = null;
Adrian Roosb19b06b2017-05-02 18:54:40 -07004402 ArrayList<Action> actions = mActions;
4403 mActions = new ArrayList<>();
Selim Cinek624c02db2015-12-14 21:00:02 -08004404 Bundle publicExtras = new Bundle();
4405 publicExtras.putBoolean(EXTRA_SHOW_WHEN,
4406 savedBundle.getBoolean(EXTRA_SHOW_WHEN));
4407 publicExtras.putBoolean(EXTRA_SHOW_CHRONOMETER,
4408 savedBundle.getBoolean(EXTRA_SHOW_CHRONOMETER));
Adrian Roos96b7e202016-05-17 13:50:38 -07004409 publicExtras.putBoolean(EXTRA_CHRONOMETER_COUNT_DOWN,
4410 savedBundle.getBoolean(EXTRA_CHRONOMETER_COUNT_DOWN));
Selim Cinek624c02db2015-12-14 21:00:02 -08004411 publicExtras.putCharSequence(EXTRA_TITLE,
Adrian Roos1a1ecfc2017-04-17 11:17:59 -07004412 mContext.getString(com.android.internal.R.string.notification_hidden_text));
Selim Cinek624c02db2015-12-14 21:00:02 -08004413 mN.extras = publicExtras;
Adrian Roos1a1ecfc2017-04-17 11:17:59 -07004414 final RemoteViews view = ambient ? makeAmbientNotification()
4415 : applyStandardTemplate(getBaseLayoutResource());
Selim Cinek624c02db2015-12-14 21:00:02 -08004416 mN.extras = savedBundle;
4417 mN.mLargeIcon = largeIcon;
Selim Cinek279fa862016-06-14 10:57:25 -07004418 mN.largeIcon = largeIconLegacy;
Adrian Roosb19b06b2017-05-02 18:54:40 -07004419 mActions = actions;
Selim Cinek624c02db2015-12-14 21:00:02 -08004420 mStyle = style;
Adrian Roos1a1ecfc2017-04-17 11:17:59 -07004421 return view;
Selim Cinek624c02db2015-12-14 21:00:02 -08004422 }
4423
Selim Cinek6743c0b2017-01-18 18:24:01 -08004424 /**
4425 * Construct a content view for the display when low - priority
4426 *
4427 * @param useRegularSubtext uses the normal subtext set if there is one available. Otherwise
4428 * a new subtext is created consisting of the content of the
4429 * notification.
4430 * @hide
4431 */
4432 public RemoteViews makeLowPriorityContentView(boolean useRegularSubtext) {
4433 int color = mN.color;
4434 mN.color = COLOR_DEFAULT;
4435 CharSequence summary = mN.extras.getCharSequence(EXTRA_SUB_TEXT);
4436 if (!useRegularSubtext || TextUtils.isEmpty(summary)) {
4437 CharSequence newSummary = createSummaryText();
4438 if (!TextUtils.isEmpty(newSummary)) {
4439 mN.extras.putCharSequence(EXTRA_SUB_TEXT, newSummary);
4440 }
4441 }
Selim Cinek875ba9b2017-02-13 16:20:17 -08004442
Adrian Roos6f6e1592017-05-02 16:22:53 -07004443 RemoteViews header = makeNotificationHeader(false /* ambient */);
Selim Cinek1b554392017-02-28 17:22:49 -08004444 header.setBoolean(R.id.notification_header, "setAcceptAllTouches", true);
Selim Cinek6743c0b2017-01-18 18:24:01 -08004445 if (summary != null) {
4446 mN.extras.putCharSequence(EXTRA_SUB_TEXT, summary);
4447 } else {
4448 mN.extras.remove(EXTRA_SUB_TEXT);
4449 }
4450 mN.color = color;
4451 return header;
4452 }
Selim Cinek624c02db2015-12-14 21:00:02 -08004453
Selim Cinek6743c0b2017-01-18 18:24:01 -08004454 private CharSequence createSummaryText() {
4455 CharSequence titleText = mN.extras.getCharSequence(Notification.EXTRA_TITLE);
4456 if (USE_ONLY_TITLE_IN_LOW_PRIORITY_SUMMARY) {
4457 return titleText;
4458 }
4459 SpannableStringBuilder summary = new SpannableStringBuilder();
4460 if (titleText == null) {
4461 titleText = mN.extras.getCharSequence(Notification.EXTRA_TITLE_BIG);
4462 }
4463 BidiFormatter bidi = BidiFormatter.getInstance();
4464 if (titleText != null) {
4465 summary.append(bidi.unicodeWrap(titleText));
4466 }
4467 CharSequence contentText = mN.extras.getCharSequence(Notification.EXTRA_TEXT);
4468 if (titleText != null && contentText != null) {
4469 summary.append(bidi.unicodeWrap(mContext.getText(
4470 R.string.notification_header_divider_symbol_with_spaces)));
4471 }
4472 if (contentText != null) {
4473 summary.append(bidi.unicodeWrap(contentText));
4474 }
4475 return summary;
4476 }
Chris Wren8fd39ec2014-02-27 17:43:26 -05004477
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004478 private RemoteViews generateActionButton(Action action, boolean emphazisedMode,
Adrian Roos487374f2017-01-11 15:48:14 -08004479 boolean oddAction, boolean ambient) {
Daniel Sandler8680bf82012-05-15 16:52:52 -04004480 final boolean tombstone = (action.actionIntent == null);
Selim Cinekf33b1112015-07-15 17:45:11 -07004481 RemoteViews button = new BuilderRemoteViews(mContext.getApplicationInfo(),
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004482 emphazisedMode ? getEmphasizedActionLayoutResource()
4483 : tombstone ? getActionTombstoneLayoutResource()
4484 : getActionLayoutResource());
Daniel Sandler8680bf82012-05-15 16:52:52 -04004485 if (!tombstone) {
Daniel Sandlere5518842012-05-10 16:20:40 -04004486 button.setOnClickPendingIntent(R.id.action0, action.actionIntent);
Daniel Sandlere5518842012-05-10 16:20:40 -04004487 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004488 button.setContentDescription(R.id.action0, action.title);
Adrian Roosfe84e1f2015-11-04 15:55:39 -08004489 if (action.mRemoteInputs != null) {
4490 button.setRemoteInputs(R.id.action0, action.mRemoteInputs);
4491 }
Selim Cinek7b9605b2017-01-19 17:36:00 -08004492 // TODO: handle emphasized mode / actions right
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004493 if (emphazisedMode) {
Selim Cinek981962e2016-07-20 20:41:58 -07004494 // change the background bgColor
Selim Cinek622c64a2017-04-17 17:10:05 -07004495 int bgColor;
4496 if (isColorized()) {
4497 bgColor = oddAction ? getActionBarColor() : getActionBarColorDeEmphasized();
4498 } else {
4499 bgColor = mContext.getColor(oddAction ? R.color.notification_action_list
4500 : R.color.notification_action_list_dark);
4501 }
Selim Cinek981962e2016-07-20 20:41:58 -07004502 button.setDrawableParameters(R.id.button_holder, true, -1, bgColor,
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004503 PorterDuff.Mode.SRC_ATOP, -1);
Selim Cinek981962e2016-07-20 20:41:58 -07004504 CharSequence title = action.title;
4505 ColorStateList[] outResultColor = null;
4506 if (isLegacy()) {
4507 title = clearColorSpans(title);
4508 } else {
4509 outResultColor = new ColorStateList[1];
4510 title = ensureColorSpanContrast(title, bgColor, outResultColor);
4511 }
4512 button.setTextViewText(R.id.action0, title);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004513 setTextViewColorPrimary(button, R.id.action0);
Selim Cinek981962e2016-07-20 20:41:58 -07004514 if (outResultColor != null && outResultColor[0] != null) {
4515 // We need to set the text color as well since changing a text to uppercase
4516 // clears its spans.
4517 button.setTextColor(R.id.action0, outResultColor[0]);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004518 } else if (mN.color != COLOR_DEFAULT && !isColorized()) {
Selim Cinek981962e2016-07-20 20:41:58 -07004519 button.setTextColor(R.id.action0,resolveContrastColor());
4520 }
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004521 } else {
Selim Cinek981962e2016-07-20 20:41:58 -07004522 button.setTextViewText(R.id.action0, processLegacyText(action.title));
Adrian Roos72171622017-01-27 10:32:06 -08004523 if (isColorized() && !ambient) {
Selim Cinek7b9605b2017-01-19 17:36:00 -08004524 setTextViewColorPrimary(button, R.id.action0);
4525 } else if (mN.color != COLOR_DEFAULT) {
Adrian Roos487374f2017-01-11 15:48:14 -08004526 button.setTextColor(R.id.action0,
4527 ambient ? resolveAmbientColor() : resolveContrastColor());
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004528 }
Adrian Roosfe84e1f2015-11-04 15:55:39 -08004529 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004530 return button;
4531 }
4532
Joe Onoratocb109a02011-01-18 17:57:41 -08004533 /**
Selim Cinek981962e2016-07-20 20:41:58 -07004534 * Clears all color spans of a text
4535 * @param charSequence the input text
4536 * @return the same text but without color spans
4537 */
4538 private CharSequence clearColorSpans(CharSequence charSequence) {
4539 if (charSequence instanceof Spanned) {
4540 Spanned ss = (Spanned) charSequence;
4541 Object[] spans = ss.getSpans(0, ss.length(), Object.class);
4542 SpannableStringBuilder builder = new SpannableStringBuilder(ss.toString());
4543 for (Object span : spans) {
4544 Object resultSpan = span;
4545 if (resultSpan instanceof CharacterStyle) {
4546 resultSpan = ((CharacterStyle) span).getUnderlying();
4547 }
4548 if (resultSpan instanceof TextAppearanceSpan) {
4549 TextAppearanceSpan originalSpan = (TextAppearanceSpan) resultSpan;
4550 if (originalSpan.getTextColor() != null) {
4551 resultSpan = new TextAppearanceSpan(
4552 originalSpan.getFamily(),
4553 originalSpan.getTextStyle(),
4554 originalSpan.getTextSize(),
4555 null,
4556 originalSpan.getLinkTextColor());
4557 }
4558 } else if (resultSpan instanceof ForegroundColorSpan
4559 || (resultSpan instanceof BackgroundColorSpan)) {
4560 continue;
4561 } else {
4562 resultSpan = span;
4563 }
4564 builder.setSpan(resultSpan, ss.getSpanStart(span), ss.getSpanEnd(span),
4565 ss.getSpanFlags(span));
4566 }
4567 return builder;
4568 }
4569 return charSequence;
4570 }
4571
4572 /**
4573 * Ensures contrast on color spans against a background color. also returns the color of the
4574 * text if a span was found that spans over the whole text.
4575 *
4576 * @param charSequence the charSequence on which the spans are
4577 * @param background the background color to ensure the contrast against
4578 * @param outResultColor an array in which a color will be returned as the first element if
4579 * there exists a full length color span.
4580 * @return the contrasted charSequence
4581 */
4582 private CharSequence ensureColorSpanContrast(CharSequence charSequence, int background,
4583 ColorStateList[] outResultColor) {
4584 if (charSequence instanceof Spanned) {
4585 Spanned ss = (Spanned) charSequence;
4586 Object[] spans = ss.getSpans(0, ss.length(), Object.class);
4587 SpannableStringBuilder builder = new SpannableStringBuilder(ss.toString());
4588 for (Object span : spans) {
4589 Object resultSpan = span;
4590 int spanStart = ss.getSpanStart(span);
4591 int spanEnd = ss.getSpanEnd(span);
4592 boolean fullLength = (spanEnd - spanStart) == charSequence.length();
4593 if (resultSpan instanceof CharacterStyle) {
4594 resultSpan = ((CharacterStyle) span).getUnderlying();
4595 }
4596 if (resultSpan instanceof TextAppearanceSpan) {
4597 TextAppearanceSpan originalSpan = (TextAppearanceSpan) resultSpan;
4598 ColorStateList textColor = originalSpan.getTextColor();
4599 if (textColor != null) {
4600 int[] colors = textColor.getColors();
4601 int[] newColors = new int[colors.length];
4602 for (int i = 0; i < newColors.length; i++) {
4603 newColors[i] = NotificationColorUtil.ensureLargeTextContrast(
4604 colors[i], background);
4605 }
4606 textColor = new ColorStateList(textColor.getStates().clone(),
4607 newColors);
4608 resultSpan = new TextAppearanceSpan(
4609 originalSpan.getFamily(),
4610 originalSpan.getTextStyle(),
4611 originalSpan.getTextSize(),
4612 textColor,
4613 originalSpan.getLinkTextColor());
4614 if (fullLength) {
4615 outResultColor[0] = new ColorStateList(
4616 textColor.getStates().clone(), newColors);
4617 }
4618 }
4619 } else if (resultSpan instanceof ForegroundColorSpan) {
4620 ForegroundColorSpan originalSpan = (ForegroundColorSpan) resultSpan;
4621 int foregroundColor = originalSpan.getForegroundColor();
4622 foregroundColor = NotificationColorUtil.ensureLargeTextContrast(
4623 foregroundColor, background);
4624 resultSpan = new ForegroundColorSpan(foregroundColor);
4625 if (fullLength) {
4626 outResultColor[0] = ColorStateList.valueOf(foregroundColor);
4627 }
4628 } else {
4629 resultSpan = span;
4630 }
4631
4632 builder.setSpan(resultSpan, spanStart, spanEnd, ss.getSpanFlags(span));
4633 }
4634 return builder;
4635 }
4636 return charSequence;
4637 }
4638
4639 /**
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004640 * @return Whether we are currently building a notification from a legacy (an app that
Alan Viverette3cb07a462014-06-06 14:19:53 -07004641 * doesn't create material notifications by itself) app.
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004642 */
4643 private boolean isLegacy() {
Selim Cinek7b9605b2017-01-19 17:36:00 -08004644 if (!mIsLegacyInitialized) {
4645 mIsLegacy = mContext.getApplicationInfo().targetSdkVersion
4646 < Build.VERSION_CODES.LOLLIPOP;
4647 mIsLegacyInitialized = true;
4648 }
4649 return mIsLegacy;
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004650 }
4651
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004652 private CharSequence processLegacyText(CharSequence charSequence) {
Adrian Roos0bc3f6a2017-03-06 11:54:05 -08004653 return processLegacyText(charSequence, false /* ambient */);
4654 }
4655
4656 private CharSequence processLegacyText(CharSequence charSequence, boolean ambient) {
4657 boolean isAlreadyLightText = isLegacy() || textColorsNeedInversion();
4658 boolean wantLightText = ambient;
4659 if (isAlreadyLightText != wantLightText) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004660 return getColorUtil().invertCharSequenceColors(charSequence);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004661 } else {
4662 return charSequence;
4663 }
4664 }
4665
Dan Sandler26e81cf2014-05-06 10:01:27 -04004666 /**
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004667 * Apply any necessariy colors to the small icon
Dan Sandler26e81cf2014-05-06 10:01:27 -04004668 */
Adrian Roos487374f2017-01-11 15:48:14 -08004669 private void processSmallIconColor(Icon smallIcon, RemoteViews contentView,
4670 boolean ambient) {
Selim Cinekea4bef72015-12-02 15:51:10 -08004671 boolean colorable = !isLegacy() || getColorUtil().isGrayscaleIcon(mContext, smallIcon);
Selim Cinek99104832017-01-25 14:47:33 -08004672 int color = ambient ? resolveAmbientColor() : getPrimaryHighlightColor();
Selim Cinekea4bef72015-12-02 15:51:10 -08004673 if (colorable) {
Adrian Roos487374f2017-01-11 15:48:14 -08004674 contentView.setDrawableParameters(R.id.icon, false, -1, color,
Jorim Jaggi92df1f22014-12-16 19:44:41 +01004675 PorterDuff.Mode.SRC_ATOP, -1);
Selim Cinekea4bef72015-12-02 15:51:10 -08004676
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004677 }
Selim Cinekea4bef72015-12-02 15:51:10 -08004678 contentView.setInt(R.id.notification_header, "setOriginalIconColor",
Adrian Roos487374f2017-01-11 15:48:14 -08004679 colorable ? color : NotificationHeaderView.NO_COLOR);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004680 }
4681
Dan Sandler26e81cf2014-05-06 10:01:27 -04004682 /**
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004683 * Make the largeIcon dark if it's a fake smallIcon (that is,
Dan Sandler26e81cf2014-05-06 10:01:27 -04004684 * if it's grayscale).
4685 */
4686 // TODO: also check bounds, transparency, that sort of thing.
Dan Sandlerd63f9322015-05-06 15:18:49 -04004687 private void processLargeLegacyIcon(Icon largeIcon, RemoteViews contentView) {
4688 if (largeIcon != null && isLegacy()
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004689 && getColorUtil().isGrayscaleIcon(mContext, largeIcon)) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004690 // resolve color will fall back to the default when legacy
Adrian Roos4ff3b122016-02-01 12:26:13 -08004691 contentView.setDrawableParameters(R.id.icon, false, -1, resolveContrastColor(),
Dan Sandler190d58d2014-05-15 09:33:39 -04004692 PorterDuff.Mode.SRC_ATOP, -1);
Jorim Jaggi92df1f22014-12-16 19:44:41 +01004693 }
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004694 }
4695
Julia Reynolds10ee1fc2015-11-09 11:04:55 -05004696 private void sanitizeColor() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004697 if (mN.color != COLOR_DEFAULT) {
4698 mN.color |= 0xFF000000; // no alpha for custom colors
Jorim Jaggi74419312014-06-10 20:57:21 +02004699 }
Jorim Jaggi74419312014-06-10 20:57:21 +02004700 }
4701
Adrian Roos4ff3b122016-02-01 12:26:13 -08004702 int resolveContrastColor() {
4703 if (mCachedContrastColorIsFor == mN.color && mCachedContrastColor != COLOR_INVALID) {
4704 return mCachedContrastColor;
Dan Sandler26e81cf2014-05-06 10:01:27 -04004705 }
Adrian Roos4ff3b122016-02-01 12:26:13 -08004706
Selim Cinekac5f0272017-05-02 16:05:41 -07004707 int color;
4708 int background = mBackgroundColorHint;
4709 if (mBackgroundColorHint == COLOR_INVALID) {
4710 background = mContext.getColor(
4711 com.android.internal.R.color.notification_material_background_color);
4712 }
4713 if (mN.color == COLOR_DEFAULT) {
4714 ensureColors();
4715 color = mSecondaryTextColor;
4716 } else {
4717 color = NotificationColorUtil.resolveContrastColor(mContext, mN.color,
4718 background);
4719 }
4720 if (Color.alpha(color) < 255) {
4721 // alpha doesn't go well for color filters, so let's blend it manually
4722 color = NotificationColorUtil.compositeColors(color, background);
4723 }
Adrian Roos4ff3b122016-02-01 12:26:13 -08004724 mCachedContrastColorIsFor = mN.color;
Selim Cinekac5f0272017-05-02 16:05:41 -07004725 return mCachedContrastColor = color;
Dan Sandler26e81cf2014-05-06 10:01:27 -04004726 }
4727
Adrian Roos487374f2017-01-11 15:48:14 -08004728 int resolveAmbientColor() {
4729 if (mCachedAmbientColorIsFor == mN.color && mCachedAmbientColorIsFor != COLOR_INVALID) {
4730 return mCachedAmbientColor;
4731 }
4732 final int contrasted = NotificationColorUtil.resolveAmbientColor(mContext, mN.color);
4733
4734 mCachedAmbientColorIsFor = mN.color;
4735 return mCachedAmbientColor = contrasted;
4736 }
4737
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004738 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004739 * Apply the unstyled operations and return a new {@link Notification} object.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04004740 * @hide
Joe Onoratocb109a02011-01-18 17:57:41 -08004741 */
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04004742 public Notification buildUnstyled() {
Daniel Sandlera0a938c2012-03-15 08:42:37 -04004743 if (mActions.size() > 0) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004744 mN.actions = new Action[mActions.size()];
4745 mActions.toArray(mN.actions);
Daniel Sandlera0a938c2012-03-15 08:42:37 -04004746 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004747 if (!mPersonList.isEmpty()) {
4748 mN.extras.putStringArray(EXTRA_PEOPLE,
4749 mPersonList.toArray(new String[mPersonList.size()]));
Dan Sandler0bf2ed82013-12-21 23:33:41 -06004750 }
Selim Cinek247fa012016-02-18 09:50:48 -08004751 if (mN.bigContentView != null || mN.contentView != null
4752 || mN.headsUpContentView != null) {
4753 mN.extras.putBoolean(EXTRA_CONTAINS_CUSTOM_VIEW, true);
4754 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004755 return mN;
Joe Onorato46439ce2010-11-19 13:56:21 -08004756 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004757
Julia Reynolds3b848122016-02-26 10:45:32 -05004758 /**
4759 * Creates a Builder from an existing notification so further changes can be made.
4760 * @param context The context for your application / activity.
4761 * @param n The notification to create a Builder from.
4762 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004763 public static Notification.Builder recoverBuilder(Context context, Notification n) {
Christoph Studer4600f9b2014-07-22 22:44:43 +02004764 // Re-create notification context so we can access app resources.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004765 ApplicationInfo applicationInfo = n.extras.getParcelable(
4766 EXTRA_BUILDER_APPLICATION_INFO);
Christoph Studer4600f9b2014-07-22 22:44:43 +02004767 Context builderContext;
Julia Reynoldsda303542015-11-23 14:00:20 -05004768 if (applicationInfo != null) {
4769 try {
4770 builderContext = context.createApplicationContext(applicationInfo,
4771 Context.CONTEXT_RESTRICTED);
4772 } catch (NameNotFoundException e) {
4773 Log.e(TAG, "ApplicationInfo " + applicationInfo + " not found");
4774 builderContext = context; // try with our context
4775 }
4776 } else {
4777 builderContext = context; // try with given context
Christoph Studer4600f9b2014-07-22 22:44:43 +02004778 }
4779
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004780 return new Builder(builderContext, n);
Christoph Studer4600f9b2014-07-22 22:44:43 +02004781 }
4782
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004783 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004784 * @deprecated Use {@link #build()} instead.
4785 */
4786 @Deprecated
4787 public Notification getNotification() {
4788 return build();
4789 }
4790
4791 /**
4792 * Combine all of the options that have been set and return a new {@link Notification}
4793 * object.
4794 */
4795 public Notification build() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004796 // first, add any extras from the calling code
4797 if (mUserExtras != null) {
4798 mN.extras = getAllExtras();
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07004799 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004800
Selim Cinekb85f36fd2016-04-20 18:46:36 -07004801 mN.creationTime = System.currentTimeMillis();
4802
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004803 // lazy stuff from mContext; see comment in Builder(Context, Notification)
Julia Reynoldsda303542015-11-23 14:00:20 -05004804 Notification.addFieldsFromContext(mContext, mN);
Christoph Studer943aa672014-08-03 20:31:16 +02004805
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004806 buildUnstyled();
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004807
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004808 if (mStyle != null) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004809 mStyle.buildStyled(mN);
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004810 }
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004811
Adrian Roos5081c0d2016-02-26 16:04:19 -08004812 if (mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N
Selim Cineka7679b62017-05-10 16:33:25 -07004813 && (useExistingRemoteView())) {
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004814 if (mN.contentView == null) {
Julia Reynolds3b848122016-02-26 10:45:32 -05004815 mN.contentView = createContentView();
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004816 mN.extras.putInt(EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT,
4817 mN.contentView.getSequenceNumber());
4818 }
4819 if (mN.bigContentView == null) {
Julia Reynolds3b848122016-02-26 10:45:32 -05004820 mN.bigContentView = createBigContentView();
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004821 if (mN.bigContentView != null) {
4822 mN.extras.putInt(EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT,
4823 mN.bigContentView.getSequenceNumber());
4824 }
4825 }
4826 if (mN.headsUpContentView == null) {
Julia Reynolds3b848122016-02-26 10:45:32 -05004827 mN.headsUpContentView = createHeadsUpContentView();
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004828 if (mN.headsUpContentView != null) {
4829 mN.extras.putInt(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT,
4830 mN.headsUpContentView.getSequenceNumber());
4831 }
4832 }
4833 }
4834
Julia Reynolds4c0c2022016-02-02 15:11:59 -05004835 if ((mN.defaults & DEFAULT_LIGHTS) != 0) {
4836 mN.flags |= FLAG_SHOW_LIGHTS;
4837 }
4838
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004839 return mN;
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004840 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05004841
4842 /**
4843 * Apply this Builder to an existing {@link Notification} object.
4844 *
4845 * @hide
4846 */
4847 public Notification buildInto(Notification n) {
Daniel Sandler1a497d32013-04-18 14:52:45 -04004848 build().cloneInto(n, true);
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05004849 return n;
4850 }
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004851
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004852 /**
Adrian Roos184bfe022016-03-03 13:41:44 -08004853 * Removes RemoteViews that were created for compatibility from {@param n}, if they did not
4854 * change.
4855 *
4856 * @return {@param n}, if no stripping is needed, otherwise a stripped clone of {@param n}.
4857 *
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004858 * @hide
4859 */
Adrian Roos184bfe022016-03-03 13:41:44 -08004860 public static Notification maybeCloneStrippedForDelivery(Notification n) {
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004861 String templateClass = n.extras.getString(EXTRA_TEMPLATE);
Adrian Roos184bfe022016-03-03 13:41:44 -08004862
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004863 // Only strip views for known Styles because we won't know how to
4864 // re-create them otherwise.
Adrian Roos184bfe022016-03-03 13:41:44 -08004865 if (!TextUtils.isEmpty(templateClass)
4866 && getNotificationStyleClass(templateClass) == null) {
4867 return n;
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004868 }
Adrian Roos184bfe022016-03-03 13:41:44 -08004869
4870 // Only strip unmodified BuilderRemoteViews.
4871 boolean stripContentView = n.contentView instanceof BuilderRemoteViews &&
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004872 n.extras.getInt(EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT, -1) ==
Adrian Roos184bfe022016-03-03 13:41:44 -08004873 n.contentView.getSequenceNumber();
4874 boolean stripBigContentView = n.bigContentView instanceof BuilderRemoteViews &&
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004875 n.extras.getInt(EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT, -1) ==
Adrian Roos184bfe022016-03-03 13:41:44 -08004876 n.bigContentView.getSequenceNumber();
4877 boolean stripHeadsUpContentView = n.headsUpContentView instanceof BuilderRemoteViews &&
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004878 n.extras.getInt(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT, -1) ==
Adrian Roos184bfe022016-03-03 13:41:44 -08004879 n.headsUpContentView.getSequenceNumber();
4880
4881 // Nothing to do here, no need to clone.
4882 if (!stripContentView && !stripBigContentView && !stripHeadsUpContentView) {
4883 return n;
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004884 }
Adrian Roos184bfe022016-03-03 13:41:44 -08004885
4886 Notification clone = n.clone();
4887 if (stripContentView) {
4888 clone.contentView = null;
4889 clone.extras.remove(EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT);
4890 }
4891 if (stripBigContentView) {
4892 clone.bigContentView = null;
4893 clone.extras.remove(EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT);
4894 }
4895 if (stripHeadsUpContentView) {
4896 clone.headsUpContentView = null;
4897 clone.extras.remove(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT);
4898 }
4899 return clone;
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004900 }
4901
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004902 private int getBaseLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07004903 return R.layout.notification_template_material_base;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004904 }
4905
4906 private int getBigBaseLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07004907 return R.layout.notification_template_material_big_base;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004908 }
4909
4910 private int getBigPictureLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07004911 return R.layout.notification_template_material_big_picture;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004912 }
4913
4914 private int getBigTextLayoutResource() {
Jorim Jaggi445d3c02014-08-19 22:33:42 +02004915 return R.layout.notification_template_material_big_text;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004916 }
4917
4918 private int getInboxLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07004919 return R.layout.notification_template_material_inbox;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004920 }
4921
Adrian Roosc1a80b02016-04-05 14:54:55 -07004922 private int getMessagingLayoutResource() {
4923 return R.layout.notification_template_material_messaging;
4924 }
4925
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004926 private int getActionLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07004927 return R.layout.notification_material_action;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004928 }
4929
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004930 private int getEmphasizedActionLayoutResource() {
4931 return R.layout.notification_material_action_emphasized;
4932 }
4933
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004934 private int getActionTombstoneLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07004935 return R.layout.notification_material_action_tombstone;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004936 }
Selim Cinek7b9605b2017-01-19 17:36:00 -08004937
4938 private int getBackgroundColor() {
4939 if (isColorized()) {
Selim Cinek5fb73f82017-04-20 16:55:38 -07004940 return mBackgroundColor != COLOR_INVALID ? mBackgroundColor : mN.color;
Selim Cinek7b9605b2017-01-19 17:36:00 -08004941 } else {
Selim Cinekac5f0272017-05-02 16:05:41 -07004942 return mBackgroundColorHint != COLOR_INVALID ? mBackgroundColorHint
4943 : COLOR_DEFAULT;
Selim Cinek7b9605b2017-01-19 17:36:00 -08004944 }
4945 }
4946
4947 private boolean isColorized() {
4948 return mN.isColorized();
4949 }
Selim Cinek99104832017-01-25 14:47:33 -08004950
4951 private boolean textColorsNeedInversion() {
4952 if (mStyle == null || !MediaStyle.class.equals(mStyle.getClass())) {
4953 return false;
4954 }
4955 int targetSdkVersion = mContext.getApplicationInfo().targetSdkVersion;
4956 return targetSdkVersion > Build.VERSION_CODES.M
4957 && targetSdkVersion < Build.VERSION_CODES.O;
4958 }
Selim Cinek5fb73f82017-04-20 16:55:38 -07004959
4960 /**
4961 * Set a color palette to be used as the background and textColors
4962 *
4963 * @param backgroundColor the color to be used as the background
4964 * @param foregroundColor the color to be used as the foreground
4965 *
4966 * @hide
4967 */
4968 public void setColorPalette(int backgroundColor, int foregroundColor) {
4969 mBackgroundColor = backgroundColor;
4970 mForegroundColor = foregroundColor;
4971 mTextColorsAreForBackground = COLOR_INVALID;
4972 ensureColors();
4973 }
Selim Cinekac5f0272017-05-02 16:05:41 -07004974
4975 /**
4976 * Sets the background color for this notification to be a different one then the default.
4977 * This is mainly used to calculate contrast and won't necessarily be applied to the
4978 * background.
4979 *
4980 * @hide
4981 */
4982 public void setBackgroundColorHint(int backgroundColor) {
4983 mBackgroundColorHint = backgroundColor;
4984 }
Selim Cineka7679b62017-05-10 16:33:25 -07004985
4986
4987 /**
4988 * Forces all styled remoteViews to be built from scratch and not use any cached
4989 * RemoteViews.
4990 * This is needed for legacy apps that are baking in their remoteviews into the
4991 * notification.
4992 *
4993 * @hide
4994 */
4995 public void setRebuildStyledRemoteViews(boolean rebuild) {
4996 mRebuildStyledRemoteViews = rebuild;
4997 }
Selim Cinek7b9605b2017-01-19 17:36:00 -08004998 }
4999
5000 /**
Selim Cinek22714f12017-04-13 16:23:53 -07005001 * @return whether this notification is a foreground service notification
Selim Cinek7b9605b2017-01-19 17:36:00 -08005002 */
Selim Cinek22714f12017-04-13 16:23:53 -07005003 private boolean isForegroundService() {
5004 return (flags & Notification.FLAG_FOREGROUND_SERVICE) != 0;
Selim Cinek7b9605b2017-01-19 17:36:00 -08005005 }
5006
5007 /**
Selim Cinek99104832017-01-25 14:47:33 -08005008 * @return whether this notification has a media session attached
5009 * @hide
5010 */
5011 public boolean hasMediaSession() {
5012 return extras.getParcelable(Notification.EXTRA_MEDIA_SESSION) != null;
5013 }
5014
5015 /**
Selim Cinek7d1009b2017-01-25 15:28:28 -08005016 * @return the style class of this notification
5017 * @hide
5018 */
5019 public Class<? extends Notification.Style> getNotificationStyle() {
5020 String templateClass = extras.getString(Notification.EXTRA_TEMPLATE);
5021
5022 if (!TextUtils.isEmpty(templateClass)) {
5023 return Notification.getNotificationStyleClass(templateClass);
5024 }
5025 return null;
5026 }
5027
5028 /**
Selim Cinek22714f12017-04-13 16:23:53 -07005029 * @return true if this notification is colorized.
Selim Cinek7b9605b2017-01-19 17:36:00 -08005030 *
5031 * @hide
5032 */
5033 public boolean isColorized() {
Selim Cinek5fb73f82017-04-20 16:55:38 -07005034 if (isColorizedMedia()) {
5035 return true;
5036 }
5037 return extras.getBoolean(EXTRA_COLORIZED) && isForegroundService();
5038 }
5039
5040 /**
5041 * @return true if this notification is colorized and it is a media notification
5042 *
5043 * @hide
5044 */
5045 public boolean isColorizedMedia() {
Selim Cinek99104832017-01-25 14:47:33 -08005046 Class<? extends Style> style = getNotificationStyle();
5047 if (MediaStyle.class.equals(style)) {
5048 Boolean colorized = (Boolean) extras.get(EXTRA_COLORIZED);
5049 if ((colorized == null || colorized) && hasMediaSession()) {
5050 return true;
5051 }
5052 } else if (DecoratedMediaCustomViewStyle.class.equals(style)) {
5053 if (extras.getBoolean(EXTRA_COLORIZED) && hasMediaSession()) {
5054 return true;
5055 }
5056 }
Selim Cinek5fb73f82017-04-20 16:55:38 -07005057 return false;
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005058 }
5059
Selim Cinek0847acd2017-04-24 19:48:29 -07005060
5061 /**
5062 * @return true if this is a media notification
5063 *
5064 * @hide
5065 */
5066 public boolean isMediaNotification() {
5067 Class<? extends Style> style = getNotificationStyle();
5068 if (MediaStyle.class.equals(style)) {
5069 return true;
5070 } else if (DecoratedMediaCustomViewStyle.class.equals(style)) {
5071 return true;
5072 }
5073 return false;
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005074 }
5075
Selim Cinek279fa862016-06-14 10:57:25 -07005076 private boolean hasLargeIcon() {
5077 return mLargeIcon != null || largeIcon != null;
5078 }
5079
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005080 /**
Selim Cinekc2c0b042016-05-18 17:13:46 -07005081 * @return true if the notification will show the time; false otherwise
Selim Cinekb85f36fd2016-04-20 18:46:36 -07005082 * @hide
5083 */
Selim Cinekc2c0b042016-05-18 17:13:46 -07005084 public boolean showsTime() {
Selim Cinekb85f36fd2016-04-20 18:46:36 -07005085 return when != 0 && extras.getBoolean(EXTRA_SHOW_WHEN);
5086 }
5087
5088 /**
Selim Cinekc2c0b042016-05-18 17:13:46 -07005089 * @return true if the notification will show a chronometer; false otherwise
5090 * @hide
5091 */
5092 public boolean showsChronometer() {
5093 return when != 0 && extras.getBoolean(EXTRA_SHOW_CHRONOMETER);
5094 }
5095
5096 /**
Julia Reynolds4a02afb2016-12-13 13:39:52 -05005097 * @hide
5098 */
5099 @SystemApi
5100 public static Class<? extends Style> getNotificationStyleClass(String templateClass) {
5101 Class<? extends Style>[] classes = new Class[] {
5102 BigTextStyle.class, BigPictureStyle.class, InboxStyle.class, MediaStyle.class,
5103 DecoratedCustomViewStyle.class, DecoratedMediaCustomViewStyle.class,
5104 MessagingStyle.class };
5105 for (Class<? extends Style> innerClass : classes) {
5106 if (templateClass.equals(innerClass.getName())) {
5107 return innerClass;
5108 }
5109 }
5110 return null;
5111 }
5112
5113 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005114 * An object that can apply a rich notification style to a {@link Notification.Builder}
5115 * object.
5116 */
Griff Hazendfcb0802014-02-11 12:00:00 -08005117 public static abstract class Style {
Chris Wrend6297db2012-05-03 16:20:13 -04005118 private CharSequence mBigContentTitle;
Jorim Jaggi457a10d2014-09-08 16:18:23 +02005119
5120 /**
5121 * @hide
5122 */
5123 protected CharSequence mSummaryText = null;
5124
5125 /**
5126 * @hide
5127 */
5128 protected boolean mSummaryTextSet = false;
Chris Wrend6297db2012-05-03 16:20:13 -04005129
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005130 protected Builder mBuilder;
5131
Chris Wrend6297db2012-05-03 16:20:13 -04005132 /**
5133 * Overrides ContentTitle in the big form of the template.
5134 * This defaults to the value passed to setContentTitle().
5135 */
5136 protected void internalSetBigContentTitle(CharSequence title) {
5137 mBigContentTitle = title;
5138 }
5139
5140 /**
5141 * Set the first line of text after the detail section in the big form of the template.
5142 */
5143 protected void internalSetSummaryText(CharSequence cs) {
5144 mSummaryText = cs;
Daniel Sandler619738c2012-06-07 16:33:08 -04005145 mSummaryTextSet = true;
Chris Wrend6297db2012-05-03 16:20:13 -04005146 }
5147
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005148 public void setBuilder(Builder builder) {
5149 if (mBuilder != builder) {
5150 mBuilder = builder;
Daniel Sandlerc08dea22012-06-28 08:35:24 -07005151 if (mBuilder != null) {
5152 mBuilder.setStyle(this);
5153 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005154 }
5155 }
5156
Chris Wrend6297db2012-05-03 16:20:13 -04005157 protected void checkBuilder() {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005158 if (mBuilder == null) {
5159 throw new IllegalArgumentException("Style requires a valid Builder object");
5160 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005161 }
Chris Wrend6297db2012-05-03 16:20:13 -04005162
5163 protected RemoteViews getStandardView(int layoutId) {
5164 checkBuilder();
5165
Christoph Studer4600f9b2014-07-22 22:44:43 +02005166 // Nasty.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005167 CharSequence oldBuilderContentTitle =
5168 mBuilder.getAllExtras().getCharSequence(EXTRA_TITLE);
Chris Wrend6297db2012-05-03 16:20:13 -04005169 if (mBigContentTitle != null) {
5170 mBuilder.setContentTitle(mBigContentTitle);
5171 }
5172
Chris Wrend6297db2012-05-03 16:20:13 -04005173 RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(layoutId);
5174
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005175 mBuilder.getAllExtras().putCharSequence(EXTRA_TITLE, oldBuilderContentTitle);
Christoph Studer4600f9b2014-07-22 22:44:43 +02005176
Chris Wrend6297db2012-05-03 16:20:13 -04005177 if (mBigContentTitle != null && mBigContentTitle.equals("")) {
5178 contentView.setViewVisibility(R.id.line1, View.GONE);
Chris Wren67dc9a02012-05-16 01:03:20 -04005179 } else {
5180 contentView.setViewVisibility(R.id.line1, View.VISIBLE);
Chris Wrend6297db2012-05-03 16:20:13 -04005181 }
5182
Chris Wrend6297db2012-05-03 16:20:13 -04005183 return contentView;
5184 }
5185
Daniel Sandlerf45564e2013-04-15 15:05:08 -04005186 /**
Selim Cinek7d1009b2017-01-25 15:28:28 -08005187 * Construct a Style-specific RemoteViews for the collapsed notification layout.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005188 * The default implementation has nothing additional to add.
Selim Cinek7d1009b2017-01-25 15:28:28 -08005189 *
5190 * @param increasedHeight true if this layout be created with an increased height.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005191 * @hide
5192 */
Selim Cinek7d1009b2017-01-25 15:28:28 -08005193 public RemoteViews makeContentView(boolean increasedHeight) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005194 return null;
5195 }
5196
5197 /**
5198 * Construct a Style-specific RemoteViews for the final big notification layout.
5199 * @hide
5200 */
5201 public RemoteViews makeBigContentView() {
5202 return null;
5203 }
5204
5205 /**
5206 * Construct a Style-specific RemoteViews for the final HUN layout.
Selim Cinek87ed69b2017-02-09 15:59:43 -08005207 *
5208 * @param increasedHeight true if this layout be created with an increased height.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005209 * @hide
5210 */
Selim Cinek87ed69b2017-02-09 15:59:43 -08005211 public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005212 return null;
5213 }
5214
5215 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005216 * Apply any style-specific extras to this notification before shipping it out.
Daniel Sandlerf45564e2013-04-15 15:05:08 -04005217 * @hide
5218 */
5219 public void addExtras(Bundle extras) {
5220 if (mSummaryTextSet) {
5221 extras.putCharSequence(EXTRA_SUMMARY_TEXT, mSummaryText);
5222 }
5223 if (mBigContentTitle != null) {
5224 extras.putCharSequence(EXTRA_TITLE_BIG, mBigContentTitle);
5225 }
Chris Wren91ad5632013-06-05 15:05:57 -04005226 extras.putString(EXTRA_TEMPLATE, this.getClass().getName());
Daniel Sandlerf45564e2013-04-15 15:05:08 -04005227 }
5228
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04005229 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005230 * Reconstruct the internal state of this Style object from extras.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04005231 * @hide
5232 */
Christoph Studer4600f9b2014-07-22 22:44:43 +02005233 protected void restoreFromExtras(Bundle extras) {
5234 if (extras.containsKey(EXTRA_SUMMARY_TEXT)) {
5235 mSummaryText = extras.getCharSequence(EXTRA_SUMMARY_TEXT);
5236 mSummaryTextSet = true;
5237 }
5238 if (extras.containsKey(EXTRA_TITLE_BIG)) {
5239 mBigContentTitle = extras.getCharSequence(EXTRA_TITLE_BIG);
5240 }
5241 }
5242
5243
5244 /**
5245 * @hide
5246 */
5247 public Notification buildStyled(Notification wip) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005248 addExtras(wip.extras);
Christoph Studer4600f9b2014-07-22 22:44:43 +02005249 return wip;
5250 }
5251
Daniel Sandler0ec46202015-06-24 01:27:05 -04005252 /**
5253 * @hide
5254 */
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07005255 public void purgeResources() {}
5256
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04005257 /**
5258 * Calls {@link android.app.Notification.Builder#build()} on the Builder this Style is
5259 * attached to.
5260 *
5261 * @return the fully constructed Notification.
5262 */
5263 public Notification build() {
5264 checkBuilder();
5265 return mBuilder.build();
5266 }
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02005267
5268 /**
5269 * @hide
5270 * @return true if the style positions the progress bar on the second line; false if the
5271 * style hides the progress bar
5272 */
5273 protected boolean hasProgress() {
5274 return true;
5275 }
Selim Cinek03d0d652015-11-13 13:18:09 -05005276
5277 /**
5278 * @hide
5279 * @return Whether we should put the summary be put into the notification header
5280 */
5281 public boolean hasSummaryInHeader() {
5282 return true;
5283 }
Selim Cinek593610c2016-02-16 18:42:57 -08005284
5285 /**
5286 * @hide
5287 * @return Whether custom content views are displayed inline in the style
5288 */
5289 public boolean displayCustomViewInline() {
5290 return false;
5291 }
Joe Onorato46439ce2010-11-19 13:56:21 -08005292 }
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005293
5294 /**
Daniel Sandler4dfbe832012-04-11 14:51:46 -04005295 * Helper class for generating large-format notifications that include a large image attachment.
Joe Malin8d40d042012-11-05 11:36:40 -08005296 *
Robert Ly91c5ce32014-06-08 15:37:00 -07005297 * Here's how you'd set the <code>BigPictureStyle</code> on a notification:
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005298 * <pre class="prettyprint">
Robert Ly91c5ce32014-06-08 15:37:00 -07005299 * Notification notif = new Notification.Builder(mContext)
5300 * .setContentTitle(&quot;New photo from &quot; + sender.toString())
5301 * .setContentText(subject)
5302 * .setSmallIcon(R.drawable.new_post)
5303 * .setLargeIcon(aBitmap)
5304 * .setStyle(new Notification.BigPictureStyle()
5305 * .bigPicture(aBigBitmap))
5306 * .build();
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005307 * </pre>
Joe Malin8d40d042012-11-05 11:36:40 -08005308 *
Daniel Sandler4dfbe832012-04-11 14:51:46 -04005309 * @see Notification#bigContentView
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005310 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005311 public static class BigPictureStyle extends Style {
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005312 private Bitmap mPicture;
Dan Sandlerd63f9322015-05-06 15:18:49 -04005313 private Icon mBigLargeIcon;
Chris Wren3745a3d2012-05-22 15:11:52 -04005314 private boolean mBigLargeIconSet = false;
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005315
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005316 public BigPictureStyle() {
5317 }
5318
Adrian Roosf5faf9d2016-05-23 13:56:15 -07005319 /**
5320 * @deprecated use {@code BigPictureStyle()}.
5321 */
5322 @Deprecated
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005323 public BigPictureStyle(Builder builder) {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005324 setBuilder(builder);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005325 }
5326
Chris Wrend6297db2012-05-03 16:20:13 -04005327 /**
5328 * Overrides ContentTitle in the big form of the template.
5329 * This defaults to the value passed to setContentTitle().
5330 */
5331 public BigPictureStyle setBigContentTitle(CharSequence title) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04005332 internalSetBigContentTitle(safeCharSequence(title));
Chris Wrend6297db2012-05-03 16:20:13 -04005333 return this;
5334 }
5335
5336 /**
5337 * Set the first line of text after the detail section in the big form of the template.
5338 */
5339 public BigPictureStyle setSummaryText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04005340 internalSetSummaryText(safeCharSequence(cs));
Chris Wrend6297db2012-05-03 16:20:13 -04005341 return this;
5342 }
5343
Chris Wren0bd664d2012-08-01 13:56:56 -04005344 /**
5345 * Provide the bitmap to be used as the payload for the BigPicture notification.
5346 */
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005347 public BigPictureStyle bigPicture(Bitmap b) {
5348 mPicture = b;
5349 return this;
5350 }
5351
Chris Wren3745a3d2012-05-22 15:11:52 -04005352 /**
Chris Wren3745a3d2012-05-22 15:11:52 -04005353 * Override the large icon when the big notification is shown.
5354 */
5355 public BigPictureStyle bigLargeIcon(Bitmap b) {
Dan Sandlerd63f9322015-05-06 15:18:49 -04005356 return bigLargeIcon(b != null ? Icon.createWithBitmap(b) : null);
5357 }
5358
5359 /**
5360 * Override the large icon when the big notification is shown.
5361 */
5362 public BigPictureStyle bigLargeIcon(Icon icon) {
Chris Wren3745a3d2012-05-22 15:11:52 -04005363 mBigLargeIconSet = true;
Dan Sandlerd63f9322015-05-06 15:18:49 -04005364 mBigLargeIcon = icon;
Chris Wren3745a3d2012-05-22 15:11:52 -04005365 return this;
5366 }
5367
Riley Andrews0394a0c2015-11-03 23:36:52 -08005368 /** @hide */
5369 public static final int MIN_ASHMEM_BITMAP_SIZE = 128 * (1 << 10);
5370
Daniel Sandler0ec46202015-06-24 01:27:05 -04005371 /**
5372 * @hide
5373 */
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07005374 @Override
5375 public void purgeResources() {
5376 super.purgeResources();
Riley Andrews8cee7c12015-11-01 23:36:04 -08005377 if (mPicture != null &&
5378 mPicture.isMutable() &&
Riley Andrews0394a0c2015-11-03 23:36:52 -08005379 mPicture.getAllocationByteCount() >= MIN_ASHMEM_BITMAP_SIZE) {
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07005380 mPicture = mPicture.createAshmemBitmap();
5381 }
5382 if (mBigLargeIcon != null) {
5383 mBigLargeIcon.convertToAshmem();
5384 }
5385 }
Christoph Studer5c510ee2014-12-15 16:32:27 +01005386
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005387 /**
5388 * @hide
5389 */
5390 public RemoteViews makeBigContentView() {
5391 // Replace mN.mLargeIcon with mBigLargeIcon if mBigLargeIconSet
Christoph Studer5c510ee2014-12-15 16:32:27 +01005392 // This covers the following cases:
5393 // 1. mBigLargeIconSet -> mBigLargeIcon (null or non-null) applies, overrides
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005394 // mN.mLargeIcon
5395 // 2. !mBigLargeIconSet -> mN.mLargeIcon applies
Dan Sandlerd63f9322015-05-06 15:18:49 -04005396 Icon oldLargeIcon = null;
Selim Cineke99acb22016-08-04 12:55:48 -07005397 Bitmap largeIconLegacy = null;
Christoph Studer5c510ee2014-12-15 16:32:27 +01005398 if (mBigLargeIconSet) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005399 oldLargeIcon = mBuilder.mN.mLargeIcon;
5400 mBuilder.mN.mLargeIcon = mBigLargeIcon;
Selim Cineke99acb22016-08-04 12:55:48 -07005401 // The legacy largeIcon might not allow us to clear the image, as it's taken in
5402 // replacement if the other one is null. Because we're restoring these legacy icons
5403 // for old listeners, this is in general non-null.
5404 largeIconLegacy = mBuilder.mN.largeIcon;
5405 mBuilder.mN.largeIcon = null;
Christoph Studer5c510ee2014-12-15 16:32:27 +01005406 }
5407
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01005408 RemoteViews contentView = getStandardView(mBuilder.getBigPictureLayoutResource());
Selim Cinek03d0d652015-11-13 13:18:09 -05005409 if (mSummaryTextSet) {
5410 contentView.setTextViewText(R.id.text, mBuilder.processLegacyText(mSummaryText));
Selim Cinek7b9605b2017-01-19 17:36:00 -08005411 mBuilder.setTextViewColorSecondary(contentView, R.id.text);
Selim Cinekc848c3a2016-01-13 15:27:30 -08005412 contentView.setViewVisibility(R.id.text, View.VISIBLE);
Selim Cinek03d0d652015-11-13 13:18:09 -05005413 }
Selim Cinek279fa862016-06-14 10:57:25 -07005414 mBuilder.setContentMinHeight(contentView, mBuilder.mN.hasLargeIcon());
Selim Cinek53e64a42015-11-16 10:40:56 -08005415
Christoph Studer5c510ee2014-12-15 16:32:27 +01005416 if (mBigLargeIconSet) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005417 mBuilder.mN.mLargeIcon = oldLargeIcon;
Selim Cineke99acb22016-08-04 12:55:48 -07005418 mBuilder.mN.largeIcon = largeIconLegacy;
Christoph Studer5c510ee2014-12-15 16:32:27 +01005419 }
5420
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005421 contentView.setImageViewBitmap(R.id.big_picture, mPicture);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005422 return contentView;
5423 }
5424
Daniel Sandlerf45564e2013-04-15 15:05:08 -04005425 /**
5426 * @hide
5427 */
5428 public void addExtras(Bundle extras) {
5429 super.addExtras(extras);
5430
5431 if (mBigLargeIconSet) {
5432 extras.putParcelable(EXTRA_LARGE_ICON_BIG, mBigLargeIcon);
5433 }
5434 extras.putParcelable(EXTRA_PICTURE, mPicture);
5435 }
5436
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04005437 /**
5438 * @hide
5439 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005440 @Override
Christoph Studer4600f9b2014-07-22 22:44:43 +02005441 protected void restoreFromExtras(Bundle extras) {
5442 super.restoreFromExtras(extras);
5443
5444 if (extras.containsKey(EXTRA_LARGE_ICON_BIG)) {
Christoph Studer5c510ee2014-12-15 16:32:27 +01005445 mBigLargeIconSet = true;
Christoph Studer4600f9b2014-07-22 22:44:43 +02005446 mBigLargeIcon = extras.getParcelable(EXTRA_LARGE_ICON_BIG);
Chris Wren3745a3d2012-05-22 15:11:52 -04005447 }
Christoph Studer4600f9b2014-07-22 22:44:43 +02005448 mPicture = extras.getParcelable(EXTRA_PICTURE);
5449 }
Selim Cinek03d0d652015-11-13 13:18:09 -05005450
5451 /**
5452 * @hide
5453 */
5454 @Override
5455 public boolean hasSummaryInHeader() {
5456 return false;
5457 }
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005458 }
5459
5460 /**
Daniel Sandler4dfbe832012-04-11 14:51:46 -04005461 * Helper class for generating large-format notifications that include a lot of text.
Joe Malin8d40d042012-11-05 11:36:40 -08005462 *
Robert Ly91c5ce32014-06-08 15:37:00 -07005463 * Here's how you'd set the <code>BigTextStyle</code> on a notification:
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005464 * <pre class="prettyprint">
Robert Ly91c5ce32014-06-08 15:37:00 -07005465 * Notification notif = new Notification.Builder(mContext)
5466 * .setContentTitle(&quot;New mail from &quot; + sender.toString())
5467 * .setContentText(subject)
5468 * .setSmallIcon(R.drawable.new_mail)
5469 * .setLargeIcon(aBitmap)
5470 * .setStyle(new Notification.BigTextStyle()
5471 * .bigText(aVeryLongString))
5472 * .build();
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005473 * </pre>
Joe Malin8d40d042012-11-05 11:36:40 -08005474 *
Daniel Sandler4dfbe832012-04-11 14:51:46 -04005475 * @see Notification#bigContentView
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005476 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005477 public static class BigTextStyle extends Style {
Jorim Jaggi457a10d2014-09-08 16:18:23 +02005478
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005479 private CharSequence mBigText;
5480
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005481 public BigTextStyle() {
5482 }
5483
Adrian Roosf5faf9d2016-05-23 13:56:15 -07005484 /**
5485 * @deprecated use {@code BigTextStyle()}.
5486 */
5487 @Deprecated
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005488 public BigTextStyle(Builder builder) {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005489 setBuilder(builder);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005490 }
5491
Chris Wrend6297db2012-05-03 16:20:13 -04005492 /**
5493 * Overrides ContentTitle in the big form of the template.
5494 * This defaults to the value passed to setContentTitle().
5495 */
5496 public BigTextStyle setBigContentTitle(CharSequence title) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04005497 internalSetBigContentTitle(safeCharSequence(title));
Chris Wrend6297db2012-05-03 16:20:13 -04005498 return this;
5499 }
5500
5501 /**
5502 * Set the first line of text after the detail section in the big form of the template.
5503 */
5504 public BigTextStyle setSummaryText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04005505 internalSetSummaryText(safeCharSequence(cs));
Chris Wrend6297db2012-05-03 16:20:13 -04005506 return this;
5507 }
5508
Chris Wren0bd664d2012-08-01 13:56:56 -04005509 /**
5510 * Provide the longer text to be displayed in the big form of the
5511 * template in place of the content text.
5512 */
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005513 public BigTextStyle bigText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04005514 mBigText = safeCharSequence(cs);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005515 return this;
5516 }
5517
Daniel Sandlerf45564e2013-04-15 15:05:08 -04005518 /**
5519 * @hide
5520 */
5521 public void addExtras(Bundle extras) {
5522 super.addExtras(extras);
5523
Christoph Studer4600f9b2014-07-22 22:44:43 +02005524 extras.putCharSequence(EXTRA_BIG_TEXT, mBigText);
5525 }
5526
5527 /**
5528 * @hide
5529 */
5530 @Override
5531 protected void restoreFromExtras(Bundle extras) {
5532 super.restoreFromExtras(extras);
5533
5534 mBigText = extras.getCharSequence(EXTRA_BIG_TEXT);
Daniel Sandlerf45564e2013-04-15 15:05:08 -04005535 }
5536
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005537 /**
Selim Cinek7d1009b2017-01-25 15:28:28 -08005538 * @param increasedHeight true if this layout be created with an increased height.
5539 *
5540 * @hide
5541 */
5542 @Override
5543 public RemoteViews makeContentView(boolean increasedHeight) {
5544 if (increasedHeight) {
5545 ArrayList<Action> actions = mBuilder.mActions;
5546 mBuilder.mActions = new ArrayList<>();
5547 RemoteViews remoteViews = makeBigContentView();
5548 mBuilder.mActions = actions;
5549 return remoteViews;
5550 }
5551 return super.makeContentView(increasedHeight);
5552 }
5553
5554 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005555 * @hide
5556 */
Selim Cinek87ed69b2017-02-09 15:59:43 -08005557 @Override
5558 public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
5559 if (increasedHeight && mBuilder.mActions.size() > 0) {
5560 return makeBigContentView();
5561 }
5562 return super.makeHeadsUpContentView(increasedHeight);
5563 }
5564
5565 /**
5566 * @hide
5567 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005568 public RemoteViews makeBigContentView() {
Christoph Studer4600f9b2014-07-22 22:44:43 +02005569
5570 // Nasty
Selim Cinek75998782016-04-26 10:39:17 -07005571 CharSequence text = mBuilder.getAllExtras().getCharSequence(EXTRA_TEXT);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005572 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, null);
Daniel Sandler916ad912012-06-13 12:17:07 -04005573
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01005574 RemoteViews contentView = getStandardView(mBuilder.getBigTextLayoutResource());
Joe Malin8d40d042012-11-05 11:36:40 -08005575
Selim Cinek75998782016-04-26 10:39:17 -07005576 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, text);
Christoph Studer4600f9b2014-07-22 22:44:43 +02005577
Selim Cinek3a2c4b92015-12-17 17:01:17 -08005578 CharSequence bigTextText = mBuilder.processLegacyText(mBigText);
Selim Cinek75998782016-04-26 10:39:17 -07005579 if (TextUtils.isEmpty(bigTextText)) {
5580 // In case the bigtext is null / empty fall back to the normal text to avoid a weird
5581 // experience
5582 bigTextText = mBuilder.processLegacyText(text);
5583 }
Adrian Roosb1f427c2016-05-26 12:27:15 -07005584 applyBigTextContentView(mBuilder, contentView, bigTextText);
Selim Cinek4fb12d32015-11-19 18:10:48 -08005585
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005586 return contentView;
5587 }
5588
Adrian Roosb1f427c2016-05-26 12:27:15 -07005589 static void applyBigTextContentView(Builder builder,
5590 RemoteViews contentView, CharSequence bigTextText) {
5591 contentView.setTextViewText(R.id.big_text, bigTextText);
Selim Cinek7b9605b2017-01-19 17:36:00 -08005592 builder.setTextViewColorSecondary(contentView, R.id.big_text);
Adrian Roosb1f427c2016-05-26 12:27:15 -07005593 contentView.setViewVisibility(R.id.big_text,
5594 TextUtils.isEmpty(bigTextText) ? View.GONE : View.VISIBLE);
Selim Cinek279fa862016-06-14 10:57:25 -07005595 contentView.setBoolean(R.id.big_text, "setHasImage", builder.mN.hasLargeIcon());
Adrian Roosb1f427c2016-05-26 12:27:15 -07005596 }
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005597 }
Daniel Sandler879c5e02012-04-17 16:46:51 -04005598
5599 /**
Alex Hillsfc737de2016-03-23 17:33:02 -04005600 * Helper class for generating large-format notifications that include multiple back-and-forth
5601 * messages of varying types between any number of people.
5602 *
5603 * <br>
5604 * If the platform does not provide large-format notifications, this method has no effect. The
5605 * user will always see the normal notification view.
5606 * <br>
5607 * This class is a "rebuilder": It attaches to a Builder object and modifies its behavior, like
5608 * so:
5609 * <pre class="prettyprint">
5610 *
5611 * Notification noti = new Notification.Builder()
5612 * .setContentTitle(&quot;2 new messages wtih &quot; + sender.toString())
5613 * .setContentText(subject)
5614 * .setSmallIcon(R.drawable.new_message)
5615 * .setLargeIcon(aBitmap)
5616 * .setStyle(new Notification.MessagingStyle(resources.getString(R.string.reply_name))
5617 * .addMessage(messages[0].getText(), messages[0].getTime(), messages[0].getSender())
5618 * .addMessage(messages[1].getText(), messages[1].getTime(), messages[1].getSender()))
5619 * .build();
5620 * </pre>
5621 */
5622 public static class MessagingStyle extends Style {
5623
5624 /**
5625 * The maximum number of messages that will be retained in the Notification itself (the
5626 * number displayed is up to the platform).
5627 */
5628 public static final int MAXIMUM_RETAINED_MESSAGES = 25;
5629
5630 CharSequence mUserDisplayName;
5631 CharSequence mConversationTitle;
Alex Hillsd9b04d92016-04-11 16:38:16 -04005632 List<Message> mMessages = new ArrayList<>();
Adrian Roos437cd562017-01-18 15:47:03 -08005633 List<Message> mHistoricMessages = new ArrayList<>();
Alex Hillsfc737de2016-03-23 17:33:02 -04005634
5635 MessagingStyle() {
5636 }
5637
5638 /**
Alex Hillsfd590442016-10-07 09:52:44 -04005639 * @param userDisplayName Required - the name to be displayed for any replies sent by the
5640 * user before the posting app reposts the notification with those messages after they've
5641 * been actually sent and in previous messages sent by the user added in
Alex Hillsfc737de2016-03-23 17:33:02 -04005642 * {@link #addMessage(Notification.MessagingStyle.Message)}
5643 */
Alex Hillsfd590442016-10-07 09:52:44 -04005644 public MessagingStyle(@NonNull CharSequence userDisplayName) {
Alex Hillsfc737de2016-03-23 17:33:02 -04005645 mUserDisplayName = userDisplayName;
5646 }
5647
5648 /**
5649 * Returns the name to be displayed for any replies sent by the user
5650 */
5651 public CharSequence getUserDisplayName() {
5652 return mUserDisplayName;
5653 }
5654
5655 /**
Alex Hillsfc737de2016-03-23 17:33:02 -04005656 * Sets the title to be displayed on this conversation. This should only be used for
5657 * group messaging and left unset for one-on-one conversations.
5658 * @param conversationTitle
5659 * @return this object for method chaining.
5660 */
5661 public MessagingStyle setConversationTitle(CharSequence conversationTitle) {
5662 mConversationTitle = conversationTitle;
5663 return this;
5664 }
5665
5666 /**
5667 * Return the title to be displayed on this conversation. Can be <code>null</code> and
5668 * should be for one-on-one conversations
5669 */
5670 public CharSequence getConversationTitle() {
5671 return mConversationTitle;
5672 }
5673
5674 /**
5675 * Adds a message for display by this notification. Convenience call for a simple
5676 * {@link Message} in {@link #addMessage(Notification.MessagingStyle.Message)}.
5677 * @param text A {@link CharSequence} to be displayed as the message content
5678 * @param timestamp Time at which the message arrived
5679 * @param sender A {@link CharSequence} to be used for displaying the name of the
5680 * sender. Should be <code>null</code> for messages by the current user, in which case
5681 * the platform will insert {@link #getUserDisplayName()}.
5682 * Should be unique amongst all individuals in the conversation, and should be
5683 * consistent during re-posts of the notification.
5684 *
5685 * @see Message#Message(CharSequence, long, CharSequence)
5686 *
5687 * @return this object for method chaining
5688 */
5689 public MessagingStyle addMessage(CharSequence text, long timestamp, CharSequence sender) {
Adrian Roos437cd562017-01-18 15:47:03 -08005690 return addMessage(new Message(text, timestamp, sender));
Alex Hillsfc737de2016-03-23 17:33:02 -04005691 }
5692
5693 /**
5694 * Adds a {@link Message} for display in this notification.
Adrian Roos437cd562017-01-18 15:47:03 -08005695 *
5696 * <p>The messages should be added in chronologic order, i.e. the oldest first,
5697 * the newest last.
5698 *
Alex Hillsfc737de2016-03-23 17:33:02 -04005699 * @param message The {@link Message} to be displayed
5700 * @return this object for method chaining
5701 */
5702 public MessagingStyle addMessage(Message message) {
5703 mMessages.add(message);
5704 if (mMessages.size() > MAXIMUM_RETAINED_MESSAGES) {
5705 mMessages.remove(0);
5706 }
5707 return this;
5708 }
5709
5710 /**
Adrian Roos437cd562017-01-18 15:47:03 -08005711 * Adds a {@link Message} for historic context in this notification.
5712 *
5713 * <p>Messages should be added as historic if they are not the main subject of the
5714 * notification but may give context to a conversation. The system may choose to present
5715 * them only when relevant, e.g. when replying to a message through a {@link RemoteInput}.
5716 *
5717 * <p>The messages should be added in chronologic order, i.e. the oldest first,
5718 * the newest last.
5719 *
5720 * @param message The historic {@link Message} to be added
5721 * @return this object for method chaining
5722 */
5723 public MessagingStyle addHistoricMessage(Message message) {
5724 mHistoricMessages.add(message);
5725 if (mHistoricMessages.size() > MAXIMUM_RETAINED_MESSAGES) {
5726 mHistoricMessages.remove(0);
5727 }
5728 return this;
5729 }
5730
5731 /**
Alex Hillsfc737de2016-03-23 17:33:02 -04005732 * Gets the list of {@code Message} objects that represent the notification
5733 */
5734 public List<Message> getMessages() {
5735 return mMessages;
5736 }
5737
5738 /**
Adrian Roos437cd562017-01-18 15:47:03 -08005739 * Gets the list of historic {@code Message}s in the notification.
5740 */
5741 public List<Message> getHistoricMessages() {
5742 return mHistoricMessages;
5743 }
5744
5745 /**
Alex Hillsfc737de2016-03-23 17:33:02 -04005746 * @hide
5747 */
5748 @Override
5749 public void addExtras(Bundle extras) {
5750 super.addExtras(extras);
5751 if (mUserDisplayName != null) {
5752 extras.putCharSequence(EXTRA_SELF_DISPLAY_NAME, mUserDisplayName);
5753 }
5754 if (mConversationTitle != null) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04005755 extras.putCharSequence(EXTRA_CONVERSATION_TITLE, mConversationTitle);
Alex Hillsfc737de2016-03-23 17:33:02 -04005756 }
Alex Hillsd9b04d92016-04-11 16:38:16 -04005757 if (!mMessages.isEmpty()) { extras.putParcelableArray(EXTRA_MESSAGES,
5758 Message.getBundleArrayForMessages(mMessages));
Alex Hillsfc737de2016-03-23 17:33:02 -04005759 }
Adrian Roos437cd562017-01-18 15:47:03 -08005760 if (!mHistoricMessages.isEmpty()) { extras.putParcelableArray(EXTRA_HISTORIC_MESSAGES,
5761 Message.getBundleArrayForMessages(mHistoricMessages));
5762 }
Adrian Roos33fbd2c2016-05-27 15:35:28 -07005763
5764 fixTitleAndTextExtras(extras);
5765 }
5766
5767 private void fixTitleAndTextExtras(Bundle extras) {
5768 Message m = findLatestIncomingMessage();
5769 CharSequence text = (m == null) ? null : m.mText;
5770 CharSequence sender = m == null ? null
5771 : TextUtils.isEmpty(m.mSender) ? mUserDisplayName : m.mSender;
5772 CharSequence title;
5773 if (!TextUtils.isEmpty(mConversationTitle)) {
5774 if (!TextUtils.isEmpty(sender)) {
5775 BidiFormatter bidi = BidiFormatter.getInstance();
5776 title = mBuilder.mContext.getString(
5777 com.android.internal.R.string.notification_messaging_title_template,
5778 bidi.unicodeWrap(mConversationTitle), bidi.unicodeWrap(m.mSender));
5779 } else {
5780 title = mConversationTitle;
5781 }
5782 } else {
5783 title = sender;
5784 }
5785
5786 if (title != null) {
5787 extras.putCharSequence(EXTRA_TITLE, title);
5788 }
5789 if (text != null) {
5790 extras.putCharSequence(EXTRA_TEXT, text);
5791 }
Alex Hillsfc737de2016-03-23 17:33:02 -04005792 }
5793
5794 /**
5795 * @hide
5796 */
5797 @Override
5798 protected void restoreFromExtras(Bundle extras) {
5799 super.restoreFromExtras(extras);
5800
5801 mMessages.clear();
Adrian Roos437cd562017-01-18 15:47:03 -08005802 mHistoricMessages.clear();
Adrian Roos96b7e202016-05-17 13:50:38 -07005803 mUserDisplayName = extras.getCharSequence(EXTRA_SELF_DISPLAY_NAME);
5804 mConversationTitle = extras.getCharSequence(EXTRA_CONVERSATION_TITLE);
Adrian Roos437cd562017-01-18 15:47:03 -08005805 Parcelable[] messages = extras.getParcelableArray(EXTRA_MESSAGES);
5806 if (messages != null && messages instanceof Parcelable[]) {
5807 mMessages = Message.getMessagesFromBundleArray(messages);
5808 }
5809 Parcelable[] histMessages = extras.getParcelableArray(EXTRA_HISTORIC_MESSAGES);
5810 if (histMessages != null && histMessages instanceof Parcelable[]) {
5811 mHistoricMessages = Message.getMessagesFromBundleArray(histMessages);
Alex Hillsfc737de2016-03-23 17:33:02 -04005812 }
5813 }
5814
5815 /**
5816 * @hide
5817 */
Adrian Roosc1a80b02016-04-05 14:54:55 -07005818 @Override
Selim Cinek7d1009b2017-01-25 15:28:28 -08005819 public RemoteViews makeContentView(boolean increasedHeight) {
5820 if (!increasedHeight) {
5821 Message m = findLatestIncomingMessage();
5822 CharSequence title = mConversationTitle != null
5823 ? mConversationTitle
5824 : (m == null) ? null : m.mSender;
5825 CharSequence text = (m == null)
5826 ? null
5827 : mConversationTitle != null ? makeMessageLine(m, mBuilder) : m.mText;
Alex Hillsfc737de2016-03-23 17:33:02 -04005828
Selim Cinek7d1009b2017-01-25 15:28:28 -08005829 return mBuilder.applyStandardTemplate(mBuilder.getBaseLayoutResource(),
5830 mBuilder.mParams.reset().hasProgress(false).title(title).text(text));
5831 } else {
5832 ArrayList<Action> actions = mBuilder.mActions;
5833 mBuilder.mActions = new ArrayList<>();
5834 RemoteViews remoteViews = makeBigContentView();
5835 mBuilder.mActions = actions;
5836 return remoteViews;
5837 }
Adrian Roosc1a80b02016-04-05 14:54:55 -07005838 }
5839
5840 private Message findLatestIncomingMessage() {
5841 for (int i = mMessages.size() - 1; i >= 0; i--) {
5842 Message m = mMessages.get(i);
5843 // Incoming messages have a non-empty sender.
5844 if (!TextUtils.isEmpty(m.mSender)) {
5845 return m;
5846 }
5847 }
Adrian Roos33fbd2c2016-05-27 15:35:28 -07005848 if (!mMessages.isEmpty()) {
5849 // No incoming messages, fall back to outgoing message
5850 return mMessages.get(mMessages.size() - 1);
5851 }
Adrian Roosc1a80b02016-04-05 14:54:55 -07005852 return null;
5853 }
5854
5855 /**
5856 * @hide
5857 */
5858 @Override
5859 public RemoteViews makeBigContentView() {
5860 CharSequence title = !TextUtils.isEmpty(super.mBigContentTitle)
5861 ? super.mBigContentTitle
5862 : mConversationTitle;
5863 boolean hasTitle = !TextUtils.isEmpty(title);
5864
Adrian Roosfeafa052016-06-01 17:09:45 -07005865 if (mMessages.size() == 1) {
5866 // Special case for a single message: Use the big text style
5867 // so the collapsed and expanded versions match nicely.
5868 CharSequence bigTitle;
5869 CharSequence text;
5870 if (hasTitle) {
5871 bigTitle = title;
Selim Cinek7b9605b2017-01-19 17:36:00 -08005872 text = makeMessageLine(mMessages.get(0), mBuilder);
Adrian Roosfeafa052016-06-01 17:09:45 -07005873 } else {
5874 bigTitle = mMessages.get(0).mSender;
5875 text = mMessages.get(0).mText;
5876 }
Adrian Roosb1f427c2016-05-26 12:27:15 -07005877 RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(
5878 mBuilder.getBigTextLayoutResource(),
Adrian Roos70d7aa32017-01-11 15:39:06 -08005879 mBuilder.mParams.reset().hasProgress(false).title(bigTitle).text(null));
Adrian Roosb1f427c2016-05-26 12:27:15 -07005880 BigTextStyle.applyBigTextContentView(mBuilder, contentView, text);
5881 return contentView;
5882 }
5883
Adrian Roos48d746a2016-04-12 14:57:28 -07005884 RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(
Adrian Roosc1a80b02016-04-05 14:54:55 -07005885 mBuilder.getMessagingLayoutResource(),
Adrian Roos70d7aa32017-01-11 15:39:06 -08005886 mBuilder.mParams.reset().hasProgress(false).title(title).text(null));
Adrian Roosc1a80b02016-04-05 14:54:55 -07005887
5888 int[] rowIds = {R.id.inbox_text0, R.id.inbox_text1, R.id.inbox_text2, R.id.inbox_text3,
5889 R.id.inbox_text4, R.id.inbox_text5, R.id.inbox_text6};
5890
5891 // Make sure all rows are gone in case we reuse a view.
5892 for (int rowId : rowIds) {
5893 contentView.setViewVisibility(rowId, View.GONE);
5894 }
5895
5896 int i=0;
Adrian Roos2d5dbba2016-06-08 17:11:53 -07005897 contentView.setViewLayoutMarginBottomDimen(R.id.line1,
5898 hasTitle ? R.dimen.notification_messaging_spacing : 0);
Adrian Roosc1a80b02016-04-05 14:54:55 -07005899 contentView.setInt(R.id.notification_messaging, "setNumIndentLines",
Selim Cinek279fa862016-06-14 10:57:25 -07005900 !mBuilder.mN.hasLargeIcon() ? 0 : (hasTitle ? 1 : 2));
Adrian Roosc1a80b02016-04-05 14:54:55 -07005901
Adrian Roosfeafa052016-06-01 17:09:45 -07005902 int contractedChildId = View.NO_ID;
5903 Message contractedMessage = findLatestIncomingMessage();
Adrian Roos437cd562017-01-18 15:47:03 -08005904 int firstHistoricMessage = Math.max(0, mHistoricMessages.size()
5905 - (rowIds.length - mMessages.size()));
5906 while (firstHistoricMessage + i < mHistoricMessages.size() && i < rowIds.length) {
5907 Message m = mHistoricMessages.get(firstHistoricMessage + i);
5908 int rowId = rowIds[i];
5909
Selim Cinek7b9605b2017-01-19 17:36:00 -08005910 contentView.setTextViewText(rowId, makeMessageLine(m, mBuilder));
Adrian Roos437cd562017-01-18 15:47:03 -08005911
5912 if (contractedMessage == m) {
5913 contractedChildId = rowId;
5914 }
5915
5916 i++;
5917 }
5918
Adrian Roosc1a80b02016-04-05 14:54:55 -07005919 int firstMessage = Math.max(0, mMessages.size() - rowIds.length);
5920 while (firstMessage + i < mMessages.size() && i < rowIds.length) {
5921 Message m = mMessages.get(firstMessage + i);
5922 int rowId = rowIds[i];
5923
5924 contentView.setViewVisibility(rowId, View.VISIBLE);
Selim Cinek7b9605b2017-01-19 17:36:00 -08005925 contentView.setTextViewText(rowId, makeMessageLine(m, mBuilder));
5926 mBuilder.setTextViewColorSecondary(contentView, rowId);
Adrian Roosc1a80b02016-04-05 14:54:55 -07005927
Adrian Roosfeafa052016-06-01 17:09:45 -07005928 if (contractedMessage == m) {
5929 contractedChildId = rowId;
5930 }
5931
Adrian Roosc1a80b02016-04-05 14:54:55 -07005932 i++;
5933 }
Adrian Roos437cd562017-01-18 15:47:03 -08005934 // Clear the remaining views for reapply. Ensures that historic message views can
5935 // reliably be identified as being GONE and having non-null text.
5936 while (i < rowIds.length) {
5937 int rowId = rowIds[i];
5938 contentView.setTextViewText(rowId, null);
5939 i++;
5940 }
5941
Adrian Roosfeafa052016-06-01 17:09:45 -07005942 // Record this here to allow transformation between the contracted and expanded views.
5943 contentView.setInt(R.id.notification_messaging, "setContractedChildId",
5944 contractedChildId);
Alex Hillsfc737de2016-03-23 17:33:02 -04005945 return contentView;
5946 }
5947
Selim Cinek7b9605b2017-01-19 17:36:00 -08005948 private CharSequence makeMessageLine(Message m, Builder builder) {
Adrian Roosc1a80b02016-04-05 14:54:55 -07005949 BidiFormatter bidi = BidiFormatter.getInstance();
5950 SpannableStringBuilder sb = new SpannableStringBuilder();
Selim Cinek7b9605b2017-01-19 17:36:00 -08005951 boolean colorize = builder.isColorized();
Adrian Roosc1a80b02016-04-05 14:54:55 -07005952 if (TextUtils.isEmpty(m.mSender)) {
5953 CharSequence replyName = mUserDisplayName == null ? "" : mUserDisplayName;
5954 sb.append(bidi.unicodeWrap(replyName),
Selim Cinek7b9605b2017-01-19 17:36:00 -08005955 makeFontColorSpan(colorize
5956 ? builder.getPrimaryTextColor()
5957 : mBuilder.resolveContrastColor()),
Adrian Roosc1a80b02016-04-05 14:54:55 -07005958 0 /* flags */);
5959 } else {
5960 sb.append(bidi.unicodeWrap(m.mSender),
Selim Cinek7b9605b2017-01-19 17:36:00 -08005961 makeFontColorSpan(colorize
5962 ? builder.getPrimaryTextColor()
5963 : Color.BLACK),
Adrian Roosc1a80b02016-04-05 14:54:55 -07005964 0 /* flags */);
5965 }
5966 CharSequence text = m.mText == null ? "" : m.mText;
5967 sb.append(" ").append(bidi.unicodeWrap(text));
5968 return sb;
5969 }
5970
Adrian Roosdedd1df2016-04-26 16:38:47 -07005971 /**
5972 * @hide
5973 */
5974 @Override
Selim Cinek87ed69b2017-02-09 15:59:43 -08005975 public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
5976 if (increasedHeight) {
5977 return makeBigContentView();
5978 }
Adrian Roosdedd1df2016-04-26 16:38:47 -07005979 Message m = findLatestIncomingMessage();
5980 CharSequence title = mConversationTitle != null
5981 ? mConversationTitle
5982 : (m == null) ? null : m.mSender;
5983 CharSequence text = (m == null)
5984 ? null
Selim Cinek7b9605b2017-01-19 17:36:00 -08005985 : mConversationTitle != null ? makeMessageLine(m, mBuilder) : m.mText;
Adrian Roosdedd1df2016-04-26 16:38:47 -07005986
5987 return mBuilder.applyStandardTemplateWithActions(mBuilder.getBigBaseLayoutResource(),
Adrian Roos70d7aa32017-01-11 15:39:06 -08005988 mBuilder.mParams.reset().hasProgress(false).title(title).text(text));
Adrian Roosdedd1df2016-04-26 16:38:47 -07005989 }
5990
Adrian Roosc1a80b02016-04-05 14:54:55 -07005991 private static TextAppearanceSpan makeFontColorSpan(int color) {
5992 return new TextAppearanceSpan(null, 0, 0,
5993 ColorStateList.valueOf(color), null);
5994 }
5995
Alex Hillsd9b04d92016-04-11 16:38:16 -04005996 public static final class Message {
5997
5998 static final String KEY_TEXT = "text";
5999 static final String KEY_TIMESTAMP = "time";
6000 static final String KEY_SENDER = "sender";
6001 static final String KEY_DATA_MIME_TYPE = "type";
6002 static final String KEY_DATA_URI= "uri";
Shane Brennan5a871862017-03-11 13:14:17 -08006003 static final String KEY_EXTRAS_BUNDLE = "extras";
Alex Hillsfc737de2016-03-23 17:33:02 -04006004
6005 private final CharSequence mText;
6006 private final long mTimestamp;
6007 private final CharSequence mSender;
6008
Shane Brennan5a871862017-03-11 13:14:17 -08006009 private Bundle mExtras = new Bundle();
Alex Hillsfc737de2016-03-23 17:33:02 -04006010 private String mDataMimeType;
6011 private Uri mDataUri;
6012
6013 /**
6014 * Constructor
6015 * @param text A {@link CharSequence} to be displayed as the message content
6016 * @param timestamp Time at which the message arrived
6017 * @param sender A {@link CharSequence} to be used for displaying the name of the
6018 * sender. Should be <code>null</code> for messages by the current user, in which case
6019 * the platform will insert {@link MessagingStyle#getUserDisplayName()}.
6020 * Should be unique amongst all individuals in the conversation, and should be
6021 * consistent during re-posts of the notification.
6022 */
6023 public Message(CharSequence text, long timestamp, CharSequence sender){
6024 mText = text;
6025 mTimestamp = timestamp;
6026 mSender = sender;
6027 }
6028
6029 /**
6030 * Sets a binary blob of data and an associated MIME type for a message. In the case
6031 * where the platform doesn't support the MIME type, the original text provided in the
6032 * constructor will be used.
6033 * @param dataMimeType The MIME type of the content. See
6034 * <a href="{@docRoot}notifications/messaging.html"> for the list of supported MIME
6035 * types on Android and Android Wear.
6036 * @param dataUri The uri containing the content whose type is given by the MIME type.
6037 * <p class="note">
6038 * <ol>
6039 * <li>Notification Listeners including the System UI need permission to access the
6040 * data the Uri points to. The recommended ways to do this are:</li>
6041 * <li>Store the data in your own ContentProvider, making sure that other apps have
6042 * the correct permission to access your provider. The preferred mechanism for
6043 * providing access is to use per-URI permissions which are temporary and only
6044 * grant access to the receiving application. An easy way to create a
6045 * ContentProvider like this is to use the FileProvider helper class.</li>
6046 * <li>Use the system MediaStore. The MediaStore is primarily aimed at video, audio
6047 * and image MIME types, however beginning with Android 3.0 (API level 11) it can
6048 * also store non-media types (see MediaStore.Files for more info). Files can be
6049 * inserted into the MediaStore using scanFile() after which a content:// style
6050 * Uri suitable for sharing is passed to the provided onScanCompleted() callback.
6051 * Note that once added to the system MediaStore the content is accessible to any
6052 * app on the device.</li>
6053 * </ol>
6054 * @return this object for method chaining
6055 */
6056 public Message setData(String dataMimeType, Uri dataUri) {
6057 mDataMimeType = dataMimeType;
6058 mDataUri = dataUri;
6059 return this;
6060 }
6061
Alex Hillsfc737de2016-03-23 17:33:02 -04006062 /**
6063 * Get the text to be used for this message, or the fallback text if a type and content
6064 * Uri have been set
6065 */
6066 public CharSequence getText() {
6067 return mText;
6068 }
6069
6070 /**
6071 * Get the time at which this message arrived
6072 */
6073 public long getTimestamp() {
6074 return mTimestamp;
6075 }
6076
6077 /**
Shane Brennan5a871862017-03-11 13:14:17 -08006078 * Get the extras Bundle for this message.
6079 */
6080 public Bundle getExtras() {
6081 return mExtras;
6082 }
6083
6084 /**
Alex Hillsfc737de2016-03-23 17:33:02 -04006085 * Get the text used to display the contact's name in the messaging experience
6086 */
6087 public CharSequence getSender() {
6088 return mSender;
6089 }
6090
6091 /**
6092 * Get the MIME type of the data pointed to by the Uri
6093 */
6094 public String getDataMimeType() {
6095 return mDataMimeType;
6096 }
6097
6098 /**
6099 * Get the the Uri pointing to the content of the message. Can be null, in which case
6100 * {@see #getText()} is used.
6101 */
6102 public Uri getDataUri() {
6103 return mDataUri;
6104 }
6105
Alex Hillsd9b04d92016-04-11 16:38:16 -04006106 private Bundle toBundle() {
6107 Bundle bundle = new Bundle();
Alex Hillsfc737de2016-03-23 17:33:02 -04006108 if (mText != null) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04006109 bundle.putCharSequence(KEY_TEXT, mText);
Alex Hillsfc737de2016-03-23 17:33:02 -04006110 }
Alex Hillsd9b04d92016-04-11 16:38:16 -04006111 bundle.putLong(KEY_TIMESTAMP, mTimestamp);
Alex Hillsfc737de2016-03-23 17:33:02 -04006112 if (mSender != null) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04006113 bundle.putCharSequence(KEY_SENDER, mSender);
Alex Hillsfc737de2016-03-23 17:33:02 -04006114 }
6115 if (mDataMimeType != null) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04006116 bundle.putString(KEY_DATA_MIME_TYPE, mDataMimeType);
Alex Hillsfc737de2016-03-23 17:33:02 -04006117 }
6118 if (mDataUri != null) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04006119 bundle.putParcelable(KEY_DATA_URI, mDataUri);
Alex Hillsfc737de2016-03-23 17:33:02 -04006120 }
Shane Brennan5a871862017-03-11 13:14:17 -08006121 if (mExtras != null) {
6122 bundle.putBundle(KEY_EXTRAS_BUNDLE, mExtras);
6123 }
Alex Hillsd9b04d92016-04-11 16:38:16 -04006124 return bundle;
Alex Hillsfc737de2016-03-23 17:33:02 -04006125 }
6126
Alex Hillsd9b04d92016-04-11 16:38:16 -04006127 static Bundle[] getBundleArrayForMessages(List<Message> messages) {
6128 Bundle[] bundles = new Bundle[messages.size()];
6129 final int N = messages.size();
6130 for (int i = 0; i < N; i++) {
6131 bundles[i] = messages.get(i).toBundle();
6132 }
6133 return bundles;
6134 }
6135
Adrian Roosdedd1df2016-04-26 16:38:47 -07006136 static List<Message> getMessagesFromBundleArray(Parcelable[] bundles) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04006137 List<Message> messages = new ArrayList<>(bundles.length);
6138 for (int i = 0; i < bundles.length; i++) {
Adrian Roosdedd1df2016-04-26 16:38:47 -07006139 if (bundles[i] instanceof Bundle) {
6140 Message message = getMessageFromBundle((Bundle)bundles[i]);
6141 if (message != null) {
6142 messages.add(message);
6143 }
Alex Hillsd9b04d92016-04-11 16:38:16 -04006144 }
6145 }
6146 return messages;
6147 }
6148
6149 static Message getMessageFromBundle(Bundle bundle) {
6150 try {
Adrian Roosfbddd2c2016-05-13 12:57:20 -07006151 if (!bundle.containsKey(KEY_TEXT) || !bundle.containsKey(KEY_TIMESTAMP)) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04006152 return null;
6153 } else {
6154 Message message = new Message(bundle.getCharSequence(KEY_TEXT),
6155 bundle.getLong(KEY_TIMESTAMP), bundle.getCharSequence(KEY_SENDER));
6156 if (bundle.containsKey(KEY_DATA_MIME_TYPE) &&
6157 bundle.containsKey(KEY_DATA_URI)) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04006158 message.setData(bundle.getString(KEY_DATA_MIME_TYPE),
6159 (Uri) bundle.getParcelable(KEY_DATA_URI));
Alex Hillsfc737de2016-03-23 17:33:02 -04006160 }
Shane Brennan5a871862017-03-11 13:14:17 -08006161 if (bundle.containsKey(KEY_EXTRAS_BUNDLE)) {
6162 message.getExtras().putAll(bundle.getBundle(KEY_EXTRAS_BUNDLE));
6163 }
Alex Hillsd9b04d92016-04-11 16:38:16 -04006164 return message;
6165 }
6166 } catch (ClassCastException e) {
6167 return null;
6168 }
6169 }
Alex Hillsfc737de2016-03-23 17:33:02 -04006170 }
6171 }
6172
6173 /**
Daniel Sandler879c5e02012-04-17 16:46:51 -04006174 * Helper class for generating large-format notifications that include a list of (up to 5) strings.
Joe Malin8d40d042012-11-05 11:36:40 -08006175 *
Robert Ly91c5ce32014-06-08 15:37:00 -07006176 * Here's how you'd set the <code>InboxStyle</code> on a notification:
Daniel Sandler879c5e02012-04-17 16:46:51 -04006177 * <pre class="prettyprint">
Robert Ly91c5ce32014-06-08 15:37:00 -07006178 * Notification notif = new Notification.Builder(mContext)
6179 * .setContentTitle(&quot;5 New mails from &quot; + sender.toString())
6180 * .setContentText(subject)
6181 * .setSmallIcon(R.drawable.new_mail)
6182 * .setLargeIcon(aBitmap)
6183 * .setStyle(new Notification.InboxStyle()
6184 * .addLine(str1)
6185 * .addLine(str2)
6186 * .setContentTitle(&quot;&quot;)
6187 * .setSummaryText(&quot;+3 more&quot;))
6188 * .build();
Daniel Sandler879c5e02012-04-17 16:46:51 -04006189 * </pre>
Joe Malin8d40d042012-11-05 11:36:40 -08006190 *
Daniel Sandler879c5e02012-04-17 16:46:51 -04006191 * @see Notification#bigContentView
6192 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04006193 public static class InboxStyle extends Style {
Daniel Sandler879c5e02012-04-17 16:46:51 -04006194 private ArrayList<CharSequence> mTexts = new ArrayList<CharSequence>(5);
6195
Chris Wrenfbd96ba2012-05-01 12:03:58 -04006196 public InboxStyle() {
6197 }
6198
Adrian Roosf5faf9d2016-05-23 13:56:15 -07006199 /**
6200 * @deprecated use {@code InboxStyle()}.
6201 */
6202 @Deprecated
Daniel Sandler879c5e02012-04-17 16:46:51 -04006203 public InboxStyle(Builder builder) {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04006204 setBuilder(builder);
Daniel Sandler879c5e02012-04-17 16:46:51 -04006205 }
6206
Chris Wrend6297db2012-05-03 16:20:13 -04006207 /**
6208 * Overrides ContentTitle in the big form of the template.
6209 * This defaults to the value passed to setContentTitle().
6210 */
6211 public InboxStyle setBigContentTitle(CharSequence title) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04006212 internalSetBigContentTitle(safeCharSequence(title));
Chris Wrend6297db2012-05-03 16:20:13 -04006213 return this;
6214 }
6215
6216 /**
6217 * Set the first line of text after the detail section in the big form of the template.
6218 */
6219 public InboxStyle setSummaryText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04006220 internalSetSummaryText(safeCharSequence(cs));
Chris Wrend6297db2012-05-03 16:20:13 -04006221 return this;
6222 }
6223
Chris Wren0bd664d2012-08-01 13:56:56 -04006224 /**
6225 * Append a line to the digest section of the Inbox notification.
6226 */
Daniel Sandler879c5e02012-04-17 16:46:51 -04006227 public InboxStyle addLine(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04006228 mTexts.add(safeCharSequence(cs));
Daniel Sandler879c5e02012-04-17 16:46:51 -04006229 return this;
6230 }
6231
Daniel Sandlerf45564e2013-04-15 15:05:08 -04006232 /**
6233 * @hide
6234 */
6235 public void addExtras(Bundle extras) {
6236 super.addExtras(extras);
Christoph Studer4600f9b2014-07-22 22:44:43 +02006237
Daniel Sandlerf45564e2013-04-15 15:05:08 -04006238 CharSequence[] a = new CharSequence[mTexts.size()];
6239 extras.putCharSequenceArray(EXTRA_TEXT_LINES, mTexts.toArray(a));
6240 }
6241
Christoph Studer4600f9b2014-07-22 22:44:43 +02006242 /**
6243 * @hide
6244 */
6245 @Override
6246 protected void restoreFromExtras(Bundle extras) {
6247 super.restoreFromExtras(extras);
6248
6249 mTexts.clear();
6250 if (extras.containsKey(EXTRA_TEXT_LINES)) {
6251 Collections.addAll(mTexts, extras.getCharSequenceArray(EXTRA_TEXT_LINES));
6252 }
6253 }
6254
Julia Reynoldsd9228f12015-10-20 10:37:27 -04006255 /**
6256 * @hide
6257 */
6258 public RemoteViews makeBigContentView() {
Selim Cinekc848c3a2016-01-13 15:27:30 -08006259 // Remove the content text so it disappears unless you have a summary
Christoph Studer4600f9b2014-07-22 22:44:43 +02006260 // Nasty
Julia Reynoldsd9228f12015-10-20 10:37:27 -04006261 CharSequence oldBuilderContentText = mBuilder.mN.extras.getCharSequence(EXTRA_TEXT);
6262 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, null);
Christoph Studer4600f9b2014-07-22 22:44:43 +02006263
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01006264 RemoteViews contentView = getStandardView(mBuilder.getInboxLayoutResource());
Daniel Sandler619738c2012-06-07 16:33:08 -04006265
Julia Reynoldsd9228f12015-10-20 10:37:27 -04006266 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, oldBuilderContentText);
Christoph Studer4600f9b2014-07-22 22:44:43 +02006267
Chris Wrend6297db2012-05-03 16:20:13 -04006268 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 -04006269 R.id.inbox_text4, R.id.inbox_text5, R.id.inbox_text6};
Chris Wrend6297db2012-05-03 16:20:13 -04006270
Chris Wren4ed80d52012-05-17 09:30:03 -04006271 // Make sure all rows are gone in case we reuse a view.
6272 for (int rowId : rowIds) {
6273 contentView.setViewVisibility(rowId, View.GONE);
6274 }
6275
Daniel Sandler879c5e02012-04-17 16:46:51 -04006276 int i=0;
Selim Cinek07c80172016-04-21 16:40:47 -07006277 int topPadding = mBuilder.mContext.getResources().getDimensionPixelSize(
6278 R.dimen.notification_inbox_item_top_padding);
Selim Cinek247fa012016-02-18 09:50:48 -08006279 boolean first = true;
Selim Cinek07c80172016-04-21 16:40:47 -07006280 int onlyViewId = 0;
6281 int maxRows = rowIds.length;
6282 if (mBuilder.mActions.size() > 0) {
6283 maxRows--;
6284 }
6285 while (i < mTexts.size() && i < maxRows) {
Daniel Sandler879c5e02012-04-17 16:46:51 -04006286 CharSequence str = mTexts.get(i);
Selim Cinek07c80172016-04-21 16:40:47 -07006287 if (!TextUtils.isEmpty(str)) {
Daniel Sandler879c5e02012-04-17 16:46:51 -04006288 contentView.setViewVisibility(rowIds[i], View.VISIBLE);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01006289 contentView.setTextViewText(rowIds[i], mBuilder.processLegacyText(str));
Selim Cinek7b9605b2017-01-19 17:36:00 -08006290 mBuilder.setTextViewColorSecondary(contentView, rowIds[i]);
Selim Cinek07c80172016-04-21 16:40:47 -07006291 contentView.setViewPadding(rowIds[i], 0, topPadding, 0, 0);
Selim Cinek247fa012016-02-18 09:50:48 -08006292 handleInboxImageMargin(contentView, rowIds[i], first);
Selim Cinek07c80172016-04-21 16:40:47 -07006293 if (first) {
6294 onlyViewId = rowIds[i];
6295 } else {
6296 onlyViewId = 0;
6297 }
Selim Cinek247fa012016-02-18 09:50:48 -08006298 first = false;
Daniel Sandler879c5e02012-04-17 16:46:51 -04006299 }
6300 i++;
6301 }
Selim Cinek07c80172016-04-21 16:40:47 -07006302 if (onlyViewId != 0) {
6303 // We only have 1 entry, lets make it look like the normal Text of a Bigtext
6304 topPadding = mBuilder.mContext.getResources().getDimensionPixelSize(
6305 R.dimen.notification_text_margin_top);
6306 contentView.setViewPadding(onlyViewId, 0, topPadding, 0, 0);
6307 }
Selim Cinek1e0bf612015-11-20 15:57:26 -08006308
Daniel Sandler879c5e02012-04-17 16:46:51 -04006309 return contentView;
6310 }
Selim Cinek1e0bf612015-11-20 15:57:26 -08006311
Selim Cinek247fa012016-02-18 09:50:48 -08006312 private void handleInboxImageMargin(RemoteViews contentView, int id, boolean first) {
Selim Cinek1e0bf612015-11-20 15:57:26 -08006313 int endMargin = 0;
Selim Cinek247fa012016-02-18 09:50:48 -08006314 if (first) {
6315 final int max = mBuilder.mN.extras.getInt(EXTRA_PROGRESS_MAX, 0);
6316 final boolean ind = mBuilder.mN.extras.getBoolean(EXTRA_PROGRESS_INDETERMINATE);
6317 boolean hasProgress = max != 0 || ind;
Selim Cinek279fa862016-06-14 10:57:25 -07006318 if (mBuilder.mN.hasLargeIcon() && !hasProgress) {
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006319 endMargin = R.dimen.notification_content_picture_margin;
Selim Cinek247fa012016-02-18 09:50:48 -08006320 }
Selim Cinek1e0bf612015-11-20 15:57:26 -08006321 }
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006322 contentView.setViewLayoutMarginEndDimen(id, endMargin);
Selim Cinek1e0bf612015-11-20 15:57:26 -08006323 }
Daniel Sandler879c5e02012-04-17 16:46:51 -04006324 }
Dan Sandler842dd772014-05-15 09:36:47 -04006325
6326 /**
6327 * Notification style for media playback notifications.
6328 *
6329 * In the expanded form, {@link Notification#bigContentView}, up to 5
6330 * {@link Notification.Action}s specified with
Dan Sandler86647982015-05-13 23:41:13 -04006331 * {@link Notification.Builder#addAction(Action) addAction} will be
Dan Sandler842dd772014-05-15 09:36:47 -04006332 * shown as icon-only pushbuttons, suitable for transport controls. The Bitmap given to
6333 * {@link Notification.Builder#setLargeIcon(android.graphics.Bitmap) setLargeIcon()} will be
6334 * treated as album artwork.
Selim Cinek99104832017-01-25 14:47:33 -08006335 * <p>
Dan Sandler842dd772014-05-15 09:36:47 -04006336 * Unlike the other styles provided here, MediaStyle can also modify the standard-size
6337 * {@link Notification#contentView}; by providing action indices to
Christoph Studerfde6f4d2014-12-12 13:23:26 +01006338 * {@link #setShowActionsInCompactView(int...)} you can promote up to 3 actions to be displayed
Dan Sandler842dd772014-05-15 09:36:47 -04006339 * in the standard view alongside the usual content.
Selim Cinek99104832017-01-25 14:47:33 -08006340 * <p>
Bryan Mawhinney6be8de32014-07-18 10:35:12 +01006341 * Notifications created with MediaStyle will have their category set to
6342 * {@link Notification#CATEGORY_TRANSPORT CATEGORY_TRANSPORT} unless you set a different
6343 * category using {@link Notification.Builder#setCategory(String) setCategory()}.
Selim Cinek99104832017-01-25 14:47:33 -08006344 * <p>
Jeff Browndba34ba2014-06-24 20:46:03 -07006345 * Finally, if you attach a {@link android.media.session.MediaSession.Token} using
6346 * {@link android.app.Notification.MediaStyle#setMediaSession(MediaSession.Token)},
Dan Sandler842dd772014-05-15 09:36:47 -04006347 * the System UI can identify this as a notification representing an active media session
6348 * and respond accordingly (by showing album artwork in the lockscreen, for example).
6349 *
Selim Cinek99104832017-01-25 14:47:33 -08006350 * <p>
6351 * Starting at {@link android.os.Build.VERSION_CODES#O Android O} any notification that has a
6352 * media session attached with {@link #setMediaSession(MediaSession.Token)} will be colorized.
6353 * You can opt-out of this behavior by using {@link Notification.Builder#setColorized(boolean)}.
6354 * <p>
6355 *
Dan Sandler842dd772014-05-15 09:36:47 -04006356 * To use this style with your Notification, feed it to
6357 * {@link Notification.Builder#setStyle(android.app.Notification.Style)} like so:
6358 * <pre class="prettyprint">
6359 * Notification noti = new Notification.Builder()
6360 * .setSmallIcon(R.drawable.ic_stat_player)
Christoph Studere935fe92014-11-24 14:18:06 +01006361 * .setContentTitle(&quot;Track title&quot;)
6362 * .setContentText(&quot;Artist - Album&quot;)
6363 * .setLargeIcon(albumArtBitmap))
Bryan Mawhinney6be8de32014-07-18 10:35:12 +01006364 * .setStyle(<b>new Notification.MediaStyle()</b>
6365 * .setMediaSession(mySession))
Dan Sandler842dd772014-05-15 09:36:47 -04006366 * .build();
6367 * </pre>
6368 *
6369 * @see Notification#bigContentView
Selim Cinek99104832017-01-25 14:47:33 -08006370 * @see Notification.Builder#setColorized(boolean)
Dan Sandler842dd772014-05-15 09:36:47 -04006371 */
6372 public static class MediaStyle extends Style {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006373 static final int MAX_MEDIA_BUTTONS_IN_COMPACT = 3;
Dan Sandler842dd772014-05-15 09:36:47 -04006374 static final int MAX_MEDIA_BUTTONS = 5;
6375
6376 private int[] mActionsToShowInCompact = null;
Jeff Browndba34ba2014-06-24 20:46:03 -07006377 private MediaSession.Token mToken;
Dan Sandler842dd772014-05-15 09:36:47 -04006378
6379 public MediaStyle() {
6380 }
6381
Adrian Roosf5faf9d2016-05-23 13:56:15 -07006382 /**
6383 * @deprecated use {@code MediaStyle()}.
6384 */
6385 @Deprecated
Dan Sandler842dd772014-05-15 09:36:47 -04006386 public MediaStyle(Builder builder) {
6387 setBuilder(builder);
6388 }
6389
6390 /**
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006391 * 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 -04006392 * notification view.
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006393 *
6394 * @param actions the indices of the actions to show in the compact notification view
Dan Sandler842dd772014-05-15 09:36:47 -04006395 */
6396 public MediaStyle setShowActionsInCompactView(int...actions) {
6397 mActionsToShowInCompact = actions;
6398 return this;
6399 }
6400
6401 /**
Jeff Browndba34ba2014-06-24 20:46:03 -07006402 * Attach a {@link android.media.session.MediaSession.Token} to this Notification
6403 * to provide additional playback information and control to the SystemUI.
Dan Sandler842dd772014-05-15 09:36:47 -04006404 */
Jeff Browndba34ba2014-06-24 20:46:03 -07006405 public MediaStyle setMediaSession(MediaSession.Token token) {
Dan Sandler842dd772014-05-15 09:36:47 -04006406 mToken = token;
6407 return this;
6408 }
6409
Christoph Studer4600f9b2014-07-22 22:44:43 +02006410 /**
6411 * @hide
6412 */
Dan Sandler842dd772014-05-15 09:36:47 -04006413 @Override
6414 public Notification buildStyled(Notification wip) {
Christoph Studer4600f9b2014-07-22 22:44:43 +02006415 super.buildStyled(wip);
Bryan Mawhinney6be8de32014-07-18 10:35:12 +01006416 if (wip.category == null) {
6417 wip.category = Notification.CATEGORY_TRANSPORT;
6418 }
Dan Sandler842dd772014-05-15 09:36:47 -04006419 return wip;
6420 }
6421
Christoph Studer4600f9b2014-07-22 22:44:43 +02006422 /**
6423 * @hide
6424 */
6425 @Override
Selim Cinek7d1009b2017-01-25 15:28:28 -08006426 public RemoteViews makeContentView(boolean increasedHeight) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04006427 return makeMediaContentView();
Christoph Studer4600f9b2014-07-22 22:44:43 +02006428 }
6429
6430 /**
6431 * @hide
6432 */
6433 @Override
Julia Reynoldsd9228f12015-10-20 10:37:27 -04006434 public RemoteViews makeBigContentView() {
6435 return makeMediaBigContentView();
Christoph Studer4600f9b2014-07-22 22:44:43 +02006436 }
6437
Selim Cinekcc10bfb2016-02-10 16:24:21 -08006438 /**
6439 * @hide
6440 */
6441 @Override
Selim Cinek87ed69b2017-02-09 15:59:43 -08006442 public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
Selim Cinekcc10bfb2016-02-10 16:24:21 -08006443 RemoteViews expanded = makeMediaBigContentView();
6444 return expanded != null ? expanded : makeMediaContentView();
6445 }
6446
Dan Sandler842dd772014-05-15 09:36:47 -04006447 /** @hide */
6448 @Override
6449 public void addExtras(Bundle extras) {
6450 super.addExtras(extras);
6451
6452 if (mToken != null) {
6453 extras.putParcelable(EXTRA_MEDIA_SESSION, mToken);
6454 }
Bryan Mawhinneye191f902014-07-22 12:50:09 +01006455 if (mActionsToShowInCompact != null) {
6456 extras.putIntArray(EXTRA_COMPACT_ACTIONS, mActionsToShowInCompact);
6457 }
Dan Sandler842dd772014-05-15 09:36:47 -04006458 }
6459
Christoph Studer4600f9b2014-07-22 22:44:43 +02006460 /**
6461 * @hide
6462 */
6463 @Override
6464 protected void restoreFromExtras(Bundle extras) {
6465 super.restoreFromExtras(extras);
6466
6467 if (extras.containsKey(EXTRA_MEDIA_SESSION)) {
6468 mToken = extras.getParcelable(EXTRA_MEDIA_SESSION);
6469 }
6470 if (extras.containsKey(EXTRA_COMPACT_ACTIONS)) {
6471 mActionsToShowInCompact = extras.getIntArray(EXTRA_COMPACT_ACTIONS);
6472 }
6473 }
6474
Selim Cinek5bf069a2015-11-10 19:14:27 -05006475 private RemoteViews generateMediaActionButton(Action action, int color) {
Dan Sandler842dd772014-05-15 09:36:47 -04006476 final boolean tombstone = (action.actionIntent == null);
Selim Cinekf33b1112015-07-15 17:45:11 -07006477 RemoteViews button = new BuilderRemoteViews(mBuilder.mContext.getApplicationInfo(),
Alan Viverette3cb07a462014-06-06 14:19:53 -07006478 R.layout.notification_material_media_action);
Dan Sandler68079d52015-07-22 10:45:30 -04006479 button.setImageViewIcon(R.id.action0, action.getIcon());
Selim Cinek5bf069a2015-11-10 19:14:27 -05006480 button.setDrawableParameters(R.id.action0, false, -1, color, PorterDuff.Mode.SRC_ATOP,
6481 -1);
Dan Sandler842dd772014-05-15 09:36:47 -04006482 if (!tombstone) {
6483 button.setOnClickPendingIntent(R.id.action0, action.actionIntent);
6484 }
6485 button.setContentDescription(R.id.action0, action.title);
6486 return button;
6487 }
6488
6489 private RemoteViews makeMediaContentView() {
6490 RemoteViews view = mBuilder.applyStandardTemplate(
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006491 R.layout.notification_template_material_media, false /* hasProgress */);
Dan Sandler842dd772014-05-15 09:36:47 -04006492
6493 final int numActions = mBuilder.mActions.size();
6494 final int N = mActionsToShowInCompact == null
6495 ? 0
6496 : Math.min(mActionsToShowInCompact.length, MAX_MEDIA_BUTTONS_IN_COMPACT);
6497 if (N > 0) {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006498 view.removeAllViews(com.android.internal.R.id.media_actions);
Dan Sandler842dd772014-05-15 09:36:47 -04006499 for (int i = 0; i < N; i++) {
6500 if (i >= numActions) {
6501 throw new IllegalArgumentException(String.format(
6502 "setShowActionsInCompactView: action %d out of bounds (max %d)",
6503 i, numActions - 1));
6504 }
6505
6506 final Action action = mBuilder.mActions.get(mActionsToShowInCompact[i]);
Selim Cinek5bf069a2015-11-10 19:14:27 -05006507 final RemoteViews button = generateMediaActionButton(action,
Selim Cinek99104832017-01-25 14:47:33 -08006508 getPrimaryHighlightColor());
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006509 view.addView(com.android.internal.R.id.media_actions, button);
Dan Sandler842dd772014-05-15 09:36:47 -04006510 }
6511 }
Selim Cinekfdc738f2016-01-27 20:04:27 -08006512 handleImage(view);
6513 // handle the content margin
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006514 int endMargin = R.dimen.notification_content_margin_end;
Selim Cinek279fa862016-06-14 10:57:25 -07006515 if (mBuilder.mN.hasLargeIcon()) {
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006516 endMargin = R.dimen.notification_content_plus_picture_margin_end;
Selim Cinekfdc738f2016-01-27 20:04:27 -08006517 }
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006518 view.setViewLayoutMarginEndDimen(R.id.notification_main_column, endMargin);
Dan Sandler842dd772014-05-15 09:36:47 -04006519 return view;
6520 }
6521
Selim Cinek99104832017-01-25 14:47:33 -08006522 private int getPrimaryHighlightColor() {
6523 return mBuilder.getPrimaryHighlightColor();
6524 }
6525
Dan Sandler842dd772014-05-15 09:36:47 -04006526 private RemoteViews makeMediaBigContentView() {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006527 final int actionCount = Math.min(mBuilder.mActions.size(), MAX_MEDIA_BUTTONS);
Selim Cinekcc10bfb2016-02-10 16:24:21 -08006528 // Dont add an expanded view if there is no more content to be revealed
6529 int actionsInCompact = mActionsToShowInCompact == null
6530 ? 0
6531 : Math.min(mActionsToShowInCompact.length, MAX_MEDIA_BUTTONS_IN_COMPACT);
Selim Cinek279fa862016-06-14 10:57:25 -07006532 if (!mBuilder.mN.hasLargeIcon() && actionCount <= actionsInCompact) {
Selim Cinekcc10bfb2016-02-10 16:24:21 -08006533 return null;
6534 }
Selim Cinek5bf069a2015-11-10 19:14:27 -05006535 RemoteViews big = mBuilder.applyStandardTemplate(
6536 R.layout.notification_template_material_big_media,
6537 false);
Dan Sandler842dd772014-05-15 09:36:47 -04006538
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006539 if (actionCount > 0) {
6540 big.removeAllViews(com.android.internal.R.id.media_actions);
6541 for (int i = 0; i < actionCount; i++) {
Selim Cinek5bf069a2015-11-10 19:14:27 -05006542 final RemoteViews button = generateMediaActionButton(mBuilder.mActions.get(i),
Selim Cinek99104832017-01-25 14:47:33 -08006543 getPrimaryHighlightColor());
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006544 big.addView(com.android.internal.R.id.media_actions, button);
Dan Sandler842dd772014-05-15 09:36:47 -04006545 }
6546 }
Selim Cinek5bf069a2015-11-10 19:14:27 -05006547 handleImage(big);
Dan Sandler842dd772014-05-15 09:36:47 -04006548 return big;
6549 }
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006550
Selim Cinek5bf069a2015-11-10 19:14:27 -05006551 private void handleImage(RemoteViews contentView) {
Selim Cinek279fa862016-06-14 10:57:25 -07006552 if (mBuilder.mN.hasLargeIcon()) {
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006553 contentView.setViewLayoutMarginEndDimen(R.id.line1, 0);
6554 contentView.setViewLayoutMarginEndDimen(R.id.text, 0);
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006555 }
6556 }
6557
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006558 /**
6559 * @hide
6560 */
6561 @Override
6562 protected boolean hasProgress() {
6563 return false;
6564 }
Dan Sandler842dd772014-05-15 09:36:47 -04006565 }
Griff Hazen61a9e862014-05-22 16:05:19 -07006566
Selim Cinek593610c2016-02-16 18:42:57 -08006567 /**
6568 * Notification style for custom views that are decorated by the system
6569 *
6570 * <p>Instead of providing a notification that is completely custom, a developer can set this
6571 * style and still obtain system decorations like the notification header with the expand
6572 * affordance and actions.
6573 *
6574 * <p>Use {@link android.app.Notification.Builder#setCustomContentView(RemoteViews)},
6575 * {@link android.app.Notification.Builder#setCustomBigContentView(RemoteViews)} and
6576 * {@link android.app.Notification.Builder#setCustomHeadsUpContentView(RemoteViews)} to set the
6577 * corresponding custom views to display.
6578 *
6579 * To use this style with your Notification, feed it to
6580 * {@link Notification.Builder#setStyle(android.app.Notification.Style)} like so:
6581 * <pre class="prettyprint">
6582 * Notification noti = new Notification.Builder()
6583 * .setSmallIcon(R.drawable.ic_stat_player)
6584 * .setLargeIcon(albumArtBitmap))
6585 * .setCustomContentView(contentView);
6586 * .setStyle(<b>new Notification.DecoratedCustomViewStyle()</b>)
6587 * .build();
6588 * </pre>
6589 */
6590 public static class DecoratedCustomViewStyle extends Style {
6591
6592 public DecoratedCustomViewStyle() {
6593 }
6594
Selim Cinek593610c2016-02-16 18:42:57 -08006595 /**
6596 * @hide
6597 */
6598 public boolean displayCustomViewInline() {
6599 return true;
6600 }
6601
6602 /**
6603 * @hide
6604 */
6605 @Override
Selim Cinek7d1009b2017-01-25 15:28:28 -08006606 public RemoteViews makeContentView(boolean increasedHeight) {
Selim Cinek593610c2016-02-16 18:42:57 -08006607 return makeStandardTemplateWithCustomContent(mBuilder.mN.contentView);
6608 }
6609
6610 /**
6611 * @hide
6612 */
6613 @Override
6614 public RemoteViews makeBigContentView() {
6615 return makeDecoratedBigContentView();
6616 }
6617
6618 /**
6619 * @hide
6620 */
6621 @Override
Selim Cinek87ed69b2017-02-09 15:59:43 -08006622 public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
Selim Cinek593610c2016-02-16 18:42:57 -08006623 return makeDecoratedHeadsUpContentView();
6624 }
6625
Selim Cinek593610c2016-02-16 18:42:57 -08006626 private RemoteViews makeDecoratedHeadsUpContentView() {
6627 RemoteViews headsUpContentView = mBuilder.mN.headsUpContentView == null
6628 ? mBuilder.mN.contentView
6629 : mBuilder.mN.headsUpContentView;
6630 if (mBuilder.mActions.size() == 0) {
6631 return makeStandardTemplateWithCustomContent(headsUpContentView);
6632 }
6633 RemoteViews remoteViews = mBuilder.applyStandardTemplateWithActions(
6634 mBuilder.getBigBaseLayoutResource());
Selim Cinek247fa012016-02-18 09:50:48 -08006635 buildIntoRemoteViewContent(remoteViews, headsUpContentView);
Selim Cinek593610c2016-02-16 18:42:57 -08006636 return remoteViews;
6637 }
6638
Selim Cinek593610c2016-02-16 18:42:57 -08006639 private RemoteViews makeStandardTemplateWithCustomContent(RemoteViews customContent) {
6640 RemoteViews remoteViews = mBuilder.applyStandardTemplate(
6641 mBuilder.getBaseLayoutResource());
Selim Cinek247fa012016-02-18 09:50:48 -08006642 buildIntoRemoteViewContent(remoteViews, customContent);
Selim Cinek593610c2016-02-16 18:42:57 -08006643 return remoteViews;
6644 }
6645
Selim Cinek593610c2016-02-16 18:42:57 -08006646 private RemoteViews makeDecoratedBigContentView() {
6647 RemoteViews bigContentView = mBuilder.mN.bigContentView == null
6648 ? mBuilder.mN.contentView
6649 : mBuilder.mN.bigContentView;
6650 if (mBuilder.mActions.size() == 0) {
6651 return makeStandardTemplateWithCustomContent(bigContentView);
6652 }
6653 RemoteViews remoteViews = mBuilder.applyStandardTemplateWithActions(
6654 mBuilder.getBigBaseLayoutResource());
Selim Cinek247fa012016-02-18 09:50:48 -08006655 buildIntoRemoteViewContent(remoteViews, bigContentView);
Selim Cinek593610c2016-02-16 18:42:57 -08006656 return remoteViews;
6657 }
Selim Cinek247fa012016-02-18 09:50:48 -08006658
6659 private void buildIntoRemoteViewContent(RemoteViews remoteViews,
6660 RemoteViews customContent) {
Adrian Roos5081c0d2016-02-26 16:04:19 -08006661 if (customContent != null) {
Selim Cinekf91017e2016-03-14 12:25:09 -07006662 // Need to clone customContent before adding, because otherwise it can no longer be
6663 // parceled independently of remoteViews.
Adrian Roos5081c0d2016-02-26 16:04:19 -08006664 customContent = customContent.clone();
Selim Cinekf91017e2016-03-14 12:25:09 -07006665 remoteViews.removeAllViews(R.id.notification_main_column);
6666 remoteViews.addView(R.id.notification_main_column, customContent);
Adrian Roos5081c0d2016-02-26 16:04:19 -08006667 }
Selim Cinek247fa012016-02-18 09:50:48 -08006668 // also update the end margin if there is an image
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006669 int endMargin = R.dimen.notification_content_margin_end;
Selim Cinek279fa862016-06-14 10:57:25 -07006670 if (mBuilder.mN.hasLargeIcon()) {
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006671 endMargin = R.dimen.notification_content_plus_picture_margin_end;
Selim Cinek247fa012016-02-18 09:50:48 -08006672 }
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006673 remoteViews.setViewLayoutMarginEndDimen(R.id.notification_main_column, endMargin);
Selim Cinek247fa012016-02-18 09:50:48 -08006674 }
Selim Cinek593610c2016-02-16 18:42:57 -08006675 }
6676
Selim Cinek03eb3b72016-02-18 10:39:45 -08006677 /**
6678 * Notification style for media custom views that are decorated by the system
6679 *
6680 * <p>Instead of providing a media notification that is completely custom, a developer can set
6681 * this style and still obtain system decorations like the notification header with the expand
6682 * affordance and actions.
6683 *
6684 * <p>Use {@link android.app.Notification.Builder#setCustomContentView(RemoteViews)},
6685 * {@link android.app.Notification.Builder#setCustomBigContentView(RemoteViews)} and
6686 * {@link android.app.Notification.Builder#setCustomHeadsUpContentView(RemoteViews)} to set the
6687 * corresponding custom views to display.
Selim Cinek99104832017-01-25 14:47:33 -08006688 * <p>
6689 * Contrary to {@link MediaStyle} a developer has to opt-in to the colorizing of the
6690 * notification by using {@link Notification.Builder#setColorized(boolean)}.
6691 * <p>
Selim Cinek03eb3b72016-02-18 10:39:45 -08006692 * To use this style with your Notification, feed it to
6693 * {@link Notification.Builder#setStyle(android.app.Notification.Style)} like so:
6694 * <pre class="prettyprint">
6695 * Notification noti = new Notification.Builder()
6696 * .setSmallIcon(R.drawable.ic_stat_player)
6697 * .setLargeIcon(albumArtBitmap))
6698 * .setCustomContentView(contentView);
6699 * .setStyle(<b>new Notification.DecoratedMediaCustomViewStyle()</b>
6700 * .setMediaSession(mySession))
6701 * .build();
6702 * </pre>
6703 *
6704 * @see android.app.Notification.DecoratedCustomViewStyle
6705 * @see android.app.Notification.MediaStyle
6706 */
6707 public static class DecoratedMediaCustomViewStyle extends MediaStyle {
6708
6709 public DecoratedMediaCustomViewStyle() {
6710 }
6711
Selim Cinek03eb3b72016-02-18 10:39:45 -08006712 /**
6713 * @hide
6714 */
6715 public boolean displayCustomViewInline() {
6716 return true;
6717 }
6718
6719 /**
6720 * @hide
6721 */
6722 @Override
Selim Cinek7d1009b2017-01-25 15:28:28 -08006723 public RemoteViews makeContentView(boolean increasedHeight) {
6724 RemoteViews remoteViews = super.makeContentView(false /* increasedHeight */);
Selim Cinek03eb3b72016-02-18 10:39:45 -08006725 return buildIntoRemoteView(remoteViews, R.id.notification_content_container,
6726 mBuilder.mN.contentView);
6727 }
6728
6729 /**
6730 * @hide
6731 */
6732 @Override
6733 public RemoteViews makeBigContentView() {
6734 RemoteViews customRemoteView = mBuilder.mN.bigContentView != null
6735 ? mBuilder.mN.bigContentView
6736 : mBuilder.mN.contentView;
6737 return makeBigContentViewWithCustomContent(customRemoteView);
6738 }
6739
6740 private RemoteViews makeBigContentViewWithCustomContent(RemoteViews customRemoteView) {
6741 RemoteViews remoteViews = super.makeBigContentView();
6742 if (remoteViews != null) {
6743 return buildIntoRemoteView(remoteViews, R.id.notification_main_column,
6744 customRemoteView);
6745 } else if (customRemoteView != mBuilder.mN.contentView){
Selim Cinek7d1009b2017-01-25 15:28:28 -08006746 remoteViews = super.makeContentView(false /* increasedHeight */);
Selim Cinek03eb3b72016-02-18 10:39:45 -08006747 return buildIntoRemoteView(remoteViews, R.id.notification_content_container,
6748 customRemoteView);
6749 } else {
6750 return null;
6751 }
6752 }
6753
6754 /**
6755 * @hide
6756 */
6757 @Override
Selim Cinek87ed69b2017-02-09 15:59:43 -08006758 public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
Selim Cinek03eb3b72016-02-18 10:39:45 -08006759 RemoteViews customRemoteView = mBuilder.mN.headsUpContentView != null
6760 ? mBuilder.mN.headsUpContentView
6761 : mBuilder.mN.contentView;
6762 return makeBigContentViewWithCustomContent(customRemoteView);
6763 }
6764
6765 private RemoteViews buildIntoRemoteView(RemoteViews remoteViews, int id,
6766 RemoteViews customContent) {
Adrian Roos5081c0d2016-02-26 16:04:19 -08006767 if (customContent != null) {
Selim Cinekf91017e2016-03-14 12:25:09 -07006768 // Need to clone customContent before adding, because otherwise it can no longer be
6769 // parceled independently of remoteViews.
Adrian Roos5081c0d2016-02-26 16:04:19 -08006770 customContent = customContent.clone();
Selim Cinekf91017e2016-03-14 12:25:09 -07006771 remoteViews.removeAllViews(id);
6772 remoteViews.addView(id, customContent);
Adrian Roos5081c0d2016-02-26 16:04:19 -08006773 }
Selim Cinek03eb3b72016-02-18 10:39:45 -08006774 return remoteViews;
6775 }
6776 }
6777
Christoph Studer4600f9b2014-07-22 22:44:43 +02006778 // When adding a new Style subclass here, don't forget to update
6779 // Builder.getNotificationStyleClass.
6780
Griff Hazen61a9e862014-05-22 16:05:19 -07006781 /**
6782 * Extender interface for use with {@link Builder#extend}. Extenders may be used to add
6783 * metadata or change options on a notification builder.
6784 */
6785 public interface Extender {
6786 /**
6787 * Apply this extender to a notification builder.
6788 * @param builder the builder to be modified.
6789 * @return the build object for chaining.
6790 */
6791 public Builder extend(Builder builder);
6792 }
6793
6794 /**
6795 * Helper class to add wearable extensions to notifications.
6796 * <p class="note"> See
6797 * <a href="{@docRoot}wear/notifications/creating.html">Creating Notifications
6798 * for Android Wear</a> for more information on how to use this class.
6799 * <p>
6800 * To create a notification with wearable extensions:
6801 * <ol>
6802 * <li>Create a {@link android.app.Notification.Builder}, setting any desired
6803 * properties.
6804 * <li>Create a {@link android.app.Notification.WearableExtender}.
6805 * <li>Set wearable-specific properties using the
6806 * {@code add} and {@code set} methods of {@link android.app.Notification.WearableExtender}.
6807 * <li>Call {@link android.app.Notification.Builder#extend} to apply the extensions to a
6808 * notification.
6809 * <li>Post the notification to the notification system with the
6810 * {@code NotificationManager.notify(...)} methods.
6811 * </ol>
6812 *
6813 * <pre class="prettyprint">
6814 * Notification notif = new Notification.Builder(mContext)
6815 * .setContentTitle(&quot;New mail from &quot; + sender.toString())
6816 * .setContentText(subject)
6817 * .setSmallIcon(R.drawable.new_mail)
6818 * .extend(new Notification.WearableExtender()
6819 * .setContentIcon(R.drawable.new_mail))
6820 * .build();
6821 * NotificationManager notificationManger =
6822 * (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
6823 * notificationManger.notify(0, notif);</pre>
6824 *
6825 * <p>Wearable extensions can be accessed on an existing notification by using the
6826 * {@code WearableExtender(Notification)} constructor,
6827 * and then using the {@code get} methods to access values.
6828 *
6829 * <pre class="prettyprint">
6830 * Notification.WearableExtender wearableExtender = new Notification.WearableExtender(
6831 * notification);
Griff Hazen14f57992014-05-26 09:07:14 -07006832 * List&lt;Notification&gt; pages = wearableExtender.getPages();</pre>
Griff Hazen61a9e862014-05-22 16:05:19 -07006833 */
6834 public static final class WearableExtender implements Extender {
6835 /**
6836 * Sentinel value for an action index that is unset.
6837 */
6838 public static final int UNSET_ACTION_INDEX = -1;
6839
6840 /**
6841 * Size value for use with {@link #setCustomSizePreset} to show this notification with
6842 * default sizing.
6843 * <p>For custom display notifications created using {@link #setDisplayIntent},
Paul Soulosaa4f4bf2015-08-04 11:59:45 -07006844 * the default is {@link #SIZE_MEDIUM}. All other notifications size automatically based
Griff Hazen61a9e862014-05-22 16:05:19 -07006845 * on their content.
6846 */
6847 public static final int SIZE_DEFAULT = 0;
6848
6849 /**
6850 * Size value for use with {@link #setCustomSizePreset} to show this notification
6851 * with an extra small size.
6852 * <p>This value is only applicable for custom display notifications created using
6853 * {@link #setDisplayIntent}.
6854 */
6855 public static final int SIZE_XSMALL = 1;
6856
6857 /**
6858 * Size value for use with {@link #setCustomSizePreset} to show this notification
6859 * with a small size.
6860 * <p>This value is only applicable for custom display notifications created using
6861 * {@link #setDisplayIntent}.
6862 */
6863 public static final int SIZE_SMALL = 2;
6864
6865 /**
6866 * Size value for use with {@link #setCustomSizePreset} to show this notification
6867 * with a medium size.
6868 * <p>This value is only applicable for custom display notifications created using
6869 * {@link #setDisplayIntent}.
6870 */
6871 public static final int SIZE_MEDIUM = 3;
6872
6873 /**
6874 * Size value for use with {@link #setCustomSizePreset} to show this notification
6875 * with a large size.
6876 * <p>This value is only applicable for custom display notifications created using
6877 * {@link #setDisplayIntent}.
6878 */
6879 public static final int SIZE_LARGE = 4;
6880
Griff Hazend5f11f92014-05-27 15:40:09 -07006881 /**
6882 * Size value for use with {@link #setCustomSizePreset} to show this notification
6883 * full screen.
6884 * <p>This value is only applicable for custom display notifications created using
6885 * {@link #setDisplayIntent}.
6886 */
6887 public static final int SIZE_FULL_SCREEN = 5;
6888
Griff Hazen5f2edfc2014-09-29 16:28:44 -07006889 /**
6890 * Sentinel value for use with {@link #setHintScreenTimeout} to keep the screen on for a
6891 * short amount of time when this notification is displayed on the screen. This
6892 * is the default value.
6893 */
6894 public static final int SCREEN_TIMEOUT_SHORT = 0;
6895
6896 /**
6897 * Sentinel value for use with {@link #setHintScreenTimeout} to keep the screen on
6898 * for a longer amount of time when this notification is displayed on the screen.
6899 */
6900 public static final int SCREEN_TIMEOUT_LONG = -1;
6901
Griff Hazen61a9e862014-05-22 16:05:19 -07006902 /** Notification extra which contains wearable extensions */
6903 private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS";
6904
Pete Gastaf6781d2014-10-07 15:17:05 -04006905 // Keys within EXTRA_WEARABLE_EXTENSIONS for wearable options.
Griff Hazen61a9e862014-05-22 16:05:19 -07006906 private static final String KEY_ACTIONS = "actions";
6907 private static final String KEY_FLAGS = "flags";
6908 private static final String KEY_DISPLAY_INTENT = "displayIntent";
6909 private static final String KEY_PAGES = "pages";
6910 private static final String KEY_BACKGROUND = "background";
6911 private static final String KEY_CONTENT_ICON = "contentIcon";
6912 private static final String KEY_CONTENT_ICON_GRAVITY = "contentIconGravity";
6913 private static final String KEY_CONTENT_ACTION_INDEX = "contentActionIndex";
6914 private static final String KEY_CUSTOM_SIZE_PRESET = "customSizePreset";
6915 private static final String KEY_CUSTOM_CONTENT_HEIGHT = "customContentHeight";
6916 private static final String KEY_GRAVITY = "gravity";
Griff Hazen5f2edfc2014-09-29 16:28:44 -07006917 private static final String KEY_HINT_SCREEN_TIMEOUT = "hintScreenTimeout";
Nadia Benbernou948627e2016-04-14 14:41:08 -04006918 private static final String KEY_DISMISSAL_ID = "dismissalId";
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04006919 private static final String KEY_BRIDGE_TAG = "bridgeTag";
Griff Hazen61a9e862014-05-22 16:05:19 -07006920
6921 // Flags bitwise-ored to mFlags
6922 private static final int FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE = 0x1;
6923 private static final int FLAG_HINT_HIDE_ICON = 1 << 1;
6924 private static final int FLAG_HINT_SHOW_BACKGROUND_ONLY = 1 << 2;
6925 private static final int FLAG_START_SCROLL_BOTTOM = 1 << 3;
Griff Hazen5f2edfc2014-09-29 16:28:44 -07006926 private static final int FLAG_HINT_AVOID_BACKGROUND_CLIPPING = 1 << 4;
Alex Hills4bcb06b2016-04-05 14:26:25 -04006927 private static final int FLAG_BIG_PICTURE_AMBIENT = 1 << 5;
Alex Hills9ab3a232016-04-05 14:54:56 -04006928 private static final int FLAG_HINT_CONTENT_INTENT_LAUNCHES_ACTIVITY = 1 << 6;
Griff Hazen61a9e862014-05-22 16:05:19 -07006929
6930 // Default value for flags integer
6931 private static final int DEFAULT_FLAGS = FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE;
6932
6933 private static final int DEFAULT_CONTENT_ICON_GRAVITY = Gravity.END;
6934 private static final int DEFAULT_GRAVITY = Gravity.BOTTOM;
6935
6936 private ArrayList<Action> mActions = new ArrayList<Action>();
6937 private int mFlags = DEFAULT_FLAGS;
6938 private PendingIntent mDisplayIntent;
6939 private ArrayList<Notification> mPages = new ArrayList<Notification>();
6940 private Bitmap mBackground;
6941 private int mContentIcon;
6942 private int mContentIconGravity = DEFAULT_CONTENT_ICON_GRAVITY;
6943 private int mContentActionIndex = UNSET_ACTION_INDEX;
6944 private int mCustomSizePreset = SIZE_DEFAULT;
6945 private int mCustomContentHeight;
6946 private int mGravity = DEFAULT_GRAVITY;
Griff Hazen5f2edfc2014-09-29 16:28:44 -07006947 private int mHintScreenTimeout;
Nadia Benbernou948627e2016-04-14 14:41:08 -04006948 private String mDismissalId;
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04006949 private String mBridgeTag;
Griff Hazen61a9e862014-05-22 16:05:19 -07006950
6951 /**
6952 * Create a {@link android.app.Notification.WearableExtender} with default
6953 * options.
6954 */
6955 public WearableExtender() {
6956 }
6957
6958 public WearableExtender(Notification notif) {
6959 Bundle wearableBundle = notif.extras.getBundle(EXTRA_WEARABLE_EXTENSIONS);
6960 if (wearableBundle != null) {
6961 List<Action> actions = wearableBundle.getParcelableArrayList(KEY_ACTIONS);
6962 if (actions != null) {
6963 mActions.addAll(actions);
6964 }
6965
6966 mFlags = wearableBundle.getInt(KEY_FLAGS, DEFAULT_FLAGS);
6967 mDisplayIntent = wearableBundle.getParcelable(KEY_DISPLAY_INTENT);
6968
6969 Notification[] pages = getNotificationArrayFromBundle(
6970 wearableBundle, KEY_PAGES);
6971 if (pages != null) {
6972 Collections.addAll(mPages, pages);
6973 }
6974
6975 mBackground = wearableBundle.getParcelable(KEY_BACKGROUND);
6976 mContentIcon = wearableBundle.getInt(KEY_CONTENT_ICON);
6977 mContentIconGravity = wearableBundle.getInt(KEY_CONTENT_ICON_GRAVITY,
6978 DEFAULT_CONTENT_ICON_GRAVITY);
6979 mContentActionIndex = wearableBundle.getInt(KEY_CONTENT_ACTION_INDEX,
6980 UNSET_ACTION_INDEX);
6981 mCustomSizePreset = wearableBundle.getInt(KEY_CUSTOM_SIZE_PRESET,
6982 SIZE_DEFAULT);
6983 mCustomContentHeight = wearableBundle.getInt(KEY_CUSTOM_CONTENT_HEIGHT);
6984 mGravity = wearableBundle.getInt(KEY_GRAVITY, DEFAULT_GRAVITY);
Griff Hazen5f2edfc2014-09-29 16:28:44 -07006985 mHintScreenTimeout = wearableBundle.getInt(KEY_HINT_SCREEN_TIMEOUT);
Nadia Benbernou948627e2016-04-14 14:41:08 -04006986 mDismissalId = wearableBundle.getString(KEY_DISMISSAL_ID);
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04006987 mBridgeTag = wearableBundle.getString(KEY_BRIDGE_TAG);
Griff Hazen61a9e862014-05-22 16:05:19 -07006988 }
6989 }
6990
6991 /**
6992 * Apply wearable extensions to a notification that is being built. This is typically
6993 * called by the {@link android.app.Notification.Builder#extend} method of
6994 * {@link android.app.Notification.Builder}.
6995 */
6996 @Override
6997 public Notification.Builder extend(Notification.Builder builder) {
6998 Bundle wearableBundle = new Bundle();
6999
7000 if (!mActions.isEmpty()) {
7001 wearableBundle.putParcelableArrayList(KEY_ACTIONS, mActions);
7002 }
7003 if (mFlags != DEFAULT_FLAGS) {
7004 wearableBundle.putInt(KEY_FLAGS, mFlags);
7005 }
7006 if (mDisplayIntent != null) {
7007 wearableBundle.putParcelable(KEY_DISPLAY_INTENT, mDisplayIntent);
7008 }
7009 if (!mPages.isEmpty()) {
7010 wearableBundle.putParcelableArray(KEY_PAGES, mPages.toArray(
7011 new Notification[mPages.size()]));
7012 }
7013 if (mBackground != null) {
7014 wearableBundle.putParcelable(KEY_BACKGROUND, mBackground);
7015 }
7016 if (mContentIcon != 0) {
7017 wearableBundle.putInt(KEY_CONTENT_ICON, mContentIcon);
7018 }
7019 if (mContentIconGravity != DEFAULT_CONTENT_ICON_GRAVITY) {
7020 wearableBundle.putInt(KEY_CONTENT_ICON_GRAVITY, mContentIconGravity);
7021 }
7022 if (mContentActionIndex != UNSET_ACTION_INDEX) {
7023 wearableBundle.putInt(KEY_CONTENT_ACTION_INDEX,
7024 mContentActionIndex);
7025 }
7026 if (mCustomSizePreset != SIZE_DEFAULT) {
7027 wearableBundle.putInt(KEY_CUSTOM_SIZE_PRESET, mCustomSizePreset);
7028 }
7029 if (mCustomContentHeight != 0) {
7030 wearableBundle.putInt(KEY_CUSTOM_CONTENT_HEIGHT, mCustomContentHeight);
7031 }
7032 if (mGravity != DEFAULT_GRAVITY) {
7033 wearableBundle.putInt(KEY_GRAVITY, mGravity);
7034 }
Griff Hazen5f2edfc2014-09-29 16:28:44 -07007035 if (mHintScreenTimeout != 0) {
7036 wearableBundle.putInt(KEY_HINT_SCREEN_TIMEOUT, mHintScreenTimeout);
7037 }
Nadia Benbernou948627e2016-04-14 14:41:08 -04007038 if (mDismissalId != null) {
7039 wearableBundle.putString(KEY_DISMISSAL_ID, mDismissalId);
7040 }
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04007041 if (mBridgeTag != null) {
7042 wearableBundle.putString(KEY_BRIDGE_TAG, mBridgeTag);
7043 }
Griff Hazen61a9e862014-05-22 16:05:19 -07007044
7045 builder.getExtras().putBundle(EXTRA_WEARABLE_EXTENSIONS, wearableBundle);
7046 return builder;
7047 }
7048
7049 @Override
7050 public WearableExtender clone() {
7051 WearableExtender that = new WearableExtender();
7052 that.mActions = new ArrayList<Action>(this.mActions);
7053 that.mFlags = this.mFlags;
7054 that.mDisplayIntent = this.mDisplayIntent;
7055 that.mPages = new ArrayList<Notification>(this.mPages);
7056 that.mBackground = this.mBackground;
7057 that.mContentIcon = this.mContentIcon;
7058 that.mContentIconGravity = this.mContentIconGravity;
7059 that.mContentActionIndex = this.mContentActionIndex;
7060 that.mCustomSizePreset = this.mCustomSizePreset;
7061 that.mCustomContentHeight = this.mCustomContentHeight;
7062 that.mGravity = this.mGravity;
Griff Hazen5f2edfc2014-09-29 16:28:44 -07007063 that.mHintScreenTimeout = this.mHintScreenTimeout;
Nadia Benbernou948627e2016-04-14 14:41:08 -04007064 that.mDismissalId = this.mDismissalId;
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04007065 that.mBridgeTag = this.mBridgeTag;
Griff Hazen61a9e862014-05-22 16:05:19 -07007066 return that;
7067 }
7068
7069 /**
7070 * Add a wearable action 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 action the action to add to this notification
7078 * @return this object for method chaining
7079 * @see android.app.Notification.Action
7080 */
7081 public WearableExtender addAction(Action action) {
7082 mActions.add(action);
7083 return this;
7084 }
7085
7086 /**
7087 * Adds wearable actions to this notification.
7088 *
7089 * <p>When wearable actions are added using this method, the set of actions that
7090 * show on a wearable device splits from devices that only show actions added
7091 * using {@link android.app.Notification.Builder#addAction}. This allows for customization
7092 * of which actions display on different devices.
7093 *
7094 * @param actions the actions to add to this notification
7095 * @return this object for method chaining
7096 * @see android.app.Notification.Action
7097 */
7098 public WearableExtender addActions(List<Action> actions) {
7099 mActions.addAll(actions);
7100 return this;
7101 }
7102
7103 /**
7104 * Clear all wearable actions present on this builder.
7105 * @return this object for method chaining.
7106 * @see #addAction
7107 */
7108 public WearableExtender clearActions() {
7109 mActions.clear();
7110 return this;
7111 }
7112
7113 /**
7114 * Get the wearable actions present on this notification.
7115 */
7116 public List<Action> getActions() {
7117 return mActions;
7118 }
7119
7120 /**
7121 * Set an intent to launch inside of an activity view when displaying
Griff Hazen14f57992014-05-26 09:07:14 -07007122 * this notification. The {@link PendingIntent} provided should be for an activity.
7123 *
7124 * <pre class="prettyprint">
7125 * Intent displayIntent = new Intent(context, MyDisplayActivity.class);
7126 * PendingIntent displayPendingIntent = PendingIntent.getActivity(context,
7127 * 0, displayIntent, PendingIntent.FLAG_UPDATE_CURRENT);
7128 * Notification notif = new Notification.Builder(context)
7129 * .extend(new Notification.WearableExtender()
7130 * .setDisplayIntent(displayPendingIntent)
7131 * .setCustomSizePreset(Notification.WearableExtender.SIZE_MEDIUM))
7132 * .build();</pre>
7133 *
7134 * <p>The activity to launch needs to allow embedding, must be exported, and
Griff Hazen831ca9d2014-06-17 00:38:38 -07007135 * should have an empty task affinity. It is also recommended to use the device
7136 * default light theme.
Griff Hazen14f57992014-05-26 09:07:14 -07007137 *
7138 * <p>Example AndroidManifest.xml entry:
7139 * <pre class="prettyprint">
7140 * &lt;activity android:name=&quot;com.example.MyDisplayActivity&quot;
7141 * android:exported=&quot;true&quot;
7142 * android:allowEmbedded=&quot;true&quot;
Griff Hazen831ca9d2014-06-17 00:38:38 -07007143 * android:taskAffinity=&quot;&quot;
7144 * android:theme=&quot;@android:style/Theme.DeviceDefault.Light&quot; /&gt;</pre>
Griff Hazen61a9e862014-05-22 16:05:19 -07007145 *
7146 * @param intent the {@link PendingIntent} for an activity
7147 * @return this object for method chaining
7148 * @see android.app.Notification.WearableExtender#getDisplayIntent
7149 */
7150 public WearableExtender setDisplayIntent(PendingIntent intent) {
7151 mDisplayIntent = intent;
7152 return this;
7153 }
7154
7155 /**
7156 * Get the intent to launch inside of an activity view when displaying this
7157 * notification. This {@code PendingIntent} should be for an activity.
7158 */
7159 public PendingIntent getDisplayIntent() {
7160 return mDisplayIntent;
7161 }
7162
7163 /**
7164 * Add an additional page of content to display with this notification. The current
7165 * notification forms the first page, and pages added using this function form
7166 * subsequent pages. This field can be used to separate a notification into multiple
7167 * sections.
7168 *
7169 * @param page the notification to add as another page
7170 * @return this object for method chaining
7171 * @see android.app.Notification.WearableExtender#getPages
7172 */
7173 public WearableExtender addPage(Notification page) {
7174 mPages.add(page);
7175 return this;
7176 }
7177
7178 /**
7179 * Add additional pages of content to display with this notification. The current
7180 * notification forms the first page, and pages added using this function form
7181 * subsequent pages. This field can be used to separate a notification into multiple
7182 * sections.
7183 *
7184 * @param pages a list of notifications
7185 * @return this object for method chaining
7186 * @see android.app.Notification.WearableExtender#getPages
7187 */
7188 public WearableExtender addPages(List<Notification> pages) {
7189 mPages.addAll(pages);
7190 return this;
7191 }
7192
7193 /**
7194 * Clear all additional pages present on this builder.
7195 * @return this object for method chaining.
7196 * @see #addPage
7197 */
7198 public WearableExtender clearPages() {
7199 mPages.clear();
7200 return this;
7201 }
7202
7203 /**
7204 * Get the array of additional pages of content for displaying this notification. The
7205 * current notification forms the first page, and elements within this array form
7206 * subsequent pages. This field can be used to separate a notification into multiple
7207 * sections.
7208 * @return the pages for this notification
7209 */
7210 public List<Notification> getPages() {
7211 return mPages;
7212 }
7213
7214 /**
7215 * Set a background image to be displayed behind the notification content.
7216 * Contrary to the {@link android.app.Notification.BigPictureStyle}, this background
7217 * will work with any notification style.
7218 *
7219 * @param background the background bitmap
7220 * @return this object for method chaining
7221 * @see android.app.Notification.WearableExtender#getBackground
7222 */
7223 public WearableExtender setBackground(Bitmap background) {
7224 mBackground = background;
7225 return this;
7226 }
7227
7228 /**
7229 * Get a background image to be displayed behind the notification content.
7230 * Contrary to the {@link android.app.Notification.BigPictureStyle}, this background
7231 * will work with any notification style.
7232 *
7233 * @return the background image
7234 * @see android.app.Notification.WearableExtender#setBackground
7235 */
7236 public Bitmap getBackground() {
7237 return mBackground;
7238 }
7239
7240 /**
7241 * Set an icon that goes with the content of this notification.
7242 */
7243 public WearableExtender setContentIcon(int icon) {
7244 mContentIcon = icon;
7245 return this;
7246 }
7247
7248 /**
7249 * Get an icon that goes with the content of this notification.
7250 */
7251 public int getContentIcon() {
7252 return mContentIcon;
7253 }
7254
7255 /**
7256 * Set the gravity that the content icon should have within the notification display.
7257 * Supported values include {@link android.view.Gravity#START} and
7258 * {@link android.view.Gravity#END}. The default value is {@link android.view.Gravity#END}.
7259 * @see #setContentIcon
7260 */
7261 public WearableExtender setContentIconGravity(int contentIconGravity) {
7262 mContentIconGravity = contentIconGravity;
7263 return this;
7264 }
7265
7266 /**
7267 * Get the gravity that the content icon should have within the notification display.
7268 * Supported values include {@link android.view.Gravity#START} and
7269 * {@link android.view.Gravity#END}. The default value is {@link android.view.Gravity#END}.
7270 * @see #getContentIcon
7271 */
7272 public int getContentIconGravity() {
7273 return mContentIconGravity;
7274 }
7275
7276 /**
7277 * Set an action from this notification's actions to be clickable with the content of
Griff Hazen14f57992014-05-26 09:07:14 -07007278 * this notification. This action will no longer display separately from the
7279 * notification's content.
7280 *
Griff Hazenca48d352014-05-28 22:37:13 -07007281 * <p>For notifications with multiple pages, child pages can also have content actions
Griff Hazen14f57992014-05-26 09:07:14 -07007282 * set, although the list of available actions comes from the main notification and not
7283 * from the child page's notification.
7284 *
7285 * @param actionIndex The index of the action to hoist onto the current notification page.
7286 * If wearable actions were added to the main notification, this index
7287 * will apply to that list, otherwise it will apply to the regular
7288 * actions list.
Griff Hazen61a9e862014-05-22 16:05:19 -07007289 */
7290 public WearableExtender setContentAction(int actionIndex) {
7291 mContentActionIndex = actionIndex;
7292 return this;
7293 }
7294
7295 /**
Griff Hazenca48d352014-05-28 22:37:13 -07007296 * Get the index of the notification action, if any, that was specified as being clickable
7297 * with the content of this notification. This action will no longer display separately
Griff Hazen14f57992014-05-26 09:07:14 -07007298 * from the notification's content.
Griff Hazen61a9e862014-05-22 16:05:19 -07007299 *
Griff Hazenca48d352014-05-28 22:37:13 -07007300 * <p>For notifications with multiple pages, child pages can also have content actions
Griff Hazen14f57992014-05-26 09:07:14 -07007301 * set, although the list of available actions comes from the main notification and not
7302 * from the child page's notification.
7303 *
7304 * <p>If wearable specific actions were added to the main notification, this index will
7305 * apply to that list, otherwise it will apply to the regular actions list.
Griff Hazenca48d352014-05-28 22:37:13 -07007306 *
7307 * @return the action index or {@link #UNSET_ACTION_INDEX} if no action was selected.
Griff Hazen61a9e862014-05-22 16:05:19 -07007308 */
7309 public int getContentAction() {
7310 return mContentActionIndex;
7311 }
7312
7313 /**
7314 * Set the gravity that this notification should have within the available viewport space.
7315 * Supported values include {@link android.view.Gravity#TOP},
7316 * {@link android.view.Gravity#CENTER_VERTICAL} and {@link android.view.Gravity#BOTTOM}.
7317 * The default value is {@link android.view.Gravity#BOTTOM}.
7318 */
7319 public WearableExtender setGravity(int gravity) {
7320 mGravity = gravity;
7321 return this;
7322 }
7323
7324 /**
7325 * Get the gravity that this notification should have within the available viewport space.
7326 * Supported values include {@link android.view.Gravity#TOP},
7327 * {@link android.view.Gravity#CENTER_VERTICAL} and {@link android.view.Gravity#BOTTOM}.
7328 * The default value is {@link android.view.Gravity#BOTTOM}.
7329 */
7330 public int getGravity() {
7331 return mGravity;
7332 }
7333
7334 /**
7335 * Set the custom size preset for the display of this notification out of the available
7336 * presets found in {@link android.app.Notification.WearableExtender}, e.g.
7337 * {@link #SIZE_LARGE}.
7338 * <p>Some custom size presets are only applicable for custom display notifications created
7339 * using {@link android.app.Notification.WearableExtender#setDisplayIntent}. Check the
7340 * documentation for the preset in question. See also
7341 * {@link #setCustomContentHeight} and {@link #getCustomSizePreset}.
7342 */
7343 public WearableExtender setCustomSizePreset(int sizePreset) {
7344 mCustomSizePreset = sizePreset;
7345 return this;
7346 }
7347
7348 /**
7349 * Get the custom size preset for the display of this notification out of the available
7350 * presets found in {@link android.app.Notification.WearableExtender}, e.g.
7351 * {@link #SIZE_LARGE}.
7352 * <p>Some custom size presets are only applicable for custom display notifications created
7353 * using {@link #setDisplayIntent}. Check the documentation for the preset in question.
7354 * See also {@link #setCustomContentHeight} and {@link #setCustomSizePreset}.
7355 */
7356 public int getCustomSizePreset() {
7357 return mCustomSizePreset;
7358 }
7359
7360 /**
7361 * Set the custom height in pixels for the display of this notification's content.
7362 * <p>This option is only available for custom display notifications created
7363 * using {@link android.app.Notification.WearableExtender#setDisplayIntent}. See also
7364 * {@link android.app.Notification.WearableExtender#setCustomSizePreset} and
7365 * {@link #getCustomContentHeight}.
7366 */
7367 public WearableExtender setCustomContentHeight(int height) {
7368 mCustomContentHeight = height;
7369 return this;
7370 }
7371
7372 /**
7373 * Get the custom height in pixels for the display of this notification's content.
7374 * <p>This option is only available for custom display notifications created
7375 * using {@link #setDisplayIntent}. See also {@link #setCustomSizePreset} and
7376 * {@link #setCustomContentHeight}.
7377 */
7378 public int getCustomContentHeight() {
7379 return mCustomContentHeight;
7380 }
7381
7382 /**
7383 * Set whether the scrolling position for the contents of this notification should start
7384 * at the bottom of the contents instead of the top when the contents are too long to
7385 * display within the screen. Default is false (start scroll at the top).
7386 */
7387 public WearableExtender setStartScrollBottom(boolean startScrollBottom) {
7388 setFlag(FLAG_START_SCROLL_BOTTOM, startScrollBottom);
7389 return this;
7390 }
7391
7392 /**
7393 * Get whether the scrolling position for the contents of this notification should start
7394 * at the bottom of the contents instead of the top when the contents are too long to
7395 * display within the screen. Default is false (start scroll at the top).
7396 */
7397 public boolean getStartScrollBottom() {
7398 return (mFlags & FLAG_START_SCROLL_BOTTOM) != 0;
7399 }
7400
7401 /**
7402 * Set whether the content intent is available when the wearable device is not connected
7403 * to a companion device. The user can still trigger this intent when the wearable device
7404 * is offline, but a visual hint will indicate that the content intent may not be available.
7405 * Defaults to true.
7406 */
7407 public WearableExtender setContentIntentAvailableOffline(
7408 boolean contentIntentAvailableOffline) {
7409 setFlag(FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE, contentIntentAvailableOffline);
7410 return this;
7411 }
7412
7413 /**
7414 * Get whether the content intent is available when the wearable device is not connected
7415 * to a companion device. The user can still trigger this intent when the wearable device
7416 * is offline, but a visual hint will indicate that the content intent may not be available.
7417 * Defaults to true.
7418 */
7419 public boolean getContentIntentAvailableOffline() {
7420 return (mFlags & FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE) != 0;
7421 }
7422
7423 /**
7424 * Set a hint that this notification's icon should not be displayed.
7425 * @param hintHideIcon {@code true} to hide the icon, {@code false} otherwise.
7426 * @return this object for method chaining
7427 */
7428 public WearableExtender setHintHideIcon(boolean hintHideIcon) {
7429 setFlag(FLAG_HINT_HIDE_ICON, hintHideIcon);
7430 return this;
7431 }
7432
7433 /**
7434 * Get a hint that this notification's icon should not be displayed.
7435 * @return {@code true} if this icon should not be displayed, false otherwise.
7436 * The default value is {@code false} if this was never set.
7437 */
7438 public boolean getHintHideIcon() {
7439 return (mFlags & FLAG_HINT_HIDE_ICON) != 0;
7440 }
7441
7442 /**
7443 * Set a visual hint that only the background image of this notification should be
7444 * displayed, and other semantic content should be hidden. This hint is only applicable
7445 * to sub-pages added using {@link #addPage}.
7446 */
7447 public WearableExtender setHintShowBackgroundOnly(boolean hintShowBackgroundOnly) {
7448 setFlag(FLAG_HINT_SHOW_BACKGROUND_ONLY, hintShowBackgroundOnly);
7449 return this;
7450 }
7451
7452 /**
7453 * Get a visual hint that only the background image of this notification should be
7454 * displayed, and other semantic content should be hidden. This hint is only applicable
7455 * to sub-pages added using {@link android.app.Notification.WearableExtender#addPage}.
7456 */
7457 public boolean getHintShowBackgroundOnly() {
7458 return (mFlags & FLAG_HINT_SHOW_BACKGROUND_ONLY) != 0;
7459 }
7460
Griff Hazen5f2edfc2014-09-29 16:28:44 -07007461 /**
Griff Hazen9c5be4e2014-11-17 17:47:50 -08007462 * Set a hint that this notification's background should not be clipped if possible,
7463 * and should instead be resized to fully display on the screen, retaining the aspect
7464 * ratio of the image. This can be useful for images like barcodes or qr codes.
Griff Hazen5f2edfc2014-09-29 16:28:44 -07007465 * @param hintAvoidBackgroundClipping {@code true} to avoid clipping if possible.
7466 * @return this object for method chaining
7467 */
7468 public WearableExtender setHintAvoidBackgroundClipping(
7469 boolean hintAvoidBackgroundClipping) {
7470 setFlag(FLAG_HINT_AVOID_BACKGROUND_CLIPPING, hintAvoidBackgroundClipping);
7471 return this;
7472 }
7473
7474 /**
Griff Hazen9c5be4e2014-11-17 17:47:50 -08007475 * Get a hint that this notification's background should not be clipped if possible,
7476 * and should instead be resized to fully display on the screen, retaining the aspect
7477 * ratio of the image. This can be useful for images like barcodes or qr codes.
Griff Hazen5f2edfc2014-09-29 16:28:44 -07007478 * @return {@code true} if it's ok if the background is clipped on the screen, false
7479 * otherwise. The default value is {@code false} if this was never set.
7480 */
7481 public boolean getHintAvoidBackgroundClipping() {
7482 return (mFlags & FLAG_HINT_AVOID_BACKGROUND_CLIPPING) != 0;
7483 }
7484
7485 /**
7486 * Set a hint that the screen should remain on for at least this duration when
7487 * this notification is displayed on the screen.
7488 * @param timeout The requested screen timeout in milliseconds. Can also be either
7489 * {@link #SCREEN_TIMEOUT_SHORT} or {@link #SCREEN_TIMEOUT_LONG}.
7490 * @return this object for method chaining
7491 */
7492 public WearableExtender setHintScreenTimeout(int timeout) {
7493 mHintScreenTimeout = timeout;
7494 return this;
7495 }
7496
7497 /**
7498 * Get the duration, in milliseconds, that the screen should remain on for
7499 * when this notification is displayed.
7500 * @return the duration in milliseconds if > 0, or either one of the sentinel values
7501 * {@link #SCREEN_TIMEOUT_SHORT} or {@link #SCREEN_TIMEOUT_LONG}.
7502 */
7503 public int getHintScreenTimeout() {
7504 return mHintScreenTimeout;
7505 }
7506
Alex Hills9ab3a232016-04-05 14:54:56 -04007507 /**
Alex Hills4bcb06b2016-04-05 14:26:25 -04007508 * Set a hint that this notification's {@link BigPictureStyle} (if present) should be
7509 * converted to low-bit and displayed in ambient mode, especially useful for barcodes and
7510 * qr codes, as well as other simple black-and-white tickets.
7511 * @param hintAmbientBigPicture {@code true} to enable converstion and ambient.
7512 * @return this object for method chaining
7513 */
7514 public WearableExtender setHintAmbientBigPicture(boolean hintAmbientBigPicture) {
7515 setFlag(FLAG_BIG_PICTURE_AMBIENT, hintAmbientBigPicture);
7516 return this;
7517 }
7518
7519 /**
7520 * Get a hint that this notification's {@link BigPictureStyle} (if present) should be
7521 * converted to low-bit and displayed in ambient mode, especially useful for barcodes and
7522 * qr codes, as well as other simple black-and-white tickets.
7523 * @return {@code true} if it should be displayed in ambient, false otherwise
7524 * otherwise. The default value is {@code false} if this was never set.
7525 */
7526 public boolean getHintAmbientBigPicture() {
7527 return (mFlags & FLAG_BIG_PICTURE_AMBIENT) != 0;
7528 }
7529
7530 /**
Alex Hills9ab3a232016-04-05 14:54:56 -04007531 * Set a hint that this notification's content intent will launch an {@link Activity}
7532 * directly, telling the platform that it can generate the appropriate transitions.
7533 * @param hintContentIntentLaunchesActivity {@code true} if the content intent will launch
7534 * an activity and transitions should be generated, false otherwise.
7535 * @return this object for method chaining
7536 */
7537 public WearableExtender setHintContentIntentLaunchesActivity(
7538 boolean hintContentIntentLaunchesActivity) {
7539 setFlag(FLAG_HINT_CONTENT_INTENT_LAUNCHES_ACTIVITY, hintContentIntentLaunchesActivity);
7540 return this;
7541 }
7542
7543 /**
7544 * Get a hint that this notification's content intent will launch an {@link Activity}
7545 * directly, telling the platform that it can generate the appropriate transitions
7546 * @return {@code true} if the content intent will launch an activity and transitions should
7547 * be generated, false otherwise. The default value is {@code false} if this was never set.
7548 */
7549 public boolean getHintContentIntentLaunchesActivity() {
7550 return (mFlags & FLAG_HINT_CONTENT_INTENT_LAUNCHES_ACTIVITY) != 0;
7551 }
7552
Nadia Benbernou948627e2016-04-14 14:41:08 -04007553 /**
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04007554 * Sets the dismissal id for this notification. If a notification is posted with a
7555 * dismissal id, then when that notification is canceled, notifications on other wearables
7556 * and the paired Android phone having that same dismissal id will also be canceled. See
Nadia Benbernou948627e2016-04-14 14:41:08 -04007557 * <a href="{@docRoot}wear/notifications/index.html">Adding Wearable Features to
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04007558 * Notifications</a> for more information.
Nadia Benbernou948627e2016-04-14 14:41:08 -04007559 * @param dismissalId the dismissal id of the notification.
7560 * @return this object for method chaining
7561 */
7562 public WearableExtender setDismissalId(String dismissalId) {
7563 mDismissalId = dismissalId;
7564 return this;
7565 }
7566
7567 /**
7568 * Returns the dismissal id of the notification.
7569 * @return the dismissal id of the notification or null if it has not been set.
7570 */
7571 public String getDismissalId() {
7572 return mDismissalId;
7573 }
7574
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04007575 /**
7576 * Sets a bridge tag for this notification. A bridge tag can be set for notifications
7577 * posted from a phone to provide finer-grained control on what notifications are bridged
7578 * to wearables. See <a href="{@docRoot}wear/notifications/index.html">Adding Wearable
7579 * Features to Notifications</a> for more information.
7580 * @param bridgeTag the bridge tag of the notification.
7581 * @return this object for method chaining
7582 */
7583 public WearableExtender setBridgeTag(String bridgeTag) {
7584 mBridgeTag = bridgeTag;
7585 return this;
7586 }
7587
7588 /**
7589 * Returns the bridge tag of the notification.
7590 * @return the bridge tag or null if not present.
7591 */
7592 public String getBridgeTag() {
7593 return mBridgeTag;
7594 }
7595
Griff Hazen61a9e862014-05-22 16:05:19 -07007596 private void setFlag(int mask, boolean value) {
7597 if (value) {
7598 mFlags |= mask;
7599 } else {
7600 mFlags &= ~mask;
7601 }
7602 }
7603 }
7604
7605 /**
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08007606 * <p>Helper class to add Android Auto extensions to notifications. To create a notification
7607 * with car extensions:
7608 *
7609 * <ol>
7610 * <li>Create an {@link Notification.Builder}, setting any desired
7611 * properties.
7612 * <li>Create a {@link CarExtender}.
7613 * <li>Set car-specific properties using the {@code add} and {@code set} methods of
7614 * {@link CarExtender}.
7615 * <li>Call {@link Notification.Builder#extend(Notification.Extender)}
7616 * to apply the extensions to a notification.
7617 * </ol>
7618 *
7619 * <pre class="prettyprint">
7620 * Notification notification = new Notification.Builder(context)
7621 * ...
7622 * .extend(new CarExtender()
7623 * .set*(...))
7624 * .build();
7625 * </pre>
7626 *
7627 * <p>Car extensions can be accessed on an existing notification by using the
7628 * {@code CarExtender(Notification)} constructor, and then using the {@code get} methods
7629 * to access values.
7630 */
7631 public static final class CarExtender implements Extender {
7632 private static final String TAG = "CarExtender";
7633
7634 private static final String EXTRA_CAR_EXTENDER = "android.car.EXTENSIONS";
7635 private static final String EXTRA_LARGE_ICON = "large_icon";
7636 private static final String EXTRA_CONVERSATION = "car_conversation";
7637 private static final String EXTRA_COLOR = "app_color";
7638
7639 private Bitmap mLargeIcon;
7640 private UnreadConversation mUnreadConversation;
7641 private int mColor = Notification.COLOR_DEFAULT;
7642
7643 /**
7644 * Create a {@link CarExtender} with default options.
7645 */
7646 public CarExtender() {
7647 }
7648
7649 /**
7650 * Create a {@link CarExtender} from the CarExtender options of an existing Notification.
7651 *
7652 * @param notif The notification from which to copy options.
7653 */
7654 public CarExtender(Notification notif) {
7655 Bundle carBundle = notif.extras == null ?
7656 null : notif.extras.getBundle(EXTRA_CAR_EXTENDER);
7657 if (carBundle != null) {
7658 mLargeIcon = carBundle.getParcelable(EXTRA_LARGE_ICON);
7659 mColor = carBundle.getInt(EXTRA_COLOR, Notification.COLOR_DEFAULT);
7660
7661 Bundle b = carBundle.getBundle(EXTRA_CONVERSATION);
7662 mUnreadConversation = UnreadConversation.getUnreadConversationFromBundle(b);
7663 }
7664 }
7665
7666 /**
7667 * Apply car extensions to a notification that is being built. This is typically called by
7668 * the {@link Notification.Builder#extend(Notification.Extender)}
7669 * method of {@link Notification.Builder}.
7670 */
7671 @Override
7672 public Notification.Builder extend(Notification.Builder builder) {
7673 Bundle carExtensions = new Bundle();
7674
7675 if (mLargeIcon != null) {
7676 carExtensions.putParcelable(EXTRA_LARGE_ICON, mLargeIcon);
7677 }
7678 if (mColor != Notification.COLOR_DEFAULT) {
7679 carExtensions.putInt(EXTRA_COLOR, mColor);
7680 }
7681
7682 if (mUnreadConversation != null) {
7683 Bundle b = mUnreadConversation.getBundleForUnreadConversation();
7684 carExtensions.putBundle(EXTRA_CONVERSATION, b);
7685 }
7686
7687 builder.getExtras().putBundle(EXTRA_CAR_EXTENDER, carExtensions);
7688 return builder;
7689 }
7690
7691 /**
7692 * Sets the accent color to use when Android Auto presents the notification.
7693 *
7694 * Android Auto uses the color set with {@link Notification.Builder#setColor(int)}
7695 * to accent the displayed notification. However, not all colors are acceptable in an
7696 * automotive setting. This method can be used to override the color provided in the
7697 * notification in such a situation.
7698 */
Tor Norbye80756e32015-03-02 09:39:27 -08007699 public CarExtender setColor(@ColorInt int color) {
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08007700 mColor = color;
7701 return this;
7702 }
7703
7704 /**
7705 * Gets the accent color.
7706 *
Julia Reynoldsd9228f12015-10-20 10:37:27 -04007707 * @see #setColor
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08007708 */
Tor Norbye80756e32015-03-02 09:39:27 -08007709 @ColorInt
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08007710 public int getColor() {
7711 return mColor;
7712 }
7713
7714 /**
7715 * Sets the large icon of the car notification.
7716 *
7717 * If no large icon is set in the extender, Android Auto will display the icon
7718 * specified by {@link Notification.Builder#setLargeIcon(android.graphics.Bitmap)}
7719 *
7720 * @param largeIcon The large icon to use in the car notification.
7721 * @return This object for method chaining.
7722 */
7723 public CarExtender setLargeIcon(Bitmap largeIcon) {
7724 mLargeIcon = largeIcon;
7725 return this;
7726 }
7727
7728 /**
7729 * Gets the large icon used in this car notification, or null if no icon has been set.
7730 *
7731 * @return The large icon for the car notification.
7732 * @see CarExtender#setLargeIcon
7733 */
7734 public Bitmap getLargeIcon() {
7735 return mLargeIcon;
7736 }
7737
7738 /**
7739 * Sets the unread conversation in a message notification.
7740 *
7741 * @param unreadConversation The unread part of the conversation this notification conveys.
7742 * @return This object for method chaining.
7743 */
7744 public CarExtender setUnreadConversation(UnreadConversation unreadConversation) {
7745 mUnreadConversation = unreadConversation;
7746 return this;
7747 }
7748
7749 /**
7750 * Returns the unread conversation conveyed by this notification.
7751 * @see #setUnreadConversation(UnreadConversation)
7752 */
7753 public UnreadConversation getUnreadConversation() {
7754 return mUnreadConversation;
7755 }
7756
7757 /**
7758 * A class which holds the unread messages from a conversation.
7759 */
7760 public static class UnreadConversation {
7761 private static final String KEY_AUTHOR = "author";
7762 private static final String KEY_TEXT = "text";
7763 private static final String KEY_MESSAGES = "messages";
7764 private static final String KEY_REMOTE_INPUT = "remote_input";
7765 private static final String KEY_ON_REPLY = "on_reply";
7766 private static final String KEY_ON_READ = "on_read";
7767 private static final String KEY_PARTICIPANTS = "participants";
7768 private static final String KEY_TIMESTAMP = "timestamp";
7769
7770 private final String[] mMessages;
7771 private final RemoteInput mRemoteInput;
7772 private final PendingIntent mReplyPendingIntent;
7773 private final PendingIntent mReadPendingIntent;
7774 private final String[] mParticipants;
7775 private final long mLatestTimestamp;
7776
7777 UnreadConversation(String[] messages, RemoteInput remoteInput,
7778 PendingIntent replyPendingIntent, PendingIntent readPendingIntent,
7779 String[] participants, long latestTimestamp) {
7780 mMessages = messages;
7781 mRemoteInput = remoteInput;
7782 mReadPendingIntent = readPendingIntent;
7783 mReplyPendingIntent = replyPendingIntent;
7784 mParticipants = participants;
7785 mLatestTimestamp = latestTimestamp;
7786 }
7787
7788 /**
7789 * Gets the list of messages conveyed by this notification.
7790 */
7791 public String[] getMessages() {
7792 return mMessages;
7793 }
7794
7795 /**
7796 * Gets the remote input that will be used to convey the response to a message list, or
7797 * null if no such remote input exists.
7798 */
7799 public RemoteInput getRemoteInput() {
7800 return mRemoteInput;
7801 }
7802
7803 /**
7804 * Gets the pending intent that will be triggered when the user replies to this
7805 * notification.
7806 */
7807 public PendingIntent getReplyPendingIntent() {
7808 return mReplyPendingIntent;
7809 }
7810
7811 /**
7812 * Gets the pending intent that Android Auto will send after it reads aloud all messages
7813 * in this object's message list.
7814 */
7815 public PendingIntent getReadPendingIntent() {
7816 return mReadPendingIntent;
7817 }
7818
7819 /**
7820 * Gets the participants in the conversation.
7821 */
7822 public String[] getParticipants() {
7823 return mParticipants;
7824 }
7825
7826 /**
7827 * Gets the firs participant in the conversation.
7828 */
7829 public String getParticipant() {
7830 return mParticipants.length > 0 ? mParticipants[0] : null;
7831 }
7832
7833 /**
7834 * Gets the timestamp of the conversation.
7835 */
7836 public long getLatestTimestamp() {
7837 return mLatestTimestamp;
7838 }
7839
7840 Bundle getBundleForUnreadConversation() {
7841 Bundle b = new Bundle();
7842 String author = null;
7843 if (mParticipants != null && mParticipants.length > 1) {
7844 author = mParticipants[0];
7845 }
7846 Parcelable[] messages = new Parcelable[mMessages.length];
7847 for (int i = 0; i < messages.length; i++) {
7848 Bundle m = new Bundle();
7849 m.putString(KEY_TEXT, mMessages[i]);
7850 m.putString(KEY_AUTHOR, author);
7851 messages[i] = m;
7852 }
7853 b.putParcelableArray(KEY_MESSAGES, messages);
7854 if (mRemoteInput != null) {
7855 b.putParcelable(KEY_REMOTE_INPUT, mRemoteInput);
7856 }
7857 b.putParcelable(KEY_ON_REPLY, mReplyPendingIntent);
7858 b.putParcelable(KEY_ON_READ, mReadPendingIntent);
7859 b.putStringArray(KEY_PARTICIPANTS, mParticipants);
7860 b.putLong(KEY_TIMESTAMP, mLatestTimestamp);
7861 return b;
7862 }
7863
7864 static UnreadConversation getUnreadConversationFromBundle(Bundle b) {
7865 if (b == null) {
7866 return null;
7867 }
7868 Parcelable[] parcelableMessages = b.getParcelableArray(KEY_MESSAGES);
7869 String[] messages = null;
7870 if (parcelableMessages != null) {
7871 String[] tmp = new String[parcelableMessages.length];
7872 boolean success = true;
7873 for (int i = 0; i < tmp.length; i++) {
7874 if (!(parcelableMessages[i] instanceof Bundle)) {
7875 success = false;
7876 break;
7877 }
7878 tmp[i] = ((Bundle) parcelableMessages[i]).getString(KEY_TEXT);
7879 if (tmp[i] == null) {
7880 success = false;
7881 break;
7882 }
7883 }
7884 if (success) {
7885 messages = tmp;
7886 } else {
7887 return null;
7888 }
7889 }
7890
7891 PendingIntent onRead = b.getParcelable(KEY_ON_READ);
7892 PendingIntent onReply = b.getParcelable(KEY_ON_REPLY);
7893
7894 RemoteInput remoteInput = b.getParcelable(KEY_REMOTE_INPUT);
7895
7896 String[] participants = b.getStringArray(KEY_PARTICIPANTS);
7897 if (participants == null || participants.length != 1) {
7898 return null;
7899 }
7900
7901 return new UnreadConversation(messages,
7902 remoteInput,
7903 onReply,
7904 onRead,
7905 participants, b.getLong(KEY_TIMESTAMP));
7906 }
7907 };
7908
7909 /**
7910 * Builder class for {@link CarExtender.UnreadConversation} objects.
7911 */
7912 public static class Builder {
7913 private final List<String> mMessages = new ArrayList<String>();
7914 private final String mParticipant;
7915 private RemoteInput mRemoteInput;
7916 private PendingIntent mReadPendingIntent;
7917 private PendingIntent mReplyPendingIntent;
7918 private long mLatestTimestamp;
7919
7920 /**
7921 * Constructs a new builder for {@link CarExtender.UnreadConversation}.
7922 *
7923 * @param name The name of the other participant in the conversation.
7924 */
7925 public Builder(String name) {
7926 mParticipant = name;
7927 }
7928
7929 /**
7930 * Appends a new unread message to the list of messages for this conversation.
7931 *
7932 * The messages should be added from oldest to newest.
7933 *
7934 * @param message The text of the new unread message.
7935 * @return This object for method chaining.
7936 */
7937 public Builder addMessage(String message) {
7938 mMessages.add(message);
7939 return this;
7940 }
7941
7942 /**
7943 * Sets the pending intent and remote input which will convey the reply to this
7944 * notification.
7945 *
7946 * @param pendingIntent The pending intent which will be triggered on a reply.
7947 * @param remoteInput The remote input parcelable which will carry the reply.
7948 * @return This object for method chaining.
7949 *
7950 * @see CarExtender.UnreadConversation#getRemoteInput
7951 * @see CarExtender.UnreadConversation#getReplyPendingIntent
7952 */
7953 public Builder setReplyAction(
7954 PendingIntent pendingIntent, RemoteInput remoteInput) {
7955 mRemoteInput = remoteInput;
7956 mReplyPendingIntent = pendingIntent;
7957
7958 return this;
7959 }
7960
7961 /**
7962 * Sets the pending intent that will be sent once the messages in this notification
7963 * are read.
7964 *
7965 * @param pendingIntent The pending intent to use.
7966 * @return This object for method chaining.
7967 */
7968 public Builder setReadPendingIntent(PendingIntent pendingIntent) {
7969 mReadPendingIntent = pendingIntent;
7970 return this;
7971 }
7972
7973 /**
7974 * Sets the timestamp of the most recent message in an unread conversation.
7975 *
7976 * If a messaging notification has been posted by your application and has not
7977 * yet been cancelled, posting a later notification with the same id and tag
7978 * but without a newer timestamp may result in Android Auto not displaying a
7979 * heads up notification for the later notification.
7980 *
7981 * @param timestamp The timestamp of the most recent message in the conversation.
7982 * @return This object for method chaining.
7983 */
7984 public Builder setLatestTimestamp(long timestamp) {
7985 mLatestTimestamp = timestamp;
7986 return this;
7987 }
7988
7989 /**
7990 * Builds a new unread conversation object.
7991 *
7992 * @return The new unread conversation object.
7993 */
7994 public UnreadConversation build() {
7995 String[] messages = mMessages.toArray(new String[mMessages.size()]);
7996 String[] participants = { mParticipant };
7997 return new UnreadConversation(messages, mRemoteInput, mReplyPendingIntent,
7998 mReadPendingIntent, participants, mLatestTimestamp);
7999 }
8000 }
8001 }
8002
8003 /**
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08008004 * <p>Helper class to add Android TV extensions to notifications. To create a notification
8005 * with a TV extension:
8006 *
8007 * <ol>
8008 * <li>Create an {@link Notification.Builder}, setting any desired properties.
8009 * <li>Create a {@link TvExtender}.
8010 * <li>Set TV-specific properties using the {@code set} methods of
8011 * {@link TvExtender}.
8012 * <li>Call {@link Notification.Builder#extend(Notification.Extender)}
8013 * to apply the extension to a notification.
8014 * </ol>
8015 *
8016 * <pre class="prettyprint">
8017 * Notification notification = new Notification.Builder(context)
8018 * ...
8019 * .extend(new TvExtender()
8020 * .set*(...))
8021 * .build();
8022 * </pre>
8023 *
8024 * <p>TV extensions can be accessed on an existing notification by using the
8025 * {@code TvExtender(Notification)} constructor, and then using the {@code get} methods
8026 * to access values.
8027 *
8028 * @hide
8029 */
8030 @SystemApi
8031 public static final class TvExtender implements Extender {
8032 private static final String TAG = "TvExtender";
8033
8034 private static final String EXTRA_TV_EXTENDER = "android.tv.EXTENSIONS";
8035 private static final String EXTRA_FLAGS = "flags";
8036 private static final String EXTRA_CONTENT_INTENT = "content_intent";
8037 private static final String EXTRA_DELETE_INTENT = "delete_intent";
Dmitri Plotnikovb8a04ee2017-01-20 10:39:22 -08008038 private static final String EXTRA_CHANNEL_ID = "channel_id";
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08008039
8040 // Flags bitwise-ored to mFlags
8041 private static final int FLAG_AVAILABLE_ON_TV = 0x1;
8042
8043 private int mFlags;
Dmitri Plotnikovb8a04ee2017-01-20 10:39:22 -08008044 private String mChannelId;
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08008045 private PendingIntent mContentIntent;
8046 private PendingIntent mDeleteIntent;
8047
8048 /**
8049 * Create a {@link TvExtender} with default options.
8050 */
8051 public TvExtender() {
8052 mFlags = FLAG_AVAILABLE_ON_TV;
8053 }
8054
8055 /**
8056 * Create a {@link TvExtender} from the TvExtender options of an existing Notification.
8057 *
8058 * @param notif The notification from which to copy options.
8059 */
8060 public TvExtender(Notification notif) {
8061 Bundle bundle = notif.extras == null ?
8062 null : notif.extras.getBundle(EXTRA_TV_EXTENDER);
8063 if (bundle != null) {
8064 mFlags = bundle.getInt(EXTRA_FLAGS);
Dmitri Plotnikovb8a04ee2017-01-20 10:39:22 -08008065 mChannelId = bundle.getString(EXTRA_CHANNEL_ID);
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08008066 mContentIntent = bundle.getParcelable(EXTRA_CONTENT_INTENT);
8067 mDeleteIntent = bundle.getParcelable(EXTRA_DELETE_INTENT);
8068 }
8069 }
8070
8071 /**
8072 * Apply a TV extension to a notification that is being built. This is typically called by
8073 * the {@link Notification.Builder#extend(Notification.Extender)}
8074 * method of {@link Notification.Builder}.
8075 */
8076 @Override
8077 public Notification.Builder extend(Notification.Builder builder) {
8078 Bundle bundle = new Bundle();
8079
8080 bundle.putInt(EXTRA_FLAGS, mFlags);
Dmitri Plotnikovb8a04ee2017-01-20 10:39:22 -08008081 bundle.putString(EXTRA_CHANNEL_ID, mChannelId);
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08008082 if (mContentIntent != null) {
8083 bundle.putParcelable(EXTRA_CONTENT_INTENT, mContentIntent);
8084 }
8085
8086 if (mDeleteIntent != null) {
8087 bundle.putParcelable(EXTRA_DELETE_INTENT, mDeleteIntent);
8088 }
8089
8090 builder.getExtras().putBundle(EXTRA_TV_EXTENDER, bundle);
8091 return builder;
8092 }
8093
8094 /**
8095 * Returns true if this notification should be shown on TV. This method return true
8096 * if the notification was extended with a TvExtender.
8097 */
8098 public boolean isAvailableOnTv() {
8099 return (mFlags & FLAG_AVAILABLE_ON_TV) != 0;
8100 }
8101
8102 /**
Dmitri Plotnikovb8a04ee2017-01-20 10:39:22 -08008103 * Specifies the channel the notification should be delivered on when shown on TV.
8104 * It can be different from the channel that the notification is delivered to when
8105 * posting on a non-TV device.
8106 */
8107 public TvExtender setChannel(String channelId) {
8108 mChannelId = channelId;
8109 return this;
8110 }
8111
8112 /**
Julia Reynoldsbad42972017-04-25 13:52:49 -04008113 * Specifies the channel the notification should be delivered on when shown on TV.
8114 * It can be different from the channel that the notification is delivered to when
8115 * posting on a non-TV device.
8116 */
8117 public TvExtender setChannelId(String channelId) {
8118 mChannelId = channelId;
8119 return this;
8120 }
8121
Jeff Sharkey000ce802017-04-29 13:13:27 -06008122 /** @removed */
8123 @Deprecated
Dmitri Plotnikovb8a04ee2017-01-20 10:39:22 -08008124 public String getChannel() {
8125 return mChannelId;
8126 }
8127
8128 /**
Julia Reynoldsbad42972017-04-25 13:52:49 -04008129 * Returns the id of the channel this notification posts to on TV.
8130 */
8131 public String getChannelId() {
8132 return mChannelId;
8133 }
8134
8135 /**
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08008136 * Supplies a {@link PendingIntent} to be sent when the notification is selected on TV.
8137 * If provided, it is used instead of the content intent specified
8138 * at the level of Notification.
8139 */
8140 public TvExtender setContentIntent(PendingIntent intent) {
8141 mContentIntent = intent;
8142 return this;
8143 }
8144
8145 /**
8146 * Returns the TV-specific content intent. If this method returns null, the
8147 * main content intent on the notification should be used.
8148 *
8149 * @see {@link Notification#contentIntent}
8150 */
8151 public PendingIntent getContentIntent() {
8152 return mContentIntent;
8153 }
8154
8155 /**
8156 * Supplies a {@link PendingIntent} to send when the notification is cleared explicitly
8157 * by the user on TV. If provided, it is used instead of the delete intent specified
8158 * at the level of Notification.
8159 */
8160 public TvExtender setDeleteIntent(PendingIntent intent) {
8161 mDeleteIntent = intent;
8162 return this;
8163 }
8164
8165 /**
8166 * Returns the TV-specific delete intent. If this method returns null, the
8167 * main delete intent on the notification should be used.
8168 *
8169 * @see {@link Notification#deleteIntent}
8170 */
8171 public PendingIntent getDeleteIntent() {
8172 return mDeleteIntent;
8173 }
8174 }
8175
8176 /**
Griff Hazen61a9e862014-05-22 16:05:19 -07008177 * Get an array of Notification objects from a parcelable array bundle field.
8178 * Update the bundle to have a typed array so fetches in the future don't need
8179 * to do an array copy.
8180 */
8181 private static Notification[] getNotificationArrayFromBundle(Bundle bundle, String key) {
8182 Parcelable[] array = bundle.getParcelableArray(key);
8183 if (array instanceof Notification[] || array == null) {
8184 return (Notification[]) array;
8185 }
8186 Notification[] typedArray = Arrays.copyOf(array, array.length,
8187 Notification[].class);
8188 bundle.putParcelableArray(key, typedArray);
8189 return typedArray;
8190 }
Christoph Studer4600f9b2014-07-22 22:44:43 +02008191
8192 private static class BuilderRemoteViews extends RemoteViews {
8193 public BuilderRemoteViews(Parcel parcel) {
8194 super(parcel);
8195 }
8196
Kenny Guy77320062014-08-27 21:37:15 +01008197 public BuilderRemoteViews(ApplicationInfo appInfo, int layoutId) {
8198 super(appInfo, layoutId);
Christoph Studer4600f9b2014-07-22 22:44:43 +02008199 }
8200
8201 @Override
8202 public BuilderRemoteViews clone() {
8203 Parcel p = Parcel.obtain();
8204 writeToParcel(p, 0);
8205 p.setDataPosition(0);
8206 BuilderRemoteViews brv = new BuilderRemoteViews(p);
8207 p.recycle();
8208 return brv;
8209 }
8210 }
Adrian Roos70d7aa32017-01-11 15:39:06 -08008211
8212 private static class StandardTemplateParams {
8213 boolean hasProgress = true;
8214 boolean ambient = false;
8215 CharSequence title;
8216 CharSequence text;
8217
8218 final StandardTemplateParams reset() {
8219 hasProgress = true;
8220 ambient = false;
8221 title = null;
8222 text = null;
8223 return this;
8224 }
8225
8226 final StandardTemplateParams hasProgress(boolean hasProgress) {
8227 this.hasProgress = hasProgress;
8228 return this;
8229 }
8230
8231 final StandardTemplateParams title(CharSequence title) {
8232 this.title = title;
8233 return this;
8234 }
8235
8236 final StandardTemplateParams text(CharSequence text) {
8237 this.text = text;
8238 return this;
8239 }
8240
8241 final StandardTemplateParams ambient(boolean ambient) {
Adrian Roos0bc3f6a2017-03-06 11:54:05 -08008242 Preconditions.checkState(title == null && text == null, "must set ambient before text");
Adrian Roos70d7aa32017-01-11 15:39:06 -08008243 this.ambient = ambient;
8244 return this;
8245 }
8246
8247 final StandardTemplateParams fillTextsFrom(Builder b) {
8248 Bundle extras = b.mN.extras;
Adrian Roos0bc3f6a2017-03-06 11:54:05 -08008249 title = b.processLegacyText(extras.getCharSequence(EXTRA_TITLE), ambient);
8250 text = b.processLegacyText(extras.getCharSequence(EXTRA_TEXT), ambient);
Adrian Roos70d7aa32017-01-11 15:39:06 -08008251 return this;
8252 }
8253 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08008254}