blob: 7be77c5548b70861fbcc4f24816d733f5a19d860 [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;
Jorim Jaggief72a192014-08-26 21:57:46 +020031import android.content.res.ColorStateList;
Joe Onoratoef1e7762010-09-17 18:38:38 -040032import android.graphics.Bitmap;
Kenny Guy8a0101b2014-05-08 23:34:12 +010033import android.graphics.Canvas;
Adrian Roosc1a80b02016-04-05 14:54:55 -070034import android.graphics.Color;
Jorim Jaggi5c2d8462014-03-21 17:37:00 +010035import android.graphics.PorterDuff;
Kenny Guy8a0101b2014-05-08 23:34:12 +010036import android.graphics.drawable.Drawable;
Dan Sandlerd63f9322015-05-06 15:18:49 -040037import android.graphics.drawable.Icon;
John Spurlockc0650f022014-07-19 13:22:39 -040038import android.media.AudioAttributes;
Jeff Sharkey098d5802012-04-26 17:30:34 -070039import android.media.AudioManager;
Jeff Browndba34ba2014-06-24 20:46:03 -070040import android.media.session.MediaSession;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080041import android.net.Uri;
Daniel Sandlerdcbaf662013-04-26 16:23:09 -040042import android.os.BadParcelableException;
Christoph Studer239f8352014-08-25 15:13:18 +020043import android.os.Build;
Daniel Sandler2561b0b2012-02-13 21:04:12 -050044import android.os.Bundle;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080045import android.os.Parcel;
46import android.os.Parcelable;
Daniel Sandlera2985ed2012-04-03 16:42:00 -040047import android.os.SystemClock;
Jeff Sharkey6d515712012-09-20 16:06:08 -070048import android.os.UserHandle;
Adrian Roosc1a80b02016-04-05 14:54:55 -070049import android.text.BidiFormatter;
Selim Cinek60a54252016-02-26 17:03:25 -080050import android.text.SpannableStringBuilder;
51import android.text.Spanned;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080052import android.text.TextUtils;
Selim Cinek60a54252016-02-26 17:03:25 -080053import android.text.style.AbsoluteSizeSpan;
Selim Cinek981962e2016-07-20 20:41:58 -070054import android.text.style.BackgroundColorSpan;
Selim Cinek89991a22016-03-07 19:51:54 -080055import android.text.style.CharacterStyle;
Selim Cinek981962e2016-07-20 20:41:58 -070056import android.text.style.ForegroundColorSpan;
Selim Cinek60a54252016-02-26 17:03:25 -080057import android.text.style.RelativeSizeSpan;
58import android.text.style.TextAppearanceSpan;
Svet Ganovddb94882016-06-23 19:55:24 -070059import android.util.ArraySet;
Daniel Sandlerdcbaf662013-04-26 16:23:09 -040060import android.util.Log;
Dan Sandler50128532015-12-08 15:42:41 -050061import android.util.SparseArray;
Griff Hazen61a9e862014-05-22 16:05:19 -070062import android.view.Gravity;
Selim Cinekea4bef72015-12-02 15:51:10 -080063import android.view.NotificationHeaderView;
Joe Onorato8595a3d2010-11-19 18:12:07 -080064import android.view.View;
Selim Cinek954cc232016-05-20 13:29:23 -070065import android.view.ViewGroup;
Jeff Sharkey1c400132011-08-05 14:50:13 -070066import android.widget.ProgressBar;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080067import android.widget.RemoteViews;
68
Griff Hazen959591e2014-05-15 22:26:18 -070069import com.android.internal.R;
Svet Ganovddb94882016-06-23 19:55:24 -070070import com.android.internal.util.ArrayUtils;
Griff Hazenc091ba82014-05-16 10:13:26 -070071import com.android.internal.util.NotificationColorUtil;
Griff Hazen959591e2014-05-15 22:26:18 -070072
Tor Norbyed9273d62013-05-30 15:59:53 -070073import java.lang.annotation.Retention;
74import java.lang.annotation.RetentionPolicy;
Christoph Studer4600f9b2014-07-22 22:44:43 +020075import java.lang.reflect.Constructor;
Daniel Sandler2561b0b2012-02-13 21:04:12 -050076import java.util.ArrayList;
Griff Hazen61a9e862014-05-22 16:05:19 -070077import java.util.Arrays;
Griff Hazen5cadc3b2014-05-20 09:55:39 -070078import java.util.Collections;
Griff Hazen61a9e862014-05-22 16:05:19 -070079import java.util.List;
Dan Sandler50128532015-12-08 15:42:41 -050080import java.util.Set;
Joe Onorato561d3852010-11-20 18:09:34 -080081
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080082/**
83 * A class that represents how a persistent notification is to be presented to
84 * the user using the {@link android.app.NotificationManager}.
85 *
Joe Onoratocb109a02011-01-18 17:57:41 -080086 * <p>The {@link Notification.Builder Notification.Builder} has been added to make it
87 * easier to construct Notifications.</p>
88 *
Joe Fernandez558459f2011-10-13 16:47:36 -070089 * <div class="special reference">
90 * <h3>Developer Guides</h3>
91 * <p>For a guide to creating notifications, read the
92 * <a href="{@docRoot}guide/topics/ui/notifiers/notifications.html">Status Bar Notifications</a>
93 * developer guide.</p>
94 * </div>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080095 */
96public class Notification implements Parcelable
97{
Daniel Sandlerdcbaf662013-04-26 16:23:09 -040098 private static final String TAG = "Notification";
99
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800100 /**
Daniel Sandler01df1c62014-06-09 10:54:01 -0400101 * An activity that provides a user interface for adjusting notification preferences for its
102 * containing application. Optional but recommended for apps that post
103 * {@link android.app.Notification Notifications}.
104 */
105 @SdkConstant(SdkConstantType.INTENT_CATEGORY)
106 public static final String INTENT_CATEGORY_NOTIFICATION_PREFERENCES
107 = "android.intent.category.NOTIFICATION_PREFERENCES";
108
109 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800110 * Use all default values (where applicable).
111 */
112 public static final int DEFAULT_ALL = ~0;
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500113
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800114 /**
115 * Use the default notification sound. This will ignore any given
116 * {@link #sound}.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500117 *
Chris Wren47c20a12014-06-18 17:27:29 -0400118 * <p>
119 * A notification that is noisy is more likely to be presented as a heads-up notification.
120 * </p>
121 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800122 * @see #defaults
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500123 */
124
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800125 public static final int DEFAULT_SOUND = 1;
126
127 /**
128 * Use the default notification vibrate. This will ignore any given
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500129 * {@link #vibrate}. Using phone vibration requires the
Scott Mainb8b36452009-04-26 15:50:49 -0700130 * {@link android.Manifest.permission#VIBRATE VIBRATE} permission.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500131 *
Chris Wren47c20a12014-06-18 17:27:29 -0400132 * <p>
133 * A notification that vibrates is more likely to be presented as a heads-up notification.
134 * </p>
135 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800136 * @see #defaults
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500137 */
138
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800139 public static final int DEFAULT_VIBRATE = 2;
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500140
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800141 /**
142 * Use the default notification lights. This will ignore the
143 * {@link #FLAG_SHOW_LIGHTS} bit, and {@link #ledARGB}, {@link #ledOffMS}, or
144 * {@link #ledOnMS}.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500145 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800146 * @see #defaults
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500147 */
148
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800149 public static final int DEFAULT_LIGHTS = 4;
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500150
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800151 /**
Christoph Studer535ec612014-09-03 15:47:47 +0200152 * Maximum length of CharSequences accepted by Builder and friends.
153 *
154 * <p>
155 * Avoids spamming the system with overly large strings such as full e-mails.
156 */
157 private static final int MAX_CHARSEQUENCE_LENGTH = 5 * 1024;
158
159 /**
Adrian Roose458aa82015-12-08 16:17:19 -0800160 * Maximum entries of reply text that are accepted by Builder and friends.
161 */
162 private static final int MAX_REPLY_HISTORY = 5;
163
164 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500165 * A timestamp related to this notification, in milliseconds since the epoch.
Joe Malin8d40d042012-11-05 11:36:40 -0800166 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500167 * Default value: {@link System#currentTimeMillis() Now}.
168 *
169 * Choose a timestamp that will be most relevant to the user. For most finite events, this
170 * corresponds to the time the event happened (or will happen, in the case of events that have
171 * yet to occur but about which the user is being informed). Indefinite events should be
Joe Malin8d40d042012-11-05 11:36:40 -0800172 * timestamped according to when the activity began.
173 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500174 * Some examples:
Joe Malin8d40d042012-11-05 11:36:40 -0800175 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500176 * <ul>
177 * <li>Notification of a new chat message should be stamped when the message was received.</li>
178 * <li>Notification of an ongoing file download (with a progress bar, for example) should be stamped when the download started.</li>
179 * <li>Notification of a completed file download should be stamped when the download finished.</li>
180 * <li>Notification of an upcoming meeting should be stamped with the time the meeting will begin (that is, in the future).</li>
181 * <li>Notification of an ongoing stopwatch (increasing timer) should be stamped with the watch's start time.
182 * <li>Notification of an ongoing countdown timer should be stamped with the timer's end time.
Joe Malin8d40d042012-11-05 11:36:40 -0800183 * </ul>
184 *
Selim Cinek0ff1ce602016-04-05 18:27:16 -0700185 * For apps targeting {@link android.os.Build.VERSION_CODES#N} and above, this time is not shown
186 * anymore by default and must be opted into by using
187 * {@link android.app.Notification.Builder#setShowWhen(boolean)}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800188 */
189 public long when;
190
191 /**
Selim Cinekb85f36fd2016-04-20 18:46:36 -0700192 * The creation time of the notification
193 */
194 private long creationTime;
195
196 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800197 * The resource id of a drawable to use as the icon in the status bar.
Dan Sandler86647982015-05-13 23:41:13 -0400198 *
199 * @deprecated Use {@link Builder#setSmallIcon(Icon)} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800200 */
Dan Sandler86647982015-05-13 23:41:13 -0400201 @Deprecated
Tor Norbye7b9c9122013-05-30 16:48:33 -0700202 @DrawableRes
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800203 public int icon;
204
205 /**
Joe Onorato46439ce2010-11-19 13:56:21 -0800206 * If the icon in the status bar is to have more than one level, you can set this. Otherwise,
207 * leave it at its default value of 0.
208 *
209 * @see android.widget.ImageView#setImageLevel
Griff Hazen959591e2014-05-15 22:26:18 -0700210 * @see android.graphics.drawable.Drawable#setLevel
Joe Onorato46439ce2010-11-19 13:56:21 -0800211 */
212 public int iconLevel;
213
214 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500215 * The number of events that this notification represents. For example, in a new mail
216 * notification, this could be the number of unread messages.
Joe Malin8d40d042012-11-05 11:36:40 -0800217 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500218 * The system may or may not use this field to modify the appearance of the notification. For
219 * example, before {@link android.os.Build.VERSION_CODES#HONEYCOMB}, this number was
220 * superimposed over the icon in the status bar. Starting with
221 * {@link android.os.Build.VERSION_CODES#HONEYCOMB}, the template used by
222 * {@link Notification.Builder} has displayed the number in the expanded notification view.
Joe Malin8d40d042012-11-05 11:36:40 -0800223 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500224 * If the number is 0 or negative, it is never shown.
Selim Cinek0f9dd1e2016-04-05 17:03:40 -0700225 *
226 * @deprecated this number is not shown anymore
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800227 */
Aurimas Liutikas514c5ef2016-05-24 15:22:55 -0700228 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800229 public int number;
230
231 /**
232 * The intent to execute when the expanded status entry is clicked. If
233 * this is an activity, it must include the
234 * {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} flag, which requires
Scott Main7aee61f2011-02-08 11:25:01 -0800235 * that you take care of task management as described in the
236 * <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back
Dianne Hackborn6ceca582012-01-10 15:24:26 -0800237 * Stack</a> document. In particular, make sure to read the notification section
238 * <a href="{@docRoot}guide/topics/ui/notifiers/notifications.html#HandlingNotifications">Handling
239 * Notifications</a> for the correct ways to launch an application from a
240 * notification.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800241 */
242 public PendingIntent contentIntent;
243
244 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500245 * The intent to execute when the notification is explicitly dismissed by the user, either with
246 * the "Clear All" button or by swiping it away individually.
247 *
248 * This probably shouldn't be launching an activity since several of those will be sent
249 * at the same time.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800250 */
251 public PendingIntent deleteIntent;
252
253 /**
Dianne Hackborn170bae72010-09-03 15:14:28 -0700254 * An intent to launch instead of posting the notification to the status bar.
Joe Onoratocb109a02011-01-18 17:57:41 -0800255 *
Chris Wren47c20a12014-06-18 17:27:29 -0400256 * <p>
257 * The system UI may choose to display a heads-up notification, instead of
258 * launching this intent, while the user is using the device.
259 * </p>
260 *
Joe Onoratocb109a02011-01-18 17:57:41 -0800261 * @see Notification.Builder#setFullScreenIntent
Daniel Sandlere46cbd32010-06-17 10:35:26 -0400262 */
263 public PendingIntent fullScreenIntent;
264
265 /**
Dan Sandler5fcdf6e2014-07-18 11:31:15 -0400266 * Text that summarizes this notification for accessibility services.
267 *
268 * As of the L release, this text is no longer shown on screen, but it is still useful to
269 * accessibility services (where it serves as an audible announcement of the notification's
270 * appearance).
Joe Onoratoef1e7762010-09-17 18:38:38 -0400271 *
Joe Onorato46439ce2010-11-19 13:56:21 -0800272 * @see #tickerView
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800273 */
274 public CharSequence tickerText;
275
276 /**
Dan Sandler5fcdf6e2014-07-18 11:31:15 -0400277 * Formerly, a view showing the {@link #tickerText}.
278 *
279 * No longer displayed in the status bar as of API 21.
Joe Onoratoef1e7762010-09-17 18:38:38 -0400280 */
Dan Sandler5fcdf6e2014-07-18 11:31:15 -0400281 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -0800282 public RemoteViews tickerView;
Joe Onoratoef1e7762010-09-17 18:38:38 -0400283
284 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400285 * The view that will represent this notification in the notification list (which is pulled
286 * down from the status bar).
287 *
Julia Reynoldsd4ea7412016-02-17 14:00:56 -0500288 * As of N, this field may be null. The notification view is determined by the inputs
289 * to {@link Notification.Builder}; a custom RemoteViews can optionally be
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400290 * supplied with {@link Notification.Builder#setCustomContentView(RemoteViews)}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800291 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400292 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800293 public RemoteViews contentView;
294
Daniel Sandlera0a938c2012-03-15 08:42:37 -0400295 /**
Daniel Sandler4dfbe832012-04-11 14:51:46 -0400296 * A large-format version of {@link #contentView}, giving the Notification an
Daniel Sandlerf3b73432012-03-27 15:01:25 -0400297 * opportunity to show more detail. The system UI may choose to show this
298 * instead of the normal content view at its discretion.
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400299 *
Julia Reynoldsd4ea7412016-02-17 14:00:56 -0500300 * As of N, this field may be null. The expanded notification view is determined by the
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400301 * inputs to {@link Notification.Builder}; a custom RemoteViews can optionally be
302 * supplied with {@link Notification.Builder#setCustomBigContentView(RemoteViews)}.
Daniel Sandlerf3b73432012-03-27 15:01:25 -0400303 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400304 @Deprecated
Daniel Sandlerf3b73432012-03-27 15:01:25 -0400305 public RemoteViews bigContentView;
306
Chris Wren8fd39ec2014-02-27 17:43:26 -0500307
308 /**
Chris Wren47c20a12014-06-18 17:27:29 -0400309 * A medium-format version of {@link #contentView}, providing the Notification an
310 * opportunity to add action buttons to contentView. At its discretion, the system UI may
311 * choose to show this as a heads-up notification, which will pop up so the user can see
312 * it without leaving their current activity.
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400313 *
Julia Reynoldsd4ea7412016-02-17 14:00:56 -0500314 * As of N, this field may be null. The heads-up notification view is determined by the
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400315 * inputs to {@link Notification.Builder}; a custom RemoteViews can optionally be
316 * supplied with {@link Notification.Builder#setCustomHeadsUpContentView(RemoteViews)}.
Chris Wren8fd39ec2014-02-27 17:43:26 -0500317 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400318 @Deprecated
Chris Wren8fd39ec2014-02-27 17:43:26 -0500319 public RemoteViews headsUpContentView;
320
Daniel Sandlerf3b73432012-03-27 15:01:25 -0400321 /**
Dan Sandler86647982015-05-13 23:41:13 -0400322 * A large bitmap to be shown in the notification content area.
323 *
324 * @deprecated Use {@link Builder#setLargeIcon(Icon)} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800325 */
Dan Sandler86647982015-05-13 23:41:13 -0400326 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -0800327 public Bitmap largeIcon;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800328
329 /**
330 * The sound to play.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500331 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800332 * <p>
Chris Wren47c20a12014-06-18 17:27:29 -0400333 * A notification that is noisy is more likely to be presented as a heads-up notification.
334 * </p>
335 *
336 * <p>
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500337 * To play the default notification sound, see {@link #defaults}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800338 * </p>
339 */
340 public Uri sound;
341
342 /**
343 * Use this constant as the value for audioStreamType to request that
344 * the default stream type for notifications be used. Currently the
Jeff Sharkey098d5802012-04-26 17:30:34 -0700345 * default stream type is {@link AudioManager#STREAM_NOTIFICATION}.
John Spurlockc0650f022014-07-19 13:22:39 -0400346 *
347 * @deprecated Use {@link #audioAttributes} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800348 */
Jean-Michel Trivi81f871e2014-08-06 16:32:38 -0700349 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800350 public static final int STREAM_DEFAULT = -1;
351
352 /**
353 * The audio stream type to use when playing the sound.
354 * Should be one of the STREAM_ constants from
355 * {@link android.media.AudioManager}.
John Spurlockc0650f022014-07-19 13:22:39 -0400356 *
357 * @deprecated Use {@link #audioAttributes} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800358 */
Jean-Michel Trivi81f871e2014-08-06 16:32:38 -0700359 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800360 public int audioStreamType = STREAM_DEFAULT;
361
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800362 /**
John Spurlockc0650f022014-07-19 13:22:39 -0400363 * The default value of {@link #audioAttributes}.
364 */
365 public static final AudioAttributes AUDIO_ATTRIBUTES_DEFAULT = new AudioAttributes.Builder()
366 .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
367 .setUsage(AudioAttributes.USAGE_NOTIFICATION)
368 .build();
369
370 /**
371 * The {@link AudioAttributes audio attributes} to use when playing the sound.
372 */
373 public AudioAttributes audioAttributes = AUDIO_ATTRIBUTES_DEFAULT;
374
375 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500376 * The pattern with which to vibrate.
377 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800378 * <p>
379 * To vibrate the default pattern, see {@link #defaults}.
380 * </p>
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500381 *
Chris Wren47c20a12014-06-18 17:27:29 -0400382 * <p>
383 * A notification that vibrates is more likely to be presented as a heads-up notification.
384 * </p>
385 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800386 * @see android.os.Vibrator#vibrate(long[],int)
387 */
388 public long[] vibrate;
389
390 /**
391 * The color of the led. The hardware will do its best approximation.
392 *
393 * @see #FLAG_SHOW_LIGHTS
394 * @see #flags
395 */
Tor Norbye80756e32015-03-02 09:39:27 -0800396 @ColorInt
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800397 public int ledARGB;
398
399 /**
400 * The number of milliseconds for the LED to be on while it's flashing.
401 * The hardware will do its best approximation.
402 *
403 * @see #FLAG_SHOW_LIGHTS
404 * @see #flags
405 */
406 public int ledOnMS;
407
408 /**
409 * The number of milliseconds for the LED to be off while it's flashing.
410 * The hardware will do its best approximation.
411 *
412 * @see #FLAG_SHOW_LIGHTS
413 * @see #flags
414 */
415 public int ledOffMS;
416
417 /**
418 * Specifies which values should be taken from the defaults.
419 * <p>
420 * To set, OR the desired from {@link #DEFAULT_SOUND},
421 * {@link #DEFAULT_VIBRATE}, {@link #DEFAULT_LIGHTS}. For all default
422 * values, use {@link #DEFAULT_ALL}.
423 * </p>
424 */
425 public int defaults;
426
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800427 /**
428 * Bit to be bitwise-ored into the {@link #flags} field that should be
429 * set if you want the LED on for this notification.
430 * <ul>
431 * <li>To turn the LED off, pass 0 in the alpha channel for colorARGB
432 * or 0 for both ledOnMS and ledOffMS.</li>
433 * <li>To turn the LED on, pass 1 for ledOnMS and 0 for ledOffMS.</li>
434 * <li>To flash the LED, pass the number of milliseconds that it should
435 * be on and off to ledOnMS and ledOffMS.</li>
436 * </ul>
437 * <p>
438 * Since hardware varies, you are not guaranteed that any of the values
439 * you pass are honored exactly. Use the system defaults (TODO) if possible
440 * because they will be set to values that work on any given hardware.
441 * <p>
442 * The alpha channel must be set for forward compatibility.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500443 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800444 */
445 public static final int FLAG_SHOW_LIGHTS = 0x00000001;
446
447 /**
448 * Bit to be bitwise-ored into the {@link #flags} field that should be
449 * set if this notification is in reference to something that is ongoing,
450 * like a phone call. It should not be set if this notification is in
451 * reference to something that happened at a particular point in time,
452 * like a missed phone call.
453 */
454 public static final int FLAG_ONGOING_EVENT = 0x00000002;
455
456 /**
457 * Bit to be bitwise-ored into the {@link #flags} field that if set,
Scott Mainb8b36452009-04-26 15:50:49 -0700458 * the audio will be repeated until the notification is
459 * cancelled or the notification window is opened.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800460 */
461 public static final int FLAG_INSISTENT = 0x00000004;
462
463 /**
464 * Bit to be bitwise-ored into the {@link #flags} field that should be
Griff Hazen293977b2014-04-28 08:37:20 -0700465 * set if you would only like the sound, vibrate and ticker to be played
466 * if the notification was not already showing.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800467 */
468 public static final int FLAG_ONLY_ALERT_ONCE = 0x00000008;
469
470 /**
471 * Bit to be bitwise-ored into the {@link #flags} field that should be
472 * set if the notification should be canceled when it is clicked by the
Daniel Sandler8aa9ae62012-12-04 23:31:47 -0500473 * user.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800474 */
475 public static final int FLAG_AUTO_CANCEL = 0x00000010;
476
477 /**
478 * Bit to be bitwise-ored into the {@link #flags} field that should be
479 * set if the notification should not be canceled when the user clicks
480 * the Clear all button.
481 */
482 public static final int FLAG_NO_CLEAR = 0x00000020;
483
Dianne Hackbornd8a43f62009-08-17 23:33:56 -0700484 /**
485 * Bit to be bitwise-ored into the {@link #flags} field that should be
486 * set if this notification represents a currently running service. This
487 * will normally be set for you by {@link Service#startForeground}.
488 */
489 public static final int FLAG_FOREGROUND_SERVICE = 0x00000040;
490
Daniel Sandlere46cbd32010-06-17 10:35:26 -0400491 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500492 * Obsolete flag indicating high-priority notifications; use the priority field instead.
Joe Malin8d40d042012-11-05 11:36:40 -0800493 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500494 * @deprecated Use {@link #priority} with a positive value.
Daniel Sandlere46cbd32010-06-17 10:35:26 -0400495 */
Aurimas Liutikas514c5ef2016-05-24 15:22:55 -0700496 @Deprecated
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500497 public static final int FLAG_HIGH_PRIORITY = 0x00000080;
Daniel Sandlere46cbd32010-06-17 10:35:26 -0400498
Griff Hazendfcb0802014-02-11 12:00:00 -0800499 /**
500 * Bit to be bitswise-ored into the {@link #flags} field that should be
501 * set if this notification is relevant to the current device only
502 * and it is not recommended that it bridge to other devices.
503 */
504 public static final int FLAG_LOCAL_ONLY = 0x00000100;
505
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700506 /**
507 * Bit to be bitswise-ored into the {@link #flags} field that should be
508 * set if this notification is the group summary for a group of notifications.
509 * Grouped notifications may display in a cluster or stack on devices which
510 * support such rendering. Requires a group key also be set using {@link Builder#setGroup}.
511 */
512 public static final int FLAG_GROUP_SUMMARY = 0x00000200;
513
Julia Reynoldse46bb372016-03-17 11:05:58 -0400514 /**
515 * Bit to be bitswise-ored into the {@link #flags} field that should be
516 * set if this notification is the group summary for an auto-group of notifications.
517 *
518 * @hide
519 */
520 @SystemApi
521 public static final int FLAG_AUTOGROUP_SUMMARY = 0x00000400;
522
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800523 public int flags;
524
Tor Norbyed9273d62013-05-30 15:59:53 -0700525 /** @hide */
526 @IntDef({PRIORITY_DEFAULT,PRIORITY_LOW,PRIORITY_MIN,PRIORITY_HIGH,PRIORITY_MAX})
527 @Retention(RetentionPolicy.SOURCE)
528 public @interface Priority {}
529
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800530 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500531 * Default notification {@link #priority}. If your application does not prioritize its own
532 * notifications, use this value for all notifications.
533 */
534 public static final int PRIORITY_DEFAULT = 0;
535
536 /**
537 * Lower {@link #priority}, for items that are less important. The UI may choose to show these
538 * items smaller, or at a different position in the list, compared with your app's
539 * {@link #PRIORITY_DEFAULT} items.
540 */
541 public static final int PRIORITY_LOW = -1;
542
543 /**
544 * Lowest {@link #priority}; these items might not be shown to the user except under special
545 * circumstances, such as detailed notification logs.
546 */
547 public static final int PRIORITY_MIN = -2;
548
549 /**
550 * Higher {@link #priority}, for more important notifications or alerts. The UI may choose to
551 * show these items larger, or at a different position in notification lists, compared with
552 * your app's {@link #PRIORITY_DEFAULT} items.
553 */
554 public static final int PRIORITY_HIGH = 1;
555
556 /**
557 * Highest {@link #priority}, for your application's most important items that require the
558 * user's prompt attention or input.
559 */
560 public static final int PRIORITY_MAX = 2;
561
562 /**
563 * Relative priority for this notification.
Joe Malin8d40d042012-11-05 11:36:40 -0800564 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500565 * Priority is an indication of how much of the user's valuable attention should be consumed by
566 * this notification. Low-priority notifications may be hidden from the user in certain
567 * situations, while the user might be interrupted for a higher-priority notification. The
Daniel Sandler6738eee2012-11-16 12:03:32 -0500568 * system will make a determination about how to interpret this priority when presenting
569 * the notification.
Chris Wren47c20a12014-06-18 17:27:29 -0400570 *
571 * <p>
572 * A notification that is at least {@link #PRIORITY_HIGH} is more likely to be presented
573 * as a heads-up notification.
574 * </p>
575 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500576 */
Tor Norbyed9273d62013-05-30 15:59:53 -0700577 @Priority
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500578 public int priority;
Joe Malin8d40d042012-11-05 11:36:40 -0800579
Dan Sandler26e81cf2014-05-06 10:01:27 -0400580 /**
581 * Accent color (an ARGB integer like the constants in {@link android.graphics.Color})
582 * to be applied by the standard Style templates when presenting this notification.
583 *
584 * The current template design constructs a colorful header image by overlaying the
585 * {@link #icon} image (stenciled in white) atop a field of this color. Alpha components are
586 * ignored.
587 */
Tor Norbye80756e32015-03-02 09:39:27 -0800588 @ColorInt
Dan Sandler26e81cf2014-05-06 10:01:27 -0400589 public int color = COLOR_DEFAULT;
590
591 /**
592 * Special value of {@link #color} telling the system not to decorate this notification with
593 * any special color but instead use default colors when presenting this notification.
594 */
Tor Norbye80756e32015-03-02 09:39:27 -0800595 @ColorInt
Dan Sandler26e81cf2014-05-06 10:01:27 -0400596 public static final int COLOR_DEFAULT = 0; // AKA Color.TRANSPARENT
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600597
598 /**
Adrian Roos4ff3b122016-02-01 12:26:13 -0800599 * Special value of {@link #color} used as a place holder for an invalid color.
600 */
601 @ColorInt
602 private static final int COLOR_INVALID = 1;
603
604 /**
Adrian Roosc1a80b02016-04-05 14:54:55 -0700605 * Sphere of visibility of this notification, which affects how and when the SystemUI reveals
606 * the notification's presence and contents in untrusted situations (namely, on the secure
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600607 * lockscreen).
608 *
609 * The default level, {@link #VISIBILITY_PRIVATE}, behaves exactly as notifications have always
610 * done on Android: The notification's {@link #icon} and {@link #tickerText} (if available) are
611 * shown in all situations, but the contents are only available if the device is unlocked for
612 * the appropriate user.
613 *
614 * A more permissive policy can be expressed by {@link #VISIBILITY_PUBLIC}; such a notification
615 * can be read even in an "insecure" context (that is, above a secure lockscreen).
616 * To modify the public version of this notification—for example, to redact some portions—see
617 * {@link Builder#setPublicVersion(Notification)}.
618 *
619 * Finally, a notification can be made {@link #VISIBILITY_SECRET}, which will suppress its icon
620 * and ticker until the user has bypassed the lockscreen.
621 */
622 public int visibility;
623
Griff Hazenfc3922d2014-08-20 11:56:44 -0700624 /**
625 * Notification visibility: Show this notification in its entirety on all lockscreens.
626 *
627 * {@see #visibility}
628 */
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600629 public static final int VISIBILITY_PUBLIC = 1;
Griff Hazenfc3922d2014-08-20 11:56:44 -0700630
631 /**
632 * Notification visibility: Show this notification on all lockscreens, but conceal sensitive or
633 * private information on secure lockscreens.
634 *
635 * {@see #visibility}
636 */
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600637 public static final int VISIBILITY_PRIVATE = 0;
Griff Hazenfc3922d2014-08-20 11:56:44 -0700638
639 /**
640 * Notification visibility: Do not reveal any part of this notification on a secure lockscreen.
641 *
642 * {@see #visibility}
643 */
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600644 public static final int VISIBILITY_SECRET = -1;
645
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500646 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400647 * Notification category: incoming call (voice or video) or similar synchronous communication request.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500648 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400649 public static final String CATEGORY_CALL = "call";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500650
651 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400652 * Notification category: incoming direct message (SMS, instant message, etc.).
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500653 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400654 public static final String CATEGORY_MESSAGE = "msg";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500655
656 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400657 * Notification category: asynchronous bulk message (email).
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500658 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400659 public static final String CATEGORY_EMAIL = "email";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500660
661 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400662 * Notification category: calendar event.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500663 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400664 public static final String CATEGORY_EVENT = "event";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500665
666 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400667 * Notification category: promotion or advertisement.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500668 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400669 public static final String CATEGORY_PROMO = "promo";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500670
671 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400672 * Notification category: alarm or timer.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500673 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400674 public static final String CATEGORY_ALARM = "alarm";
675
676 /**
677 * Notification category: progress of a long-running background operation.
678 */
679 public static final String CATEGORY_PROGRESS = "progress";
680
681 /**
682 * Notification category: social network or sharing update.
683 */
684 public static final String CATEGORY_SOCIAL = "social";
685
686 /**
687 * Notification category: error in background operation or authentication status.
688 */
689 public static final String CATEGORY_ERROR = "err";
690
691 /**
692 * Notification category: media transport control for playback.
693 */
694 public static final String CATEGORY_TRANSPORT = "transport";
695
696 /**
697 * Notification category: system or device status update. Reserved for system use.
698 */
699 public static final String CATEGORY_SYSTEM = "sys";
700
701 /**
702 * Notification category: indication of running background service.
703 */
704 public static final String CATEGORY_SERVICE = "service";
705
706 /**
John Spurlock0a69c8c2014-03-21 13:30:57 -0400707 * Notification category: a specific, timely recommendation for a single thing.
708 * For example, a news app might want to recommend a news story it believes the user will
709 * want to read next.
710 */
711 public static final String CATEGORY_RECOMMENDATION = "recommendation";
712
713 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400714 * Notification category: ongoing information about device or contextual status.
715 */
716 public static final String CATEGORY_STATUS = "status";
717
718 /**
John Spurlock24d3dad2015-04-02 12:24:02 -0400719 * Notification category: user-scheduled reminder.
720 */
721 public static final String CATEGORY_REMINDER = "reminder";
722
723 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400724 * One of the predefined notification categories (see the <code>CATEGORY_*</code> constants)
725 * that best describes this Notification. May be used by the system for ranking and filtering.
726 */
727 public String category;
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500728
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700729 private String mGroupKey;
730
731 /**
732 * Get the key used to group this notification into a cluster or stack
733 * with other notifications on devices which support such rendering.
734 */
735 public String getGroup() {
736 return mGroupKey;
737 }
738
739 private String mSortKey;
740
741 /**
742 * Get a sort key that orders this notification among other notifications from the
743 * same package. This can be useful if an external sort was already applied and an app
744 * would like to preserve this. Notifications will be sorted lexicographically using this
745 * value, although providing different priorities in addition to providing sort key may
746 * cause this value to be ignored.
747 *
748 * <p>This sort key can also be used to order members of a notification group. See
749 * {@link Builder#setGroup}.
750 *
751 * @see String#compareTo(String)
752 */
753 public String getSortKey() {
754 return mSortKey;
755 }
756
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500757 /**
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400758 * Additional semantic data to be carried around with this Notification.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400759 * <p>
760 * The extras keys defined here are intended to capture the original inputs to {@link Builder}
761 * APIs, and are intended to be used by
762 * {@link android.service.notification.NotificationListenerService} implementations to extract
763 * detailed information from notification objects.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500764 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400765 public Bundle extras = new Bundle();
766
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400767 /**
Felipe Lemedd85da62016-06-28 11:29:54 -0700768 * All pending intents in the notification as the system needs to be able to access them but
769 * touching the extras bundle in the system process is not safe because the bundle may contain
Svet Ganovddb94882016-06-23 19:55:24 -0700770 * custom parcelable objects.
771 *
772 * @hide
773 */
Felipe Lemedd85da62016-06-28 11:29:54 -0700774 public ArraySet<PendingIntent> allPendingIntents;
Svet Ganovddb94882016-06-23 19:55:24 -0700775
776 /**
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400777 * {@link #extras} key: this is the title of the notification,
778 * as supplied to {@link Builder#setContentTitle(CharSequence)}.
779 */
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -0500780 public static final String EXTRA_TITLE = "android.title";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400781
782 /**
783 * {@link #extras} key: this is the title of the notification when shown in expanded form,
784 * e.g. as supplied to {@link BigTextStyle#setBigContentTitle(CharSequence)}.
785 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400786 public static final String EXTRA_TITLE_BIG = EXTRA_TITLE + ".big";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400787
788 /**
789 * {@link #extras} key: this is the main text payload, as supplied to
790 * {@link Builder#setContentText(CharSequence)}.
791 */
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -0500792 public static final String EXTRA_TEXT = "android.text";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400793
794 /**
795 * {@link #extras} key: this is a third line of text, as supplied to
796 * {@link Builder#setSubText(CharSequence)}.
797 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400798 public static final String EXTRA_SUB_TEXT = "android.subText";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400799
800 /**
Adrian Roose458aa82015-12-08 16:17:19 -0800801 * {@link #extras} key: this is the remote input history, as supplied to
802 * {@link Builder#setRemoteInputHistory(CharSequence[])}.
Adrian Roos005e7742016-04-13 15:02:20 -0700803 *
804 * Apps can fill this through {@link Builder#setRemoteInputHistory(CharSequence[])}
805 * with the most recent inputs that have been sent through a {@link RemoteInput} of this
806 * Notification and are expected to clear it once the it is no longer relevant (e.g. for chat
807 * notifications once the other party has responded).
808 *
809 * The extra with this key is of type CharSequence[] and contains the most recent entry at
810 * the 0 index, the second most recent at the 1 index, etc.
811 *
812 * @see Builder#setRemoteInputHistory(CharSequence[])
Adrian Roose458aa82015-12-08 16:17:19 -0800813 */
814 public static final String EXTRA_REMOTE_INPUT_HISTORY = "android.remoteInputHistory";
815
816 /**
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400817 * {@link #extras} key: this is a small piece of additional text as supplied to
818 * {@link Builder#setContentInfo(CharSequence)}.
819 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400820 public static final String EXTRA_INFO_TEXT = "android.infoText";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400821
822 /**
823 * {@link #extras} key: this is a line of summary information intended to be shown
824 * alongside expanded notifications, as supplied to (e.g.)
825 * {@link BigTextStyle#setSummaryText(CharSequence)}.
826 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400827 public static final String EXTRA_SUMMARY_TEXT = "android.summaryText";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400828
829 /**
Christoph Studer4600f9b2014-07-22 22:44:43 +0200830 * {@link #extras} key: this is the longer text shown in the big form of a
831 * {@link BigTextStyle} notification, as supplied to
832 * {@link BigTextStyle#bigText(CharSequence)}.
833 */
834 public static final String EXTRA_BIG_TEXT = "android.bigText";
835
836 /**
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400837 * {@link #extras} key: this is the resource ID of the notification's main small icon, as
838 * supplied to {@link Builder#setSmallIcon(int)}.
839 */
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -0500840 public static final String EXTRA_SMALL_ICON = "android.icon";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400841
842 /**
843 * {@link #extras} key: this is a bitmap to be used instead of the small icon when showing the
844 * notification payload, as
845 * supplied to {@link Builder#setLargeIcon(android.graphics.Bitmap)}.
846 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400847 public static final String EXTRA_LARGE_ICON = "android.largeIcon";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400848
849 /**
850 * {@link #extras} key: this is a bitmap to be used instead of the one from
851 * {@link Builder#setLargeIcon(android.graphics.Bitmap)} when the notification is
852 * shown in its expanded form, as supplied to
853 * {@link BigPictureStyle#bigLargeIcon(android.graphics.Bitmap)}.
854 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400855 public static final String EXTRA_LARGE_ICON_BIG = EXTRA_LARGE_ICON + ".big";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400856
857 /**
858 * {@link #extras} key: this is the progress value supplied to
859 * {@link Builder#setProgress(int, int, boolean)}.
860 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400861 public static final String EXTRA_PROGRESS = "android.progress";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400862
863 /**
864 * {@link #extras} key: this is the maximum value supplied to
865 * {@link Builder#setProgress(int, int, boolean)}.
866 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400867 public static final String EXTRA_PROGRESS_MAX = "android.progressMax";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400868
869 /**
870 * {@link #extras} key: whether the progress bar is indeterminate, supplied to
871 * {@link Builder#setProgress(int, int, boolean)}.
872 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400873 public static final String EXTRA_PROGRESS_INDETERMINATE = "android.progressIndeterminate";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400874
875 /**
876 * {@link #extras} key: whether {@link #when} should be shown as a count-up timer (specifically
877 * a {@link android.widget.Chronometer}) instead of a timestamp, as supplied to
878 * {@link Builder#setUsesChronometer(boolean)}.
879 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400880 public static final String EXTRA_SHOW_CHRONOMETER = "android.showChronometer";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400881
882 /**
Selim Cinek81c23aa2016-02-25 16:23:13 -0800883 * {@link #extras} key: whether the chronometer set on the notification should count down
884 * instead of counting up. Is only relevant if key {@link #EXTRA_SHOW_CHRONOMETER} is present.
Adrian Roos96b7e202016-05-17 13:50:38 -0700885 * This extra is a boolean. The default is false.
Selim Cinek81c23aa2016-02-25 16:23:13 -0800886 */
Adrian Roos96b7e202016-05-17 13:50:38 -0700887 public static final String EXTRA_CHRONOMETER_COUNT_DOWN = "android.chronometerCountDown";
Selim Cinek81c23aa2016-02-25 16:23:13 -0800888
889 /**
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400890 * {@link #extras} key: whether {@link #when} should be shown,
891 * as supplied to {@link Builder#setShowWhen(boolean)}.
892 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400893 public static final String EXTRA_SHOW_WHEN = "android.showWhen";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400894
895 /**
896 * {@link #extras} key: this is a bitmap to be shown in {@link BigPictureStyle} expanded
897 * notifications, supplied to {@link BigPictureStyle#bigPicture(android.graphics.Bitmap)}.
898 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400899 public static final String EXTRA_PICTURE = "android.picture";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400900
901 /**
902 * {@link #extras} key: An array of CharSequences to show in {@link InboxStyle} expanded
903 * notifications, each of which was supplied to {@link InboxStyle#addLine(CharSequence)}.
904 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400905 public static final String EXTRA_TEXT_LINES = "android.textLines";
Dan Sandler842dd772014-05-15 09:36:47 -0400906
907 /**
908 * {@link #extras} key: A string representing the name of the specific
909 * {@link android.app.Notification.Style} used to create this notification.
910 */
Chris Wren91ad5632013-06-05 15:05:57 -0400911 public static final String EXTRA_TEMPLATE = "android.template";
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400912
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400913 /**
Chris Wrene6c48932014-09-29 17:19:27 -0400914 * {@link #extras} key: A String array containing the people that this notification relates to,
915 * each of which was supplied to {@link Builder#addPerson(String)}.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400916 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400917 public static final String EXTRA_PEOPLE = "android.people";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500918
919 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400920 * Allow certain system-generated notifications to appear before the device is provisioned.
921 * Only available to notifications coming from the android package.
922 * @hide
923 */
924 public static final String EXTRA_ALLOW_DURING_SETUP = "android.allowDuringSetup";
925
926 /**
Jose Limae9e3b3b2014-05-18 23:44:50 -0700927 * {@link #extras} key: A
928 * {@link android.content.ContentUris content URI} pointing to an image that can be displayed
929 * in the background when the notification is selected. The URI must point to an image stream
930 * suitable for passing into
931 * {@link android.graphics.BitmapFactory#decodeStream(java.io.InputStream)
932 * BitmapFactory.decodeStream}; all other content types will be ignored. The content provider
933 * URI used for this purpose must require no permissions to read the image data.
934 */
935 public static final String EXTRA_BACKGROUND_IMAGE_URI = "android.backgroundImageUri";
936
937 /**
Dan Sandler842dd772014-05-15 09:36:47 -0400938 * {@link #extras} key: A
Jeff Browndba34ba2014-06-24 20:46:03 -0700939 * {@link android.media.session.MediaSession.Token} associated with a
Dan Sandler842dd772014-05-15 09:36:47 -0400940 * {@link android.app.Notification.MediaStyle} notification.
941 */
942 public static final String EXTRA_MEDIA_SESSION = "android.mediaSession";
943
944 /**
Bryan Mawhinneye191f902014-07-22 12:50:09 +0100945 * {@link #extras} key: the indices of actions to be shown in the compact view,
946 * as supplied to (e.g.) {@link MediaStyle#setShowActionsInCompactView(int...)}.
947 */
948 public static final String EXTRA_COMPACT_ACTIONS = "android.compactActions";
949
Christoph Studer943aa672014-08-03 20:31:16 +0200950 /**
Alex Hillsfc737de2016-03-23 17:33:02 -0400951 * {@link #extras} key: the username to be displayed for all messages sent by the user including
952 * direct replies
Adrian Roos96b7e202016-05-17 13:50:38 -0700953 * {@link android.app.Notification.MessagingStyle} notification. This extra is a
954 * {@link CharSequence}
Alex Hillsfc737de2016-03-23 17:33:02 -0400955 */
956 public static final String EXTRA_SELF_DISPLAY_NAME = "android.selfDisplayName";
957
958 /**
Adrian Roos96b7e202016-05-17 13:50:38 -0700959 * {@link #extras} key: a {@link CharSequence} to be displayed as the title to a conversation
Alex Hillsd9b04d92016-04-11 16:38:16 -0400960 * represented by a {@link android.app.Notification.MessagingStyle}
Alex Hillsfc737de2016-03-23 17:33:02 -0400961 */
Alex Hillsd9b04d92016-04-11 16:38:16 -0400962 public static final String EXTRA_CONVERSATION_TITLE = "android.conversationTitle";
Alex Hillsfc737de2016-03-23 17:33:02 -0400963
964 /**
965 * {@link #extras} key: an array of {@link android.app.Notification.MessagingStyle.Message}
966 * bundles provided by a
Adrian Roos96b7e202016-05-17 13:50:38 -0700967 * {@link android.app.Notification.MessagingStyle} notification. This extra is a parcelable
968 * array of bundles.
Alex Hillsfc737de2016-03-23 17:33:02 -0400969 */
970 public static final String EXTRA_MESSAGES = "android.messages";
971
972 /**
Kenny Guy8942bcd2014-09-08 21:09:47 +0100973 * {@link #extras} key: the user that built the notification.
974 *
975 * @hide
976 */
977 public static final String EXTRA_ORIGINATING_USERID = "android.originatingUserId";
978
979 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400980 * @hide
981 */
982 public static final String EXTRA_BUILDER_APPLICATION_INFO = "android.appInfo";
983
Selim Cinek247fa012016-02-18 09:50:48 -0800984 /**
985 * @hide
986 */
987 public static final String EXTRA_CONTAINS_CUSTOM_VIEW = "android.contains.customView";
988
Dan Sandler80eaa592016-04-14 23:34:54 -0400989 /** @hide */
990 @SystemApi
Dan Sandler732bd6c2016-04-12 14:20:32 -0400991 public static final String EXTRA_SUBSTITUTE_APP_NAME = "android.substName";
992
Dan Sandlerd63f9322015-05-06 15:18:49 -0400993 private Icon mSmallIcon;
994 private Icon mLargeIcon;
995
Julia Reynoldsb5e44b72016-08-16 15:00:25 -0400996 private String mChannelId;
997
Chris Wren51c75102013-07-16 20:49:17 -0400998 /**
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400999 * Structure to encapsulate a named action that can be shown as part of this notification.
1000 * It must include an icon, a label, and a {@link PendingIntent} to be fired when the action is
1001 * selected by the user.
1002 * <p>
Griff Hazen959591e2014-05-15 22:26:18 -07001003 * Apps should use {@link Notification.Builder#addAction(int, CharSequence, PendingIntent)}
1004 * or {@link Notification.Builder#addAction(Notification.Action)}
1005 * to attach actions.
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001006 */
Daniel Sandlerea2a3172013-02-20 22:24:20 -05001007 public static class Action implements Parcelable {
Griff Hazen959591e2014-05-15 22:26:18 -07001008 private final Bundle mExtras;
Dan Sandler86647982015-05-13 23:41:13 -04001009 private Icon mIcon;
Griff Hazen61a9e862014-05-22 16:05:19 -07001010 private final RemoteInput[] mRemoteInputs;
Alex Hills42b0c4d2016-04-26 13:35:36 -04001011 private boolean mAllowGeneratedReplies = false;
Griff Hazen959591e2014-05-15 22:26:18 -07001012
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001013 /**
1014 * Small icon representing the action.
Dan Sandler86647982015-05-13 23:41:13 -04001015 *
1016 * @deprecated Use {@link Action#getIcon()} instead.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001017 */
Dan Sandler86647982015-05-13 23:41:13 -04001018 @Deprecated
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001019 public int icon;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001020
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001021 /**
1022 * Title of the action.
1023 */
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001024 public CharSequence title;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001025
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001026 /**
1027 * Intent to send when the user invokes this action. May be null, in which case the action
1028 * may be rendered in a disabled presentation by the system UI.
1029 */
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001030 public PendingIntent actionIntent;
Griff Hazen959591e2014-05-15 22:26:18 -07001031
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001032 private Action(Parcel in) {
Dan Sandler86647982015-05-13 23:41:13 -04001033 if (in.readInt() != 0) {
1034 mIcon = Icon.CREATOR.createFromParcel(in);
Dan Sandler68079d52015-07-22 10:45:30 -04001035 if (mIcon.getType() == Icon.TYPE_RESOURCE) {
1036 icon = mIcon.getResId();
1037 }
Dan Sandler86647982015-05-13 23:41:13 -04001038 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001039 title = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
1040 if (in.readInt() == 1) {
1041 actionIntent = PendingIntent.CREATOR.createFromParcel(in);
1042 }
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001043 mExtras = Bundle.setDefusable(in.readBundle(), true);
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001044 mRemoteInputs = in.createTypedArray(RemoteInput.CREATOR);
Alex Hills42b0c4d2016-04-26 13:35:36 -04001045 mAllowGeneratedReplies = in.readInt() == 1;
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001046 }
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001047
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001048 /**
Dan Sandler86647982015-05-13 23:41:13 -04001049 * @deprecated Use {@link android.app.Notification.Action.Builder}.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001050 */
Dan Sandler86647982015-05-13 23:41:13 -04001051 @Deprecated
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001052 public Action(int icon, CharSequence title, PendingIntent intent) {
Alex Hills42b0c4d2016-04-26 13:35:36 -04001053 this(Icon.createWithResource("", icon), title, intent, new Bundle(), null, false);
Griff Hazen959591e2014-05-15 22:26:18 -07001054 }
1055
Dan Sandler86647982015-05-13 23:41:13 -04001056 private Action(Icon icon, CharSequence title, PendingIntent intent, Bundle extras,
Alex Hills42b0c4d2016-04-26 13:35:36 -04001057 RemoteInput[] remoteInputs, boolean allowGeneratedReplies) {
Dan Sandler86647982015-05-13 23:41:13 -04001058 this.mIcon = icon;
Gus Prevasf5bff46f2015-08-24 10:34:28 -04001059 if (icon != null && icon.getType() == Icon.TYPE_RESOURCE) {
1060 this.icon = icon.getResId();
1061 }
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001062 this.title = title;
1063 this.actionIntent = intent;
Griff Hazen959591e2014-05-15 22:26:18 -07001064 this.mExtras = extras != null ? extras : new Bundle();
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001065 this.mRemoteInputs = remoteInputs;
Alex Hills42b0c4d2016-04-26 13:35:36 -04001066 this.mAllowGeneratedReplies = allowGeneratedReplies;
Griff Hazen959591e2014-05-15 22:26:18 -07001067 }
1068
1069 /**
Dan Sandler86647982015-05-13 23:41:13 -04001070 * Return an icon representing the action.
1071 */
1072 public Icon getIcon() {
1073 if (mIcon == null && icon != 0) {
1074 // you snuck an icon in here without using the builder; let's try to keep it
1075 mIcon = Icon.createWithResource("", icon);
1076 }
1077 return mIcon;
1078 }
1079
1080 /**
Griff Hazen959591e2014-05-15 22:26:18 -07001081 * Get additional metadata carried around with this Action.
1082 */
1083 public Bundle getExtras() {
1084 return mExtras;
1085 }
1086
1087 /**
Alex Hills42b0c4d2016-04-26 13:35:36 -04001088 * Return whether the platform should automatically generate possible replies for this
1089 * {@link Action}
1090 */
1091 public boolean getAllowGeneratedReplies() {
1092 return mAllowGeneratedReplies;
1093 }
1094
1095 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001096 * Get the list of inputs to be collected from the user when this action is sent.
1097 * May return null if no remote inputs were added.
1098 */
1099 public RemoteInput[] getRemoteInputs() {
1100 return mRemoteInputs;
1101 }
1102
1103 /**
Griff Hazen959591e2014-05-15 22:26:18 -07001104 * Builder class for {@link Action} objects.
1105 */
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001106 public static final class Builder {
Dan Sandler86647982015-05-13 23:41:13 -04001107 private final Icon mIcon;
Griff Hazen959591e2014-05-15 22:26:18 -07001108 private final CharSequence mTitle;
1109 private final PendingIntent mIntent;
Alex Hills42b0c4d2016-04-26 13:35:36 -04001110 private boolean mAllowGeneratedReplies;
Griff Hazen959591e2014-05-15 22:26:18 -07001111 private final Bundle mExtras;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001112 private ArrayList<RemoteInput> mRemoteInputs;
Griff Hazen959591e2014-05-15 22:26:18 -07001113
1114 /**
1115 * Construct a new builder for {@link Action} object.
1116 * @param icon icon to show for this action
1117 * @param title the title of the action
1118 * @param intent the {@link PendingIntent} to fire when users trigger this action
1119 */
Dan Sandler86647982015-05-13 23:41:13 -04001120 @Deprecated
Griff Hazen959591e2014-05-15 22:26:18 -07001121 public Builder(int icon, CharSequence title, PendingIntent intent) {
Dan Sandler86647982015-05-13 23:41:13 -04001122 this(Icon.createWithResource("", icon), title, intent, new Bundle(), null);
1123 }
1124
1125 /**
1126 * Construct a new builder for {@link Action} object.
1127 * @param icon icon to show for this action
1128 * @param title the title of the action
1129 * @param intent the {@link PendingIntent} to fire when users trigger this action
1130 */
1131 public Builder(Icon icon, CharSequence title, PendingIntent intent) {
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001132 this(icon, title, intent, new Bundle(), null);
Griff Hazen959591e2014-05-15 22:26:18 -07001133 }
1134
1135 /**
1136 * Construct a new builder for {@link Action} object using the fields from an
1137 * {@link Action}.
1138 * @param action the action to read fields from.
1139 */
1140 public Builder(Action action) {
Dan Sandler86647982015-05-13 23:41:13 -04001141 this(action.getIcon(), action.title, action.actionIntent, new Bundle(action.mExtras),
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001142 action.getRemoteInputs());
Griff Hazen959591e2014-05-15 22:26:18 -07001143 }
1144
Dan Sandler86647982015-05-13 23:41:13 -04001145 private Builder(Icon icon, CharSequence title, PendingIntent intent, Bundle extras,
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001146 RemoteInput[] remoteInputs) {
Griff Hazen959591e2014-05-15 22:26:18 -07001147 mIcon = icon;
1148 mTitle = title;
1149 mIntent = intent;
1150 mExtras = extras;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001151 if (remoteInputs != null) {
1152 mRemoteInputs = new ArrayList<RemoteInput>(remoteInputs.length);
1153 Collections.addAll(mRemoteInputs, remoteInputs);
1154 }
Griff Hazen959591e2014-05-15 22:26:18 -07001155 }
1156
1157 /**
1158 * Merge additional metadata into this builder.
1159 *
1160 * <p>Values within the Bundle will replace existing extras values in this Builder.
1161 *
1162 * @see Notification.Action#extras
1163 */
1164 public Builder addExtras(Bundle extras) {
1165 if (extras != null) {
1166 mExtras.putAll(extras);
1167 }
1168 return this;
1169 }
1170
1171 /**
1172 * Get the metadata Bundle used by this Builder.
1173 *
1174 * <p>The returned Bundle is shared with this Builder.
1175 */
1176 public Bundle getExtras() {
1177 return mExtras;
1178 }
1179
1180 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001181 * Add an input to be collected from the user when this action is sent.
1182 * Response values can be retrieved from the fired intent by using the
1183 * {@link RemoteInput#getResultsFromIntent} function.
1184 * @param remoteInput a {@link RemoteInput} to add to the action
1185 * @return this object for method chaining
1186 */
1187 public Builder addRemoteInput(RemoteInput remoteInput) {
1188 if (mRemoteInputs == null) {
1189 mRemoteInputs = new ArrayList<RemoteInput>();
1190 }
1191 mRemoteInputs.add(remoteInput);
1192 return this;
1193 }
1194
1195 /**
Alex Hills42b0c4d2016-04-26 13:35:36 -04001196 * Set whether the platform should automatically generate possible replies to add to
1197 * {@link RemoteInput#getChoices()}. If the {@link Action} doesn't have a
1198 * {@link RemoteInput}, this has no effect.
1199 * @param allowGeneratedReplies {@code true} to allow generated replies, {@code false}
1200 * otherwise
1201 * @return this object for method chaining
1202 * The default value is {@code false}
1203 */
1204 public Builder setAllowGeneratedReplies(boolean allowGeneratedReplies) {
1205 mAllowGeneratedReplies = allowGeneratedReplies;
1206 return this;
1207 }
1208
1209 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001210 * Apply an extender to this action builder. Extenders may be used to add
1211 * metadata or change options on this builder.
1212 */
Griff Hazen61a9e862014-05-22 16:05:19 -07001213 public Builder extend(Extender extender) {
1214 extender.extend(this);
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001215 return this;
1216 }
1217
1218 /**
Griff Hazen959591e2014-05-15 22:26:18 -07001219 * Combine all of the options that have been set and return a new {@link Action}
1220 * object.
1221 * @return the built action
1222 */
1223 public Action build() {
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001224 RemoteInput[] remoteInputs = mRemoteInputs != null
1225 ? mRemoteInputs.toArray(new RemoteInput[mRemoteInputs.size()]) : null;
Alex Hills42b0c4d2016-04-26 13:35:36 -04001226 return new Action(mIcon, mTitle, mIntent, mExtras, remoteInputs,
1227 mAllowGeneratedReplies);
Griff Hazen959591e2014-05-15 22:26:18 -07001228 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001229 }
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001230
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001231 @Override
1232 public Action clone() {
1233 return new Action(
Dan Sandler86647982015-05-13 23:41:13 -04001234 getIcon(),
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001235 title,
1236 actionIntent, // safe to alias
Julia Reynolds53c934c2016-09-16 14:03:43 -04001237 mExtras == null ? new Bundle() : new Bundle(mExtras),
Alex Hills42b0c4d2016-04-26 13:35:36 -04001238 getRemoteInputs(),
1239 getAllowGeneratedReplies());
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001240 }
1241 @Override
1242 public int describeContents() {
1243 return 0;
1244 }
1245 @Override
1246 public void writeToParcel(Parcel out, int flags) {
Dan Sandler86647982015-05-13 23:41:13 -04001247 final Icon ic = getIcon();
1248 if (ic != null) {
1249 out.writeInt(1);
1250 ic.writeToParcel(out, 0);
1251 } else {
1252 out.writeInt(0);
1253 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001254 TextUtils.writeToParcel(title, out, flags);
1255 if (actionIntent != null) {
1256 out.writeInt(1);
1257 actionIntent.writeToParcel(out, flags);
1258 } else {
1259 out.writeInt(0);
1260 }
Griff Hazen959591e2014-05-15 22:26:18 -07001261 out.writeBundle(mExtras);
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001262 out.writeTypedArray(mRemoteInputs, flags);
Alex Hills42b0c4d2016-04-26 13:35:36 -04001263 out.writeInt(mAllowGeneratedReplies ? 1 : 0);
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001264 }
Griff Hazen959591e2014-05-15 22:26:18 -07001265 public static final Parcelable.Creator<Action> CREATOR =
1266 new Parcelable.Creator<Action>() {
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001267 public Action createFromParcel(Parcel in) {
1268 return new Action(in);
1269 }
1270 public Action[] newArray(int size) {
1271 return new Action[size];
1272 }
1273 };
Griff Hazen61a9e862014-05-22 16:05:19 -07001274
1275 /**
1276 * Extender interface for use with {@link Builder#extend}. Extenders may be used to add
1277 * metadata or change options on an action builder.
1278 */
1279 public interface Extender {
1280 /**
1281 * Apply this extender to a notification action builder.
1282 * @param builder the builder to be modified.
1283 * @return the build object for chaining.
1284 */
1285 public Builder extend(Builder builder);
1286 }
1287
1288 /**
1289 * Wearable extender for notification actions. To add extensions to an action,
1290 * create a new {@link android.app.Notification.Action.WearableExtender} object using
1291 * the {@code WearableExtender()} constructor and apply it to a
1292 * {@link android.app.Notification.Action.Builder} using
1293 * {@link android.app.Notification.Action.Builder#extend}.
1294 *
1295 * <pre class="prettyprint">
1296 * Notification.Action action = new Notification.Action.Builder(
1297 * R.drawable.archive_all, "Archive all", actionIntent)
Griff Hazen14f57992014-05-26 09:07:14 -07001298 * .extend(new Notification.Action.WearableExtender()
Griff Hazen61a9e862014-05-22 16:05:19 -07001299 * .setAvailableOffline(false))
Griff Hazen14f57992014-05-26 09:07:14 -07001300 * .build();</pre>
Griff Hazen61a9e862014-05-22 16:05:19 -07001301 */
1302 public static final class WearableExtender implements Extender {
1303 /** Notification action extra which contains wearable extensions */
1304 private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS";
1305
Pete Gastaf6781d2014-10-07 15:17:05 -04001306 // Keys within EXTRA_WEARABLE_EXTENSIONS for wearable options.
Griff Hazen61a9e862014-05-22 16:05:19 -07001307 private static final String KEY_FLAGS = "flags";
Pete Gastaf6781d2014-10-07 15:17:05 -04001308 private static final String KEY_IN_PROGRESS_LABEL = "inProgressLabel";
1309 private static final String KEY_CONFIRM_LABEL = "confirmLabel";
1310 private static final String KEY_CANCEL_LABEL = "cancelLabel";
Griff Hazen61a9e862014-05-22 16:05:19 -07001311
1312 // Flags bitwise-ored to mFlags
1313 private static final int FLAG_AVAILABLE_OFFLINE = 0x1;
Alex Hills9ab3a232016-04-05 14:54:56 -04001314 private static final int FLAG_HINT_LAUNCHES_ACTIVITY = 1 << 1;
Alex Hills9f087612016-06-07 09:08:59 -04001315 private static final int FLAG_HINT_DISPLAY_INLINE = 1 << 2;
Griff Hazen61a9e862014-05-22 16:05:19 -07001316
1317 // Default value for flags integer
1318 private static final int DEFAULT_FLAGS = FLAG_AVAILABLE_OFFLINE;
1319
1320 private int mFlags = DEFAULT_FLAGS;
1321
Pete Gastaf6781d2014-10-07 15:17:05 -04001322 private CharSequence mInProgressLabel;
1323 private CharSequence mConfirmLabel;
1324 private CharSequence mCancelLabel;
1325
Griff Hazen61a9e862014-05-22 16:05:19 -07001326 /**
1327 * Create a {@link android.app.Notification.Action.WearableExtender} with default
1328 * options.
1329 */
1330 public WearableExtender() {
1331 }
1332
1333 /**
1334 * Create a {@link android.app.Notification.Action.WearableExtender} by reading
1335 * wearable options present in an existing notification action.
1336 * @param action the notification action to inspect.
1337 */
1338 public WearableExtender(Action action) {
1339 Bundle wearableBundle = action.getExtras().getBundle(EXTRA_WEARABLE_EXTENSIONS);
1340 if (wearableBundle != null) {
1341 mFlags = wearableBundle.getInt(KEY_FLAGS, DEFAULT_FLAGS);
Pete Gastaf6781d2014-10-07 15:17:05 -04001342 mInProgressLabel = wearableBundle.getCharSequence(KEY_IN_PROGRESS_LABEL);
1343 mConfirmLabel = wearableBundle.getCharSequence(KEY_CONFIRM_LABEL);
1344 mCancelLabel = wearableBundle.getCharSequence(KEY_CANCEL_LABEL);
Griff Hazen61a9e862014-05-22 16:05:19 -07001345 }
1346 }
1347
1348 /**
1349 * Apply wearable extensions to a notification action that is being built. This is
1350 * typically called by the {@link android.app.Notification.Action.Builder#extend}
1351 * method of {@link android.app.Notification.Action.Builder}.
1352 */
1353 @Override
1354 public Action.Builder extend(Action.Builder builder) {
1355 Bundle wearableBundle = new Bundle();
1356
1357 if (mFlags != DEFAULT_FLAGS) {
1358 wearableBundle.putInt(KEY_FLAGS, mFlags);
1359 }
Pete Gastaf6781d2014-10-07 15:17:05 -04001360 if (mInProgressLabel != null) {
1361 wearableBundle.putCharSequence(KEY_IN_PROGRESS_LABEL, mInProgressLabel);
1362 }
1363 if (mConfirmLabel != null) {
1364 wearableBundle.putCharSequence(KEY_CONFIRM_LABEL, mConfirmLabel);
1365 }
1366 if (mCancelLabel != null) {
1367 wearableBundle.putCharSequence(KEY_CANCEL_LABEL, mCancelLabel);
1368 }
Griff Hazen61a9e862014-05-22 16:05:19 -07001369
1370 builder.getExtras().putBundle(EXTRA_WEARABLE_EXTENSIONS, wearableBundle);
1371 return builder;
1372 }
1373
1374 @Override
1375 public WearableExtender clone() {
1376 WearableExtender that = new WearableExtender();
1377 that.mFlags = this.mFlags;
Pete Gastaf6781d2014-10-07 15:17:05 -04001378 that.mInProgressLabel = this.mInProgressLabel;
1379 that.mConfirmLabel = this.mConfirmLabel;
1380 that.mCancelLabel = this.mCancelLabel;
Griff Hazen61a9e862014-05-22 16:05:19 -07001381 return that;
1382 }
1383
1384 /**
1385 * Set whether this action is available when the wearable device is not connected to
1386 * a companion device. The user can still trigger this action when the wearable device is
1387 * offline, but a visual hint will indicate that the action may not be available.
1388 * Defaults to true.
1389 */
1390 public WearableExtender setAvailableOffline(boolean availableOffline) {
1391 setFlag(FLAG_AVAILABLE_OFFLINE, availableOffline);
1392 return this;
1393 }
1394
1395 /**
1396 * Get whether this action is available when the wearable device is not connected to
1397 * a companion device. The user can still trigger this action when the wearable device is
1398 * offline, but a visual hint will indicate that the action may not be available.
1399 * Defaults to true.
1400 */
1401 public boolean isAvailableOffline() {
1402 return (mFlags & FLAG_AVAILABLE_OFFLINE) != 0;
1403 }
1404
1405 private void setFlag(int mask, boolean value) {
1406 if (value) {
1407 mFlags |= mask;
1408 } else {
1409 mFlags &= ~mask;
1410 }
1411 }
Pete Gastaf6781d2014-10-07 15:17:05 -04001412
1413 /**
1414 * Set a label to display while the wearable is preparing to automatically execute the
1415 * action. This is usually a 'ing' verb ending in ellipsis like "Sending..."
1416 *
1417 * @param label the label to display while the action is being prepared to execute
1418 * @return this object for method chaining
1419 */
1420 public WearableExtender setInProgressLabel(CharSequence label) {
1421 mInProgressLabel = label;
1422 return this;
1423 }
1424
1425 /**
1426 * Get the label to display while the wearable is preparing to automatically execute
1427 * the action. This is usually a 'ing' verb ending in ellipsis like "Sending..."
1428 *
1429 * @return the label to display while the action is being prepared to execute
1430 */
1431 public CharSequence getInProgressLabel() {
1432 return mInProgressLabel;
1433 }
1434
1435 /**
1436 * Set a label to display to confirm that the action should be executed.
1437 * This is usually an imperative verb like "Send".
1438 *
1439 * @param label the label to confirm the action should be executed
1440 * @return this object for method chaining
1441 */
1442 public WearableExtender setConfirmLabel(CharSequence label) {
1443 mConfirmLabel = label;
1444 return this;
1445 }
1446
1447 /**
1448 * Get the label to display to confirm that the action should be executed.
1449 * This is usually an imperative verb like "Send".
1450 *
1451 * @return the label to confirm the action should be executed
1452 */
1453 public CharSequence getConfirmLabel() {
1454 return mConfirmLabel;
1455 }
1456
1457 /**
1458 * Set a label to display to cancel the action.
1459 * This is usually an imperative verb, like "Cancel".
1460 *
1461 * @param label the label to display to cancel the action
1462 * @return this object for method chaining
1463 */
1464 public WearableExtender setCancelLabel(CharSequence label) {
1465 mCancelLabel = label;
1466 return this;
1467 }
1468
1469 /**
1470 * Get the label to display to cancel the action.
1471 * This is usually an imperative verb like "Cancel".
1472 *
1473 * @return the label to display to cancel the action
1474 */
1475 public CharSequence getCancelLabel() {
1476 return mCancelLabel;
1477 }
Alex Hills9ab3a232016-04-05 14:54:56 -04001478
1479 /**
1480 * Set a hint that this Action will launch an {@link Activity} directly, telling the
1481 * platform that it can generate the appropriate transitions.
1482 * @param hintLaunchesActivity {@code true} if the content intent will launch
1483 * an activity and transitions should be generated, false otherwise.
1484 * @return this object for method chaining
1485 */
Alex Hills4ec3ff42016-04-12 11:36:18 -04001486 public WearableExtender setHintLaunchesActivity(
Alex Hills9ab3a232016-04-05 14:54:56 -04001487 boolean hintLaunchesActivity) {
1488 setFlag(FLAG_HINT_LAUNCHES_ACTIVITY, hintLaunchesActivity);
1489 return this;
1490 }
1491
1492 /**
1493 * Get a hint that this Action will launch an {@link Activity} directly, telling the
1494 * platform that it can generate the appropriate transitions
1495 * @return {@code true} if the content intent will launch an activity and transitions
1496 * should be generated, false otherwise. The default value is {@code false} if this was
1497 * never set.
1498 */
Alex Hills4ec3ff42016-04-12 11:36:18 -04001499 public boolean getHintLaunchesActivity() {
Alex Hills9ab3a232016-04-05 14:54:56 -04001500 return (mFlags & FLAG_HINT_LAUNCHES_ACTIVITY) != 0;
1501 }
Alex Hills9f087612016-06-07 09:08:59 -04001502
1503 /**
1504 * Set a hint that this Action should be displayed inline.
1505 *
1506 * @param hintDisplayInline {@code true} if action should be displayed inline, false
1507 * otherwise
1508 * @return this object for method chaining
1509 */
1510 public WearableExtender setHintDisplayActionInline(
1511 boolean hintDisplayInline) {
1512 setFlag(FLAG_HINT_DISPLAY_INLINE, hintDisplayInline);
1513 return this;
1514 }
1515
1516 /**
1517 * Get a hint that this Action should be displayed inline.
1518 *
1519 * @return {@code true} if the Action should be displayed inline, {@code false}
1520 * otherwise. The default value is {@code false} if this was never set.
1521 */
1522 public boolean getHintDisplayActionInline() {
1523 return (mFlags & FLAG_HINT_DISPLAY_INLINE) != 0;
1524 }
Griff Hazen61a9e862014-05-22 16:05:19 -07001525 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001526 }
1527
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001528 /**
1529 * Array of all {@link Action} structures attached to this notification by
1530 * {@link Builder#addAction(int, CharSequence, PendingIntent)}. Mostly useful for instances of
1531 * {@link android.service.notification.NotificationListenerService} that provide an alternative
1532 * interface for invoking actions.
1533 */
Daniel Sandlerea2a3172013-02-20 22:24:20 -05001534 public Action[] actions;
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001535
1536 /**
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001537 * Replacement version of this notification whose content will be shown
1538 * in an insecure context such as atop a secure keyguard. See {@link #visibility}
1539 * and {@link #VISIBILITY_PUBLIC}.
1540 */
1541 public Notification publicVersion;
1542
1543 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001544 * Constructs a Notification object with default values.
Joe Onorato46439ce2010-11-19 13:56:21 -08001545 * You might want to consider using {@link Builder} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001546 */
1547 public Notification()
1548 {
1549 this.when = System.currentTimeMillis();
Selim Cinekb85f36fd2016-04-20 18:46:36 -07001550 this.creationTime = System.currentTimeMillis();
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001551 this.priority = PRIORITY_DEFAULT;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001552 }
1553
1554 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001555 * @hide
1556 */
1557 public Notification(Context context, int icon, CharSequence tickerText, long when,
1558 CharSequence contentTitle, CharSequence contentText, Intent contentIntent)
1559 {
Chris Wren1ce4b6d2015-06-11 10:19:43 -04001560 new Builder(context)
1561 .setWhen(when)
1562 .setSmallIcon(icon)
1563 .setTicker(tickerText)
1564 .setContentTitle(contentTitle)
1565 .setContentText(contentText)
1566 .setContentIntent(PendingIntent.getActivity(context, 0, contentIntent, 0))
1567 .buildInto(this);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001568 }
1569
1570 /**
1571 * Constructs a Notification object with the information needed to
1572 * have a status bar icon without the standard expanded view.
1573 *
1574 * @param icon The resource id of the icon to put in the status bar.
1575 * @param tickerText The text that flows by in the status bar when the notification first
1576 * activates.
1577 * @param when The time to show in the time field. In the System.currentTimeMillis
1578 * timebase.
Joe Onorato46439ce2010-11-19 13:56:21 -08001579 *
1580 * @deprecated Use {@link Builder} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001581 */
Joe Onorato46439ce2010-11-19 13:56:21 -08001582 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001583 public Notification(int icon, CharSequence tickerText, long when)
1584 {
1585 this.icon = icon;
1586 this.tickerText = tickerText;
1587 this.when = when;
Selim Cinekb85f36fd2016-04-20 18:46:36 -07001588 this.creationTime = System.currentTimeMillis();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001589 }
1590
1591 /**
1592 * Unflatten the notification from a parcel.
1593 */
Svet Ganovddb94882016-06-23 19:55:24 -07001594 @SuppressWarnings("unchecked")
1595 public Notification(Parcel parcel) {
1596 // IMPORTANT: Add unmarshaling code in readFromParcel as the pending
1597 // intents in extras are always written as the last entry.
1598 readFromParcelImpl(parcel);
1599 // Must be read last!
Felipe Lemedd85da62016-06-28 11:29:54 -07001600 allPendingIntents = (ArraySet<PendingIntent>) parcel.readArraySet(null);
Svet Ganovddb94882016-06-23 19:55:24 -07001601 }
1602
1603 private void readFromParcelImpl(Parcel parcel)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001604 {
1605 int version = parcel.readInt();
1606
1607 when = parcel.readLong();
Selim Cinekb85f36fd2016-04-20 18:46:36 -07001608 creationTime = parcel.readLong();
Dan Sandler3936e7a2015-05-19 20:59:12 -04001609 if (parcel.readInt() != 0) {
1610 mSmallIcon = Icon.CREATOR.createFromParcel(parcel);
Dan Sandler4e787062015-06-17 15:09:48 -04001611 if (mSmallIcon.getType() == Icon.TYPE_RESOURCE) {
1612 icon = mSmallIcon.getResId();
1613 }
Dan Sandler3936e7a2015-05-19 20:59:12 -04001614 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001615 number = parcel.readInt();
1616 if (parcel.readInt() != 0) {
1617 contentIntent = PendingIntent.CREATOR.createFromParcel(parcel);
1618 }
1619 if (parcel.readInt() != 0) {
1620 deleteIntent = PendingIntent.CREATOR.createFromParcel(parcel);
1621 }
1622 if (parcel.readInt() != 0) {
1623 tickerText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
1624 }
1625 if (parcel.readInt() != 0) {
Joe Onorato46439ce2010-11-19 13:56:21 -08001626 tickerView = RemoteViews.CREATOR.createFromParcel(parcel);
Joe Onoratoef1e7762010-09-17 18:38:38 -04001627 }
1628 if (parcel.readInt() != 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001629 contentView = RemoteViews.CREATOR.createFromParcel(parcel);
1630 }
Joe Onorato561d3852010-11-20 18:09:34 -08001631 if (parcel.readInt() != 0) {
Dan Sandlerd63f9322015-05-06 15:18:49 -04001632 mLargeIcon = Icon.CREATOR.createFromParcel(parcel);
Joe Onorato561d3852010-11-20 18:09:34 -08001633 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001634 defaults = parcel.readInt();
1635 flags = parcel.readInt();
1636 if (parcel.readInt() != 0) {
1637 sound = Uri.CREATOR.createFromParcel(parcel);
1638 }
1639
1640 audioStreamType = parcel.readInt();
John Spurlockc0650f022014-07-19 13:22:39 -04001641 if (parcel.readInt() != 0) {
1642 audioAttributes = AudioAttributes.CREATOR.createFromParcel(parcel);
1643 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001644 vibrate = parcel.createLongArray();
1645 ledARGB = parcel.readInt();
1646 ledOnMS = parcel.readInt();
1647 ledOffMS = parcel.readInt();
1648 iconLevel = parcel.readInt();
Daniel Sandlere46cbd32010-06-17 10:35:26 -04001649
1650 if (parcel.readInt() != 0) {
1651 fullScreenIntent = PendingIntent.CREATOR.createFromParcel(parcel);
1652 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001653
1654 priority = parcel.readInt();
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001655
John Spurlockfd7f1e02014-03-18 16:41:57 -04001656 category = parcel.readString();
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001657
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001658 mGroupKey = parcel.readString();
1659
1660 mSortKey = parcel.readString();
1661
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001662 extras = Bundle.setDefusable(parcel.readBundle(), true); // may be null
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001663
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001664 actions = parcel.createTypedArray(Action.CREATOR); // may be null
1665
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001666 if (parcel.readInt() != 0) {
Daniel Sandlerf3b73432012-03-27 15:01:25 -04001667 bigContentView = RemoteViews.CREATOR.createFromParcel(parcel);
1668 }
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001669
Chris Wren8fd39ec2014-02-27 17:43:26 -05001670 if (parcel.readInt() != 0) {
1671 headsUpContentView = RemoteViews.CREATOR.createFromParcel(parcel);
1672 }
1673
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001674 visibility = parcel.readInt();
1675
1676 if (parcel.readInt() != 0) {
1677 publicVersion = Notification.CREATOR.createFromParcel(parcel);
1678 }
Dan Sandler26e81cf2014-05-06 10:01:27 -04001679
1680 color = parcel.readInt();
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04001681
1682 if (parcel.readInt() != 0) {
1683 mChannelId = parcel.readString();
1684 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001685 }
1686
Andy Stadler110988c2010-12-03 14:29:16 -08001687 @Override
Joe Onorato18e69df2010-05-17 22:26:12 -07001688 public Notification clone() {
1689 Notification that = new Notification();
Daniel Sandler1a497d32013-04-18 14:52:45 -04001690 cloneInto(that, true);
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001691 return that;
1692 }
Joe Onorato18e69df2010-05-17 22:26:12 -07001693
Daniel Sandler1a497d32013-04-18 14:52:45 -04001694 /**
1695 * Copy all (or if heavy is false, all except Bitmaps and RemoteViews) members
1696 * of this into that.
1697 * @hide
1698 */
1699 public void cloneInto(Notification that, boolean heavy) {
Joe Onorato18e69df2010-05-17 22:26:12 -07001700 that.when = this.when;
Selim Cinekb85f36fd2016-04-20 18:46:36 -07001701 that.creationTime = this.creationTime;
Dan Sandlerd63f9322015-05-06 15:18:49 -04001702 that.mSmallIcon = this.mSmallIcon;
Joe Onorato18e69df2010-05-17 22:26:12 -07001703 that.number = this.number;
1704
1705 // PendingIntents are global, so there's no reason (or way) to clone them.
1706 that.contentIntent = this.contentIntent;
1707 that.deleteIntent = this.deleteIntent;
Daniel Sandlere46cbd32010-06-17 10:35:26 -04001708 that.fullScreenIntent = this.fullScreenIntent;
Joe Onorato18e69df2010-05-17 22:26:12 -07001709
1710 if (this.tickerText != null) {
1711 that.tickerText = this.tickerText.toString();
1712 }
Daniel Sandler1a497d32013-04-18 14:52:45 -04001713 if (heavy && this.tickerView != null) {
Joe Onorato46439ce2010-11-19 13:56:21 -08001714 that.tickerView = this.tickerView.clone();
Joe Onoratoef1e7762010-09-17 18:38:38 -04001715 }
Daniel Sandler1a497d32013-04-18 14:52:45 -04001716 if (heavy && this.contentView != null) {
Joe Onorato18e69df2010-05-17 22:26:12 -07001717 that.contentView = this.contentView.clone();
1718 }
Dan Sandlerd63f9322015-05-06 15:18:49 -04001719 if (heavy && this.mLargeIcon != null) {
1720 that.mLargeIcon = this.mLargeIcon;
Joe Onorato561d3852010-11-20 18:09:34 -08001721 }
Jozef BABJAKa8b91832011-02-22 08:05:08 +01001722 that.iconLevel = this.iconLevel;
Joe Onorato18e69df2010-05-17 22:26:12 -07001723 that.sound = this.sound; // android.net.Uri is immutable
1724 that.audioStreamType = this.audioStreamType;
John Spurlockc0650f022014-07-19 13:22:39 -04001725 if (this.audioAttributes != null) {
1726 that.audioAttributes = new AudioAttributes.Builder(this.audioAttributes).build();
1727 }
Joe Onorato18e69df2010-05-17 22:26:12 -07001728
1729 final long[] vibrate = this.vibrate;
1730 if (vibrate != null) {
1731 final int N = vibrate.length;
1732 final long[] vib = that.vibrate = new long[N];
1733 System.arraycopy(vibrate, 0, vib, 0, N);
1734 }
1735
1736 that.ledARGB = this.ledARGB;
1737 that.ledOnMS = this.ledOnMS;
1738 that.ledOffMS = this.ledOffMS;
1739 that.defaults = this.defaults;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001740
Joe Onorato18e69df2010-05-17 22:26:12 -07001741 that.flags = this.flags;
1742
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001743 that.priority = this.priority;
Joe Malin8d40d042012-11-05 11:36:40 -08001744
John Spurlockfd7f1e02014-03-18 16:41:57 -04001745 that.category = this.category;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001746
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001747 that.mGroupKey = this.mGroupKey;
1748
1749 that.mSortKey = this.mSortKey;
1750
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001751 if (this.extras != null) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04001752 try {
1753 that.extras = new Bundle(this.extras);
1754 // will unparcel
1755 that.extras.size();
1756 } catch (BadParcelableException e) {
1757 Log.e(TAG, "could not unparcel extras from notification: " + this, e);
1758 that.extras = null;
1759 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001760 }
1761
Felipe Lemedd85da62016-06-28 11:29:54 -07001762 if (!ArrayUtils.isEmpty(allPendingIntents)) {
1763 that.allPendingIntents = new ArraySet<>(allPendingIntents);
Svet Ganovddb94882016-06-23 19:55:24 -07001764 }
1765
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001766 if (this.actions != null) {
1767 that.actions = new Action[this.actions.length];
1768 for(int i=0; i<this.actions.length; i++) {
1769 that.actions[i] = this.actions[i].clone();
1770 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001771 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001772
Daniel Sandler1a497d32013-04-18 14:52:45 -04001773 if (heavy && this.bigContentView != null) {
Daniel Sandlerf3b73432012-03-27 15:01:25 -04001774 that.bigContentView = this.bigContentView.clone();
1775 }
Daniel Sandler1a497d32013-04-18 14:52:45 -04001776
Chris Wren8fd39ec2014-02-27 17:43:26 -05001777 if (heavy && this.headsUpContentView != null) {
1778 that.headsUpContentView = this.headsUpContentView.clone();
1779 }
1780
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001781 that.visibility = this.visibility;
1782
1783 if (this.publicVersion != null) {
1784 that.publicVersion = new Notification();
1785 this.publicVersion.cloneInto(that.publicVersion, heavy);
1786 }
1787
Dan Sandler26e81cf2014-05-06 10:01:27 -04001788 that.color = this.color;
1789
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04001790 that.mChannelId = this.mChannelId;
1791
Daniel Sandler1a497d32013-04-18 14:52:45 -04001792 if (!heavy) {
1793 that.lightenPayload(); // will clean out extras
1794 }
1795 }
1796
1797 /**
1798 * Removes heavyweight parts of the Notification object for archival or for sending to
1799 * listeners when the full contents are not necessary.
1800 * @hide
1801 */
1802 public final void lightenPayload() {
1803 tickerView = null;
1804 contentView = null;
1805 bigContentView = null;
Chris Wren8fd39ec2014-02-27 17:43:26 -05001806 headsUpContentView = null;
Dan Sandlerd63f9322015-05-06 15:18:49 -04001807 mLargeIcon = null;
Dan Sandler50128532015-12-08 15:42:41 -05001808 if (extras != null && !extras.isEmpty()) {
1809 final Set<String> keyset = extras.keySet();
1810 final int N = keyset.size();
1811 final String[] keys = keyset.toArray(new String[N]);
1812 for (int i=0; i<N; i++) {
1813 final String key = keys[i];
1814 final Object obj = extras.get(key);
1815 if (obj != null &&
1816 ( obj instanceof Parcelable
1817 || obj instanceof Parcelable[]
1818 || obj instanceof SparseArray
1819 || obj instanceof ArrayList)) {
1820 extras.remove(key);
1821 }
1822 }
Daniel Sandler1a497d32013-04-18 14:52:45 -04001823 }
Joe Onorato18e69df2010-05-17 22:26:12 -07001824 }
1825
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04001826 /**
1827 * Make sure this CharSequence is safe to put into a bundle, which basically
1828 * means it had better not be some custom Parcelable implementation.
1829 * @hide
1830 */
1831 public static CharSequence safeCharSequence(CharSequence cs) {
Christoph Studer535ec612014-09-03 15:47:47 +02001832 if (cs == null) return cs;
1833 if (cs.length() > MAX_CHARSEQUENCE_LENGTH) {
1834 cs = cs.subSequence(0, MAX_CHARSEQUENCE_LENGTH);
1835 }
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04001836 if (cs instanceof Parcelable) {
1837 Log.e(TAG, "warning: " + cs.getClass().getCanonicalName()
1838 + " instance is a custom Parcelable and not allowed in Notification");
1839 return cs.toString();
1840 }
Selim Cinek60a54252016-02-26 17:03:25 -08001841 return removeTextSizeSpans(cs);
1842 }
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04001843
Selim Cinek60a54252016-02-26 17:03:25 -08001844 private static CharSequence removeTextSizeSpans(CharSequence charSequence) {
1845 if (charSequence instanceof Spanned) {
1846 Spanned ss = (Spanned) charSequence;
1847 Object[] spans = ss.getSpans(0, ss.length(), Object.class);
1848 SpannableStringBuilder builder = new SpannableStringBuilder(ss.toString());
1849 for (Object span : spans) {
1850 Object resultSpan = span;
Selim Cinek89991a22016-03-07 19:51:54 -08001851 if (resultSpan instanceof CharacterStyle) {
1852 resultSpan = ((CharacterStyle) span).getUnderlying();
1853 }
1854 if (resultSpan instanceof TextAppearanceSpan) {
1855 TextAppearanceSpan originalSpan = (TextAppearanceSpan) resultSpan;
Selim Cinek60a54252016-02-26 17:03:25 -08001856 resultSpan = new TextAppearanceSpan(
1857 originalSpan.getFamily(),
1858 originalSpan.getTextStyle(),
1859 -1,
1860 originalSpan.getTextColor(),
1861 originalSpan.getLinkTextColor());
Selim Cinek89991a22016-03-07 19:51:54 -08001862 } else if (resultSpan instanceof RelativeSizeSpan
1863 || resultSpan instanceof AbsoluteSizeSpan) {
Selim Cinek60a54252016-02-26 17:03:25 -08001864 continue;
Selim Cinek89991a22016-03-07 19:51:54 -08001865 } else {
1866 resultSpan = span;
Selim Cinek60a54252016-02-26 17:03:25 -08001867 }
1868 builder.setSpan(resultSpan, ss.getSpanStart(span), ss.getSpanEnd(span),
1869 ss.getSpanFlags(span));
1870 }
1871 return builder;
1872 }
1873 return charSequence;
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04001874 }
1875
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001876 public int describeContents() {
1877 return 0;
1878 }
1879
1880 /**
Dan Sandler4e787062015-06-17 15:09:48 -04001881 * Flatten this notification into a parcel.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001882 */
Svet Ganovddb94882016-06-23 19:55:24 -07001883 public void writeToParcel(Parcel parcel, int flags) {
1884 // We need to mark all pending intents getting into the notification
1885 // system as being put there to later allow the notification ranker
1886 // to launch them and by doing so add the app to the battery saver white
1887 // list for a short period of time. The problem is that the system
1888 // cannot look into the extras as there may be parcelables there that
1889 // the platform does not know how to handle. To go around that we have
1890 // an explicit list of the pending intents in the extras bundle.
Felipe Lemedd85da62016-06-28 11:29:54 -07001891 final boolean collectPendingIntents = (allPendingIntents == null);
Svet Ganovddb94882016-06-23 19:55:24 -07001892 if (collectPendingIntents) {
1893 PendingIntent.setOnMarshaledListener(
1894 (PendingIntent intent, Parcel out, int outFlags) -> {
1895 if (parcel == out) {
Felipe Lemedd85da62016-06-28 11:29:54 -07001896 if (allPendingIntents == null) {
1897 allPendingIntents = new ArraySet<>();
Svet Ganovddb94882016-06-23 19:55:24 -07001898 }
Felipe Lemedd85da62016-06-28 11:29:54 -07001899 allPendingIntents.add(intent);
Svet Ganovddb94882016-06-23 19:55:24 -07001900 }
1901 });
1902 }
1903 try {
1904 // IMPORTANT: Add marshaling code in writeToParcelImpl as we
1905 // want to intercept all pending events written to the pacel.
1906 writeToParcelImpl(parcel, flags);
1907 // Must be written last!
Felipe Lemedd85da62016-06-28 11:29:54 -07001908 parcel.writeArraySet(allPendingIntents);
Svet Ganovddb94882016-06-23 19:55:24 -07001909 } finally {
1910 if (collectPendingIntents) {
1911 PendingIntent.setOnMarshaledListener(null);
1912 }
1913 }
1914 }
1915
1916 private void writeToParcelImpl(Parcel parcel, int flags) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001917 parcel.writeInt(1);
1918
1919 parcel.writeLong(when);
Selim Cinekb85f36fd2016-04-20 18:46:36 -07001920 parcel.writeLong(creationTime);
Dan Sandler4e787062015-06-17 15:09:48 -04001921 if (mSmallIcon == null && icon != 0) {
1922 // you snuck an icon in here without using the builder; let's try to keep it
1923 mSmallIcon = Icon.createWithResource("", icon);
1924 }
Dan Sandler3936e7a2015-05-19 20:59:12 -04001925 if (mSmallIcon != null) {
1926 parcel.writeInt(1);
1927 mSmallIcon.writeToParcel(parcel, 0);
1928 } else {
1929 parcel.writeInt(0);
1930 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001931 parcel.writeInt(number);
1932 if (contentIntent != null) {
1933 parcel.writeInt(1);
1934 contentIntent.writeToParcel(parcel, 0);
1935 } else {
1936 parcel.writeInt(0);
1937 }
1938 if (deleteIntent != null) {
1939 parcel.writeInt(1);
1940 deleteIntent.writeToParcel(parcel, 0);
1941 } else {
1942 parcel.writeInt(0);
1943 }
1944 if (tickerText != null) {
1945 parcel.writeInt(1);
1946 TextUtils.writeToParcel(tickerText, parcel, flags);
1947 } else {
1948 parcel.writeInt(0);
1949 }
Joe Onorato46439ce2010-11-19 13:56:21 -08001950 if (tickerView != null) {
Joe Onoratoef1e7762010-09-17 18:38:38 -04001951 parcel.writeInt(1);
Joe Onorato46439ce2010-11-19 13:56:21 -08001952 tickerView.writeToParcel(parcel, 0);
Joe Onoratoef1e7762010-09-17 18:38:38 -04001953 } else {
1954 parcel.writeInt(0);
1955 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001956 if (contentView != null) {
1957 parcel.writeInt(1);
1958 contentView.writeToParcel(parcel, 0);
1959 } else {
1960 parcel.writeInt(0);
1961 }
Selim Cinek279fa862016-06-14 10:57:25 -07001962 if (mLargeIcon == null && largeIcon != null) {
1963 // you snuck an icon in here without using the builder; let's try to keep it
1964 mLargeIcon = Icon.createWithBitmap(largeIcon);
1965 }
Dan Sandlerd63f9322015-05-06 15:18:49 -04001966 if (mLargeIcon != null) {
Joe Onorato561d3852010-11-20 18:09:34 -08001967 parcel.writeInt(1);
Dan Sandlerd63f9322015-05-06 15:18:49 -04001968 mLargeIcon.writeToParcel(parcel, 0);
Joe Onorato561d3852010-11-20 18:09:34 -08001969 } else {
1970 parcel.writeInt(0);
1971 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001972
1973 parcel.writeInt(defaults);
1974 parcel.writeInt(this.flags);
1975
1976 if (sound != null) {
1977 parcel.writeInt(1);
1978 sound.writeToParcel(parcel, 0);
1979 } else {
1980 parcel.writeInt(0);
1981 }
1982 parcel.writeInt(audioStreamType);
John Spurlockc0650f022014-07-19 13:22:39 -04001983
1984 if (audioAttributes != null) {
1985 parcel.writeInt(1);
1986 audioAttributes.writeToParcel(parcel, 0);
1987 } else {
1988 parcel.writeInt(0);
1989 }
1990
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001991 parcel.writeLongArray(vibrate);
1992 parcel.writeInt(ledARGB);
1993 parcel.writeInt(ledOnMS);
1994 parcel.writeInt(ledOffMS);
1995 parcel.writeInt(iconLevel);
Daniel Sandlere46cbd32010-06-17 10:35:26 -04001996
1997 if (fullScreenIntent != null) {
1998 parcel.writeInt(1);
1999 fullScreenIntent.writeToParcel(parcel, 0);
2000 } else {
2001 parcel.writeInt(0);
2002 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002003
2004 parcel.writeInt(priority);
Joe Malin8d40d042012-11-05 11:36:40 -08002005
John Spurlockfd7f1e02014-03-18 16:41:57 -04002006 parcel.writeString(category);
Joe Malin8d40d042012-11-05 11:36:40 -08002007
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002008 parcel.writeString(mGroupKey);
2009
2010 parcel.writeString(mSortKey);
2011
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002012 parcel.writeBundle(extras); // null ok
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002013
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002014 parcel.writeTypedArray(actions, 0); // null ok
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002015
Daniel Sandlerf3b73432012-03-27 15:01:25 -04002016 if (bigContentView != null) {
2017 parcel.writeInt(1);
2018 bigContentView.writeToParcel(parcel, 0);
2019 } else {
2020 parcel.writeInt(0);
2021 }
Dan Sandler0bf2ed82013-12-21 23:33:41 -06002022
Chris Wren8fd39ec2014-02-27 17:43:26 -05002023 if (headsUpContentView != null) {
2024 parcel.writeInt(1);
2025 headsUpContentView.writeToParcel(parcel, 0);
2026 } else {
2027 parcel.writeInt(0);
2028 }
2029
Dan Sandler0bf2ed82013-12-21 23:33:41 -06002030 parcel.writeInt(visibility);
2031
2032 if (publicVersion != null) {
2033 parcel.writeInt(1);
2034 publicVersion.writeToParcel(parcel, 0);
2035 } else {
2036 parcel.writeInt(0);
2037 }
Dan Sandler26e81cf2014-05-06 10:01:27 -04002038
2039 parcel.writeInt(color);
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04002040
2041 if (mChannelId != null) {
2042 parcel.writeInt(1);
2043 parcel.writeString(mChannelId);
2044 } else {
2045 parcel.writeInt(0);
2046 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002047 }
2048
2049 /**
2050 * Parcelable.Creator that instantiates Notification objects
2051 */
2052 public static final Parcelable.Creator<Notification> CREATOR
2053 = new Parcelable.Creator<Notification>()
2054 {
2055 public Notification createFromParcel(Parcel parcel)
2056 {
2057 return new Notification(parcel);
2058 }
2059
2060 public Notification[] newArray(int size)
2061 {
2062 return new Notification[size];
2063 }
2064 };
2065
2066 /**
2067 * Sets the {@link #contentView} field to be a view with the standard "Latest Event"
2068 * layout.
2069 *
2070 * <p>Uses the {@link #icon} and {@link #when} fields to set the icon and time fields
2071 * in the view.</p>
2072 * @param context The context for your application / activity.
2073 * @param contentTitle The title that goes in the expanded entry.
2074 * @param contentText The text that goes in the expanded entry.
2075 * @param contentIntent The intent to launch when the user clicks the expanded notification.
2076 * If this is an activity, it must include the
2077 * {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} flag, which requires
Scott Main7aee61f2011-02-08 11:25:01 -08002078 * that you take care of task management as described in the
2079 * <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back
2080 * Stack</a> document.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002081 *
Joe Onorato46439ce2010-11-19 13:56:21 -08002082 * @deprecated Use {@link Builder} instead.
Chris Wrena05db382015-06-24 15:18:34 -04002083 * @removed
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002084 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002085 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002086 public void setLatestEventInfo(Context context,
2087 CharSequence contentTitle, CharSequence contentText, PendingIntent contentIntent) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002088 if (context.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1){
2089 Log.e(TAG, "setLatestEventInfo() is deprecated and you should feel deprecated.",
2090 new Throwable());
2091 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002092
Selim Cinek4ac6f602016-06-13 15:47:03 -07002093 if (context.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N) {
2094 extras.putBoolean(EXTRA_SHOW_WHEN, true);
2095 }
2096
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002097 // ensure that any information already set directly is preserved
2098 final Notification.Builder builder = new Notification.Builder(context, this);
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002099
2100 // now apply the latestEventInfo fields
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002101 if (contentTitle != null) {
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002102 builder.setContentTitle(contentTitle);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002103 }
2104 if (contentText != null) {
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002105 builder.setContentText(contentText);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002106 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002107 builder.setContentIntent(contentIntent);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002108
2109 builder.build(); // callers expect this notification to be ready to use
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002110 }
2111
Julia Reynoldsda303542015-11-23 14:00:20 -05002112 /**
2113 * @hide
2114 */
2115 public static void addFieldsFromContext(Context context, Notification notification) {
Jeff Sharkey012bc7b2016-04-11 16:30:27 -06002116 addFieldsFromContext(context.getApplicationInfo(), context.getUserId(), notification);
2117 }
2118
2119 /**
2120 * @hide
2121 */
2122 public static void addFieldsFromContext(ApplicationInfo ai, int userId,
2123 Notification notification) {
2124 notification.extras.putParcelable(EXTRA_BUILDER_APPLICATION_INFO, ai);
2125 notification.extras.putInt(EXTRA_ORIGINATING_USERID, userId);
Julia Reynoldsda303542015-11-23 14:00:20 -05002126 }
2127
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002128 @Override
2129 public String toString() {
2130 StringBuilder sb = new StringBuilder();
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002131 sb.append("Notification(pri=");
2132 sb.append(priority);
2133 sb.append(" contentView=");
Joe Onoratoc9596d62011-01-12 17:03:11 -08002134 if (contentView != null) {
2135 sb.append(contentView.getPackage());
2136 sb.append("/0x");
2137 sb.append(Integer.toHexString(contentView.getLayoutId()));
2138 } else {
2139 sb.append("null");
2140 }
2141 sb.append(" vibrate=");
Daniel Sandler6738eee2012-11-16 12:03:32 -05002142 if ((this.defaults & DEFAULT_VIBRATE) != 0) {
2143 sb.append("default");
2144 } else if (this.vibrate != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002145 int N = this.vibrate.length-1;
2146 sb.append("[");
2147 for (int i=0; i<N; i++) {
2148 sb.append(this.vibrate[i]);
2149 sb.append(',');
2150 }
Simon Schoar8cf97d92009-06-10 22:08:37 +02002151 if (N != -1) {
2152 sb.append(this.vibrate[N]);
2153 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002154 sb.append("]");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002155 } else {
2156 sb.append("null");
2157 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002158 sb.append(" sound=");
Daniel Sandler6738eee2012-11-16 12:03:32 -05002159 if ((this.defaults & DEFAULT_SOUND) != 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002160 sb.append("default");
Daniel Sandler6738eee2012-11-16 12:03:32 -05002161 } else if (this.sound != null) {
2162 sb.append(this.sound.toString());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002163 } else {
2164 sb.append("null");
2165 }
Chris Wren365b6d32015-07-16 10:39:26 -04002166 if (this.tickerText != null) {
2167 sb.append(" tick");
2168 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002169 sb.append(" defaults=0x");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002170 sb.append(Integer.toHexString(this.defaults));
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002171 sb.append(" flags=0x");
Daniel Sandlere46cbd32010-06-17 10:35:26 -04002172 sb.append(Integer.toHexString(this.flags));
Dan Sandler26e81cf2014-05-06 10:01:27 -04002173 sb.append(String.format(" color=0x%08x", this.color));
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002174 if (this.category != null) {
2175 sb.append(" category=");
2176 sb.append(this.category);
2177 }
2178 if (this.mGroupKey != null) {
2179 sb.append(" groupKey=");
2180 sb.append(this.mGroupKey);
2181 }
2182 if (this.mSortKey != null) {
2183 sb.append(" sortKey=");
2184 sb.append(this.mSortKey);
2185 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002186 if (actions != null) {
Dan Sandler1b718782014-07-18 12:43:45 -04002187 sb.append(" actions=");
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002188 sb.append(actions.length);
Dan Sandler1b718782014-07-18 12:43:45 -04002189 }
2190 sb.append(" vis=");
2191 sb.append(visibilityToString(this.visibility));
2192 if (this.publicVersion != null) {
2193 sb.append(" publicVersion=");
2194 sb.append(publicVersion.toString());
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002195 }
2196 sb.append(")");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002197 return sb.toString();
2198 }
Joe Onorato46439ce2010-11-19 13:56:21 -08002199
Dan Sandler1b718782014-07-18 12:43:45 -04002200 /**
2201 * {@hide}
2202 */
2203 public static String visibilityToString(int vis) {
2204 switch (vis) {
2205 case VISIBILITY_PRIVATE:
2206 return "PRIVATE";
2207 case VISIBILITY_PUBLIC:
2208 return "PUBLIC";
2209 case VISIBILITY_SECRET:
2210 return "SECRET";
2211 default:
2212 return "UNKNOWN(" + String.valueOf(vis) + ")";
2213 }
2214 }
2215
Joe Onoratocb109a02011-01-18 17:57:41 -08002216 /**
John Spurlock1d881a12015-03-18 19:21:54 -04002217 * {@hide}
2218 */
2219 public static String priorityToString(@Priority int pri) {
2220 switch (pri) {
2221 case PRIORITY_MIN:
2222 return "MIN";
2223 case PRIORITY_LOW:
2224 return "LOW";
2225 case PRIORITY_DEFAULT:
2226 return "DEFAULT";
2227 case PRIORITY_HIGH:
2228 return "HIGH";
2229 case PRIORITY_MAX:
2230 return "MAX";
2231 default:
2232 return "UNKNOWN(" + String.valueOf(pri) + ")";
2233 }
2234 }
2235
2236 /**
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04002237 * Returns the id of the channel this notification posts to.
2238 */
2239 public String getNotificationChannel() {
2240 return mChannelId;
2241 }
2242
2243 /**
Dan Sandlerd63f9322015-05-06 15:18:49 -04002244 * The small icon representing this notification in the status bar and content view.
2245 *
2246 * @return the small icon representing this notification.
2247 *
2248 * @see Builder#getSmallIcon()
2249 * @see Builder#setSmallIcon(Icon)
2250 */
2251 public Icon getSmallIcon() {
2252 return mSmallIcon;
2253 }
2254
2255 /**
2256 * Used when notifying to clean up legacy small icons.
2257 * @hide
2258 */
2259 public void setSmallIcon(Icon icon) {
2260 mSmallIcon = icon;
2261 }
2262
2263 /**
2264 * The large icon shown in this notification's content view.
2265 * @see Builder#getLargeIcon()
2266 * @see Builder#setLargeIcon(Icon)
2267 */
2268 public Icon getLargeIcon() {
2269 return mLargeIcon;
2270 }
2271
2272 /**
Christoph Studer4600f9b2014-07-22 22:44:43 +02002273 * @hide
2274 */
Christoph Studerc8db24b2014-07-25 17:50:30 +02002275 public boolean isGroupSummary() {
2276 return mGroupKey != null && (flags & FLAG_GROUP_SUMMARY) != 0;
2277 }
2278
2279 /**
2280 * @hide
2281 */
2282 public boolean isGroupChild() {
2283 return mGroupKey != null && (flags & FLAG_GROUP_SUMMARY) == 0;
2284 }
2285
2286 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002287 * Builder class for {@link Notification} objects.
Joe Malin8d40d042012-11-05 11:36:40 -08002288 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002289 * Provides a convenient way to set the various fields of a {@link Notification} and generate
Scott Main183bf112012-08-13 19:12:13 -07002290 * content views using the platform's notification layout template. If your app supports
2291 * versions of Android as old as API level 4, you can instead use
2292 * {@link android.support.v4.app.NotificationCompat.Builder NotificationCompat.Builder},
2293 * available in the <a href="{@docRoot}tools/extras/support-library.html">Android Support
2294 * library</a>.
Joe Malin8d40d042012-11-05 11:36:40 -08002295 *
Scott Main183bf112012-08-13 19:12:13 -07002296 * <p>Example:
Joe Malin8d40d042012-11-05 11:36:40 -08002297 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002298 * <pre class="prettyprint">
Scott Main183bf112012-08-13 19:12:13 -07002299 * Notification noti = new Notification.Builder(mContext)
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002300 * .setContentTitle(&quot;New mail from &quot; + sender.toString())
2301 * .setContentText(subject)
2302 * .setSmallIcon(R.drawable.new_mail)
2303 * .setLargeIcon(aBitmap)
Chris Wrenfbd96ba2012-05-01 12:03:58 -04002304 * .build();
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002305 * </pre>
Joe Onoratocb109a02011-01-18 17:57:41 -08002306 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002307 public static class Builder {
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05002308 /**
2309 * @hide
2310 */
2311 public static final String EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT =
2312 "android.rebuild.contentViewActionCount";
2313 /**
2314 * @hide
2315 */
2316 public static final String EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT
2317 = "android.rebuild.bigViewActionCount";
2318 /**
2319 * @hide
2320 */
2321 public static final String EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT
2322 = "android.rebuild.hudViewActionCount";
2323
Daniel Sandler602ad1c2012-06-12 16:06:27 -04002324 private static final int MAX_ACTION_BUTTONS = 3;
Daniel Sandler8680bf82012-05-15 16:52:52 -04002325
Joe Onorato46439ce2010-11-19 13:56:21 -08002326 private Context mContext;
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002327 private Notification mN;
2328 private Bundle mUserExtras = new Bundle();
Chris Wrenfbd96ba2012-05-01 12:03:58 -04002329 private Style mStyle;
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002330 private ArrayList<Action> mActions = new ArrayList<Action>(MAX_ACTION_BUTTONS);
2331 private ArrayList<String> mPersonList = new ArrayList<String>();
2332 private NotificationColorUtil mColorUtil;
2333 private boolean mColorUtilInited = false;
Christoph Studer7ac80e62014-08-04 16:01:57 +02002334
2335 /**
Adrian Roos4ff3b122016-02-01 12:26:13 -08002336 * Caches a contrast-enhanced version of {@link #mCachedContrastColorIsFor}.
2337 */
2338 private int mCachedContrastColor = COLOR_INVALID;
2339 private int mCachedContrastColorIsFor = COLOR_INVALID;
2340
2341 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002342 * Constructs a new Builder with the defaults:
Joe Onoratocb109a02011-01-18 17:57:41 -08002343 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002344
2345 * <table>
2346 * <tr><th align=right>priority</th>
2347 * <td>{@link #PRIORITY_DEFAULT}</td></tr>
2348 * <tr><th align=right>when</th>
2349 * <td>now ({@link System#currentTimeMillis()})</td></tr>
2350 * <tr><th align=right>audio stream</th>
2351 * <td>{@link #STREAM_DEFAULT}</td></tr>
2352 * </table>
Joe Onoratocb109a02011-01-18 17:57:41 -08002353 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002354
2355 * @param context
2356 * A {@link Context} that will be used by the Builder to construct the
2357 * RemoteViews. The Context will not be held past the lifetime of this Builder
2358 * object.
Joe Onoratocb109a02011-01-18 17:57:41 -08002359 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002360 public Builder(Context context) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002361 this(context, null);
Joe Onorato46439ce2010-11-19 13:56:21 -08002362 }
2363
Joe Onoratocb109a02011-01-18 17:57:41 -08002364 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002365 * @hide
Christoph Studer4600f9b2014-07-22 22:44:43 +02002366 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002367 public Builder(Context context, Notification toAdopt) {
2368 mContext = context;
Christoph Studer4600f9b2014-07-22 22:44:43 +02002369
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002370 if (toAdopt == null) {
2371 mN = new Notification();
Selim Cinek0ff1ce602016-04-05 18:27:16 -07002372 if (context.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N) {
2373 mN.extras.putBoolean(EXTRA_SHOW_WHEN, true);
2374 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002375 mN.priority = PRIORITY_DEFAULT;
2376 mN.visibility = VISIBILITY_PRIVATE;
2377 } else {
2378 mN = toAdopt;
2379 if (mN.actions != null) {
2380 Collections.addAll(mActions, mN.actions);
Christoph Studer4600f9b2014-07-22 22:44:43 +02002381 }
2382
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002383 if (mN.extras.containsKey(EXTRA_PEOPLE)) {
2384 Collections.addAll(mPersonList, mN.extras.getStringArray(EXTRA_PEOPLE));
2385 }
2386
Selim Cinek4ac6f602016-06-13 15:47:03 -07002387 if (mN.getSmallIcon() == null && mN.icon != 0) {
2388 setSmallIcon(mN.icon);
2389 }
2390
2391 if (mN.getLargeIcon() == null && mN.largeIcon != null) {
2392 setLargeIcon(mN.largeIcon);
2393 }
2394
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002395 String templateClass = mN.extras.getString(EXTRA_TEMPLATE);
2396 if (!TextUtils.isEmpty(templateClass)) {
2397 final Class<? extends Style> styleClass
2398 = getNotificationStyleClass(templateClass);
2399 if (styleClass == null) {
2400 Log.d(TAG, "Unknown style class: " + templateClass);
2401 } else {
2402 try {
Adrian Roosc1a80b02016-04-05 14:54:55 -07002403 final Constructor<? extends Style> ctor =
2404 styleClass.getDeclaredConstructor();
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002405 ctor.setAccessible(true);
2406 final Style style = ctor.newInstance();
2407 style.restoreFromExtras(mN.extras);
2408
2409 if (style != null) {
2410 setStyle(style);
2411 }
2412 } catch (Throwable t) {
2413 Log.e(TAG, "Could not create Style", t);
2414 }
2415 }
2416 }
2417
2418 }
2419 }
2420
2421 private NotificationColorUtil getColorUtil() {
2422 if (!mColorUtilInited) {
2423 mColorUtilInited = true;
2424 if (mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.LOLLIPOP) {
2425 mColorUtil = NotificationColorUtil.getInstance(mContext);
Christoph Studer4600f9b2014-07-22 22:44:43 +02002426 }
2427 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002428 return mColorUtil;
Christoph Studer4600f9b2014-07-22 22:44:43 +02002429 }
2430
2431 /**
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04002432 * Specifies the channel the notification should be delivered on.
2433 */
2434 public Builder setChannel(String channelId) {
2435 mN.mChannelId = channelId;
2436 return this;
2437 }
2438
2439 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002440 * Add a timestamp pertaining to the notification (usually the time the event occurred).
Selim Cinek0ff1ce602016-04-05 18:27:16 -07002441 *
2442 * For apps targeting {@link android.os.Build.VERSION_CODES#N} and above, this time is not
2443 * shown anymore by default and must be opted into by using
2444 * {@link android.app.Notification.Builder#setShowWhen(boolean)}
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002445 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002446 * @see Notification#when
Joe Onoratocb109a02011-01-18 17:57:41 -08002447 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002448 public Builder setWhen(long when) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002449 mN.when = when;
Joe Onorato46439ce2010-11-19 13:56:21 -08002450 return this;
2451 }
2452
Joe Onoratocb109a02011-01-18 17:57:41 -08002453 /**
Griff Hazen50c11652014-05-16 09:46:31 -07002454 * Control whether the timestamp set with {@link #setWhen(long) setWhen} is shown
Daniel Sandler0c890492012-09-12 17:23:10 -07002455 * in the content view.
Selim Cinek0ff1ce602016-04-05 18:27:16 -07002456 * For apps targeting {@link android.os.Build.VERSION_CODES#N} and above, this defaults to
2457 * {@code false}. For earlier apps, the default is {@code true}.
Daniel Sandler0c890492012-09-12 17:23:10 -07002458 */
2459 public Builder setShowWhen(boolean show) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002460 mN.extras.putBoolean(EXTRA_SHOW_WHEN, show);
Daniel Sandler0c890492012-09-12 17:23:10 -07002461 return this;
2462 }
2463
2464 /**
Daniel Sandlerd33b8032012-05-10 11:41:48 -04002465 * Show the {@link Notification#when} field as a stopwatch.
Joe Malin8d40d042012-11-05 11:36:40 -08002466 *
2467 * Instead of presenting <code>when</code> as a timestamp, the notification will show an
Daniel Sandlerd33b8032012-05-10 11:41:48 -04002468 * automatically updating display of the minutes and seconds since <code>when</code>.
Daniel Sandlera2985ed2012-04-03 16:42:00 -04002469 *
Daniel Sandlerd33b8032012-05-10 11:41:48 -04002470 * Useful when showing an elapsed time (like an ongoing phone call).
2471 *
Selim Cinek81c23aa2016-02-25 16:23:13 -08002472 * The counter can also be set to count down to <code>when</code> when using
Adrian Roos96b7e202016-05-17 13:50:38 -07002473 * {@link #setChronometerCountDown(boolean)}.
Selim Cinek81c23aa2016-02-25 16:23:13 -08002474 *
Daniel Sandlerd33b8032012-05-10 11:41:48 -04002475 * @see android.widget.Chronometer
Daniel Sandlera2985ed2012-04-03 16:42:00 -04002476 * @see Notification#when
Adrian Roos96b7e202016-05-17 13:50:38 -07002477 * @see #setChronometerCountDown(boolean)
Daniel Sandlera2985ed2012-04-03 16:42:00 -04002478 */
2479 public Builder setUsesChronometer(boolean b) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002480 mN.extras.putBoolean(EXTRA_SHOW_CHRONOMETER, b);
Daniel Sandlera2985ed2012-04-03 16:42:00 -04002481 return this;
2482 }
2483
2484 /**
Selim Cinek81c23aa2016-02-25 16:23:13 -08002485 * Sets the Chronometer to count down instead of counting up.
2486 *
2487 * <p>This is only relevant if {@link #setUsesChronometer(boolean)} has been set to true.
2488 * If it isn't set the chronometer will count up.
2489 *
2490 * @see #setUsesChronometer(boolean)
2491 */
Adrian Roos96b7e202016-05-17 13:50:38 -07002492 public Builder setChronometerCountDown(boolean countDown) {
2493 mN.extras.putBoolean(EXTRA_CHRONOMETER_COUNT_DOWN, countDown);
Selim Cinek81c23aa2016-02-25 16:23:13 -08002494 return this;
2495 }
2496
2497 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002498 * Set the small icon resource, which will be used to represent the notification in the
2499 * status bar.
Joe Onoratocb109a02011-01-18 17:57:41 -08002500 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002501
2502 * The platform template for the expanded view will draw this icon in the left, unless a
2503 * {@link #setLargeIcon(Bitmap) large icon} has also been specified, in which case the small
2504 * icon will be moved to the right-hand side.
2505 *
2506
2507 * @param icon
2508 * A resource ID in the application's package of the drawable to use.
2509 * @see Notification#icon
Joe Onoratocb109a02011-01-18 17:57:41 -08002510 */
Tor Norbye7b9c9122013-05-30 16:48:33 -07002511 public Builder setSmallIcon(@DrawableRes int icon) {
Dan Sandlerd63f9322015-05-06 15:18:49 -04002512 return setSmallIcon(icon != 0
2513 ? Icon.createWithResource(mContext, icon)
2514 : null);
Joe Onorato46439ce2010-11-19 13:56:21 -08002515 }
2516
Joe Onoratocb109a02011-01-18 17:57:41 -08002517 /**
2518 * A variant of {@link #setSmallIcon(int) setSmallIcon(int)} that takes an additional
2519 * level parameter for when the icon is a {@link android.graphics.drawable.LevelListDrawable
2520 * LevelListDrawable}.
2521 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002522 * @param icon A resource ID in the application's package of the drawable to use.
Joe Onoratocb109a02011-01-18 17:57:41 -08002523 * @param level The level to use for the icon.
2524 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002525 * @see Notification#icon
2526 * @see Notification#iconLevel
Joe Onoratocb109a02011-01-18 17:57:41 -08002527 */
Tor Norbye7b9c9122013-05-30 16:48:33 -07002528 public Builder setSmallIcon(@DrawableRes int icon, int level) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002529 mN.iconLevel = level;
Dan Sandlerd63f9322015-05-06 15:18:49 -04002530 return setSmallIcon(icon);
2531 }
2532
2533 /**
2534 * Set the small icon, which will be used to represent the notification in the
2535 * status bar and content view (unless overriden there by a
2536 * {@link #setLargeIcon(Bitmap) large icon}).
2537 *
2538 * @param icon An Icon object to use.
2539 * @see Notification#icon
2540 */
2541 public Builder setSmallIcon(Icon icon) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002542 mN.setSmallIcon(icon);
2543 if (icon != null && icon.getType() == Icon.TYPE_RESOURCE) {
2544 mN.icon = icon.getResId();
2545 }
Joe Onorato46439ce2010-11-19 13:56:21 -08002546 return this;
2547 }
2548
Joe Onoratocb109a02011-01-18 17:57:41 -08002549 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002550 * Set the first line of text in the platform notification template.
Joe Onoratocb109a02011-01-18 17:57:41 -08002551 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002552 public Builder setContentTitle(CharSequence title) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002553 mN.extras.putCharSequence(EXTRA_TITLE, safeCharSequence(title));
Joe Onorato46439ce2010-11-19 13:56:21 -08002554 return this;
2555 }
2556
Joe Onoratocb109a02011-01-18 17:57:41 -08002557 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002558 * Set the second line of text in the platform notification template.
Joe Onoratocb109a02011-01-18 17:57:41 -08002559 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002560 public Builder setContentText(CharSequence text) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002561 mN.extras.putCharSequence(EXTRA_TEXT, safeCharSequence(text));
Joe Onorato46439ce2010-11-19 13:56:21 -08002562 return this;
2563 }
2564
Joe Onoratocb109a02011-01-18 17:57:41 -08002565 /**
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07002566 * This provides some additional information that is displayed in the notification. No
2567 * guarantees are given where exactly it is displayed.
2568 *
2569 * <p>This information should only be provided if it provides an essential
2570 * benefit to the understanding of the notification. The more text you provide the
2571 * less readable it becomes. For example, an email client should only provide the account
2572 * name here if more than one email account has been added.</p>
2573 *
2574 * <p>As of {@link android.os.Build.VERSION_CODES#N} this information is displayed in the
2575 * notification header area.
2576 *
2577 * On Android versions before {@link android.os.Build.VERSION_CODES#N}
2578 * this will be shown in the third line of text in the platform notification template.
2579 * You should not be using {@link #setProgress(int, int, boolean)} at the
2580 * same time on those versions; they occupy the same place.
2581 * </p>
Daniel Sandlerf3b73432012-03-27 15:01:25 -04002582 */
2583 public Builder setSubText(CharSequence text) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002584 mN.extras.putCharSequence(EXTRA_SUB_TEXT, safeCharSequence(text));
Daniel Sandlerf3b73432012-03-27 15:01:25 -04002585 return this;
2586 }
2587
2588 /**
Adrian Roose458aa82015-12-08 16:17:19 -08002589 * Set the remote input history.
2590 *
2591 * This should be set to the most recent inputs that have been sent
2592 * through a {@link RemoteInput} of this Notification and cleared once the it is no
2593 * longer relevant (e.g. for chat notifications once the other party has responded).
2594 *
2595 * The most recent input must be stored at the 0 index, the second most recent at the
2596 * 1 index, etc. Note that the system will limit both how far back the inputs will be shown
2597 * and how much of each individual input is shown.
2598 *
2599 * <p>Note: The reply text will only be shown on notifications that have least one action
2600 * with a {@code RemoteInput}.</p>
2601 */
2602 public Builder setRemoteInputHistory(CharSequence[] text) {
2603 if (text == null) {
2604 mN.extras.putCharSequenceArray(EXTRA_REMOTE_INPUT_HISTORY, null);
2605 } else {
2606 final int N = Math.min(MAX_REPLY_HISTORY, text.length);
2607 CharSequence[] safe = new CharSequence[N];
2608 for (int i = 0; i < N; i++) {
2609 safe[i] = safeCharSequence(text[i]);
2610 }
2611 mN.extras.putCharSequenceArray(EXTRA_REMOTE_INPUT_HISTORY, safe);
2612 }
2613 return this;
2614 }
2615
2616 /**
Joe Onoratocb109a02011-01-18 17:57:41 -08002617 * Set the large number at the right-hand side of the notification. This is
2618 * equivalent to setContentInfo, although it might show the number in a different
2619 * font size for readability.
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07002620 *
2621 * @deprecated this number is not shown anywhere anymore
Joe Onoratocb109a02011-01-18 17:57:41 -08002622 */
Aurimas Liutikas514c5ef2016-05-24 15:22:55 -07002623 @Deprecated
Joe Onorato8595a3d2010-11-19 18:12:07 -08002624 public Builder setNumber(int number) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002625 mN.number = number;
Joe Onorato8595a3d2010-11-19 18:12:07 -08002626 return this;
2627 }
2628
Joe Onoratocb109a02011-01-18 17:57:41 -08002629 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002630 * A small piece of additional information pertaining to this notification.
2631 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002632 * The platform template will draw this on the last line of the notification, at the far
2633 * right (to the right of a smallIcon if it has been placed there).
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07002634 *
2635 * @deprecated use {@link #setSubText(CharSequence)} instead to set a text in the header.
2636 * For legacy apps targeting a version below {@link android.os.Build.VERSION_CODES#N} this
2637 * field will still show up, but the subtext will take precedence.
Joe Onoratocb109a02011-01-18 17:57:41 -08002638 */
Aurimas Liutikas514c5ef2016-05-24 15:22:55 -07002639 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08002640 public Builder setContentInfo(CharSequence info) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002641 mN.extras.putCharSequence(EXTRA_INFO_TEXT, safeCharSequence(info));
Joe Onorato46439ce2010-11-19 13:56:21 -08002642 return this;
2643 }
2644
Joe Onoratocb109a02011-01-18 17:57:41 -08002645 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002646 * Set the progress this notification represents.
2647 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002648 * The platform template will represent this using a {@link ProgressBar}.
Jeff Sharkey1c400132011-08-05 14:50:13 -07002649 */
2650 public Builder setProgress(int max, int progress, boolean indeterminate) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002651 mN.extras.putInt(EXTRA_PROGRESS, progress);
2652 mN.extras.putInt(EXTRA_PROGRESS_MAX, max);
2653 mN.extras.putBoolean(EXTRA_PROGRESS_INDETERMINATE, indeterminate);
Jeff Sharkey1c400132011-08-05 14:50:13 -07002654 return this;
2655 }
2656
2657 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002658 * Supply a custom RemoteViews to use instead of the platform template.
2659 *
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002660 * Use {@link #setCustomContentView(RemoteViews)} instead.
Joe Onoratocb109a02011-01-18 17:57:41 -08002661 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002662 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08002663 public Builder setContent(RemoteViews views) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002664 return setCustomContentView(views);
2665 }
2666
2667 /**
2668 * Supply custom RemoteViews to use instead of the platform template.
2669 *
2670 * This will override the layout that would otherwise be constructed by this Builder
2671 * object.
2672 */
2673 public Builder setCustomContentView(RemoteViews contentView) {
2674 mN.contentView = contentView;
2675 return this;
2676 }
2677
2678 /**
2679 * Supply custom RemoteViews to use instead of the platform template in the expanded form.
2680 *
2681 * This will override the expanded layout that would otherwise be constructed by this
2682 * Builder object.
2683 */
2684 public Builder setCustomBigContentView(RemoteViews contentView) {
2685 mN.bigContentView = contentView;
2686 return this;
2687 }
2688
2689 /**
2690 * Supply custom RemoteViews to use instead of the platform template in the heads up dialog.
2691 *
2692 * This will override the heads-up layout that would otherwise be constructed by this
2693 * Builder object.
2694 */
2695 public Builder setCustomHeadsUpContentView(RemoteViews contentView) {
2696 mN.headsUpContentView = contentView;
Joe Onorato46439ce2010-11-19 13:56:21 -08002697 return this;
2698 }
2699
Joe Onoratocb109a02011-01-18 17:57:41 -08002700 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002701 * Supply a {@link PendingIntent} to be sent when the notification is clicked.
2702 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002703 * As of {@link android.os.Build.VERSION_CODES#HONEYCOMB}, if this field is unset and you
2704 * have specified a custom RemoteViews with {@link #setContent(RemoteViews)}, you can use
2705 * {@link RemoteViews#setOnClickPendingIntent RemoteViews.setOnClickPendingIntent(int,PendingIntent)}
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002706 * to assign PendingIntents to individual views in that custom layout (i.e., to create
Daniel Sandlerf3b73432012-03-27 15:01:25 -04002707 * clickable buttons inside the notification view).
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002708 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002709 * @see Notification#contentIntent Notification.contentIntent
Joe Onoratocb109a02011-01-18 17:57:41 -08002710 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002711 public Builder setContentIntent(PendingIntent intent) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002712 mN.contentIntent = intent;
Joe Onorato46439ce2010-11-19 13:56:21 -08002713 return this;
2714 }
2715
Joe Onoratocb109a02011-01-18 17:57:41 -08002716 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002717 * Supply a {@link PendingIntent} to send when the notification is cleared explicitly by the user.
2718 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002719 * @see Notification#deleteIntent
Joe Onoratocb109a02011-01-18 17:57:41 -08002720 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002721 public Builder setDeleteIntent(PendingIntent intent) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002722 mN.deleteIntent = intent;
Joe Onorato46439ce2010-11-19 13:56:21 -08002723 return this;
2724 }
2725
Joe Onoratocb109a02011-01-18 17:57:41 -08002726 /**
2727 * An intent to launch instead of posting the notification to the status bar.
2728 * Only for use with extremely high-priority notifications demanding the user's
2729 * <strong>immediate</strong> attention, such as an incoming phone call or
2730 * alarm clock that the user has explicitly set to a particular time.
2731 * If this facility is used for something else, please give the user an option
2732 * to turn it off and use a normal notification, as this can be extremely
2733 * disruptive.
2734 *
Chris Wren47c20a12014-06-18 17:27:29 -04002735 * <p>
2736 * The system UI may choose to display a heads-up notification, instead of
2737 * launching this intent, while the user is using the device.
2738 * </p>
2739 *
Joe Onoratocb109a02011-01-18 17:57:41 -08002740 * @param intent The pending intent to launch.
2741 * @param highPriority Passing true will cause this notification to be sent
2742 * even if other notifications are suppressed.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002743 *
2744 * @see Notification#fullScreenIntent
Joe Onoratocb109a02011-01-18 17:57:41 -08002745 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002746 public Builder setFullScreenIntent(PendingIntent intent, boolean highPriority) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002747 mN.fullScreenIntent = intent;
Joe Onorato46439ce2010-11-19 13:56:21 -08002748 setFlag(FLAG_HIGH_PRIORITY, highPriority);
2749 return this;
2750 }
2751
Joe Onoratocb109a02011-01-18 17:57:41 -08002752 /**
Dan Sandler5fcdf6e2014-07-18 11:31:15 -04002753 * Set the "ticker" text which is sent to accessibility services.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002754 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002755 * @see Notification#tickerText
Joe Onoratocb109a02011-01-18 17:57:41 -08002756 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002757 public Builder setTicker(CharSequence tickerText) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002758 mN.tickerText = safeCharSequence(tickerText);
Joe Onorato46439ce2010-11-19 13:56:21 -08002759 return this;
2760 }
2761
Joe Onoratocb109a02011-01-18 17:57:41 -08002762 /**
Dan Sandler5fcdf6e2014-07-18 11:31:15 -04002763 * Obsolete version of {@link #setTicker(CharSequence)}.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002764 *
Joe Onoratocb109a02011-01-18 17:57:41 -08002765 */
Dan Sandler5fcdf6e2014-07-18 11:31:15 -04002766 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08002767 public Builder setTicker(CharSequence tickerText, RemoteViews views) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002768 setTicker(tickerText);
2769 // views is ignored
Joe Onorato46439ce2010-11-19 13:56:21 -08002770 return this;
2771 }
2772
Joe Onoratocb109a02011-01-18 17:57:41 -08002773 /**
Dan Sandlerd63f9322015-05-06 15:18:49 -04002774 * Add a large icon to the notification content view.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002775 *
2776 * In the platform template, this image will be shown on the left of the notification view
Dan Sandlerd63f9322015-05-06 15:18:49 -04002777 * in place of the {@link #setSmallIcon(Icon) small icon} (which will be placed in a small
2778 * badge atop the large icon).
Dan Sandler08a04c12015-05-06 15:18:49 -04002779 */
Dan Sandlerd63f9322015-05-06 15:18:49 -04002780 public Builder setLargeIcon(Bitmap b) {
2781 return setLargeIcon(b != null ? Icon.createWithBitmap(b) : null);
2782 }
2783
2784 /**
2785 * Add a large icon to the notification content view.
2786 *
2787 * In the platform template, this image will be shown on the left of the notification view
2788 * in place of the {@link #setSmallIcon(Icon) small icon} (which will be placed in a small
2789 * badge atop the large icon).
2790 */
2791 public Builder setLargeIcon(Icon icon) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002792 mN.mLargeIcon = icon;
2793 mN.extras.putParcelable(EXTRA_LARGE_ICON, icon);
Joe Onorato46439ce2010-11-19 13:56:21 -08002794 return this;
2795 }
2796
Joe Onoratocb109a02011-01-18 17:57:41 -08002797 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002798 * Set the sound to play.
2799 *
John Spurlockc0650f022014-07-19 13:22:39 -04002800 * It will be played using the {@link #AUDIO_ATTRIBUTES_DEFAULT default audio attributes}
2801 * for notifications.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002802 *
Chris Wren47c20a12014-06-18 17:27:29 -04002803 * <p>
2804 * A notification that is noisy is more likely to be presented as a heads-up notification.
2805 * </p>
2806 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002807 * @see Notification#sound
Joe Onoratocb109a02011-01-18 17:57:41 -08002808 */
Joe Onorato52f80cd2010-11-21 15:34:48 -08002809 public Builder setSound(Uri sound) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002810 mN.sound = sound;
2811 mN.audioAttributes = AUDIO_ATTRIBUTES_DEFAULT;
Joe Onorato52f80cd2010-11-21 15:34:48 -08002812 return this;
2813 }
2814
Joe Onoratocb109a02011-01-18 17:57:41 -08002815 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002816 * Set the sound to play, along with a specific stream on which to play it.
Joe Onoratocb109a02011-01-18 17:57:41 -08002817 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002818 * See {@link android.media.AudioManager} for the <code>STREAM_</code> constants.
2819 *
Chris Wren47c20a12014-06-18 17:27:29 -04002820 * <p>
2821 * A notification that is noisy is more likely to be presented as a heads-up notification.
2822 * </p>
John Spurlockc0650f022014-07-19 13:22:39 -04002823 * @deprecated use {@link #setSound(Uri, AudioAttributes)} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002824 * @see Notification#sound
Joe Onoratocb109a02011-01-18 17:57:41 -08002825 */
Jean-Michel Trivi81f871e2014-08-06 16:32:38 -07002826 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08002827 public Builder setSound(Uri sound, int streamType) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002828 mN.sound = sound;
2829 mN.audioStreamType = streamType;
Joe Onorato46439ce2010-11-19 13:56:21 -08002830 return this;
2831 }
2832
Joe Onoratocb109a02011-01-18 17:57:41 -08002833 /**
John Spurlockc0650f022014-07-19 13:22:39 -04002834 * Set the sound to play, along with specific {@link AudioAttributes audio attributes} to
2835 * use during playback.
2836 *
2837 * <p>
2838 * A notification that is noisy is more likely to be presented as a heads-up notification.
2839 * </p>
2840 *
2841 * @see Notification#sound
2842 */
2843 public Builder setSound(Uri sound, AudioAttributes audioAttributes) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002844 mN.sound = sound;
2845 mN.audioAttributes = audioAttributes;
John Spurlockc0650f022014-07-19 13:22:39 -04002846 return this;
2847 }
2848
2849 /**
Joe Onoratocb109a02011-01-18 17:57:41 -08002850 * Set the vibration pattern to use.
2851 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002852 * See {@link android.os.Vibrator#vibrate(long[], int)} for a discussion of the
2853 * <code>pattern</code> parameter.
2854 *
Chris Wren47c20a12014-06-18 17:27:29 -04002855 * <p>
2856 * A notification that vibrates is more likely to be presented as a heads-up notification.
2857 * </p>
2858 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002859 * @see Notification#vibrate
Joe Onoratocb109a02011-01-18 17:57:41 -08002860 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002861 public Builder setVibrate(long[] pattern) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002862 mN.vibrate = pattern;
Joe Onorato46439ce2010-11-19 13:56:21 -08002863 return this;
2864 }
2865
Joe Onoratocb109a02011-01-18 17:57:41 -08002866 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002867 * Set the desired color for the indicator LED on the device, as well as the
2868 * blink duty cycle (specified in milliseconds).
2869 *
2870
2871 * Not all devices will honor all (or even any) of these values.
2872 *
2873
2874 * @see Notification#ledARGB
2875 * @see Notification#ledOnMS
2876 * @see Notification#ledOffMS
Joe Onoratocb109a02011-01-18 17:57:41 -08002877 */
Tor Norbye80756e32015-03-02 09:39:27 -08002878 public Builder setLights(@ColorInt int argb, int onMs, int offMs) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002879 mN.ledARGB = argb;
2880 mN.ledOnMS = onMs;
2881 mN.ledOffMS = offMs;
Julia Reynolds10ee1fc2015-11-09 11:04:55 -05002882 if (onMs != 0 || offMs != 0) {
2883 mN.flags |= FLAG_SHOW_LIGHTS;
2884 }
Joe Onorato46439ce2010-11-19 13:56:21 -08002885 return this;
2886 }
2887
Joe Onoratocb109a02011-01-18 17:57:41 -08002888 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002889 * Set whether this is an "ongoing" notification.
Joe Onoratocb109a02011-01-18 17:57:41 -08002890 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002891
2892 * Ongoing notifications cannot be dismissed by the user, so your application or service
2893 * must take care of canceling them.
2894 *
2895
2896 * They are typically used to indicate a background task that the user is actively engaged
2897 * with (e.g., playing music) or is pending in some way and therefore occupying the device
2898 * (e.g., a file download, sync operation, active network connection).
2899 *
2900
2901 * @see Notification#FLAG_ONGOING_EVENT
2902 * @see Service#setForeground(boolean)
Joe Onoratocb109a02011-01-18 17:57:41 -08002903 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002904 public Builder setOngoing(boolean ongoing) {
2905 setFlag(FLAG_ONGOING_EVENT, ongoing);
2906 return this;
2907 }
2908
Joe Onoratocb109a02011-01-18 17:57:41 -08002909 /**
2910 * Set this flag if you would only like the sound, vibrate
2911 * and ticker to be played if the notification is not already showing.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002912 *
2913 * @see Notification#FLAG_ONLY_ALERT_ONCE
Joe Onoratocb109a02011-01-18 17:57:41 -08002914 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002915 public Builder setOnlyAlertOnce(boolean onlyAlertOnce) {
2916 setFlag(FLAG_ONLY_ALERT_ONCE, onlyAlertOnce);
2917 return this;
2918 }
2919
Joe Onoratocb109a02011-01-18 17:57:41 -08002920 /**
Julia Reynolds04499532016-09-13 14:04:53 -04002921 * Make this notification automatically dismissed when the user touches it.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002922 *
2923 * @see Notification#FLAG_AUTO_CANCEL
Joe Onoratocb109a02011-01-18 17:57:41 -08002924 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002925 public Builder setAutoCancel(boolean autoCancel) {
Joe Onorato281d83f2011-01-04 17:13:10 -08002926 setFlag(FLAG_AUTO_CANCEL, autoCancel);
Joe Onorato46439ce2010-11-19 13:56:21 -08002927 return this;
2928 }
2929
Joe Onoratocb109a02011-01-18 17:57:41 -08002930 /**
Griff Hazendfcb0802014-02-11 12:00:00 -08002931 * Set whether or not this notification should not bridge to other devices.
2932 *
2933 * <p>Some notifications can be bridged to other devices for remote display.
2934 * This hint can be set to recommend this notification not be bridged.
2935 */
2936 public Builder setLocalOnly(boolean localOnly) {
2937 setFlag(FLAG_LOCAL_ONLY, localOnly);
2938 return this;
2939 }
2940
2941 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002942 * Set which notification properties will be inherited from system defaults.
Joe Onoratocb109a02011-01-18 17:57:41 -08002943 * <p>
2944 * The value should be one or more of the following fields combined with
2945 * bitwise-or:
2946 * {@link #DEFAULT_SOUND}, {@link #DEFAULT_VIBRATE}, {@link #DEFAULT_LIGHTS}.
2947 * <p>
2948 * For all default values, use {@link #DEFAULT_ALL}.
2949 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002950 public Builder setDefaults(int defaults) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002951 mN.defaults = defaults;
Joe Onorato46439ce2010-11-19 13:56:21 -08002952 return this;
2953 }
2954
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002955 /**
2956 * Set the priority of this notification.
2957 *
2958 * @see Notification#priority
2959 */
Tor Norbyed9273d62013-05-30 15:59:53 -07002960 public Builder setPriority(@Priority int pri) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002961 mN.priority = pri;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002962 return this;
2963 }
Joe Malin8d40d042012-11-05 11:36:40 -08002964
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002965 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -04002966 * Set the notification category.
Joe Malin8d40d042012-11-05 11:36:40 -08002967 *
John Spurlockfd7f1e02014-03-18 16:41:57 -04002968 * @see Notification#category
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002969 */
John Spurlockfd7f1e02014-03-18 16:41:57 -04002970 public Builder setCategory(String category) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002971 mN.category = category;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002972 return this;
2973 }
2974
2975 /**
Chris Wrendde75302014-03-26 17:24:15 -04002976 * Add a person that is relevant to this notification.
2977 *
Chris Wrene6c48932014-09-29 17:19:27 -04002978 * <P>
2979 * Depending on user preferences, this annotation may allow the notification to pass
2980 * through interruption filters, and to appear more prominently in the user interface.
2981 * </P>
2982 *
2983 * <P>
2984 * The person should be specified by the {@code String} representation of a
2985 * {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI}.
2986 * </P>
2987 *
2988 * <P>The system will also attempt to resolve {@code mailto:} and {@code tel:} schema
2989 * URIs. The path part of these URIs must exist in the contacts database, in the
2990 * appropriate column, or the reference will be discarded as invalid. Telephone schema
2991 * URIs will be resolved by {@link android.provider.ContactsContract.PhoneLookup}.
2992 * </P>
2993 *
2994 * @param uri A URI for the person.
Chris Wrendde75302014-03-26 17:24:15 -04002995 * @see Notification#EXTRA_PEOPLE
2996 */
Chris Wrene6c48932014-09-29 17:19:27 -04002997 public Builder addPerson(String uri) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002998 mPersonList.add(uri);
Chris Wrendde75302014-03-26 17:24:15 -04002999 return this;
3000 }
3001
3002 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003003 * Set this notification to be part of a group of notifications sharing the same key.
3004 * Grouped notifications may display in a cluster or stack on devices which
3005 * support such rendering.
3006 *
3007 * <p>To make this notification the summary for its group, also call
3008 * {@link #setGroupSummary}. A sort order can be specified for group members by using
3009 * {@link #setSortKey}.
3010 * @param groupKey The group key of the group.
3011 * @return this object for method chaining
3012 */
3013 public Builder setGroup(String groupKey) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003014 mN.mGroupKey = groupKey;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003015 return this;
3016 }
3017
3018 /**
3019 * Set this notification to be the group summary for a group of notifications.
3020 * Grouped notifications may display in a cluster or stack on devices which
Julia Reynolds04499532016-09-13 14:04:53 -04003021 * support such rendering. If thereRequires a group key also be set using {@link #setGroup}.
3022 * The group summary may be suppressed if too few notifications are included in the group.
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003023 * @param isGroupSummary Whether this notification should be a group summary.
3024 * @return this object for method chaining
3025 */
3026 public Builder setGroupSummary(boolean isGroupSummary) {
3027 setFlag(FLAG_GROUP_SUMMARY, isGroupSummary);
3028 return this;
3029 }
3030
3031 /**
3032 * Set a sort key that orders this notification among other notifications from the
3033 * same package. This can be useful if an external sort was already applied and an app
3034 * would like to preserve this. Notifications will be sorted lexicographically using this
3035 * value, although providing different priorities in addition to providing sort key may
3036 * cause this value to be ignored.
3037 *
3038 * <p>This sort key can also be used to order members of a notification group. See
Griff Hazen9e1379f2014-05-20 12:50:51 -07003039 * {@link #setGroup}.
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003040 *
3041 * @see String#compareTo(String)
3042 */
3043 public Builder setSortKey(String sortKey) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003044 mN.mSortKey = sortKey;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003045 return this;
3046 }
3047
3048 /**
Griff Hazen720042b2014-02-24 15:46:56 -08003049 * Merge additional metadata into this notification.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003050 *
Griff Hazen720042b2014-02-24 15:46:56 -08003051 * <p>Values within the Bundle will replace existing extras values in this Builder.
3052 *
3053 * @see Notification#extras
3054 */
Griff Hazen959591e2014-05-15 22:26:18 -07003055 public Builder addExtras(Bundle extras) {
3056 if (extras != null) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003057 mUserExtras.putAll(extras);
Griff Hazen720042b2014-02-24 15:46:56 -08003058 }
3059 return this;
3060 }
3061
3062 /**
3063 * Set metadata for this notification.
3064 *
3065 * <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 -04003066 * current contents are copied into the Notification each time {@link #build()} is
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003067 * called.
3068 *
Griff Hazen720042b2014-02-24 15:46:56 -08003069 * <p>Replaces any existing extras values with those from the provided Bundle.
3070 * Use {@link #addExtras} to merge in metadata instead.
3071 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003072 * @see Notification#extras
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003073 */
Griff Hazen959591e2014-05-15 22:26:18 -07003074 public Builder setExtras(Bundle extras) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003075 if (extras != null) {
3076 mUserExtras = extras;
3077 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003078 return this;
3079 }
3080
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003081 /**
Griff Hazen720042b2014-02-24 15:46:56 -08003082 * Get the current metadata Bundle used by this notification Builder.
3083 *
3084 * <p>The returned Bundle is shared with this Builder.
3085 *
3086 * <p>The current contents of this Bundle are copied into the Notification each time
3087 * {@link #build()} is called.
3088 *
3089 * @see Notification#extras
3090 */
3091 public Bundle getExtras() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003092 return mUserExtras;
3093 }
3094
3095 private Bundle getAllExtras() {
3096 final Bundle saveExtras = (Bundle) mUserExtras.clone();
3097 saveExtras.putAll(mN.extras);
3098 return saveExtras;
Griff Hazen720042b2014-02-24 15:46:56 -08003099 }
3100
3101 /**
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003102 * Add an action to this notification. Actions are typically displayed by
3103 * the system as a button adjacent to the notification content.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04003104 * <p>
3105 * Every action must have an icon (32dp square and matching the
3106 * <a href="{@docRoot}design/style/iconography.html#action-bar">Holo
3107 * Dark action bar</a> visual style), a textual label, and a {@link PendingIntent}.
3108 * <p>
3109 * A notification in its expanded form can display up to 3 actions, from left to right in
3110 * the order they were added. Actions will not be displayed when the notification is
3111 * collapsed, however, so be sure that any essential functions may be accessed by the user
3112 * in some other way (for example, in the Activity pointed to by {@link #contentIntent}).
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003113 *
3114 * @param icon Resource ID of a drawable that represents the action.
3115 * @param title Text describing the action.
3116 * @param intent PendingIntent to be fired when the action is invoked.
Dan Sandler86647982015-05-13 23:41:13 -04003117 *
3118 * @deprecated Use {@link #addAction(Action)} instead.
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003119 */
Dan Sandler86647982015-05-13 23:41:13 -04003120 @Deprecated
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003121 public Builder addAction(int icon, CharSequence title, PendingIntent intent) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04003122 mActions.add(new Action(icon, safeCharSequence(title), intent));
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003123 return this;
3124 }
3125
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003126 /**
Griff Hazen959591e2014-05-15 22:26:18 -07003127 * Add an action to this notification. Actions are typically displayed by
3128 * the system as a button adjacent to the notification content.
3129 * <p>
3130 * Every action must have an icon (32dp square and matching the
3131 * <a href="{@docRoot}design/style/iconography.html#action-bar">Holo
3132 * Dark action bar</a> visual style), a textual label, and a {@link PendingIntent}.
3133 * <p>
3134 * A notification in its expanded form can display up to 3 actions, from left to right in
3135 * the order they were added. Actions will not be displayed when the notification is
3136 * collapsed, however, so be sure that any essential functions may be accessed by the user
3137 * in some other way (for example, in the Activity pointed to by {@link #contentIntent}).
3138 *
3139 * @param action The action to add.
3140 */
3141 public Builder addAction(Action action) {
3142 mActions.add(action);
3143 return this;
3144 }
3145
3146 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003147 * Alter the complete list of actions attached to this notification.
3148 * @see #addAction(Action).
3149 *
3150 * @param actions
3151 * @return
3152 */
3153 public Builder setActions(Action... actions) {
3154 mActions.clear();
3155 for (int i = 0; i < actions.length; i++) {
3156 mActions.add(actions[i]);
3157 }
3158 return this;
3159 }
3160
3161 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003162 * Add a rich notification style to be applied at build time.
3163 *
3164 * @param style Object responsible for modifying the notification style.
3165 */
3166 public Builder setStyle(Style style) {
3167 if (mStyle != style) {
3168 mStyle = style;
Daniel Sandlerc08dea22012-06-28 08:35:24 -07003169 if (mStyle != null) {
3170 mStyle.setBuilder(this);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003171 mN.extras.putString(EXTRA_TEMPLATE, style.getClass().getName());
3172 } else {
3173 mN.extras.remove(EXTRA_TEMPLATE);
Daniel Sandlerc08dea22012-06-28 08:35:24 -07003174 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003175 }
3176 return this;
3177 }
3178
Dan Sandler0bf2ed82013-12-21 23:33:41 -06003179 /**
3180 * Specify the value of {@link #visibility}.
Griff Hazenb720abe2014-05-20 13:15:30 -07003181 *
Dan Sandler0bf2ed82013-12-21 23:33:41 -06003182 * @param visibility One of {@link #VISIBILITY_PRIVATE} (the default),
3183 * {@link #VISIBILITY_SECRET}, or {@link #VISIBILITY_PUBLIC}.
3184 *
3185 * @return The same Builder.
3186 */
3187 public Builder setVisibility(int visibility) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003188 mN.visibility = visibility;
Dan Sandler0bf2ed82013-12-21 23:33:41 -06003189 return this;
3190 }
3191
3192 /**
3193 * Supply a replacement Notification whose contents should be shown in insecure contexts
3194 * (i.e. atop the secure lockscreen). See {@link #visibility} and {@link #VISIBILITY_PUBLIC}.
3195 * @param n A replacement notification, presumably with some or all info redacted.
3196 * @return The same Builder.
3197 */
3198 public Builder setPublicVersion(Notification n) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003199 if (n != null) {
3200 mN.publicVersion = new Notification();
3201 n.cloneInto(mN.publicVersion, /*heavy=*/ true);
3202 } else {
3203 mN.publicVersion = null;
3204 }
Dan Sandler0bf2ed82013-12-21 23:33:41 -06003205 return this;
3206 }
3207
Griff Hazenb720abe2014-05-20 13:15:30 -07003208 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003209 * Apply an extender to this notification builder. Extenders may be used to add
3210 * metadata or change options on this builder.
3211 */
Griff Hazen61a9e862014-05-22 16:05:19 -07003212 public Builder extend(Extender extender) {
3213 extender.extend(this);
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003214 return this;
3215 }
3216
Dan Sandler4e787062015-06-17 15:09:48 -04003217 /**
3218 * @hide
3219 */
Julia Reynoldse46bb372016-03-17 11:05:58 -04003220 public Builder setFlag(int mask, boolean value) {
Joe Onorato46439ce2010-11-19 13:56:21 -08003221 if (value) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003222 mN.flags |= mask;
Joe Onorato46439ce2010-11-19 13:56:21 -08003223 } else {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003224 mN.flags &= ~mask;
Joe Onorato46439ce2010-11-19 13:56:21 -08003225 }
Julia Reynoldse46bb372016-03-17 11:05:58 -04003226 return this;
Joe Onorato46439ce2010-11-19 13:56:21 -08003227 }
3228
Dan Sandler26e81cf2014-05-06 10:01:27 -04003229 /**
3230 * Sets {@link Notification#color}.
3231 *
3232 * @param argb The accent color to use
3233 *
3234 * @return The same Builder.
3235 */
Tor Norbye80756e32015-03-02 09:39:27 -08003236 public Builder setColor(@ColorInt int argb) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003237 mN.color = argb;
Julia Reynolds10ee1fc2015-11-09 11:04:55 -05003238 sanitizeColor();
Dan Sandler26e81cf2014-05-06 10:01:27 -04003239 return this;
3240 }
3241
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003242 private Drawable getProfileBadgeDrawable() {
Chris Wren66619a22016-05-12 16:42:37 -04003243 if (mContext.getUserId() == UserHandle.USER_SYSTEM) {
3244 // This user can never be a badged profile,
3245 // and also includes USER_ALL system notifications.
3246 return null;
3247 }
Christoph Studer7ac80e62014-08-04 16:01:57 +02003248 // Note: This assumes that the current user can read the profile badge of the
3249 // originating user.
Selim Cineke6ff9462016-01-15 15:07:06 -08003250 return mContext.getPackageManager().getUserBadgeForDensityNoBackground(
Julia Reynoldsda303542015-11-23 14:00:20 -05003251 new UserHandle(mContext.getUserId()), 0);
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003252 }
3253
3254 private Bitmap getProfileBadge() {
3255 Drawable badge = getProfileBadgeDrawable();
Kenny Guy8a0101b2014-05-08 23:34:12 +01003256 if (badge == null) {
3257 return null;
3258 }
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003259 final int size = mContext.getResources().getDimensionPixelSize(
3260 R.dimen.notification_badge_size);
3261 Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
Kenny Guy8a0101b2014-05-08 23:34:12 +01003262 Canvas canvas = new Canvas(bitmap);
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003263 badge.setBounds(0, 0, size, size);
Kenny Guy8a0101b2014-05-08 23:34:12 +01003264 badge.draw(canvas);
3265 return bitmap;
3266 }
3267
Selim Cinekc848c3a2016-01-13 15:27:30 -08003268 private void bindProfileBadge(RemoteViews contentView) {
Kenny Guy98193ea2014-07-24 19:54:37 +01003269 Bitmap profileBadge = getProfileBadge();
3270
Kenny Guy98193ea2014-07-24 19:54:37 +01003271 if (profileBadge != null) {
Selim Cinekc848c3a2016-01-13 15:27:30 -08003272 contentView.setImageViewBitmap(R.id.profile_badge, profileBadge);
3273 contentView.setViewVisibility(R.id.profile_badge, View.VISIBLE);
Kenny Guy98193ea2014-07-24 19:54:37 +01003274 }
Kenny Guy98193ea2014-07-24 19:54:37 +01003275 }
3276
Christoph Studerfe718432014-09-01 18:21:18 +02003277 private void resetStandardTemplate(RemoteViews contentView) {
Selim Cinekeaa29ca2015-11-23 13:51:13 -08003278 resetNotificationHeader(contentView);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003279 resetContentMargins(contentView);
Christoph Studerfe718432014-09-01 18:21:18 +02003280 contentView.setViewVisibility(R.id.right_icon, View.GONE);
Selim Cinek860b6da2015-12-16 19:02:19 -08003281 contentView.setViewVisibility(R.id.title, View.GONE);
Christoph Studerfe718432014-09-01 18:21:18 +02003282 contentView.setTextViewText(R.id.title, null);
Selim Cinek41598732016-01-11 16:58:37 -08003283 contentView.setViewVisibility(R.id.text, View.GONE);
Christoph Studerfe718432014-09-01 18:21:18 +02003284 contentView.setTextViewText(R.id.text, null);
Selim Cinek29603462015-11-17 19:04:39 -08003285 contentView.setViewVisibility(R.id.text_line_1, View.GONE);
Selim Cinek41598732016-01-11 16:58:37 -08003286 contentView.setTextViewText(R.id.text_line_1, null);
Christoph Studerfe718432014-09-01 18:21:18 +02003287 contentView.setViewVisibility(R.id.progress, View.GONE);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003288 }
3289
Selim Cinekeaa29ca2015-11-23 13:51:13 -08003290 /**
3291 * Resets the notification header to its original state
3292 */
3293 private void resetNotificationHeader(RemoteViews contentView) {
Adrian Roosc4337a32016-08-02 18:30:34 -07003294 // Small icon doesn't need to be reset, as it's always set. Resetting would prevent
3295 // re-using the drawable when the notification is updated.
Selim Cinek7b836392015-12-04 20:02:59 -08003296 contentView.setBoolean(R.id.notification_header, "setExpanded", false);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003297 contentView.setTextViewText(R.id.app_name_text, null);
Christoph Studerca1db712014-09-10 17:31:33 +02003298 contentView.setViewVisibility(R.id.chronometer, View.GONE);
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07003299 contentView.setViewVisibility(R.id.header_text, View.GONE);
Adrian Roos9dfb78f2016-06-30 15:43:44 -07003300 contentView.setTextViewText(R.id.header_text, null);
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07003301 contentView.setViewVisibility(R.id.header_text_divider, View.GONE);
Selim Cinek29603462015-11-17 19:04:39 -08003302 contentView.setViewVisibility(R.id.time_divider, View.GONE);
Selim Cinekb85f36fd2016-04-20 18:46:36 -07003303 contentView.setViewVisibility(R.id.time, View.GONE);
Selim Cinekc848c3a2016-01-13 15:27:30 -08003304 contentView.setImageViewIcon(R.id.profile_badge, null);
3305 contentView.setViewVisibility(R.id.profile_badge, View.GONE);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003306 }
3307
3308 private void resetContentMargins(RemoteViews contentView) {
Adrian Roos2d5dbba2016-06-08 17:11:53 -07003309 contentView.setViewLayoutMarginEndDimen(R.id.line1, 0);
3310 contentView.setViewLayoutMarginEndDimen(R.id.text, 0);
Christoph Studerfe718432014-09-01 18:21:18 +02003311 }
3312
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003313 private RemoteViews applyStandardTemplate(int resId) {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02003314 return applyStandardTemplate(resId, true /* hasProgress */);
3315 }
3316
3317 /**
3318 * @param hasProgress whether the progress bar should be shown and set
3319 */
3320 private RemoteViews applyStandardTemplate(int resId, boolean hasProgress) {
Adrian Roosc1a80b02016-04-05 14:54:55 -07003321 final Bundle ex = mN.extras;
3322
3323 CharSequence title = processLegacyText(ex.getCharSequence(EXTRA_TITLE));
3324 CharSequence text = processLegacyText(ex.getCharSequence(EXTRA_TEXT));
3325 return applyStandardTemplate(resId, hasProgress, title, text);
3326 }
3327
3328 /**
3329 * @param hasProgress whether the progress bar should be shown and set
3330 */
3331 private RemoteViews applyStandardTemplate(int resId, boolean hasProgress,
3332 CharSequence title, CharSequence text) {
Kenny Guy77320062014-08-27 21:37:15 +01003333 RemoteViews contentView = new BuilderRemoteViews(mContext.getApplicationInfo(), resId);
Dan Sandler539aad42014-08-04 00:43:39 -04003334
Christoph Studerfe718432014-09-01 18:21:18 +02003335 resetStandardTemplate(contentView);
3336
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003337 final Bundle ex = mN.extras;
Dan Sandler190d58d2014-05-15 09:33:39 -04003338
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003339 bindNotificationHeader(contentView);
3340 bindLargeIcon(contentView);
Selim Cinek954cc232016-05-20 13:29:23 -07003341 boolean showProgress = handleProgressBar(hasProgress, contentView, ex);
Adrian Roosc1a80b02016-04-05 14:54:55 -07003342 if (title != null) {
Selim Cinek860b6da2015-12-16 19:02:19 -08003343 contentView.setViewVisibility(R.id.title, View.VISIBLE);
Adrian Roosc1a80b02016-04-05 14:54:55 -07003344 contentView.setTextViewText(R.id.title, title);
Selim Cinek954cc232016-05-20 13:29:23 -07003345 contentView.setViewLayoutWidth(R.id.title, showProgress
3346 ? ViewGroup.LayoutParams.WRAP_CONTENT
3347 : ViewGroup.LayoutParams.MATCH_PARENT);
Joe Onorato561d3852010-11-20 18:09:34 -08003348 }
Adrian Roosc1a80b02016-04-05 14:54:55 -07003349 if (text != null) {
Selim Cinek41598732016-01-11 16:58:37 -08003350 int textId = showProgress ? com.android.internal.R.id.text_line_1
3351 : com.android.internal.R.id.text;
Adrian Roosc1a80b02016-04-05 14:54:55 -07003352 contentView.setTextViewText(textId, text);
Selim Cinek41598732016-01-11 16:58:37 -08003353 contentView.setViewVisibility(textId, View.VISIBLE);
Joe Onorato561d3852010-11-20 18:09:34 -08003354 }
Selim Cinekc848c3a2016-01-13 15:27:30 -08003355
Selim Cinek279fa862016-06-14 10:57:25 -07003356 setContentMinHeight(contentView, showProgress || mN.hasLargeIcon());
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003357
Selim Cinek29603462015-11-17 19:04:39 -08003358 return contentView;
3359 }
3360
Selim Cinek860b6da2015-12-16 19:02:19 -08003361 /**
3362 * @param remoteView the remote view to update the minheight in
3363 * @param hasMinHeight does it have a mimHeight
3364 * @hide
3365 */
3366 void setContentMinHeight(RemoteViews remoteView, boolean hasMinHeight) {
3367 int minHeight = 0;
3368 if (hasMinHeight) {
3369 // we need to set the minHeight of the notification
3370 minHeight = mContext.getResources().getDimensionPixelSize(
3371 com.android.internal.R.dimen.notification_min_content_height);
3372 }
3373 remoteView.setInt(R.id.notification_main_column, "setMinimumHeight", minHeight);
3374 }
3375
Selim Cinek29603462015-11-17 19:04:39 -08003376 private boolean handleProgressBar(boolean hasProgress, RemoteViews contentView, Bundle ex) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003377 final int max = ex.getInt(EXTRA_PROGRESS_MAX, 0);
3378 final int progress = ex.getInt(EXTRA_PROGRESS, 0);
3379 final boolean ind = ex.getBoolean(EXTRA_PROGRESS_INDETERMINATE);
3380 if (hasProgress && (max != 0 || ind)) {
Selim Cinek29603462015-11-17 19:04:39 -08003381 contentView.setViewVisibility(com.android.internal.R.id.progress, View.VISIBLE);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003382 contentView.setProgressBar(
Selim Cinek29603462015-11-17 19:04:39 -08003383 R.id.progress, max, progress, ind);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003384 contentView.setProgressBackgroundTintList(
3385 R.id.progress, ColorStateList.valueOf(mContext.getColor(
3386 R.color.notification_progress_background_color)));
Selim Cinek29603462015-11-17 19:04:39 -08003387 if (mN.color != COLOR_DEFAULT) {
Adrian Roos4ff3b122016-02-01 12:26:13 -08003388 ColorStateList colorStateList = ColorStateList.valueOf(resolveContrastColor());
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003389 contentView.setProgressTintList(R.id.progress, colorStateList);
3390 contentView.setProgressIndeterminateTintList(R.id.progress, colorStateList);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003391 }
Selim Cinek29603462015-11-17 19:04:39 -08003392 return true;
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003393 } else {
3394 contentView.setViewVisibility(R.id.progress, View.GONE);
Selim Cinek29603462015-11-17 19:04:39 -08003395 return false;
Jeff Sharkey1c400132011-08-05 14:50:13 -07003396 }
Joe Onorato561d3852010-11-20 18:09:34 -08003397 }
3398
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003399 private void bindLargeIcon(RemoteViews contentView) {
Selim Cinek279fa862016-06-14 10:57:25 -07003400 if (mN.mLargeIcon == null && mN.largeIcon != null) {
3401 mN.mLargeIcon = Icon.createWithBitmap(mN.largeIcon);
3402 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003403 if (mN.mLargeIcon != null) {
3404 contentView.setViewVisibility(R.id.right_icon, View.VISIBLE);
3405 contentView.setImageViewIcon(R.id.right_icon, mN.mLargeIcon);
3406 processLargeLegacyIcon(mN.mLargeIcon, contentView);
Adrian Roos2d5dbba2016-06-08 17:11:53 -07003407 int endMargin = R.dimen.notification_content_picture_margin;
3408 contentView.setViewLayoutMarginEndDimen(R.id.line1, endMargin);
3409 contentView.setViewLayoutMarginEndDimen(R.id.text, endMargin);
3410 contentView.setViewLayoutMarginEndDimen(R.id.progress, endMargin);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003411 }
3412 }
3413
3414 private void bindNotificationHeader(RemoteViews contentView) {
3415 bindSmallIcon(contentView);
3416 bindHeaderAppName(contentView);
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07003417 bindHeaderText(contentView);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003418 bindHeaderChronometerAndTime(contentView);
3419 bindExpandButton(contentView);
Selim Cinekc848c3a2016-01-13 15:27:30 -08003420 bindProfileBadge(contentView);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003421 }
3422
3423 private void bindExpandButton(RemoteViews contentView) {
Adrian Roos4ff3b122016-02-01 12:26:13 -08003424 contentView.setDrawableParameters(R.id.expand_button, false, -1, resolveContrastColor(),
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003425 PorterDuff.Mode.SRC_ATOP, -1);
Selim Cinekea4bef72015-12-02 15:51:10 -08003426 contentView.setInt(R.id.notification_header, "setOriginalNotificationColor",
Adrian Roos4ff3b122016-02-01 12:26:13 -08003427 resolveContrastColor());
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003428 }
3429
3430 private void bindHeaderChronometerAndTime(RemoteViews contentView) {
3431 if (showsTimeOrChronometer()) {
Selim Cinek29603462015-11-17 19:04:39 -08003432 contentView.setViewVisibility(R.id.time_divider, View.VISIBLE);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003433 if (mN.extras.getBoolean(EXTRA_SHOW_CHRONOMETER)) {
3434 contentView.setViewVisibility(R.id.chronometer, View.VISIBLE);
3435 contentView.setLong(R.id.chronometer, "setBase",
3436 mN.when + (SystemClock.elapsedRealtime() - System.currentTimeMillis()));
3437 contentView.setBoolean(R.id.chronometer, "setStarted", true);
Adrian Roos96b7e202016-05-17 13:50:38 -07003438 boolean countsDown = mN.extras.getBoolean(EXTRA_CHRONOMETER_COUNT_DOWN);
Selim Cinekc3b752e2016-04-20 16:13:59 -07003439 contentView.setChronometerCountDown(R.id.chronometer, countsDown);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003440 } else {
3441 contentView.setViewVisibility(R.id.time, View.VISIBLE);
3442 contentView.setLong(R.id.time, "setTime", mN.when);
3443 }
Selim Cinekb85f36fd2016-04-20 18:46:36 -07003444 } else {
3445 // We still want a time to be set but gone, such that we can show and hide it
3446 // on demand in case it's a child notification without anything in the header
3447 contentView.setLong(R.id.time, "setTime", mN.when != 0 ? mN.when : mN.creationTime);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003448 }
3449 }
3450
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07003451 private void bindHeaderText(RemoteViews contentView) {
3452 CharSequence headerText = mN.extras.getCharSequence(EXTRA_SUB_TEXT);
3453 if (headerText == null && mStyle != null && mStyle.mSummaryTextSet
Selim Cinek03d0d652015-11-13 13:18:09 -05003454 && mStyle.hasSummaryInHeader()) {
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07003455 headerText = mStyle.mSummaryText;
Selim Cinek03d0d652015-11-13 13:18:09 -05003456 }
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07003457 if (headerText == null
3458 && mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N
3459 && mN.extras.getCharSequence(EXTRA_INFO_TEXT) != null) {
3460 headerText = mN.extras.getCharSequence(EXTRA_INFO_TEXT);
3461 }
3462 if (headerText != null) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003463 // TODO: Remove the span entirely to only have the string with propper formating.
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07003464 contentView.setTextViewText(R.id.header_text, processLegacyText(headerText));
3465 contentView.setViewVisibility(R.id.header_text, View.VISIBLE);
3466 contentView.setViewVisibility(R.id.header_text_divider, View.VISIBLE);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003467 }
3468 }
3469
Adrian Rooseba05822016-04-22 17:09:27 -07003470 /**
3471 * @hide
3472 */
3473 public String loadHeaderAppName() {
Dan Sandler732bd6c2016-04-12 14:20:32 -04003474 CharSequence name = null;
3475 final PackageManager pm = mContext.getPackageManager();
3476 if (mN.extras.containsKey(EXTRA_SUBSTITUTE_APP_NAME)) {
3477 // only system packages which lump together a bunch of unrelated stuff
3478 // may substitute a different name to make the purpose of the
3479 // notification more clear. the correct package label should always
3480 // be accessible via SystemUI.
3481 final String pkg = mContext.getPackageName();
3482 final String subName = mN.extras.getString(EXTRA_SUBSTITUTE_APP_NAME);
3483 if (PackageManager.PERMISSION_GRANTED == pm.checkPermission(
3484 android.Manifest.permission.SUBSTITUTE_NOTIFICATION_APP_NAME, pkg)) {
3485 name = subName;
3486 } else {
3487 Log.w(TAG, "warning: pkg "
3488 + pkg + " attempting to substitute app name '" + subName
3489 + "' without holding perm "
3490 + android.Manifest.permission.SUBSTITUTE_NOTIFICATION_APP_NAME);
3491 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003492 }
Dan Sandler732bd6c2016-04-12 14:20:32 -04003493 if (TextUtils.isEmpty(name)) {
3494 name = pm.getApplicationLabel(mContext.getApplicationInfo());
3495 }
3496 if (TextUtils.isEmpty(name)) {
3497 // still nothing?
3498 return null;
3499 }
3500
3501 return String.valueOf(name);
3502 }
3503 private void bindHeaderAppName(RemoteViews contentView) {
3504 contentView.setTextViewText(R.id.app_name_text, loadHeaderAppName());
Adrian Roos4ff3b122016-02-01 12:26:13 -08003505 contentView.setTextColor(R.id.app_name_text, resolveContrastColor());
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003506 }
3507
3508 private void bindSmallIcon(RemoteViews contentView) {
Selim Cinek279fa862016-06-14 10:57:25 -07003509 if (mN.mSmallIcon == null && mN.icon != 0) {
3510 mN.mSmallIcon = Icon.createWithResource(mContext, mN.icon);
3511 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003512 contentView.setImageViewIcon(R.id.icon, mN.mSmallIcon);
Adrian Roos9b45a152016-06-28 13:32:29 -07003513 contentView.setDrawableParameters(R.id.icon, false /* targetBackground */,
3514 -1 /* alpha */, -1 /* colorFilter */, null /* mode */, mN.iconLevel);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003515 processSmallIconColor(mN.mSmallIcon, contentView);
3516 }
3517
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003518 /**
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02003519 * @return true if the built notification will show the time or the chronometer; false
3520 * otherwise
3521 */
3522 private boolean showsTimeOrChronometer() {
Selim Cinekc2c0b042016-05-18 17:13:46 -07003523 return mN.showsTime() || mN.showsChronometer();
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02003524 }
3525
Christoph Studerfe718432014-09-01 18:21:18 +02003526 private void resetStandardTemplateWithActions(RemoteViews big) {
Adrian Roos4c1fcc82016-03-31 14:39:39 -07003527 // actions_container is only reset when there are no actions to avoid focus issues with
3528 // remote inputs.
Christoph Studerfe718432014-09-01 18:21:18 +02003529 big.setViewVisibility(R.id.actions, View.GONE);
Christoph Studerfe718432014-09-01 18:21:18 +02003530 big.removeAllViews(R.id.actions);
Adrian Roose458aa82015-12-08 16:17:19 -08003531
3532 big.setViewVisibility(R.id.notification_material_reply_container, View.GONE);
3533 big.setTextViewText(R.id.notification_material_reply_text_1, null);
3534
3535 big.setViewVisibility(R.id.notification_material_reply_text_2, View.GONE);
3536 big.setTextViewText(R.id.notification_material_reply_text_2, null);
3537 big.setViewVisibility(R.id.notification_material_reply_text_3, View.GONE);
3538 big.setTextViewText(R.id.notification_material_reply_text_3, null);
Adrian Roosf852a422016-06-03 13:33:43 -07003539
3540 big.setViewLayoutMarginBottomDimen(R.id.notification_action_list_margin_target, 0);
Christoph Studerfe718432014-09-01 18:21:18 +02003541 }
3542
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003543 private RemoteViews applyStandardTemplateWithActions(int layoutId) {
Adrian Roos48d746a2016-04-12 14:57:28 -07003544 final Bundle ex = mN.extras;
3545
3546 CharSequence title = processLegacyText(ex.getCharSequence(EXTRA_TITLE));
3547 CharSequence text = processLegacyText(ex.getCharSequence(EXTRA_TEXT));
3548 return applyStandardTemplateWithActions(layoutId, true /* hasProgress */, title, text);
3549 }
3550
3551 private RemoteViews applyStandardTemplateWithActions(int layoutId, boolean hasProgress,
3552 CharSequence title, CharSequence text) {
3553 RemoteViews big = applyStandardTemplate(layoutId, hasProgress, title, text);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003554
Christoph Studerfe718432014-09-01 18:21:18 +02003555 resetStandardTemplateWithActions(big);
3556
Adrian Roose458aa82015-12-08 16:17:19 -08003557 boolean validRemoteInput = false;
3558
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003559 int N = mActions.size();
Selim Cinek06e9e1f2016-07-08 17:14:16 -07003560 boolean emphazisedMode = mN.fullScreenIntent != null;
3561 big.setBoolean(R.id.actions, "setEmphasizedMode", emphazisedMode);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003562 if (N > 0) {
Adrian Roos7052de52016-03-03 15:53:34 -08003563 big.setViewVisibility(R.id.actions_container, View.VISIBLE);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003564 big.setViewVisibility(R.id.actions, View.VISIBLE);
Adrian Roosf852a422016-06-03 13:33:43 -07003565 big.setViewLayoutMarginBottomDimen(R.id.notification_action_list_margin_target,
3566 R.dimen.notification_action_list_height);
Daniel Sandler8680bf82012-05-15 16:52:52 -04003567 if (N>MAX_ACTION_BUTTONS) N=MAX_ACTION_BUTTONS;
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003568 for (int i=0; i<N; i++) {
Adrian Roose458aa82015-12-08 16:17:19 -08003569 Action action = mActions.get(i);
3570 validRemoteInput |= hasValidRemoteInput(action);
3571
Selim Cinek06e9e1f2016-07-08 17:14:16 -07003572 final RemoteViews button = generateActionButton(action, emphazisedMode,
3573 i % 2 != 0);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003574 big.addView(R.id.actions, button);
3575 }
Adrian Roos4c1fcc82016-03-31 14:39:39 -07003576 } else {
3577 big.setViewVisibility(R.id.actions_container, View.GONE);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003578 }
Adrian Roose458aa82015-12-08 16:17:19 -08003579
3580 CharSequence[] replyText = mN.extras.getCharSequenceArray(EXTRA_REMOTE_INPUT_HISTORY);
3581 if (validRemoteInput && replyText != null
3582 && replyText.length > 0 && !TextUtils.isEmpty(replyText[0])) {
3583 big.setViewVisibility(R.id.notification_material_reply_container, View.VISIBLE);
3584 big.setTextViewText(R.id.notification_material_reply_text_1, replyText[0]);
3585
3586 if (replyText.length > 1 && !TextUtils.isEmpty(replyText[1])) {
3587 big.setViewVisibility(R.id.notification_material_reply_text_2, View.VISIBLE);
3588 big.setTextViewText(R.id.notification_material_reply_text_2, replyText[1]);
3589
3590 if (replyText.length > 2 && !TextUtils.isEmpty(replyText[2])) {
3591 big.setViewVisibility(
3592 R.id.notification_material_reply_text_3, View.VISIBLE);
3593 big.setTextViewText(R.id.notification_material_reply_text_3, replyText[2]);
3594 }
3595 }
3596 }
3597
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003598 return big;
3599 }
3600
Adrian Roose458aa82015-12-08 16:17:19 -08003601 private boolean hasValidRemoteInput(Action action) {
3602 if (TextUtils.isEmpty(action.title) || action.actionIntent == null) {
3603 // Weird actions
3604 return false;
3605 }
3606
3607 RemoteInput[] remoteInputs = action.getRemoteInputs();
3608 if (remoteInputs == null) {
3609 return false;
3610 }
3611
3612 for (RemoteInput r : remoteInputs) {
3613 CharSequence[] choices = r.getChoices();
3614 if (r.getAllowFreeFormInput() || (choices != null && choices.length != 0)) {
3615 return true;
3616 }
3617 }
3618 return false;
3619 }
3620
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003621 /**
3622 * Construct a RemoteViews for the final 1U notification layout. In order:
3623 * 1. Custom contentView from the caller
3624 * 2. Style's proposed content view
3625 * 3. Standard template view
3626 */
Julia Reynolds3b848122016-02-26 10:45:32 -05003627 public RemoteViews createContentView() {
Selim Cinek593610c2016-02-16 18:42:57 -08003628 if (mN.contentView != null && (mStyle == null || !mStyle.displayCustomViewInline())) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003629 return mN.contentView;
3630 } else if (mStyle != null) {
3631 final RemoteViews styleView = mStyle.makeContentView();
3632 if (styleView != null) {
3633 return styleView;
3634 }
Joe Onorato46439ce2010-11-19 13:56:21 -08003635 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003636 return applyStandardTemplate(getBaseLayoutResource());
Joe Onorato46439ce2010-11-19 13:56:21 -08003637 }
3638
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003639 /**
3640 * Construct a RemoteViews for the final big notification layout.
3641 */
Julia Reynolds3b848122016-02-26 10:45:32 -05003642 public RemoteViews createBigContentView() {
Selim Cinek850a8542015-11-11 11:48:36 -05003643 RemoteViews result = null;
Selim Cinek593610c2016-02-16 18:42:57 -08003644 if (mN.bigContentView != null
3645 && (mStyle == null || !mStyle.displayCustomViewInline())) {
Julia Reynolds089e3e42015-11-18 09:59:57 -05003646 return mN.bigContentView;
3647 } else if (mStyle != null) {
Selim Cinek850a8542015-11-11 11:48:36 -05003648 result = mStyle.makeBigContentView();
Selim Cinek90dcf6d2015-11-18 20:24:13 -08003649 hideLine1Text(result);
Selim Cinekcc10bfb2016-02-10 16:24:21 -08003650 } else if (mActions.size() != 0) {
3651 result = applyStandardTemplateWithActions(getBigBaseLayoutResource());
Selim Cinek850a8542015-11-11 11:48:36 -05003652 }
3653 adaptNotificationHeaderForBigContentView(result);
3654 return result;
3655 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003656
Selim Cinekeaa29ca2015-11-23 13:51:13 -08003657 /**
3658 * Construct a RemoteViews for the final notification header only
3659 *
3660 * @hide
3661 */
3662 public RemoteViews makeNotificationHeader() {
3663 RemoteViews header = new BuilderRemoteViews(mContext.getApplicationInfo(),
3664 R.layout.notification_template_header);
3665 resetNotificationHeader(header);
3666 bindNotificationHeader(header);
3667 return header;
3668 }
3669
Selim Cinek29603462015-11-17 19:04:39 -08003670 private void hideLine1Text(RemoteViews result) {
Selim Cinekcc10bfb2016-02-10 16:24:21 -08003671 if (result != null) {
3672 result.setViewVisibility(R.id.text_line_1, View.GONE);
3673 }
Selim Cinek29603462015-11-17 19:04:39 -08003674 }
3675
Selim Cinek850a8542015-11-11 11:48:36 -05003676 private void adaptNotificationHeaderForBigContentView(RemoteViews result) {
Selim Cinekcc10bfb2016-02-10 16:24:21 -08003677 if (result != null) {
3678 result.setBoolean(R.id.notification_header, "setExpanded", true);
3679 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003680 }
3681
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003682 /**
3683 * Construct a RemoteViews for the final heads-up notification layout.
3684 */
Julia Reynolds3b848122016-02-26 10:45:32 -05003685 public RemoteViews createHeadsUpContentView() {
Selim Cinek593610c2016-02-16 18:42:57 -08003686 if (mN.headsUpContentView != null
3687 && (mStyle == null || !mStyle.displayCustomViewInline())) {
Julia Reynolds089e3e42015-11-18 09:59:57 -05003688 return mN.headsUpContentView;
3689 } else if (mStyle != null) {
3690 final RemoteViews styleView = mStyle.makeHeadsUpContentView();
3691 if (styleView != null) {
3692 return styleView;
3693 }
3694 } else if (mActions.size() == 0) {
3695 return null;
3696 }
3697
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003698 return applyStandardTemplateWithActions(getBigBaseLayoutResource());
Chris Wren8fd39ec2014-02-27 17:43:26 -05003699 }
3700
Selim Cinek624c02db2015-12-14 21:00:02 -08003701 /**
3702 * Construct a RemoteViews for the display in public contexts like on the lockscreen.
3703 *
3704 * @hide
3705 */
3706 public RemoteViews makePublicContentView() {
3707 if (mN.publicVersion != null) {
3708 final Builder builder = recoverBuilder(mContext, mN.publicVersion);
Julia Reynolds3b848122016-02-26 10:45:32 -05003709 return builder.createContentView();
Selim Cinek624c02db2015-12-14 21:00:02 -08003710 }
3711 Bundle savedBundle = mN.extras;
3712 Style style = mStyle;
3713 mStyle = null;
3714 Icon largeIcon = mN.mLargeIcon;
3715 mN.mLargeIcon = null;
Selim Cinek279fa862016-06-14 10:57:25 -07003716 Bitmap largeIconLegacy = mN.largeIcon;
3717 mN.largeIcon = null;
Selim Cinek624c02db2015-12-14 21:00:02 -08003718 Bundle publicExtras = new Bundle();
3719 publicExtras.putBoolean(EXTRA_SHOW_WHEN,
3720 savedBundle.getBoolean(EXTRA_SHOW_WHEN));
3721 publicExtras.putBoolean(EXTRA_SHOW_CHRONOMETER,
3722 savedBundle.getBoolean(EXTRA_SHOW_CHRONOMETER));
Adrian Roos96b7e202016-05-17 13:50:38 -07003723 publicExtras.putBoolean(EXTRA_CHRONOMETER_COUNT_DOWN,
3724 savedBundle.getBoolean(EXTRA_CHRONOMETER_COUNT_DOWN));
Selim Cinek624c02db2015-12-14 21:00:02 -08003725 publicExtras.putCharSequence(EXTRA_TITLE,
3726 mContext.getString(R.string.notification_hidden_text));
3727 mN.extras = publicExtras;
3728 final RemoteViews publicView = applyStandardTemplate(getBaseLayoutResource());
3729 mN.extras = savedBundle;
3730 mN.mLargeIcon = largeIcon;
Selim Cinek279fa862016-06-14 10:57:25 -07003731 mN.largeIcon = largeIconLegacy;
Selim Cinek624c02db2015-12-14 21:00:02 -08003732 mStyle = style;
3733 return publicView;
3734 }
3735
3736
Chris Wren8fd39ec2014-02-27 17:43:26 -05003737
Selim Cinek06e9e1f2016-07-08 17:14:16 -07003738 private RemoteViews generateActionButton(Action action, boolean emphazisedMode,
3739 boolean oddAction) {
Daniel Sandler8680bf82012-05-15 16:52:52 -04003740 final boolean tombstone = (action.actionIntent == null);
Selim Cinekf33b1112015-07-15 17:45:11 -07003741 RemoteViews button = new BuilderRemoteViews(mContext.getApplicationInfo(),
Selim Cinek06e9e1f2016-07-08 17:14:16 -07003742 emphazisedMode ? getEmphasizedActionLayoutResource()
3743 : tombstone ? getActionTombstoneLayoutResource()
3744 : getActionLayoutResource());
Daniel Sandler8680bf82012-05-15 16:52:52 -04003745 if (!tombstone) {
Daniel Sandlere5518842012-05-10 16:20:40 -04003746 button.setOnClickPendingIntent(R.id.action0, action.actionIntent);
Daniel Sandlere5518842012-05-10 16:20:40 -04003747 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003748 button.setContentDescription(R.id.action0, action.title);
Adrian Roosfe84e1f2015-11-04 15:55:39 -08003749 if (action.mRemoteInputs != null) {
3750 button.setRemoteInputs(R.id.action0, action.mRemoteInputs);
3751 }
Selim Cinek06e9e1f2016-07-08 17:14:16 -07003752 if (emphazisedMode) {
Selim Cinek981962e2016-07-20 20:41:58 -07003753 // change the background bgColor
3754 int bgColor = mContext.getColor(oddAction ? R.color.notification_action_list
3755 : R.color.notification_action_list_dark);
3756 button.setDrawableParameters(R.id.button_holder, true, -1, bgColor,
Selim Cinek06e9e1f2016-07-08 17:14:16 -07003757 PorterDuff.Mode.SRC_ATOP, -1);
Selim Cinek981962e2016-07-20 20:41:58 -07003758 CharSequence title = action.title;
3759 ColorStateList[] outResultColor = null;
3760 if (isLegacy()) {
3761 title = clearColorSpans(title);
3762 } else {
3763 outResultColor = new ColorStateList[1];
3764 title = ensureColorSpanContrast(title, bgColor, outResultColor);
3765 }
3766 button.setTextViewText(R.id.action0, title);
3767 if (outResultColor != null && outResultColor[0] != null) {
3768 // We need to set the text color as well since changing a text to uppercase
3769 // clears its spans.
3770 button.setTextColor(R.id.action0, outResultColor[0]);
3771 } else if (mN.color != COLOR_DEFAULT) {
3772 button.setTextColor(R.id.action0,resolveContrastColor());
3773 }
Selim Cinek06e9e1f2016-07-08 17:14:16 -07003774 } else {
Selim Cinek981962e2016-07-20 20:41:58 -07003775 button.setTextViewText(R.id.action0, processLegacyText(action.title));
Selim Cinek06e9e1f2016-07-08 17:14:16 -07003776 if (mN.color != COLOR_DEFAULT) {
3777 button.setTextColor(R.id.action0, resolveContrastColor());
3778 }
Adrian Roosfe84e1f2015-11-04 15:55:39 -08003779 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003780 return button;
3781 }
3782
Joe Onoratocb109a02011-01-18 17:57:41 -08003783 /**
Selim Cinek981962e2016-07-20 20:41:58 -07003784 * Clears all color spans of a text
3785 * @param charSequence the input text
3786 * @return the same text but without color spans
3787 */
3788 private CharSequence clearColorSpans(CharSequence charSequence) {
3789 if (charSequence instanceof Spanned) {
3790 Spanned ss = (Spanned) charSequence;
3791 Object[] spans = ss.getSpans(0, ss.length(), Object.class);
3792 SpannableStringBuilder builder = new SpannableStringBuilder(ss.toString());
3793 for (Object span : spans) {
3794 Object resultSpan = span;
3795 if (resultSpan instanceof CharacterStyle) {
3796 resultSpan = ((CharacterStyle) span).getUnderlying();
3797 }
3798 if (resultSpan instanceof TextAppearanceSpan) {
3799 TextAppearanceSpan originalSpan = (TextAppearanceSpan) resultSpan;
3800 if (originalSpan.getTextColor() != null) {
3801 resultSpan = new TextAppearanceSpan(
3802 originalSpan.getFamily(),
3803 originalSpan.getTextStyle(),
3804 originalSpan.getTextSize(),
3805 null,
3806 originalSpan.getLinkTextColor());
3807 }
3808 } else if (resultSpan instanceof ForegroundColorSpan
3809 || (resultSpan instanceof BackgroundColorSpan)) {
3810 continue;
3811 } else {
3812 resultSpan = span;
3813 }
3814 builder.setSpan(resultSpan, ss.getSpanStart(span), ss.getSpanEnd(span),
3815 ss.getSpanFlags(span));
3816 }
3817 return builder;
3818 }
3819 return charSequence;
3820 }
3821
3822 /**
3823 * Ensures contrast on color spans against a background color. also returns the color of the
3824 * text if a span was found that spans over the whole text.
3825 *
3826 * @param charSequence the charSequence on which the spans are
3827 * @param background the background color to ensure the contrast against
3828 * @param outResultColor an array in which a color will be returned as the first element if
3829 * there exists a full length color span.
3830 * @return the contrasted charSequence
3831 */
3832 private CharSequence ensureColorSpanContrast(CharSequence charSequence, int background,
3833 ColorStateList[] outResultColor) {
3834 if (charSequence instanceof Spanned) {
3835 Spanned ss = (Spanned) charSequence;
3836 Object[] spans = ss.getSpans(0, ss.length(), Object.class);
3837 SpannableStringBuilder builder = new SpannableStringBuilder(ss.toString());
3838 for (Object span : spans) {
3839 Object resultSpan = span;
3840 int spanStart = ss.getSpanStart(span);
3841 int spanEnd = ss.getSpanEnd(span);
3842 boolean fullLength = (spanEnd - spanStart) == charSequence.length();
3843 if (resultSpan instanceof CharacterStyle) {
3844 resultSpan = ((CharacterStyle) span).getUnderlying();
3845 }
3846 if (resultSpan instanceof TextAppearanceSpan) {
3847 TextAppearanceSpan originalSpan = (TextAppearanceSpan) resultSpan;
3848 ColorStateList textColor = originalSpan.getTextColor();
3849 if (textColor != null) {
3850 int[] colors = textColor.getColors();
3851 int[] newColors = new int[colors.length];
3852 for (int i = 0; i < newColors.length; i++) {
3853 newColors[i] = NotificationColorUtil.ensureLargeTextContrast(
3854 colors[i], background);
3855 }
3856 textColor = new ColorStateList(textColor.getStates().clone(),
3857 newColors);
3858 resultSpan = new TextAppearanceSpan(
3859 originalSpan.getFamily(),
3860 originalSpan.getTextStyle(),
3861 originalSpan.getTextSize(),
3862 textColor,
3863 originalSpan.getLinkTextColor());
3864 if (fullLength) {
3865 outResultColor[0] = new ColorStateList(
3866 textColor.getStates().clone(), newColors);
3867 }
3868 }
3869 } else if (resultSpan instanceof ForegroundColorSpan) {
3870 ForegroundColorSpan originalSpan = (ForegroundColorSpan) resultSpan;
3871 int foregroundColor = originalSpan.getForegroundColor();
3872 foregroundColor = NotificationColorUtil.ensureLargeTextContrast(
3873 foregroundColor, background);
3874 resultSpan = new ForegroundColorSpan(foregroundColor);
3875 if (fullLength) {
3876 outResultColor[0] = ColorStateList.valueOf(foregroundColor);
3877 }
3878 } else {
3879 resultSpan = span;
3880 }
3881
3882 builder.setSpan(resultSpan, spanStart, spanEnd, ss.getSpanFlags(span));
3883 }
3884 return builder;
3885 }
3886 return charSequence;
3887 }
3888
3889 /**
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003890 * @return Whether we are currently building a notification from a legacy (an app that
Alan Viverette3cb07a462014-06-06 14:19:53 -07003891 * doesn't create material notifications by itself) app.
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003892 */
3893 private boolean isLegacy() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003894 return getColorUtil() != null;
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003895 }
3896
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003897 private CharSequence processLegacyText(CharSequence charSequence) {
3898 if (isLegacy()) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003899 return getColorUtil().invertCharSequenceColors(charSequence);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003900 } else {
3901 return charSequence;
3902 }
3903 }
3904
Dan Sandler26e81cf2014-05-06 10:01:27 -04003905 /**
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003906 * Apply any necessariy colors to the small icon
Dan Sandler26e81cf2014-05-06 10:01:27 -04003907 */
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003908 private void processSmallIconColor(Icon smallIcon, RemoteViews contentView) {
Selim Cinekea4bef72015-12-02 15:51:10 -08003909 boolean colorable = !isLegacy() || getColorUtil().isGrayscaleIcon(mContext, smallIcon);
3910 if (colorable) {
Adrian Roos4ff3b122016-02-01 12:26:13 -08003911 contentView.setDrawableParameters(R.id.icon, false, -1, resolveContrastColor(),
Jorim Jaggi92df1f22014-12-16 19:44:41 +01003912 PorterDuff.Mode.SRC_ATOP, -1);
Selim Cinekea4bef72015-12-02 15:51:10 -08003913
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003914 }
Selim Cinekea4bef72015-12-02 15:51:10 -08003915 contentView.setInt(R.id.notification_header, "setOriginalIconColor",
Adrian Roos4ff3b122016-02-01 12:26:13 -08003916 colorable ? resolveContrastColor() : NotificationHeaderView.NO_COLOR);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003917 }
3918
Dan Sandler26e81cf2014-05-06 10:01:27 -04003919 /**
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003920 * Make the largeIcon dark if it's a fake smallIcon (that is,
Dan Sandler26e81cf2014-05-06 10:01:27 -04003921 * if it's grayscale).
3922 */
3923 // TODO: also check bounds, transparency, that sort of thing.
Dan Sandlerd63f9322015-05-06 15:18:49 -04003924 private void processLargeLegacyIcon(Icon largeIcon, RemoteViews contentView) {
3925 if (largeIcon != null && isLegacy()
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003926 && getColorUtil().isGrayscaleIcon(mContext, largeIcon)) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003927 // resolve color will fall back to the default when legacy
Adrian Roos4ff3b122016-02-01 12:26:13 -08003928 contentView.setDrawableParameters(R.id.icon, false, -1, resolveContrastColor(),
Dan Sandler190d58d2014-05-15 09:33:39 -04003929 PorterDuff.Mode.SRC_ATOP, -1);
Jorim Jaggi92df1f22014-12-16 19:44:41 +01003930 }
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003931 }
3932
Julia Reynolds10ee1fc2015-11-09 11:04:55 -05003933 private void sanitizeColor() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003934 if (mN.color != COLOR_DEFAULT) {
3935 mN.color |= 0xFF000000; // no alpha for custom colors
Jorim Jaggi74419312014-06-10 20:57:21 +02003936 }
Jorim Jaggi74419312014-06-10 20:57:21 +02003937 }
3938
Adrian Roos4ff3b122016-02-01 12:26:13 -08003939 int resolveContrastColor() {
3940 if (mCachedContrastColorIsFor == mN.color && mCachedContrastColor != COLOR_INVALID) {
3941 return mCachedContrastColor;
Dan Sandler26e81cf2014-05-06 10:01:27 -04003942 }
Adrian Roos4ff3b122016-02-01 12:26:13 -08003943 final int contrasted = NotificationColorUtil.resolveContrastColor(mContext, mN.color);
3944
3945 mCachedContrastColorIsFor = mN.color;
3946 return mCachedContrastColor = contrasted;
Dan Sandler26e81cf2014-05-06 10:01:27 -04003947 }
3948
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003949 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003950 * Apply the unstyled operations and return a new {@link Notification} object.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04003951 * @hide
Joe Onoratocb109a02011-01-18 17:57:41 -08003952 */
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04003953 public Notification buildUnstyled() {
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003954 if (mActions.size() > 0) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003955 mN.actions = new Action[mActions.size()];
3956 mActions.toArray(mN.actions);
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003957 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003958 if (!mPersonList.isEmpty()) {
3959 mN.extras.putStringArray(EXTRA_PEOPLE,
3960 mPersonList.toArray(new String[mPersonList.size()]));
Dan Sandler0bf2ed82013-12-21 23:33:41 -06003961 }
Selim Cinek247fa012016-02-18 09:50:48 -08003962 if (mN.bigContentView != null || mN.contentView != null
3963 || mN.headsUpContentView != null) {
3964 mN.extras.putBoolean(EXTRA_CONTAINS_CUSTOM_VIEW, true);
3965 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003966 return mN;
Joe Onorato46439ce2010-11-19 13:56:21 -08003967 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003968
Julia Reynolds3b848122016-02-26 10:45:32 -05003969 /**
3970 * Creates a Builder from an existing notification so further changes can be made.
3971 * @param context The context for your application / activity.
3972 * @param n The notification to create a Builder from.
3973 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003974 public static Notification.Builder recoverBuilder(Context context, Notification n) {
Christoph Studer4600f9b2014-07-22 22:44:43 +02003975 // Re-create notification context so we can access app resources.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003976 ApplicationInfo applicationInfo = n.extras.getParcelable(
3977 EXTRA_BUILDER_APPLICATION_INFO);
Christoph Studer4600f9b2014-07-22 22:44:43 +02003978 Context builderContext;
Julia Reynoldsda303542015-11-23 14:00:20 -05003979 if (applicationInfo != null) {
3980 try {
3981 builderContext = context.createApplicationContext(applicationInfo,
3982 Context.CONTEXT_RESTRICTED);
3983 } catch (NameNotFoundException e) {
3984 Log.e(TAG, "ApplicationInfo " + applicationInfo + " not found");
3985 builderContext = context; // try with our context
3986 }
3987 } else {
3988 builderContext = context; // try with given context
Christoph Studer4600f9b2014-07-22 22:44:43 +02003989 }
3990
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003991 return new Builder(builderContext, n);
Christoph Studer4600f9b2014-07-22 22:44:43 +02003992 }
3993
3994 private static Class<? extends Style> getNotificationStyleClass(String templateClass) {
Selim Cinek593610c2016-02-16 18:42:57 -08003995 Class<? extends Style>[] classes = new Class[] {
3996 BigTextStyle.class, BigPictureStyle.class, InboxStyle.class, MediaStyle.class,
Alex Hillsfc737de2016-03-23 17:33:02 -04003997 DecoratedCustomViewStyle.class, DecoratedMediaCustomViewStyle.class,
3998 MessagingStyle.class };
Christoph Studer4600f9b2014-07-22 22:44:43 +02003999 for (Class<? extends Style> innerClass : classes) {
4000 if (templateClass.equals(innerClass.getName())) {
4001 return innerClass;
4002 }
4003 }
4004 return null;
4005 }
4006
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004007 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004008 * @deprecated Use {@link #build()} instead.
4009 */
4010 @Deprecated
4011 public Notification getNotification() {
4012 return build();
4013 }
4014
4015 /**
4016 * Combine all of the options that have been set and return a new {@link Notification}
4017 * object.
4018 */
4019 public Notification build() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004020 // first, add any extras from the calling code
4021 if (mUserExtras != null) {
4022 mN.extras = getAllExtras();
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07004023 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004024
Selim Cinekb85f36fd2016-04-20 18:46:36 -07004025 mN.creationTime = System.currentTimeMillis();
4026
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004027 // lazy stuff from mContext; see comment in Builder(Context, Notification)
Julia Reynoldsda303542015-11-23 14:00:20 -05004028 Notification.addFieldsFromContext(mContext, mN);
Christoph Studer943aa672014-08-03 20:31:16 +02004029
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004030 buildUnstyled();
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004031
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004032 if (mStyle != null) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004033 mStyle.buildStyled(mN);
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004034 }
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004035
Adrian Roos5081c0d2016-02-26 16:04:19 -08004036 if (mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N
4037 && (mStyle == null || !mStyle.displayCustomViewInline())) {
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004038 if (mN.contentView == null) {
Julia Reynolds3b848122016-02-26 10:45:32 -05004039 mN.contentView = createContentView();
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004040 mN.extras.putInt(EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT,
4041 mN.contentView.getSequenceNumber());
4042 }
4043 if (mN.bigContentView == null) {
Julia Reynolds3b848122016-02-26 10:45:32 -05004044 mN.bigContentView = createBigContentView();
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004045 if (mN.bigContentView != null) {
4046 mN.extras.putInt(EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT,
4047 mN.bigContentView.getSequenceNumber());
4048 }
4049 }
4050 if (mN.headsUpContentView == null) {
Julia Reynolds3b848122016-02-26 10:45:32 -05004051 mN.headsUpContentView = createHeadsUpContentView();
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004052 if (mN.headsUpContentView != null) {
4053 mN.extras.putInt(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT,
4054 mN.headsUpContentView.getSequenceNumber());
4055 }
4056 }
4057 }
4058
Julia Reynolds4c0c2022016-02-02 15:11:59 -05004059 if ((mN.defaults & DEFAULT_LIGHTS) != 0) {
4060 mN.flags |= FLAG_SHOW_LIGHTS;
4061 }
4062
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004063 return mN;
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004064 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05004065
4066 /**
4067 * Apply this Builder to an existing {@link Notification} object.
4068 *
4069 * @hide
4070 */
4071 public Notification buildInto(Notification n) {
Daniel Sandler1a497d32013-04-18 14:52:45 -04004072 build().cloneInto(n, true);
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05004073 return n;
4074 }
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004075
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004076 /**
Adrian Roos184bfe022016-03-03 13:41:44 -08004077 * Removes RemoteViews that were created for compatibility from {@param n}, if they did not
4078 * change.
4079 *
4080 * @return {@param n}, if no stripping is needed, otherwise a stripped clone of {@param n}.
4081 *
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004082 * @hide
4083 */
Adrian Roos184bfe022016-03-03 13:41:44 -08004084 public static Notification maybeCloneStrippedForDelivery(Notification n) {
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004085 String templateClass = n.extras.getString(EXTRA_TEMPLATE);
Adrian Roos184bfe022016-03-03 13:41:44 -08004086
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004087 // Only strip views for known Styles because we won't know how to
4088 // re-create them otherwise.
Adrian Roos184bfe022016-03-03 13:41:44 -08004089 if (!TextUtils.isEmpty(templateClass)
4090 && getNotificationStyleClass(templateClass) == null) {
4091 return n;
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004092 }
Adrian Roos184bfe022016-03-03 13:41:44 -08004093
4094 // Only strip unmodified BuilderRemoteViews.
4095 boolean stripContentView = n.contentView instanceof BuilderRemoteViews &&
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004096 n.extras.getInt(EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT, -1) ==
Adrian Roos184bfe022016-03-03 13:41:44 -08004097 n.contentView.getSequenceNumber();
4098 boolean stripBigContentView = n.bigContentView instanceof BuilderRemoteViews &&
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004099 n.extras.getInt(EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT, -1) ==
Adrian Roos184bfe022016-03-03 13:41:44 -08004100 n.bigContentView.getSequenceNumber();
4101 boolean stripHeadsUpContentView = n.headsUpContentView instanceof BuilderRemoteViews &&
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004102 n.extras.getInt(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT, -1) ==
Adrian Roos184bfe022016-03-03 13:41:44 -08004103 n.headsUpContentView.getSequenceNumber();
4104
4105 // Nothing to do here, no need to clone.
4106 if (!stripContentView && !stripBigContentView && !stripHeadsUpContentView) {
4107 return n;
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004108 }
Adrian Roos184bfe022016-03-03 13:41:44 -08004109
4110 Notification clone = n.clone();
4111 if (stripContentView) {
4112 clone.contentView = null;
4113 clone.extras.remove(EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT);
4114 }
4115 if (stripBigContentView) {
4116 clone.bigContentView = null;
4117 clone.extras.remove(EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT);
4118 }
4119 if (stripHeadsUpContentView) {
4120 clone.headsUpContentView = null;
4121 clone.extras.remove(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT);
4122 }
4123 return clone;
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004124 }
4125
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004126 private int getBaseLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07004127 return R.layout.notification_template_material_base;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004128 }
4129
4130 private int getBigBaseLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07004131 return R.layout.notification_template_material_big_base;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004132 }
4133
4134 private int getBigPictureLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07004135 return R.layout.notification_template_material_big_picture;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004136 }
4137
4138 private int getBigTextLayoutResource() {
Jorim Jaggi445d3c02014-08-19 22:33:42 +02004139 return R.layout.notification_template_material_big_text;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004140 }
4141
4142 private int getInboxLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07004143 return R.layout.notification_template_material_inbox;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004144 }
4145
Adrian Roosc1a80b02016-04-05 14:54:55 -07004146 private int getMessagingLayoutResource() {
4147 return R.layout.notification_template_material_messaging;
4148 }
4149
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004150 private int getActionLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07004151 return R.layout.notification_material_action;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004152 }
4153
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004154 private int getEmphasizedActionLayoutResource() {
4155 return R.layout.notification_material_action_emphasized;
4156 }
4157
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004158 private int getActionTombstoneLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07004159 return R.layout.notification_material_action_tombstone;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004160 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004161 }
4162
Selim Cinek279fa862016-06-14 10:57:25 -07004163 private boolean hasLargeIcon() {
4164 return mLargeIcon != null || largeIcon != null;
4165 }
4166
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004167 /**
Selim Cinekc2c0b042016-05-18 17:13:46 -07004168 * @return true if the notification will show the time; false otherwise
Selim Cinekb85f36fd2016-04-20 18:46:36 -07004169 * @hide
4170 */
Selim Cinekc2c0b042016-05-18 17:13:46 -07004171 public boolean showsTime() {
Selim Cinekb85f36fd2016-04-20 18:46:36 -07004172 return when != 0 && extras.getBoolean(EXTRA_SHOW_WHEN);
4173 }
4174
4175 /**
Selim Cinekc2c0b042016-05-18 17:13:46 -07004176 * @return true if the notification will show a chronometer; false otherwise
4177 * @hide
4178 */
4179 public boolean showsChronometer() {
4180 return when != 0 && extras.getBoolean(EXTRA_SHOW_CHRONOMETER);
4181 }
4182
4183 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004184 * An object that can apply a rich notification style to a {@link Notification.Builder}
4185 * object.
4186 */
Griff Hazendfcb0802014-02-11 12:00:00 -08004187 public static abstract class Style {
Chris Wrend6297db2012-05-03 16:20:13 -04004188 private CharSequence mBigContentTitle;
Jorim Jaggi457a10d2014-09-08 16:18:23 +02004189
4190 /**
4191 * @hide
4192 */
4193 protected CharSequence mSummaryText = null;
4194
4195 /**
4196 * @hide
4197 */
4198 protected boolean mSummaryTextSet = false;
Chris Wrend6297db2012-05-03 16:20:13 -04004199
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004200 protected Builder mBuilder;
4201
Chris Wrend6297db2012-05-03 16:20:13 -04004202 /**
4203 * Overrides ContentTitle in the big form of the template.
4204 * This defaults to the value passed to setContentTitle().
4205 */
4206 protected void internalSetBigContentTitle(CharSequence title) {
4207 mBigContentTitle = title;
4208 }
4209
4210 /**
4211 * Set the first line of text after the detail section in the big form of the template.
4212 */
4213 protected void internalSetSummaryText(CharSequence cs) {
4214 mSummaryText = cs;
Daniel Sandler619738c2012-06-07 16:33:08 -04004215 mSummaryTextSet = true;
Chris Wrend6297db2012-05-03 16:20:13 -04004216 }
4217
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004218 public void setBuilder(Builder builder) {
4219 if (mBuilder != builder) {
4220 mBuilder = builder;
Daniel Sandlerc08dea22012-06-28 08:35:24 -07004221 if (mBuilder != null) {
4222 mBuilder.setStyle(this);
4223 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004224 }
4225 }
4226
Chris Wrend6297db2012-05-03 16:20:13 -04004227 protected void checkBuilder() {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004228 if (mBuilder == null) {
4229 throw new IllegalArgumentException("Style requires a valid Builder object");
4230 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004231 }
Chris Wrend6297db2012-05-03 16:20:13 -04004232
4233 protected RemoteViews getStandardView(int layoutId) {
4234 checkBuilder();
4235
Christoph Studer4600f9b2014-07-22 22:44:43 +02004236 // Nasty.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004237 CharSequence oldBuilderContentTitle =
4238 mBuilder.getAllExtras().getCharSequence(EXTRA_TITLE);
Chris Wrend6297db2012-05-03 16:20:13 -04004239 if (mBigContentTitle != null) {
4240 mBuilder.setContentTitle(mBigContentTitle);
4241 }
4242
Chris Wrend6297db2012-05-03 16:20:13 -04004243 RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(layoutId);
4244
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004245 mBuilder.getAllExtras().putCharSequence(EXTRA_TITLE, oldBuilderContentTitle);
Christoph Studer4600f9b2014-07-22 22:44:43 +02004246
Chris Wrend6297db2012-05-03 16:20:13 -04004247 if (mBigContentTitle != null && mBigContentTitle.equals("")) {
4248 contentView.setViewVisibility(R.id.line1, View.GONE);
Chris Wren67dc9a02012-05-16 01:03:20 -04004249 } else {
4250 contentView.setViewVisibility(R.id.line1, View.VISIBLE);
Chris Wrend6297db2012-05-03 16:20:13 -04004251 }
4252
Chris Wrend6297db2012-05-03 16:20:13 -04004253 return contentView;
4254 }
4255
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004256 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004257 * Construct a Style-specific RemoteViews for the final 1U notification layout.
4258 * The default implementation has nothing additional to add.
4259 * @hide
4260 */
4261 public RemoteViews makeContentView() {
4262 return null;
4263 }
4264
4265 /**
4266 * Construct a Style-specific RemoteViews for the final big notification layout.
4267 * @hide
4268 */
4269 public RemoteViews makeBigContentView() {
4270 return null;
4271 }
4272
4273 /**
4274 * Construct a Style-specific RemoteViews for the final HUN layout.
4275 * @hide
4276 */
4277 public RemoteViews makeHeadsUpContentView() {
4278 return null;
4279 }
4280
4281 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004282 * Apply any style-specific extras to this notification before shipping it out.
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004283 * @hide
4284 */
4285 public void addExtras(Bundle extras) {
4286 if (mSummaryTextSet) {
4287 extras.putCharSequence(EXTRA_SUMMARY_TEXT, mSummaryText);
4288 }
4289 if (mBigContentTitle != null) {
4290 extras.putCharSequence(EXTRA_TITLE_BIG, mBigContentTitle);
4291 }
Chris Wren91ad5632013-06-05 15:05:57 -04004292 extras.putString(EXTRA_TEMPLATE, this.getClass().getName());
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004293 }
4294
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04004295 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004296 * Reconstruct the internal state of this Style object from extras.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04004297 * @hide
4298 */
Christoph Studer4600f9b2014-07-22 22:44:43 +02004299 protected void restoreFromExtras(Bundle extras) {
4300 if (extras.containsKey(EXTRA_SUMMARY_TEXT)) {
4301 mSummaryText = extras.getCharSequence(EXTRA_SUMMARY_TEXT);
4302 mSummaryTextSet = true;
4303 }
4304 if (extras.containsKey(EXTRA_TITLE_BIG)) {
4305 mBigContentTitle = extras.getCharSequence(EXTRA_TITLE_BIG);
4306 }
4307 }
4308
4309
4310 /**
4311 * @hide
4312 */
4313 public Notification buildStyled(Notification wip) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004314 addExtras(wip.extras);
Christoph Studer4600f9b2014-07-22 22:44:43 +02004315 return wip;
4316 }
4317
Daniel Sandler0ec46202015-06-24 01:27:05 -04004318 /**
4319 * @hide
4320 */
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07004321 public void purgeResources() {}
4322
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04004323 /**
4324 * Calls {@link android.app.Notification.Builder#build()} on the Builder this Style is
4325 * attached to.
4326 *
4327 * @return the fully constructed Notification.
4328 */
4329 public Notification build() {
4330 checkBuilder();
4331 return mBuilder.build();
4332 }
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004333
4334 /**
4335 * @hide
4336 * @return true if the style positions the progress bar on the second line; false if the
4337 * style hides the progress bar
4338 */
4339 protected boolean hasProgress() {
4340 return true;
4341 }
Selim Cinek03d0d652015-11-13 13:18:09 -05004342
4343 /**
4344 * @hide
4345 * @return Whether we should put the summary be put into the notification header
4346 */
4347 public boolean hasSummaryInHeader() {
4348 return true;
4349 }
Selim Cinek593610c2016-02-16 18:42:57 -08004350
4351 /**
4352 * @hide
4353 * @return Whether custom content views are displayed inline in the style
4354 */
4355 public boolean displayCustomViewInline() {
4356 return false;
4357 }
Joe Onorato46439ce2010-11-19 13:56:21 -08004358 }
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004359
4360 /**
Daniel Sandler4dfbe832012-04-11 14:51:46 -04004361 * Helper class for generating large-format notifications that include a large image attachment.
Joe Malin8d40d042012-11-05 11:36:40 -08004362 *
Robert Ly91c5ce32014-06-08 15:37:00 -07004363 * Here's how you'd set the <code>BigPictureStyle</code> on a notification:
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004364 * <pre class="prettyprint">
Robert Ly91c5ce32014-06-08 15:37:00 -07004365 * Notification notif = new Notification.Builder(mContext)
4366 * .setContentTitle(&quot;New photo from &quot; + sender.toString())
4367 * .setContentText(subject)
4368 * .setSmallIcon(R.drawable.new_post)
4369 * .setLargeIcon(aBitmap)
4370 * .setStyle(new Notification.BigPictureStyle()
4371 * .bigPicture(aBigBitmap))
4372 * .build();
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004373 * </pre>
Joe Malin8d40d042012-11-05 11:36:40 -08004374 *
Daniel Sandler4dfbe832012-04-11 14:51:46 -04004375 * @see Notification#bigContentView
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004376 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004377 public static class BigPictureStyle extends Style {
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004378 private Bitmap mPicture;
Dan Sandlerd63f9322015-05-06 15:18:49 -04004379 private Icon mBigLargeIcon;
Chris Wren3745a3d2012-05-22 15:11:52 -04004380 private boolean mBigLargeIconSet = false;
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004381
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004382 public BigPictureStyle() {
4383 }
4384
Adrian Roosf5faf9d2016-05-23 13:56:15 -07004385 /**
4386 * @deprecated use {@code BigPictureStyle()}.
4387 */
4388 @Deprecated
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004389 public BigPictureStyle(Builder builder) {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004390 setBuilder(builder);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004391 }
4392
Chris Wrend6297db2012-05-03 16:20:13 -04004393 /**
4394 * Overrides ContentTitle in the big form of the template.
4395 * This defaults to the value passed to setContentTitle().
4396 */
4397 public BigPictureStyle setBigContentTitle(CharSequence title) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04004398 internalSetBigContentTitle(safeCharSequence(title));
Chris Wrend6297db2012-05-03 16:20:13 -04004399 return this;
4400 }
4401
4402 /**
4403 * Set the first line of text after the detail section in the big form of the template.
4404 */
4405 public BigPictureStyle setSummaryText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04004406 internalSetSummaryText(safeCharSequence(cs));
Chris Wrend6297db2012-05-03 16:20:13 -04004407 return this;
4408 }
4409
Chris Wren0bd664d2012-08-01 13:56:56 -04004410 /**
4411 * Provide the bitmap to be used as the payload for the BigPicture notification.
4412 */
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004413 public BigPictureStyle bigPicture(Bitmap b) {
4414 mPicture = b;
4415 return this;
4416 }
4417
Chris Wren3745a3d2012-05-22 15:11:52 -04004418 /**
Chris Wren3745a3d2012-05-22 15:11:52 -04004419 * Override the large icon when the big notification is shown.
4420 */
4421 public BigPictureStyle bigLargeIcon(Bitmap b) {
Dan Sandlerd63f9322015-05-06 15:18:49 -04004422 return bigLargeIcon(b != null ? Icon.createWithBitmap(b) : null);
4423 }
4424
4425 /**
4426 * Override the large icon when the big notification is shown.
4427 */
4428 public BigPictureStyle bigLargeIcon(Icon icon) {
Chris Wren3745a3d2012-05-22 15:11:52 -04004429 mBigLargeIconSet = true;
Dan Sandlerd63f9322015-05-06 15:18:49 -04004430 mBigLargeIcon = icon;
Chris Wren3745a3d2012-05-22 15:11:52 -04004431 return this;
4432 }
4433
Riley Andrews0394a0c2015-11-03 23:36:52 -08004434 /** @hide */
4435 public static final int MIN_ASHMEM_BITMAP_SIZE = 128 * (1 << 10);
4436
Daniel Sandler0ec46202015-06-24 01:27:05 -04004437 /**
4438 * @hide
4439 */
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07004440 @Override
4441 public void purgeResources() {
4442 super.purgeResources();
Riley Andrews8cee7c12015-11-01 23:36:04 -08004443 if (mPicture != null &&
4444 mPicture.isMutable() &&
Riley Andrews0394a0c2015-11-03 23:36:52 -08004445 mPicture.getAllocationByteCount() >= MIN_ASHMEM_BITMAP_SIZE) {
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07004446 mPicture = mPicture.createAshmemBitmap();
4447 }
4448 if (mBigLargeIcon != null) {
4449 mBigLargeIcon.convertToAshmem();
4450 }
4451 }
Christoph Studer5c510ee2014-12-15 16:32:27 +01004452
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004453 /**
4454 * @hide
4455 */
4456 public RemoteViews makeBigContentView() {
4457 // Replace mN.mLargeIcon with mBigLargeIcon if mBigLargeIconSet
Christoph Studer5c510ee2014-12-15 16:32:27 +01004458 // This covers the following cases:
4459 // 1. mBigLargeIconSet -> mBigLargeIcon (null or non-null) applies, overrides
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004460 // mN.mLargeIcon
4461 // 2. !mBigLargeIconSet -> mN.mLargeIcon applies
Dan Sandlerd63f9322015-05-06 15:18:49 -04004462 Icon oldLargeIcon = null;
Selim Cineke99acb22016-08-04 12:55:48 -07004463 Bitmap largeIconLegacy = null;
Christoph Studer5c510ee2014-12-15 16:32:27 +01004464 if (mBigLargeIconSet) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004465 oldLargeIcon = mBuilder.mN.mLargeIcon;
4466 mBuilder.mN.mLargeIcon = mBigLargeIcon;
Selim Cineke99acb22016-08-04 12:55:48 -07004467 // The legacy largeIcon might not allow us to clear the image, as it's taken in
4468 // replacement if the other one is null. Because we're restoring these legacy icons
4469 // for old listeners, this is in general non-null.
4470 largeIconLegacy = mBuilder.mN.largeIcon;
4471 mBuilder.mN.largeIcon = null;
Christoph Studer5c510ee2014-12-15 16:32:27 +01004472 }
4473
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004474 RemoteViews contentView = getStandardView(mBuilder.getBigPictureLayoutResource());
Selim Cinek03d0d652015-11-13 13:18:09 -05004475 if (mSummaryTextSet) {
4476 contentView.setTextViewText(R.id.text, mBuilder.processLegacyText(mSummaryText));
Selim Cinekc848c3a2016-01-13 15:27:30 -08004477 contentView.setViewVisibility(R.id.text, View.VISIBLE);
Selim Cinek03d0d652015-11-13 13:18:09 -05004478 }
Selim Cinek279fa862016-06-14 10:57:25 -07004479 mBuilder.setContentMinHeight(contentView, mBuilder.mN.hasLargeIcon());
Selim Cinek53e64a42015-11-16 10:40:56 -08004480
Christoph Studer5c510ee2014-12-15 16:32:27 +01004481 if (mBigLargeIconSet) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004482 mBuilder.mN.mLargeIcon = oldLargeIcon;
Selim Cineke99acb22016-08-04 12:55:48 -07004483 mBuilder.mN.largeIcon = largeIconLegacy;
Christoph Studer5c510ee2014-12-15 16:32:27 +01004484 }
4485
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004486 contentView.setImageViewBitmap(R.id.big_picture, mPicture);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004487 return contentView;
4488 }
4489
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004490 /**
4491 * @hide
4492 */
4493 public void addExtras(Bundle extras) {
4494 super.addExtras(extras);
4495
4496 if (mBigLargeIconSet) {
4497 extras.putParcelable(EXTRA_LARGE_ICON_BIG, mBigLargeIcon);
4498 }
4499 extras.putParcelable(EXTRA_PICTURE, mPicture);
4500 }
4501
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04004502 /**
4503 * @hide
4504 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004505 @Override
Christoph Studer4600f9b2014-07-22 22:44:43 +02004506 protected void restoreFromExtras(Bundle extras) {
4507 super.restoreFromExtras(extras);
4508
4509 if (extras.containsKey(EXTRA_LARGE_ICON_BIG)) {
Christoph Studer5c510ee2014-12-15 16:32:27 +01004510 mBigLargeIconSet = true;
Christoph Studer4600f9b2014-07-22 22:44:43 +02004511 mBigLargeIcon = extras.getParcelable(EXTRA_LARGE_ICON_BIG);
Chris Wren3745a3d2012-05-22 15:11:52 -04004512 }
Christoph Studer4600f9b2014-07-22 22:44:43 +02004513 mPicture = extras.getParcelable(EXTRA_PICTURE);
4514 }
Selim Cinek03d0d652015-11-13 13:18:09 -05004515
4516 /**
4517 * @hide
4518 */
4519 @Override
4520 public boolean hasSummaryInHeader() {
4521 return false;
4522 }
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004523 }
4524
4525 /**
Daniel Sandler4dfbe832012-04-11 14:51:46 -04004526 * Helper class for generating large-format notifications that include a lot of text.
Joe Malin8d40d042012-11-05 11:36:40 -08004527 *
Robert Ly91c5ce32014-06-08 15:37:00 -07004528 * Here's how you'd set the <code>BigTextStyle</code> on a notification:
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004529 * <pre class="prettyprint">
Robert Ly91c5ce32014-06-08 15:37:00 -07004530 * Notification notif = new Notification.Builder(mContext)
4531 * .setContentTitle(&quot;New mail from &quot; + sender.toString())
4532 * .setContentText(subject)
4533 * .setSmallIcon(R.drawable.new_mail)
4534 * .setLargeIcon(aBitmap)
4535 * .setStyle(new Notification.BigTextStyle()
4536 * .bigText(aVeryLongString))
4537 * .build();
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004538 * </pre>
Joe Malin8d40d042012-11-05 11:36:40 -08004539 *
Daniel Sandler4dfbe832012-04-11 14:51:46 -04004540 * @see Notification#bigContentView
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004541 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004542 public static class BigTextStyle extends Style {
Jorim Jaggi457a10d2014-09-08 16:18:23 +02004543
4544 private static final int MAX_LINES = 13;
Selim Cinek3a2c4b92015-12-17 17:01:17 -08004545 private static final int LINES_CONSUMED_BY_ACTIONS = 4;
Jorim Jaggi457a10d2014-09-08 16:18:23 +02004546
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004547 private CharSequence mBigText;
4548
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004549 public BigTextStyle() {
4550 }
4551
Adrian Roosf5faf9d2016-05-23 13:56:15 -07004552 /**
4553 * @deprecated use {@code BigTextStyle()}.
4554 */
4555 @Deprecated
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004556 public BigTextStyle(Builder builder) {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004557 setBuilder(builder);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004558 }
4559
Chris Wrend6297db2012-05-03 16:20:13 -04004560 /**
4561 * Overrides ContentTitle in the big form of the template.
4562 * This defaults to the value passed to setContentTitle().
4563 */
4564 public BigTextStyle setBigContentTitle(CharSequence title) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04004565 internalSetBigContentTitle(safeCharSequence(title));
Chris Wrend6297db2012-05-03 16:20:13 -04004566 return this;
4567 }
4568
4569 /**
4570 * Set the first line of text after the detail section in the big form of the template.
4571 */
4572 public BigTextStyle setSummaryText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04004573 internalSetSummaryText(safeCharSequence(cs));
Chris Wrend6297db2012-05-03 16:20:13 -04004574 return this;
4575 }
4576
Chris Wren0bd664d2012-08-01 13:56:56 -04004577 /**
4578 * Provide the longer text to be displayed in the big form of the
4579 * template in place of the content text.
4580 */
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004581 public BigTextStyle bigText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04004582 mBigText = safeCharSequence(cs);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004583 return this;
4584 }
4585
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004586 /**
4587 * @hide
4588 */
4589 public void addExtras(Bundle extras) {
4590 super.addExtras(extras);
4591
Christoph Studer4600f9b2014-07-22 22:44:43 +02004592 extras.putCharSequence(EXTRA_BIG_TEXT, mBigText);
4593 }
4594
4595 /**
4596 * @hide
4597 */
4598 @Override
4599 protected void restoreFromExtras(Bundle extras) {
4600 super.restoreFromExtras(extras);
4601
4602 mBigText = extras.getCharSequence(EXTRA_BIG_TEXT);
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004603 }
4604
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004605 /**
4606 * @hide
4607 */
4608 public RemoteViews makeBigContentView() {
Christoph Studer4600f9b2014-07-22 22:44:43 +02004609
4610 // Nasty
Selim Cinek75998782016-04-26 10:39:17 -07004611 CharSequence text = mBuilder.getAllExtras().getCharSequence(EXTRA_TEXT);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004612 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, null);
Daniel Sandler916ad912012-06-13 12:17:07 -04004613
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004614 RemoteViews contentView = getStandardView(mBuilder.getBigTextLayoutResource());
Joe Malin8d40d042012-11-05 11:36:40 -08004615
Selim Cinek75998782016-04-26 10:39:17 -07004616 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, text);
Christoph Studer4600f9b2014-07-22 22:44:43 +02004617
Selim Cinek3a2c4b92015-12-17 17:01:17 -08004618 CharSequence bigTextText = mBuilder.processLegacyText(mBigText);
Selim Cinek75998782016-04-26 10:39:17 -07004619 if (TextUtils.isEmpty(bigTextText)) {
4620 // In case the bigtext is null / empty fall back to the normal text to avoid a weird
4621 // experience
4622 bigTextText = mBuilder.processLegacyText(text);
4623 }
Adrian Roosb1f427c2016-05-26 12:27:15 -07004624 applyBigTextContentView(mBuilder, contentView, bigTextText);
Selim Cinek4fb12d32015-11-19 18:10:48 -08004625
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004626 return contentView;
4627 }
4628
Adrian Roosb1f427c2016-05-26 12:27:15 -07004629 static void applyBigTextContentView(Builder builder,
4630 RemoteViews contentView, CharSequence bigTextText) {
4631 contentView.setTextViewText(R.id.big_text, bigTextText);
4632 contentView.setViewVisibility(R.id.big_text,
4633 TextUtils.isEmpty(bigTextText) ? View.GONE : View.VISIBLE);
4634 contentView.setInt(R.id.big_text, "setMaxLines", calculateMaxLines(builder));
Selim Cinek279fa862016-06-14 10:57:25 -07004635 contentView.setBoolean(R.id.big_text, "setHasImage", builder.mN.hasLargeIcon());
Adrian Roosb1f427c2016-05-26 12:27:15 -07004636 }
4637
4638 private static int calculateMaxLines(Builder builder) {
Jorim Jaggi457a10d2014-09-08 16:18:23 +02004639 int lineCount = MAX_LINES;
Adrian Roosb1f427c2016-05-26 12:27:15 -07004640 boolean hasActions = builder.mActions.size() > 0;
Jorim Jaggi457a10d2014-09-08 16:18:23 +02004641 if (hasActions) {
4642 lineCount -= LINES_CONSUMED_BY_ACTIONS;
4643 }
Jorim Jaggi457a10d2014-09-08 16:18:23 +02004644 return lineCount;
4645 }
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004646 }
Daniel Sandler879c5e02012-04-17 16:46:51 -04004647
4648 /**
Alex Hillsfc737de2016-03-23 17:33:02 -04004649 * Helper class for generating large-format notifications that include multiple back-and-forth
4650 * messages of varying types between any number of people.
4651 *
4652 * <br>
4653 * If the platform does not provide large-format notifications, this method has no effect. The
4654 * user will always see the normal notification view.
4655 * <br>
4656 * This class is a "rebuilder": It attaches to a Builder object and modifies its behavior, like
4657 * so:
4658 * <pre class="prettyprint">
4659 *
4660 * Notification noti = new Notification.Builder()
4661 * .setContentTitle(&quot;2 new messages wtih &quot; + sender.toString())
4662 * .setContentText(subject)
4663 * .setSmallIcon(R.drawable.new_message)
4664 * .setLargeIcon(aBitmap)
4665 * .setStyle(new Notification.MessagingStyle(resources.getString(R.string.reply_name))
4666 * .addMessage(messages[0].getText(), messages[0].getTime(), messages[0].getSender())
4667 * .addMessage(messages[1].getText(), messages[1].getTime(), messages[1].getSender()))
4668 * .build();
4669 * </pre>
4670 */
4671 public static class MessagingStyle extends Style {
4672
4673 /**
4674 * The maximum number of messages that will be retained in the Notification itself (the
4675 * number displayed is up to the platform).
4676 */
4677 public static final int MAXIMUM_RETAINED_MESSAGES = 25;
4678
4679 CharSequence mUserDisplayName;
4680 CharSequence mConversationTitle;
Alex Hillsd9b04d92016-04-11 16:38:16 -04004681 List<Message> mMessages = new ArrayList<>();
Alex Hillsfc737de2016-03-23 17:33:02 -04004682
4683 MessagingStyle() {
4684 }
4685
4686 /**
Alex Hillsfd590442016-10-07 09:52:44 -04004687 * @param userDisplayName Required - the name to be displayed for any replies sent by the
4688 * user before the posting app reposts the notification with those messages after they've
4689 * been actually sent and in previous messages sent by the user added in
Alex Hillsfc737de2016-03-23 17:33:02 -04004690 * {@link #addMessage(Notification.MessagingStyle.Message)}
4691 */
Alex Hillsfd590442016-10-07 09:52:44 -04004692 public MessagingStyle(@NonNull CharSequence userDisplayName) {
Alex Hillsfc737de2016-03-23 17:33:02 -04004693 mUserDisplayName = userDisplayName;
4694 }
4695
4696 /**
4697 * Returns the name to be displayed for any replies sent by the user
4698 */
4699 public CharSequence getUserDisplayName() {
4700 return mUserDisplayName;
4701 }
4702
4703 /**
Alex Hillsfc737de2016-03-23 17:33:02 -04004704 * Sets the title to be displayed on this conversation. This should only be used for
4705 * group messaging and left unset for one-on-one conversations.
4706 * @param conversationTitle
4707 * @return this object for method chaining.
4708 */
4709 public MessagingStyle setConversationTitle(CharSequence conversationTitle) {
4710 mConversationTitle = conversationTitle;
4711 return this;
4712 }
4713
4714 /**
4715 * Return the title to be displayed on this conversation. Can be <code>null</code> and
4716 * should be for one-on-one conversations
4717 */
4718 public CharSequence getConversationTitle() {
4719 return mConversationTitle;
4720 }
4721
4722 /**
4723 * Adds a message for display by this notification. Convenience call for a simple
4724 * {@link Message} in {@link #addMessage(Notification.MessagingStyle.Message)}.
4725 * @param text A {@link CharSequence} to be displayed as the message content
4726 * @param timestamp Time at which the message arrived
4727 * @param sender A {@link CharSequence} to be used for displaying the name of the
4728 * sender. Should be <code>null</code> for messages by the current user, in which case
4729 * the platform will insert {@link #getUserDisplayName()}.
4730 * Should be unique amongst all individuals in the conversation, and should be
4731 * consistent during re-posts of the notification.
4732 *
4733 * @see Message#Message(CharSequence, long, CharSequence)
4734 *
4735 * @return this object for method chaining
4736 */
4737 public MessagingStyle addMessage(CharSequence text, long timestamp, CharSequence sender) {
4738 mMessages.add(new Message(text, timestamp, sender));
4739 if (mMessages.size() > MAXIMUM_RETAINED_MESSAGES) {
4740 mMessages.remove(0);
4741 }
4742 return this;
4743 }
4744
4745 /**
4746 * Adds a {@link Message} for display in this notification.
4747 * @param message The {@link Message} to be displayed
4748 * @return this object for method chaining
4749 */
4750 public MessagingStyle addMessage(Message message) {
4751 mMessages.add(message);
4752 if (mMessages.size() > MAXIMUM_RETAINED_MESSAGES) {
4753 mMessages.remove(0);
4754 }
4755 return this;
4756 }
4757
4758 /**
4759 * Gets the list of {@code Message} objects that represent the notification
4760 */
4761 public List<Message> getMessages() {
4762 return mMessages;
4763 }
4764
4765 /**
4766 * @hide
4767 */
4768 @Override
4769 public void addExtras(Bundle extras) {
4770 super.addExtras(extras);
4771 if (mUserDisplayName != null) {
4772 extras.putCharSequence(EXTRA_SELF_DISPLAY_NAME, mUserDisplayName);
4773 }
4774 if (mConversationTitle != null) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04004775 extras.putCharSequence(EXTRA_CONVERSATION_TITLE, mConversationTitle);
Alex Hillsfc737de2016-03-23 17:33:02 -04004776 }
Alex Hillsd9b04d92016-04-11 16:38:16 -04004777 if (!mMessages.isEmpty()) { extras.putParcelableArray(EXTRA_MESSAGES,
4778 Message.getBundleArrayForMessages(mMessages));
Alex Hillsfc737de2016-03-23 17:33:02 -04004779 }
Adrian Roos33fbd2c2016-05-27 15:35:28 -07004780
4781 fixTitleAndTextExtras(extras);
4782 }
4783
4784 private void fixTitleAndTextExtras(Bundle extras) {
4785 Message m = findLatestIncomingMessage();
4786 CharSequence text = (m == null) ? null : m.mText;
4787 CharSequence sender = m == null ? null
4788 : TextUtils.isEmpty(m.mSender) ? mUserDisplayName : m.mSender;
4789 CharSequence title;
4790 if (!TextUtils.isEmpty(mConversationTitle)) {
4791 if (!TextUtils.isEmpty(sender)) {
4792 BidiFormatter bidi = BidiFormatter.getInstance();
4793 title = mBuilder.mContext.getString(
4794 com.android.internal.R.string.notification_messaging_title_template,
4795 bidi.unicodeWrap(mConversationTitle), bidi.unicodeWrap(m.mSender));
4796 } else {
4797 title = mConversationTitle;
4798 }
4799 } else {
4800 title = sender;
4801 }
4802
4803 if (title != null) {
4804 extras.putCharSequence(EXTRA_TITLE, title);
4805 }
4806 if (text != null) {
4807 extras.putCharSequence(EXTRA_TEXT, text);
4808 }
Alex Hillsfc737de2016-03-23 17:33:02 -04004809 }
4810
4811 /**
4812 * @hide
4813 */
4814 @Override
4815 protected void restoreFromExtras(Bundle extras) {
4816 super.restoreFromExtras(extras);
4817
4818 mMessages.clear();
Adrian Roos96b7e202016-05-17 13:50:38 -07004819 mUserDisplayName = extras.getCharSequence(EXTRA_SELF_DISPLAY_NAME);
4820 mConversationTitle = extras.getCharSequence(EXTRA_CONVERSATION_TITLE);
Alex Hillsd9b04d92016-04-11 16:38:16 -04004821 Parcelable[] parcelables = extras.getParcelableArray(EXTRA_MESSAGES);
Adrian Roosdedd1df2016-04-26 16:38:47 -07004822 if (parcelables != null && parcelables instanceof Parcelable[]) {
4823 mMessages = Message.getMessagesFromBundleArray(parcelables);
Alex Hillsfc737de2016-03-23 17:33:02 -04004824 }
4825 }
4826
4827 /**
4828 * @hide
4829 */
Adrian Roosc1a80b02016-04-05 14:54:55 -07004830 @Override
4831 public RemoteViews makeContentView() {
4832 Message m = findLatestIncomingMessage();
4833 CharSequence title = mConversationTitle != null
4834 ? mConversationTitle
4835 : (m == null) ? null : m.mSender;
4836 CharSequence text = (m == null)
4837 ? null
4838 : mConversationTitle != null ? makeMessageLine(m) : m.mText;
Alex Hillsfc737de2016-03-23 17:33:02 -04004839
Adrian Roosc1a80b02016-04-05 14:54:55 -07004840 return mBuilder.applyStandardTemplate(mBuilder.getBaseLayoutResource(),
4841 false /* hasProgress */,
4842 title,
4843 text);
4844 }
4845
4846 private Message findLatestIncomingMessage() {
4847 for (int i = mMessages.size() - 1; i >= 0; i--) {
4848 Message m = mMessages.get(i);
4849 // Incoming messages have a non-empty sender.
4850 if (!TextUtils.isEmpty(m.mSender)) {
4851 return m;
4852 }
4853 }
Adrian Roos33fbd2c2016-05-27 15:35:28 -07004854 if (!mMessages.isEmpty()) {
4855 // No incoming messages, fall back to outgoing message
4856 return mMessages.get(mMessages.size() - 1);
4857 }
Adrian Roosc1a80b02016-04-05 14:54:55 -07004858 return null;
4859 }
4860
4861 /**
4862 * @hide
4863 */
4864 @Override
4865 public RemoteViews makeBigContentView() {
4866 CharSequence title = !TextUtils.isEmpty(super.mBigContentTitle)
4867 ? super.mBigContentTitle
4868 : mConversationTitle;
4869 boolean hasTitle = !TextUtils.isEmpty(title);
4870
Adrian Roosfeafa052016-06-01 17:09:45 -07004871 if (mMessages.size() == 1) {
4872 // Special case for a single message: Use the big text style
4873 // so the collapsed and expanded versions match nicely.
4874 CharSequence bigTitle;
4875 CharSequence text;
4876 if (hasTitle) {
4877 bigTitle = title;
4878 text = makeMessageLine(mMessages.get(0));
4879 } else {
4880 bigTitle = mMessages.get(0).mSender;
4881 text = mMessages.get(0).mText;
4882 }
Adrian Roosb1f427c2016-05-26 12:27:15 -07004883 RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(
4884 mBuilder.getBigTextLayoutResource(),
Adrian Roosfeafa052016-06-01 17:09:45 -07004885 false /* progress */, bigTitle, null /* text */);
Adrian Roosb1f427c2016-05-26 12:27:15 -07004886 BigTextStyle.applyBigTextContentView(mBuilder, contentView, text);
4887 return contentView;
4888 }
4889
Adrian Roos48d746a2016-04-12 14:57:28 -07004890 RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(
Adrian Roosc1a80b02016-04-05 14:54:55 -07004891 mBuilder.getMessagingLayoutResource(),
4892 false /* hasProgress */,
4893 title,
4894 null /* text */);
4895
4896 int[] rowIds = {R.id.inbox_text0, R.id.inbox_text1, R.id.inbox_text2, R.id.inbox_text3,
4897 R.id.inbox_text4, R.id.inbox_text5, R.id.inbox_text6};
4898
4899 // Make sure all rows are gone in case we reuse a view.
4900 for (int rowId : rowIds) {
4901 contentView.setViewVisibility(rowId, View.GONE);
4902 }
4903
4904 int i=0;
Adrian Roos2d5dbba2016-06-08 17:11:53 -07004905 contentView.setViewLayoutMarginBottomDimen(R.id.line1,
4906 hasTitle ? R.dimen.notification_messaging_spacing : 0);
Adrian Roosc1a80b02016-04-05 14:54:55 -07004907 contentView.setInt(R.id.notification_messaging, "setNumIndentLines",
Selim Cinek279fa862016-06-14 10:57:25 -07004908 !mBuilder.mN.hasLargeIcon() ? 0 : (hasTitle ? 1 : 2));
Adrian Roosc1a80b02016-04-05 14:54:55 -07004909
Adrian Roosfeafa052016-06-01 17:09:45 -07004910 int contractedChildId = View.NO_ID;
4911 Message contractedMessage = findLatestIncomingMessage();
Adrian Roosc1a80b02016-04-05 14:54:55 -07004912 int firstMessage = Math.max(0, mMessages.size() - rowIds.length);
4913 while (firstMessage + i < mMessages.size() && i < rowIds.length) {
4914 Message m = mMessages.get(firstMessage + i);
4915 int rowId = rowIds[i];
4916
4917 contentView.setViewVisibility(rowId, View.VISIBLE);
4918 contentView.setTextViewText(rowId, makeMessageLine(m));
4919
Adrian Roosfeafa052016-06-01 17:09:45 -07004920 if (contractedMessage == m) {
4921 contractedChildId = rowId;
4922 }
4923
Adrian Roosc1a80b02016-04-05 14:54:55 -07004924 i++;
4925 }
Adrian Roosfeafa052016-06-01 17:09:45 -07004926 // Record this here to allow transformation between the contracted and expanded views.
4927 contentView.setInt(R.id.notification_messaging, "setContractedChildId",
4928 contractedChildId);
Alex Hillsfc737de2016-03-23 17:33:02 -04004929 return contentView;
4930 }
4931
Adrian Roosc1a80b02016-04-05 14:54:55 -07004932 private CharSequence makeMessageLine(Message m) {
4933 BidiFormatter bidi = BidiFormatter.getInstance();
4934 SpannableStringBuilder sb = new SpannableStringBuilder();
4935 if (TextUtils.isEmpty(m.mSender)) {
4936 CharSequence replyName = mUserDisplayName == null ? "" : mUserDisplayName;
4937 sb.append(bidi.unicodeWrap(replyName),
4938 makeFontColorSpan(mBuilder.resolveContrastColor()),
4939 0 /* flags */);
4940 } else {
4941 sb.append(bidi.unicodeWrap(m.mSender),
4942 makeFontColorSpan(Color.BLACK),
4943 0 /* flags */);
4944 }
4945 CharSequence text = m.mText == null ? "" : m.mText;
4946 sb.append(" ").append(bidi.unicodeWrap(text));
4947 return sb;
4948 }
4949
Adrian Roosdedd1df2016-04-26 16:38:47 -07004950 /**
4951 * @hide
4952 */
4953 @Override
4954 public RemoteViews makeHeadsUpContentView() {
4955 Message m = findLatestIncomingMessage();
4956 CharSequence title = mConversationTitle != null
4957 ? mConversationTitle
4958 : (m == null) ? null : m.mSender;
4959 CharSequence text = (m == null)
4960 ? null
4961 : mConversationTitle != null ? makeMessageLine(m) : m.mText;
4962
4963 return mBuilder.applyStandardTemplateWithActions(mBuilder.getBigBaseLayoutResource(),
4964 false /* hasProgress */,
4965 title,
4966 text);
4967 }
4968
Adrian Roosc1a80b02016-04-05 14:54:55 -07004969 private static TextAppearanceSpan makeFontColorSpan(int color) {
4970 return new TextAppearanceSpan(null, 0, 0,
4971 ColorStateList.valueOf(color), null);
4972 }
4973
Alex Hillsd9b04d92016-04-11 16:38:16 -04004974 public static final class Message {
4975
4976 static final String KEY_TEXT = "text";
4977 static final String KEY_TIMESTAMP = "time";
4978 static final String KEY_SENDER = "sender";
4979 static final String KEY_DATA_MIME_TYPE = "type";
4980 static final String KEY_DATA_URI= "uri";
Alex Hillsfc737de2016-03-23 17:33:02 -04004981
4982 private final CharSequence mText;
4983 private final long mTimestamp;
4984 private final CharSequence mSender;
4985
4986 private String mDataMimeType;
4987 private Uri mDataUri;
4988
4989 /**
4990 * Constructor
4991 * @param text A {@link CharSequence} to be displayed as the message content
4992 * @param timestamp Time at which the message arrived
4993 * @param sender A {@link CharSequence} to be used for displaying the name of the
4994 * sender. Should be <code>null</code> for messages by the current user, in which case
4995 * the platform will insert {@link MessagingStyle#getUserDisplayName()}.
4996 * Should be unique amongst all individuals in the conversation, and should be
4997 * consistent during re-posts of the notification.
4998 */
4999 public Message(CharSequence text, long timestamp, CharSequence sender){
5000 mText = text;
5001 mTimestamp = timestamp;
5002 mSender = sender;
5003 }
5004
5005 /**
5006 * Sets a binary blob of data and an associated MIME type for a message. In the case
5007 * where the platform doesn't support the MIME type, the original text provided in the
5008 * constructor will be used.
5009 * @param dataMimeType The MIME type of the content. See
5010 * <a href="{@docRoot}notifications/messaging.html"> for the list of supported MIME
5011 * types on Android and Android Wear.
5012 * @param dataUri The uri containing the content whose type is given by the MIME type.
5013 * <p class="note">
5014 * <ol>
5015 * <li>Notification Listeners including the System UI need permission to access the
5016 * data the Uri points to. The recommended ways to do this are:</li>
5017 * <li>Store the data in your own ContentProvider, making sure that other apps have
5018 * the correct permission to access your provider. The preferred mechanism for
5019 * providing access is to use per-URI permissions which are temporary and only
5020 * grant access to the receiving application. An easy way to create a
5021 * ContentProvider like this is to use the FileProvider helper class.</li>
5022 * <li>Use the system MediaStore. The MediaStore is primarily aimed at video, audio
5023 * and image MIME types, however beginning with Android 3.0 (API level 11) it can
5024 * also store non-media types (see MediaStore.Files for more info). Files can be
5025 * inserted into the MediaStore using scanFile() after which a content:// style
5026 * Uri suitable for sharing is passed to the provided onScanCompleted() callback.
5027 * Note that once added to the system MediaStore the content is accessible to any
5028 * app on the device.</li>
5029 * </ol>
5030 * @return this object for method chaining
5031 */
5032 public Message setData(String dataMimeType, Uri dataUri) {
5033 mDataMimeType = dataMimeType;
5034 mDataUri = dataUri;
5035 return this;
5036 }
5037
Alex Hillsfc737de2016-03-23 17:33:02 -04005038 /**
5039 * Get the text to be used for this message, or the fallback text if a type and content
5040 * Uri have been set
5041 */
5042 public CharSequence getText() {
5043 return mText;
5044 }
5045
5046 /**
5047 * Get the time at which this message arrived
5048 */
5049 public long getTimestamp() {
5050 return mTimestamp;
5051 }
5052
5053 /**
5054 * Get the text used to display the contact's name in the messaging experience
5055 */
5056 public CharSequence getSender() {
5057 return mSender;
5058 }
5059
5060 /**
5061 * Get the MIME type of the data pointed to by the Uri
5062 */
5063 public String getDataMimeType() {
5064 return mDataMimeType;
5065 }
5066
5067 /**
5068 * Get the the Uri pointing to the content of the message. Can be null, in which case
5069 * {@see #getText()} is used.
5070 */
5071 public Uri getDataUri() {
5072 return mDataUri;
5073 }
5074
Alex Hillsd9b04d92016-04-11 16:38:16 -04005075 private Bundle toBundle() {
5076 Bundle bundle = new Bundle();
Alex Hillsfc737de2016-03-23 17:33:02 -04005077 if (mText != null) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04005078 bundle.putCharSequence(KEY_TEXT, mText);
Alex Hillsfc737de2016-03-23 17:33:02 -04005079 }
Alex Hillsd9b04d92016-04-11 16:38:16 -04005080 bundle.putLong(KEY_TIMESTAMP, mTimestamp);
Alex Hillsfc737de2016-03-23 17:33:02 -04005081 if (mSender != null) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04005082 bundle.putCharSequence(KEY_SENDER, mSender);
Alex Hillsfc737de2016-03-23 17:33:02 -04005083 }
5084 if (mDataMimeType != null) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04005085 bundle.putString(KEY_DATA_MIME_TYPE, mDataMimeType);
Alex Hillsfc737de2016-03-23 17:33:02 -04005086 }
5087 if (mDataUri != null) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04005088 bundle.putParcelable(KEY_DATA_URI, mDataUri);
Alex Hillsfc737de2016-03-23 17:33:02 -04005089 }
Alex Hillsd9b04d92016-04-11 16:38:16 -04005090 return bundle;
Alex Hillsfc737de2016-03-23 17:33:02 -04005091 }
5092
Alex Hillsd9b04d92016-04-11 16:38:16 -04005093 static Bundle[] getBundleArrayForMessages(List<Message> messages) {
5094 Bundle[] bundles = new Bundle[messages.size()];
5095 final int N = messages.size();
5096 for (int i = 0; i < N; i++) {
5097 bundles[i] = messages.get(i).toBundle();
5098 }
5099 return bundles;
5100 }
5101
Adrian Roosdedd1df2016-04-26 16:38:47 -07005102 static List<Message> getMessagesFromBundleArray(Parcelable[] bundles) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04005103 List<Message> messages = new ArrayList<>(bundles.length);
5104 for (int i = 0; i < bundles.length; i++) {
Adrian Roosdedd1df2016-04-26 16:38:47 -07005105 if (bundles[i] instanceof Bundle) {
5106 Message message = getMessageFromBundle((Bundle)bundles[i]);
5107 if (message != null) {
5108 messages.add(message);
5109 }
Alex Hillsd9b04d92016-04-11 16:38:16 -04005110 }
5111 }
5112 return messages;
5113 }
5114
5115 static Message getMessageFromBundle(Bundle bundle) {
5116 try {
Adrian Roosfbddd2c2016-05-13 12:57:20 -07005117 if (!bundle.containsKey(KEY_TEXT) || !bundle.containsKey(KEY_TIMESTAMP)) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04005118 return null;
5119 } else {
5120 Message message = new Message(bundle.getCharSequence(KEY_TEXT),
5121 bundle.getLong(KEY_TIMESTAMP), bundle.getCharSequence(KEY_SENDER));
5122 if (bundle.containsKey(KEY_DATA_MIME_TYPE) &&
5123 bundle.containsKey(KEY_DATA_URI)) {
5124
5125 message.setData(bundle.getString(KEY_DATA_MIME_TYPE),
5126 (Uri) bundle.getParcelable(KEY_DATA_URI));
Alex Hillsfc737de2016-03-23 17:33:02 -04005127 }
Alex Hillsd9b04d92016-04-11 16:38:16 -04005128 return message;
5129 }
5130 } catch (ClassCastException e) {
5131 return null;
5132 }
5133 }
Alex Hillsfc737de2016-03-23 17:33:02 -04005134 }
5135 }
5136
5137 /**
Daniel Sandler879c5e02012-04-17 16:46:51 -04005138 * Helper class for generating large-format notifications that include a list of (up to 5) strings.
Joe Malin8d40d042012-11-05 11:36:40 -08005139 *
Robert Ly91c5ce32014-06-08 15:37:00 -07005140 * Here's how you'd set the <code>InboxStyle</code> on a notification:
Daniel Sandler879c5e02012-04-17 16:46:51 -04005141 * <pre class="prettyprint">
Robert Ly91c5ce32014-06-08 15:37:00 -07005142 * Notification notif = new Notification.Builder(mContext)
5143 * .setContentTitle(&quot;5 New mails from &quot; + sender.toString())
5144 * .setContentText(subject)
5145 * .setSmallIcon(R.drawable.new_mail)
5146 * .setLargeIcon(aBitmap)
5147 * .setStyle(new Notification.InboxStyle()
5148 * .addLine(str1)
5149 * .addLine(str2)
5150 * .setContentTitle(&quot;&quot;)
5151 * .setSummaryText(&quot;+3 more&quot;))
5152 * .build();
Daniel Sandler879c5e02012-04-17 16:46:51 -04005153 * </pre>
Joe Malin8d40d042012-11-05 11:36:40 -08005154 *
Daniel Sandler879c5e02012-04-17 16:46:51 -04005155 * @see Notification#bigContentView
5156 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005157 public static class InboxStyle extends Style {
Daniel Sandler879c5e02012-04-17 16:46:51 -04005158 private ArrayList<CharSequence> mTexts = new ArrayList<CharSequence>(5);
5159
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005160 public InboxStyle() {
5161 }
5162
Adrian Roosf5faf9d2016-05-23 13:56:15 -07005163 /**
5164 * @deprecated use {@code InboxStyle()}.
5165 */
5166 @Deprecated
Daniel Sandler879c5e02012-04-17 16:46:51 -04005167 public InboxStyle(Builder builder) {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005168 setBuilder(builder);
Daniel Sandler879c5e02012-04-17 16:46:51 -04005169 }
5170
Chris Wrend6297db2012-05-03 16:20:13 -04005171 /**
5172 * Overrides ContentTitle in the big form of the template.
5173 * This defaults to the value passed to setContentTitle().
5174 */
5175 public InboxStyle setBigContentTitle(CharSequence title) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04005176 internalSetBigContentTitle(safeCharSequence(title));
Chris Wrend6297db2012-05-03 16:20:13 -04005177 return this;
5178 }
5179
5180 /**
5181 * Set the first line of text after the detail section in the big form of the template.
5182 */
5183 public InboxStyle setSummaryText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04005184 internalSetSummaryText(safeCharSequence(cs));
Chris Wrend6297db2012-05-03 16:20:13 -04005185 return this;
5186 }
5187
Chris Wren0bd664d2012-08-01 13:56:56 -04005188 /**
5189 * Append a line to the digest section of the Inbox notification.
5190 */
Daniel Sandler879c5e02012-04-17 16:46:51 -04005191 public InboxStyle addLine(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04005192 mTexts.add(safeCharSequence(cs));
Daniel Sandler879c5e02012-04-17 16:46:51 -04005193 return this;
5194 }
5195
Daniel Sandlerf45564e2013-04-15 15:05:08 -04005196 /**
5197 * @hide
5198 */
5199 public void addExtras(Bundle extras) {
5200 super.addExtras(extras);
Christoph Studer4600f9b2014-07-22 22:44:43 +02005201
Daniel Sandlerf45564e2013-04-15 15:05:08 -04005202 CharSequence[] a = new CharSequence[mTexts.size()];
5203 extras.putCharSequenceArray(EXTRA_TEXT_LINES, mTexts.toArray(a));
5204 }
5205
Christoph Studer4600f9b2014-07-22 22:44:43 +02005206 /**
5207 * @hide
5208 */
5209 @Override
5210 protected void restoreFromExtras(Bundle extras) {
5211 super.restoreFromExtras(extras);
5212
5213 mTexts.clear();
5214 if (extras.containsKey(EXTRA_TEXT_LINES)) {
5215 Collections.addAll(mTexts, extras.getCharSequenceArray(EXTRA_TEXT_LINES));
5216 }
5217 }
5218
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005219 /**
5220 * @hide
5221 */
5222 public RemoteViews makeBigContentView() {
Selim Cinekc848c3a2016-01-13 15:27:30 -08005223 // Remove the content text so it disappears unless you have a summary
Christoph Studer4600f9b2014-07-22 22:44:43 +02005224 // Nasty
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005225 CharSequence oldBuilderContentText = mBuilder.mN.extras.getCharSequence(EXTRA_TEXT);
5226 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, null);
Christoph Studer4600f9b2014-07-22 22:44:43 +02005227
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01005228 RemoteViews contentView = getStandardView(mBuilder.getInboxLayoutResource());
Daniel Sandler619738c2012-06-07 16:33:08 -04005229
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005230 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, oldBuilderContentText);
Christoph Studer4600f9b2014-07-22 22:44:43 +02005231
Chris Wrend6297db2012-05-03 16:20:13 -04005232 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 -04005233 R.id.inbox_text4, R.id.inbox_text5, R.id.inbox_text6};
Chris Wrend6297db2012-05-03 16:20:13 -04005234
Chris Wren4ed80d52012-05-17 09:30:03 -04005235 // Make sure all rows are gone in case we reuse a view.
5236 for (int rowId : rowIds) {
5237 contentView.setViewVisibility(rowId, View.GONE);
5238 }
5239
Daniel Sandler879c5e02012-04-17 16:46:51 -04005240 int i=0;
Selim Cinek07c80172016-04-21 16:40:47 -07005241 int topPadding = mBuilder.mContext.getResources().getDimensionPixelSize(
5242 R.dimen.notification_inbox_item_top_padding);
Selim Cinek247fa012016-02-18 09:50:48 -08005243 boolean first = true;
Selim Cinek07c80172016-04-21 16:40:47 -07005244 int onlyViewId = 0;
5245 int maxRows = rowIds.length;
5246 if (mBuilder.mActions.size() > 0) {
5247 maxRows--;
5248 }
5249 while (i < mTexts.size() && i < maxRows) {
Daniel Sandler879c5e02012-04-17 16:46:51 -04005250 CharSequence str = mTexts.get(i);
Selim Cinek07c80172016-04-21 16:40:47 -07005251 if (!TextUtils.isEmpty(str)) {
Daniel Sandler879c5e02012-04-17 16:46:51 -04005252 contentView.setViewVisibility(rowIds[i], View.VISIBLE);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01005253 contentView.setTextViewText(rowIds[i], mBuilder.processLegacyText(str));
Selim Cinek07c80172016-04-21 16:40:47 -07005254 contentView.setViewPadding(rowIds[i], 0, topPadding, 0, 0);
Selim Cinek247fa012016-02-18 09:50:48 -08005255 handleInboxImageMargin(contentView, rowIds[i], first);
Selim Cinek07c80172016-04-21 16:40:47 -07005256 if (first) {
5257 onlyViewId = rowIds[i];
5258 } else {
5259 onlyViewId = 0;
5260 }
Selim Cinek247fa012016-02-18 09:50:48 -08005261 first = false;
Daniel Sandler879c5e02012-04-17 16:46:51 -04005262 }
5263 i++;
5264 }
Selim Cinek07c80172016-04-21 16:40:47 -07005265 if (onlyViewId != 0) {
5266 // We only have 1 entry, lets make it look like the normal Text of a Bigtext
5267 topPadding = mBuilder.mContext.getResources().getDimensionPixelSize(
5268 R.dimen.notification_text_margin_top);
5269 contentView.setViewPadding(onlyViewId, 0, topPadding, 0, 0);
5270 }
Selim Cinek1e0bf612015-11-20 15:57:26 -08005271
Daniel Sandler879c5e02012-04-17 16:46:51 -04005272 return contentView;
5273 }
Selim Cinek1e0bf612015-11-20 15:57:26 -08005274
Selim Cinek247fa012016-02-18 09:50:48 -08005275 private void handleInboxImageMargin(RemoteViews contentView, int id, boolean first) {
Selim Cinek1e0bf612015-11-20 15:57:26 -08005276 int endMargin = 0;
Selim Cinek247fa012016-02-18 09:50:48 -08005277 if (first) {
5278 final int max = mBuilder.mN.extras.getInt(EXTRA_PROGRESS_MAX, 0);
5279 final boolean ind = mBuilder.mN.extras.getBoolean(EXTRA_PROGRESS_INDETERMINATE);
5280 boolean hasProgress = max != 0 || ind;
Selim Cinek279fa862016-06-14 10:57:25 -07005281 if (mBuilder.mN.hasLargeIcon() && !hasProgress) {
Adrian Roos2d5dbba2016-06-08 17:11:53 -07005282 endMargin = R.dimen.notification_content_picture_margin;
Selim Cinek247fa012016-02-18 09:50:48 -08005283 }
Selim Cinek1e0bf612015-11-20 15:57:26 -08005284 }
Adrian Roos2d5dbba2016-06-08 17:11:53 -07005285 contentView.setViewLayoutMarginEndDimen(id, endMargin);
Selim Cinek1e0bf612015-11-20 15:57:26 -08005286 }
Daniel Sandler879c5e02012-04-17 16:46:51 -04005287 }
Dan Sandler842dd772014-05-15 09:36:47 -04005288
5289 /**
5290 * Notification style for media playback notifications.
5291 *
5292 * In the expanded form, {@link Notification#bigContentView}, up to 5
5293 * {@link Notification.Action}s specified with
Dan Sandler86647982015-05-13 23:41:13 -04005294 * {@link Notification.Builder#addAction(Action) addAction} will be
Dan Sandler842dd772014-05-15 09:36:47 -04005295 * shown as icon-only pushbuttons, suitable for transport controls. The Bitmap given to
5296 * {@link Notification.Builder#setLargeIcon(android.graphics.Bitmap) setLargeIcon()} will be
5297 * treated as album artwork.
5298 *
5299 * Unlike the other styles provided here, MediaStyle can also modify the standard-size
5300 * {@link Notification#contentView}; by providing action indices to
Christoph Studerfde6f4d2014-12-12 13:23:26 +01005301 * {@link #setShowActionsInCompactView(int...)} you can promote up to 3 actions to be displayed
Dan Sandler842dd772014-05-15 09:36:47 -04005302 * in the standard view alongside the usual content.
5303 *
Bryan Mawhinney6be8de32014-07-18 10:35:12 +01005304 * Notifications created with MediaStyle will have their category set to
5305 * {@link Notification#CATEGORY_TRANSPORT CATEGORY_TRANSPORT} unless you set a different
5306 * category using {@link Notification.Builder#setCategory(String) setCategory()}.
5307 *
Jeff Browndba34ba2014-06-24 20:46:03 -07005308 * Finally, if you attach a {@link android.media.session.MediaSession.Token} using
5309 * {@link android.app.Notification.MediaStyle#setMediaSession(MediaSession.Token)},
Dan Sandler842dd772014-05-15 09:36:47 -04005310 * the System UI can identify this as a notification representing an active media session
5311 * and respond accordingly (by showing album artwork in the lockscreen, for example).
5312 *
5313 * To use this style with your Notification, feed it to
5314 * {@link Notification.Builder#setStyle(android.app.Notification.Style)} like so:
5315 * <pre class="prettyprint">
5316 * Notification noti = new Notification.Builder()
5317 * .setSmallIcon(R.drawable.ic_stat_player)
Christoph Studere935fe92014-11-24 14:18:06 +01005318 * .setContentTitle(&quot;Track title&quot;)
5319 * .setContentText(&quot;Artist - Album&quot;)
5320 * .setLargeIcon(albumArtBitmap))
Bryan Mawhinney6be8de32014-07-18 10:35:12 +01005321 * .setStyle(<b>new Notification.MediaStyle()</b>
5322 * .setMediaSession(mySession))
Dan Sandler842dd772014-05-15 09:36:47 -04005323 * .build();
5324 * </pre>
5325 *
5326 * @see Notification#bigContentView
5327 */
5328 public static class MediaStyle extends Style {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02005329 static final int MAX_MEDIA_BUTTONS_IN_COMPACT = 3;
Dan Sandler842dd772014-05-15 09:36:47 -04005330 static final int MAX_MEDIA_BUTTONS = 5;
5331
5332 private int[] mActionsToShowInCompact = null;
Jeff Browndba34ba2014-06-24 20:46:03 -07005333 private MediaSession.Token mToken;
Dan Sandler842dd772014-05-15 09:36:47 -04005334
5335 public MediaStyle() {
5336 }
5337
Adrian Roosf5faf9d2016-05-23 13:56:15 -07005338 /**
5339 * @deprecated use {@code MediaStyle()}.
5340 */
5341 @Deprecated
Dan Sandler842dd772014-05-15 09:36:47 -04005342 public MediaStyle(Builder builder) {
5343 setBuilder(builder);
5344 }
5345
5346 /**
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02005347 * 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 -04005348 * notification view.
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02005349 *
5350 * @param actions the indices of the actions to show in the compact notification view
Dan Sandler842dd772014-05-15 09:36:47 -04005351 */
5352 public MediaStyle setShowActionsInCompactView(int...actions) {
5353 mActionsToShowInCompact = actions;
5354 return this;
5355 }
5356
5357 /**
Jeff Browndba34ba2014-06-24 20:46:03 -07005358 * Attach a {@link android.media.session.MediaSession.Token} to this Notification
5359 * to provide additional playback information and control to the SystemUI.
Dan Sandler842dd772014-05-15 09:36:47 -04005360 */
Jeff Browndba34ba2014-06-24 20:46:03 -07005361 public MediaStyle setMediaSession(MediaSession.Token token) {
Dan Sandler842dd772014-05-15 09:36:47 -04005362 mToken = token;
5363 return this;
5364 }
5365
Christoph Studer4600f9b2014-07-22 22:44:43 +02005366 /**
5367 * @hide
5368 */
Dan Sandler842dd772014-05-15 09:36:47 -04005369 @Override
5370 public Notification buildStyled(Notification wip) {
Christoph Studer4600f9b2014-07-22 22:44:43 +02005371 super.buildStyled(wip);
Bryan Mawhinney6be8de32014-07-18 10:35:12 +01005372 if (wip.category == null) {
5373 wip.category = Notification.CATEGORY_TRANSPORT;
5374 }
Dan Sandler842dd772014-05-15 09:36:47 -04005375 return wip;
5376 }
5377
Christoph Studer4600f9b2014-07-22 22:44:43 +02005378 /**
5379 * @hide
5380 */
5381 @Override
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005382 public RemoteViews makeContentView() {
5383 return makeMediaContentView();
Christoph Studer4600f9b2014-07-22 22:44:43 +02005384 }
5385
5386 /**
5387 * @hide
5388 */
5389 @Override
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005390 public RemoteViews makeBigContentView() {
5391 return makeMediaBigContentView();
Christoph Studer4600f9b2014-07-22 22:44:43 +02005392 }
5393
Selim Cinekcc10bfb2016-02-10 16:24:21 -08005394 /**
5395 * @hide
5396 */
5397 @Override
5398 public RemoteViews makeHeadsUpContentView() {
5399 RemoteViews expanded = makeMediaBigContentView();
5400 return expanded != null ? expanded : makeMediaContentView();
5401 }
5402
Dan Sandler842dd772014-05-15 09:36:47 -04005403 /** @hide */
5404 @Override
5405 public void addExtras(Bundle extras) {
5406 super.addExtras(extras);
5407
5408 if (mToken != null) {
5409 extras.putParcelable(EXTRA_MEDIA_SESSION, mToken);
5410 }
Bryan Mawhinneye191f902014-07-22 12:50:09 +01005411 if (mActionsToShowInCompact != null) {
5412 extras.putIntArray(EXTRA_COMPACT_ACTIONS, mActionsToShowInCompact);
5413 }
Dan Sandler842dd772014-05-15 09:36:47 -04005414 }
5415
Christoph Studer4600f9b2014-07-22 22:44:43 +02005416 /**
5417 * @hide
5418 */
5419 @Override
5420 protected void restoreFromExtras(Bundle extras) {
5421 super.restoreFromExtras(extras);
5422
5423 if (extras.containsKey(EXTRA_MEDIA_SESSION)) {
5424 mToken = extras.getParcelable(EXTRA_MEDIA_SESSION);
5425 }
5426 if (extras.containsKey(EXTRA_COMPACT_ACTIONS)) {
5427 mActionsToShowInCompact = extras.getIntArray(EXTRA_COMPACT_ACTIONS);
5428 }
5429 }
5430
Selim Cinek5bf069a2015-11-10 19:14:27 -05005431 private RemoteViews generateMediaActionButton(Action action, int color) {
Dan Sandler842dd772014-05-15 09:36:47 -04005432 final boolean tombstone = (action.actionIntent == null);
Selim Cinekf33b1112015-07-15 17:45:11 -07005433 RemoteViews button = new BuilderRemoteViews(mBuilder.mContext.getApplicationInfo(),
Alan Viverette3cb07a462014-06-06 14:19:53 -07005434 R.layout.notification_material_media_action);
Dan Sandler68079d52015-07-22 10:45:30 -04005435 button.setImageViewIcon(R.id.action0, action.getIcon());
Selim Cinek5bf069a2015-11-10 19:14:27 -05005436 button.setDrawableParameters(R.id.action0, false, -1, color, PorterDuff.Mode.SRC_ATOP,
5437 -1);
Dan Sandler842dd772014-05-15 09:36:47 -04005438 if (!tombstone) {
5439 button.setOnClickPendingIntent(R.id.action0, action.actionIntent);
5440 }
5441 button.setContentDescription(R.id.action0, action.title);
5442 return button;
5443 }
5444
5445 private RemoteViews makeMediaContentView() {
5446 RemoteViews view = mBuilder.applyStandardTemplate(
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02005447 R.layout.notification_template_material_media, false /* hasProgress */);
Dan Sandler842dd772014-05-15 09:36:47 -04005448
5449 final int numActions = mBuilder.mActions.size();
5450 final int N = mActionsToShowInCompact == null
5451 ? 0
5452 : Math.min(mActionsToShowInCompact.length, MAX_MEDIA_BUTTONS_IN_COMPACT);
5453 if (N > 0) {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02005454 view.removeAllViews(com.android.internal.R.id.media_actions);
Dan Sandler842dd772014-05-15 09:36:47 -04005455 for (int i = 0; i < N; i++) {
5456 if (i >= numActions) {
5457 throw new IllegalArgumentException(String.format(
5458 "setShowActionsInCompactView: action %d out of bounds (max %d)",
5459 i, numActions - 1));
5460 }
5461
5462 final Action action = mBuilder.mActions.get(mActionsToShowInCompact[i]);
Selim Cinek5bf069a2015-11-10 19:14:27 -05005463 final RemoteViews button = generateMediaActionButton(action,
Adrian Roos4ff3b122016-02-01 12:26:13 -08005464 mBuilder.resolveContrastColor());
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02005465 view.addView(com.android.internal.R.id.media_actions, button);
Dan Sandler842dd772014-05-15 09:36:47 -04005466 }
5467 }
Selim Cinekfdc738f2016-01-27 20:04:27 -08005468 handleImage(view);
5469 // handle the content margin
Adrian Roos2d5dbba2016-06-08 17:11:53 -07005470 int endMargin = R.dimen.notification_content_margin_end;
Selim Cinek279fa862016-06-14 10:57:25 -07005471 if (mBuilder.mN.hasLargeIcon()) {
Adrian Roos2d5dbba2016-06-08 17:11:53 -07005472 endMargin = R.dimen.notification_content_plus_picture_margin_end;
Selim Cinekfdc738f2016-01-27 20:04:27 -08005473 }
Adrian Roos2d5dbba2016-06-08 17:11:53 -07005474 view.setViewLayoutMarginEndDimen(R.id.notification_main_column, endMargin);
Dan Sandler842dd772014-05-15 09:36:47 -04005475 return view;
5476 }
5477
5478 private RemoteViews makeMediaBigContentView() {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02005479 final int actionCount = Math.min(mBuilder.mActions.size(), MAX_MEDIA_BUTTONS);
Selim Cinekcc10bfb2016-02-10 16:24:21 -08005480 // Dont add an expanded view if there is no more content to be revealed
5481 int actionsInCompact = mActionsToShowInCompact == null
5482 ? 0
5483 : Math.min(mActionsToShowInCompact.length, MAX_MEDIA_BUTTONS_IN_COMPACT);
Selim Cinek279fa862016-06-14 10:57:25 -07005484 if (!mBuilder.mN.hasLargeIcon() && actionCount <= actionsInCompact) {
Selim Cinekcc10bfb2016-02-10 16:24:21 -08005485 return null;
5486 }
Selim Cinek5bf069a2015-11-10 19:14:27 -05005487 RemoteViews big = mBuilder.applyStandardTemplate(
5488 R.layout.notification_template_material_big_media,
5489 false);
Dan Sandler842dd772014-05-15 09:36:47 -04005490
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02005491 if (actionCount > 0) {
5492 big.removeAllViews(com.android.internal.R.id.media_actions);
5493 for (int i = 0; i < actionCount; i++) {
Selim Cinek5bf069a2015-11-10 19:14:27 -05005494 final RemoteViews button = generateMediaActionButton(mBuilder.mActions.get(i),
Adrian Roos4ff3b122016-02-01 12:26:13 -08005495 mBuilder.resolveContrastColor());
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02005496 big.addView(com.android.internal.R.id.media_actions, button);
Dan Sandler842dd772014-05-15 09:36:47 -04005497 }
5498 }
Selim Cinek5bf069a2015-11-10 19:14:27 -05005499 handleImage(big);
Dan Sandler842dd772014-05-15 09:36:47 -04005500 return big;
5501 }
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02005502
Selim Cinek5bf069a2015-11-10 19:14:27 -05005503 private void handleImage(RemoteViews contentView) {
Selim Cinek279fa862016-06-14 10:57:25 -07005504 if (mBuilder.mN.hasLargeIcon()) {
Adrian Roos2d5dbba2016-06-08 17:11:53 -07005505 contentView.setViewLayoutMarginEndDimen(R.id.line1, 0);
5506 contentView.setViewLayoutMarginEndDimen(R.id.text, 0);
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02005507 }
5508 }
5509
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02005510 /**
5511 * @hide
5512 */
5513 @Override
5514 protected boolean hasProgress() {
5515 return false;
5516 }
Dan Sandler842dd772014-05-15 09:36:47 -04005517 }
Griff Hazen61a9e862014-05-22 16:05:19 -07005518
Selim Cinek593610c2016-02-16 18:42:57 -08005519 /**
5520 * Notification style for custom views that are decorated by the system
5521 *
5522 * <p>Instead of providing a notification that is completely custom, a developer can set this
5523 * style and still obtain system decorations like the notification header with the expand
5524 * affordance and actions.
5525 *
5526 * <p>Use {@link android.app.Notification.Builder#setCustomContentView(RemoteViews)},
5527 * {@link android.app.Notification.Builder#setCustomBigContentView(RemoteViews)} and
5528 * {@link android.app.Notification.Builder#setCustomHeadsUpContentView(RemoteViews)} to set the
5529 * corresponding custom views to display.
5530 *
5531 * To use this style with your Notification, feed it to
5532 * {@link Notification.Builder#setStyle(android.app.Notification.Style)} like so:
5533 * <pre class="prettyprint">
5534 * Notification noti = new Notification.Builder()
5535 * .setSmallIcon(R.drawable.ic_stat_player)
5536 * .setLargeIcon(albumArtBitmap))
5537 * .setCustomContentView(contentView);
5538 * .setStyle(<b>new Notification.DecoratedCustomViewStyle()</b>)
5539 * .build();
5540 * </pre>
5541 */
5542 public static class DecoratedCustomViewStyle extends Style {
5543
5544 public DecoratedCustomViewStyle() {
5545 }
5546
Selim Cinek593610c2016-02-16 18:42:57 -08005547 /**
5548 * @hide
5549 */
5550 public boolean displayCustomViewInline() {
5551 return true;
5552 }
5553
5554 /**
5555 * @hide
5556 */
5557 @Override
5558 public RemoteViews makeContentView() {
5559 return makeStandardTemplateWithCustomContent(mBuilder.mN.contentView);
5560 }
5561
5562 /**
5563 * @hide
5564 */
5565 @Override
5566 public RemoteViews makeBigContentView() {
5567 return makeDecoratedBigContentView();
5568 }
5569
5570 /**
5571 * @hide
5572 */
5573 @Override
5574 public RemoteViews makeHeadsUpContentView() {
5575 return makeDecoratedHeadsUpContentView();
5576 }
5577
Selim Cinek593610c2016-02-16 18:42:57 -08005578 private RemoteViews makeDecoratedHeadsUpContentView() {
5579 RemoteViews headsUpContentView = mBuilder.mN.headsUpContentView == null
5580 ? mBuilder.mN.contentView
5581 : mBuilder.mN.headsUpContentView;
5582 if (mBuilder.mActions.size() == 0) {
5583 return makeStandardTemplateWithCustomContent(headsUpContentView);
5584 }
5585 RemoteViews remoteViews = mBuilder.applyStandardTemplateWithActions(
5586 mBuilder.getBigBaseLayoutResource());
Selim Cinek247fa012016-02-18 09:50:48 -08005587 buildIntoRemoteViewContent(remoteViews, headsUpContentView);
Selim Cinek593610c2016-02-16 18:42:57 -08005588 return remoteViews;
5589 }
5590
Selim Cinek593610c2016-02-16 18:42:57 -08005591 private RemoteViews makeStandardTemplateWithCustomContent(RemoteViews customContent) {
5592 RemoteViews remoteViews = mBuilder.applyStandardTemplate(
5593 mBuilder.getBaseLayoutResource());
Selim Cinek247fa012016-02-18 09:50:48 -08005594 buildIntoRemoteViewContent(remoteViews, customContent);
Selim Cinek593610c2016-02-16 18:42:57 -08005595 return remoteViews;
5596 }
5597
Selim Cinek593610c2016-02-16 18:42:57 -08005598 private RemoteViews makeDecoratedBigContentView() {
5599 RemoteViews bigContentView = mBuilder.mN.bigContentView == null
5600 ? mBuilder.mN.contentView
5601 : mBuilder.mN.bigContentView;
5602 if (mBuilder.mActions.size() == 0) {
5603 return makeStandardTemplateWithCustomContent(bigContentView);
5604 }
5605 RemoteViews remoteViews = mBuilder.applyStandardTemplateWithActions(
5606 mBuilder.getBigBaseLayoutResource());
Selim Cinek247fa012016-02-18 09:50:48 -08005607 buildIntoRemoteViewContent(remoteViews, bigContentView);
Selim Cinek593610c2016-02-16 18:42:57 -08005608 return remoteViews;
5609 }
Selim Cinek247fa012016-02-18 09:50:48 -08005610
5611 private void buildIntoRemoteViewContent(RemoteViews remoteViews,
5612 RemoteViews customContent) {
Adrian Roos5081c0d2016-02-26 16:04:19 -08005613 if (customContent != null) {
Selim Cinekf91017e2016-03-14 12:25:09 -07005614 // Need to clone customContent before adding, because otherwise it can no longer be
5615 // parceled independently of remoteViews.
Adrian Roos5081c0d2016-02-26 16:04:19 -08005616 customContent = customContent.clone();
Selim Cinekf91017e2016-03-14 12:25:09 -07005617 remoteViews.removeAllViews(R.id.notification_main_column);
5618 remoteViews.addView(R.id.notification_main_column, customContent);
Adrian Roos5081c0d2016-02-26 16:04:19 -08005619 }
Selim Cinek247fa012016-02-18 09:50:48 -08005620 // also update the end margin if there is an image
Adrian Roos2d5dbba2016-06-08 17:11:53 -07005621 int endMargin = R.dimen.notification_content_margin_end;
Selim Cinek279fa862016-06-14 10:57:25 -07005622 if (mBuilder.mN.hasLargeIcon()) {
Adrian Roos2d5dbba2016-06-08 17:11:53 -07005623 endMargin = R.dimen.notification_content_plus_picture_margin_end;
Selim Cinek247fa012016-02-18 09:50:48 -08005624 }
Adrian Roos2d5dbba2016-06-08 17:11:53 -07005625 remoteViews.setViewLayoutMarginEndDimen(R.id.notification_main_column, endMargin);
Selim Cinek247fa012016-02-18 09:50:48 -08005626 }
Selim Cinek593610c2016-02-16 18:42:57 -08005627 }
5628
Selim Cinek03eb3b72016-02-18 10:39:45 -08005629 /**
5630 * Notification style for media custom views that are decorated by the system
5631 *
5632 * <p>Instead of providing a media notification that is completely custom, a developer can set
5633 * this style and still obtain system decorations like the notification header with the expand
5634 * affordance and actions.
5635 *
5636 * <p>Use {@link android.app.Notification.Builder#setCustomContentView(RemoteViews)},
5637 * {@link android.app.Notification.Builder#setCustomBigContentView(RemoteViews)} and
5638 * {@link android.app.Notification.Builder#setCustomHeadsUpContentView(RemoteViews)} to set the
5639 * corresponding custom views to display.
5640 *
5641 * To use this style with your Notification, feed it to
5642 * {@link Notification.Builder#setStyle(android.app.Notification.Style)} like so:
5643 * <pre class="prettyprint">
5644 * Notification noti = new Notification.Builder()
5645 * .setSmallIcon(R.drawable.ic_stat_player)
5646 * .setLargeIcon(albumArtBitmap))
5647 * .setCustomContentView(contentView);
5648 * .setStyle(<b>new Notification.DecoratedMediaCustomViewStyle()</b>
5649 * .setMediaSession(mySession))
5650 * .build();
5651 * </pre>
5652 *
5653 * @see android.app.Notification.DecoratedCustomViewStyle
5654 * @see android.app.Notification.MediaStyle
5655 */
5656 public static class DecoratedMediaCustomViewStyle extends MediaStyle {
5657
5658 public DecoratedMediaCustomViewStyle() {
5659 }
5660
Selim Cinek03eb3b72016-02-18 10:39:45 -08005661 /**
5662 * @hide
5663 */
5664 public boolean displayCustomViewInline() {
5665 return true;
5666 }
5667
5668 /**
5669 * @hide
5670 */
5671 @Override
5672 public RemoteViews makeContentView() {
5673 RemoteViews remoteViews = super.makeContentView();
5674 return buildIntoRemoteView(remoteViews, R.id.notification_content_container,
5675 mBuilder.mN.contentView);
5676 }
5677
5678 /**
5679 * @hide
5680 */
5681 @Override
5682 public RemoteViews makeBigContentView() {
5683 RemoteViews customRemoteView = mBuilder.mN.bigContentView != null
5684 ? mBuilder.mN.bigContentView
5685 : mBuilder.mN.contentView;
5686 return makeBigContentViewWithCustomContent(customRemoteView);
5687 }
5688
5689 private RemoteViews makeBigContentViewWithCustomContent(RemoteViews customRemoteView) {
5690 RemoteViews remoteViews = super.makeBigContentView();
5691 if (remoteViews != null) {
5692 return buildIntoRemoteView(remoteViews, R.id.notification_main_column,
5693 customRemoteView);
5694 } else if (customRemoteView != mBuilder.mN.contentView){
5695 remoteViews = super.makeContentView();
5696 return buildIntoRemoteView(remoteViews, R.id.notification_content_container,
5697 customRemoteView);
5698 } else {
5699 return null;
5700 }
5701 }
5702
5703 /**
5704 * @hide
5705 */
5706 @Override
5707 public RemoteViews makeHeadsUpContentView() {
5708 RemoteViews customRemoteView = mBuilder.mN.headsUpContentView != null
5709 ? mBuilder.mN.headsUpContentView
5710 : mBuilder.mN.contentView;
5711 return makeBigContentViewWithCustomContent(customRemoteView);
5712 }
5713
5714 private RemoteViews buildIntoRemoteView(RemoteViews remoteViews, int id,
5715 RemoteViews customContent) {
Adrian Roos5081c0d2016-02-26 16:04:19 -08005716 if (customContent != null) {
Selim Cinekf91017e2016-03-14 12:25:09 -07005717 // Need to clone customContent before adding, because otherwise it can no longer be
5718 // parceled independently of remoteViews.
Adrian Roos5081c0d2016-02-26 16:04:19 -08005719 customContent = customContent.clone();
Selim Cinekf91017e2016-03-14 12:25:09 -07005720 remoteViews.removeAllViews(id);
5721 remoteViews.addView(id, customContent);
Adrian Roos5081c0d2016-02-26 16:04:19 -08005722 }
Selim Cinek03eb3b72016-02-18 10:39:45 -08005723 return remoteViews;
5724 }
5725 }
5726
Christoph Studer4600f9b2014-07-22 22:44:43 +02005727 // When adding a new Style subclass here, don't forget to update
5728 // Builder.getNotificationStyleClass.
5729
Griff Hazen61a9e862014-05-22 16:05:19 -07005730 /**
5731 * Extender interface for use with {@link Builder#extend}. Extenders may be used to add
5732 * metadata or change options on a notification builder.
5733 */
5734 public interface Extender {
5735 /**
5736 * Apply this extender to a notification builder.
5737 * @param builder the builder to be modified.
5738 * @return the build object for chaining.
5739 */
5740 public Builder extend(Builder builder);
5741 }
5742
5743 /**
5744 * Helper class to add wearable extensions to notifications.
5745 * <p class="note"> See
5746 * <a href="{@docRoot}wear/notifications/creating.html">Creating Notifications
5747 * for Android Wear</a> for more information on how to use this class.
5748 * <p>
5749 * To create a notification with wearable extensions:
5750 * <ol>
5751 * <li>Create a {@link android.app.Notification.Builder}, setting any desired
5752 * properties.
5753 * <li>Create a {@link android.app.Notification.WearableExtender}.
5754 * <li>Set wearable-specific properties using the
5755 * {@code add} and {@code set} methods of {@link android.app.Notification.WearableExtender}.
5756 * <li>Call {@link android.app.Notification.Builder#extend} to apply the extensions to a
5757 * notification.
5758 * <li>Post the notification to the notification system with the
5759 * {@code NotificationManager.notify(...)} methods.
5760 * </ol>
5761 *
5762 * <pre class="prettyprint">
5763 * Notification notif = new Notification.Builder(mContext)
5764 * .setContentTitle(&quot;New mail from &quot; + sender.toString())
5765 * .setContentText(subject)
5766 * .setSmallIcon(R.drawable.new_mail)
5767 * .extend(new Notification.WearableExtender()
5768 * .setContentIcon(R.drawable.new_mail))
5769 * .build();
5770 * NotificationManager notificationManger =
5771 * (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
5772 * notificationManger.notify(0, notif);</pre>
5773 *
5774 * <p>Wearable extensions can be accessed on an existing notification by using the
5775 * {@code WearableExtender(Notification)} constructor,
5776 * and then using the {@code get} methods to access values.
5777 *
5778 * <pre class="prettyprint">
5779 * Notification.WearableExtender wearableExtender = new Notification.WearableExtender(
5780 * notification);
Griff Hazen14f57992014-05-26 09:07:14 -07005781 * List&lt;Notification&gt; pages = wearableExtender.getPages();</pre>
Griff Hazen61a9e862014-05-22 16:05:19 -07005782 */
5783 public static final class WearableExtender implements Extender {
5784 /**
5785 * Sentinel value for an action index that is unset.
5786 */
5787 public static final int UNSET_ACTION_INDEX = -1;
5788
5789 /**
5790 * Size value for use with {@link #setCustomSizePreset} to show this notification with
5791 * default sizing.
5792 * <p>For custom display notifications created using {@link #setDisplayIntent},
Paul Soulosaa4f4bf2015-08-04 11:59:45 -07005793 * the default is {@link #SIZE_MEDIUM}. All other notifications size automatically based
Griff Hazen61a9e862014-05-22 16:05:19 -07005794 * on their content.
5795 */
5796 public static final int SIZE_DEFAULT = 0;
5797
5798 /**
5799 * Size value for use with {@link #setCustomSizePreset} to show this notification
5800 * with an extra small size.
5801 * <p>This value is only applicable for custom display notifications created using
5802 * {@link #setDisplayIntent}.
5803 */
5804 public static final int SIZE_XSMALL = 1;
5805
5806 /**
5807 * Size value for use with {@link #setCustomSizePreset} to show this notification
5808 * with a small size.
5809 * <p>This value is only applicable for custom display notifications created using
5810 * {@link #setDisplayIntent}.
5811 */
5812 public static final int SIZE_SMALL = 2;
5813
5814 /**
5815 * Size value for use with {@link #setCustomSizePreset} to show this notification
5816 * with a medium size.
5817 * <p>This value is only applicable for custom display notifications created using
5818 * {@link #setDisplayIntent}.
5819 */
5820 public static final int SIZE_MEDIUM = 3;
5821
5822 /**
5823 * Size value for use with {@link #setCustomSizePreset} to show this notification
5824 * with a large size.
5825 * <p>This value is only applicable for custom display notifications created using
5826 * {@link #setDisplayIntent}.
5827 */
5828 public static final int SIZE_LARGE = 4;
5829
Griff Hazend5f11f92014-05-27 15:40:09 -07005830 /**
5831 * Size value for use with {@link #setCustomSizePreset} to show this notification
5832 * full screen.
5833 * <p>This value is only applicable for custom display notifications created using
5834 * {@link #setDisplayIntent}.
5835 */
5836 public static final int SIZE_FULL_SCREEN = 5;
5837
Griff Hazen5f2edfc2014-09-29 16:28:44 -07005838 /**
5839 * Sentinel value for use with {@link #setHintScreenTimeout} to keep the screen on for a
5840 * short amount of time when this notification is displayed on the screen. This
5841 * is the default value.
5842 */
5843 public static final int SCREEN_TIMEOUT_SHORT = 0;
5844
5845 /**
5846 * Sentinel value for use with {@link #setHintScreenTimeout} to keep the screen on
5847 * for a longer amount of time when this notification is displayed on the screen.
5848 */
5849 public static final int SCREEN_TIMEOUT_LONG = -1;
5850
Griff Hazen61a9e862014-05-22 16:05:19 -07005851 /** Notification extra which contains wearable extensions */
5852 private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS";
5853
Pete Gastaf6781d2014-10-07 15:17:05 -04005854 // Keys within EXTRA_WEARABLE_EXTENSIONS for wearable options.
Griff Hazen61a9e862014-05-22 16:05:19 -07005855 private static final String KEY_ACTIONS = "actions";
5856 private static final String KEY_FLAGS = "flags";
5857 private static final String KEY_DISPLAY_INTENT = "displayIntent";
5858 private static final String KEY_PAGES = "pages";
5859 private static final String KEY_BACKGROUND = "background";
5860 private static final String KEY_CONTENT_ICON = "contentIcon";
5861 private static final String KEY_CONTENT_ICON_GRAVITY = "contentIconGravity";
5862 private static final String KEY_CONTENT_ACTION_INDEX = "contentActionIndex";
5863 private static final String KEY_CUSTOM_SIZE_PRESET = "customSizePreset";
5864 private static final String KEY_CUSTOM_CONTENT_HEIGHT = "customContentHeight";
5865 private static final String KEY_GRAVITY = "gravity";
Griff Hazen5f2edfc2014-09-29 16:28:44 -07005866 private static final String KEY_HINT_SCREEN_TIMEOUT = "hintScreenTimeout";
Nadia Benbernou948627e2016-04-14 14:41:08 -04005867 private static final String KEY_DISMISSAL_ID = "dismissalId";
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04005868 private static final String KEY_BRIDGE_TAG = "bridgeTag";
Griff Hazen61a9e862014-05-22 16:05:19 -07005869
5870 // Flags bitwise-ored to mFlags
5871 private static final int FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE = 0x1;
5872 private static final int FLAG_HINT_HIDE_ICON = 1 << 1;
5873 private static final int FLAG_HINT_SHOW_BACKGROUND_ONLY = 1 << 2;
5874 private static final int FLAG_START_SCROLL_BOTTOM = 1 << 3;
Griff Hazen5f2edfc2014-09-29 16:28:44 -07005875 private static final int FLAG_HINT_AVOID_BACKGROUND_CLIPPING = 1 << 4;
Alex Hills4bcb06b2016-04-05 14:26:25 -04005876 private static final int FLAG_BIG_PICTURE_AMBIENT = 1 << 5;
Alex Hills9ab3a232016-04-05 14:54:56 -04005877 private static final int FLAG_HINT_CONTENT_INTENT_LAUNCHES_ACTIVITY = 1 << 6;
Griff Hazen61a9e862014-05-22 16:05:19 -07005878
5879 // Default value for flags integer
5880 private static final int DEFAULT_FLAGS = FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE;
5881
5882 private static final int DEFAULT_CONTENT_ICON_GRAVITY = Gravity.END;
5883 private static final int DEFAULT_GRAVITY = Gravity.BOTTOM;
5884
5885 private ArrayList<Action> mActions = new ArrayList<Action>();
5886 private int mFlags = DEFAULT_FLAGS;
5887 private PendingIntent mDisplayIntent;
5888 private ArrayList<Notification> mPages = new ArrayList<Notification>();
5889 private Bitmap mBackground;
5890 private int mContentIcon;
5891 private int mContentIconGravity = DEFAULT_CONTENT_ICON_GRAVITY;
5892 private int mContentActionIndex = UNSET_ACTION_INDEX;
5893 private int mCustomSizePreset = SIZE_DEFAULT;
5894 private int mCustomContentHeight;
5895 private int mGravity = DEFAULT_GRAVITY;
Griff Hazen5f2edfc2014-09-29 16:28:44 -07005896 private int mHintScreenTimeout;
Nadia Benbernou948627e2016-04-14 14:41:08 -04005897 private String mDismissalId;
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04005898 private String mBridgeTag;
Griff Hazen61a9e862014-05-22 16:05:19 -07005899
5900 /**
5901 * Create a {@link android.app.Notification.WearableExtender} with default
5902 * options.
5903 */
5904 public WearableExtender() {
5905 }
5906
5907 public WearableExtender(Notification notif) {
5908 Bundle wearableBundle = notif.extras.getBundle(EXTRA_WEARABLE_EXTENSIONS);
5909 if (wearableBundle != null) {
5910 List<Action> actions = wearableBundle.getParcelableArrayList(KEY_ACTIONS);
5911 if (actions != null) {
5912 mActions.addAll(actions);
5913 }
5914
5915 mFlags = wearableBundle.getInt(KEY_FLAGS, DEFAULT_FLAGS);
5916 mDisplayIntent = wearableBundle.getParcelable(KEY_DISPLAY_INTENT);
5917
5918 Notification[] pages = getNotificationArrayFromBundle(
5919 wearableBundle, KEY_PAGES);
5920 if (pages != null) {
5921 Collections.addAll(mPages, pages);
5922 }
5923
5924 mBackground = wearableBundle.getParcelable(KEY_BACKGROUND);
5925 mContentIcon = wearableBundle.getInt(KEY_CONTENT_ICON);
5926 mContentIconGravity = wearableBundle.getInt(KEY_CONTENT_ICON_GRAVITY,
5927 DEFAULT_CONTENT_ICON_GRAVITY);
5928 mContentActionIndex = wearableBundle.getInt(KEY_CONTENT_ACTION_INDEX,
5929 UNSET_ACTION_INDEX);
5930 mCustomSizePreset = wearableBundle.getInt(KEY_CUSTOM_SIZE_PRESET,
5931 SIZE_DEFAULT);
5932 mCustomContentHeight = wearableBundle.getInt(KEY_CUSTOM_CONTENT_HEIGHT);
5933 mGravity = wearableBundle.getInt(KEY_GRAVITY, DEFAULT_GRAVITY);
Griff Hazen5f2edfc2014-09-29 16:28:44 -07005934 mHintScreenTimeout = wearableBundle.getInt(KEY_HINT_SCREEN_TIMEOUT);
Nadia Benbernou948627e2016-04-14 14:41:08 -04005935 mDismissalId = wearableBundle.getString(KEY_DISMISSAL_ID);
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04005936 mBridgeTag = wearableBundle.getString(KEY_BRIDGE_TAG);
Griff Hazen61a9e862014-05-22 16:05:19 -07005937 }
5938 }
5939
5940 /**
5941 * Apply wearable extensions to a notification that is being built. This is typically
5942 * called by the {@link android.app.Notification.Builder#extend} method of
5943 * {@link android.app.Notification.Builder}.
5944 */
5945 @Override
5946 public Notification.Builder extend(Notification.Builder builder) {
5947 Bundle wearableBundle = new Bundle();
5948
5949 if (!mActions.isEmpty()) {
5950 wearableBundle.putParcelableArrayList(KEY_ACTIONS, mActions);
5951 }
5952 if (mFlags != DEFAULT_FLAGS) {
5953 wearableBundle.putInt(KEY_FLAGS, mFlags);
5954 }
5955 if (mDisplayIntent != null) {
5956 wearableBundle.putParcelable(KEY_DISPLAY_INTENT, mDisplayIntent);
5957 }
5958 if (!mPages.isEmpty()) {
5959 wearableBundle.putParcelableArray(KEY_PAGES, mPages.toArray(
5960 new Notification[mPages.size()]));
5961 }
5962 if (mBackground != null) {
5963 wearableBundle.putParcelable(KEY_BACKGROUND, mBackground);
5964 }
5965 if (mContentIcon != 0) {
5966 wearableBundle.putInt(KEY_CONTENT_ICON, mContentIcon);
5967 }
5968 if (mContentIconGravity != DEFAULT_CONTENT_ICON_GRAVITY) {
5969 wearableBundle.putInt(KEY_CONTENT_ICON_GRAVITY, mContentIconGravity);
5970 }
5971 if (mContentActionIndex != UNSET_ACTION_INDEX) {
5972 wearableBundle.putInt(KEY_CONTENT_ACTION_INDEX,
5973 mContentActionIndex);
5974 }
5975 if (mCustomSizePreset != SIZE_DEFAULT) {
5976 wearableBundle.putInt(KEY_CUSTOM_SIZE_PRESET, mCustomSizePreset);
5977 }
5978 if (mCustomContentHeight != 0) {
5979 wearableBundle.putInt(KEY_CUSTOM_CONTENT_HEIGHT, mCustomContentHeight);
5980 }
5981 if (mGravity != DEFAULT_GRAVITY) {
5982 wearableBundle.putInt(KEY_GRAVITY, mGravity);
5983 }
Griff Hazen5f2edfc2014-09-29 16:28:44 -07005984 if (mHintScreenTimeout != 0) {
5985 wearableBundle.putInt(KEY_HINT_SCREEN_TIMEOUT, mHintScreenTimeout);
5986 }
Nadia Benbernou948627e2016-04-14 14:41:08 -04005987 if (mDismissalId != null) {
5988 wearableBundle.putString(KEY_DISMISSAL_ID, mDismissalId);
5989 }
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04005990 if (mBridgeTag != null) {
5991 wearableBundle.putString(KEY_BRIDGE_TAG, mBridgeTag);
5992 }
Griff Hazen61a9e862014-05-22 16:05:19 -07005993
5994 builder.getExtras().putBundle(EXTRA_WEARABLE_EXTENSIONS, wearableBundle);
5995 return builder;
5996 }
5997
5998 @Override
5999 public WearableExtender clone() {
6000 WearableExtender that = new WearableExtender();
6001 that.mActions = new ArrayList<Action>(this.mActions);
6002 that.mFlags = this.mFlags;
6003 that.mDisplayIntent = this.mDisplayIntent;
6004 that.mPages = new ArrayList<Notification>(this.mPages);
6005 that.mBackground = this.mBackground;
6006 that.mContentIcon = this.mContentIcon;
6007 that.mContentIconGravity = this.mContentIconGravity;
6008 that.mContentActionIndex = this.mContentActionIndex;
6009 that.mCustomSizePreset = this.mCustomSizePreset;
6010 that.mCustomContentHeight = this.mCustomContentHeight;
6011 that.mGravity = this.mGravity;
Griff Hazen5f2edfc2014-09-29 16:28:44 -07006012 that.mHintScreenTimeout = this.mHintScreenTimeout;
Nadia Benbernou948627e2016-04-14 14:41:08 -04006013 that.mDismissalId = this.mDismissalId;
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04006014 that.mBridgeTag = this.mBridgeTag;
Griff Hazen61a9e862014-05-22 16:05:19 -07006015 return that;
6016 }
6017
6018 /**
6019 * Add a wearable action to this notification.
6020 *
6021 * <p>When wearable actions are added using this method, the set of actions that
6022 * show on a wearable device splits from devices that only show actions added
6023 * using {@link android.app.Notification.Builder#addAction}. This allows for customization
6024 * of which actions display on different devices.
6025 *
6026 * @param action the action to add to this notification
6027 * @return this object for method chaining
6028 * @see android.app.Notification.Action
6029 */
6030 public WearableExtender addAction(Action action) {
6031 mActions.add(action);
6032 return this;
6033 }
6034
6035 /**
6036 * Adds wearable actions to this notification.
6037 *
6038 * <p>When wearable actions are added using this method, the set of actions that
6039 * show on a wearable device splits from devices that only show actions added
6040 * using {@link android.app.Notification.Builder#addAction}. This allows for customization
6041 * of which actions display on different devices.
6042 *
6043 * @param actions the actions to add to this notification
6044 * @return this object for method chaining
6045 * @see android.app.Notification.Action
6046 */
6047 public WearableExtender addActions(List<Action> actions) {
6048 mActions.addAll(actions);
6049 return this;
6050 }
6051
6052 /**
6053 * Clear all wearable actions present on this builder.
6054 * @return this object for method chaining.
6055 * @see #addAction
6056 */
6057 public WearableExtender clearActions() {
6058 mActions.clear();
6059 return this;
6060 }
6061
6062 /**
6063 * Get the wearable actions present on this notification.
6064 */
6065 public List<Action> getActions() {
6066 return mActions;
6067 }
6068
6069 /**
6070 * Set an intent to launch inside of an activity view when displaying
Griff Hazen14f57992014-05-26 09:07:14 -07006071 * this notification. The {@link PendingIntent} provided should be for an activity.
6072 *
6073 * <pre class="prettyprint">
6074 * Intent displayIntent = new Intent(context, MyDisplayActivity.class);
6075 * PendingIntent displayPendingIntent = PendingIntent.getActivity(context,
6076 * 0, displayIntent, PendingIntent.FLAG_UPDATE_CURRENT);
6077 * Notification notif = new Notification.Builder(context)
6078 * .extend(new Notification.WearableExtender()
6079 * .setDisplayIntent(displayPendingIntent)
6080 * .setCustomSizePreset(Notification.WearableExtender.SIZE_MEDIUM))
6081 * .build();</pre>
6082 *
6083 * <p>The activity to launch needs to allow embedding, must be exported, and
Griff Hazen831ca9d2014-06-17 00:38:38 -07006084 * should have an empty task affinity. It is also recommended to use the device
6085 * default light theme.
Griff Hazen14f57992014-05-26 09:07:14 -07006086 *
6087 * <p>Example AndroidManifest.xml entry:
6088 * <pre class="prettyprint">
6089 * &lt;activity android:name=&quot;com.example.MyDisplayActivity&quot;
6090 * android:exported=&quot;true&quot;
6091 * android:allowEmbedded=&quot;true&quot;
Griff Hazen831ca9d2014-06-17 00:38:38 -07006092 * android:taskAffinity=&quot;&quot;
6093 * android:theme=&quot;@android:style/Theme.DeviceDefault.Light&quot; /&gt;</pre>
Griff Hazen61a9e862014-05-22 16:05:19 -07006094 *
6095 * @param intent the {@link PendingIntent} for an activity
6096 * @return this object for method chaining
6097 * @see android.app.Notification.WearableExtender#getDisplayIntent
6098 */
6099 public WearableExtender setDisplayIntent(PendingIntent intent) {
6100 mDisplayIntent = intent;
6101 return this;
6102 }
6103
6104 /**
6105 * Get the intent to launch inside of an activity view when displaying this
6106 * notification. This {@code PendingIntent} should be for an activity.
6107 */
6108 public PendingIntent getDisplayIntent() {
6109 return mDisplayIntent;
6110 }
6111
6112 /**
6113 * Add an additional page of content to display with this notification. The current
6114 * notification forms the first page, and pages added using this function form
6115 * subsequent pages. This field can be used to separate a notification into multiple
6116 * sections.
6117 *
6118 * @param page the notification to add as another page
6119 * @return this object for method chaining
6120 * @see android.app.Notification.WearableExtender#getPages
6121 */
6122 public WearableExtender addPage(Notification page) {
6123 mPages.add(page);
6124 return this;
6125 }
6126
6127 /**
6128 * Add additional pages of content to display with this notification. The current
6129 * notification forms the first page, and pages added using this function form
6130 * subsequent pages. This field can be used to separate a notification into multiple
6131 * sections.
6132 *
6133 * @param pages a list of notifications
6134 * @return this object for method chaining
6135 * @see android.app.Notification.WearableExtender#getPages
6136 */
6137 public WearableExtender addPages(List<Notification> pages) {
6138 mPages.addAll(pages);
6139 return this;
6140 }
6141
6142 /**
6143 * Clear all additional pages present on this builder.
6144 * @return this object for method chaining.
6145 * @see #addPage
6146 */
6147 public WearableExtender clearPages() {
6148 mPages.clear();
6149 return this;
6150 }
6151
6152 /**
6153 * Get the array of additional pages of content for displaying this notification. The
6154 * current notification forms the first page, and elements within this array form
6155 * subsequent pages. This field can be used to separate a notification into multiple
6156 * sections.
6157 * @return the pages for this notification
6158 */
6159 public List<Notification> getPages() {
6160 return mPages;
6161 }
6162
6163 /**
6164 * Set a background image to be displayed behind the notification content.
6165 * Contrary to the {@link android.app.Notification.BigPictureStyle}, this background
6166 * will work with any notification style.
6167 *
6168 * @param background the background bitmap
6169 * @return this object for method chaining
6170 * @see android.app.Notification.WearableExtender#getBackground
6171 */
6172 public WearableExtender setBackground(Bitmap background) {
6173 mBackground = background;
6174 return this;
6175 }
6176
6177 /**
6178 * Get a background image to be displayed behind the notification content.
6179 * Contrary to the {@link android.app.Notification.BigPictureStyle}, this background
6180 * will work with any notification style.
6181 *
6182 * @return the background image
6183 * @see android.app.Notification.WearableExtender#setBackground
6184 */
6185 public Bitmap getBackground() {
6186 return mBackground;
6187 }
6188
6189 /**
6190 * Set an icon that goes with the content of this notification.
6191 */
6192 public WearableExtender setContentIcon(int icon) {
6193 mContentIcon = icon;
6194 return this;
6195 }
6196
6197 /**
6198 * Get an icon that goes with the content of this notification.
6199 */
6200 public int getContentIcon() {
6201 return mContentIcon;
6202 }
6203
6204 /**
6205 * Set the gravity that the content icon should have within the notification display.
6206 * Supported values include {@link android.view.Gravity#START} and
6207 * {@link android.view.Gravity#END}. The default value is {@link android.view.Gravity#END}.
6208 * @see #setContentIcon
6209 */
6210 public WearableExtender setContentIconGravity(int contentIconGravity) {
6211 mContentIconGravity = contentIconGravity;
6212 return this;
6213 }
6214
6215 /**
6216 * Get the gravity that the content icon should have within the notification display.
6217 * Supported values include {@link android.view.Gravity#START} and
6218 * {@link android.view.Gravity#END}. The default value is {@link android.view.Gravity#END}.
6219 * @see #getContentIcon
6220 */
6221 public int getContentIconGravity() {
6222 return mContentIconGravity;
6223 }
6224
6225 /**
6226 * Set an action from this notification's actions to be clickable with the content of
Griff Hazen14f57992014-05-26 09:07:14 -07006227 * this notification. This action will no longer display separately from the
6228 * notification's content.
6229 *
Griff Hazenca48d352014-05-28 22:37:13 -07006230 * <p>For notifications with multiple pages, child pages can also have content actions
Griff Hazen14f57992014-05-26 09:07:14 -07006231 * set, although the list of available actions comes from the main notification and not
6232 * from the child page's notification.
6233 *
6234 * @param actionIndex The index of the action to hoist onto the current notification page.
6235 * If wearable actions were added to the main notification, this index
6236 * will apply to that list, otherwise it will apply to the regular
6237 * actions list.
Griff Hazen61a9e862014-05-22 16:05:19 -07006238 */
6239 public WearableExtender setContentAction(int actionIndex) {
6240 mContentActionIndex = actionIndex;
6241 return this;
6242 }
6243
6244 /**
Griff Hazenca48d352014-05-28 22:37:13 -07006245 * Get the index of the notification action, if any, that was specified as being clickable
6246 * with the content of this notification. This action will no longer display separately
Griff Hazen14f57992014-05-26 09:07:14 -07006247 * from the notification's content.
Griff Hazen61a9e862014-05-22 16:05:19 -07006248 *
Griff Hazenca48d352014-05-28 22:37:13 -07006249 * <p>For notifications with multiple pages, child pages can also have content actions
Griff Hazen14f57992014-05-26 09:07:14 -07006250 * set, although the list of available actions comes from the main notification and not
6251 * from the child page's notification.
6252 *
6253 * <p>If wearable specific actions were added to the main notification, this index will
6254 * apply to that list, otherwise it will apply to the regular actions list.
Griff Hazenca48d352014-05-28 22:37:13 -07006255 *
6256 * @return the action index or {@link #UNSET_ACTION_INDEX} if no action was selected.
Griff Hazen61a9e862014-05-22 16:05:19 -07006257 */
6258 public int getContentAction() {
6259 return mContentActionIndex;
6260 }
6261
6262 /**
6263 * Set the gravity that this notification should have within the available viewport space.
6264 * Supported values include {@link android.view.Gravity#TOP},
6265 * {@link android.view.Gravity#CENTER_VERTICAL} and {@link android.view.Gravity#BOTTOM}.
6266 * The default value is {@link android.view.Gravity#BOTTOM}.
6267 */
6268 public WearableExtender setGravity(int gravity) {
6269 mGravity = gravity;
6270 return this;
6271 }
6272
6273 /**
6274 * Get the gravity that this notification should have within the available viewport space.
6275 * Supported values include {@link android.view.Gravity#TOP},
6276 * {@link android.view.Gravity#CENTER_VERTICAL} and {@link android.view.Gravity#BOTTOM}.
6277 * The default value is {@link android.view.Gravity#BOTTOM}.
6278 */
6279 public int getGravity() {
6280 return mGravity;
6281 }
6282
6283 /**
6284 * Set the custom size preset for the display of this notification out of the available
6285 * presets found in {@link android.app.Notification.WearableExtender}, e.g.
6286 * {@link #SIZE_LARGE}.
6287 * <p>Some custom size presets are only applicable for custom display notifications created
6288 * using {@link android.app.Notification.WearableExtender#setDisplayIntent}. Check the
6289 * documentation for the preset in question. See also
6290 * {@link #setCustomContentHeight} and {@link #getCustomSizePreset}.
6291 */
6292 public WearableExtender setCustomSizePreset(int sizePreset) {
6293 mCustomSizePreset = sizePreset;
6294 return this;
6295 }
6296
6297 /**
6298 * Get the custom size preset for the display of this notification out of the available
6299 * presets found in {@link android.app.Notification.WearableExtender}, e.g.
6300 * {@link #SIZE_LARGE}.
6301 * <p>Some custom size presets are only applicable for custom display notifications created
6302 * using {@link #setDisplayIntent}. Check the documentation for the preset in question.
6303 * See also {@link #setCustomContentHeight} and {@link #setCustomSizePreset}.
6304 */
6305 public int getCustomSizePreset() {
6306 return mCustomSizePreset;
6307 }
6308
6309 /**
6310 * Set the custom height in pixels for the display of this notification's content.
6311 * <p>This option is only available for custom display notifications created
6312 * using {@link android.app.Notification.WearableExtender#setDisplayIntent}. See also
6313 * {@link android.app.Notification.WearableExtender#setCustomSizePreset} and
6314 * {@link #getCustomContentHeight}.
6315 */
6316 public WearableExtender setCustomContentHeight(int height) {
6317 mCustomContentHeight = height;
6318 return this;
6319 }
6320
6321 /**
6322 * Get the custom height in pixels for the display of this notification's content.
6323 * <p>This option is only available for custom display notifications created
6324 * using {@link #setDisplayIntent}. See also {@link #setCustomSizePreset} and
6325 * {@link #setCustomContentHeight}.
6326 */
6327 public int getCustomContentHeight() {
6328 return mCustomContentHeight;
6329 }
6330
6331 /**
6332 * Set whether the scrolling position for the contents of this notification should start
6333 * at the bottom of the contents instead of the top when the contents are too long to
6334 * display within the screen. Default is false (start scroll at the top).
6335 */
6336 public WearableExtender setStartScrollBottom(boolean startScrollBottom) {
6337 setFlag(FLAG_START_SCROLL_BOTTOM, startScrollBottom);
6338 return this;
6339 }
6340
6341 /**
6342 * Get whether the scrolling position for the contents of this notification should start
6343 * at the bottom of the contents instead of the top when the contents are too long to
6344 * display within the screen. Default is false (start scroll at the top).
6345 */
6346 public boolean getStartScrollBottom() {
6347 return (mFlags & FLAG_START_SCROLL_BOTTOM) != 0;
6348 }
6349
6350 /**
6351 * Set whether the content intent is available when the wearable device is not connected
6352 * to a companion device. The user can still trigger this intent when the wearable device
6353 * is offline, but a visual hint will indicate that the content intent may not be available.
6354 * Defaults to true.
6355 */
6356 public WearableExtender setContentIntentAvailableOffline(
6357 boolean contentIntentAvailableOffline) {
6358 setFlag(FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE, contentIntentAvailableOffline);
6359 return this;
6360 }
6361
6362 /**
6363 * Get whether the content intent is available when the wearable device is not connected
6364 * to a companion device. The user can still trigger this intent when the wearable device
6365 * is offline, but a visual hint will indicate that the content intent may not be available.
6366 * Defaults to true.
6367 */
6368 public boolean getContentIntentAvailableOffline() {
6369 return (mFlags & FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE) != 0;
6370 }
6371
6372 /**
6373 * Set a hint that this notification's icon should not be displayed.
6374 * @param hintHideIcon {@code true} to hide the icon, {@code false} otherwise.
6375 * @return this object for method chaining
6376 */
6377 public WearableExtender setHintHideIcon(boolean hintHideIcon) {
6378 setFlag(FLAG_HINT_HIDE_ICON, hintHideIcon);
6379 return this;
6380 }
6381
6382 /**
6383 * Get a hint that this notification's icon should not be displayed.
6384 * @return {@code true} if this icon should not be displayed, false otherwise.
6385 * The default value is {@code false} if this was never set.
6386 */
6387 public boolean getHintHideIcon() {
6388 return (mFlags & FLAG_HINT_HIDE_ICON) != 0;
6389 }
6390
6391 /**
6392 * Set a visual hint that only the background image of this notification should be
6393 * displayed, and other semantic content should be hidden. This hint is only applicable
6394 * to sub-pages added using {@link #addPage}.
6395 */
6396 public WearableExtender setHintShowBackgroundOnly(boolean hintShowBackgroundOnly) {
6397 setFlag(FLAG_HINT_SHOW_BACKGROUND_ONLY, hintShowBackgroundOnly);
6398 return this;
6399 }
6400
6401 /**
6402 * Get a visual hint that only the background image of this notification should be
6403 * displayed, and other semantic content should be hidden. This hint is only applicable
6404 * to sub-pages added using {@link android.app.Notification.WearableExtender#addPage}.
6405 */
6406 public boolean getHintShowBackgroundOnly() {
6407 return (mFlags & FLAG_HINT_SHOW_BACKGROUND_ONLY) != 0;
6408 }
6409
Griff Hazen5f2edfc2014-09-29 16:28:44 -07006410 /**
Griff Hazen9c5be4e2014-11-17 17:47:50 -08006411 * Set a hint that this notification's background should not be clipped if possible,
6412 * and should instead be resized to fully display on the screen, retaining the aspect
6413 * ratio of the image. This can be useful for images like barcodes or qr codes.
Griff Hazen5f2edfc2014-09-29 16:28:44 -07006414 * @param hintAvoidBackgroundClipping {@code true} to avoid clipping if possible.
6415 * @return this object for method chaining
6416 */
6417 public WearableExtender setHintAvoidBackgroundClipping(
6418 boolean hintAvoidBackgroundClipping) {
6419 setFlag(FLAG_HINT_AVOID_BACKGROUND_CLIPPING, hintAvoidBackgroundClipping);
6420 return this;
6421 }
6422
6423 /**
Griff Hazen9c5be4e2014-11-17 17:47:50 -08006424 * Get a hint that this notification's background should not be clipped if possible,
6425 * and should instead be resized to fully display on the screen, retaining the aspect
6426 * ratio of the image. This can be useful for images like barcodes or qr codes.
Griff Hazen5f2edfc2014-09-29 16:28:44 -07006427 * @return {@code true} if it's ok if the background is clipped on the screen, false
6428 * otherwise. The default value is {@code false} if this was never set.
6429 */
6430 public boolean getHintAvoidBackgroundClipping() {
6431 return (mFlags & FLAG_HINT_AVOID_BACKGROUND_CLIPPING) != 0;
6432 }
6433
6434 /**
6435 * Set a hint that the screen should remain on for at least this duration when
6436 * this notification is displayed on the screen.
6437 * @param timeout The requested screen timeout in milliseconds. Can also be either
6438 * {@link #SCREEN_TIMEOUT_SHORT} or {@link #SCREEN_TIMEOUT_LONG}.
6439 * @return this object for method chaining
6440 */
6441 public WearableExtender setHintScreenTimeout(int timeout) {
6442 mHintScreenTimeout = timeout;
6443 return this;
6444 }
6445
6446 /**
6447 * Get the duration, in milliseconds, that the screen should remain on for
6448 * when this notification is displayed.
6449 * @return the duration in milliseconds if > 0, or either one of the sentinel values
6450 * {@link #SCREEN_TIMEOUT_SHORT} or {@link #SCREEN_TIMEOUT_LONG}.
6451 */
6452 public int getHintScreenTimeout() {
6453 return mHintScreenTimeout;
6454 }
6455
Alex Hills9ab3a232016-04-05 14:54:56 -04006456 /**
Alex Hills4bcb06b2016-04-05 14:26:25 -04006457 * Set a hint that this notification's {@link BigPictureStyle} (if present) should be
6458 * converted to low-bit and displayed in ambient mode, especially useful for barcodes and
6459 * qr codes, as well as other simple black-and-white tickets.
6460 * @param hintAmbientBigPicture {@code true} to enable converstion and ambient.
6461 * @return this object for method chaining
6462 */
6463 public WearableExtender setHintAmbientBigPicture(boolean hintAmbientBigPicture) {
6464 setFlag(FLAG_BIG_PICTURE_AMBIENT, hintAmbientBigPicture);
6465 return this;
6466 }
6467
6468 /**
6469 * Get a hint that this notification's {@link BigPictureStyle} (if present) should be
6470 * converted to low-bit and displayed in ambient mode, especially useful for barcodes and
6471 * qr codes, as well as other simple black-and-white tickets.
6472 * @return {@code true} if it should be displayed in ambient, false otherwise
6473 * otherwise. The default value is {@code false} if this was never set.
6474 */
6475 public boolean getHintAmbientBigPicture() {
6476 return (mFlags & FLAG_BIG_PICTURE_AMBIENT) != 0;
6477 }
6478
6479 /**
Alex Hills9ab3a232016-04-05 14:54:56 -04006480 * Set a hint that this notification's content intent will launch an {@link Activity}
6481 * directly, telling the platform that it can generate the appropriate transitions.
6482 * @param hintContentIntentLaunchesActivity {@code true} if the content intent will launch
6483 * an activity and transitions should be generated, false otherwise.
6484 * @return this object for method chaining
6485 */
6486 public WearableExtender setHintContentIntentLaunchesActivity(
6487 boolean hintContentIntentLaunchesActivity) {
6488 setFlag(FLAG_HINT_CONTENT_INTENT_LAUNCHES_ACTIVITY, hintContentIntentLaunchesActivity);
6489 return this;
6490 }
6491
6492 /**
6493 * Get a hint that this notification's content intent will launch an {@link Activity}
6494 * directly, telling the platform that it can generate the appropriate transitions
6495 * @return {@code true} if the content intent will launch an activity and transitions should
6496 * be generated, false otherwise. The default value is {@code false} if this was never set.
6497 */
6498 public boolean getHintContentIntentLaunchesActivity() {
6499 return (mFlags & FLAG_HINT_CONTENT_INTENT_LAUNCHES_ACTIVITY) != 0;
6500 }
6501
Nadia Benbernou948627e2016-04-14 14:41:08 -04006502 /**
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04006503 * Sets the dismissal id for this notification. If a notification is posted with a
6504 * dismissal id, then when that notification is canceled, notifications on other wearables
6505 * and the paired Android phone having that same dismissal id will also be canceled. See
Nadia Benbernou948627e2016-04-14 14:41:08 -04006506 * <a href="{@docRoot}wear/notifications/index.html">Adding Wearable Features to
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04006507 * Notifications</a> for more information.
Nadia Benbernou948627e2016-04-14 14:41:08 -04006508 * @param dismissalId the dismissal id of the notification.
6509 * @return this object for method chaining
6510 */
6511 public WearableExtender setDismissalId(String dismissalId) {
6512 mDismissalId = dismissalId;
6513 return this;
6514 }
6515
6516 /**
6517 * Returns the dismissal id of the notification.
6518 * @return the dismissal id of the notification or null if it has not been set.
6519 */
6520 public String getDismissalId() {
6521 return mDismissalId;
6522 }
6523
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04006524 /**
6525 * Sets a bridge tag for this notification. A bridge tag can be set for notifications
6526 * posted from a phone to provide finer-grained control on what notifications are bridged
6527 * to wearables. See <a href="{@docRoot}wear/notifications/index.html">Adding Wearable
6528 * Features to Notifications</a> for more information.
6529 * @param bridgeTag the bridge tag of the notification.
6530 * @return this object for method chaining
6531 */
6532 public WearableExtender setBridgeTag(String bridgeTag) {
6533 mBridgeTag = bridgeTag;
6534 return this;
6535 }
6536
6537 /**
6538 * Returns the bridge tag of the notification.
6539 * @return the bridge tag or null if not present.
6540 */
6541 public String getBridgeTag() {
6542 return mBridgeTag;
6543 }
6544
Griff Hazen61a9e862014-05-22 16:05:19 -07006545 private void setFlag(int mask, boolean value) {
6546 if (value) {
6547 mFlags |= mask;
6548 } else {
6549 mFlags &= ~mask;
6550 }
6551 }
6552 }
6553
6554 /**
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08006555 * <p>Helper class to add Android Auto extensions to notifications. To create a notification
6556 * with car extensions:
6557 *
6558 * <ol>
6559 * <li>Create an {@link Notification.Builder}, setting any desired
6560 * properties.
6561 * <li>Create a {@link CarExtender}.
6562 * <li>Set car-specific properties using the {@code add} and {@code set} methods of
6563 * {@link CarExtender}.
6564 * <li>Call {@link Notification.Builder#extend(Notification.Extender)}
6565 * to apply the extensions to a notification.
6566 * </ol>
6567 *
6568 * <pre class="prettyprint">
6569 * Notification notification = new Notification.Builder(context)
6570 * ...
6571 * .extend(new CarExtender()
6572 * .set*(...))
6573 * .build();
6574 * </pre>
6575 *
6576 * <p>Car extensions can be accessed on an existing notification by using the
6577 * {@code CarExtender(Notification)} constructor, and then using the {@code get} methods
6578 * to access values.
6579 */
6580 public static final class CarExtender implements Extender {
6581 private static final String TAG = "CarExtender";
6582
6583 private static final String EXTRA_CAR_EXTENDER = "android.car.EXTENSIONS";
6584 private static final String EXTRA_LARGE_ICON = "large_icon";
6585 private static final String EXTRA_CONVERSATION = "car_conversation";
6586 private static final String EXTRA_COLOR = "app_color";
6587
6588 private Bitmap mLargeIcon;
6589 private UnreadConversation mUnreadConversation;
6590 private int mColor = Notification.COLOR_DEFAULT;
6591
6592 /**
6593 * Create a {@link CarExtender} with default options.
6594 */
6595 public CarExtender() {
6596 }
6597
6598 /**
6599 * Create a {@link CarExtender} from the CarExtender options of an existing Notification.
6600 *
6601 * @param notif The notification from which to copy options.
6602 */
6603 public CarExtender(Notification notif) {
6604 Bundle carBundle = notif.extras == null ?
6605 null : notif.extras.getBundle(EXTRA_CAR_EXTENDER);
6606 if (carBundle != null) {
6607 mLargeIcon = carBundle.getParcelable(EXTRA_LARGE_ICON);
6608 mColor = carBundle.getInt(EXTRA_COLOR, Notification.COLOR_DEFAULT);
6609
6610 Bundle b = carBundle.getBundle(EXTRA_CONVERSATION);
6611 mUnreadConversation = UnreadConversation.getUnreadConversationFromBundle(b);
6612 }
6613 }
6614
6615 /**
6616 * Apply car extensions to a notification that is being built. This is typically called by
6617 * the {@link Notification.Builder#extend(Notification.Extender)}
6618 * method of {@link Notification.Builder}.
6619 */
6620 @Override
6621 public Notification.Builder extend(Notification.Builder builder) {
6622 Bundle carExtensions = new Bundle();
6623
6624 if (mLargeIcon != null) {
6625 carExtensions.putParcelable(EXTRA_LARGE_ICON, mLargeIcon);
6626 }
6627 if (mColor != Notification.COLOR_DEFAULT) {
6628 carExtensions.putInt(EXTRA_COLOR, mColor);
6629 }
6630
6631 if (mUnreadConversation != null) {
6632 Bundle b = mUnreadConversation.getBundleForUnreadConversation();
6633 carExtensions.putBundle(EXTRA_CONVERSATION, b);
6634 }
6635
6636 builder.getExtras().putBundle(EXTRA_CAR_EXTENDER, carExtensions);
6637 return builder;
6638 }
6639
6640 /**
6641 * Sets the accent color to use when Android Auto presents the notification.
6642 *
6643 * Android Auto uses the color set with {@link Notification.Builder#setColor(int)}
6644 * to accent the displayed notification. However, not all colors are acceptable in an
6645 * automotive setting. This method can be used to override the color provided in the
6646 * notification in such a situation.
6647 */
Tor Norbye80756e32015-03-02 09:39:27 -08006648 public CarExtender setColor(@ColorInt int color) {
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08006649 mColor = color;
6650 return this;
6651 }
6652
6653 /**
6654 * Gets the accent color.
6655 *
Julia Reynoldsd9228f12015-10-20 10:37:27 -04006656 * @see #setColor
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08006657 */
Tor Norbye80756e32015-03-02 09:39:27 -08006658 @ColorInt
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08006659 public int getColor() {
6660 return mColor;
6661 }
6662
6663 /**
6664 * Sets the large icon of the car notification.
6665 *
6666 * If no large icon is set in the extender, Android Auto will display the icon
6667 * specified by {@link Notification.Builder#setLargeIcon(android.graphics.Bitmap)}
6668 *
6669 * @param largeIcon The large icon to use in the car notification.
6670 * @return This object for method chaining.
6671 */
6672 public CarExtender setLargeIcon(Bitmap largeIcon) {
6673 mLargeIcon = largeIcon;
6674 return this;
6675 }
6676
6677 /**
6678 * Gets the large icon used in this car notification, or null if no icon has been set.
6679 *
6680 * @return The large icon for the car notification.
6681 * @see CarExtender#setLargeIcon
6682 */
6683 public Bitmap getLargeIcon() {
6684 return mLargeIcon;
6685 }
6686
6687 /**
6688 * Sets the unread conversation in a message notification.
6689 *
6690 * @param unreadConversation The unread part of the conversation this notification conveys.
6691 * @return This object for method chaining.
6692 */
6693 public CarExtender setUnreadConversation(UnreadConversation unreadConversation) {
6694 mUnreadConversation = unreadConversation;
6695 return this;
6696 }
6697
6698 /**
6699 * Returns the unread conversation conveyed by this notification.
6700 * @see #setUnreadConversation(UnreadConversation)
6701 */
6702 public UnreadConversation getUnreadConversation() {
6703 return mUnreadConversation;
6704 }
6705
6706 /**
6707 * A class which holds the unread messages from a conversation.
6708 */
6709 public static class UnreadConversation {
6710 private static final String KEY_AUTHOR = "author";
6711 private static final String KEY_TEXT = "text";
6712 private static final String KEY_MESSAGES = "messages";
6713 private static final String KEY_REMOTE_INPUT = "remote_input";
6714 private static final String KEY_ON_REPLY = "on_reply";
6715 private static final String KEY_ON_READ = "on_read";
6716 private static final String KEY_PARTICIPANTS = "participants";
6717 private static final String KEY_TIMESTAMP = "timestamp";
6718
6719 private final String[] mMessages;
6720 private final RemoteInput mRemoteInput;
6721 private final PendingIntent mReplyPendingIntent;
6722 private final PendingIntent mReadPendingIntent;
6723 private final String[] mParticipants;
6724 private final long mLatestTimestamp;
6725
6726 UnreadConversation(String[] messages, RemoteInput remoteInput,
6727 PendingIntent replyPendingIntent, PendingIntent readPendingIntent,
6728 String[] participants, long latestTimestamp) {
6729 mMessages = messages;
6730 mRemoteInput = remoteInput;
6731 mReadPendingIntent = readPendingIntent;
6732 mReplyPendingIntent = replyPendingIntent;
6733 mParticipants = participants;
6734 mLatestTimestamp = latestTimestamp;
6735 }
6736
6737 /**
6738 * Gets the list of messages conveyed by this notification.
6739 */
6740 public String[] getMessages() {
6741 return mMessages;
6742 }
6743
6744 /**
6745 * Gets the remote input that will be used to convey the response to a message list, or
6746 * null if no such remote input exists.
6747 */
6748 public RemoteInput getRemoteInput() {
6749 return mRemoteInput;
6750 }
6751
6752 /**
6753 * Gets the pending intent that will be triggered when the user replies to this
6754 * notification.
6755 */
6756 public PendingIntent getReplyPendingIntent() {
6757 return mReplyPendingIntent;
6758 }
6759
6760 /**
6761 * Gets the pending intent that Android Auto will send after it reads aloud all messages
6762 * in this object's message list.
6763 */
6764 public PendingIntent getReadPendingIntent() {
6765 return mReadPendingIntent;
6766 }
6767
6768 /**
6769 * Gets the participants in the conversation.
6770 */
6771 public String[] getParticipants() {
6772 return mParticipants;
6773 }
6774
6775 /**
6776 * Gets the firs participant in the conversation.
6777 */
6778 public String getParticipant() {
6779 return mParticipants.length > 0 ? mParticipants[0] : null;
6780 }
6781
6782 /**
6783 * Gets the timestamp of the conversation.
6784 */
6785 public long getLatestTimestamp() {
6786 return mLatestTimestamp;
6787 }
6788
6789 Bundle getBundleForUnreadConversation() {
6790 Bundle b = new Bundle();
6791 String author = null;
6792 if (mParticipants != null && mParticipants.length > 1) {
6793 author = mParticipants[0];
6794 }
6795 Parcelable[] messages = new Parcelable[mMessages.length];
6796 for (int i = 0; i < messages.length; i++) {
6797 Bundle m = new Bundle();
6798 m.putString(KEY_TEXT, mMessages[i]);
6799 m.putString(KEY_AUTHOR, author);
6800 messages[i] = m;
6801 }
6802 b.putParcelableArray(KEY_MESSAGES, messages);
6803 if (mRemoteInput != null) {
6804 b.putParcelable(KEY_REMOTE_INPUT, mRemoteInput);
6805 }
6806 b.putParcelable(KEY_ON_REPLY, mReplyPendingIntent);
6807 b.putParcelable(KEY_ON_READ, mReadPendingIntent);
6808 b.putStringArray(KEY_PARTICIPANTS, mParticipants);
6809 b.putLong(KEY_TIMESTAMP, mLatestTimestamp);
6810 return b;
6811 }
6812
6813 static UnreadConversation getUnreadConversationFromBundle(Bundle b) {
6814 if (b == null) {
6815 return null;
6816 }
6817 Parcelable[] parcelableMessages = b.getParcelableArray(KEY_MESSAGES);
6818 String[] messages = null;
6819 if (parcelableMessages != null) {
6820 String[] tmp = new String[parcelableMessages.length];
6821 boolean success = true;
6822 for (int i = 0; i < tmp.length; i++) {
6823 if (!(parcelableMessages[i] instanceof Bundle)) {
6824 success = false;
6825 break;
6826 }
6827 tmp[i] = ((Bundle) parcelableMessages[i]).getString(KEY_TEXT);
6828 if (tmp[i] == null) {
6829 success = false;
6830 break;
6831 }
6832 }
6833 if (success) {
6834 messages = tmp;
6835 } else {
6836 return null;
6837 }
6838 }
6839
6840 PendingIntent onRead = b.getParcelable(KEY_ON_READ);
6841 PendingIntent onReply = b.getParcelable(KEY_ON_REPLY);
6842
6843 RemoteInput remoteInput = b.getParcelable(KEY_REMOTE_INPUT);
6844
6845 String[] participants = b.getStringArray(KEY_PARTICIPANTS);
6846 if (participants == null || participants.length != 1) {
6847 return null;
6848 }
6849
6850 return new UnreadConversation(messages,
6851 remoteInput,
6852 onReply,
6853 onRead,
6854 participants, b.getLong(KEY_TIMESTAMP));
6855 }
6856 };
6857
6858 /**
6859 * Builder class for {@link CarExtender.UnreadConversation} objects.
6860 */
6861 public static class Builder {
6862 private final List<String> mMessages = new ArrayList<String>();
6863 private final String mParticipant;
6864 private RemoteInput mRemoteInput;
6865 private PendingIntent mReadPendingIntent;
6866 private PendingIntent mReplyPendingIntent;
6867 private long mLatestTimestamp;
6868
6869 /**
6870 * Constructs a new builder for {@link CarExtender.UnreadConversation}.
6871 *
6872 * @param name The name of the other participant in the conversation.
6873 */
6874 public Builder(String name) {
6875 mParticipant = name;
6876 }
6877
6878 /**
6879 * Appends a new unread message to the list of messages for this conversation.
6880 *
6881 * The messages should be added from oldest to newest.
6882 *
6883 * @param message The text of the new unread message.
6884 * @return This object for method chaining.
6885 */
6886 public Builder addMessage(String message) {
6887 mMessages.add(message);
6888 return this;
6889 }
6890
6891 /**
6892 * Sets the pending intent and remote input which will convey the reply to this
6893 * notification.
6894 *
6895 * @param pendingIntent The pending intent which will be triggered on a reply.
6896 * @param remoteInput The remote input parcelable which will carry the reply.
6897 * @return This object for method chaining.
6898 *
6899 * @see CarExtender.UnreadConversation#getRemoteInput
6900 * @see CarExtender.UnreadConversation#getReplyPendingIntent
6901 */
6902 public Builder setReplyAction(
6903 PendingIntent pendingIntent, RemoteInput remoteInput) {
6904 mRemoteInput = remoteInput;
6905 mReplyPendingIntent = pendingIntent;
6906
6907 return this;
6908 }
6909
6910 /**
6911 * Sets the pending intent that will be sent once the messages in this notification
6912 * are read.
6913 *
6914 * @param pendingIntent The pending intent to use.
6915 * @return This object for method chaining.
6916 */
6917 public Builder setReadPendingIntent(PendingIntent pendingIntent) {
6918 mReadPendingIntent = pendingIntent;
6919 return this;
6920 }
6921
6922 /**
6923 * Sets the timestamp of the most recent message in an unread conversation.
6924 *
6925 * If a messaging notification has been posted by your application and has not
6926 * yet been cancelled, posting a later notification with the same id and tag
6927 * but without a newer timestamp may result in Android Auto not displaying a
6928 * heads up notification for the later notification.
6929 *
6930 * @param timestamp The timestamp of the most recent message in the conversation.
6931 * @return This object for method chaining.
6932 */
6933 public Builder setLatestTimestamp(long timestamp) {
6934 mLatestTimestamp = timestamp;
6935 return this;
6936 }
6937
6938 /**
6939 * Builds a new unread conversation object.
6940 *
6941 * @return The new unread conversation object.
6942 */
6943 public UnreadConversation build() {
6944 String[] messages = mMessages.toArray(new String[mMessages.size()]);
6945 String[] participants = { mParticipant };
6946 return new UnreadConversation(messages, mRemoteInput, mReplyPendingIntent,
6947 mReadPendingIntent, participants, mLatestTimestamp);
6948 }
6949 }
6950 }
6951
6952 /**
Griff Hazen61a9e862014-05-22 16:05:19 -07006953 * Get an array of Notification objects from a parcelable array bundle field.
6954 * Update the bundle to have a typed array so fetches in the future don't need
6955 * to do an array copy.
6956 */
6957 private static Notification[] getNotificationArrayFromBundle(Bundle bundle, String key) {
6958 Parcelable[] array = bundle.getParcelableArray(key);
6959 if (array instanceof Notification[] || array == null) {
6960 return (Notification[]) array;
6961 }
6962 Notification[] typedArray = Arrays.copyOf(array, array.length,
6963 Notification[].class);
6964 bundle.putParcelableArray(key, typedArray);
6965 return typedArray;
6966 }
Christoph Studer4600f9b2014-07-22 22:44:43 +02006967
6968 private static class BuilderRemoteViews extends RemoteViews {
6969 public BuilderRemoteViews(Parcel parcel) {
6970 super(parcel);
6971 }
6972
Kenny Guy77320062014-08-27 21:37:15 +01006973 public BuilderRemoteViews(ApplicationInfo appInfo, int layoutId) {
6974 super(appInfo, layoutId);
Christoph Studer4600f9b2014-07-22 22:44:43 +02006975 }
6976
6977 @Override
6978 public BuilderRemoteViews clone() {
6979 Parcel p = Parcel.obtain();
6980 writeToParcel(p, 0);
6981 p.setDataPosition(0);
6982 BuilderRemoteViews brv = new BuilderRemoteViews(p);
6983 p.recycle();
6984 return brv;
6985 }
6986 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006987}