blob: 9a2cd0042a341947a6180a57e5039e6788907efd [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;
Daniel Sandler01df1c62014-06-09 10:54:01 -040022import android.annotation.SdkConstant;
23import android.annotation.SdkConstant.SdkConstantType;
Julia Reynoldse46bb372016-03-17 11:05:58 -040024import android.annotation.SystemApi;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080025import android.content.Context;
26import android.content.Intent;
Kenny Guy77320062014-08-27 21:37:15 +010027import android.content.pm.ApplicationInfo;
Dan Sandler732bd6c2016-04-12 14:20:32 -040028import android.content.pm.PackageManager;
Christoph Studer4600f9b2014-07-22 22:44:43 +020029import android.content.pm.PackageManager.NameNotFoundException;
Jorim Jaggief72a192014-08-26 21:57:46 +020030import android.content.res.ColorStateList;
Joe Onoratoef1e7762010-09-17 18:38:38 -040031import android.graphics.Bitmap;
Kenny Guy8a0101b2014-05-08 23:34:12 +010032import android.graphics.Canvas;
Adrian Roosc1a80b02016-04-05 14:54:55 -070033import android.graphics.Color;
Jorim Jaggi5c2d8462014-03-21 17:37:00 +010034import android.graphics.PorterDuff;
Kenny Guy8a0101b2014-05-08 23:34:12 +010035import android.graphics.drawable.Drawable;
Dan Sandlerd63f9322015-05-06 15:18:49 -040036import android.graphics.drawable.Icon;
John Spurlockc0650f022014-07-19 13:22:39 -040037import android.media.AudioAttributes;
Jeff Sharkey098d5802012-04-26 17:30:34 -070038import android.media.AudioManager;
Jeff Browndba34ba2014-06-24 20:46:03 -070039import android.media.session.MediaSession;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080040import android.net.Uri;
Daniel Sandlerdcbaf662013-04-26 16:23:09 -040041import android.os.BadParcelableException;
Christoph Studer239f8352014-08-25 15:13:18 +020042import android.os.Build;
Daniel Sandler2561b0b2012-02-13 21:04:12 -050043import android.os.Bundle;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080044import android.os.Parcel;
45import android.os.Parcelable;
Daniel Sandlera2985ed2012-04-03 16:42:00 -040046import android.os.SystemClock;
Jeff Sharkey6d515712012-09-20 16:06:08 -070047import android.os.UserHandle;
Adrian Roosc1a80b02016-04-05 14:54:55 -070048import android.text.BidiFormatter;
Selim Cinek60a54252016-02-26 17:03:25 -080049import android.text.SpannableStringBuilder;
50import android.text.Spanned;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080051import android.text.TextUtils;
Selim Cinek60a54252016-02-26 17:03:25 -080052import android.text.style.AbsoluteSizeSpan;
Selim Cinek89991a22016-03-07 19:51:54 -080053import android.text.style.CharacterStyle;
Selim Cinek60a54252016-02-26 17:03:25 -080054import android.text.style.RelativeSizeSpan;
55import android.text.style.TextAppearanceSpan;
Daniel Sandlerdcbaf662013-04-26 16:23:09 -040056import android.util.Log;
Dan Sandler50128532015-12-08 15:42:41 -050057import android.util.SparseArray;
Daniel Sandler9f7936a2012-05-21 16:14:28 -040058import android.util.TypedValue;
Griff Hazen61a9e862014-05-22 16:05:19 -070059import android.view.Gravity;
Selim Cinekea4bef72015-12-02 15:51:10 -080060import android.view.NotificationHeaderView;
Joe Onorato8595a3d2010-11-19 18:12:07 -080061import android.view.View;
Adrian Roos9b123cf2016-02-04 14:55:57 -080062import android.view.ViewGroup;
Jeff Sharkey1c400132011-08-05 14:50:13 -070063import android.widget.ProgressBar;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080064import android.widget.RemoteViews;
65
Griff Hazen959591e2014-05-15 22:26:18 -070066import com.android.internal.R;
Griff Hazenc091ba82014-05-16 10:13:26 -070067import com.android.internal.util.NotificationColorUtil;
Griff Hazen959591e2014-05-15 22:26:18 -070068
Tor Norbyed9273d62013-05-30 15:59:53 -070069import java.lang.annotation.Retention;
70import java.lang.annotation.RetentionPolicy;
Christoph Studer4600f9b2014-07-22 22:44:43 +020071import java.lang.reflect.Constructor;
Daniel Sandler2561b0b2012-02-13 21:04:12 -050072import java.util.ArrayList;
Griff Hazen61a9e862014-05-22 16:05:19 -070073import java.util.Arrays;
Griff Hazen5cadc3b2014-05-20 09:55:39 -070074import java.util.Collections;
Griff Hazen61a9e862014-05-22 16:05:19 -070075import java.util.List;
Dan Sandler50128532015-12-08 15:42:41 -050076import java.util.Set;
Joe Onorato561d3852010-11-20 18:09:34 -080077
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080078/**
79 * A class that represents how a persistent notification is to be presented to
80 * the user using the {@link android.app.NotificationManager}.
81 *
Joe Onoratocb109a02011-01-18 17:57:41 -080082 * <p>The {@link Notification.Builder Notification.Builder} has been added to make it
83 * easier to construct Notifications.</p>
84 *
Joe Fernandez558459f2011-10-13 16:47:36 -070085 * <div class="special reference">
86 * <h3>Developer Guides</h3>
87 * <p>For a guide to creating notifications, read the
88 * <a href="{@docRoot}guide/topics/ui/notifiers/notifications.html">Status Bar Notifications</a>
89 * developer guide.</p>
90 * </div>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080091 */
92public class Notification implements Parcelable
93{
Daniel Sandlerdcbaf662013-04-26 16:23:09 -040094 private static final String TAG = "Notification";
95
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080096 /**
Daniel Sandler01df1c62014-06-09 10:54:01 -040097 * An activity that provides a user interface for adjusting notification preferences for its
98 * containing application. Optional but recommended for apps that post
99 * {@link android.app.Notification Notifications}.
100 */
101 @SdkConstant(SdkConstantType.INTENT_CATEGORY)
102 public static final String INTENT_CATEGORY_NOTIFICATION_PREFERENCES
103 = "android.intent.category.NOTIFICATION_PREFERENCES";
104
105 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800106 * Use all default values (where applicable).
107 */
108 public static final int DEFAULT_ALL = ~0;
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500109
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800110 /**
111 * Use the default notification sound. This will ignore any given
112 * {@link #sound}.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500113 *
Chris Wren47c20a12014-06-18 17:27:29 -0400114 * <p>
115 * A notification that is noisy is more likely to be presented as a heads-up notification.
116 * </p>
117 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800118 * @see #defaults
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500119 */
120
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800121 public static final int DEFAULT_SOUND = 1;
122
123 /**
124 * Use the default notification vibrate. This will ignore any given
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500125 * {@link #vibrate}. Using phone vibration requires the
Scott Mainb8b36452009-04-26 15:50:49 -0700126 * {@link android.Manifest.permission#VIBRATE VIBRATE} permission.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500127 *
Chris Wren47c20a12014-06-18 17:27:29 -0400128 * <p>
129 * A notification that vibrates is more likely to be presented as a heads-up notification.
130 * </p>
131 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800132 * @see #defaults
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500133 */
134
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800135 public static final int DEFAULT_VIBRATE = 2;
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500136
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800137 /**
138 * Use the default notification lights. This will ignore the
139 * {@link #FLAG_SHOW_LIGHTS} bit, and {@link #ledARGB}, {@link #ledOffMS}, or
140 * {@link #ledOnMS}.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500141 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800142 * @see #defaults
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500143 */
144
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800145 public static final int DEFAULT_LIGHTS = 4;
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500146
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800147 /**
Christoph Studer535ec612014-09-03 15:47:47 +0200148 * Maximum length of CharSequences accepted by Builder and friends.
149 *
150 * <p>
151 * Avoids spamming the system with overly large strings such as full e-mails.
152 */
153 private static final int MAX_CHARSEQUENCE_LENGTH = 5 * 1024;
154
155 /**
Adrian Roose458aa82015-12-08 16:17:19 -0800156 * Maximum entries of reply text that are accepted by Builder and friends.
157 */
158 private static final int MAX_REPLY_HISTORY = 5;
159
160 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500161 * A timestamp related to this notification, in milliseconds since the epoch.
Joe Malin8d40d042012-11-05 11:36:40 -0800162 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500163 * Default value: {@link System#currentTimeMillis() Now}.
164 *
165 * Choose a timestamp that will be most relevant to the user. For most finite events, this
166 * corresponds to the time the event happened (or will happen, in the case of events that have
167 * yet to occur but about which the user is being informed). Indefinite events should be
Joe Malin8d40d042012-11-05 11:36:40 -0800168 * timestamped according to when the activity began.
169 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500170 * Some examples:
Joe Malin8d40d042012-11-05 11:36:40 -0800171 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500172 * <ul>
173 * <li>Notification of a new chat message should be stamped when the message was received.</li>
174 * <li>Notification of an ongoing file download (with a progress bar, for example) should be stamped when the download started.</li>
175 * <li>Notification of a completed file download should be stamped when the download finished.</li>
176 * <li>Notification of an upcoming meeting should be stamped with the time the meeting will begin (that is, in the future).</li>
177 * <li>Notification of an ongoing stopwatch (increasing timer) should be stamped with the watch's start time.
178 * <li>Notification of an ongoing countdown timer should be stamped with the timer's end time.
Joe Malin8d40d042012-11-05 11:36:40 -0800179 * </ul>
180 *
Selim Cinek0ff1ce602016-04-05 18:27:16 -0700181 * For apps targeting {@link android.os.Build.VERSION_CODES#N} and above, this time is not shown
182 * anymore by default and must be opted into by using
183 * {@link android.app.Notification.Builder#setShowWhen(boolean)}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800184 */
185 public long when;
186
187 /**
Selim Cinekb85f36fd2016-04-20 18:46:36 -0700188 * The creation time of the notification
189 */
190 private long creationTime;
191
192 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800193 * The resource id of a drawable to use as the icon in the status bar.
Dan Sandler86647982015-05-13 23:41:13 -0400194 *
195 * @deprecated Use {@link Builder#setSmallIcon(Icon)} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800196 */
Dan Sandler86647982015-05-13 23:41:13 -0400197 @Deprecated
Tor Norbye7b9c9122013-05-30 16:48:33 -0700198 @DrawableRes
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800199 public int icon;
200
201 /**
Joe Onorato46439ce2010-11-19 13:56:21 -0800202 * If the icon in the status bar is to have more than one level, you can set this. Otherwise,
203 * leave it at its default value of 0.
204 *
205 * @see android.widget.ImageView#setImageLevel
Griff Hazen959591e2014-05-15 22:26:18 -0700206 * @see android.graphics.drawable.Drawable#setLevel
Joe Onorato46439ce2010-11-19 13:56:21 -0800207 */
208 public int iconLevel;
209
210 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500211 * The number of events that this notification represents. For example, in a new mail
212 * notification, this could be the number of unread messages.
Joe Malin8d40d042012-11-05 11:36:40 -0800213 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500214 * The system may or may not use this field to modify the appearance of the notification. For
215 * example, before {@link android.os.Build.VERSION_CODES#HONEYCOMB}, this number was
216 * superimposed over the icon in the status bar. Starting with
217 * {@link android.os.Build.VERSION_CODES#HONEYCOMB}, the template used by
218 * {@link Notification.Builder} has displayed the number in the expanded notification view.
Joe Malin8d40d042012-11-05 11:36:40 -0800219 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500220 * If the number is 0 or negative, it is never shown.
Selim Cinek0f9dd1e2016-04-05 17:03:40 -0700221 *
222 * @deprecated this number is not shown anymore
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800223 */
224 public int number;
225
226 /**
227 * The intent to execute when the expanded status entry is clicked. If
228 * this is an activity, it must include the
229 * {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} flag, which requires
Scott Main7aee61f2011-02-08 11:25:01 -0800230 * that you take care of task management as described in the
231 * <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back
Dianne Hackborn6ceca582012-01-10 15:24:26 -0800232 * Stack</a> document. In particular, make sure to read the notification section
233 * <a href="{@docRoot}guide/topics/ui/notifiers/notifications.html#HandlingNotifications">Handling
234 * Notifications</a> for the correct ways to launch an application from a
235 * notification.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800236 */
237 public PendingIntent contentIntent;
238
239 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500240 * The intent to execute when the notification is explicitly dismissed by the user, either with
241 * the "Clear All" button or by swiping it away individually.
242 *
243 * This probably shouldn't be launching an activity since several of those will be sent
244 * at the same time.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800245 */
246 public PendingIntent deleteIntent;
247
248 /**
Dianne Hackborn170bae72010-09-03 15:14:28 -0700249 * An intent to launch instead of posting the notification to the status bar.
Joe Onoratocb109a02011-01-18 17:57:41 -0800250 *
Chris Wren47c20a12014-06-18 17:27:29 -0400251 * <p>
252 * The system UI may choose to display a heads-up notification, instead of
253 * launching this intent, while the user is using the device.
254 * </p>
255 *
Joe Onoratocb109a02011-01-18 17:57:41 -0800256 * @see Notification.Builder#setFullScreenIntent
Daniel Sandlere46cbd32010-06-17 10:35:26 -0400257 */
258 public PendingIntent fullScreenIntent;
259
260 /**
Dan Sandler5fcdf6e2014-07-18 11:31:15 -0400261 * Text that summarizes this notification for accessibility services.
262 *
263 * As of the L release, this text is no longer shown on screen, but it is still useful to
264 * accessibility services (where it serves as an audible announcement of the notification's
265 * appearance).
Joe Onoratoef1e7762010-09-17 18:38:38 -0400266 *
Joe Onorato46439ce2010-11-19 13:56:21 -0800267 * @see #tickerView
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800268 */
269 public CharSequence tickerText;
270
271 /**
Dan Sandler5fcdf6e2014-07-18 11:31:15 -0400272 * Formerly, a view showing the {@link #tickerText}.
273 *
274 * No longer displayed in the status bar as of API 21.
Joe Onoratoef1e7762010-09-17 18:38:38 -0400275 */
Dan Sandler5fcdf6e2014-07-18 11:31:15 -0400276 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -0800277 public RemoteViews tickerView;
Joe Onoratoef1e7762010-09-17 18:38:38 -0400278
279 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400280 * The view that will represent this notification in the notification list (which is pulled
281 * down from the status bar).
282 *
Julia Reynoldsd4ea7412016-02-17 14:00:56 -0500283 * As of N, this field may be null. The notification view is determined by the inputs
284 * to {@link Notification.Builder}; a custom RemoteViews can optionally be
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400285 * supplied with {@link Notification.Builder#setCustomContentView(RemoteViews)}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800286 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400287 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800288 public RemoteViews contentView;
289
Daniel Sandlera0a938c2012-03-15 08:42:37 -0400290 /**
Daniel Sandler4dfbe832012-04-11 14:51:46 -0400291 * A large-format version of {@link #contentView}, giving the Notification an
Daniel Sandlerf3b73432012-03-27 15:01:25 -0400292 * opportunity to show more detail. The system UI may choose to show this
293 * instead of the normal content view at its discretion.
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400294 *
Julia Reynoldsd4ea7412016-02-17 14:00:56 -0500295 * As of N, this field may be null. The expanded notification view is determined by the
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400296 * inputs to {@link Notification.Builder}; a custom RemoteViews can optionally be
297 * supplied with {@link Notification.Builder#setCustomBigContentView(RemoteViews)}.
Daniel Sandlerf3b73432012-03-27 15:01:25 -0400298 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400299 @Deprecated
Daniel Sandlerf3b73432012-03-27 15:01:25 -0400300 public RemoteViews bigContentView;
301
Chris Wren8fd39ec2014-02-27 17:43:26 -0500302
303 /**
Chris Wren47c20a12014-06-18 17:27:29 -0400304 * A medium-format version of {@link #contentView}, providing the Notification an
305 * opportunity to add action buttons to contentView. At its discretion, the system UI may
306 * choose to show this as a heads-up notification, which will pop up so the user can see
307 * it without leaving their current activity.
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400308 *
Julia Reynoldsd4ea7412016-02-17 14:00:56 -0500309 * As of N, this field may be null. The heads-up notification view is determined by the
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400310 * inputs to {@link Notification.Builder}; a custom RemoteViews can optionally be
311 * supplied with {@link Notification.Builder#setCustomHeadsUpContentView(RemoteViews)}.
Chris Wren8fd39ec2014-02-27 17:43:26 -0500312 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400313 @Deprecated
Chris Wren8fd39ec2014-02-27 17:43:26 -0500314 public RemoteViews headsUpContentView;
315
Daniel Sandlerf3b73432012-03-27 15:01:25 -0400316 /**
Dan Sandler86647982015-05-13 23:41:13 -0400317 * A large bitmap to be shown in the notification content area.
318 *
319 * @deprecated Use {@link Builder#setLargeIcon(Icon)} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800320 */
Dan Sandler86647982015-05-13 23:41:13 -0400321 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -0800322 public Bitmap largeIcon;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800323
324 /**
325 * The sound to play.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500326 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800327 * <p>
Chris Wren47c20a12014-06-18 17:27:29 -0400328 * A notification that is noisy is more likely to be presented as a heads-up notification.
329 * </p>
330 *
331 * <p>
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500332 * To play the default notification sound, see {@link #defaults}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800333 * </p>
334 */
335 public Uri sound;
336
337 /**
338 * Use this constant as the value for audioStreamType to request that
339 * the default stream type for notifications be used. Currently the
Jeff Sharkey098d5802012-04-26 17:30:34 -0700340 * default stream type is {@link AudioManager#STREAM_NOTIFICATION}.
John Spurlockc0650f022014-07-19 13:22:39 -0400341 *
342 * @deprecated Use {@link #audioAttributes} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800343 */
Jean-Michel Trivi81f871e2014-08-06 16:32:38 -0700344 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800345 public static final int STREAM_DEFAULT = -1;
346
347 /**
348 * The audio stream type to use when playing the sound.
349 * Should be one of the STREAM_ constants from
350 * {@link android.media.AudioManager}.
John Spurlockc0650f022014-07-19 13:22:39 -0400351 *
352 * @deprecated Use {@link #audioAttributes} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800353 */
Jean-Michel Trivi81f871e2014-08-06 16:32:38 -0700354 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800355 public int audioStreamType = STREAM_DEFAULT;
356
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800357 /**
John Spurlockc0650f022014-07-19 13:22:39 -0400358 * The default value of {@link #audioAttributes}.
359 */
360 public static final AudioAttributes AUDIO_ATTRIBUTES_DEFAULT = new AudioAttributes.Builder()
361 .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
362 .setUsage(AudioAttributes.USAGE_NOTIFICATION)
363 .build();
364
365 /**
366 * The {@link AudioAttributes audio attributes} to use when playing the sound.
367 */
368 public AudioAttributes audioAttributes = AUDIO_ATTRIBUTES_DEFAULT;
369
370 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500371 * The pattern with which to vibrate.
372 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800373 * <p>
374 * To vibrate the default pattern, see {@link #defaults}.
375 * </p>
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500376 *
Chris Wren47c20a12014-06-18 17:27:29 -0400377 * <p>
378 * A notification that vibrates is more likely to be presented as a heads-up notification.
379 * </p>
380 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800381 * @see android.os.Vibrator#vibrate(long[],int)
382 */
383 public long[] vibrate;
384
385 /**
386 * The color of the led. The hardware will do its best approximation.
387 *
388 * @see #FLAG_SHOW_LIGHTS
389 * @see #flags
390 */
Tor Norbye80756e32015-03-02 09:39:27 -0800391 @ColorInt
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800392 public int ledARGB;
393
394 /**
395 * The number of milliseconds for the LED to be on while it's flashing.
396 * The hardware will do its best approximation.
397 *
398 * @see #FLAG_SHOW_LIGHTS
399 * @see #flags
400 */
401 public int ledOnMS;
402
403 /**
404 * The number of milliseconds for the LED to be off while it's flashing.
405 * The hardware will do its best approximation.
406 *
407 * @see #FLAG_SHOW_LIGHTS
408 * @see #flags
409 */
410 public int ledOffMS;
411
412 /**
413 * Specifies which values should be taken from the defaults.
414 * <p>
415 * To set, OR the desired from {@link #DEFAULT_SOUND},
416 * {@link #DEFAULT_VIBRATE}, {@link #DEFAULT_LIGHTS}. For all default
417 * values, use {@link #DEFAULT_ALL}.
418 * </p>
419 */
420 public int defaults;
421
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800422 /**
423 * Bit to be bitwise-ored into the {@link #flags} field that should be
424 * set if you want the LED on for this notification.
425 * <ul>
426 * <li>To turn the LED off, pass 0 in the alpha channel for colorARGB
427 * or 0 for both ledOnMS and ledOffMS.</li>
428 * <li>To turn the LED on, pass 1 for ledOnMS and 0 for ledOffMS.</li>
429 * <li>To flash the LED, pass the number of milliseconds that it should
430 * be on and off to ledOnMS and ledOffMS.</li>
431 * </ul>
432 * <p>
433 * Since hardware varies, you are not guaranteed that any of the values
434 * you pass are honored exactly. Use the system defaults (TODO) if possible
435 * because they will be set to values that work on any given hardware.
436 * <p>
437 * The alpha channel must be set for forward compatibility.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500438 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800439 */
440 public static final int FLAG_SHOW_LIGHTS = 0x00000001;
441
442 /**
443 * Bit to be bitwise-ored into the {@link #flags} field that should be
444 * set if this notification is in reference to something that is ongoing,
445 * like a phone call. It should not be set if this notification is in
446 * reference to something that happened at a particular point in time,
447 * like a missed phone call.
448 */
449 public static final int FLAG_ONGOING_EVENT = 0x00000002;
450
451 /**
452 * Bit to be bitwise-ored into the {@link #flags} field that if set,
Scott Mainb8b36452009-04-26 15:50:49 -0700453 * the audio will be repeated until the notification is
454 * cancelled or the notification window is opened.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800455 */
456 public static final int FLAG_INSISTENT = 0x00000004;
457
458 /**
459 * Bit to be bitwise-ored into the {@link #flags} field that should be
Griff Hazen293977b2014-04-28 08:37:20 -0700460 * set if you would only like the sound, vibrate and ticker to be played
461 * if the notification was not already showing.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800462 */
463 public static final int FLAG_ONLY_ALERT_ONCE = 0x00000008;
464
465 /**
466 * Bit to be bitwise-ored into the {@link #flags} field that should be
467 * set if the notification should be canceled when it is clicked by the
Daniel Sandler8aa9ae62012-12-04 23:31:47 -0500468 * user.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800469 */
470 public static final int FLAG_AUTO_CANCEL = 0x00000010;
471
472 /**
473 * Bit to be bitwise-ored into the {@link #flags} field that should be
474 * set if the notification should not be canceled when the user clicks
475 * the Clear all button.
476 */
477 public static final int FLAG_NO_CLEAR = 0x00000020;
478
Dianne Hackbornd8a43f62009-08-17 23:33:56 -0700479 /**
480 * Bit to be bitwise-ored into the {@link #flags} field that should be
481 * set if this notification represents a currently running service. This
482 * will normally be set for you by {@link Service#startForeground}.
483 */
484 public static final int FLAG_FOREGROUND_SERVICE = 0x00000040;
485
Daniel Sandlere46cbd32010-06-17 10:35:26 -0400486 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500487 * Obsolete flag indicating high-priority notifications; use the priority field instead.
Joe Malin8d40d042012-11-05 11:36:40 -0800488 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500489 * @deprecated Use {@link #priority} with a positive value.
Daniel Sandlere46cbd32010-06-17 10:35:26 -0400490 */
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500491 public static final int FLAG_HIGH_PRIORITY = 0x00000080;
Daniel Sandlere46cbd32010-06-17 10:35:26 -0400492
Griff Hazendfcb0802014-02-11 12:00:00 -0800493 /**
494 * Bit to be bitswise-ored into the {@link #flags} field that should be
495 * set if this notification is relevant to the current device only
496 * and it is not recommended that it bridge to other devices.
497 */
498 public static final int FLAG_LOCAL_ONLY = 0x00000100;
499
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700500 /**
501 * Bit to be bitswise-ored into the {@link #flags} field that should be
502 * set if this notification is the group summary for a group of notifications.
503 * Grouped notifications may display in a cluster or stack on devices which
504 * support such rendering. Requires a group key also be set using {@link Builder#setGroup}.
505 */
506 public static final int FLAG_GROUP_SUMMARY = 0x00000200;
507
Julia Reynoldse46bb372016-03-17 11:05:58 -0400508 /**
509 * Bit to be bitswise-ored into the {@link #flags} field that should be
510 * set if this notification is the group summary for an auto-group of notifications.
511 *
512 * @hide
513 */
514 @SystemApi
515 public static final int FLAG_AUTOGROUP_SUMMARY = 0x00000400;
516
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800517 public int flags;
518
Tor Norbyed9273d62013-05-30 15:59:53 -0700519 /** @hide */
520 @IntDef({PRIORITY_DEFAULT,PRIORITY_LOW,PRIORITY_MIN,PRIORITY_HIGH,PRIORITY_MAX})
521 @Retention(RetentionPolicy.SOURCE)
522 public @interface Priority {}
523
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800524 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500525 * Default notification {@link #priority}. If your application does not prioritize its own
526 * notifications, use this value for all notifications.
527 */
528 public static final int PRIORITY_DEFAULT = 0;
529
530 /**
531 * Lower {@link #priority}, for items that are less important. The UI may choose to show these
532 * items smaller, or at a different position in the list, compared with your app's
533 * {@link #PRIORITY_DEFAULT} items.
534 */
535 public static final int PRIORITY_LOW = -1;
536
537 /**
538 * Lowest {@link #priority}; these items might not be shown to the user except under special
539 * circumstances, such as detailed notification logs.
540 */
541 public static final int PRIORITY_MIN = -2;
542
543 /**
544 * Higher {@link #priority}, for more important notifications or alerts. The UI may choose to
545 * show these items larger, or at a different position in notification lists, compared with
546 * your app's {@link #PRIORITY_DEFAULT} items.
547 */
548 public static final int PRIORITY_HIGH = 1;
549
550 /**
551 * Highest {@link #priority}, for your application's most important items that require the
552 * user's prompt attention or input.
553 */
554 public static final int PRIORITY_MAX = 2;
555
556 /**
557 * Relative priority for this notification.
Joe Malin8d40d042012-11-05 11:36:40 -0800558 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500559 * Priority is an indication of how much of the user's valuable attention should be consumed by
560 * this notification. Low-priority notifications may be hidden from the user in certain
561 * situations, while the user might be interrupted for a higher-priority notification. The
Daniel Sandler6738eee2012-11-16 12:03:32 -0500562 * system will make a determination about how to interpret this priority when presenting
563 * the notification.
Chris Wren47c20a12014-06-18 17:27:29 -0400564 *
565 * <p>
566 * A notification that is at least {@link #PRIORITY_HIGH} is more likely to be presented
567 * as a heads-up notification.
568 * </p>
569 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500570 */
Tor Norbyed9273d62013-05-30 15:59:53 -0700571 @Priority
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500572 public int priority;
Joe Malin8d40d042012-11-05 11:36:40 -0800573
Dan Sandler26e81cf2014-05-06 10:01:27 -0400574 /**
575 * Accent color (an ARGB integer like the constants in {@link android.graphics.Color})
576 * to be applied by the standard Style templates when presenting this notification.
577 *
578 * The current template design constructs a colorful header image by overlaying the
579 * {@link #icon} image (stenciled in white) atop a field of this color. Alpha components are
580 * ignored.
581 */
Tor Norbye80756e32015-03-02 09:39:27 -0800582 @ColorInt
Dan Sandler26e81cf2014-05-06 10:01:27 -0400583 public int color = COLOR_DEFAULT;
584
585 /**
586 * Special value of {@link #color} telling the system not to decorate this notification with
587 * any special color but instead use default colors when presenting this notification.
588 */
Tor Norbye80756e32015-03-02 09:39:27 -0800589 @ColorInt
Dan Sandler26e81cf2014-05-06 10:01:27 -0400590 public static final int COLOR_DEFAULT = 0; // AKA Color.TRANSPARENT
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600591
592 /**
Adrian Roos4ff3b122016-02-01 12:26:13 -0800593 * Special value of {@link #color} used as a place holder for an invalid color.
594 */
595 @ColorInt
596 private static final int COLOR_INVALID = 1;
597
598 /**
Adrian Roosc1a80b02016-04-05 14:54:55 -0700599 * Sphere of visibility of this notification, which affects how and when the SystemUI reveals
600 * the notification's presence and contents in untrusted situations (namely, on the secure
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600601 * lockscreen).
602 *
603 * The default level, {@link #VISIBILITY_PRIVATE}, behaves exactly as notifications have always
604 * done on Android: The notification's {@link #icon} and {@link #tickerText} (if available) are
605 * shown in all situations, but the contents are only available if the device is unlocked for
606 * the appropriate user.
607 *
608 * A more permissive policy can be expressed by {@link #VISIBILITY_PUBLIC}; such a notification
609 * can be read even in an "insecure" context (that is, above a secure lockscreen).
610 * To modify the public version of this notification—for example, to redact some portions—see
611 * {@link Builder#setPublicVersion(Notification)}.
612 *
613 * Finally, a notification can be made {@link #VISIBILITY_SECRET}, which will suppress its icon
614 * and ticker until the user has bypassed the lockscreen.
615 */
616 public int visibility;
617
Griff Hazenfc3922d2014-08-20 11:56:44 -0700618 /**
619 * Notification visibility: Show this notification in its entirety on all lockscreens.
620 *
621 * {@see #visibility}
622 */
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600623 public static final int VISIBILITY_PUBLIC = 1;
Griff Hazenfc3922d2014-08-20 11:56:44 -0700624
625 /**
626 * Notification visibility: Show this notification on all lockscreens, but conceal sensitive or
627 * private information on secure lockscreens.
628 *
629 * {@see #visibility}
630 */
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600631 public static final int VISIBILITY_PRIVATE = 0;
Griff Hazenfc3922d2014-08-20 11:56:44 -0700632
633 /**
634 * Notification visibility: Do not reveal any part of this notification on a secure lockscreen.
635 *
636 * {@see #visibility}
637 */
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600638 public static final int VISIBILITY_SECRET = -1;
639
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500640 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400641 * Notification category: incoming call (voice or video) or similar synchronous communication request.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500642 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400643 public static final String CATEGORY_CALL = "call";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500644
645 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400646 * Notification category: incoming direct message (SMS, instant message, etc.).
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500647 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400648 public static final String CATEGORY_MESSAGE = "msg";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500649
650 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400651 * Notification category: asynchronous bulk message (email).
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500652 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400653 public static final String CATEGORY_EMAIL = "email";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500654
655 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400656 * Notification category: calendar event.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500657 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400658 public static final String CATEGORY_EVENT = "event";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500659
660 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400661 * Notification category: promotion or advertisement.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500662 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400663 public static final String CATEGORY_PROMO = "promo";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500664
665 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400666 * Notification category: alarm or timer.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500667 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400668 public static final String CATEGORY_ALARM = "alarm";
669
670 /**
671 * Notification category: progress of a long-running background operation.
672 */
673 public static final String CATEGORY_PROGRESS = "progress";
674
675 /**
676 * Notification category: social network or sharing update.
677 */
678 public static final String CATEGORY_SOCIAL = "social";
679
680 /**
681 * Notification category: error in background operation or authentication status.
682 */
683 public static final String CATEGORY_ERROR = "err";
684
685 /**
686 * Notification category: media transport control for playback.
687 */
688 public static final String CATEGORY_TRANSPORT = "transport";
689
690 /**
691 * Notification category: system or device status update. Reserved for system use.
692 */
693 public static final String CATEGORY_SYSTEM = "sys";
694
695 /**
696 * Notification category: indication of running background service.
697 */
698 public static final String CATEGORY_SERVICE = "service";
699
700 /**
John Spurlock0a69c8c2014-03-21 13:30:57 -0400701 * Notification category: a specific, timely recommendation for a single thing.
702 * For example, a news app might want to recommend a news story it believes the user will
703 * want to read next.
704 */
705 public static final String CATEGORY_RECOMMENDATION = "recommendation";
706
707 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400708 * Notification category: ongoing information about device or contextual status.
709 */
710 public static final String CATEGORY_STATUS = "status";
711
712 /**
John Spurlock24d3dad2015-04-02 12:24:02 -0400713 * Notification category: user-scheduled reminder.
714 */
715 public static final String CATEGORY_REMINDER = "reminder";
716
717 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400718 * One of the predefined notification categories (see the <code>CATEGORY_*</code> constants)
719 * that best describes this Notification. May be used by the system for ranking and filtering.
720 */
721 public String category;
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500722
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700723 private String mGroupKey;
724
725 /**
726 * Get the key used to group this notification into a cluster or stack
727 * with other notifications on devices which support such rendering.
728 */
729 public String getGroup() {
730 return mGroupKey;
731 }
732
733 private String mSortKey;
734
735 /**
736 * Get a sort key that orders this notification among other notifications from the
737 * same package. This can be useful if an external sort was already applied and an app
738 * would like to preserve this. Notifications will be sorted lexicographically using this
739 * value, although providing different priorities in addition to providing sort key may
740 * cause this value to be ignored.
741 *
742 * <p>This sort key can also be used to order members of a notification group. See
743 * {@link Builder#setGroup}.
744 *
745 * @see String#compareTo(String)
746 */
747 public String getSortKey() {
748 return mSortKey;
749 }
750
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500751 /**
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400752 * Additional semantic data to be carried around with this Notification.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400753 * <p>
754 * The extras keys defined here are intended to capture the original inputs to {@link Builder}
755 * APIs, and are intended to be used by
756 * {@link android.service.notification.NotificationListenerService} implementations to extract
757 * detailed information from notification objects.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500758 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400759 public Bundle extras = new Bundle();
760
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400761 /**
762 * {@link #extras} key: this is the title of the notification,
763 * as supplied to {@link Builder#setContentTitle(CharSequence)}.
764 */
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -0500765 public static final String EXTRA_TITLE = "android.title";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400766
767 /**
768 * {@link #extras} key: this is the title of the notification when shown in expanded form,
769 * e.g. as supplied to {@link BigTextStyle#setBigContentTitle(CharSequence)}.
770 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400771 public static final String EXTRA_TITLE_BIG = EXTRA_TITLE + ".big";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400772
773 /**
774 * {@link #extras} key: this is the main text payload, as supplied to
775 * {@link Builder#setContentText(CharSequence)}.
776 */
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -0500777 public static final String EXTRA_TEXT = "android.text";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400778
779 /**
780 * {@link #extras} key: this is a third line of text, as supplied to
781 * {@link Builder#setSubText(CharSequence)}.
782 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400783 public static final String EXTRA_SUB_TEXT = "android.subText";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400784
785 /**
Adrian Roose458aa82015-12-08 16:17:19 -0800786 * {@link #extras} key: this is the remote input history, as supplied to
787 * {@link Builder#setRemoteInputHistory(CharSequence[])}.
Adrian Roos005e7742016-04-13 15:02:20 -0700788 *
789 * Apps can fill this through {@link Builder#setRemoteInputHistory(CharSequence[])}
790 * with the most recent inputs that have been sent through a {@link RemoteInput} of this
791 * Notification and are expected to clear it once the it is no longer relevant (e.g. for chat
792 * notifications once the other party has responded).
793 *
794 * The extra with this key is of type CharSequence[] and contains the most recent entry at
795 * the 0 index, the second most recent at the 1 index, etc.
796 *
797 * @see Builder#setRemoteInputHistory(CharSequence[])
Adrian Roose458aa82015-12-08 16:17:19 -0800798 */
799 public static final String EXTRA_REMOTE_INPUT_HISTORY = "android.remoteInputHistory";
800
801 /**
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400802 * {@link #extras} key: this is a small piece of additional text as supplied to
803 * {@link Builder#setContentInfo(CharSequence)}.
804 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400805 public static final String EXTRA_INFO_TEXT = "android.infoText";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400806
807 /**
808 * {@link #extras} key: this is a line of summary information intended to be shown
809 * alongside expanded notifications, as supplied to (e.g.)
810 * {@link BigTextStyle#setSummaryText(CharSequence)}.
811 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400812 public static final String EXTRA_SUMMARY_TEXT = "android.summaryText";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400813
814 /**
Christoph Studer4600f9b2014-07-22 22:44:43 +0200815 * {@link #extras} key: this is the longer text shown in the big form of a
816 * {@link BigTextStyle} notification, as supplied to
817 * {@link BigTextStyle#bigText(CharSequence)}.
818 */
819 public static final String EXTRA_BIG_TEXT = "android.bigText";
820
821 /**
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400822 * {@link #extras} key: this is the resource ID of the notification's main small icon, as
823 * supplied to {@link Builder#setSmallIcon(int)}.
824 */
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -0500825 public static final String EXTRA_SMALL_ICON = "android.icon";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400826
827 /**
828 * {@link #extras} key: this is a bitmap to be used instead of the small icon when showing the
829 * notification payload, as
830 * supplied to {@link Builder#setLargeIcon(android.graphics.Bitmap)}.
831 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400832 public static final String EXTRA_LARGE_ICON = "android.largeIcon";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400833
834 /**
835 * {@link #extras} key: this is a bitmap to be used instead of the one from
836 * {@link Builder#setLargeIcon(android.graphics.Bitmap)} when the notification is
837 * shown in its expanded form, as supplied to
838 * {@link BigPictureStyle#bigLargeIcon(android.graphics.Bitmap)}.
839 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400840 public static final String EXTRA_LARGE_ICON_BIG = EXTRA_LARGE_ICON + ".big";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400841
842 /**
843 * {@link #extras} key: this is the progress value supplied to
844 * {@link Builder#setProgress(int, int, boolean)}.
845 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400846 public static final String EXTRA_PROGRESS = "android.progress";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400847
848 /**
849 * {@link #extras} key: this is the maximum value supplied to
850 * {@link Builder#setProgress(int, int, boolean)}.
851 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400852 public static final String EXTRA_PROGRESS_MAX = "android.progressMax";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400853
854 /**
855 * {@link #extras} key: whether the progress bar is indeterminate, supplied to
856 * {@link Builder#setProgress(int, int, boolean)}.
857 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400858 public static final String EXTRA_PROGRESS_INDETERMINATE = "android.progressIndeterminate";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400859
860 /**
861 * {@link #extras} key: whether {@link #when} should be shown as a count-up timer (specifically
862 * a {@link android.widget.Chronometer}) instead of a timestamp, as supplied to
863 * {@link Builder#setUsesChronometer(boolean)}.
864 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400865 public static final String EXTRA_SHOW_CHRONOMETER = "android.showChronometer";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400866
867 /**
Selim Cinek81c23aa2016-02-25 16:23:13 -0800868 * {@link #extras} key: whether the chronometer set on the notification should count down
869 * instead of counting up. Is only relevant if key {@link #EXTRA_SHOW_CHRONOMETER} is present.
870 */
871 public static final String EXTRA_CHRONOMETER_COUNTS_DOWN = "android.chronometerCountsDown";
872
873 /**
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400874 * {@link #extras} key: whether {@link #when} should be shown,
875 * as supplied to {@link Builder#setShowWhen(boolean)}.
876 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400877 public static final String EXTRA_SHOW_WHEN = "android.showWhen";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400878
879 /**
880 * {@link #extras} key: this is a bitmap to be shown in {@link BigPictureStyle} expanded
881 * notifications, supplied to {@link BigPictureStyle#bigPicture(android.graphics.Bitmap)}.
882 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400883 public static final String EXTRA_PICTURE = "android.picture";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400884
885 /**
886 * {@link #extras} key: An array of CharSequences to show in {@link InboxStyle} expanded
887 * notifications, each of which was supplied to {@link InboxStyle#addLine(CharSequence)}.
888 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400889 public static final String EXTRA_TEXT_LINES = "android.textLines";
Dan Sandler842dd772014-05-15 09:36:47 -0400890
891 /**
892 * {@link #extras} key: A string representing the name of the specific
893 * {@link android.app.Notification.Style} used to create this notification.
894 */
Chris Wren91ad5632013-06-05 15:05:57 -0400895 public static final String EXTRA_TEMPLATE = "android.template";
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400896
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400897 /**
Chris Wrene6c48932014-09-29 17:19:27 -0400898 * {@link #extras} key: A String array containing the people that this notification relates to,
899 * each of which was supplied to {@link Builder#addPerson(String)}.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400900 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400901 public static final String EXTRA_PEOPLE = "android.people";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500902
903 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400904 * Allow certain system-generated notifications to appear before the device is provisioned.
905 * Only available to notifications coming from the android package.
906 * @hide
907 */
908 public static final String EXTRA_ALLOW_DURING_SETUP = "android.allowDuringSetup";
909
910 /**
Jose Limae9e3b3b2014-05-18 23:44:50 -0700911 * {@link #extras} key: A
912 * {@link android.content.ContentUris content URI} pointing to an image that can be displayed
913 * in the background when the notification is selected. The URI must point to an image stream
914 * suitable for passing into
915 * {@link android.graphics.BitmapFactory#decodeStream(java.io.InputStream)
916 * BitmapFactory.decodeStream}; all other content types will be ignored. The content provider
917 * URI used for this purpose must require no permissions to read the image data.
918 */
919 public static final String EXTRA_BACKGROUND_IMAGE_URI = "android.backgroundImageUri";
920
921 /**
Dan Sandler842dd772014-05-15 09:36:47 -0400922 * {@link #extras} key: A
Jeff Browndba34ba2014-06-24 20:46:03 -0700923 * {@link android.media.session.MediaSession.Token} associated with a
Dan Sandler842dd772014-05-15 09:36:47 -0400924 * {@link android.app.Notification.MediaStyle} notification.
925 */
926 public static final String EXTRA_MEDIA_SESSION = "android.mediaSession";
927
928 /**
Bryan Mawhinneye191f902014-07-22 12:50:09 +0100929 * {@link #extras} key: the indices of actions to be shown in the compact view,
930 * as supplied to (e.g.) {@link MediaStyle#setShowActionsInCompactView(int...)}.
931 */
932 public static final String EXTRA_COMPACT_ACTIONS = "android.compactActions";
933
Christoph Studer943aa672014-08-03 20:31:16 +0200934 /**
Alex Hillsfc737de2016-03-23 17:33:02 -0400935 * {@link #extras} key: the username to be displayed for all messages sent by the user including
936 * direct replies
937 * {@link android.app.Notification.MessagingStyle} notification.
938 */
939 public static final String EXTRA_SELF_DISPLAY_NAME = "android.selfDisplayName";
940
941 /**
942 * {@link #extras} key: a boolean describing whether the platform should automatically
943 * generate possible replies to
944 * {@link android.app.Notification.MessagingStyle.Message} objects provided by a
945 * {@link android.app.Notification.MessagingStyle} notification.
946 */
947 public static final String EXTRA_ALLOW_GENERATED_REPLIES = "android.allowGeneratedReplies";
948
949 /**
950 * {@link #extras} key: a {@link String} to be displayed as the title to a thread represented by
951 * a {@link android.app.Notification.MessagingStyle}
952 */
953 public static final String EXTRA_THREAD_TITLE = "android.threadTitle";
954
955 /**
956 * {@link #extras} key: an array of {@link android.app.Notification.MessagingStyle.Message}
957 * bundles provided by a
958 * {@link android.app.Notification.MessagingStyle} notification.
959 */
960 public static final String EXTRA_MESSAGES = "android.messages";
961
962 /**
Kenny Guy8942bcd2014-09-08 21:09:47 +0100963 * {@link #extras} key: the user that built the notification.
964 *
965 * @hide
966 */
967 public static final String EXTRA_ORIGINATING_USERID = "android.originatingUserId";
968
969 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400970 * @hide
971 */
972 public static final String EXTRA_BUILDER_APPLICATION_INFO = "android.appInfo";
973
Selim Cinek247fa012016-02-18 09:50:48 -0800974 /**
975 * @hide
976 */
977 public static final String EXTRA_CONTAINS_CUSTOM_VIEW = "android.contains.customView";
978
Dan Sandler732bd6c2016-04-12 14:20:32 -0400979 /**
980 * @SystemApi
981 * @hide
982 */
983 public static final String EXTRA_SUBSTITUTE_APP_NAME = "android.substName";
984
Dan Sandlerd63f9322015-05-06 15:18:49 -0400985 private Icon mSmallIcon;
986 private Icon mLargeIcon;
987
Chris Wren51c75102013-07-16 20:49:17 -0400988 /**
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400989 * Structure to encapsulate a named action that can be shown as part of this notification.
990 * It must include an icon, a label, and a {@link PendingIntent} to be fired when the action is
991 * selected by the user.
992 * <p>
Griff Hazen959591e2014-05-15 22:26:18 -0700993 * Apps should use {@link Notification.Builder#addAction(int, CharSequence, PendingIntent)}
994 * or {@link Notification.Builder#addAction(Notification.Action)}
995 * to attach actions.
Daniel Sandlera0a938c2012-03-15 08:42:37 -0400996 */
Daniel Sandlerea2a3172013-02-20 22:24:20 -0500997 public static class Action implements Parcelable {
Griff Hazen959591e2014-05-15 22:26:18 -0700998 private final Bundle mExtras;
Dan Sandler86647982015-05-13 23:41:13 -0400999 private Icon mIcon;
Griff Hazen61a9e862014-05-22 16:05:19 -07001000 private final RemoteInput[] mRemoteInputs;
Griff Hazen959591e2014-05-15 22:26:18 -07001001
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001002 /**
1003 * Small icon representing the action.
Dan Sandler86647982015-05-13 23:41:13 -04001004 *
1005 * @deprecated Use {@link Action#getIcon()} instead.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001006 */
Dan Sandler86647982015-05-13 23:41:13 -04001007 @Deprecated
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001008 public int icon;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001009
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001010 /**
1011 * Title of the action.
1012 */
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001013 public CharSequence title;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001014
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001015 /**
1016 * Intent to send when the user invokes this action. May be null, in which case the action
1017 * may be rendered in a disabled presentation by the system UI.
1018 */
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001019 public PendingIntent actionIntent;
Griff Hazen959591e2014-05-15 22:26:18 -07001020
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001021 private Action(Parcel in) {
Dan Sandler86647982015-05-13 23:41:13 -04001022 if (in.readInt() != 0) {
1023 mIcon = Icon.CREATOR.createFromParcel(in);
Dan Sandler68079d52015-07-22 10:45:30 -04001024 if (mIcon.getType() == Icon.TYPE_RESOURCE) {
1025 icon = mIcon.getResId();
1026 }
Dan Sandler86647982015-05-13 23:41:13 -04001027 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001028 title = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
1029 if (in.readInt() == 1) {
1030 actionIntent = PendingIntent.CREATOR.createFromParcel(in);
1031 }
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001032 mExtras = Bundle.setDefusable(in.readBundle(), true);
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001033 mRemoteInputs = in.createTypedArray(RemoteInput.CREATOR);
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001034 }
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001035
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001036 /**
Dan Sandler86647982015-05-13 23:41:13 -04001037 * @deprecated Use {@link android.app.Notification.Action.Builder}.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001038 */
Dan Sandler86647982015-05-13 23:41:13 -04001039 @Deprecated
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001040 public Action(int icon, CharSequence title, PendingIntent intent) {
Dan Sandler86647982015-05-13 23:41:13 -04001041 this(Icon.createWithResource("", icon), title, intent, new Bundle(), null);
Griff Hazen959591e2014-05-15 22:26:18 -07001042 }
1043
Dan Sandler86647982015-05-13 23:41:13 -04001044 private Action(Icon icon, CharSequence title, PendingIntent intent, Bundle extras,
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001045 RemoteInput[] remoteInputs) {
Dan Sandler86647982015-05-13 23:41:13 -04001046 this.mIcon = icon;
Gus Prevasf5bff46f2015-08-24 10:34:28 -04001047 if (icon != null && icon.getType() == Icon.TYPE_RESOURCE) {
1048 this.icon = icon.getResId();
1049 }
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001050 this.title = title;
1051 this.actionIntent = intent;
Griff Hazen959591e2014-05-15 22:26:18 -07001052 this.mExtras = extras != null ? extras : new Bundle();
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001053 this.mRemoteInputs = remoteInputs;
Griff Hazen959591e2014-05-15 22:26:18 -07001054 }
1055
1056 /**
Dan Sandler86647982015-05-13 23:41:13 -04001057 * Return an icon representing the action.
1058 */
1059 public Icon getIcon() {
1060 if (mIcon == null && icon != 0) {
1061 // you snuck an icon in here without using the builder; let's try to keep it
1062 mIcon = Icon.createWithResource("", icon);
1063 }
1064 return mIcon;
1065 }
1066
1067 /**
Griff Hazen959591e2014-05-15 22:26:18 -07001068 * Get additional metadata carried around with this Action.
1069 */
1070 public Bundle getExtras() {
1071 return mExtras;
1072 }
1073
1074 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001075 * Get the list of inputs to be collected from the user when this action is sent.
1076 * May return null if no remote inputs were added.
1077 */
1078 public RemoteInput[] getRemoteInputs() {
1079 return mRemoteInputs;
1080 }
1081
1082 /**
Griff Hazen959591e2014-05-15 22:26:18 -07001083 * Builder class for {@link Action} objects.
1084 */
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001085 public static final class Builder {
Dan Sandler86647982015-05-13 23:41:13 -04001086 private final Icon mIcon;
Griff Hazen959591e2014-05-15 22:26:18 -07001087 private final CharSequence mTitle;
1088 private final PendingIntent mIntent;
1089 private final Bundle mExtras;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001090 private ArrayList<RemoteInput> mRemoteInputs;
Griff Hazen959591e2014-05-15 22:26:18 -07001091
1092 /**
1093 * Construct a new builder for {@link Action} object.
1094 * @param icon icon to show for this action
1095 * @param title the title of the action
1096 * @param intent the {@link PendingIntent} to fire when users trigger this action
1097 */
Dan Sandler86647982015-05-13 23:41:13 -04001098 @Deprecated
Griff Hazen959591e2014-05-15 22:26:18 -07001099 public Builder(int icon, CharSequence title, PendingIntent intent) {
Dan Sandler86647982015-05-13 23:41:13 -04001100 this(Icon.createWithResource("", icon), title, intent, new Bundle(), null);
1101 }
1102
1103 /**
1104 * Construct a new builder for {@link Action} object.
1105 * @param icon icon to show for this action
1106 * @param title the title of the action
1107 * @param intent the {@link PendingIntent} to fire when users trigger this action
1108 */
1109 public Builder(Icon icon, CharSequence title, PendingIntent intent) {
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001110 this(icon, title, intent, new Bundle(), null);
Griff Hazen959591e2014-05-15 22:26:18 -07001111 }
1112
1113 /**
1114 * Construct a new builder for {@link Action} object using the fields from an
1115 * {@link Action}.
1116 * @param action the action to read fields from.
1117 */
1118 public Builder(Action action) {
Dan Sandler86647982015-05-13 23:41:13 -04001119 this(action.getIcon(), action.title, action.actionIntent, new Bundle(action.mExtras),
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001120 action.getRemoteInputs());
Griff Hazen959591e2014-05-15 22:26:18 -07001121 }
1122
Dan Sandler86647982015-05-13 23:41:13 -04001123 private Builder(Icon icon, CharSequence title, PendingIntent intent, Bundle extras,
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001124 RemoteInput[] remoteInputs) {
Griff Hazen959591e2014-05-15 22:26:18 -07001125 mIcon = icon;
1126 mTitle = title;
1127 mIntent = intent;
1128 mExtras = extras;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001129 if (remoteInputs != null) {
1130 mRemoteInputs = new ArrayList<RemoteInput>(remoteInputs.length);
1131 Collections.addAll(mRemoteInputs, remoteInputs);
1132 }
Griff Hazen959591e2014-05-15 22:26:18 -07001133 }
1134
1135 /**
1136 * Merge additional metadata into this builder.
1137 *
1138 * <p>Values within the Bundle will replace existing extras values in this Builder.
1139 *
1140 * @see Notification.Action#extras
1141 */
1142 public Builder addExtras(Bundle extras) {
1143 if (extras != null) {
1144 mExtras.putAll(extras);
1145 }
1146 return this;
1147 }
1148
1149 /**
1150 * Get the metadata Bundle used by this Builder.
1151 *
1152 * <p>The returned Bundle is shared with this Builder.
1153 */
1154 public Bundle getExtras() {
1155 return mExtras;
1156 }
1157
1158 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001159 * Add an input to be collected from the user when this action is sent.
1160 * Response values can be retrieved from the fired intent by using the
1161 * {@link RemoteInput#getResultsFromIntent} function.
1162 * @param remoteInput a {@link RemoteInput} to add to the action
1163 * @return this object for method chaining
1164 */
1165 public Builder addRemoteInput(RemoteInput remoteInput) {
1166 if (mRemoteInputs == null) {
1167 mRemoteInputs = new ArrayList<RemoteInput>();
1168 }
1169 mRemoteInputs.add(remoteInput);
1170 return this;
1171 }
1172
1173 /**
1174 * Apply an extender to this action builder. Extenders may be used to add
1175 * metadata or change options on this builder.
1176 */
Griff Hazen61a9e862014-05-22 16:05:19 -07001177 public Builder extend(Extender extender) {
1178 extender.extend(this);
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001179 return this;
1180 }
1181
1182 /**
Griff Hazen959591e2014-05-15 22:26:18 -07001183 * Combine all of the options that have been set and return a new {@link Action}
1184 * object.
1185 * @return the built action
1186 */
1187 public Action build() {
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001188 RemoteInput[] remoteInputs = mRemoteInputs != null
1189 ? mRemoteInputs.toArray(new RemoteInput[mRemoteInputs.size()]) : null;
1190 return new Action(mIcon, mTitle, mIntent, mExtras, remoteInputs);
Griff Hazen959591e2014-05-15 22:26:18 -07001191 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001192 }
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001193
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001194 @Override
1195 public Action clone() {
1196 return new Action(
Dan Sandler86647982015-05-13 23:41:13 -04001197 getIcon(),
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001198 title,
1199 actionIntent, // safe to alias
1200 new Bundle(mExtras),
1201 getRemoteInputs());
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001202 }
1203 @Override
1204 public int describeContents() {
1205 return 0;
1206 }
1207 @Override
1208 public void writeToParcel(Parcel out, int flags) {
Dan Sandler86647982015-05-13 23:41:13 -04001209 final Icon ic = getIcon();
1210 if (ic != null) {
1211 out.writeInt(1);
1212 ic.writeToParcel(out, 0);
1213 } else {
1214 out.writeInt(0);
1215 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001216 TextUtils.writeToParcel(title, out, flags);
1217 if (actionIntent != null) {
1218 out.writeInt(1);
1219 actionIntent.writeToParcel(out, flags);
1220 } else {
1221 out.writeInt(0);
1222 }
Griff Hazen959591e2014-05-15 22:26:18 -07001223 out.writeBundle(mExtras);
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001224 out.writeTypedArray(mRemoteInputs, flags);
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001225 }
Griff Hazen959591e2014-05-15 22:26:18 -07001226 public static final Parcelable.Creator<Action> CREATOR =
1227 new Parcelable.Creator<Action>() {
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001228 public Action createFromParcel(Parcel in) {
1229 return new Action(in);
1230 }
1231 public Action[] newArray(int size) {
1232 return new Action[size];
1233 }
1234 };
Griff Hazen61a9e862014-05-22 16:05:19 -07001235
1236 /**
1237 * Extender interface for use with {@link Builder#extend}. Extenders may be used to add
1238 * metadata or change options on an action builder.
1239 */
1240 public interface Extender {
1241 /**
1242 * Apply this extender to a notification action builder.
1243 * @param builder the builder to be modified.
1244 * @return the build object for chaining.
1245 */
1246 public Builder extend(Builder builder);
1247 }
1248
1249 /**
1250 * Wearable extender for notification actions. To add extensions to an action,
1251 * create a new {@link android.app.Notification.Action.WearableExtender} object using
1252 * the {@code WearableExtender()} constructor and apply it to a
1253 * {@link android.app.Notification.Action.Builder} using
1254 * {@link android.app.Notification.Action.Builder#extend}.
1255 *
1256 * <pre class="prettyprint">
1257 * Notification.Action action = new Notification.Action.Builder(
1258 * R.drawable.archive_all, "Archive all", actionIntent)
Griff Hazen14f57992014-05-26 09:07:14 -07001259 * .extend(new Notification.Action.WearableExtender()
Griff Hazen61a9e862014-05-22 16:05:19 -07001260 * .setAvailableOffline(false))
Griff Hazen14f57992014-05-26 09:07:14 -07001261 * .build();</pre>
Griff Hazen61a9e862014-05-22 16:05:19 -07001262 */
1263 public static final class WearableExtender implements Extender {
1264 /** Notification action extra which contains wearable extensions */
1265 private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS";
1266
Pete Gastaf6781d2014-10-07 15:17:05 -04001267 // Keys within EXTRA_WEARABLE_EXTENSIONS for wearable options.
Griff Hazen61a9e862014-05-22 16:05:19 -07001268 private static final String KEY_FLAGS = "flags";
Pete Gastaf6781d2014-10-07 15:17:05 -04001269 private static final String KEY_IN_PROGRESS_LABEL = "inProgressLabel";
1270 private static final String KEY_CONFIRM_LABEL = "confirmLabel";
1271 private static final String KEY_CANCEL_LABEL = "cancelLabel";
Griff Hazen61a9e862014-05-22 16:05:19 -07001272
1273 // Flags bitwise-ored to mFlags
1274 private static final int FLAG_AVAILABLE_OFFLINE = 0x1;
Alex Hills9ab3a232016-04-05 14:54:56 -04001275 private static final int FLAG_HINT_LAUNCHES_ACTIVITY = 1 << 1;
Griff Hazen61a9e862014-05-22 16:05:19 -07001276
1277 // Default value for flags integer
1278 private static final int DEFAULT_FLAGS = FLAG_AVAILABLE_OFFLINE;
1279
1280 private int mFlags = DEFAULT_FLAGS;
1281
Pete Gastaf6781d2014-10-07 15:17:05 -04001282 private CharSequence mInProgressLabel;
1283 private CharSequence mConfirmLabel;
1284 private CharSequence mCancelLabel;
1285
Griff Hazen61a9e862014-05-22 16:05:19 -07001286 /**
1287 * Create a {@link android.app.Notification.Action.WearableExtender} with default
1288 * options.
1289 */
1290 public WearableExtender() {
1291 }
1292
1293 /**
1294 * Create a {@link android.app.Notification.Action.WearableExtender} by reading
1295 * wearable options present in an existing notification action.
1296 * @param action the notification action to inspect.
1297 */
1298 public WearableExtender(Action action) {
1299 Bundle wearableBundle = action.getExtras().getBundle(EXTRA_WEARABLE_EXTENSIONS);
1300 if (wearableBundle != null) {
1301 mFlags = wearableBundle.getInt(KEY_FLAGS, DEFAULT_FLAGS);
Pete Gastaf6781d2014-10-07 15:17:05 -04001302 mInProgressLabel = wearableBundle.getCharSequence(KEY_IN_PROGRESS_LABEL);
1303 mConfirmLabel = wearableBundle.getCharSequence(KEY_CONFIRM_LABEL);
1304 mCancelLabel = wearableBundle.getCharSequence(KEY_CANCEL_LABEL);
Griff Hazen61a9e862014-05-22 16:05:19 -07001305 }
1306 }
1307
1308 /**
1309 * Apply wearable extensions to a notification action that is being built. This is
1310 * typically called by the {@link android.app.Notification.Action.Builder#extend}
1311 * method of {@link android.app.Notification.Action.Builder}.
1312 */
1313 @Override
1314 public Action.Builder extend(Action.Builder builder) {
1315 Bundle wearableBundle = new Bundle();
1316
1317 if (mFlags != DEFAULT_FLAGS) {
1318 wearableBundle.putInt(KEY_FLAGS, mFlags);
1319 }
Pete Gastaf6781d2014-10-07 15:17:05 -04001320 if (mInProgressLabel != null) {
1321 wearableBundle.putCharSequence(KEY_IN_PROGRESS_LABEL, mInProgressLabel);
1322 }
1323 if (mConfirmLabel != null) {
1324 wearableBundle.putCharSequence(KEY_CONFIRM_LABEL, mConfirmLabel);
1325 }
1326 if (mCancelLabel != null) {
1327 wearableBundle.putCharSequence(KEY_CANCEL_LABEL, mCancelLabel);
1328 }
Griff Hazen61a9e862014-05-22 16:05:19 -07001329
1330 builder.getExtras().putBundle(EXTRA_WEARABLE_EXTENSIONS, wearableBundle);
1331 return builder;
1332 }
1333
1334 @Override
1335 public WearableExtender clone() {
1336 WearableExtender that = new WearableExtender();
1337 that.mFlags = this.mFlags;
Pete Gastaf6781d2014-10-07 15:17:05 -04001338 that.mInProgressLabel = this.mInProgressLabel;
1339 that.mConfirmLabel = this.mConfirmLabel;
1340 that.mCancelLabel = this.mCancelLabel;
Griff Hazen61a9e862014-05-22 16:05:19 -07001341 return that;
1342 }
1343
1344 /**
1345 * Set whether this action is available when the wearable device is not connected to
1346 * a companion device. The user can still trigger this action when the wearable device is
1347 * offline, but a visual hint will indicate that the action may not be available.
1348 * Defaults to true.
1349 */
1350 public WearableExtender setAvailableOffline(boolean availableOffline) {
1351 setFlag(FLAG_AVAILABLE_OFFLINE, availableOffline);
1352 return this;
1353 }
1354
1355 /**
1356 * Get whether this action is available when the wearable device is not connected to
1357 * a companion device. The user can still trigger this action when the wearable device is
1358 * offline, but a visual hint will indicate that the action may not be available.
1359 * Defaults to true.
1360 */
1361 public boolean isAvailableOffline() {
1362 return (mFlags & FLAG_AVAILABLE_OFFLINE) != 0;
1363 }
1364
1365 private void setFlag(int mask, boolean value) {
1366 if (value) {
1367 mFlags |= mask;
1368 } else {
1369 mFlags &= ~mask;
1370 }
1371 }
Pete Gastaf6781d2014-10-07 15:17:05 -04001372
1373 /**
1374 * Set a label to display while the wearable is preparing to automatically execute the
1375 * action. This is usually a 'ing' verb ending in ellipsis like "Sending..."
1376 *
1377 * @param label the label to display while the action is being prepared to execute
1378 * @return this object for method chaining
1379 */
1380 public WearableExtender setInProgressLabel(CharSequence label) {
1381 mInProgressLabel = label;
1382 return this;
1383 }
1384
1385 /**
1386 * Get the label to display while the wearable is preparing to automatically execute
1387 * the action. This is usually a 'ing' verb ending in ellipsis like "Sending..."
1388 *
1389 * @return the label to display while the action is being prepared to execute
1390 */
1391 public CharSequence getInProgressLabel() {
1392 return mInProgressLabel;
1393 }
1394
1395 /**
1396 * Set a label to display to confirm that the action should be executed.
1397 * This is usually an imperative verb like "Send".
1398 *
1399 * @param label the label to confirm the action should be executed
1400 * @return this object for method chaining
1401 */
1402 public WearableExtender setConfirmLabel(CharSequence label) {
1403 mConfirmLabel = label;
1404 return this;
1405 }
1406
1407 /**
1408 * Get the label to display to confirm that the action should be executed.
1409 * This is usually an imperative verb like "Send".
1410 *
1411 * @return the label to confirm the action should be executed
1412 */
1413 public CharSequence getConfirmLabel() {
1414 return mConfirmLabel;
1415 }
1416
1417 /**
1418 * Set a label to display to cancel the action.
1419 * This is usually an imperative verb, like "Cancel".
1420 *
1421 * @param label the label to display to cancel the action
1422 * @return this object for method chaining
1423 */
1424 public WearableExtender setCancelLabel(CharSequence label) {
1425 mCancelLabel = label;
1426 return this;
1427 }
1428
1429 /**
1430 * Get the label to display to cancel the action.
1431 * This is usually an imperative verb like "Cancel".
1432 *
1433 * @return the label to display to cancel the action
1434 */
1435 public CharSequence getCancelLabel() {
1436 return mCancelLabel;
1437 }
Alex Hills9ab3a232016-04-05 14:54:56 -04001438
1439 /**
1440 * Set a hint that this Action will launch an {@link Activity} directly, telling the
1441 * platform that it can generate the appropriate transitions.
1442 * @param hintLaunchesActivity {@code true} if the content intent will launch
1443 * an activity and transitions should be generated, false otherwise.
1444 * @return this object for method chaining
1445 */
Alex Hills4ec3ff42016-04-12 11:36:18 -04001446 public WearableExtender setHintLaunchesActivity(
Alex Hills9ab3a232016-04-05 14:54:56 -04001447 boolean hintLaunchesActivity) {
1448 setFlag(FLAG_HINT_LAUNCHES_ACTIVITY, hintLaunchesActivity);
1449 return this;
1450 }
1451
1452 /**
1453 * Get a hint that this Action will launch an {@link Activity} directly, telling the
1454 * platform that it can generate the appropriate transitions
1455 * @return {@code true} if the content intent will launch an activity and transitions
1456 * should be generated, false otherwise. The default value is {@code false} if this was
1457 * never set.
1458 */
Alex Hills4ec3ff42016-04-12 11:36:18 -04001459 public boolean getHintLaunchesActivity() {
Alex Hills9ab3a232016-04-05 14:54:56 -04001460 return (mFlags & FLAG_HINT_LAUNCHES_ACTIVITY) != 0;
1461 }
Griff Hazen61a9e862014-05-22 16:05:19 -07001462 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001463 }
1464
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001465 /**
1466 * Array of all {@link Action} structures attached to this notification by
1467 * {@link Builder#addAction(int, CharSequence, PendingIntent)}. Mostly useful for instances of
1468 * {@link android.service.notification.NotificationListenerService} that provide an alternative
1469 * interface for invoking actions.
1470 */
Daniel Sandlerea2a3172013-02-20 22:24:20 -05001471 public Action[] actions;
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001472
1473 /**
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001474 * Replacement version of this notification whose content will be shown
1475 * in an insecure context such as atop a secure keyguard. See {@link #visibility}
1476 * and {@link #VISIBILITY_PUBLIC}.
1477 */
1478 public Notification publicVersion;
1479
1480 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001481 * Constructs a Notification object with default values.
Joe Onorato46439ce2010-11-19 13:56:21 -08001482 * You might want to consider using {@link Builder} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001483 */
1484 public Notification()
1485 {
1486 this.when = System.currentTimeMillis();
Selim Cinekb85f36fd2016-04-20 18:46:36 -07001487 this.creationTime = System.currentTimeMillis();
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001488 this.priority = PRIORITY_DEFAULT;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001489 }
1490
1491 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001492 * @hide
1493 */
1494 public Notification(Context context, int icon, CharSequence tickerText, long when,
1495 CharSequence contentTitle, CharSequence contentText, Intent contentIntent)
1496 {
Chris Wren1ce4b6d2015-06-11 10:19:43 -04001497 new Builder(context)
1498 .setWhen(when)
1499 .setSmallIcon(icon)
1500 .setTicker(tickerText)
1501 .setContentTitle(contentTitle)
1502 .setContentText(contentText)
1503 .setContentIntent(PendingIntent.getActivity(context, 0, contentIntent, 0))
1504 .buildInto(this);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001505 }
1506
1507 /**
1508 * Constructs a Notification object with the information needed to
1509 * have a status bar icon without the standard expanded view.
1510 *
1511 * @param icon The resource id of the icon to put in the status bar.
1512 * @param tickerText The text that flows by in the status bar when the notification first
1513 * activates.
1514 * @param when The time to show in the time field. In the System.currentTimeMillis
1515 * timebase.
Joe Onorato46439ce2010-11-19 13:56:21 -08001516 *
1517 * @deprecated Use {@link Builder} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001518 */
Joe Onorato46439ce2010-11-19 13:56:21 -08001519 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001520 public Notification(int icon, CharSequence tickerText, long when)
1521 {
1522 this.icon = icon;
1523 this.tickerText = tickerText;
1524 this.when = when;
Selim Cinekb85f36fd2016-04-20 18:46:36 -07001525 this.creationTime = System.currentTimeMillis();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001526 }
1527
1528 /**
1529 * Unflatten the notification from a parcel.
1530 */
1531 public Notification(Parcel parcel)
1532 {
1533 int version = parcel.readInt();
1534
1535 when = parcel.readLong();
Selim Cinekb85f36fd2016-04-20 18:46:36 -07001536 creationTime = parcel.readLong();
Dan Sandler3936e7a2015-05-19 20:59:12 -04001537 if (parcel.readInt() != 0) {
1538 mSmallIcon = Icon.CREATOR.createFromParcel(parcel);
Dan Sandler4e787062015-06-17 15:09:48 -04001539 if (mSmallIcon.getType() == Icon.TYPE_RESOURCE) {
1540 icon = mSmallIcon.getResId();
1541 }
Dan Sandler3936e7a2015-05-19 20:59:12 -04001542 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001543 number = parcel.readInt();
1544 if (parcel.readInt() != 0) {
1545 contentIntent = PendingIntent.CREATOR.createFromParcel(parcel);
1546 }
1547 if (parcel.readInt() != 0) {
1548 deleteIntent = PendingIntent.CREATOR.createFromParcel(parcel);
1549 }
1550 if (parcel.readInt() != 0) {
1551 tickerText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
1552 }
1553 if (parcel.readInt() != 0) {
Joe Onorato46439ce2010-11-19 13:56:21 -08001554 tickerView = RemoteViews.CREATOR.createFromParcel(parcel);
Joe Onoratoef1e7762010-09-17 18:38:38 -04001555 }
1556 if (parcel.readInt() != 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001557 contentView = RemoteViews.CREATOR.createFromParcel(parcel);
1558 }
Joe Onorato561d3852010-11-20 18:09:34 -08001559 if (parcel.readInt() != 0) {
Dan Sandlerd63f9322015-05-06 15:18:49 -04001560 mLargeIcon = Icon.CREATOR.createFromParcel(parcel);
Joe Onorato561d3852010-11-20 18:09:34 -08001561 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001562 defaults = parcel.readInt();
1563 flags = parcel.readInt();
1564 if (parcel.readInt() != 0) {
1565 sound = Uri.CREATOR.createFromParcel(parcel);
1566 }
1567
1568 audioStreamType = parcel.readInt();
John Spurlockc0650f022014-07-19 13:22:39 -04001569 if (parcel.readInt() != 0) {
1570 audioAttributes = AudioAttributes.CREATOR.createFromParcel(parcel);
1571 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001572 vibrate = parcel.createLongArray();
1573 ledARGB = parcel.readInt();
1574 ledOnMS = parcel.readInt();
1575 ledOffMS = parcel.readInt();
1576 iconLevel = parcel.readInt();
Daniel Sandlere46cbd32010-06-17 10:35:26 -04001577
1578 if (parcel.readInt() != 0) {
1579 fullScreenIntent = PendingIntent.CREATOR.createFromParcel(parcel);
1580 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001581
1582 priority = parcel.readInt();
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001583
John Spurlockfd7f1e02014-03-18 16:41:57 -04001584 category = parcel.readString();
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001585
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001586 mGroupKey = parcel.readString();
1587
1588 mSortKey = parcel.readString();
1589
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001590 extras = Bundle.setDefusable(parcel.readBundle(), true); // may be null
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001591
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001592 actions = parcel.createTypedArray(Action.CREATOR); // may be null
1593
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001594 if (parcel.readInt() != 0) {
Daniel Sandlerf3b73432012-03-27 15:01:25 -04001595 bigContentView = RemoteViews.CREATOR.createFromParcel(parcel);
1596 }
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001597
Chris Wren8fd39ec2014-02-27 17:43:26 -05001598 if (parcel.readInt() != 0) {
1599 headsUpContentView = RemoteViews.CREATOR.createFromParcel(parcel);
1600 }
1601
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001602 visibility = parcel.readInt();
1603
1604 if (parcel.readInt() != 0) {
1605 publicVersion = Notification.CREATOR.createFromParcel(parcel);
1606 }
Dan Sandler26e81cf2014-05-06 10:01:27 -04001607
1608 color = parcel.readInt();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001609 }
1610
Andy Stadler110988c2010-12-03 14:29:16 -08001611 @Override
Joe Onorato18e69df2010-05-17 22:26:12 -07001612 public Notification clone() {
1613 Notification that = new Notification();
Daniel Sandler1a497d32013-04-18 14:52:45 -04001614 cloneInto(that, true);
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001615 return that;
1616 }
Joe Onorato18e69df2010-05-17 22:26:12 -07001617
Daniel Sandler1a497d32013-04-18 14:52:45 -04001618 /**
1619 * Copy all (or if heavy is false, all except Bitmaps and RemoteViews) members
1620 * of this into that.
1621 * @hide
1622 */
1623 public void cloneInto(Notification that, boolean heavy) {
Joe Onorato18e69df2010-05-17 22:26:12 -07001624 that.when = this.when;
Selim Cinekb85f36fd2016-04-20 18:46:36 -07001625 that.creationTime = this.creationTime;
Dan Sandlerd63f9322015-05-06 15:18:49 -04001626 that.mSmallIcon = this.mSmallIcon;
Joe Onorato18e69df2010-05-17 22:26:12 -07001627 that.number = this.number;
1628
1629 // PendingIntents are global, so there's no reason (or way) to clone them.
1630 that.contentIntent = this.contentIntent;
1631 that.deleteIntent = this.deleteIntent;
Daniel Sandlere46cbd32010-06-17 10:35:26 -04001632 that.fullScreenIntent = this.fullScreenIntent;
Joe Onorato18e69df2010-05-17 22:26:12 -07001633
1634 if (this.tickerText != null) {
1635 that.tickerText = this.tickerText.toString();
1636 }
Daniel Sandler1a497d32013-04-18 14:52:45 -04001637 if (heavy && this.tickerView != null) {
Joe Onorato46439ce2010-11-19 13:56:21 -08001638 that.tickerView = this.tickerView.clone();
Joe Onoratoef1e7762010-09-17 18:38:38 -04001639 }
Daniel Sandler1a497d32013-04-18 14:52:45 -04001640 if (heavy && this.contentView != null) {
Joe Onorato18e69df2010-05-17 22:26:12 -07001641 that.contentView = this.contentView.clone();
1642 }
Dan Sandlerd63f9322015-05-06 15:18:49 -04001643 if (heavy && this.mLargeIcon != null) {
1644 that.mLargeIcon = this.mLargeIcon;
Joe Onorato561d3852010-11-20 18:09:34 -08001645 }
Jozef BABJAKa8b91832011-02-22 08:05:08 +01001646 that.iconLevel = this.iconLevel;
Joe Onorato18e69df2010-05-17 22:26:12 -07001647 that.sound = this.sound; // android.net.Uri is immutable
1648 that.audioStreamType = this.audioStreamType;
John Spurlockc0650f022014-07-19 13:22:39 -04001649 if (this.audioAttributes != null) {
1650 that.audioAttributes = new AudioAttributes.Builder(this.audioAttributes).build();
1651 }
Joe Onorato18e69df2010-05-17 22:26:12 -07001652
1653 final long[] vibrate = this.vibrate;
1654 if (vibrate != null) {
1655 final int N = vibrate.length;
1656 final long[] vib = that.vibrate = new long[N];
1657 System.arraycopy(vibrate, 0, vib, 0, N);
1658 }
1659
1660 that.ledARGB = this.ledARGB;
1661 that.ledOnMS = this.ledOnMS;
1662 that.ledOffMS = this.ledOffMS;
1663 that.defaults = this.defaults;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001664
Joe Onorato18e69df2010-05-17 22:26:12 -07001665 that.flags = this.flags;
1666
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001667 that.priority = this.priority;
Joe Malin8d40d042012-11-05 11:36:40 -08001668
John Spurlockfd7f1e02014-03-18 16:41:57 -04001669 that.category = this.category;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001670
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001671 that.mGroupKey = this.mGroupKey;
1672
1673 that.mSortKey = this.mSortKey;
1674
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001675 if (this.extras != null) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04001676 try {
1677 that.extras = new Bundle(this.extras);
1678 // will unparcel
1679 that.extras.size();
1680 } catch (BadParcelableException e) {
1681 Log.e(TAG, "could not unparcel extras from notification: " + this, e);
1682 that.extras = null;
1683 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001684 }
1685
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001686 if (this.actions != null) {
1687 that.actions = new Action[this.actions.length];
1688 for(int i=0; i<this.actions.length; i++) {
1689 that.actions[i] = this.actions[i].clone();
1690 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001691 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001692
Daniel Sandler1a497d32013-04-18 14:52:45 -04001693 if (heavy && this.bigContentView != null) {
Daniel Sandlerf3b73432012-03-27 15:01:25 -04001694 that.bigContentView = this.bigContentView.clone();
1695 }
Daniel Sandler1a497d32013-04-18 14:52:45 -04001696
Chris Wren8fd39ec2014-02-27 17:43:26 -05001697 if (heavy && this.headsUpContentView != null) {
1698 that.headsUpContentView = this.headsUpContentView.clone();
1699 }
1700
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001701 that.visibility = this.visibility;
1702
1703 if (this.publicVersion != null) {
1704 that.publicVersion = new Notification();
1705 this.publicVersion.cloneInto(that.publicVersion, heavy);
1706 }
1707
Dan Sandler26e81cf2014-05-06 10:01:27 -04001708 that.color = this.color;
1709
Daniel Sandler1a497d32013-04-18 14:52:45 -04001710 if (!heavy) {
1711 that.lightenPayload(); // will clean out extras
1712 }
1713 }
1714
1715 /**
1716 * Removes heavyweight parts of the Notification object for archival or for sending to
1717 * listeners when the full contents are not necessary.
1718 * @hide
1719 */
1720 public final void lightenPayload() {
1721 tickerView = null;
1722 contentView = null;
1723 bigContentView = null;
Chris Wren8fd39ec2014-02-27 17:43:26 -05001724 headsUpContentView = null;
Dan Sandlerd63f9322015-05-06 15:18:49 -04001725 mLargeIcon = null;
Dan Sandler50128532015-12-08 15:42:41 -05001726 if (extras != null && !extras.isEmpty()) {
1727 final Set<String> keyset = extras.keySet();
1728 final int N = keyset.size();
1729 final String[] keys = keyset.toArray(new String[N]);
1730 for (int i=0; i<N; i++) {
1731 final String key = keys[i];
1732 final Object obj = extras.get(key);
1733 if (obj != null &&
1734 ( obj instanceof Parcelable
1735 || obj instanceof Parcelable[]
1736 || obj instanceof SparseArray
1737 || obj instanceof ArrayList)) {
1738 extras.remove(key);
1739 }
1740 }
Daniel Sandler1a497d32013-04-18 14:52:45 -04001741 }
Joe Onorato18e69df2010-05-17 22:26:12 -07001742 }
1743
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04001744 /**
1745 * Make sure this CharSequence is safe to put into a bundle, which basically
1746 * means it had better not be some custom Parcelable implementation.
1747 * @hide
1748 */
1749 public static CharSequence safeCharSequence(CharSequence cs) {
Christoph Studer535ec612014-09-03 15:47:47 +02001750 if (cs == null) return cs;
1751 if (cs.length() > MAX_CHARSEQUENCE_LENGTH) {
1752 cs = cs.subSequence(0, MAX_CHARSEQUENCE_LENGTH);
1753 }
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04001754 if (cs instanceof Parcelable) {
1755 Log.e(TAG, "warning: " + cs.getClass().getCanonicalName()
1756 + " instance is a custom Parcelable and not allowed in Notification");
1757 return cs.toString();
1758 }
Selim Cinek60a54252016-02-26 17:03:25 -08001759 return removeTextSizeSpans(cs);
1760 }
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04001761
Selim Cinek60a54252016-02-26 17:03:25 -08001762 private static CharSequence removeTextSizeSpans(CharSequence charSequence) {
1763 if (charSequence instanceof Spanned) {
1764 Spanned ss = (Spanned) charSequence;
1765 Object[] spans = ss.getSpans(0, ss.length(), Object.class);
1766 SpannableStringBuilder builder = new SpannableStringBuilder(ss.toString());
1767 for (Object span : spans) {
1768 Object resultSpan = span;
Selim Cinek89991a22016-03-07 19:51:54 -08001769 if (resultSpan instanceof CharacterStyle) {
1770 resultSpan = ((CharacterStyle) span).getUnderlying();
1771 }
1772 if (resultSpan instanceof TextAppearanceSpan) {
1773 TextAppearanceSpan originalSpan = (TextAppearanceSpan) resultSpan;
Selim Cinek60a54252016-02-26 17:03:25 -08001774 resultSpan = new TextAppearanceSpan(
1775 originalSpan.getFamily(),
1776 originalSpan.getTextStyle(),
1777 -1,
1778 originalSpan.getTextColor(),
1779 originalSpan.getLinkTextColor());
Selim Cinek89991a22016-03-07 19:51:54 -08001780 } else if (resultSpan instanceof RelativeSizeSpan
1781 || resultSpan instanceof AbsoluteSizeSpan) {
Selim Cinek60a54252016-02-26 17:03:25 -08001782 continue;
Selim Cinek89991a22016-03-07 19:51:54 -08001783 } else {
1784 resultSpan = span;
Selim Cinek60a54252016-02-26 17:03:25 -08001785 }
1786 builder.setSpan(resultSpan, ss.getSpanStart(span), ss.getSpanEnd(span),
1787 ss.getSpanFlags(span));
1788 }
1789 return builder;
1790 }
1791 return charSequence;
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04001792 }
1793
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001794 public int describeContents() {
1795 return 0;
1796 }
1797
1798 /**
Dan Sandler4e787062015-06-17 15:09:48 -04001799 * Flatten this notification into a parcel.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001800 */
1801 public void writeToParcel(Parcel parcel, int flags)
1802 {
1803 parcel.writeInt(1);
1804
1805 parcel.writeLong(when);
Selim Cinekb85f36fd2016-04-20 18:46:36 -07001806 parcel.writeLong(creationTime);
Dan Sandler4e787062015-06-17 15:09:48 -04001807 if (mSmallIcon == null && icon != 0) {
1808 // you snuck an icon in here without using the builder; let's try to keep it
1809 mSmallIcon = Icon.createWithResource("", icon);
1810 }
Dan Sandler3936e7a2015-05-19 20:59:12 -04001811 if (mSmallIcon != null) {
1812 parcel.writeInt(1);
1813 mSmallIcon.writeToParcel(parcel, 0);
1814 } else {
1815 parcel.writeInt(0);
1816 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001817 parcel.writeInt(number);
1818 if (contentIntent != null) {
1819 parcel.writeInt(1);
1820 contentIntent.writeToParcel(parcel, 0);
1821 } else {
1822 parcel.writeInt(0);
1823 }
1824 if (deleteIntent != null) {
1825 parcel.writeInt(1);
1826 deleteIntent.writeToParcel(parcel, 0);
1827 } else {
1828 parcel.writeInt(0);
1829 }
1830 if (tickerText != null) {
1831 parcel.writeInt(1);
1832 TextUtils.writeToParcel(tickerText, parcel, flags);
1833 } else {
1834 parcel.writeInt(0);
1835 }
Joe Onorato46439ce2010-11-19 13:56:21 -08001836 if (tickerView != null) {
Joe Onoratoef1e7762010-09-17 18:38:38 -04001837 parcel.writeInt(1);
Joe Onorato46439ce2010-11-19 13:56:21 -08001838 tickerView.writeToParcel(parcel, 0);
Joe Onoratoef1e7762010-09-17 18:38:38 -04001839 } else {
1840 parcel.writeInt(0);
1841 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001842 if (contentView != null) {
1843 parcel.writeInt(1);
1844 contentView.writeToParcel(parcel, 0);
1845 } else {
1846 parcel.writeInt(0);
1847 }
Dan Sandlerd63f9322015-05-06 15:18:49 -04001848 if (mLargeIcon != null) {
Joe Onorato561d3852010-11-20 18:09:34 -08001849 parcel.writeInt(1);
Dan Sandlerd63f9322015-05-06 15:18:49 -04001850 mLargeIcon.writeToParcel(parcel, 0);
Joe Onorato561d3852010-11-20 18:09:34 -08001851 } else {
1852 parcel.writeInt(0);
1853 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001854
1855 parcel.writeInt(defaults);
1856 parcel.writeInt(this.flags);
1857
1858 if (sound != null) {
1859 parcel.writeInt(1);
1860 sound.writeToParcel(parcel, 0);
1861 } else {
1862 parcel.writeInt(0);
1863 }
1864 parcel.writeInt(audioStreamType);
John Spurlockc0650f022014-07-19 13:22:39 -04001865
1866 if (audioAttributes != null) {
1867 parcel.writeInt(1);
1868 audioAttributes.writeToParcel(parcel, 0);
1869 } else {
1870 parcel.writeInt(0);
1871 }
1872
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001873 parcel.writeLongArray(vibrate);
1874 parcel.writeInt(ledARGB);
1875 parcel.writeInt(ledOnMS);
1876 parcel.writeInt(ledOffMS);
1877 parcel.writeInt(iconLevel);
Daniel Sandlere46cbd32010-06-17 10:35:26 -04001878
1879 if (fullScreenIntent != null) {
1880 parcel.writeInt(1);
1881 fullScreenIntent.writeToParcel(parcel, 0);
1882 } else {
1883 parcel.writeInt(0);
1884 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001885
1886 parcel.writeInt(priority);
Joe Malin8d40d042012-11-05 11:36:40 -08001887
John Spurlockfd7f1e02014-03-18 16:41:57 -04001888 parcel.writeString(category);
Joe Malin8d40d042012-11-05 11:36:40 -08001889
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001890 parcel.writeString(mGroupKey);
1891
1892 parcel.writeString(mSortKey);
1893
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001894 parcel.writeBundle(extras); // null ok
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001895
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001896 parcel.writeTypedArray(actions, 0); // null ok
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001897
Daniel Sandlerf3b73432012-03-27 15:01:25 -04001898 if (bigContentView != null) {
1899 parcel.writeInt(1);
1900 bigContentView.writeToParcel(parcel, 0);
1901 } else {
1902 parcel.writeInt(0);
1903 }
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001904
Chris Wren8fd39ec2014-02-27 17:43:26 -05001905 if (headsUpContentView != null) {
1906 parcel.writeInt(1);
1907 headsUpContentView.writeToParcel(parcel, 0);
1908 } else {
1909 parcel.writeInt(0);
1910 }
1911
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001912 parcel.writeInt(visibility);
1913
1914 if (publicVersion != null) {
1915 parcel.writeInt(1);
1916 publicVersion.writeToParcel(parcel, 0);
1917 } else {
1918 parcel.writeInt(0);
1919 }
Dan Sandler26e81cf2014-05-06 10:01:27 -04001920
1921 parcel.writeInt(color);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001922 }
1923
1924 /**
1925 * Parcelable.Creator that instantiates Notification objects
1926 */
1927 public static final Parcelable.Creator<Notification> CREATOR
1928 = new Parcelable.Creator<Notification>()
1929 {
1930 public Notification createFromParcel(Parcel parcel)
1931 {
1932 return new Notification(parcel);
1933 }
1934
1935 public Notification[] newArray(int size)
1936 {
1937 return new Notification[size];
1938 }
1939 };
1940
1941 /**
1942 * Sets the {@link #contentView} field to be a view with the standard "Latest Event"
1943 * layout.
1944 *
1945 * <p>Uses the {@link #icon} and {@link #when} fields to set the icon and time fields
1946 * in the view.</p>
1947 * @param context The context for your application / activity.
1948 * @param contentTitle The title that goes in the expanded entry.
1949 * @param contentText The text that goes in the expanded entry.
1950 * @param contentIntent The intent to launch when the user clicks the expanded notification.
1951 * If this is an activity, it must include the
1952 * {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} flag, which requires
Scott Main7aee61f2011-02-08 11:25:01 -08001953 * that you take care of task management as described in the
1954 * <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back
1955 * Stack</a> document.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001956 *
Joe Onorato46439ce2010-11-19 13:56:21 -08001957 * @deprecated Use {@link Builder} instead.
Chris Wrena05db382015-06-24 15:18:34 -04001958 * @removed
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001959 */
Joe Onorato46439ce2010-11-19 13:56:21 -08001960 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001961 public void setLatestEventInfo(Context context,
1962 CharSequence contentTitle, CharSequence contentText, PendingIntent contentIntent) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04001963 if (context.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1){
1964 Log.e(TAG, "setLatestEventInfo() is deprecated and you should feel deprecated.",
1965 new Throwable());
1966 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001967
Julia Reynoldsd9228f12015-10-20 10:37:27 -04001968 // ensure that any information already set directly is preserved
1969 final Notification.Builder builder = new Notification.Builder(context, this);
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001970
1971 // now apply the latestEventInfo fields
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001972 if (contentTitle != null) {
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001973 builder.setContentTitle(contentTitle);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001974 }
1975 if (contentText != null) {
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001976 builder.setContentText(contentText);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001977 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001978 builder.setContentIntent(contentIntent);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04001979
1980 builder.build(); // callers expect this notification to be ready to use
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001981 }
1982
Julia Reynoldsda303542015-11-23 14:00:20 -05001983 /**
1984 * @hide
1985 */
1986 public static void addFieldsFromContext(Context context, Notification notification) {
Jeff Sharkey012bc7b2016-04-11 16:30:27 -06001987 addFieldsFromContext(context.getApplicationInfo(), context.getUserId(), notification);
1988 }
1989
1990 /**
1991 * @hide
1992 */
1993 public static void addFieldsFromContext(ApplicationInfo ai, int userId,
1994 Notification notification) {
1995 notification.extras.putParcelable(EXTRA_BUILDER_APPLICATION_INFO, ai);
1996 notification.extras.putInt(EXTRA_ORIGINATING_USERID, userId);
Julia Reynoldsda303542015-11-23 14:00:20 -05001997 }
1998
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001999 @Override
2000 public String toString() {
2001 StringBuilder sb = new StringBuilder();
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002002 sb.append("Notification(pri=");
2003 sb.append(priority);
2004 sb.append(" contentView=");
Joe Onoratoc9596d62011-01-12 17:03:11 -08002005 if (contentView != null) {
2006 sb.append(contentView.getPackage());
2007 sb.append("/0x");
2008 sb.append(Integer.toHexString(contentView.getLayoutId()));
2009 } else {
2010 sb.append("null");
2011 }
2012 sb.append(" vibrate=");
Daniel Sandler6738eee2012-11-16 12:03:32 -05002013 if ((this.defaults & DEFAULT_VIBRATE) != 0) {
2014 sb.append("default");
2015 } else if (this.vibrate != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002016 int N = this.vibrate.length-1;
2017 sb.append("[");
2018 for (int i=0; i<N; i++) {
2019 sb.append(this.vibrate[i]);
2020 sb.append(',');
2021 }
Simon Schoar8cf97d92009-06-10 22:08:37 +02002022 if (N != -1) {
2023 sb.append(this.vibrate[N]);
2024 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002025 sb.append("]");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002026 } else {
2027 sb.append("null");
2028 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002029 sb.append(" sound=");
Daniel Sandler6738eee2012-11-16 12:03:32 -05002030 if ((this.defaults & DEFAULT_SOUND) != 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002031 sb.append("default");
Daniel Sandler6738eee2012-11-16 12:03:32 -05002032 } else if (this.sound != null) {
2033 sb.append(this.sound.toString());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002034 } else {
2035 sb.append("null");
2036 }
Chris Wren365b6d32015-07-16 10:39:26 -04002037 if (this.tickerText != null) {
2038 sb.append(" tick");
2039 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002040 sb.append(" defaults=0x");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002041 sb.append(Integer.toHexString(this.defaults));
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002042 sb.append(" flags=0x");
Daniel Sandlere46cbd32010-06-17 10:35:26 -04002043 sb.append(Integer.toHexString(this.flags));
Dan Sandler26e81cf2014-05-06 10:01:27 -04002044 sb.append(String.format(" color=0x%08x", this.color));
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002045 if (this.category != null) {
2046 sb.append(" category=");
2047 sb.append(this.category);
2048 }
2049 if (this.mGroupKey != null) {
2050 sb.append(" groupKey=");
2051 sb.append(this.mGroupKey);
2052 }
2053 if (this.mSortKey != null) {
2054 sb.append(" sortKey=");
2055 sb.append(this.mSortKey);
2056 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002057 if (actions != null) {
Dan Sandler1b718782014-07-18 12:43:45 -04002058 sb.append(" actions=");
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002059 sb.append(actions.length);
Dan Sandler1b718782014-07-18 12:43:45 -04002060 }
2061 sb.append(" vis=");
2062 sb.append(visibilityToString(this.visibility));
2063 if (this.publicVersion != null) {
2064 sb.append(" publicVersion=");
2065 sb.append(publicVersion.toString());
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002066 }
2067 sb.append(")");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002068 return sb.toString();
2069 }
Joe Onorato46439ce2010-11-19 13:56:21 -08002070
Dan Sandler1b718782014-07-18 12:43:45 -04002071 /**
2072 * {@hide}
2073 */
2074 public static String visibilityToString(int vis) {
2075 switch (vis) {
2076 case VISIBILITY_PRIVATE:
2077 return "PRIVATE";
2078 case VISIBILITY_PUBLIC:
2079 return "PUBLIC";
2080 case VISIBILITY_SECRET:
2081 return "SECRET";
2082 default:
2083 return "UNKNOWN(" + String.valueOf(vis) + ")";
2084 }
2085 }
2086
Joe Onoratocb109a02011-01-18 17:57:41 -08002087 /**
John Spurlock1d881a12015-03-18 19:21:54 -04002088 * {@hide}
2089 */
2090 public static String priorityToString(@Priority int pri) {
2091 switch (pri) {
2092 case PRIORITY_MIN:
2093 return "MIN";
2094 case PRIORITY_LOW:
2095 return "LOW";
2096 case PRIORITY_DEFAULT:
2097 return "DEFAULT";
2098 case PRIORITY_HIGH:
2099 return "HIGH";
2100 case PRIORITY_MAX:
2101 return "MAX";
2102 default:
2103 return "UNKNOWN(" + String.valueOf(pri) + ")";
2104 }
2105 }
2106
2107 /**
Dan Sandlerd63f9322015-05-06 15:18:49 -04002108 * The small icon representing this notification in the status bar and content view.
2109 *
2110 * @return the small icon representing this notification.
2111 *
2112 * @see Builder#getSmallIcon()
2113 * @see Builder#setSmallIcon(Icon)
2114 */
2115 public Icon getSmallIcon() {
2116 return mSmallIcon;
2117 }
2118
2119 /**
2120 * Used when notifying to clean up legacy small icons.
2121 * @hide
2122 */
2123 public void setSmallIcon(Icon icon) {
2124 mSmallIcon = icon;
2125 }
2126
2127 /**
2128 * The large icon shown in this notification's content view.
2129 * @see Builder#getLargeIcon()
2130 * @see Builder#setLargeIcon(Icon)
2131 */
2132 public Icon getLargeIcon() {
2133 return mLargeIcon;
2134 }
2135
2136 /**
Christoph Studer4600f9b2014-07-22 22:44:43 +02002137 * @hide
2138 */
Christoph Studerc8db24b2014-07-25 17:50:30 +02002139 public boolean isGroupSummary() {
2140 return mGroupKey != null && (flags & FLAG_GROUP_SUMMARY) != 0;
2141 }
2142
2143 /**
2144 * @hide
2145 */
2146 public boolean isGroupChild() {
2147 return mGroupKey != null && (flags & FLAG_GROUP_SUMMARY) == 0;
2148 }
2149
2150 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002151 * Builder class for {@link Notification} objects.
Joe Malin8d40d042012-11-05 11:36:40 -08002152 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002153 * Provides a convenient way to set the various fields of a {@link Notification} and generate
Scott Main183bf112012-08-13 19:12:13 -07002154 * content views using the platform's notification layout template. If your app supports
2155 * versions of Android as old as API level 4, you can instead use
2156 * {@link android.support.v4.app.NotificationCompat.Builder NotificationCompat.Builder},
2157 * available in the <a href="{@docRoot}tools/extras/support-library.html">Android Support
2158 * library</a>.
Joe Malin8d40d042012-11-05 11:36:40 -08002159 *
Scott Main183bf112012-08-13 19:12:13 -07002160 * <p>Example:
Joe Malin8d40d042012-11-05 11:36:40 -08002161 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002162 * <pre class="prettyprint">
Scott Main183bf112012-08-13 19:12:13 -07002163 * Notification noti = new Notification.Builder(mContext)
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002164 * .setContentTitle(&quot;New mail from &quot; + sender.toString())
2165 * .setContentText(subject)
2166 * .setSmallIcon(R.drawable.new_mail)
2167 * .setLargeIcon(aBitmap)
Chris Wrenfbd96ba2012-05-01 12:03:58 -04002168 * .build();
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002169 * </pre>
Joe Onoratocb109a02011-01-18 17:57:41 -08002170 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002171 public static class Builder {
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05002172 /**
2173 * @hide
2174 */
2175 public static final String EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT =
2176 "android.rebuild.contentViewActionCount";
2177 /**
2178 * @hide
2179 */
2180 public static final String EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT
2181 = "android.rebuild.bigViewActionCount";
2182 /**
2183 * @hide
2184 */
2185 public static final String EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT
2186 = "android.rebuild.hudViewActionCount";
2187
Daniel Sandler602ad1c2012-06-12 16:06:27 -04002188 private static final int MAX_ACTION_BUTTONS = 3;
Daniel Sandler8680bf82012-05-15 16:52:52 -04002189
Joe Onorato46439ce2010-11-19 13:56:21 -08002190 private Context mContext;
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002191 private Notification mN;
2192 private Bundle mUserExtras = new Bundle();
Chris Wrenfbd96ba2012-05-01 12:03:58 -04002193 private Style mStyle;
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002194 private ArrayList<Action> mActions = new ArrayList<Action>(MAX_ACTION_BUTTONS);
2195 private ArrayList<String> mPersonList = new ArrayList<String>();
2196 private NotificationColorUtil mColorUtil;
2197 private boolean mColorUtilInited = false;
Christoph Studer7ac80e62014-08-04 16:01:57 +02002198
2199 /**
Adrian Roos4ff3b122016-02-01 12:26:13 -08002200 * Caches a contrast-enhanced version of {@link #mCachedContrastColorIsFor}.
2201 */
2202 private int mCachedContrastColor = COLOR_INVALID;
2203 private int mCachedContrastColorIsFor = COLOR_INVALID;
2204
2205 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002206 * Constructs a new Builder with the defaults:
Joe Onoratocb109a02011-01-18 17:57:41 -08002207 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002208
2209 * <table>
2210 * <tr><th align=right>priority</th>
2211 * <td>{@link #PRIORITY_DEFAULT}</td></tr>
2212 * <tr><th align=right>when</th>
2213 * <td>now ({@link System#currentTimeMillis()})</td></tr>
2214 * <tr><th align=right>audio stream</th>
2215 * <td>{@link #STREAM_DEFAULT}</td></tr>
2216 * </table>
Joe Onoratocb109a02011-01-18 17:57:41 -08002217 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002218
2219 * @param context
2220 * A {@link Context} that will be used by the Builder to construct the
2221 * RemoteViews. The Context will not be held past the lifetime of this Builder
2222 * object.
Joe Onoratocb109a02011-01-18 17:57:41 -08002223 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002224 public Builder(Context context) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002225 this(context, null);
Joe Onorato46439ce2010-11-19 13:56:21 -08002226 }
2227
Joe Onoratocb109a02011-01-18 17:57:41 -08002228 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002229 * @hide
Christoph Studer4600f9b2014-07-22 22:44:43 +02002230 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002231 public Builder(Context context, Notification toAdopt) {
2232 mContext = context;
Christoph Studer4600f9b2014-07-22 22:44:43 +02002233
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002234 if (toAdopt == null) {
2235 mN = new Notification();
Selim Cinek0ff1ce602016-04-05 18:27:16 -07002236 if (context.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N) {
2237 mN.extras.putBoolean(EXTRA_SHOW_WHEN, true);
2238 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002239 mN.priority = PRIORITY_DEFAULT;
2240 mN.visibility = VISIBILITY_PRIVATE;
2241 } else {
2242 mN = toAdopt;
2243 if (mN.actions != null) {
2244 Collections.addAll(mActions, mN.actions);
Christoph Studer4600f9b2014-07-22 22:44:43 +02002245 }
2246
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002247 if (mN.extras.containsKey(EXTRA_PEOPLE)) {
2248 Collections.addAll(mPersonList, mN.extras.getStringArray(EXTRA_PEOPLE));
2249 }
2250
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002251 String templateClass = mN.extras.getString(EXTRA_TEMPLATE);
2252 if (!TextUtils.isEmpty(templateClass)) {
2253 final Class<? extends Style> styleClass
2254 = getNotificationStyleClass(templateClass);
2255 if (styleClass == null) {
2256 Log.d(TAG, "Unknown style class: " + templateClass);
2257 } else {
2258 try {
Adrian Roosc1a80b02016-04-05 14:54:55 -07002259 final Constructor<? extends Style> ctor =
2260 styleClass.getDeclaredConstructor();
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002261 ctor.setAccessible(true);
2262 final Style style = ctor.newInstance();
2263 style.restoreFromExtras(mN.extras);
2264
2265 if (style != null) {
2266 setStyle(style);
2267 }
2268 } catch (Throwable t) {
2269 Log.e(TAG, "Could not create Style", t);
2270 }
2271 }
2272 }
2273
2274 }
2275 }
2276
2277 private NotificationColorUtil getColorUtil() {
2278 if (!mColorUtilInited) {
2279 mColorUtilInited = true;
2280 if (mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.LOLLIPOP) {
2281 mColorUtil = NotificationColorUtil.getInstance(mContext);
Christoph Studer4600f9b2014-07-22 22:44:43 +02002282 }
2283 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002284 return mColorUtil;
Christoph Studer4600f9b2014-07-22 22:44:43 +02002285 }
2286
2287 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002288 * Add a timestamp pertaining to the notification (usually the time the event occurred).
Selim Cinek0ff1ce602016-04-05 18:27:16 -07002289 *
2290 * For apps targeting {@link android.os.Build.VERSION_CODES#N} and above, this time is not
2291 * shown anymore by default and must be opted into by using
2292 * {@link android.app.Notification.Builder#setShowWhen(boolean)}
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002293 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002294 * @see Notification#when
Joe Onoratocb109a02011-01-18 17:57:41 -08002295 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002296 public Builder setWhen(long when) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002297 mN.when = when;
Joe Onorato46439ce2010-11-19 13:56:21 -08002298 return this;
2299 }
2300
Joe Onoratocb109a02011-01-18 17:57:41 -08002301 /**
Griff Hazen50c11652014-05-16 09:46:31 -07002302 * Control whether the timestamp set with {@link #setWhen(long) setWhen} is shown
Daniel Sandler0c890492012-09-12 17:23:10 -07002303 * in the content view.
Selim Cinek0ff1ce602016-04-05 18:27:16 -07002304 * For apps targeting {@link android.os.Build.VERSION_CODES#N} and above, this defaults to
2305 * {@code false}. For earlier apps, the default is {@code true}.
Daniel Sandler0c890492012-09-12 17:23:10 -07002306 */
2307 public Builder setShowWhen(boolean show) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002308 mN.extras.putBoolean(EXTRA_SHOW_WHEN, show);
Daniel Sandler0c890492012-09-12 17:23:10 -07002309 return this;
2310 }
2311
2312 /**
Daniel Sandlerd33b8032012-05-10 11:41:48 -04002313 * Show the {@link Notification#when} field as a stopwatch.
Joe Malin8d40d042012-11-05 11:36:40 -08002314 *
2315 * Instead of presenting <code>when</code> as a timestamp, the notification will show an
Daniel Sandlerd33b8032012-05-10 11:41:48 -04002316 * automatically updating display of the minutes and seconds since <code>when</code>.
Daniel Sandlera2985ed2012-04-03 16:42:00 -04002317 *
Daniel Sandlerd33b8032012-05-10 11:41:48 -04002318 * Useful when showing an elapsed time (like an ongoing phone call).
2319 *
Selim Cinek81c23aa2016-02-25 16:23:13 -08002320 * The counter can also be set to count down to <code>when</code> when using
2321 * {@link #setChronometerCountsDown(boolean)}.
2322 *
Daniel Sandlerd33b8032012-05-10 11:41:48 -04002323 * @see android.widget.Chronometer
Daniel Sandlera2985ed2012-04-03 16:42:00 -04002324 * @see Notification#when
Selim Cinek81c23aa2016-02-25 16:23:13 -08002325 * @see #setChronometerCountsDown(boolean)
Daniel Sandlera2985ed2012-04-03 16:42:00 -04002326 */
2327 public Builder setUsesChronometer(boolean b) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002328 mN.extras.putBoolean(EXTRA_SHOW_CHRONOMETER, b);
Daniel Sandlera2985ed2012-04-03 16:42:00 -04002329 return this;
2330 }
2331
2332 /**
Selim Cinek81c23aa2016-02-25 16:23:13 -08002333 * Sets the Chronometer to count down instead of counting up.
2334 *
2335 * <p>This is only relevant if {@link #setUsesChronometer(boolean)} has been set to true.
2336 * If it isn't set the chronometer will count up.
2337 *
2338 * @see #setUsesChronometer(boolean)
2339 */
2340 public Builder setChronometerCountsDown(boolean countsDown) {
2341 mN.extras.putBoolean(EXTRA_CHRONOMETER_COUNTS_DOWN, countsDown);
2342 return this;
2343 }
2344
2345 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002346 * Set the small icon resource, which will be used to represent the notification in the
2347 * status bar.
Joe Onoratocb109a02011-01-18 17:57:41 -08002348 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002349
2350 * The platform template for the expanded view will draw this icon in the left, unless a
2351 * {@link #setLargeIcon(Bitmap) large icon} has also been specified, in which case the small
2352 * icon will be moved to the right-hand side.
2353 *
2354
2355 * @param icon
2356 * A resource ID in the application's package of the drawable to use.
2357 * @see Notification#icon
Joe Onoratocb109a02011-01-18 17:57:41 -08002358 */
Tor Norbye7b9c9122013-05-30 16:48:33 -07002359 public Builder setSmallIcon(@DrawableRes int icon) {
Dan Sandlerd63f9322015-05-06 15:18:49 -04002360 return setSmallIcon(icon != 0
2361 ? Icon.createWithResource(mContext, icon)
2362 : null);
Joe Onorato46439ce2010-11-19 13:56:21 -08002363 }
2364
Joe Onoratocb109a02011-01-18 17:57:41 -08002365 /**
2366 * A variant of {@link #setSmallIcon(int) setSmallIcon(int)} that takes an additional
2367 * level parameter for when the icon is a {@link android.graphics.drawable.LevelListDrawable
2368 * LevelListDrawable}.
2369 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002370 * @param icon A resource ID in the application's package of the drawable to use.
Joe Onoratocb109a02011-01-18 17:57:41 -08002371 * @param level The level to use for the icon.
2372 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002373 * @see Notification#icon
2374 * @see Notification#iconLevel
Joe Onoratocb109a02011-01-18 17:57:41 -08002375 */
Tor Norbye7b9c9122013-05-30 16:48:33 -07002376 public Builder setSmallIcon(@DrawableRes int icon, int level) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002377 mN.iconLevel = level;
Dan Sandlerd63f9322015-05-06 15:18:49 -04002378 return setSmallIcon(icon);
2379 }
2380
2381 /**
2382 * Set the small icon, which will be used to represent the notification in the
2383 * status bar and content view (unless overriden there by a
2384 * {@link #setLargeIcon(Bitmap) large icon}).
2385 *
2386 * @param icon An Icon object to use.
2387 * @see Notification#icon
2388 */
2389 public Builder setSmallIcon(Icon icon) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002390 mN.setSmallIcon(icon);
2391 if (icon != null && icon.getType() == Icon.TYPE_RESOURCE) {
2392 mN.icon = icon.getResId();
2393 }
Joe Onorato46439ce2010-11-19 13:56:21 -08002394 return this;
2395 }
2396
Joe Onoratocb109a02011-01-18 17:57:41 -08002397 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002398 * Set the first line of text in the platform notification template.
Joe Onoratocb109a02011-01-18 17:57:41 -08002399 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002400 public Builder setContentTitle(CharSequence title) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002401 mN.extras.putCharSequence(EXTRA_TITLE, safeCharSequence(title));
Joe Onorato46439ce2010-11-19 13:56:21 -08002402 return this;
2403 }
2404
Joe Onoratocb109a02011-01-18 17:57:41 -08002405 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002406 * Set the second line of text in the platform notification template.
Joe Onoratocb109a02011-01-18 17:57:41 -08002407 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002408 public Builder setContentText(CharSequence text) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002409 mN.extras.putCharSequence(EXTRA_TEXT, safeCharSequence(text));
Joe Onorato46439ce2010-11-19 13:56:21 -08002410 return this;
2411 }
2412
Joe Onoratocb109a02011-01-18 17:57:41 -08002413 /**
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07002414 * This provides some additional information that is displayed in the notification. No
2415 * guarantees are given where exactly it is displayed.
2416 *
2417 * <p>This information should only be provided if it provides an essential
2418 * benefit to the understanding of the notification. The more text you provide the
2419 * less readable it becomes. For example, an email client should only provide the account
2420 * name here if more than one email account has been added.</p>
2421 *
2422 * <p>As of {@link android.os.Build.VERSION_CODES#N} this information is displayed in the
2423 * notification header area.
2424 *
2425 * On Android versions before {@link android.os.Build.VERSION_CODES#N}
2426 * this will be shown in the third line of text in the platform notification template.
2427 * You should not be using {@link #setProgress(int, int, boolean)} at the
2428 * same time on those versions; they occupy the same place.
2429 * </p>
Daniel Sandlerf3b73432012-03-27 15:01:25 -04002430 */
2431 public Builder setSubText(CharSequence text) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002432 mN.extras.putCharSequence(EXTRA_SUB_TEXT, safeCharSequence(text));
Daniel Sandlerf3b73432012-03-27 15:01:25 -04002433 return this;
2434 }
2435
2436 /**
Adrian Roose458aa82015-12-08 16:17:19 -08002437 * Set the remote input history.
2438 *
2439 * This should be set to the most recent inputs that have been sent
2440 * through a {@link RemoteInput} of this Notification and cleared once the it is no
2441 * longer relevant (e.g. for chat notifications once the other party has responded).
2442 *
2443 * The most recent input must be stored at the 0 index, the second most recent at the
2444 * 1 index, etc. Note that the system will limit both how far back the inputs will be shown
2445 * and how much of each individual input is shown.
2446 *
2447 * <p>Note: The reply text will only be shown on notifications that have least one action
2448 * with a {@code RemoteInput}.</p>
2449 */
2450 public Builder setRemoteInputHistory(CharSequence[] text) {
2451 if (text == null) {
2452 mN.extras.putCharSequenceArray(EXTRA_REMOTE_INPUT_HISTORY, null);
2453 } else {
2454 final int N = Math.min(MAX_REPLY_HISTORY, text.length);
2455 CharSequence[] safe = new CharSequence[N];
2456 for (int i = 0; i < N; i++) {
2457 safe[i] = safeCharSequence(text[i]);
2458 }
2459 mN.extras.putCharSequenceArray(EXTRA_REMOTE_INPUT_HISTORY, safe);
2460 }
2461 return this;
2462 }
2463
2464 /**
Joe Onoratocb109a02011-01-18 17:57:41 -08002465 * Set the large number at the right-hand side of the notification. This is
2466 * equivalent to setContentInfo, although it might show the number in a different
2467 * font size for readability.
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07002468 *
2469 * @deprecated this number is not shown anywhere anymore
Joe Onoratocb109a02011-01-18 17:57:41 -08002470 */
Joe Onorato8595a3d2010-11-19 18:12:07 -08002471 public Builder setNumber(int number) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002472 mN.number = number;
Joe Onorato8595a3d2010-11-19 18:12:07 -08002473 return this;
2474 }
2475
Joe Onoratocb109a02011-01-18 17:57:41 -08002476 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002477 * A small piece of additional information pertaining to this notification.
2478 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002479 * The platform template will draw this on the last line of the notification, at the far
2480 * right (to the right of a smallIcon if it has been placed there).
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07002481 *
2482 * @deprecated use {@link #setSubText(CharSequence)} instead to set a text in the header.
2483 * For legacy apps targeting a version below {@link android.os.Build.VERSION_CODES#N} this
2484 * field will still show up, but the subtext will take precedence.
Joe Onoratocb109a02011-01-18 17:57:41 -08002485 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002486 public Builder setContentInfo(CharSequence info) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002487 mN.extras.putCharSequence(EXTRA_INFO_TEXT, safeCharSequence(info));
Joe Onorato46439ce2010-11-19 13:56:21 -08002488 return this;
2489 }
2490
Joe Onoratocb109a02011-01-18 17:57:41 -08002491 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002492 * Set the progress this notification represents.
2493 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002494 * The platform template will represent this using a {@link ProgressBar}.
Jeff Sharkey1c400132011-08-05 14:50:13 -07002495 */
2496 public Builder setProgress(int max, int progress, boolean indeterminate) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002497 mN.extras.putInt(EXTRA_PROGRESS, progress);
2498 mN.extras.putInt(EXTRA_PROGRESS_MAX, max);
2499 mN.extras.putBoolean(EXTRA_PROGRESS_INDETERMINATE, indeterminate);
Jeff Sharkey1c400132011-08-05 14:50:13 -07002500 return this;
2501 }
2502
2503 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002504 * Supply a custom RemoteViews to use instead of the platform template.
2505 *
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002506 * Use {@link #setCustomContentView(RemoteViews)} instead.
Joe Onoratocb109a02011-01-18 17:57:41 -08002507 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002508 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08002509 public Builder setContent(RemoteViews views) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002510 return setCustomContentView(views);
2511 }
2512
2513 /**
2514 * Supply custom RemoteViews to use instead of the platform template.
2515 *
2516 * This will override the layout that would otherwise be constructed by this Builder
2517 * object.
2518 */
2519 public Builder setCustomContentView(RemoteViews contentView) {
2520 mN.contentView = contentView;
2521 return this;
2522 }
2523
2524 /**
2525 * Supply custom RemoteViews to use instead of the platform template in the expanded form.
2526 *
2527 * This will override the expanded layout that would otherwise be constructed by this
2528 * Builder object.
2529 */
2530 public Builder setCustomBigContentView(RemoteViews contentView) {
2531 mN.bigContentView = contentView;
2532 return this;
2533 }
2534
2535 /**
2536 * Supply custom RemoteViews to use instead of the platform template in the heads up dialog.
2537 *
2538 * This will override the heads-up layout that would otherwise be constructed by this
2539 * Builder object.
2540 */
2541 public Builder setCustomHeadsUpContentView(RemoteViews contentView) {
2542 mN.headsUpContentView = contentView;
Joe Onorato46439ce2010-11-19 13:56:21 -08002543 return this;
2544 }
2545
Joe Onoratocb109a02011-01-18 17:57:41 -08002546 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002547 * Supply a {@link PendingIntent} to be sent when the notification is clicked.
2548 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002549 * As of {@link android.os.Build.VERSION_CODES#HONEYCOMB}, if this field is unset and you
2550 * have specified a custom RemoteViews with {@link #setContent(RemoteViews)}, you can use
2551 * {@link RemoteViews#setOnClickPendingIntent RemoteViews.setOnClickPendingIntent(int,PendingIntent)}
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002552 * to assign PendingIntents to individual views in that custom layout (i.e., to create
Daniel Sandlerf3b73432012-03-27 15:01:25 -04002553 * clickable buttons inside the notification view).
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002554 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002555 * @see Notification#contentIntent Notification.contentIntent
Joe Onoratocb109a02011-01-18 17:57:41 -08002556 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002557 public Builder setContentIntent(PendingIntent intent) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002558 mN.contentIntent = intent;
Joe Onorato46439ce2010-11-19 13:56:21 -08002559 return this;
2560 }
2561
Joe Onoratocb109a02011-01-18 17:57:41 -08002562 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002563 * Supply a {@link PendingIntent} to send when the notification is cleared explicitly by the user.
2564 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002565 * @see Notification#deleteIntent
Joe Onoratocb109a02011-01-18 17:57:41 -08002566 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002567 public Builder setDeleteIntent(PendingIntent intent) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002568 mN.deleteIntent = intent;
Joe Onorato46439ce2010-11-19 13:56:21 -08002569 return this;
2570 }
2571
Joe Onoratocb109a02011-01-18 17:57:41 -08002572 /**
2573 * An intent to launch instead of posting the notification to the status bar.
2574 * Only for use with extremely high-priority notifications demanding the user's
2575 * <strong>immediate</strong> attention, such as an incoming phone call or
2576 * alarm clock that the user has explicitly set to a particular time.
2577 * If this facility is used for something else, please give the user an option
2578 * to turn it off and use a normal notification, as this can be extremely
2579 * disruptive.
2580 *
Chris Wren47c20a12014-06-18 17:27:29 -04002581 * <p>
2582 * The system UI may choose to display a heads-up notification, instead of
2583 * launching this intent, while the user is using the device.
2584 * </p>
2585 *
Joe Onoratocb109a02011-01-18 17:57:41 -08002586 * @param intent The pending intent to launch.
2587 * @param highPriority Passing true will cause this notification to be sent
2588 * even if other notifications are suppressed.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002589 *
2590 * @see Notification#fullScreenIntent
Joe Onoratocb109a02011-01-18 17:57:41 -08002591 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002592 public Builder setFullScreenIntent(PendingIntent intent, boolean highPriority) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002593 mN.fullScreenIntent = intent;
Joe Onorato46439ce2010-11-19 13:56:21 -08002594 setFlag(FLAG_HIGH_PRIORITY, highPriority);
2595 return this;
2596 }
2597
Joe Onoratocb109a02011-01-18 17:57:41 -08002598 /**
Dan Sandler5fcdf6e2014-07-18 11:31:15 -04002599 * Set the "ticker" text which is sent to accessibility services.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002600 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002601 * @see Notification#tickerText
Joe Onoratocb109a02011-01-18 17:57:41 -08002602 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002603 public Builder setTicker(CharSequence tickerText) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002604 mN.tickerText = safeCharSequence(tickerText);
Joe Onorato46439ce2010-11-19 13:56:21 -08002605 return this;
2606 }
2607
Joe Onoratocb109a02011-01-18 17:57:41 -08002608 /**
Dan Sandler5fcdf6e2014-07-18 11:31:15 -04002609 * Obsolete version of {@link #setTicker(CharSequence)}.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002610 *
Joe Onoratocb109a02011-01-18 17:57:41 -08002611 */
Dan Sandler5fcdf6e2014-07-18 11:31:15 -04002612 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08002613 public Builder setTicker(CharSequence tickerText, RemoteViews views) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002614 setTicker(tickerText);
2615 // views is ignored
Joe Onorato46439ce2010-11-19 13:56:21 -08002616 return this;
2617 }
2618
Joe Onoratocb109a02011-01-18 17:57:41 -08002619 /**
Dan Sandlerd63f9322015-05-06 15:18:49 -04002620 * Add a large icon to the notification content view.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002621 *
2622 * In the platform template, this image will be shown on the left of the notification view
Dan Sandlerd63f9322015-05-06 15:18:49 -04002623 * in place of the {@link #setSmallIcon(Icon) small icon} (which will be placed in a small
2624 * badge atop the large icon).
Dan Sandler08a04c12015-05-06 15:18:49 -04002625 */
Dan Sandlerd63f9322015-05-06 15:18:49 -04002626 public Builder setLargeIcon(Bitmap b) {
2627 return setLargeIcon(b != null ? Icon.createWithBitmap(b) : null);
2628 }
2629
2630 /**
2631 * Add a large icon to the notification content view.
2632 *
2633 * In the platform template, this image will be shown on the left of the notification view
2634 * in place of the {@link #setSmallIcon(Icon) small icon} (which will be placed in a small
2635 * badge atop the large icon).
2636 */
2637 public Builder setLargeIcon(Icon icon) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002638 mN.mLargeIcon = icon;
2639 mN.extras.putParcelable(EXTRA_LARGE_ICON, icon);
Joe Onorato46439ce2010-11-19 13:56:21 -08002640 return this;
2641 }
2642
Joe Onoratocb109a02011-01-18 17:57:41 -08002643 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002644 * Set the sound to play.
2645 *
John Spurlockc0650f022014-07-19 13:22:39 -04002646 * It will be played using the {@link #AUDIO_ATTRIBUTES_DEFAULT default audio attributes}
2647 * for notifications.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002648 *
Chris Wren47c20a12014-06-18 17:27:29 -04002649 * <p>
2650 * A notification that is noisy is more likely to be presented as a heads-up notification.
2651 * </p>
2652 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002653 * @see Notification#sound
Joe Onoratocb109a02011-01-18 17:57:41 -08002654 */
Joe Onorato52f80cd2010-11-21 15:34:48 -08002655 public Builder setSound(Uri sound) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002656 mN.sound = sound;
2657 mN.audioAttributes = AUDIO_ATTRIBUTES_DEFAULT;
Joe Onorato52f80cd2010-11-21 15:34:48 -08002658 return this;
2659 }
2660
Joe Onoratocb109a02011-01-18 17:57:41 -08002661 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002662 * Set the sound to play, along with a specific stream on which to play it.
Joe Onoratocb109a02011-01-18 17:57:41 -08002663 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002664 * See {@link android.media.AudioManager} for the <code>STREAM_</code> constants.
2665 *
Chris Wren47c20a12014-06-18 17:27:29 -04002666 * <p>
2667 * A notification that is noisy is more likely to be presented as a heads-up notification.
2668 * </p>
John Spurlockc0650f022014-07-19 13:22:39 -04002669 * @deprecated use {@link #setSound(Uri, AudioAttributes)} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002670 * @see Notification#sound
Joe Onoratocb109a02011-01-18 17:57:41 -08002671 */
Jean-Michel Trivi81f871e2014-08-06 16:32:38 -07002672 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08002673 public Builder setSound(Uri sound, int streamType) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002674 mN.sound = sound;
2675 mN.audioStreamType = streamType;
Joe Onorato46439ce2010-11-19 13:56:21 -08002676 return this;
2677 }
2678
Joe Onoratocb109a02011-01-18 17:57:41 -08002679 /**
John Spurlockc0650f022014-07-19 13:22:39 -04002680 * Set the sound to play, along with specific {@link AudioAttributes audio attributes} to
2681 * use during playback.
2682 *
2683 * <p>
2684 * A notification that is noisy is more likely to be presented as a heads-up notification.
2685 * </p>
2686 *
2687 * @see Notification#sound
2688 */
2689 public Builder setSound(Uri sound, AudioAttributes audioAttributes) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002690 mN.sound = sound;
2691 mN.audioAttributes = audioAttributes;
John Spurlockc0650f022014-07-19 13:22:39 -04002692 return this;
2693 }
2694
2695 /**
Joe Onoratocb109a02011-01-18 17:57:41 -08002696 * Set the vibration pattern to use.
2697 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002698 * See {@link android.os.Vibrator#vibrate(long[], int)} for a discussion of the
2699 * <code>pattern</code> parameter.
2700 *
Chris Wren47c20a12014-06-18 17:27:29 -04002701 * <p>
2702 * A notification that vibrates is more likely to be presented as a heads-up notification.
2703 * </p>
2704 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002705 * @see Notification#vibrate
Joe Onoratocb109a02011-01-18 17:57:41 -08002706 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002707 public Builder setVibrate(long[] pattern) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002708 mN.vibrate = pattern;
Joe Onorato46439ce2010-11-19 13:56:21 -08002709 return this;
2710 }
2711
Joe Onoratocb109a02011-01-18 17:57:41 -08002712 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002713 * Set the desired color for the indicator LED on the device, as well as the
2714 * blink duty cycle (specified in milliseconds).
2715 *
2716
2717 * Not all devices will honor all (or even any) of these values.
2718 *
2719
2720 * @see Notification#ledARGB
2721 * @see Notification#ledOnMS
2722 * @see Notification#ledOffMS
Joe Onoratocb109a02011-01-18 17:57:41 -08002723 */
Tor Norbye80756e32015-03-02 09:39:27 -08002724 public Builder setLights(@ColorInt int argb, int onMs, int offMs) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002725 mN.ledARGB = argb;
2726 mN.ledOnMS = onMs;
2727 mN.ledOffMS = offMs;
Julia Reynolds10ee1fc2015-11-09 11:04:55 -05002728 if (onMs != 0 || offMs != 0) {
2729 mN.flags |= FLAG_SHOW_LIGHTS;
2730 }
Joe Onorato46439ce2010-11-19 13:56:21 -08002731 return this;
2732 }
2733
Joe Onoratocb109a02011-01-18 17:57:41 -08002734 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002735 * Set whether this is an "ongoing" notification.
Joe Onoratocb109a02011-01-18 17:57:41 -08002736 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002737
2738 * Ongoing notifications cannot be dismissed by the user, so your application or service
2739 * must take care of canceling them.
2740 *
2741
2742 * They are typically used to indicate a background task that the user is actively engaged
2743 * with (e.g., playing music) or is pending in some way and therefore occupying the device
2744 * (e.g., a file download, sync operation, active network connection).
2745 *
2746
2747 * @see Notification#FLAG_ONGOING_EVENT
2748 * @see Service#setForeground(boolean)
Joe Onoratocb109a02011-01-18 17:57:41 -08002749 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002750 public Builder setOngoing(boolean ongoing) {
2751 setFlag(FLAG_ONGOING_EVENT, ongoing);
2752 return this;
2753 }
2754
Joe Onoratocb109a02011-01-18 17:57:41 -08002755 /**
2756 * Set this flag if you would only like the sound, vibrate
2757 * and ticker to be played if the notification is not already showing.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002758 *
2759 * @see Notification#FLAG_ONLY_ALERT_ONCE
Joe Onoratocb109a02011-01-18 17:57:41 -08002760 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002761 public Builder setOnlyAlertOnce(boolean onlyAlertOnce) {
2762 setFlag(FLAG_ONLY_ALERT_ONCE, onlyAlertOnce);
2763 return this;
2764 }
2765
Joe Onoratocb109a02011-01-18 17:57:41 -08002766 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002767 * Make this notification automatically dismissed when the user touches it. The
2768 * PendingIntent set with {@link #setDeleteIntent} will be sent when this happens.
2769 *
2770 * @see Notification#FLAG_AUTO_CANCEL
Joe Onoratocb109a02011-01-18 17:57:41 -08002771 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002772 public Builder setAutoCancel(boolean autoCancel) {
Joe Onorato281d83f2011-01-04 17:13:10 -08002773 setFlag(FLAG_AUTO_CANCEL, autoCancel);
Joe Onorato46439ce2010-11-19 13:56:21 -08002774 return this;
2775 }
2776
Joe Onoratocb109a02011-01-18 17:57:41 -08002777 /**
Griff Hazendfcb0802014-02-11 12:00:00 -08002778 * Set whether or not this notification should not bridge to other devices.
2779 *
2780 * <p>Some notifications can be bridged to other devices for remote display.
2781 * This hint can be set to recommend this notification not be bridged.
2782 */
2783 public Builder setLocalOnly(boolean localOnly) {
2784 setFlag(FLAG_LOCAL_ONLY, localOnly);
2785 return this;
2786 }
2787
2788 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002789 * Set which notification properties will be inherited from system defaults.
Joe Onoratocb109a02011-01-18 17:57:41 -08002790 * <p>
2791 * The value should be one or more of the following fields combined with
2792 * bitwise-or:
2793 * {@link #DEFAULT_SOUND}, {@link #DEFAULT_VIBRATE}, {@link #DEFAULT_LIGHTS}.
2794 * <p>
2795 * For all default values, use {@link #DEFAULT_ALL}.
2796 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002797 public Builder setDefaults(int defaults) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002798 mN.defaults = defaults;
Joe Onorato46439ce2010-11-19 13:56:21 -08002799 return this;
2800 }
2801
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002802 /**
2803 * Set the priority of this notification.
2804 *
2805 * @see Notification#priority
2806 */
Tor Norbyed9273d62013-05-30 15:59:53 -07002807 public Builder setPriority(@Priority int pri) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002808 mN.priority = pri;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002809 return this;
2810 }
Joe Malin8d40d042012-11-05 11:36:40 -08002811
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002812 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -04002813 * Set the notification category.
Joe Malin8d40d042012-11-05 11:36:40 -08002814 *
John Spurlockfd7f1e02014-03-18 16:41:57 -04002815 * @see Notification#category
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002816 */
John Spurlockfd7f1e02014-03-18 16:41:57 -04002817 public Builder setCategory(String category) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002818 mN.category = category;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002819 return this;
2820 }
2821
2822 /**
Chris Wrendde75302014-03-26 17:24:15 -04002823 * Add a person that is relevant to this notification.
2824 *
Chris Wrene6c48932014-09-29 17:19:27 -04002825 * <P>
2826 * Depending on user preferences, this annotation may allow the notification to pass
2827 * through interruption filters, and to appear more prominently in the user interface.
2828 * </P>
2829 *
2830 * <P>
2831 * The person should be specified by the {@code String} representation of a
2832 * {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI}.
2833 * </P>
2834 *
2835 * <P>The system will also attempt to resolve {@code mailto:} and {@code tel:} schema
2836 * URIs. The path part of these URIs must exist in the contacts database, in the
2837 * appropriate column, or the reference will be discarded as invalid. Telephone schema
2838 * URIs will be resolved by {@link android.provider.ContactsContract.PhoneLookup}.
2839 * </P>
2840 *
2841 * @param uri A URI for the person.
Chris Wrendde75302014-03-26 17:24:15 -04002842 * @see Notification#EXTRA_PEOPLE
2843 */
Chris Wrene6c48932014-09-29 17:19:27 -04002844 public Builder addPerson(String uri) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002845 mPersonList.add(uri);
Chris Wrendde75302014-03-26 17:24:15 -04002846 return this;
2847 }
2848
2849 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002850 * Set this notification to be part of a group of notifications sharing the same key.
2851 * Grouped notifications may display in a cluster or stack on devices which
2852 * support such rendering.
2853 *
2854 * <p>To make this notification the summary for its group, also call
2855 * {@link #setGroupSummary}. A sort order can be specified for group members by using
2856 * {@link #setSortKey}.
2857 * @param groupKey The group key of the group.
2858 * @return this object for method chaining
2859 */
2860 public Builder setGroup(String groupKey) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002861 mN.mGroupKey = groupKey;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002862 return this;
2863 }
2864
2865 /**
2866 * Set this notification to be the group summary for a group of notifications.
2867 * Grouped notifications may display in a cluster or stack on devices which
2868 * support such rendering. Requires a group key also be set using {@link #setGroup}.
2869 * @param isGroupSummary Whether this notification should be a group summary.
2870 * @return this object for method chaining
2871 */
2872 public Builder setGroupSummary(boolean isGroupSummary) {
2873 setFlag(FLAG_GROUP_SUMMARY, isGroupSummary);
2874 return this;
2875 }
2876
2877 /**
2878 * Set a sort key that orders this notification among other notifications from the
2879 * same package. This can be useful if an external sort was already applied and an app
2880 * would like to preserve this. Notifications will be sorted lexicographically using this
2881 * value, although providing different priorities in addition to providing sort key may
2882 * cause this value to be ignored.
2883 *
2884 * <p>This sort key can also be used to order members of a notification group. See
Griff Hazen9e1379f2014-05-20 12:50:51 -07002885 * {@link #setGroup}.
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002886 *
2887 * @see String#compareTo(String)
2888 */
2889 public Builder setSortKey(String sortKey) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002890 mN.mSortKey = sortKey;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002891 return this;
2892 }
2893
2894 /**
Griff Hazen720042b2014-02-24 15:46:56 -08002895 * Merge additional metadata into this notification.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002896 *
Griff Hazen720042b2014-02-24 15:46:56 -08002897 * <p>Values within the Bundle will replace existing extras values in this Builder.
2898 *
2899 * @see Notification#extras
2900 */
Griff Hazen959591e2014-05-15 22:26:18 -07002901 public Builder addExtras(Bundle extras) {
2902 if (extras != null) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002903 mUserExtras.putAll(extras);
Griff Hazen720042b2014-02-24 15:46:56 -08002904 }
2905 return this;
2906 }
2907
2908 /**
2909 * Set metadata for this notification.
2910 *
2911 * <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 -04002912 * current contents are copied into the Notification each time {@link #build()} is
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002913 * called.
2914 *
Griff Hazen720042b2014-02-24 15:46:56 -08002915 * <p>Replaces any existing extras values with those from the provided Bundle.
2916 * Use {@link #addExtras} to merge in metadata instead.
2917 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002918 * @see Notification#extras
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002919 */
Griff Hazen959591e2014-05-15 22:26:18 -07002920 public Builder setExtras(Bundle extras) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002921 if (extras != null) {
2922 mUserExtras = extras;
2923 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002924 return this;
2925 }
2926
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002927 /**
Griff Hazen720042b2014-02-24 15:46:56 -08002928 * Get the current metadata Bundle used by this notification Builder.
2929 *
2930 * <p>The returned Bundle is shared with this Builder.
2931 *
2932 * <p>The current contents of this Bundle are copied into the Notification each time
2933 * {@link #build()} is called.
2934 *
2935 * @see Notification#extras
2936 */
2937 public Bundle getExtras() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002938 return mUserExtras;
2939 }
2940
2941 private Bundle getAllExtras() {
2942 final Bundle saveExtras = (Bundle) mUserExtras.clone();
2943 saveExtras.putAll(mN.extras);
2944 return saveExtras;
Griff Hazen720042b2014-02-24 15:46:56 -08002945 }
2946
2947 /**
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002948 * Add an action to this notification. Actions are typically displayed by
2949 * the system as a button adjacent to the notification content.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04002950 * <p>
2951 * Every action must have an icon (32dp square and matching the
2952 * <a href="{@docRoot}design/style/iconography.html#action-bar">Holo
2953 * Dark action bar</a> visual style), a textual label, and a {@link PendingIntent}.
2954 * <p>
2955 * A notification in its expanded form can display up to 3 actions, from left to right in
2956 * the order they were added. Actions will not be displayed when the notification is
2957 * collapsed, however, so be sure that any essential functions may be accessed by the user
2958 * in some other way (for example, in the Activity pointed to by {@link #contentIntent}).
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002959 *
2960 * @param icon Resource ID of a drawable that represents the action.
2961 * @param title Text describing the action.
2962 * @param intent PendingIntent to be fired when the action is invoked.
Dan Sandler86647982015-05-13 23:41:13 -04002963 *
2964 * @deprecated Use {@link #addAction(Action)} instead.
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002965 */
Dan Sandler86647982015-05-13 23:41:13 -04002966 @Deprecated
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002967 public Builder addAction(int icon, CharSequence title, PendingIntent intent) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04002968 mActions.add(new Action(icon, safeCharSequence(title), intent));
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002969 return this;
2970 }
2971
Chris Wrenfbd96ba2012-05-01 12:03:58 -04002972 /**
Griff Hazen959591e2014-05-15 22:26:18 -07002973 * Add an action to this notification. Actions are typically displayed by
2974 * the system as a button adjacent to the notification content.
2975 * <p>
2976 * Every action must have an icon (32dp square and matching the
2977 * <a href="{@docRoot}design/style/iconography.html#action-bar">Holo
2978 * Dark action bar</a> visual style), a textual label, and a {@link PendingIntent}.
2979 * <p>
2980 * A notification in its expanded form can display up to 3 actions, from left to right in
2981 * the order they were added. Actions will not be displayed when the notification is
2982 * collapsed, however, so be sure that any essential functions may be accessed by the user
2983 * in some other way (for example, in the Activity pointed to by {@link #contentIntent}).
2984 *
2985 * @param action The action to add.
2986 */
2987 public Builder addAction(Action action) {
2988 mActions.add(action);
2989 return this;
2990 }
2991
2992 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002993 * Alter the complete list of actions attached to this notification.
2994 * @see #addAction(Action).
2995 *
2996 * @param actions
2997 * @return
2998 */
2999 public Builder setActions(Action... actions) {
3000 mActions.clear();
3001 for (int i = 0; i < actions.length; i++) {
3002 mActions.add(actions[i]);
3003 }
3004 return this;
3005 }
3006
3007 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003008 * Add a rich notification style to be applied at build time.
3009 *
3010 * @param style Object responsible for modifying the notification style.
3011 */
3012 public Builder setStyle(Style style) {
3013 if (mStyle != style) {
3014 mStyle = style;
Daniel Sandlerc08dea22012-06-28 08:35:24 -07003015 if (mStyle != null) {
3016 mStyle.setBuilder(this);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003017 mN.extras.putString(EXTRA_TEMPLATE, style.getClass().getName());
3018 } else {
3019 mN.extras.remove(EXTRA_TEMPLATE);
Daniel Sandlerc08dea22012-06-28 08:35:24 -07003020 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003021 }
3022 return this;
3023 }
3024
Dan Sandler0bf2ed82013-12-21 23:33:41 -06003025 /**
3026 * Specify the value of {@link #visibility}.
Griff Hazenb720abe2014-05-20 13:15:30 -07003027 *
Dan Sandler0bf2ed82013-12-21 23:33:41 -06003028 * @param visibility One of {@link #VISIBILITY_PRIVATE} (the default),
3029 * {@link #VISIBILITY_SECRET}, or {@link #VISIBILITY_PUBLIC}.
3030 *
3031 * @return The same Builder.
3032 */
3033 public Builder setVisibility(int visibility) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003034 mN.visibility = visibility;
Dan Sandler0bf2ed82013-12-21 23:33:41 -06003035 return this;
3036 }
3037
3038 /**
3039 * Supply a replacement Notification whose contents should be shown in insecure contexts
3040 * (i.e. atop the secure lockscreen). See {@link #visibility} and {@link #VISIBILITY_PUBLIC}.
3041 * @param n A replacement notification, presumably with some or all info redacted.
3042 * @return The same Builder.
3043 */
3044 public Builder setPublicVersion(Notification n) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003045 if (n != null) {
3046 mN.publicVersion = new Notification();
3047 n.cloneInto(mN.publicVersion, /*heavy=*/ true);
3048 } else {
3049 mN.publicVersion = null;
3050 }
Dan Sandler0bf2ed82013-12-21 23:33:41 -06003051 return this;
3052 }
3053
Griff Hazenb720abe2014-05-20 13:15:30 -07003054 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003055 * Apply an extender to this notification builder. Extenders may be used to add
3056 * metadata or change options on this builder.
3057 */
Griff Hazen61a9e862014-05-22 16:05:19 -07003058 public Builder extend(Extender extender) {
3059 extender.extend(this);
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003060 return this;
3061 }
3062
Dan Sandler4e787062015-06-17 15:09:48 -04003063 /**
3064 * @hide
3065 */
Julia Reynoldse46bb372016-03-17 11:05:58 -04003066 public Builder setFlag(int mask, boolean value) {
Joe Onorato46439ce2010-11-19 13:56:21 -08003067 if (value) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003068 mN.flags |= mask;
Joe Onorato46439ce2010-11-19 13:56:21 -08003069 } else {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003070 mN.flags &= ~mask;
Joe Onorato46439ce2010-11-19 13:56:21 -08003071 }
Julia Reynoldse46bb372016-03-17 11:05:58 -04003072 return this;
Joe Onorato46439ce2010-11-19 13:56:21 -08003073 }
3074
Dan Sandler26e81cf2014-05-06 10:01:27 -04003075 /**
3076 * Sets {@link Notification#color}.
3077 *
3078 * @param argb The accent color to use
3079 *
3080 * @return The same Builder.
3081 */
Tor Norbye80756e32015-03-02 09:39:27 -08003082 public Builder setColor(@ColorInt int argb) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003083 mN.color = argb;
Julia Reynolds10ee1fc2015-11-09 11:04:55 -05003084 sanitizeColor();
Dan Sandler26e81cf2014-05-06 10:01:27 -04003085 return this;
3086 }
3087
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003088 private Drawable getProfileBadgeDrawable() {
Christoph Studer7ac80e62014-08-04 16:01:57 +02003089 // Note: This assumes that the current user can read the profile badge of the
3090 // originating user.
Selim Cineke6ff9462016-01-15 15:07:06 -08003091 return mContext.getPackageManager().getUserBadgeForDensityNoBackground(
Julia Reynoldsda303542015-11-23 14:00:20 -05003092 new UserHandle(mContext.getUserId()), 0);
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003093 }
3094
3095 private Bitmap getProfileBadge() {
3096 Drawable badge = getProfileBadgeDrawable();
Kenny Guy8a0101b2014-05-08 23:34:12 +01003097 if (badge == null) {
3098 return null;
3099 }
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003100 final int size = mContext.getResources().getDimensionPixelSize(
3101 R.dimen.notification_badge_size);
3102 Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
Kenny Guy8a0101b2014-05-08 23:34:12 +01003103 Canvas canvas = new Canvas(bitmap);
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003104 badge.setBounds(0, 0, size, size);
Kenny Guy8a0101b2014-05-08 23:34:12 +01003105 badge.draw(canvas);
3106 return bitmap;
3107 }
3108
Selim Cinekc848c3a2016-01-13 15:27:30 -08003109 private void bindProfileBadge(RemoteViews contentView) {
Kenny Guy98193ea2014-07-24 19:54:37 +01003110 Bitmap profileBadge = getProfileBadge();
3111
Kenny Guy98193ea2014-07-24 19:54:37 +01003112 if (profileBadge != null) {
Selim Cinekc848c3a2016-01-13 15:27:30 -08003113 contentView.setImageViewBitmap(R.id.profile_badge, profileBadge);
3114 contentView.setViewVisibility(R.id.profile_badge, View.VISIBLE);
Kenny Guy98193ea2014-07-24 19:54:37 +01003115 }
Kenny Guy98193ea2014-07-24 19:54:37 +01003116 }
3117
Christoph Studerfe718432014-09-01 18:21:18 +02003118 private void resetStandardTemplate(RemoteViews contentView) {
Selim Cinekeaa29ca2015-11-23 13:51:13 -08003119 resetNotificationHeader(contentView);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003120 resetContentMargins(contentView);
Christoph Studerfe718432014-09-01 18:21:18 +02003121 contentView.setViewVisibility(R.id.right_icon, View.GONE);
Selim Cinek860b6da2015-12-16 19:02:19 -08003122 contentView.setViewVisibility(R.id.title, View.GONE);
Christoph Studerfe718432014-09-01 18:21:18 +02003123 contentView.setTextViewText(R.id.title, null);
Selim Cinek41598732016-01-11 16:58:37 -08003124 contentView.setViewVisibility(R.id.text, View.GONE);
Christoph Studerfe718432014-09-01 18:21:18 +02003125 contentView.setTextViewText(R.id.text, null);
Selim Cinek29603462015-11-17 19:04:39 -08003126 contentView.setViewVisibility(R.id.text_line_1, View.GONE);
Selim Cinek41598732016-01-11 16:58:37 -08003127 contentView.setTextViewText(R.id.text_line_1, null);
Christoph Studerfe718432014-09-01 18:21:18 +02003128 contentView.setViewVisibility(R.id.progress, View.GONE);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003129 }
3130
Selim Cinekeaa29ca2015-11-23 13:51:13 -08003131 /**
3132 * Resets the notification header to its original state
3133 */
3134 private void resetNotificationHeader(RemoteViews contentView) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003135 contentView.setImageViewResource(R.id.icon, 0);
Selim Cinek7b836392015-12-04 20:02:59 -08003136 contentView.setBoolean(R.id.notification_header, "setExpanded", false);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003137 contentView.setTextViewText(R.id.app_name_text, null);
Christoph Studerca1db712014-09-10 17:31:33 +02003138 contentView.setViewVisibility(R.id.chronometer, View.GONE);
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07003139 contentView.setViewVisibility(R.id.header_text, View.GONE);
3140 contentView.setViewVisibility(R.id.header_text_divider, View.GONE);
Selim Cinek29603462015-11-17 19:04:39 -08003141 contentView.setViewVisibility(R.id.time_divider, View.GONE);
Selim Cinekb85f36fd2016-04-20 18:46:36 -07003142 contentView.setViewVisibility(R.id.time, View.GONE);
Selim Cinekc848c3a2016-01-13 15:27:30 -08003143 contentView.setImageViewIcon(R.id.profile_badge, null);
3144 contentView.setViewVisibility(R.id.profile_badge, View.GONE);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003145 }
3146
3147 private void resetContentMargins(RemoteViews contentView) {
3148 contentView.setViewLayoutMarginEnd(R.id.line1, 0);
Selim Cinekc848c3a2016-01-13 15:27:30 -08003149 contentView.setViewLayoutMarginEnd(R.id.text, 0);
Christoph Studerfe718432014-09-01 18:21:18 +02003150 }
3151
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003152 private RemoteViews applyStandardTemplate(int resId) {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02003153 return applyStandardTemplate(resId, true /* hasProgress */);
3154 }
3155
3156 /**
3157 * @param hasProgress whether the progress bar should be shown and set
3158 */
3159 private RemoteViews applyStandardTemplate(int resId, boolean hasProgress) {
Adrian Roosc1a80b02016-04-05 14:54:55 -07003160 final Bundle ex = mN.extras;
3161
3162 CharSequence title = processLegacyText(ex.getCharSequence(EXTRA_TITLE));
3163 CharSequence text = processLegacyText(ex.getCharSequence(EXTRA_TEXT));
3164 return applyStandardTemplate(resId, hasProgress, title, text);
3165 }
3166
3167 /**
3168 * @param hasProgress whether the progress bar should be shown and set
3169 */
3170 private RemoteViews applyStandardTemplate(int resId, boolean hasProgress,
3171 CharSequence title, CharSequence text) {
Kenny Guy77320062014-08-27 21:37:15 +01003172 RemoteViews contentView = new BuilderRemoteViews(mContext.getApplicationInfo(), resId);
Dan Sandler539aad42014-08-04 00:43:39 -04003173
Christoph Studerfe718432014-09-01 18:21:18 +02003174 resetStandardTemplate(contentView);
3175
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003176 final Bundle ex = mN.extras;
Dan Sandler190d58d2014-05-15 09:33:39 -04003177
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003178 bindNotificationHeader(contentView);
3179 bindLargeIcon(contentView);
Adrian Roosc1a80b02016-04-05 14:54:55 -07003180 if (title != null) {
Selim Cinek860b6da2015-12-16 19:02:19 -08003181 contentView.setViewVisibility(R.id.title, View.VISIBLE);
Adrian Roosc1a80b02016-04-05 14:54:55 -07003182 contentView.setTextViewText(R.id.title, title);
Joe Onorato561d3852010-11-20 18:09:34 -08003183 }
Selim Cinek29603462015-11-17 19:04:39 -08003184 boolean showProgress = handleProgressBar(hasProgress, contentView, ex);
Adrian Roosc1a80b02016-04-05 14:54:55 -07003185 if (text != null) {
Selim Cinek41598732016-01-11 16:58:37 -08003186 int textId = showProgress ? com.android.internal.R.id.text_line_1
3187 : com.android.internal.R.id.text;
Adrian Roosc1a80b02016-04-05 14:54:55 -07003188 contentView.setTextViewText(textId, text);
Selim Cinek41598732016-01-11 16:58:37 -08003189 contentView.setViewVisibility(textId, View.VISIBLE);
Joe Onorato561d3852010-11-20 18:09:34 -08003190 }
Selim Cinekc848c3a2016-01-13 15:27:30 -08003191
Selim Cinek860b6da2015-12-16 19:02:19 -08003192 setContentMinHeight(contentView, showProgress || mN.mLargeIcon != null);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003193
Selim Cinek29603462015-11-17 19:04:39 -08003194 return contentView;
3195 }
3196
Selim Cinek860b6da2015-12-16 19:02:19 -08003197 /**
3198 * @param remoteView the remote view to update the minheight in
3199 * @param hasMinHeight does it have a mimHeight
3200 * @hide
3201 */
3202 void setContentMinHeight(RemoteViews remoteView, boolean hasMinHeight) {
3203 int minHeight = 0;
3204 if (hasMinHeight) {
3205 // we need to set the minHeight of the notification
3206 minHeight = mContext.getResources().getDimensionPixelSize(
3207 com.android.internal.R.dimen.notification_min_content_height);
3208 }
3209 remoteView.setInt(R.id.notification_main_column, "setMinimumHeight", minHeight);
3210 }
3211
Selim Cinek29603462015-11-17 19:04:39 -08003212 private boolean handleProgressBar(boolean hasProgress, RemoteViews contentView, Bundle ex) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003213 final int max = ex.getInt(EXTRA_PROGRESS_MAX, 0);
3214 final int progress = ex.getInt(EXTRA_PROGRESS, 0);
3215 final boolean ind = ex.getBoolean(EXTRA_PROGRESS_INDETERMINATE);
3216 if (hasProgress && (max != 0 || ind)) {
Selim Cinek29603462015-11-17 19:04:39 -08003217 contentView.setViewVisibility(com.android.internal.R.id.progress, View.VISIBLE);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003218 contentView.setProgressBar(
Selim Cinek29603462015-11-17 19:04:39 -08003219 R.id.progress, max, progress, ind);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003220 contentView.setProgressBackgroundTintList(
3221 R.id.progress, ColorStateList.valueOf(mContext.getColor(
3222 R.color.notification_progress_background_color)));
Selim Cinek29603462015-11-17 19:04:39 -08003223 if (mN.color != COLOR_DEFAULT) {
Adrian Roos4ff3b122016-02-01 12:26:13 -08003224 ColorStateList colorStateList = ColorStateList.valueOf(resolveContrastColor());
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003225 contentView.setProgressTintList(R.id.progress, colorStateList);
3226 contentView.setProgressIndeterminateTintList(R.id.progress, colorStateList);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003227 }
Selim Cinek29603462015-11-17 19:04:39 -08003228 return true;
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003229 } else {
3230 contentView.setViewVisibility(R.id.progress, View.GONE);
Selim Cinek29603462015-11-17 19:04:39 -08003231 return false;
Jeff Sharkey1c400132011-08-05 14:50:13 -07003232 }
Joe Onorato561d3852010-11-20 18:09:34 -08003233 }
3234
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003235 private void bindLargeIcon(RemoteViews contentView) {
3236 if (mN.mLargeIcon != null) {
3237 contentView.setViewVisibility(R.id.right_icon, View.VISIBLE);
3238 contentView.setImageViewIcon(R.id.right_icon, mN.mLargeIcon);
3239 processLargeLegacyIcon(mN.mLargeIcon, contentView);
3240 int endMargin = mContext.getResources().getDimensionPixelSize(
3241 R.dimen.notification_content_picture_margin);
3242 contentView.setViewLayoutMarginEnd(R.id.line1, endMargin);
Selim Cinekc848c3a2016-01-13 15:27:30 -08003243 contentView.setViewLayoutMarginEnd(R.id.text, endMargin);
Selim Cinek29603462015-11-17 19:04:39 -08003244 contentView.setViewLayoutMarginEnd(R.id.progress, endMargin);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003245 }
3246 }
3247
3248 private void bindNotificationHeader(RemoteViews contentView) {
3249 bindSmallIcon(contentView);
3250 bindHeaderAppName(contentView);
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07003251 bindHeaderText(contentView);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003252 bindHeaderChronometerAndTime(contentView);
3253 bindExpandButton(contentView);
Selim Cinekc848c3a2016-01-13 15:27:30 -08003254 bindProfileBadge(contentView);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003255 }
3256
3257 private void bindExpandButton(RemoteViews contentView) {
Adrian Roos4ff3b122016-02-01 12:26:13 -08003258 contentView.setDrawableParameters(R.id.expand_button, false, -1, resolveContrastColor(),
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003259 PorterDuff.Mode.SRC_ATOP, -1);
Selim Cinekea4bef72015-12-02 15:51:10 -08003260 contentView.setInt(R.id.notification_header, "setOriginalNotificationColor",
Adrian Roos4ff3b122016-02-01 12:26:13 -08003261 resolveContrastColor());
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003262 }
3263
3264 private void bindHeaderChronometerAndTime(RemoteViews contentView) {
3265 if (showsTimeOrChronometer()) {
Selim Cinek29603462015-11-17 19:04:39 -08003266 contentView.setViewVisibility(R.id.time_divider, View.VISIBLE);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003267 if (mN.extras.getBoolean(EXTRA_SHOW_CHRONOMETER)) {
3268 contentView.setViewVisibility(R.id.chronometer, View.VISIBLE);
3269 contentView.setLong(R.id.chronometer, "setBase",
3270 mN.when + (SystemClock.elapsedRealtime() - System.currentTimeMillis()));
3271 contentView.setBoolean(R.id.chronometer, "setStarted", true);
Selim Cinek81c23aa2016-02-25 16:23:13 -08003272 boolean countsDown = mN.extras.getBoolean(EXTRA_CHRONOMETER_COUNTS_DOWN);
Selim Cinekc3b752e2016-04-20 16:13:59 -07003273 contentView.setChronometerCountDown(R.id.chronometer, countsDown);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003274 } else {
3275 contentView.setViewVisibility(R.id.time, View.VISIBLE);
3276 contentView.setLong(R.id.time, "setTime", mN.when);
3277 }
Selim Cinekb85f36fd2016-04-20 18:46:36 -07003278 } else {
3279 // We still want a time to be set but gone, such that we can show and hide it
3280 // on demand in case it's a child notification without anything in the header
3281 contentView.setLong(R.id.time, "setTime", mN.when != 0 ? mN.when : mN.creationTime);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003282 }
3283 }
3284
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07003285 private void bindHeaderText(RemoteViews contentView) {
3286 CharSequence headerText = mN.extras.getCharSequence(EXTRA_SUB_TEXT);
3287 if (headerText == null && mStyle != null && mStyle.mSummaryTextSet
Selim Cinek03d0d652015-11-13 13:18:09 -05003288 && mStyle.hasSummaryInHeader()) {
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07003289 headerText = mStyle.mSummaryText;
Selim Cinek03d0d652015-11-13 13:18:09 -05003290 }
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07003291 if (headerText == null
3292 && mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N
3293 && mN.extras.getCharSequence(EXTRA_INFO_TEXT) != null) {
3294 headerText = mN.extras.getCharSequence(EXTRA_INFO_TEXT);
3295 }
3296 if (headerText != null) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003297 // TODO: Remove the span entirely to only have the string with propper formating.
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07003298 contentView.setTextViewText(R.id.header_text, processLegacyText(headerText));
3299 contentView.setViewVisibility(R.id.header_text, View.VISIBLE);
3300 contentView.setViewVisibility(R.id.header_text_divider, View.VISIBLE);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003301 }
3302 }
3303
Dan Sandler732bd6c2016-04-12 14:20:32 -04003304 private String loadHeaderAppName() {
3305 CharSequence name = null;
3306 final PackageManager pm = mContext.getPackageManager();
3307 if (mN.extras.containsKey(EXTRA_SUBSTITUTE_APP_NAME)) {
3308 // only system packages which lump together a bunch of unrelated stuff
3309 // may substitute a different name to make the purpose of the
3310 // notification more clear. the correct package label should always
3311 // be accessible via SystemUI.
3312 final String pkg = mContext.getPackageName();
3313 final String subName = mN.extras.getString(EXTRA_SUBSTITUTE_APP_NAME);
3314 if (PackageManager.PERMISSION_GRANTED == pm.checkPermission(
3315 android.Manifest.permission.SUBSTITUTE_NOTIFICATION_APP_NAME, pkg)) {
3316 name = subName;
3317 } else {
3318 Log.w(TAG, "warning: pkg "
3319 + pkg + " attempting to substitute app name '" + subName
3320 + "' without holding perm "
3321 + android.Manifest.permission.SUBSTITUTE_NOTIFICATION_APP_NAME);
3322 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003323 }
Dan Sandler732bd6c2016-04-12 14:20:32 -04003324 if (TextUtils.isEmpty(name)) {
3325 name = pm.getApplicationLabel(mContext.getApplicationInfo());
3326 }
3327 if (TextUtils.isEmpty(name)) {
3328 // still nothing?
3329 return null;
3330 }
3331
3332 return String.valueOf(name);
3333 }
3334 private void bindHeaderAppName(RemoteViews contentView) {
3335 contentView.setTextViewText(R.id.app_name_text, loadHeaderAppName());
Adrian Roos4ff3b122016-02-01 12:26:13 -08003336 contentView.setTextColor(R.id.app_name_text, resolveContrastColor());
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003337 }
3338
3339 private void bindSmallIcon(RemoteViews contentView) {
3340 contentView.setImageViewIcon(R.id.icon, mN.mSmallIcon);
3341 processSmallIconColor(mN.mSmallIcon, contentView);
3342 }
3343
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003344 /**
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02003345 * @return true if the built notification will show the time or the chronometer; false
3346 * otherwise
3347 */
3348 private boolean showsTimeOrChronometer() {
Selim Cinekb85f36fd2016-04-20 18:46:36 -07003349 return mN.showsTimeOrChronometer();
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02003350 }
3351
Christoph Studerfe718432014-09-01 18:21:18 +02003352 private void resetStandardTemplateWithActions(RemoteViews big) {
Adrian Roos7052de52016-03-03 15:53:34 -08003353 big.setViewVisibility(R.id.actions_container, View.GONE);
Christoph Studerfe718432014-09-01 18:21:18 +02003354 big.setViewVisibility(R.id.actions, View.GONE);
Christoph Studerfe718432014-09-01 18:21:18 +02003355 big.removeAllViews(R.id.actions);
Adrian Roose458aa82015-12-08 16:17:19 -08003356
3357 big.setViewVisibility(R.id.notification_material_reply_container, View.GONE);
3358 big.setTextViewText(R.id.notification_material_reply_text_1, null);
3359
3360 big.setViewVisibility(R.id.notification_material_reply_text_2, View.GONE);
3361 big.setTextViewText(R.id.notification_material_reply_text_2, null);
3362 big.setViewVisibility(R.id.notification_material_reply_text_3, View.GONE);
3363 big.setTextViewText(R.id.notification_material_reply_text_3, null);
Christoph Studerfe718432014-09-01 18:21:18 +02003364 }
3365
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003366 private RemoteViews applyStandardTemplateWithActions(int layoutId) {
Adrian Roos48d746a2016-04-12 14:57:28 -07003367 final Bundle ex = mN.extras;
3368
3369 CharSequence title = processLegacyText(ex.getCharSequence(EXTRA_TITLE));
3370 CharSequence text = processLegacyText(ex.getCharSequence(EXTRA_TEXT));
3371 return applyStandardTemplateWithActions(layoutId, true /* hasProgress */, title, text);
3372 }
3373
3374 private RemoteViews applyStandardTemplateWithActions(int layoutId, boolean hasProgress,
3375 CharSequence title, CharSequence text) {
3376 RemoteViews big = applyStandardTemplate(layoutId, hasProgress, title, text);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003377
Christoph Studerfe718432014-09-01 18:21:18 +02003378 resetStandardTemplateWithActions(big);
3379
Adrian Roose458aa82015-12-08 16:17:19 -08003380 boolean validRemoteInput = false;
3381
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003382 int N = mActions.size();
3383 if (N > 0) {
Adrian Roos7052de52016-03-03 15:53:34 -08003384 big.setViewVisibility(R.id.actions_container, View.VISIBLE);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003385 big.setViewVisibility(R.id.actions, View.VISIBLE);
Daniel Sandler8680bf82012-05-15 16:52:52 -04003386 if (N>MAX_ACTION_BUTTONS) N=MAX_ACTION_BUTTONS;
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003387 for (int i=0; i<N; i++) {
Adrian Roose458aa82015-12-08 16:17:19 -08003388 Action action = mActions.get(i);
3389 validRemoteInput |= hasValidRemoteInput(action);
3390
3391 final RemoteViews button = generateActionButton(action);
Adrian Roos9b123cf2016-02-04 14:55:57 -08003392 if (i == N - 1) {
3393 button.setViewLayoutWidth(com.android.internal.R.id.action0,
3394 ViewGroup.LayoutParams.MATCH_PARENT);
3395 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003396 big.addView(R.id.actions, button);
3397 }
3398 }
Adrian Roose458aa82015-12-08 16:17:19 -08003399
3400 CharSequence[] replyText = mN.extras.getCharSequenceArray(EXTRA_REMOTE_INPUT_HISTORY);
3401 if (validRemoteInput && replyText != null
3402 && replyText.length > 0 && !TextUtils.isEmpty(replyText[0])) {
3403 big.setViewVisibility(R.id.notification_material_reply_container, View.VISIBLE);
3404 big.setTextViewText(R.id.notification_material_reply_text_1, replyText[0]);
3405
3406 if (replyText.length > 1 && !TextUtils.isEmpty(replyText[1])) {
3407 big.setViewVisibility(R.id.notification_material_reply_text_2, View.VISIBLE);
3408 big.setTextViewText(R.id.notification_material_reply_text_2, replyText[1]);
3409
3410 if (replyText.length > 2 && !TextUtils.isEmpty(replyText[2])) {
3411 big.setViewVisibility(
3412 R.id.notification_material_reply_text_3, View.VISIBLE);
3413 big.setTextViewText(R.id.notification_material_reply_text_3, replyText[2]);
3414 }
3415 }
3416 }
3417
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003418 return big;
3419 }
3420
Adrian Roose458aa82015-12-08 16:17:19 -08003421 private boolean hasValidRemoteInput(Action action) {
3422 if (TextUtils.isEmpty(action.title) || action.actionIntent == null) {
3423 // Weird actions
3424 return false;
3425 }
3426
3427 RemoteInput[] remoteInputs = action.getRemoteInputs();
3428 if (remoteInputs == null) {
3429 return false;
3430 }
3431
3432 for (RemoteInput r : remoteInputs) {
3433 CharSequence[] choices = r.getChoices();
3434 if (r.getAllowFreeFormInput() || (choices != null && choices.length != 0)) {
3435 return true;
3436 }
3437 }
3438 return false;
3439 }
3440
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003441 /**
3442 * Construct a RemoteViews for the final 1U notification layout. In order:
3443 * 1. Custom contentView from the caller
3444 * 2. Style's proposed content view
3445 * 3. Standard template view
3446 */
Julia Reynolds3b848122016-02-26 10:45:32 -05003447 public RemoteViews createContentView() {
Selim Cinek593610c2016-02-16 18:42:57 -08003448 if (mN.contentView != null && (mStyle == null || !mStyle.displayCustomViewInline())) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003449 return mN.contentView;
3450 } else if (mStyle != null) {
3451 final RemoteViews styleView = mStyle.makeContentView();
3452 if (styleView != null) {
3453 return styleView;
3454 }
Joe Onorato46439ce2010-11-19 13:56:21 -08003455 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003456 return applyStandardTemplate(getBaseLayoutResource());
Joe Onorato46439ce2010-11-19 13:56:21 -08003457 }
3458
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003459 /**
3460 * Construct a RemoteViews for the final big notification layout.
3461 */
Julia Reynolds3b848122016-02-26 10:45:32 -05003462 public RemoteViews createBigContentView() {
Selim Cinek850a8542015-11-11 11:48:36 -05003463 RemoteViews result = null;
Selim Cinek593610c2016-02-16 18:42:57 -08003464 if (mN.bigContentView != null
3465 && (mStyle == null || !mStyle.displayCustomViewInline())) {
Julia Reynolds089e3e42015-11-18 09:59:57 -05003466 return mN.bigContentView;
3467 } else if (mStyle != null) {
Selim Cinek850a8542015-11-11 11:48:36 -05003468 result = mStyle.makeBigContentView();
Selim Cinek90dcf6d2015-11-18 20:24:13 -08003469 hideLine1Text(result);
Selim Cinekcc10bfb2016-02-10 16:24:21 -08003470 } else if (mActions.size() != 0) {
3471 result = applyStandardTemplateWithActions(getBigBaseLayoutResource());
Selim Cinek850a8542015-11-11 11:48:36 -05003472 }
3473 adaptNotificationHeaderForBigContentView(result);
3474 return result;
3475 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003476
Selim Cinekeaa29ca2015-11-23 13:51:13 -08003477 /**
3478 * Construct a RemoteViews for the final notification header only
3479 *
3480 * @hide
3481 */
3482 public RemoteViews makeNotificationHeader() {
3483 RemoteViews header = new BuilderRemoteViews(mContext.getApplicationInfo(),
3484 R.layout.notification_template_header);
3485 resetNotificationHeader(header);
3486 bindNotificationHeader(header);
3487 return header;
3488 }
3489
Selim Cinek29603462015-11-17 19:04:39 -08003490 private void hideLine1Text(RemoteViews result) {
Selim Cinekcc10bfb2016-02-10 16:24:21 -08003491 if (result != null) {
3492 result.setViewVisibility(R.id.text_line_1, View.GONE);
3493 }
Selim Cinek29603462015-11-17 19:04:39 -08003494 }
3495
Selim Cinek850a8542015-11-11 11:48:36 -05003496 private void adaptNotificationHeaderForBigContentView(RemoteViews result) {
Selim Cinekcc10bfb2016-02-10 16:24:21 -08003497 if (result != null) {
3498 result.setBoolean(R.id.notification_header, "setExpanded", true);
3499 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003500 }
3501
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003502 /**
3503 * Construct a RemoteViews for the final heads-up notification layout.
3504 */
Julia Reynolds3b848122016-02-26 10:45:32 -05003505 public RemoteViews createHeadsUpContentView() {
Selim Cinek593610c2016-02-16 18:42:57 -08003506 if (mN.headsUpContentView != null
3507 && (mStyle == null || !mStyle.displayCustomViewInline())) {
Julia Reynolds089e3e42015-11-18 09:59:57 -05003508 return mN.headsUpContentView;
3509 } else if (mStyle != null) {
3510 final RemoteViews styleView = mStyle.makeHeadsUpContentView();
3511 if (styleView != null) {
3512 return styleView;
3513 }
3514 } else if (mActions.size() == 0) {
3515 return null;
3516 }
3517
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003518 return applyStandardTemplateWithActions(getBigBaseLayoutResource());
Chris Wren8fd39ec2014-02-27 17:43:26 -05003519 }
3520
Selim Cinek624c02db2015-12-14 21:00:02 -08003521 /**
3522 * Construct a RemoteViews for the display in public contexts like on the lockscreen.
3523 *
3524 * @hide
3525 */
3526 public RemoteViews makePublicContentView() {
3527 if (mN.publicVersion != null) {
3528 final Builder builder = recoverBuilder(mContext, mN.publicVersion);
Julia Reynolds3b848122016-02-26 10:45:32 -05003529 return builder.createContentView();
Selim Cinek624c02db2015-12-14 21:00:02 -08003530 }
3531 Bundle savedBundle = mN.extras;
3532 Style style = mStyle;
3533 mStyle = null;
3534 Icon largeIcon = mN.mLargeIcon;
3535 mN.mLargeIcon = null;
3536 Bundle publicExtras = new Bundle();
3537 publicExtras.putBoolean(EXTRA_SHOW_WHEN,
3538 savedBundle.getBoolean(EXTRA_SHOW_WHEN));
3539 publicExtras.putBoolean(EXTRA_SHOW_CHRONOMETER,
3540 savedBundle.getBoolean(EXTRA_SHOW_CHRONOMETER));
Selim Cinek81c23aa2016-02-25 16:23:13 -08003541 publicExtras.putBoolean(EXTRA_CHRONOMETER_COUNTS_DOWN,
3542 savedBundle.getBoolean(EXTRA_CHRONOMETER_COUNTS_DOWN));
Selim Cinek624c02db2015-12-14 21:00:02 -08003543 publicExtras.putCharSequence(EXTRA_TITLE,
3544 mContext.getString(R.string.notification_hidden_text));
3545 mN.extras = publicExtras;
3546 final RemoteViews publicView = applyStandardTemplate(getBaseLayoutResource());
3547 mN.extras = savedBundle;
3548 mN.mLargeIcon = largeIcon;
3549 mStyle = style;
3550 return publicView;
3551 }
3552
3553
Chris Wren8fd39ec2014-02-27 17:43:26 -05003554
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003555 private RemoteViews generateActionButton(Action action) {
Daniel Sandler8680bf82012-05-15 16:52:52 -04003556 final boolean tombstone = (action.actionIntent == null);
Selim Cinekf33b1112015-07-15 17:45:11 -07003557 RemoteViews button = new BuilderRemoteViews(mContext.getApplicationInfo(),
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003558 tombstone ? getActionTombstoneLayoutResource()
3559 : getActionLayoutResource());
Dan Sandler68079d52015-07-22 10:45:30 -04003560 final Icon ai = action.getIcon();
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003561 button.setTextViewText(R.id.action0, processLegacyText(action.title));
Daniel Sandler8680bf82012-05-15 16:52:52 -04003562 if (!tombstone) {
Daniel Sandlere5518842012-05-10 16:20:40 -04003563 button.setOnClickPendingIntent(R.id.action0, action.actionIntent);
Daniel Sandlere5518842012-05-10 16:20:40 -04003564 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003565 button.setContentDescription(R.id.action0, action.title);
Adrian Roosfe84e1f2015-11-04 15:55:39 -08003566 if (action.mRemoteInputs != null) {
3567 button.setRemoteInputs(R.id.action0, action.mRemoteInputs);
3568 }
3569 if (mN.color != COLOR_DEFAULT) {
Adrian Roos4ff3b122016-02-01 12:26:13 -08003570 button.setTextColor(R.id.action0, resolveContrastColor());
Adrian Roosfe84e1f2015-11-04 15:55:39 -08003571 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003572 return button;
3573 }
3574
Joe Onoratocb109a02011-01-18 17:57:41 -08003575 /**
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003576 * @return Whether we are currently building a notification from a legacy (an app that
Alan Viverette3cb07a462014-06-06 14:19:53 -07003577 * doesn't create material notifications by itself) app.
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003578 */
3579 private boolean isLegacy() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003580 return getColorUtil() != null;
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003581 }
3582
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003583 private CharSequence processLegacyText(CharSequence charSequence) {
3584 if (isLegacy()) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003585 return getColorUtil().invertCharSequenceColors(charSequence);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003586 } else {
3587 return charSequence;
3588 }
3589 }
3590
Dan Sandler26e81cf2014-05-06 10:01:27 -04003591 /**
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003592 * Apply any necessariy colors to the small icon
Dan Sandler26e81cf2014-05-06 10:01:27 -04003593 */
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003594 private void processSmallIconColor(Icon smallIcon, RemoteViews contentView) {
Selim Cinekea4bef72015-12-02 15:51:10 -08003595 boolean colorable = !isLegacy() || getColorUtil().isGrayscaleIcon(mContext, smallIcon);
3596 if (colorable) {
Adrian Roos4ff3b122016-02-01 12:26:13 -08003597 contentView.setDrawableParameters(R.id.icon, false, -1, resolveContrastColor(),
Jorim Jaggi92df1f22014-12-16 19:44:41 +01003598 PorterDuff.Mode.SRC_ATOP, -1);
Selim Cinekea4bef72015-12-02 15:51:10 -08003599
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003600 }
Selim Cinekea4bef72015-12-02 15:51:10 -08003601 contentView.setInt(R.id.notification_header, "setOriginalIconColor",
Adrian Roos4ff3b122016-02-01 12:26:13 -08003602 colorable ? resolveContrastColor() : NotificationHeaderView.NO_COLOR);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003603 }
3604
Dan Sandler26e81cf2014-05-06 10:01:27 -04003605 /**
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003606 * Make the largeIcon dark if it's a fake smallIcon (that is,
Dan Sandler26e81cf2014-05-06 10:01:27 -04003607 * if it's grayscale).
3608 */
3609 // TODO: also check bounds, transparency, that sort of thing.
Dan Sandlerd63f9322015-05-06 15:18:49 -04003610 private void processLargeLegacyIcon(Icon largeIcon, RemoteViews contentView) {
3611 if (largeIcon != null && isLegacy()
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003612 && getColorUtil().isGrayscaleIcon(mContext, largeIcon)) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003613 // resolve color will fall back to the default when legacy
Adrian Roos4ff3b122016-02-01 12:26:13 -08003614 contentView.setDrawableParameters(R.id.icon, false, -1, resolveContrastColor(),
Dan Sandler190d58d2014-05-15 09:33:39 -04003615 PorterDuff.Mode.SRC_ATOP, -1);
Jorim Jaggi92df1f22014-12-16 19:44:41 +01003616 }
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003617 }
3618
Julia Reynolds10ee1fc2015-11-09 11:04:55 -05003619 private void sanitizeColor() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003620 if (mN.color != COLOR_DEFAULT) {
3621 mN.color |= 0xFF000000; // no alpha for custom colors
Jorim Jaggi74419312014-06-10 20:57:21 +02003622 }
Jorim Jaggi74419312014-06-10 20:57:21 +02003623 }
3624
Adrian Roos4ff3b122016-02-01 12:26:13 -08003625 int resolveContrastColor() {
3626 if (mCachedContrastColorIsFor == mN.color && mCachedContrastColor != COLOR_INVALID) {
3627 return mCachedContrastColor;
Dan Sandler26e81cf2014-05-06 10:01:27 -04003628 }
Adrian Roos4ff3b122016-02-01 12:26:13 -08003629 final int contrasted = NotificationColorUtil.resolveContrastColor(mContext, mN.color);
3630
3631 mCachedContrastColorIsFor = mN.color;
3632 return mCachedContrastColor = contrasted;
Dan Sandler26e81cf2014-05-06 10:01:27 -04003633 }
3634
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003635 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003636 * Apply the unstyled operations and return a new {@link Notification} object.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04003637 * @hide
Joe Onoratocb109a02011-01-18 17:57:41 -08003638 */
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04003639 public Notification buildUnstyled() {
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003640 if (mActions.size() > 0) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003641 mN.actions = new Action[mActions.size()];
3642 mActions.toArray(mN.actions);
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003643 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003644 if (!mPersonList.isEmpty()) {
3645 mN.extras.putStringArray(EXTRA_PEOPLE,
3646 mPersonList.toArray(new String[mPersonList.size()]));
Dan Sandler0bf2ed82013-12-21 23:33:41 -06003647 }
Selim Cinek247fa012016-02-18 09:50:48 -08003648 if (mN.bigContentView != null || mN.contentView != null
3649 || mN.headsUpContentView != null) {
3650 mN.extras.putBoolean(EXTRA_CONTAINS_CUSTOM_VIEW, true);
3651 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003652 return mN;
Joe Onorato46439ce2010-11-19 13:56:21 -08003653 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003654
Julia Reynolds3b848122016-02-26 10:45:32 -05003655 /**
3656 * Creates a Builder from an existing notification so further changes can be made.
3657 * @param context The context for your application / activity.
3658 * @param n The notification to create a Builder from.
3659 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003660 public static Notification.Builder recoverBuilder(Context context, Notification n) {
Christoph Studer4600f9b2014-07-22 22:44:43 +02003661 // Re-create notification context so we can access app resources.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003662 ApplicationInfo applicationInfo = n.extras.getParcelable(
3663 EXTRA_BUILDER_APPLICATION_INFO);
Christoph Studer4600f9b2014-07-22 22:44:43 +02003664 Context builderContext;
Julia Reynoldsda303542015-11-23 14:00:20 -05003665 if (applicationInfo != null) {
3666 try {
3667 builderContext = context.createApplicationContext(applicationInfo,
3668 Context.CONTEXT_RESTRICTED);
3669 } catch (NameNotFoundException e) {
3670 Log.e(TAG, "ApplicationInfo " + applicationInfo + " not found");
3671 builderContext = context; // try with our context
3672 }
3673 } else {
3674 builderContext = context; // try with given context
Christoph Studer4600f9b2014-07-22 22:44:43 +02003675 }
3676
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003677 return new Builder(builderContext, n);
Christoph Studer4600f9b2014-07-22 22:44:43 +02003678 }
3679
3680 private static Class<? extends Style> getNotificationStyleClass(String templateClass) {
Selim Cinek593610c2016-02-16 18:42:57 -08003681 Class<? extends Style>[] classes = new Class[] {
3682 BigTextStyle.class, BigPictureStyle.class, InboxStyle.class, MediaStyle.class,
Alex Hillsfc737de2016-03-23 17:33:02 -04003683 DecoratedCustomViewStyle.class, DecoratedMediaCustomViewStyle.class,
3684 MessagingStyle.class };
Christoph Studer4600f9b2014-07-22 22:44:43 +02003685 for (Class<? extends Style> innerClass : classes) {
3686 if (templateClass.equals(innerClass.getName())) {
3687 return innerClass;
3688 }
3689 }
3690 return null;
3691 }
3692
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003693 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003694 * @deprecated Use {@link #build()} instead.
3695 */
3696 @Deprecated
3697 public Notification getNotification() {
3698 return build();
3699 }
3700
3701 /**
3702 * Combine all of the options that have been set and return a new {@link Notification}
3703 * object.
3704 */
3705 public Notification build() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003706 // first, add any extras from the calling code
3707 if (mUserExtras != null) {
3708 mN.extras = getAllExtras();
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07003709 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003710
Selim Cinekb85f36fd2016-04-20 18:46:36 -07003711 mN.creationTime = System.currentTimeMillis();
3712
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003713 // lazy stuff from mContext; see comment in Builder(Context, Notification)
Julia Reynoldsda303542015-11-23 14:00:20 -05003714 Notification.addFieldsFromContext(mContext, mN);
Christoph Studer943aa672014-08-03 20:31:16 +02003715
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003716 buildUnstyled();
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003717
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003718 if (mStyle != null) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003719 mStyle.buildStyled(mN);
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003720 }
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003721
Adrian Roos5081c0d2016-02-26 16:04:19 -08003722 if (mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N
3723 && (mStyle == null || !mStyle.displayCustomViewInline())) {
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05003724 if (mN.contentView == null) {
Julia Reynolds3b848122016-02-26 10:45:32 -05003725 mN.contentView = createContentView();
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05003726 mN.extras.putInt(EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT,
3727 mN.contentView.getSequenceNumber());
3728 }
3729 if (mN.bigContentView == null) {
Julia Reynolds3b848122016-02-26 10:45:32 -05003730 mN.bigContentView = createBigContentView();
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05003731 if (mN.bigContentView != null) {
3732 mN.extras.putInt(EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT,
3733 mN.bigContentView.getSequenceNumber());
3734 }
3735 }
3736 if (mN.headsUpContentView == null) {
Julia Reynolds3b848122016-02-26 10:45:32 -05003737 mN.headsUpContentView = createHeadsUpContentView();
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05003738 if (mN.headsUpContentView != null) {
3739 mN.extras.putInt(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT,
3740 mN.headsUpContentView.getSequenceNumber());
3741 }
3742 }
3743 }
3744
Julia Reynolds4c0c2022016-02-02 15:11:59 -05003745 if ((mN.defaults & DEFAULT_LIGHTS) != 0) {
3746 mN.flags |= FLAG_SHOW_LIGHTS;
3747 }
3748
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003749 return mN;
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003750 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05003751
3752 /**
3753 * Apply this Builder to an existing {@link Notification} object.
3754 *
3755 * @hide
3756 */
3757 public Notification buildInto(Notification n) {
Daniel Sandler1a497d32013-04-18 14:52:45 -04003758 build().cloneInto(n, true);
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05003759 return n;
3760 }
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003761
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05003762 /**
Adrian Roos184bfe022016-03-03 13:41:44 -08003763 * Removes RemoteViews that were created for compatibility from {@param n}, if they did not
3764 * change.
3765 *
3766 * @return {@param n}, if no stripping is needed, otherwise a stripped clone of {@param n}.
3767 *
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05003768 * @hide
3769 */
Adrian Roos184bfe022016-03-03 13:41:44 -08003770 public static Notification maybeCloneStrippedForDelivery(Notification n) {
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05003771 String templateClass = n.extras.getString(EXTRA_TEMPLATE);
Adrian Roos184bfe022016-03-03 13:41:44 -08003772
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05003773 // Only strip views for known Styles because we won't know how to
3774 // re-create them otherwise.
Adrian Roos184bfe022016-03-03 13:41:44 -08003775 if (!TextUtils.isEmpty(templateClass)
3776 && getNotificationStyleClass(templateClass) == null) {
3777 return n;
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05003778 }
Adrian Roos184bfe022016-03-03 13:41:44 -08003779
3780 // Only strip unmodified BuilderRemoteViews.
3781 boolean stripContentView = n.contentView instanceof BuilderRemoteViews &&
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05003782 n.extras.getInt(EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT, -1) ==
Adrian Roos184bfe022016-03-03 13:41:44 -08003783 n.contentView.getSequenceNumber();
3784 boolean stripBigContentView = n.bigContentView instanceof BuilderRemoteViews &&
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05003785 n.extras.getInt(EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT, -1) ==
Adrian Roos184bfe022016-03-03 13:41:44 -08003786 n.bigContentView.getSequenceNumber();
3787 boolean stripHeadsUpContentView = n.headsUpContentView instanceof BuilderRemoteViews &&
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05003788 n.extras.getInt(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT, -1) ==
Adrian Roos184bfe022016-03-03 13:41:44 -08003789 n.headsUpContentView.getSequenceNumber();
3790
3791 // Nothing to do here, no need to clone.
3792 if (!stripContentView && !stripBigContentView && !stripHeadsUpContentView) {
3793 return n;
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05003794 }
Adrian Roos184bfe022016-03-03 13:41:44 -08003795
3796 Notification clone = n.clone();
3797 if (stripContentView) {
3798 clone.contentView = null;
3799 clone.extras.remove(EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT);
3800 }
3801 if (stripBigContentView) {
3802 clone.bigContentView = null;
3803 clone.extras.remove(EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT);
3804 }
3805 if (stripHeadsUpContentView) {
3806 clone.headsUpContentView = null;
3807 clone.extras.remove(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT);
3808 }
3809 return clone;
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05003810 }
3811
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003812 private int getBaseLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07003813 return R.layout.notification_template_material_base;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003814 }
3815
3816 private int getBigBaseLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07003817 return R.layout.notification_template_material_big_base;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003818 }
3819
3820 private int getBigPictureLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07003821 return R.layout.notification_template_material_big_picture;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003822 }
3823
3824 private int getBigTextLayoutResource() {
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003825 return R.layout.notification_template_material_big_text;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003826 }
3827
3828 private int getInboxLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07003829 return R.layout.notification_template_material_inbox;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003830 }
3831
Adrian Roosc1a80b02016-04-05 14:54:55 -07003832 private int getMessagingLayoutResource() {
3833 return R.layout.notification_template_material_messaging;
3834 }
3835
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003836 private int getActionLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07003837 return R.layout.notification_material_action;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003838 }
3839
3840 private int getActionTombstoneLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07003841 return R.layout.notification_material_action_tombstone;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003842 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003843 }
3844
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003845 /**
Selim Cinekb85f36fd2016-04-20 18:46:36 -07003846 * @return true if the notification will show the time or the chronometer; false
3847 * otherwise
3848 * @hide
3849 */
3850 public boolean showsTimeOrChronometer() {
3851 return when != 0 && extras.getBoolean(EXTRA_SHOW_WHEN);
3852 }
3853
3854 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003855 * An object that can apply a rich notification style to a {@link Notification.Builder}
3856 * object.
3857 */
Griff Hazendfcb0802014-02-11 12:00:00 -08003858 public static abstract class Style {
Chris Wrend6297db2012-05-03 16:20:13 -04003859 private CharSequence mBigContentTitle;
Jorim Jaggi457a10d2014-09-08 16:18:23 +02003860
3861 /**
3862 * @hide
3863 */
3864 protected CharSequence mSummaryText = null;
3865
3866 /**
3867 * @hide
3868 */
3869 protected boolean mSummaryTextSet = false;
Chris Wrend6297db2012-05-03 16:20:13 -04003870
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003871 protected Builder mBuilder;
3872
Chris Wrend6297db2012-05-03 16:20:13 -04003873 /**
3874 * Overrides ContentTitle in the big form of the template.
3875 * This defaults to the value passed to setContentTitle().
3876 */
3877 protected void internalSetBigContentTitle(CharSequence title) {
3878 mBigContentTitle = title;
3879 }
3880
3881 /**
3882 * Set the first line of text after the detail section in the big form of the template.
3883 */
3884 protected void internalSetSummaryText(CharSequence cs) {
3885 mSummaryText = cs;
Daniel Sandler619738c2012-06-07 16:33:08 -04003886 mSummaryTextSet = true;
Chris Wrend6297db2012-05-03 16:20:13 -04003887 }
3888
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003889 public void setBuilder(Builder builder) {
3890 if (mBuilder != builder) {
3891 mBuilder = builder;
Daniel Sandlerc08dea22012-06-28 08:35:24 -07003892 if (mBuilder != null) {
3893 mBuilder.setStyle(this);
3894 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003895 }
3896 }
3897
Chris Wrend6297db2012-05-03 16:20:13 -04003898 protected void checkBuilder() {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003899 if (mBuilder == null) {
3900 throw new IllegalArgumentException("Style requires a valid Builder object");
3901 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003902 }
Chris Wrend6297db2012-05-03 16:20:13 -04003903
3904 protected RemoteViews getStandardView(int layoutId) {
3905 checkBuilder();
3906
Christoph Studer4600f9b2014-07-22 22:44:43 +02003907 // Nasty.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003908 CharSequence oldBuilderContentTitle =
3909 mBuilder.getAllExtras().getCharSequence(EXTRA_TITLE);
Chris Wrend6297db2012-05-03 16:20:13 -04003910 if (mBigContentTitle != null) {
3911 mBuilder.setContentTitle(mBigContentTitle);
3912 }
3913
Chris Wrend6297db2012-05-03 16:20:13 -04003914 RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(layoutId);
3915
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003916 mBuilder.getAllExtras().putCharSequence(EXTRA_TITLE, oldBuilderContentTitle);
Christoph Studer4600f9b2014-07-22 22:44:43 +02003917
Chris Wrend6297db2012-05-03 16:20:13 -04003918 if (mBigContentTitle != null && mBigContentTitle.equals("")) {
3919 contentView.setViewVisibility(R.id.line1, View.GONE);
Chris Wren67dc9a02012-05-16 01:03:20 -04003920 } else {
3921 contentView.setViewVisibility(R.id.line1, View.VISIBLE);
Chris Wrend6297db2012-05-03 16:20:13 -04003922 }
3923
Chris Wrend6297db2012-05-03 16:20:13 -04003924 return contentView;
3925 }
3926
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003927 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003928 * Construct a Style-specific RemoteViews for the final 1U notification layout.
3929 * The default implementation has nothing additional to add.
3930 * @hide
3931 */
3932 public RemoteViews makeContentView() {
3933 return null;
3934 }
3935
3936 /**
3937 * Construct a Style-specific RemoteViews for the final big notification layout.
3938 * @hide
3939 */
3940 public RemoteViews makeBigContentView() {
3941 return null;
3942 }
3943
3944 /**
3945 * Construct a Style-specific RemoteViews for the final HUN layout.
3946 * @hide
3947 */
3948 public RemoteViews makeHeadsUpContentView() {
3949 return null;
3950 }
3951
3952 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003953 * Apply any style-specific extras to this notification before shipping it out.
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003954 * @hide
3955 */
3956 public void addExtras(Bundle extras) {
3957 if (mSummaryTextSet) {
3958 extras.putCharSequence(EXTRA_SUMMARY_TEXT, mSummaryText);
3959 }
3960 if (mBigContentTitle != null) {
3961 extras.putCharSequence(EXTRA_TITLE_BIG, mBigContentTitle);
3962 }
Chris Wren91ad5632013-06-05 15:05:57 -04003963 extras.putString(EXTRA_TEMPLATE, this.getClass().getName());
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003964 }
3965
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04003966 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003967 * Reconstruct the internal state of this Style object from extras.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04003968 * @hide
3969 */
Christoph Studer4600f9b2014-07-22 22:44:43 +02003970 protected void restoreFromExtras(Bundle extras) {
3971 if (extras.containsKey(EXTRA_SUMMARY_TEXT)) {
3972 mSummaryText = extras.getCharSequence(EXTRA_SUMMARY_TEXT);
3973 mSummaryTextSet = true;
3974 }
3975 if (extras.containsKey(EXTRA_TITLE_BIG)) {
3976 mBigContentTitle = extras.getCharSequence(EXTRA_TITLE_BIG);
3977 }
3978 }
3979
3980
3981 /**
3982 * @hide
3983 */
3984 public Notification buildStyled(Notification wip) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003985 addExtras(wip.extras);
Christoph Studer4600f9b2014-07-22 22:44:43 +02003986 return wip;
3987 }
3988
Daniel Sandler0ec46202015-06-24 01:27:05 -04003989 /**
3990 * @hide
3991 */
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07003992 public void purgeResources() {}
3993
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04003994 /**
3995 * Calls {@link android.app.Notification.Builder#build()} on the Builder this Style is
3996 * attached to.
3997 *
3998 * @return the fully constructed Notification.
3999 */
4000 public Notification build() {
4001 checkBuilder();
4002 return mBuilder.build();
4003 }
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004004
4005 /**
4006 * @hide
4007 * @return true if the style positions the progress bar on the second line; false if the
4008 * style hides the progress bar
4009 */
4010 protected boolean hasProgress() {
4011 return true;
4012 }
Selim Cinek03d0d652015-11-13 13:18:09 -05004013
4014 /**
4015 * @hide
4016 * @return Whether we should put the summary be put into the notification header
4017 */
4018 public boolean hasSummaryInHeader() {
4019 return true;
4020 }
Selim Cinek593610c2016-02-16 18:42:57 -08004021
4022 /**
4023 * @hide
4024 * @return Whether custom content views are displayed inline in the style
4025 */
4026 public boolean displayCustomViewInline() {
4027 return false;
4028 }
Joe Onorato46439ce2010-11-19 13:56:21 -08004029 }
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004030
4031 /**
Daniel Sandler4dfbe832012-04-11 14:51:46 -04004032 * Helper class for generating large-format notifications that include a large image attachment.
Joe Malin8d40d042012-11-05 11:36:40 -08004033 *
Robert Ly91c5ce32014-06-08 15:37:00 -07004034 * Here's how you'd set the <code>BigPictureStyle</code> on a notification:
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004035 * <pre class="prettyprint">
Robert Ly91c5ce32014-06-08 15:37:00 -07004036 * Notification notif = new Notification.Builder(mContext)
4037 * .setContentTitle(&quot;New photo from &quot; + sender.toString())
4038 * .setContentText(subject)
4039 * .setSmallIcon(R.drawable.new_post)
4040 * .setLargeIcon(aBitmap)
4041 * .setStyle(new Notification.BigPictureStyle()
4042 * .bigPicture(aBigBitmap))
4043 * .build();
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004044 * </pre>
Joe Malin8d40d042012-11-05 11:36:40 -08004045 *
Daniel Sandler4dfbe832012-04-11 14:51:46 -04004046 * @see Notification#bigContentView
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004047 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004048 public static class BigPictureStyle extends Style {
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004049 private Bitmap mPicture;
Dan Sandlerd63f9322015-05-06 15:18:49 -04004050 private Icon mBigLargeIcon;
Chris Wren3745a3d2012-05-22 15:11:52 -04004051 private boolean mBigLargeIconSet = false;
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004052
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004053 public BigPictureStyle() {
4054 }
4055
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004056 public BigPictureStyle(Builder builder) {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004057 setBuilder(builder);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004058 }
4059
Chris Wrend6297db2012-05-03 16:20:13 -04004060 /**
4061 * Overrides ContentTitle in the big form of the template.
4062 * This defaults to the value passed to setContentTitle().
4063 */
4064 public BigPictureStyle setBigContentTitle(CharSequence title) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04004065 internalSetBigContentTitle(safeCharSequence(title));
Chris Wrend6297db2012-05-03 16:20:13 -04004066 return this;
4067 }
4068
4069 /**
4070 * Set the first line of text after the detail section in the big form of the template.
4071 */
4072 public BigPictureStyle setSummaryText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04004073 internalSetSummaryText(safeCharSequence(cs));
Chris Wrend6297db2012-05-03 16:20:13 -04004074 return this;
4075 }
4076
Chris Wren0bd664d2012-08-01 13:56:56 -04004077 /**
4078 * Provide the bitmap to be used as the payload for the BigPicture notification.
4079 */
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004080 public BigPictureStyle bigPicture(Bitmap b) {
4081 mPicture = b;
4082 return this;
4083 }
4084
Chris Wren3745a3d2012-05-22 15:11:52 -04004085 /**
Chris Wren3745a3d2012-05-22 15:11:52 -04004086 * Override the large icon when the big notification is shown.
4087 */
4088 public BigPictureStyle bigLargeIcon(Bitmap b) {
Dan Sandlerd63f9322015-05-06 15:18:49 -04004089 return bigLargeIcon(b != null ? Icon.createWithBitmap(b) : null);
4090 }
4091
4092 /**
4093 * Override the large icon when the big notification is shown.
4094 */
4095 public BigPictureStyle bigLargeIcon(Icon icon) {
Chris Wren3745a3d2012-05-22 15:11:52 -04004096 mBigLargeIconSet = true;
Dan Sandlerd63f9322015-05-06 15:18:49 -04004097 mBigLargeIcon = icon;
Chris Wren3745a3d2012-05-22 15:11:52 -04004098 return this;
4099 }
4100
Riley Andrews0394a0c2015-11-03 23:36:52 -08004101 /** @hide */
4102 public static final int MIN_ASHMEM_BITMAP_SIZE = 128 * (1 << 10);
4103
Daniel Sandler0ec46202015-06-24 01:27:05 -04004104 /**
4105 * @hide
4106 */
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07004107 @Override
4108 public void purgeResources() {
4109 super.purgeResources();
Riley Andrews8cee7c12015-11-01 23:36:04 -08004110 if (mPicture != null &&
4111 mPicture.isMutable() &&
Riley Andrews0394a0c2015-11-03 23:36:52 -08004112 mPicture.getAllocationByteCount() >= MIN_ASHMEM_BITMAP_SIZE) {
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07004113 mPicture = mPicture.createAshmemBitmap();
4114 }
4115 if (mBigLargeIcon != null) {
4116 mBigLargeIcon.convertToAshmem();
4117 }
4118 }
Christoph Studer5c510ee2014-12-15 16:32:27 +01004119
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004120 /**
4121 * @hide
4122 */
4123 public RemoteViews makeBigContentView() {
4124 // Replace mN.mLargeIcon with mBigLargeIcon if mBigLargeIconSet
Christoph Studer5c510ee2014-12-15 16:32:27 +01004125 // This covers the following cases:
4126 // 1. mBigLargeIconSet -> mBigLargeIcon (null or non-null) applies, overrides
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004127 // mN.mLargeIcon
4128 // 2. !mBigLargeIconSet -> mN.mLargeIcon applies
Dan Sandlerd63f9322015-05-06 15:18:49 -04004129 Icon oldLargeIcon = null;
Christoph Studer5c510ee2014-12-15 16:32:27 +01004130 if (mBigLargeIconSet) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004131 oldLargeIcon = mBuilder.mN.mLargeIcon;
4132 mBuilder.mN.mLargeIcon = mBigLargeIcon;
Christoph Studer5c510ee2014-12-15 16:32:27 +01004133 }
4134
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004135 RemoteViews contentView = getStandardView(mBuilder.getBigPictureLayoutResource());
Selim Cinek03d0d652015-11-13 13:18:09 -05004136 if (mSummaryTextSet) {
4137 contentView.setTextViewText(R.id.text, mBuilder.processLegacyText(mSummaryText));
Selim Cinekc848c3a2016-01-13 15:27:30 -08004138 contentView.setViewVisibility(R.id.text, View.VISIBLE);
Selim Cinek03d0d652015-11-13 13:18:09 -05004139 }
Selim Cinek860b6da2015-12-16 19:02:19 -08004140 mBuilder.setContentMinHeight(contentView, mBuilder.mN.mLargeIcon != null);
Selim Cinek53e64a42015-11-16 10:40:56 -08004141
Christoph Studer5c510ee2014-12-15 16:32:27 +01004142 if (mBigLargeIconSet) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004143 mBuilder.mN.mLargeIcon = oldLargeIcon;
Christoph Studer5c510ee2014-12-15 16:32:27 +01004144 }
4145
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004146 contentView.setImageViewBitmap(R.id.big_picture, mPicture);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004147 return contentView;
4148 }
4149
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004150 /**
4151 * @hide
4152 */
4153 public void addExtras(Bundle extras) {
4154 super.addExtras(extras);
4155
4156 if (mBigLargeIconSet) {
4157 extras.putParcelable(EXTRA_LARGE_ICON_BIG, mBigLargeIcon);
4158 }
4159 extras.putParcelable(EXTRA_PICTURE, mPicture);
4160 }
4161
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04004162 /**
4163 * @hide
4164 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004165 @Override
Christoph Studer4600f9b2014-07-22 22:44:43 +02004166 protected void restoreFromExtras(Bundle extras) {
4167 super.restoreFromExtras(extras);
4168
4169 if (extras.containsKey(EXTRA_LARGE_ICON_BIG)) {
Christoph Studer5c510ee2014-12-15 16:32:27 +01004170 mBigLargeIconSet = true;
Christoph Studer4600f9b2014-07-22 22:44:43 +02004171 mBigLargeIcon = extras.getParcelable(EXTRA_LARGE_ICON_BIG);
Chris Wren3745a3d2012-05-22 15:11:52 -04004172 }
Christoph Studer4600f9b2014-07-22 22:44:43 +02004173 mPicture = extras.getParcelable(EXTRA_PICTURE);
4174 }
Selim Cinek03d0d652015-11-13 13:18:09 -05004175
4176 /**
4177 * @hide
4178 */
4179 @Override
4180 public boolean hasSummaryInHeader() {
4181 return false;
4182 }
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004183 }
4184
4185 /**
Daniel Sandler4dfbe832012-04-11 14:51:46 -04004186 * Helper class for generating large-format notifications that include a lot of text.
Joe Malin8d40d042012-11-05 11:36:40 -08004187 *
Robert Ly91c5ce32014-06-08 15:37:00 -07004188 * Here's how you'd set the <code>BigTextStyle</code> on a notification:
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004189 * <pre class="prettyprint">
Robert Ly91c5ce32014-06-08 15:37:00 -07004190 * Notification notif = new Notification.Builder(mContext)
4191 * .setContentTitle(&quot;New mail from &quot; + sender.toString())
4192 * .setContentText(subject)
4193 * .setSmallIcon(R.drawable.new_mail)
4194 * .setLargeIcon(aBitmap)
4195 * .setStyle(new Notification.BigTextStyle()
4196 * .bigText(aVeryLongString))
4197 * .build();
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004198 * </pre>
Joe Malin8d40d042012-11-05 11:36:40 -08004199 *
Daniel Sandler4dfbe832012-04-11 14:51:46 -04004200 * @see Notification#bigContentView
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004201 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004202 public static class BigTextStyle extends Style {
Jorim Jaggi457a10d2014-09-08 16:18:23 +02004203
4204 private static final int MAX_LINES = 13;
Selim Cinek3a2c4b92015-12-17 17:01:17 -08004205 private static final int LINES_CONSUMED_BY_ACTIONS = 4;
Jorim Jaggi457a10d2014-09-08 16:18:23 +02004206
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004207 private CharSequence mBigText;
4208
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004209 public BigTextStyle() {
4210 }
4211
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004212 public BigTextStyle(Builder builder) {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004213 setBuilder(builder);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004214 }
4215
Chris Wrend6297db2012-05-03 16:20:13 -04004216 /**
4217 * Overrides ContentTitle in the big form of the template.
4218 * This defaults to the value passed to setContentTitle().
4219 */
4220 public BigTextStyle setBigContentTitle(CharSequence title) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04004221 internalSetBigContentTitle(safeCharSequence(title));
Chris Wrend6297db2012-05-03 16:20:13 -04004222 return this;
4223 }
4224
4225 /**
4226 * Set the first line of text after the detail section in the big form of the template.
4227 */
4228 public BigTextStyle setSummaryText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04004229 internalSetSummaryText(safeCharSequence(cs));
Chris Wrend6297db2012-05-03 16:20:13 -04004230 return this;
4231 }
4232
Chris Wren0bd664d2012-08-01 13:56:56 -04004233 /**
4234 * Provide the longer text to be displayed in the big form of the
4235 * template in place of the content text.
4236 */
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004237 public BigTextStyle bigText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04004238 mBigText = safeCharSequence(cs);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004239 return this;
4240 }
4241
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004242 /**
4243 * @hide
4244 */
4245 public void addExtras(Bundle extras) {
4246 super.addExtras(extras);
4247
Christoph Studer4600f9b2014-07-22 22:44:43 +02004248 extras.putCharSequence(EXTRA_BIG_TEXT, mBigText);
4249 }
4250
4251 /**
4252 * @hide
4253 */
4254 @Override
4255 protected void restoreFromExtras(Bundle extras) {
4256 super.restoreFromExtras(extras);
4257
4258 mBigText = extras.getCharSequence(EXTRA_BIG_TEXT);
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004259 }
4260
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004261 /**
4262 * @hide
4263 */
4264 public RemoteViews makeBigContentView() {
Christoph Studer4600f9b2014-07-22 22:44:43 +02004265
4266 // Nasty
Selim Cinek75998782016-04-26 10:39:17 -07004267 CharSequence text = mBuilder.getAllExtras().getCharSequence(EXTRA_TEXT);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004268 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, null);
Daniel Sandler916ad912012-06-13 12:17:07 -04004269
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004270 RemoteViews contentView = getStandardView(mBuilder.getBigTextLayoutResource());
Joe Malin8d40d042012-11-05 11:36:40 -08004271
Selim Cinek75998782016-04-26 10:39:17 -07004272 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, text);
Christoph Studer4600f9b2014-07-22 22:44:43 +02004273
Selim Cinek3a2c4b92015-12-17 17:01:17 -08004274 CharSequence bigTextText = mBuilder.processLegacyText(mBigText);
Selim Cinek75998782016-04-26 10:39:17 -07004275 if (TextUtils.isEmpty(bigTextText)) {
4276 // In case the bigtext is null / empty fall back to the normal text to avoid a weird
4277 // experience
4278 bigTextText = mBuilder.processLegacyText(text);
4279 }
Selim Cinek3a2c4b92015-12-17 17:01:17 -08004280 contentView.setTextViewText(R.id.big_text, bigTextText);
4281 contentView.setViewVisibility(R.id.big_text,
4282 TextUtils.isEmpty(bigTextText) ? View.GONE : View.VISIBLE);
Jorim Jaggi457a10d2014-09-08 16:18:23 +02004283 contentView.setInt(R.id.big_text, "setMaxLines", calculateMaxLines());
Selim Cinek4fb12d32015-11-19 18:10:48 -08004284 contentView.setBoolean(R.id.big_text, "setHasImage", mBuilder.mN.mLargeIcon != null);
4285
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004286 return contentView;
4287 }
4288
Jorim Jaggi457a10d2014-09-08 16:18:23 +02004289 private int calculateMaxLines() {
4290 int lineCount = MAX_LINES;
4291 boolean hasActions = mBuilder.mActions.size() > 0;
Jorim Jaggi457a10d2014-09-08 16:18:23 +02004292 if (hasActions) {
4293 lineCount -= LINES_CONSUMED_BY_ACTIONS;
4294 }
Jorim Jaggi457a10d2014-09-08 16:18:23 +02004295 return lineCount;
4296 }
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004297 }
Daniel Sandler879c5e02012-04-17 16:46:51 -04004298
4299 /**
Alex Hillsfc737de2016-03-23 17:33:02 -04004300 * Helper class for generating large-format notifications that include multiple back-and-forth
4301 * messages of varying types between any number of people.
4302 *
4303 * <br>
4304 * If the platform does not provide large-format notifications, this method has no effect. The
4305 * user will always see the normal notification view.
4306 * <br>
4307 * This class is a "rebuilder": It attaches to a Builder object and modifies its behavior, like
4308 * so:
4309 * <pre class="prettyprint">
4310 *
4311 * Notification noti = new Notification.Builder()
4312 * .setContentTitle(&quot;2 new messages wtih &quot; + sender.toString())
4313 * .setContentText(subject)
4314 * .setSmallIcon(R.drawable.new_message)
4315 * .setLargeIcon(aBitmap)
4316 * .setStyle(new Notification.MessagingStyle(resources.getString(R.string.reply_name))
4317 * .addMessage(messages[0].getText(), messages[0].getTime(), messages[0].getSender())
4318 * .addMessage(messages[1].getText(), messages[1].getTime(), messages[1].getSender()))
4319 * .build();
4320 * </pre>
4321 */
4322 public static class MessagingStyle extends Style {
4323
4324 /**
4325 * The maximum number of messages that will be retained in the Notification itself (the
4326 * number displayed is up to the platform).
4327 */
4328 public static final int MAXIMUM_RETAINED_MESSAGES = 25;
4329
4330 CharSequence mUserDisplayName;
4331 CharSequence mConversationTitle;
4332 boolean mAllowGeneratedReplies = true;
4333 ArrayList<Message> mMessages = new ArrayList<>();
4334
4335 MessagingStyle() {
4336 }
4337
4338 /**
4339 * @param userDisplayName the name to be displayed for any replies sent by the user before the
4340 * posting app reposts the notification with those messages after they've been actually
4341 * sent and in previous messages sent by the user added in
4342 * {@link #addMessage(Notification.MessagingStyle.Message)}
4343 */
4344 public MessagingStyle(CharSequence userDisplayName) {
4345 mUserDisplayName = userDisplayName;
4346 }
4347
4348 /**
4349 * Returns the name to be displayed for any replies sent by the user
4350 */
4351 public CharSequence getUserDisplayName() {
4352 return mUserDisplayName;
4353 }
4354
4355 /**
4356 * Set whether the platform should automatically generate possible replies from messages.
4357 * @param allowGeneratedReplies {@code true} to allow generated replies, {@code false}
4358 * otherwise
4359 * @return this object for method chaining
4360 * The default value is {@code true}
4361 */
4362 public MessagingStyle setAllowGeneratedReplies(boolean allowGeneratedReplies) {
4363 mAllowGeneratedReplies = allowGeneratedReplies;
4364 return this;
4365 }
4366
4367 /**
4368 * Return whether the platform should automatically generate possible replies from messages.
4369 */
4370 public boolean getAllowGeneratedReplies() {
4371 return mAllowGeneratedReplies;
4372 }
4373
4374 /**
4375 * Sets the title to be displayed on this conversation. This should only be used for
4376 * group messaging and left unset for one-on-one conversations.
4377 * @param conversationTitle
4378 * @return this object for method chaining.
4379 */
4380 public MessagingStyle setConversationTitle(CharSequence conversationTitle) {
4381 mConversationTitle = conversationTitle;
4382 return this;
4383 }
4384
4385 /**
4386 * Return the title to be displayed on this conversation. Can be <code>null</code> and
4387 * should be for one-on-one conversations
4388 */
4389 public CharSequence getConversationTitle() {
4390 return mConversationTitle;
4391 }
4392
4393 /**
4394 * Adds a message for display by this notification. Convenience call for a simple
4395 * {@link Message} in {@link #addMessage(Notification.MessagingStyle.Message)}.
4396 * @param text A {@link CharSequence} to be displayed as the message content
4397 * @param timestamp Time at which the message arrived
4398 * @param sender A {@link CharSequence} to be used for displaying the name of the
4399 * sender. Should be <code>null</code> for messages by the current user, in which case
4400 * the platform will insert {@link #getUserDisplayName()}.
4401 * Should be unique amongst all individuals in the conversation, and should be
4402 * consistent during re-posts of the notification.
4403 *
4404 * @see Message#Message(CharSequence, long, CharSequence)
4405 *
4406 * @return this object for method chaining
4407 */
4408 public MessagingStyle addMessage(CharSequence text, long timestamp, CharSequence sender) {
4409 mMessages.add(new Message(text, timestamp, sender));
4410 if (mMessages.size() > MAXIMUM_RETAINED_MESSAGES) {
4411 mMessages.remove(0);
4412 }
4413 return this;
4414 }
4415
4416 /**
4417 * Adds a {@link Message} for display in this notification.
4418 * @param message The {@link Message} to be displayed
4419 * @return this object for method chaining
4420 */
4421 public MessagingStyle addMessage(Message message) {
4422 mMessages.add(message);
4423 if (mMessages.size() > MAXIMUM_RETAINED_MESSAGES) {
4424 mMessages.remove(0);
4425 }
4426 return this;
4427 }
4428
4429 /**
4430 * Gets the list of {@code Message} objects that represent the notification
4431 */
4432 public List<Message> getMessages() {
4433 return mMessages;
4434 }
4435
4436 /**
4437 * @hide
4438 */
4439 @Override
4440 public void addExtras(Bundle extras) {
4441 super.addExtras(extras);
4442 if (mUserDisplayName != null) {
4443 extras.putCharSequence(EXTRA_SELF_DISPLAY_NAME, mUserDisplayName);
4444 }
4445 if (mConversationTitle != null) {
4446 extras.putCharSequence(EXTRA_THREAD_TITLE, mConversationTitle);
4447 }
4448 extras.putBoolean(EXTRA_ALLOW_GENERATED_REPLIES, mAllowGeneratedReplies);
4449 if (!mMessages.isEmpty()) {
4450 extras.putParcelableArrayList(EXTRA_MESSAGES, mMessages);
4451 }
4452 }
4453
4454 /**
4455 * @hide
4456 */
4457 @Override
4458 protected void restoreFromExtras(Bundle extras) {
4459 super.restoreFromExtras(extras);
4460
4461 mMessages.clear();
4462 mUserDisplayName = extras.getString(EXTRA_SELF_DISPLAY_NAME);
4463 mConversationTitle = extras.getString(EXTRA_THREAD_TITLE);
4464 mAllowGeneratedReplies = extras.getBoolean(EXTRA_ALLOW_GENERATED_REPLIES,
4465 mAllowGeneratedReplies);
4466 List<Message> messages = extras.getParcelableArrayList(EXTRA_MESSAGES);
4467 if (messages != null) {
4468 mMessages.addAll(messages);
4469 }
4470 }
4471
4472 /**
4473 * @hide
4474 */
Adrian Roosc1a80b02016-04-05 14:54:55 -07004475 @Override
4476 public RemoteViews makeContentView() {
4477 Message m = findLatestIncomingMessage();
4478 CharSequence title = mConversationTitle != null
4479 ? mConversationTitle
4480 : (m == null) ? null : m.mSender;
4481 CharSequence text = (m == null)
4482 ? null
4483 : mConversationTitle != null ? makeMessageLine(m) : m.mText;
Alex Hillsfc737de2016-03-23 17:33:02 -04004484
Adrian Roosc1a80b02016-04-05 14:54:55 -07004485 return mBuilder.applyStandardTemplate(mBuilder.getBaseLayoutResource(),
4486 false /* hasProgress */,
4487 title,
4488 text);
4489 }
4490
4491 private Message findLatestIncomingMessage() {
4492 for (int i = mMessages.size() - 1; i >= 0; i--) {
4493 Message m = mMessages.get(i);
4494 // Incoming messages have a non-empty sender.
4495 if (!TextUtils.isEmpty(m.mSender)) {
4496 return m;
4497 }
4498 }
4499 return null;
4500 }
4501
4502 /**
4503 * @hide
4504 */
4505 @Override
4506 public RemoteViews makeBigContentView() {
4507 CharSequence title = !TextUtils.isEmpty(super.mBigContentTitle)
4508 ? super.mBigContentTitle
4509 : mConversationTitle;
4510 boolean hasTitle = !TextUtils.isEmpty(title);
4511
Adrian Roos48d746a2016-04-12 14:57:28 -07004512 RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(
Adrian Roosc1a80b02016-04-05 14:54:55 -07004513 mBuilder.getMessagingLayoutResource(),
4514 false /* hasProgress */,
4515 title,
4516 null /* text */);
4517
4518 int[] rowIds = {R.id.inbox_text0, R.id.inbox_text1, R.id.inbox_text2, R.id.inbox_text3,
4519 R.id.inbox_text4, R.id.inbox_text5, R.id.inbox_text6};
4520
4521 // Make sure all rows are gone in case we reuse a view.
4522 for (int rowId : rowIds) {
4523 contentView.setViewVisibility(rowId, View.GONE);
4524 }
4525
4526 int i=0;
4527 int titlePadding = mBuilder.mContext.getResources().getDimensionPixelSize(
4528 R.dimen.notification_messaging_spacing);
4529 contentView.setViewLayoutMarginBottom(R.id.line1, hasTitle ? titlePadding : 0);
4530 contentView.setInt(R.id.notification_messaging, "setNumIndentLines",
4531 mBuilder.mN.mLargeIcon == null ? 0 : (hasTitle ? 1 : 2));
4532
4533 int firstMessage = Math.max(0, mMessages.size() - rowIds.length);
4534 while (firstMessage + i < mMessages.size() && i < rowIds.length) {
4535 Message m = mMessages.get(firstMessage + i);
4536 int rowId = rowIds[i];
4537
4538 contentView.setViewVisibility(rowId, View.VISIBLE);
4539 contentView.setTextViewText(rowId, makeMessageLine(m));
4540
4541 i++;
4542 }
Alex Hillsfc737de2016-03-23 17:33:02 -04004543 return contentView;
4544 }
4545
Adrian Roosc1a80b02016-04-05 14:54:55 -07004546 private CharSequence makeMessageLine(Message m) {
4547 BidiFormatter bidi = BidiFormatter.getInstance();
4548 SpannableStringBuilder sb = new SpannableStringBuilder();
4549 if (TextUtils.isEmpty(m.mSender)) {
4550 CharSequence replyName = mUserDisplayName == null ? "" : mUserDisplayName;
4551 sb.append(bidi.unicodeWrap(replyName),
4552 makeFontColorSpan(mBuilder.resolveContrastColor()),
4553 0 /* flags */);
4554 } else {
4555 sb.append(bidi.unicodeWrap(m.mSender),
4556 makeFontColorSpan(Color.BLACK),
4557 0 /* flags */);
4558 }
4559 CharSequence text = m.mText == null ? "" : m.mText;
4560 sb.append(" ").append(bidi.unicodeWrap(text));
4561 return sb;
4562 }
4563
4564 private static TextAppearanceSpan makeFontColorSpan(int color) {
4565 return new TextAppearanceSpan(null, 0, 0,
4566 ColorStateList.valueOf(color), null);
4567 }
4568
Alex Hillsfc737de2016-03-23 17:33:02 -04004569 public static final class Message implements Parcelable {
4570
4571 private final CharSequence mText;
4572 private final long mTimestamp;
4573 private final CharSequence mSender;
4574
4575 private String mDataMimeType;
4576 private Uri mDataUri;
4577
4578 /**
4579 * Constructor
4580 * @param text A {@link CharSequence} to be displayed as the message content
4581 * @param timestamp Time at which the message arrived
4582 * @param sender A {@link CharSequence} to be used for displaying the name of the
4583 * sender. Should be <code>null</code> for messages by the current user, in which case
4584 * the platform will insert {@link MessagingStyle#getUserDisplayName()}.
4585 * Should be unique amongst all individuals in the conversation, and should be
4586 * consistent during re-posts of the notification.
4587 */
4588 public Message(CharSequence text, long timestamp, CharSequence sender){
4589 mText = text;
4590 mTimestamp = timestamp;
4591 mSender = sender;
4592 }
4593
4594 /**
4595 * Sets a binary blob of data and an associated MIME type for a message. In the case
4596 * where the platform doesn't support the MIME type, the original text provided in the
4597 * constructor will be used.
4598 * @param dataMimeType The MIME type of the content. See
4599 * <a href="{@docRoot}notifications/messaging.html"> for the list of supported MIME
4600 * types on Android and Android Wear.
4601 * @param dataUri The uri containing the content whose type is given by the MIME type.
4602 * <p class="note">
4603 * <ol>
4604 * <li>Notification Listeners including the System UI need permission to access the
4605 * data the Uri points to. The recommended ways to do this are:</li>
4606 * <li>Store the data in your own ContentProvider, making sure that other apps have
4607 * the correct permission to access your provider. The preferred mechanism for
4608 * providing access is to use per-URI permissions which are temporary and only
4609 * grant access to the receiving application. An easy way to create a
4610 * ContentProvider like this is to use the FileProvider helper class.</li>
4611 * <li>Use the system MediaStore. The MediaStore is primarily aimed at video, audio
4612 * and image MIME types, however beginning with Android 3.0 (API level 11) it can
4613 * also store non-media types (see MediaStore.Files for more info). Files can be
4614 * inserted into the MediaStore using scanFile() after which a content:// style
4615 * Uri suitable for sharing is passed to the provided onScanCompleted() callback.
4616 * Note that once added to the system MediaStore the content is accessible to any
4617 * app on the device.</li>
4618 * </ol>
4619 * @return this object for method chaining
4620 */
4621 public Message setData(String dataMimeType, Uri dataUri) {
4622 mDataMimeType = dataMimeType;
4623 mDataUri = dataUri;
4624 return this;
4625 }
4626
4627 private Message(Parcel in) {
4628 if (in.readInt() != 0) {
4629 mText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
4630 } else {
4631 mText = null;
4632 }
4633 mTimestamp = in.readLong();
4634 if (in.readInt() != 0) {
4635 mSender = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
4636 } else {
4637 mSender = null;
4638 }
4639 if (in.readInt() != 0) {
4640 mDataMimeType = in.readString();
4641 }
4642 if (in.readInt() != 0) {
4643 mDataUri = in.readParcelable(Uri.class.getClassLoader());
4644 }
4645 }
4646
4647 /**
4648 * Get the text to be used for this message, or the fallback text if a type and content
4649 * Uri have been set
4650 */
4651 public CharSequence getText() {
4652 return mText;
4653 }
4654
4655 /**
4656 * Get the time at which this message arrived
4657 */
4658 public long getTimestamp() {
4659 return mTimestamp;
4660 }
4661
4662 /**
4663 * Get the text used to display the contact's name in the messaging experience
4664 */
4665 public CharSequence getSender() {
4666 return mSender;
4667 }
4668
4669 /**
4670 * Get the MIME type of the data pointed to by the Uri
4671 */
4672 public String getDataMimeType() {
4673 return mDataMimeType;
4674 }
4675
4676 /**
4677 * Get the the Uri pointing to the content of the message. Can be null, in which case
4678 * {@see #getText()} is used.
4679 */
4680 public Uri getDataUri() {
4681 return mDataUri;
4682 }
4683
4684 @Override
4685 public int describeContents() {
4686 return 0;
4687 }
4688
4689 @Override
4690 public void writeToParcel(Parcel out, int flags) {
4691 if (mText != null) {
4692 out.writeInt(1);
4693 TextUtils.writeToParcel(mText, out, flags);
4694 } else {
4695 out.writeInt(0);
4696 }
4697 out.writeLong(mTimestamp);
4698 if (mSender != null) {
4699 out.writeInt(1);
4700 TextUtils.writeToParcel(mSender, out, flags);
4701 } else {
4702 out.writeInt(0);
4703 }
4704 if (mDataMimeType != null) {
4705 out.writeInt(1);
4706 out.writeString(mDataMimeType);
4707 } else {
4708 out.writeInt(0);
4709 }
4710 if (mDataUri != null) {
4711 out.writeInt(1);
4712 out.writeParcelable(mDataUri, flags);
4713 } else {
4714 out.writeInt(0);
4715 }
4716 }
4717
4718 public static final Parcelable.Creator<Message> CREATOR =
4719 new Parcelable.Creator<Message>() {
4720 public Message createFromParcel(Parcel in) {
4721 return new Message(in);
4722 }
4723 public Message[] newArray(int size) {
4724 return new Message[size];
4725 }
4726 };
4727 }
4728 }
4729
4730 /**
Daniel Sandler879c5e02012-04-17 16:46:51 -04004731 * Helper class for generating large-format notifications that include a list of (up to 5) strings.
Joe Malin8d40d042012-11-05 11:36:40 -08004732 *
Robert Ly91c5ce32014-06-08 15:37:00 -07004733 * Here's how you'd set the <code>InboxStyle</code> on a notification:
Daniel Sandler879c5e02012-04-17 16:46:51 -04004734 * <pre class="prettyprint">
Robert Ly91c5ce32014-06-08 15:37:00 -07004735 * Notification notif = new Notification.Builder(mContext)
4736 * .setContentTitle(&quot;5 New mails from &quot; + sender.toString())
4737 * .setContentText(subject)
4738 * .setSmallIcon(R.drawable.new_mail)
4739 * .setLargeIcon(aBitmap)
4740 * .setStyle(new Notification.InboxStyle()
4741 * .addLine(str1)
4742 * .addLine(str2)
4743 * .setContentTitle(&quot;&quot;)
4744 * .setSummaryText(&quot;+3 more&quot;))
4745 * .build();
Daniel Sandler879c5e02012-04-17 16:46:51 -04004746 * </pre>
Joe Malin8d40d042012-11-05 11:36:40 -08004747 *
Daniel Sandler879c5e02012-04-17 16:46:51 -04004748 * @see Notification#bigContentView
4749 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004750 public static class InboxStyle extends Style {
Daniel Sandler879c5e02012-04-17 16:46:51 -04004751 private ArrayList<CharSequence> mTexts = new ArrayList<CharSequence>(5);
4752
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004753 public InboxStyle() {
4754 }
4755
Daniel Sandler879c5e02012-04-17 16:46:51 -04004756 public InboxStyle(Builder builder) {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004757 setBuilder(builder);
Daniel Sandler879c5e02012-04-17 16:46:51 -04004758 }
4759
Chris Wrend6297db2012-05-03 16:20:13 -04004760 /**
4761 * Overrides ContentTitle in the big form of the template.
4762 * This defaults to the value passed to setContentTitle().
4763 */
4764 public InboxStyle setBigContentTitle(CharSequence title) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04004765 internalSetBigContentTitle(safeCharSequence(title));
Chris Wrend6297db2012-05-03 16:20:13 -04004766 return this;
4767 }
4768
4769 /**
4770 * Set the first line of text after the detail section in the big form of the template.
4771 */
4772 public InboxStyle setSummaryText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04004773 internalSetSummaryText(safeCharSequence(cs));
Chris Wrend6297db2012-05-03 16:20:13 -04004774 return this;
4775 }
4776
Chris Wren0bd664d2012-08-01 13:56:56 -04004777 /**
4778 * Append a line to the digest section of the Inbox notification.
4779 */
Daniel Sandler879c5e02012-04-17 16:46:51 -04004780 public InboxStyle addLine(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04004781 mTexts.add(safeCharSequence(cs));
Daniel Sandler879c5e02012-04-17 16:46:51 -04004782 return this;
4783 }
4784
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004785 /**
4786 * @hide
4787 */
4788 public void addExtras(Bundle extras) {
4789 super.addExtras(extras);
Christoph Studer4600f9b2014-07-22 22:44:43 +02004790
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004791 CharSequence[] a = new CharSequence[mTexts.size()];
4792 extras.putCharSequenceArray(EXTRA_TEXT_LINES, mTexts.toArray(a));
4793 }
4794
Christoph Studer4600f9b2014-07-22 22:44:43 +02004795 /**
4796 * @hide
4797 */
4798 @Override
4799 protected void restoreFromExtras(Bundle extras) {
4800 super.restoreFromExtras(extras);
4801
4802 mTexts.clear();
4803 if (extras.containsKey(EXTRA_TEXT_LINES)) {
4804 Collections.addAll(mTexts, extras.getCharSequenceArray(EXTRA_TEXT_LINES));
4805 }
4806 }
4807
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004808 /**
4809 * @hide
4810 */
4811 public RemoteViews makeBigContentView() {
Selim Cinekc848c3a2016-01-13 15:27:30 -08004812 // Remove the content text so it disappears unless you have a summary
Christoph Studer4600f9b2014-07-22 22:44:43 +02004813 // Nasty
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004814 CharSequence oldBuilderContentText = mBuilder.mN.extras.getCharSequence(EXTRA_TEXT);
4815 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, null);
Christoph Studer4600f9b2014-07-22 22:44:43 +02004816
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004817 RemoteViews contentView = getStandardView(mBuilder.getInboxLayoutResource());
Daniel Sandler619738c2012-06-07 16:33:08 -04004818
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004819 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, oldBuilderContentText);
Christoph Studer4600f9b2014-07-22 22:44:43 +02004820
Chris Wrend6297db2012-05-03 16:20:13 -04004821 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 -04004822 R.id.inbox_text4, R.id.inbox_text5, R.id.inbox_text6};
Chris Wrend6297db2012-05-03 16:20:13 -04004823
Chris Wren4ed80d52012-05-17 09:30:03 -04004824 // Make sure all rows are gone in case we reuse a view.
4825 for (int rowId : rowIds) {
4826 contentView.setViewVisibility(rowId, View.GONE);
4827 }
4828
Daniel Sandler879c5e02012-04-17 16:46:51 -04004829 int i=0;
Selim Cinek07c80172016-04-21 16:40:47 -07004830 int topPadding = mBuilder.mContext.getResources().getDimensionPixelSize(
4831 R.dimen.notification_inbox_item_top_padding);
Selim Cinek247fa012016-02-18 09:50:48 -08004832 boolean first = true;
Selim Cinek07c80172016-04-21 16:40:47 -07004833 int onlyViewId = 0;
4834 int maxRows = rowIds.length;
4835 if (mBuilder.mActions.size() > 0) {
4836 maxRows--;
4837 }
4838 while (i < mTexts.size() && i < maxRows) {
Daniel Sandler879c5e02012-04-17 16:46:51 -04004839 CharSequence str = mTexts.get(i);
Selim Cinek07c80172016-04-21 16:40:47 -07004840 if (!TextUtils.isEmpty(str)) {
Daniel Sandler879c5e02012-04-17 16:46:51 -04004841 contentView.setViewVisibility(rowIds[i], View.VISIBLE);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004842 contentView.setTextViewText(rowIds[i], mBuilder.processLegacyText(str));
Selim Cinek07c80172016-04-21 16:40:47 -07004843 contentView.setViewPadding(rowIds[i], 0, topPadding, 0, 0);
Selim Cinek247fa012016-02-18 09:50:48 -08004844 handleInboxImageMargin(contentView, rowIds[i], first);
Selim Cinek07c80172016-04-21 16:40:47 -07004845 if (first) {
4846 onlyViewId = rowIds[i];
4847 } else {
4848 onlyViewId = 0;
4849 }
Selim Cinek247fa012016-02-18 09:50:48 -08004850 first = false;
Daniel Sandler879c5e02012-04-17 16:46:51 -04004851 }
4852 i++;
4853 }
Selim Cinek07c80172016-04-21 16:40:47 -07004854 if (onlyViewId != 0) {
4855 // We only have 1 entry, lets make it look like the normal Text of a Bigtext
4856 topPadding = mBuilder.mContext.getResources().getDimensionPixelSize(
4857 R.dimen.notification_text_margin_top);
4858 contentView.setViewPadding(onlyViewId, 0, topPadding, 0, 0);
4859 }
Selim Cinek1e0bf612015-11-20 15:57:26 -08004860
Daniel Sandler879c5e02012-04-17 16:46:51 -04004861 return contentView;
4862 }
Selim Cinek1e0bf612015-11-20 15:57:26 -08004863
Selim Cinek247fa012016-02-18 09:50:48 -08004864 private void handleInboxImageMargin(RemoteViews contentView, int id, boolean first) {
Selim Cinek1e0bf612015-11-20 15:57:26 -08004865 int endMargin = 0;
Selim Cinek247fa012016-02-18 09:50:48 -08004866 if (first) {
4867 final int max = mBuilder.mN.extras.getInt(EXTRA_PROGRESS_MAX, 0);
4868 final boolean ind = mBuilder.mN.extras.getBoolean(EXTRA_PROGRESS_INDETERMINATE);
4869 boolean hasProgress = max != 0 || ind;
4870 if (mBuilder.mN.mLargeIcon != null && !hasProgress) {
4871 endMargin = mBuilder.mContext.getResources().getDimensionPixelSize(
4872 R.dimen.notification_content_picture_margin);
4873 }
Selim Cinek1e0bf612015-11-20 15:57:26 -08004874 }
4875 contentView.setViewLayoutMarginEnd(id, endMargin);
4876 }
Daniel Sandler879c5e02012-04-17 16:46:51 -04004877 }
Dan Sandler842dd772014-05-15 09:36:47 -04004878
4879 /**
4880 * Notification style for media playback notifications.
4881 *
4882 * In the expanded form, {@link Notification#bigContentView}, up to 5
4883 * {@link Notification.Action}s specified with
Dan Sandler86647982015-05-13 23:41:13 -04004884 * {@link Notification.Builder#addAction(Action) addAction} will be
Dan Sandler842dd772014-05-15 09:36:47 -04004885 * shown as icon-only pushbuttons, suitable for transport controls. The Bitmap given to
4886 * {@link Notification.Builder#setLargeIcon(android.graphics.Bitmap) setLargeIcon()} will be
4887 * treated as album artwork.
4888 *
4889 * Unlike the other styles provided here, MediaStyle can also modify the standard-size
4890 * {@link Notification#contentView}; by providing action indices to
Christoph Studerfde6f4d2014-12-12 13:23:26 +01004891 * {@link #setShowActionsInCompactView(int...)} you can promote up to 3 actions to be displayed
Dan Sandler842dd772014-05-15 09:36:47 -04004892 * in the standard view alongside the usual content.
4893 *
Bryan Mawhinney6be8de32014-07-18 10:35:12 +01004894 * Notifications created with MediaStyle will have their category set to
4895 * {@link Notification#CATEGORY_TRANSPORT CATEGORY_TRANSPORT} unless you set a different
4896 * category using {@link Notification.Builder#setCategory(String) setCategory()}.
4897 *
Jeff Browndba34ba2014-06-24 20:46:03 -07004898 * Finally, if you attach a {@link android.media.session.MediaSession.Token} using
4899 * {@link android.app.Notification.MediaStyle#setMediaSession(MediaSession.Token)},
Dan Sandler842dd772014-05-15 09:36:47 -04004900 * the System UI can identify this as a notification representing an active media session
4901 * and respond accordingly (by showing album artwork in the lockscreen, for example).
4902 *
4903 * To use this style with your Notification, feed it to
4904 * {@link Notification.Builder#setStyle(android.app.Notification.Style)} like so:
4905 * <pre class="prettyprint">
4906 * Notification noti = new Notification.Builder()
4907 * .setSmallIcon(R.drawable.ic_stat_player)
Christoph Studere935fe92014-11-24 14:18:06 +01004908 * .setContentTitle(&quot;Track title&quot;)
4909 * .setContentText(&quot;Artist - Album&quot;)
4910 * .setLargeIcon(albumArtBitmap))
Bryan Mawhinney6be8de32014-07-18 10:35:12 +01004911 * .setStyle(<b>new Notification.MediaStyle()</b>
4912 * .setMediaSession(mySession))
Dan Sandler842dd772014-05-15 09:36:47 -04004913 * .build();
4914 * </pre>
4915 *
4916 * @see Notification#bigContentView
4917 */
4918 public static class MediaStyle extends Style {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004919 static final int MAX_MEDIA_BUTTONS_IN_COMPACT = 3;
Dan Sandler842dd772014-05-15 09:36:47 -04004920 static final int MAX_MEDIA_BUTTONS = 5;
4921
4922 private int[] mActionsToShowInCompact = null;
Jeff Browndba34ba2014-06-24 20:46:03 -07004923 private MediaSession.Token mToken;
Dan Sandler842dd772014-05-15 09:36:47 -04004924
4925 public MediaStyle() {
4926 }
4927
4928 public MediaStyle(Builder builder) {
4929 setBuilder(builder);
4930 }
4931
4932 /**
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004933 * 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 -04004934 * notification view.
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004935 *
4936 * @param actions the indices of the actions to show in the compact notification view
Dan Sandler842dd772014-05-15 09:36:47 -04004937 */
4938 public MediaStyle setShowActionsInCompactView(int...actions) {
4939 mActionsToShowInCompact = actions;
4940 return this;
4941 }
4942
4943 /**
Jeff Browndba34ba2014-06-24 20:46:03 -07004944 * Attach a {@link android.media.session.MediaSession.Token} to this Notification
4945 * to provide additional playback information and control to the SystemUI.
Dan Sandler842dd772014-05-15 09:36:47 -04004946 */
Jeff Browndba34ba2014-06-24 20:46:03 -07004947 public MediaStyle setMediaSession(MediaSession.Token token) {
Dan Sandler842dd772014-05-15 09:36:47 -04004948 mToken = token;
4949 return this;
4950 }
4951
Christoph Studer4600f9b2014-07-22 22:44:43 +02004952 /**
4953 * @hide
4954 */
Dan Sandler842dd772014-05-15 09:36:47 -04004955 @Override
4956 public Notification buildStyled(Notification wip) {
Christoph Studer4600f9b2014-07-22 22:44:43 +02004957 super.buildStyled(wip);
Bryan Mawhinney6be8de32014-07-18 10:35:12 +01004958 if (wip.category == null) {
4959 wip.category = Notification.CATEGORY_TRANSPORT;
4960 }
Dan Sandler842dd772014-05-15 09:36:47 -04004961 return wip;
4962 }
4963
Christoph Studer4600f9b2014-07-22 22:44:43 +02004964 /**
4965 * @hide
4966 */
4967 @Override
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004968 public RemoteViews makeContentView() {
4969 return makeMediaContentView();
Christoph Studer4600f9b2014-07-22 22:44:43 +02004970 }
4971
4972 /**
4973 * @hide
4974 */
4975 @Override
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004976 public RemoteViews makeBigContentView() {
4977 return makeMediaBigContentView();
Christoph Studer4600f9b2014-07-22 22:44:43 +02004978 }
4979
Selim Cinekcc10bfb2016-02-10 16:24:21 -08004980 /**
4981 * @hide
4982 */
4983 @Override
4984 public RemoteViews makeHeadsUpContentView() {
4985 RemoteViews expanded = makeMediaBigContentView();
4986 return expanded != null ? expanded : makeMediaContentView();
4987 }
4988
Dan Sandler842dd772014-05-15 09:36:47 -04004989 /** @hide */
4990 @Override
4991 public void addExtras(Bundle extras) {
4992 super.addExtras(extras);
4993
4994 if (mToken != null) {
4995 extras.putParcelable(EXTRA_MEDIA_SESSION, mToken);
4996 }
Bryan Mawhinneye191f902014-07-22 12:50:09 +01004997 if (mActionsToShowInCompact != null) {
4998 extras.putIntArray(EXTRA_COMPACT_ACTIONS, mActionsToShowInCompact);
4999 }
Dan Sandler842dd772014-05-15 09:36:47 -04005000 }
5001
Christoph Studer4600f9b2014-07-22 22:44:43 +02005002 /**
5003 * @hide
5004 */
5005 @Override
5006 protected void restoreFromExtras(Bundle extras) {
5007 super.restoreFromExtras(extras);
5008
5009 if (extras.containsKey(EXTRA_MEDIA_SESSION)) {
5010 mToken = extras.getParcelable(EXTRA_MEDIA_SESSION);
5011 }
5012 if (extras.containsKey(EXTRA_COMPACT_ACTIONS)) {
5013 mActionsToShowInCompact = extras.getIntArray(EXTRA_COMPACT_ACTIONS);
5014 }
5015 }
5016
Selim Cinek5bf069a2015-11-10 19:14:27 -05005017 private RemoteViews generateMediaActionButton(Action action, int color) {
Dan Sandler842dd772014-05-15 09:36:47 -04005018 final boolean tombstone = (action.actionIntent == null);
Selim Cinekf33b1112015-07-15 17:45:11 -07005019 RemoteViews button = new BuilderRemoteViews(mBuilder.mContext.getApplicationInfo(),
Alan Viverette3cb07a462014-06-06 14:19:53 -07005020 R.layout.notification_material_media_action);
Dan Sandler68079d52015-07-22 10:45:30 -04005021 button.setImageViewIcon(R.id.action0, action.getIcon());
Selim Cinek5bf069a2015-11-10 19:14:27 -05005022 button.setDrawableParameters(R.id.action0, false, -1, color, PorterDuff.Mode.SRC_ATOP,
5023 -1);
Dan Sandler842dd772014-05-15 09:36:47 -04005024 if (!tombstone) {
5025 button.setOnClickPendingIntent(R.id.action0, action.actionIntent);
5026 }
5027 button.setContentDescription(R.id.action0, action.title);
5028 return button;
5029 }
5030
5031 private RemoteViews makeMediaContentView() {
5032 RemoteViews view = mBuilder.applyStandardTemplate(
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02005033 R.layout.notification_template_material_media, false /* hasProgress */);
Dan Sandler842dd772014-05-15 09:36:47 -04005034
5035 final int numActions = mBuilder.mActions.size();
5036 final int N = mActionsToShowInCompact == null
5037 ? 0
5038 : Math.min(mActionsToShowInCompact.length, MAX_MEDIA_BUTTONS_IN_COMPACT);
5039 if (N > 0) {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02005040 view.removeAllViews(com.android.internal.R.id.media_actions);
Dan Sandler842dd772014-05-15 09:36:47 -04005041 for (int i = 0; i < N; i++) {
5042 if (i >= numActions) {
5043 throw new IllegalArgumentException(String.format(
5044 "setShowActionsInCompactView: action %d out of bounds (max %d)",
5045 i, numActions - 1));
5046 }
5047
5048 final Action action = mBuilder.mActions.get(mActionsToShowInCompact[i]);
Selim Cinek5bf069a2015-11-10 19:14:27 -05005049 final RemoteViews button = generateMediaActionButton(action,
Adrian Roos4ff3b122016-02-01 12:26:13 -08005050 mBuilder.resolveContrastColor());
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02005051 view.addView(com.android.internal.R.id.media_actions, button);
Dan Sandler842dd772014-05-15 09:36:47 -04005052 }
5053 }
Selim Cinekfdc738f2016-01-27 20:04:27 -08005054 handleImage(view);
5055 // handle the content margin
Selim Cinek247fa012016-02-18 09:50:48 -08005056 int endMargin = mBuilder.mContext.getResources().getDimensionPixelSize(
5057 R.dimen.notification_content_margin_end);;
Selim Cinekfdc738f2016-01-27 20:04:27 -08005058 if (mBuilder.mN.mLargeIcon != null) {
Selim Cinek247fa012016-02-18 09:50:48 -08005059 endMargin += mBuilder.mContext.getResources().getDimensionPixelSize(
5060 R.dimen.notification_content_picture_margin);
Selim Cinekfdc738f2016-01-27 20:04:27 -08005061 }
5062 view.setViewLayoutMarginEnd(R.id.notification_main_column, endMargin);
Dan Sandler842dd772014-05-15 09:36:47 -04005063 return view;
5064 }
5065
5066 private RemoteViews makeMediaBigContentView() {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02005067 final int actionCount = Math.min(mBuilder.mActions.size(), MAX_MEDIA_BUTTONS);
Selim Cinekcc10bfb2016-02-10 16:24:21 -08005068 // Dont add an expanded view if there is no more content to be revealed
5069 int actionsInCompact = mActionsToShowInCompact == null
5070 ? 0
5071 : Math.min(mActionsToShowInCompact.length, MAX_MEDIA_BUTTONS_IN_COMPACT);
5072 if (mBuilder.mN.mLargeIcon == null && actionCount <= actionsInCompact) {
5073 return null;
5074 }
Selim Cinek5bf069a2015-11-10 19:14:27 -05005075 RemoteViews big = mBuilder.applyStandardTemplate(
5076 R.layout.notification_template_material_big_media,
5077 false);
Dan Sandler842dd772014-05-15 09:36:47 -04005078
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02005079 if (actionCount > 0) {
5080 big.removeAllViews(com.android.internal.R.id.media_actions);
5081 for (int i = 0; i < actionCount; i++) {
Selim Cinek5bf069a2015-11-10 19:14:27 -05005082 final RemoteViews button = generateMediaActionButton(mBuilder.mActions.get(i),
Adrian Roos4ff3b122016-02-01 12:26:13 -08005083 mBuilder.resolveContrastColor());
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02005084 big.addView(com.android.internal.R.id.media_actions, button);
Dan Sandler842dd772014-05-15 09:36:47 -04005085 }
5086 }
Selim Cinek5bf069a2015-11-10 19:14:27 -05005087 handleImage(big);
Dan Sandler842dd772014-05-15 09:36:47 -04005088 return big;
5089 }
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02005090
Selim Cinek5bf069a2015-11-10 19:14:27 -05005091 private void handleImage(RemoteViews contentView) {
5092 if (mBuilder.mN.mLargeIcon != null) {
5093 contentView.setViewLayoutMarginEnd(R.id.line1, 0);
Selim Cinekc848c3a2016-01-13 15:27:30 -08005094 contentView.setViewLayoutMarginEnd(R.id.text, 0);
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02005095 }
5096 }
5097
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02005098 /**
5099 * @hide
5100 */
5101 @Override
5102 protected boolean hasProgress() {
5103 return false;
5104 }
Dan Sandler842dd772014-05-15 09:36:47 -04005105 }
Griff Hazen61a9e862014-05-22 16:05:19 -07005106
Selim Cinek593610c2016-02-16 18:42:57 -08005107 /**
5108 * Notification style for custom views that are decorated by the system
5109 *
5110 * <p>Instead of providing a notification that is completely custom, a developer can set this
5111 * style and still obtain system decorations like the notification header with the expand
5112 * affordance and actions.
5113 *
5114 * <p>Use {@link android.app.Notification.Builder#setCustomContentView(RemoteViews)},
5115 * {@link android.app.Notification.Builder#setCustomBigContentView(RemoteViews)} and
5116 * {@link android.app.Notification.Builder#setCustomHeadsUpContentView(RemoteViews)} to set the
5117 * corresponding custom views to display.
5118 *
5119 * To use this style with your Notification, feed it to
5120 * {@link Notification.Builder#setStyle(android.app.Notification.Style)} like so:
5121 * <pre class="prettyprint">
5122 * Notification noti = new Notification.Builder()
5123 * .setSmallIcon(R.drawable.ic_stat_player)
5124 * .setLargeIcon(albumArtBitmap))
5125 * .setCustomContentView(contentView);
5126 * .setStyle(<b>new Notification.DecoratedCustomViewStyle()</b>)
5127 * .build();
5128 * </pre>
5129 */
5130 public static class DecoratedCustomViewStyle extends Style {
5131
5132 public DecoratedCustomViewStyle() {
5133 }
5134
5135 public DecoratedCustomViewStyle(Builder builder) {
5136 setBuilder(builder);
5137 }
5138
5139 /**
5140 * @hide
5141 */
5142 public boolean displayCustomViewInline() {
5143 return true;
5144 }
5145
5146 /**
5147 * @hide
5148 */
5149 @Override
5150 public RemoteViews makeContentView() {
5151 return makeStandardTemplateWithCustomContent(mBuilder.mN.contentView);
5152 }
5153
5154 /**
5155 * @hide
5156 */
5157 @Override
5158 public RemoteViews makeBigContentView() {
5159 return makeDecoratedBigContentView();
5160 }
5161
5162 /**
5163 * @hide
5164 */
5165 @Override
5166 public RemoteViews makeHeadsUpContentView() {
5167 return makeDecoratedHeadsUpContentView();
5168 }
5169
Selim Cinek593610c2016-02-16 18:42:57 -08005170 private RemoteViews makeDecoratedHeadsUpContentView() {
5171 RemoteViews headsUpContentView = mBuilder.mN.headsUpContentView == null
5172 ? mBuilder.mN.contentView
5173 : mBuilder.mN.headsUpContentView;
5174 if (mBuilder.mActions.size() == 0) {
5175 return makeStandardTemplateWithCustomContent(headsUpContentView);
5176 }
5177 RemoteViews remoteViews = mBuilder.applyStandardTemplateWithActions(
5178 mBuilder.getBigBaseLayoutResource());
Selim Cinek247fa012016-02-18 09:50:48 -08005179 buildIntoRemoteViewContent(remoteViews, headsUpContentView);
Selim Cinek593610c2016-02-16 18:42:57 -08005180 return remoteViews;
5181 }
5182
Selim Cinek593610c2016-02-16 18:42:57 -08005183 private RemoteViews makeStandardTemplateWithCustomContent(RemoteViews customContent) {
5184 RemoteViews remoteViews = mBuilder.applyStandardTemplate(
5185 mBuilder.getBaseLayoutResource());
Selim Cinek247fa012016-02-18 09:50:48 -08005186 buildIntoRemoteViewContent(remoteViews, customContent);
Selim Cinek593610c2016-02-16 18:42:57 -08005187 return remoteViews;
5188 }
5189
Selim Cinek593610c2016-02-16 18:42:57 -08005190 private RemoteViews makeDecoratedBigContentView() {
5191 RemoteViews bigContentView = mBuilder.mN.bigContentView == null
5192 ? mBuilder.mN.contentView
5193 : mBuilder.mN.bigContentView;
5194 if (mBuilder.mActions.size() == 0) {
5195 return makeStandardTemplateWithCustomContent(bigContentView);
5196 }
5197 RemoteViews remoteViews = mBuilder.applyStandardTemplateWithActions(
5198 mBuilder.getBigBaseLayoutResource());
Selim Cinek247fa012016-02-18 09:50:48 -08005199 buildIntoRemoteViewContent(remoteViews, bigContentView);
Selim Cinek593610c2016-02-16 18:42:57 -08005200 return remoteViews;
5201 }
Selim Cinek247fa012016-02-18 09:50:48 -08005202
5203 private void buildIntoRemoteViewContent(RemoteViews remoteViews,
5204 RemoteViews customContent) {
Adrian Roos5081c0d2016-02-26 16:04:19 -08005205 if (customContent != null) {
Selim Cinekf91017e2016-03-14 12:25:09 -07005206 // Need to clone customContent before adding, because otherwise it can no longer be
5207 // parceled independently of remoteViews.
Adrian Roos5081c0d2016-02-26 16:04:19 -08005208 customContent = customContent.clone();
Selim Cinekf91017e2016-03-14 12:25:09 -07005209 remoteViews.removeAllViews(R.id.notification_main_column);
5210 remoteViews.addView(R.id.notification_main_column, customContent);
Adrian Roos5081c0d2016-02-26 16:04:19 -08005211 }
Selim Cinek247fa012016-02-18 09:50:48 -08005212 // also update the end margin if there is an image
5213 int endMargin = mBuilder.mContext.getResources().getDimensionPixelSize(
5214 R.dimen.notification_content_margin_end);
5215 if (mBuilder.mN.mLargeIcon != null) {
5216 endMargin += mBuilder.mContext.getResources().getDimensionPixelSize(
5217 R.dimen.notification_content_picture_margin);
5218 }
5219 remoteViews.setViewLayoutMarginEnd(R.id.notification_main_column, endMargin);
5220 }
Selim Cinek593610c2016-02-16 18:42:57 -08005221 }
5222
Selim Cinek03eb3b72016-02-18 10:39:45 -08005223 /**
5224 * Notification style for media custom views that are decorated by the system
5225 *
5226 * <p>Instead of providing a media notification that is completely custom, a developer can set
5227 * this style and still obtain system decorations like the notification header with the expand
5228 * affordance and actions.
5229 *
5230 * <p>Use {@link android.app.Notification.Builder#setCustomContentView(RemoteViews)},
5231 * {@link android.app.Notification.Builder#setCustomBigContentView(RemoteViews)} and
5232 * {@link android.app.Notification.Builder#setCustomHeadsUpContentView(RemoteViews)} to set the
5233 * corresponding custom views to display.
5234 *
5235 * To use this style with your Notification, feed it to
5236 * {@link Notification.Builder#setStyle(android.app.Notification.Style)} like so:
5237 * <pre class="prettyprint">
5238 * Notification noti = new Notification.Builder()
5239 * .setSmallIcon(R.drawable.ic_stat_player)
5240 * .setLargeIcon(albumArtBitmap))
5241 * .setCustomContentView(contentView);
5242 * .setStyle(<b>new Notification.DecoratedMediaCustomViewStyle()</b>
5243 * .setMediaSession(mySession))
5244 * .build();
5245 * </pre>
5246 *
5247 * @see android.app.Notification.DecoratedCustomViewStyle
5248 * @see android.app.Notification.MediaStyle
5249 */
5250 public static class DecoratedMediaCustomViewStyle extends MediaStyle {
5251
5252 public DecoratedMediaCustomViewStyle() {
5253 }
5254
5255 public DecoratedMediaCustomViewStyle(Builder builder) {
5256 setBuilder(builder);
5257 }
5258
5259 /**
5260 * @hide
5261 */
5262 public boolean displayCustomViewInline() {
5263 return true;
5264 }
5265
5266 /**
5267 * @hide
5268 */
5269 @Override
5270 public RemoteViews makeContentView() {
5271 RemoteViews remoteViews = super.makeContentView();
5272 return buildIntoRemoteView(remoteViews, R.id.notification_content_container,
5273 mBuilder.mN.contentView);
5274 }
5275
5276 /**
5277 * @hide
5278 */
5279 @Override
5280 public RemoteViews makeBigContentView() {
5281 RemoteViews customRemoteView = mBuilder.mN.bigContentView != null
5282 ? mBuilder.mN.bigContentView
5283 : mBuilder.mN.contentView;
5284 return makeBigContentViewWithCustomContent(customRemoteView);
5285 }
5286
5287 private RemoteViews makeBigContentViewWithCustomContent(RemoteViews customRemoteView) {
5288 RemoteViews remoteViews = super.makeBigContentView();
5289 if (remoteViews != null) {
5290 return buildIntoRemoteView(remoteViews, R.id.notification_main_column,
5291 customRemoteView);
5292 } else if (customRemoteView != mBuilder.mN.contentView){
5293 remoteViews = super.makeContentView();
5294 return buildIntoRemoteView(remoteViews, R.id.notification_content_container,
5295 customRemoteView);
5296 } else {
5297 return null;
5298 }
5299 }
5300
5301 /**
5302 * @hide
5303 */
5304 @Override
5305 public RemoteViews makeHeadsUpContentView() {
5306 RemoteViews customRemoteView = mBuilder.mN.headsUpContentView != null
5307 ? mBuilder.mN.headsUpContentView
5308 : mBuilder.mN.contentView;
5309 return makeBigContentViewWithCustomContent(customRemoteView);
5310 }
5311
5312 private RemoteViews buildIntoRemoteView(RemoteViews remoteViews, int id,
5313 RemoteViews customContent) {
Adrian Roos5081c0d2016-02-26 16:04:19 -08005314 if (customContent != null) {
Selim Cinekf91017e2016-03-14 12:25:09 -07005315 // Need to clone customContent before adding, because otherwise it can no longer be
5316 // parceled independently of remoteViews.
Adrian Roos5081c0d2016-02-26 16:04:19 -08005317 customContent = customContent.clone();
Selim Cinekf91017e2016-03-14 12:25:09 -07005318 remoteViews.removeAllViews(id);
5319 remoteViews.addView(id, customContent);
Adrian Roos5081c0d2016-02-26 16:04:19 -08005320 }
Selim Cinek03eb3b72016-02-18 10:39:45 -08005321 return remoteViews;
5322 }
5323 }
5324
Christoph Studer4600f9b2014-07-22 22:44:43 +02005325 // When adding a new Style subclass here, don't forget to update
5326 // Builder.getNotificationStyleClass.
5327
Griff Hazen61a9e862014-05-22 16:05:19 -07005328 /**
5329 * Extender interface for use with {@link Builder#extend}. Extenders may be used to add
5330 * metadata or change options on a notification builder.
5331 */
5332 public interface Extender {
5333 /**
5334 * Apply this extender to a notification builder.
5335 * @param builder the builder to be modified.
5336 * @return the build object for chaining.
5337 */
5338 public Builder extend(Builder builder);
5339 }
5340
5341 /**
5342 * Helper class to add wearable extensions to notifications.
5343 * <p class="note"> See
5344 * <a href="{@docRoot}wear/notifications/creating.html">Creating Notifications
5345 * for Android Wear</a> for more information on how to use this class.
5346 * <p>
5347 * To create a notification with wearable extensions:
5348 * <ol>
5349 * <li>Create a {@link android.app.Notification.Builder}, setting any desired
5350 * properties.
5351 * <li>Create a {@link android.app.Notification.WearableExtender}.
5352 * <li>Set wearable-specific properties using the
5353 * {@code add} and {@code set} methods of {@link android.app.Notification.WearableExtender}.
5354 * <li>Call {@link android.app.Notification.Builder#extend} to apply the extensions to a
5355 * notification.
5356 * <li>Post the notification to the notification system with the
5357 * {@code NotificationManager.notify(...)} methods.
5358 * </ol>
5359 *
5360 * <pre class="prettyprint">
5361 * Notification notif = new Notification.Builder(mContext)
5362 * .setContentTitle(&quot;New mail from &quot; + sender.toString())
5363 * .setContentText(subject)
5364 * .setSmallIcon(R.drawable.new_mail)
5365 * .extend(new Notification.WearableExtender()
5366 * .setContentIcon(R.drawable.new_mail))
5367 * .build();
5368 * NotificationManager notificationManger =
5369 * (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
5370 * notificationManger.notify(0, notif);</pre>
5371 *
5372 * <p>Wearable extensions can be accessed on an existing notification by using the
5373 * {@code WearableExtender(Notification)} constructor,
5374 * and then using the {@code get} methods to access values.
5375 *
5376 * <pre class="prettyprint">
5377 * Notification.WearableExtender wearableExtender = new Notification.WearableExtender(
5378 * notification);
Griff Hazen14f57992014-05-26 09:07:14 -07005379 * List&lt;Notification&gt; pages = wearableExtender.getPages();</pre>
Griff Hazen61a9e862014-05-22 16:05:19 -07005380 */
5381 public static final class WearableExtender implements Extender {
5382 /**
5383 * Sentinel value for an action index that is unset.
5384 */
5385 public static final int UNSET_ACTION_INDEX = -1;
5386
5387 /**
5388 * Size value for use with {@link #setCustomSizePreset} to show this notification with
5389 * default sizing.
5390 * <p>For custom display notifications created using {@link #setDisplayIntent},
Paul Soulosaa4f4bf2015-08-04 11:59:45 -07005391 * the default is {@link #SIZE_MEDIUM}. All other notifications size automatically based
Griff Hazen61a9e862014-05-22 16:05:19 -07005392 * on their content.
5393 */
5394 public static final int SIZE_DEFAULT = 0;
5395
5396 /**
5397 * Size value for use with {@link #setCustomSizePreset} to show this notification
5398 * with an extra small size.
5399 * <p>This value is only applicable for custom display notifications created using
5400 * {@link #setDisplayIntent}.
5401 */
5402 public static final int SIZE_XSMALL = 1;
5403
5404 /**
5405 * Size value for use with {@link #setCustomSizePreset} to show this notification
5406 * with a small size.
5407 * <p>This value is only applicable for custom display notifications created using
5408 * {@link #setDisplayIntent}.
5409 */
5410 public static final int SIZE_SMALL = 2;
5411
5412 /**
5413 * Size value for use with {@link #setCustomSizePreset} to show this notification
5414 * with a medium size.
5415 * <p>This value is only applicable for custom display notifications created using
5416 * {@link #setDisplayIntent}.
5417 */
5418 public static final int SIZE_MEDIUM = 3;
5419
5420 /**
5421 * Size value for use with {@link #setCustomSizePreset} to show this notification
5422 * with a large size.
5423 * <p>This value is only applicable for custom display notifications created using
5424 * {@link #setDisplayIntent}.
5425 */
5426 public static final int SIZE_LARGE = 4;
5427
Griff Hazend5f11f92014-05-27 15:40:09 -07005428 /**
5429 * Size value for use with {@link #setCustomSizePreset} to show this notification
5430 * full screen.
5431 * <p>This value is only applicable for custom display notifications created using
5432 * {@link #setDisplayIntent}.
5433 */
5434 public static final int SIZE_FULL_SCREEN = 5;
5435
Griff Hazen5f2edfc2014-09-29 16:28:44 -07005436 /**
5437 * Sentinel value for use with {@link #setHintScreenTimeout} to keep the screen on for a
5438 * short amount of time when this notification is displayed on the screen. This
5439 * is the default value.
5440 */
5441 public static final int SCREEN_TIMEOUT_SHORT = 0;
5442
5443 /**
5444 * Sentinel value for use with {@link #setHintScreenTimeout} to keep the screen on
5445 * for a longer amount of time when this notification is displayed on the screen.
5446 */
5447 public static final int SCREEN_TIMEOUT_LONG = -1;
5448
Griff Hazen61a9e862014-05-22 16:05:19 -07005449 /** Notification extra which contains wearable extensions */
5450 private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS";
5451
Pete Gastaf6781d2014-10-07 15:17:05 -04005452 // Keys within EXTRA_WEARABLE_EXTENSIONS for wearable options.
Griff Hazen61a9e862014-05-22 16:05:19 -07005453 private static final String KEY_ACTIONS = "actions";
5454 private static final String KEY_FLAGS = "flags";
5455 private static final String KEY_DISPLAY_INTENT = "displayIntent";
5456 private static final String KEY_PAGES = "pages";
5457 private static final String KEY_BACKGROUND = "background";
5458 private static final String KEY_CONTENT_ICON = "contentIcon";
5459 private static final String KEY_CONTENT_ICON_GRAVITY = "contentIconGravity";
5460 private static final String KEY_CONTENT_ACTION_INDEX = "contentActionIndex";
5461 private static final String KEY_CUSTOM_SIZE_PRESET = "customSizePreset";
5462 private static final String KEY_CUSTOM_CONTENT_HEIGHT = "customContentHeight";
5463 private static final String KEY_GRAVITY = "gravity";
Griff Hazen5f2edfc2014-09-29 16:28:44 -07005464 private static final String KEY_HINT_SCREEN_TIMEOUT = "hintScreenTimeout";
Griff Hazen61a9e862014-05-22 16:05:19 -07005465
5466 // Flags bitwise-ored to mFlags
5467 private static final int FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE = 0x1;
5468 private static final int FLAG_HINT_HIDE_ICON = 1 << 1;
5469 private static final int FLAG_HINT_SHOW_BACKGROUND_ONLY = 1 << 2;
5470 private static final int FLAG_START_SCROLL_BOTTOM = 1 << 3;
Griff Hazen5f2edfc2014-09-29 16:28:44 -07005471 private static final int FLAG_HINT_AVOID_BACKGROUND_CLIPPING = 1 << 4;
Alex Hills9ab3a232016-04-05 14:54:56 -04005472 private static final int FLAG_HINT_CONTENT_INTENT_LAUNCHES_ACTIVITY = 1 << 6;
Griff Hazen61a9e862014-05-22 16:05:19 -07005473
5474 // Default value for flags integer
5475 private static final int DEFAULT_FLAGS = FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE;
5476
5477 private static final int DEFAULT_CONTENT_ICON_GRAVITY = Gravity.END;
5478 private static final int DEFAULT_GRAVITY = Gravity.BOTTOM;
5479
5480 private ArrayList<Action> mActions = new ArrayList<Action>();
5481 private int mFlags = DEFAULT_FLAGS;
5482 private PendingIntent mDisplayIntent;
5483 private ArrayList<Notification> mPages = new ArrayList<Notification>();
5484 private Bitmap mBackground;
5485 private int mContentIcon;
5486 private int mContentIconGravity = DEFAULT_CONTENT_ICON_GRAVITY;
5487 private int mContentActionIndex = UNSET_ACTION_INDEX;
5488 private int mCustomSizePreset = SIZE_DEFAULT;
5489 private int mCustomContentHeight;
5490 private int mGravity = DEFAULT_GRAVITY;
Griff Hazen5f2edfc2014-09-29 16:28:44 -07005491 private int mHintScreenTimeout;
Griff Hazen61a9e862014-05-22 16:05:19 -07005492
5493 /**
5494 * Create a {@link android.app.Notification.WearableExtender} with default
5495 * options.
5496 */
5497 public WearableExtender() {
5498 }
5499
5500 public WearableExtender(Notification notif) {
5501 Bundle wearableBundle = notif.extras.getBundle(EXTRA_WEARABLE_EXTENSIONS);
5502 if (wearableBundle != null) {
5503 List<Action> actions = wearableBundle.getParcelableArrayList(KEY_ACTIONS);
5504 if (actions != null) {
5505 mActions.addAll(actions);
5506 }
5507
5508 mFlags = wearableBundle.getInt(KEY_FLAGS, DEFAULT_FLAGS);
5509 mDisplayIntent = wearableBundle.getParcelable(KEY_DISPLAY_INTENT);
5510
5511 Notification[] pages = getNotificationArrayFromBundle(
5512 wearableBundle, KEY_PAGES);
5513 if (pages != null) {
5514 Collections.addAll(mPages, pages);
5515 }
5516
5517 mBackground = wearableBundle.getParcelable(KEY_BACKGROUND);
5518 mContentIcon = wearableBundle.getInt(KEY_CONTENT_ICON);
5519 mContentIconGravity = wearableBundle.getInt(KEY_CONTENT_ICON_GRAVITY,
5520 DEFAULT_CONTENT_ICON_GRAVITY);
5521 mContentActionIndex = wearableBundle.getInt(KEY_CONTENT_ACTION_INDEX,
5522 UNSET_ACTION_INDEX);
5523 mCustomSizePreset = wearableBundle.getInt(KEY_CUSTOM_SIZE_PRESET,
5524 SIZE_DEFAULT);
5525 mCustomContentHeight = wearableBundle.getInt(KEY_CUSTOM_CONTENT_HEIGHT);
5526 mGravity = wearableBundle.getInt(KEY_GRAVITY, DEFAULT_GRAVITY);
Griff Hazen5f2edfc2014-09-29 16:28:44 -07005527 mHintScreenTimeout = wearableBundle.getInt(KEY_HINT_SCREEN_TIMEOUT);
Griff Hazen61a9e862014-05-22 16:05:19 -07005528 }
5529 }
5530
5531 /**
5532 * Apply wearable extensions to a notification that is being built. This is typically
5533 * called by the {@link android.app.Notification.Builder#extend} method of
5534 * {@link android.app.Notification.Builder}.
5535 */
5536 @Override
5537 public Notification.Builder extend(Notification.Builder builder) {
5538 Bundle wearableBundle = new Bundle();
5539
5540 if (!mActions.isEmpty()) {
5541 wearableBundle.putParcelableArrayList(KEY_ACTIONS, mActions);
5542 }
5543 if (mFlags != DEFAULT_FLAGS) {
5544 wearableBundle.putInt(KEY_FLAGS, mFlags);
5545 }
5546 if (mDisplayIntent != null) {
5547 wearableBundle.putParcelable(KEY_DISPLAY_INTENT, mDisplayIntent);
5548 }
5549 if (!mPages.isEmpty()) {
5550 wearableBundle.putParcelableArray(KEY_PAGES, mPages.toArray(
5551 new Notification[mPages.size()]));
5552 }
5553 if (mBackground != null) {
5554 wearableBundle.putParcelable(KEY_BACKGROUND, mBackground);
5555 }
5556 if (mContentIcon != 0) {
5557 wearableBundle.putInt(KEY_CONTENT_ICON, mContentIcon);
5558 }
5559 if (mContentIconGravity != DEFAULT_CONTENT_ICON_GRAVITY) {
5560 wearableBundle.putInt(KEY_CONTENT_ICON_GRAVITY, mContentIconGravity);
5561 }
5562 if (mContentActionIndex != UNSET_ACTION_INDEX) {
5563 wearableBundle.putInt(KEY_CONTENT_ACTION_INDEX,
5564 mContentActionIndex);
5565 }
5566 if (mCustomSizePreset != SIZE_DEFAULT) {
5567 wearableBundle.putInt(KEY_CUSTOM_SIZE_PRESET, mCustomSizePreset);
5568 }
5569 if (mCustomContentHeight != 0) {
5570 wearableBundle.putInt(KEY_CUSTOM_CONTENT_HEIGHT, mCustomContentHeight);
5571 }
5572 if (mGravity != DEFAULT_GRAVITY) {
5573 wearableBundle.putInt(KEY_GRAVITY, mGravity);
5574 }
Griff Hazen5f2edfc2014-09-29 16:28:44 -07005575 if (mHintScreenTimeout != 0) {
5576 wearableBundle.putInt(KEY_HINT_SCREEN_TIMEOUT, mHintScreenTimeout);
5577 }
Griff Hazen61a9e862014-05-22 16:05:19 -07005578
5579 builder.getExtras().putBundle(EXTRA_WEARABLE_EXTENSIONS, wearableBundle);
5580 return builder;
5581 }
5582
5583 @Override
5584 public WearableExtender clone() {
5585 WearableExtender that = new WearableExtender();
5586 that.mActions = new ArrayList<Action>(this.mActions);
5587 that.mFlags = this.mFlags;
5588 that.mDisplayIntent = this.mDisplayIntent;
5589 that.mPages = new ArrayList<Notification>(this.mPages);
5590 that.mBackground = this.mBackground;
5591 that.mContentIcon = this.mContentIcon;
5592 that.mContentIconGravity = this.mContentIconGravity;
5593 that.mContentActionIndex = this.mContentActionIndex;
5594 that.mCustomSizePreset = this.mCustomSizePreset;
5595 that.mCustomContentHeight = this.mCustomContentHeight;
5596 that.mGravity = this.mGravity;
Griff Hazen5f2edfc2014-09-29 16:28:44 -07005597 that.mHintScreenTimeout = this.mHintScreenTimeout;
Griff Hazen61a9e862014-05-22 16:05:19 -07005598 return that;
5599 }
5600
5601 /**
5602 * Add a wearable action to this notification.
5603 *
5604 * <p>When wearable actions are added using this method, the set of actions that
5605 * show on a wearable device splits from devices that only show actions added
5606 * using {@link android.app.Notification.Builder#addAction}. This allows for customization
5607 * of which actions display on different devices.
5608 *
5609 * @param action the action to add to this notification
5610 * @return this object for method chaining
5611 * @see android.app.Notification.Action
5612 */
5613 public WearableExtender addAction(Action action) {
5614 mActions.add(action);
5615 return this;
5616 }
5617
5618 /**
5619 * Adds wearable actions to this notification.
5620 *
5621 * <p>When wearable actions are added using this method, the set of actions that
5622 * show on a wearable device splits from devices that only show actions added
5623 * using {@link android.app.Notification.Builder#addAction}. This allows for customization
5624 * of which actions display on different devices.
5625 *
5626 * @param actions the actions to add to this notification
5627 * @return this object for method chaining
5628 * @see android.app.Notification.Action
5629 */
5630 public WearableExtender addActions(List<Action> actions) {
5631 mActions.addAll(actions);
5632 return this;
5633 }
5634
5635 /**
5636 * Clear all wearable actions present on this builder.
5637 * @return this object for method chaining.
5638 * @see #addAction
5639 */
5640 public WearableExtender clearActions() {
5641 mActions.clear();
5642 return this;
5643 }
5644
5645 /**
5646 * Get the wearable actions present on this notification.
5647 */
5648 public List<Action> getActions() {
5649 return mActions;
5650 }
5651
5652 /**
5653 * Set an intent to launch inside of an activity view when displaying
Griff Hazen14f57992014-05-26 09:07:14 -07005654 * this notification. The {@link PendingIntent} provided should be for an activity.
5655 *
5656 * <pre class="prettyprint">
5657 * Intent displayIntent = new Intent(context, MyDisplayActivity.class);
5658 * PendingIntent displayPendingIntent = PendingIntent.getActivity(context,
5659 * 0, displayIntent, PendingIntent.FLAG_UPDATE_CURRENT);
5660 * Notification notif = new Notification.Builder(context)
5661 * .extend(new Notification.WearableExtender()
5662 * .setDisplayIntent(displayPendingIntent)
5663 * .setCustomSizePreset(Notification.WearableExtender.SIZE_MEDIUM))
5664 * .build();</pre>
5665 *
5666 * <p>The activity to launch needs to allow embedding, must be exported, and
Griff Hazen831ca9d2014-06-17 00:38:38 -07005667 * should have an empty task affinity. It is also recommended to use the device
5668 * default light theme.
Griff Hazen14f57992014-05-26 09:07:14 -07005669 *
5670 * <p>Example AndroidManifest.xml entry:
5671 * <pre class="prettyprint">
5672 * &lt;activity android:name=&quot;com.example.MyDisplayActivity&quot;
5673 * android:exported=&quot;true&quot;
5674 * android:allowEmbedded=&quot;true&quot;
Griff Hazen831ca9d2014-06-17 00:38:38 -07005675 * android:taskAffinity=&quot;&quot;
5676 * android:theme=&quot;@android:style/Theme.DeviceDefault.Light&quot; /&gt;</pre>
Griff Hazen61a9e862014-05-22 16:05:19 -07005677 *
5678 * @param intent the {@link PendingIntent} for an activity
5679 * @return this object for method chaining
5680 * @see android.app.Notification.WearableExtender#getDisplayIntent
5681 */
5682 public WearableExtender setDisplayIntent(PendingIntent intent) {
5683 mDisplayIntent = intent;
5684 return this;
5685 }
5686
5687 /**
5688 * Get the intent to launch inside of an activity view when displaying this
5689 * notification. This {@code PendingIntent} should be for an activity.
5690 */
5691 public PendingIntent getDisplayIntent() {
5692 return mDisplayIntent;
5693 }
5694
5695 /**
5696 * Add an additional page of content to display with this notification. The current
5697 * notification forms the first page, and pages added using this function form
5698 * subsequent pages. This field can be used to separate a notification into multiple
5699 * sections.
5700 *
5701 * @param page the notification to add as another page
5702 * @return this object for method chaining
5703 * @see android.app.Notification.WearableExtender#getPages
5704 */
5705 public WearableExtender addPage(Notification page) {
5706 mPages.add(page);
5707 return this;
5708 }
5709
5710 /**
5711 * Add additional pages of content to display with this notification. The current
5712 * notification forms the first page, and pages added using this function form
5713 * subsequent pages. This field can be used to separate a notification into multiple
5714 * sections.
5715 *
5716 * @param pages a list of notifications
5717 * @return this object for method chaining
5718 * @see android.app.Notification.WearableExtender#getPages
5719 */
5720 public WearableExtender addPages(List<Notification> pages) {
5721 mPages.addAll(pages);
5722 return this;
5723 }
5724
5725 /**
5726 * Clear all additional pages present on this builder.
5727 * @return this object for method chaining.
5728 * @see #addPage
5729 */
5730 public WearableExtender clearPages() {
5731 mPages.clear();
5732 return this;
5733 }
5734
5735 /**
5736 * Get the array of additional pages of content for displaying this notification. The
5737 * current notification forms the first page, and elements within this array form
5738 * subsequent pages. This field can be used to separate a notification into multiple
5739 * sections.
5740 * @return the pages for this notification
5741 */
5742 public List<Notification> getPages() {
5743 return mPages;
5744 }
5745
5746 /**
5747 * Set a background image to be displayed behind the notification content.
5748 * Contrary to the {@link android.app.Notification.BigPictureStyle}, this background
5749 * will work with any notification style.
5750 *
5751 * @param background the background bitmap
5752 * @return this object for method chaining
5753 * @see android.app.Notification.WearableExtender#getBackground
5754 */
5755 public WearableExtender setBackground(Bitmap background) {
5756 mBackground = background;
5757 return this;
5758 }
5759
5760 /**
5761 * Get a background image to be displayed behind the notification content.
5762 * Contrary to the {@link android.app.Notification.BigPictureStyle}, this background
5763 * will work with any notification style.
5764 *
5765 * @return the background image
5766 * @see android.app.Notification.WearableExtender#setBackground
5767 */
5768 public Bitmap getBackground() {
5769 return mBackground;
5770 }
5771
5772 /**
5773 * Set an icon that goes with the content of this notification.
5774 */
5775 public WearableExtender setContentIcon(int icon) {
5776 mContentIcon = icon;
5777 return this;
5778 }
5779
5780 /**
5781 * Get an icon that goes with the content of this notification.
5782 */
5783 public int getContentIcon() {
5784 return mContentIcon;
5785 }
5786
5787 /**
5788 * Set the gravity that the content icon should have within the notification display.
5789 * Supported values include {@link android.view.Gravity#START} and
5790 * {@link android.view.Gravity#END}. The default value is {@link android.view.Gravity#END}.
5791 * @see #setContentIcon
5792 */
5793 public WearableExtender setContentIconGravity(int contentIconGravity) {
5794 mContentIconGravity = contentIconGravity;
5795 return this;
5796 }
5797
5798 /**
5799 * Get the gravity that the content icon should have within the notification display.
5800 * Supported values include {@link android.view.Gravity#START} and
5801 * {@link android.view.Gravity#END}. The default value is {@link android.view.Gravity#END}.
5802 * @see #getContentIcon
5803 */
5804 public int getContentIconGravity() {
5805 return mContentIconGravity;
5806 }
5807
5808 /**
5809 * Set an action from this notification's actions to be clickable with the content of
Griff Hazen14f57992014-05-26 09:07:14 -07005810 * this notification. This action will no longer display separately from the
5811 * notification's content.
5812 *
Griff Hazenca48d352014-05-28 22:37:13 -07005813 * <p>For notifications with multiple pages, child pages can also have content actions
Griff Hazen14f57992014-05-26 09:07:14 -07005814 * set, although the list of available actions comes from the main notification and not
5815 * from the child page's notification.
5816 *
5817 * @param actionIndex The index of the action to hoist onto the current notification page.
5818 * If wearable actions were added to the main notification, this index
5819 * will apply to that list, otherwise it will apply to the regular
5820 * actions list.
Griff Hazen61a9e862014-05-22 16:05:19 -07005821 */
5822 public WearableExtender setContentAction(int actionIndex) {
5823 mContentActionIndex = actionIndex;
5824 return this;
5825 }
5826
5827 /**
Griff Hazenca48d352014-05-28 22:37:13 -07005828 * Get the index of the notification action, if any, that was specified as being clickable
5829 * with the content of this notification. This action will no longer display separately
Griff Hazen14f57992014-05-26 09:07:14 -07005830 * from the notification's content.
Griff Hazen61a9e862014-05-22 16:05:19 -07005831 *
Griff Hazenca48d352014-05-28 22:37:13 -07005832 * <p>For notifications with multiple pages, child pages can also have content actions
Griff Hazen14f57992014-05-26 09:07:14 -07005833 * set, although the list of available actions comes from the main notification and not
5834 * from the child page's notification.
5835 *
5836 * <p>If wearable specific actions were added to the main notification, this index will
5837 * apply to that list, otherwise it will apply to the regular actions list.
Griff Hazenca48d352014-05-28 22:37:13 -07005838 *
5839 * @return the action index or {@link #UNSET_ACTION_INDEX} if no action was selected.
Griff Hazen61a9e862014-05-22 16:05:19 -07005840 */
5841 public int getContentAction() {
5842 return mContentActionIndex;
5843 }
5844
5845 /**
5846 * Set the gravity that this notification should have within the available viewport space.
5847 * Supported values include {@link android.view.Gravity#TOP},
5848 * {@link android.view.Gravity#CENTER_VERTICAL} and {@link android.view.Gravity#BOTTOM}.
5849 * The default value is {@link android.view.Gravity#BOTTOM}.
5850 */
5851 public WearableExtender setGravity(int gravity) {
5852 mGravity = gravity;
5853 return this;
5854 }
5855
5856 /**
5857 * Get the gravity that this notification should have within the available viewport space.
5858 * Supported values include {@link android.view.Gravity#TOP},
5859 * {@link android.view.Gravity#CENTER_VERTICAL} and {@link android.view.Gravity#BOTTOM}.
5860 * The default value is {@link android.view.Gravity#BOTTOM}.
5861 */
5862 public int getGravity() {
5863 return mGravity;
5864 }
5865
5866 /**
5867 * Set the custom size preset for the display of this notification out of the available
5868 * presets found in {@link android.app.Notification.WearableExtender}, e.g.
5869 * {@link #SIZE_LARGE}.
5870 * <p>Some custom size presets are only applicable for custom display notifications created
5871 * using {@link android.app.Notification.WearableExtender#setDisplayIntent}. Check the
5872 * documentation for the preset in question. See also
5873 * {@link #setCustomContentHeight} and {@link #getCustomSizePreset}.
5874 */
5875 public WearableExtender setCustomSizePreset(int sizePreset) {
5876 mCustomSizePreset = sizePreset;
5877 return this;
5878 }
5879
5880 /**
5881 * Get the custom size preset for the display of this notification out of the available
5882 * presets found in {@link android.app.Notification.WearableExtender}, e.g.
5883 * {@link #SIZE_LARGE}.
5884 * <p>Some custom size presets are only applicable for custom display notifications created
5885 * using {@link #setDisplayIntent}. Check the documentation for the preset in question.
5886 * See also {@link #setCustomContentHeight} and {@link #setCustomSizePreset}.
5887 */
5888 public int getCustomSizePreset() {
5889 return mCustomSizePreset;
5890 }
5891
5892 /**
5893 * Set the custom height in pixels for the display of this notification's content.
5894 * <p>This option is only available for custom display notifications created
5895 * using {@link android.app.Notification.WearableExtender#setDisplayIntent}. See also
5896 * {@link android.app.Notification.WearableExtender#setCustomSizePreset} and
5897 * {@link #getCustomContentHeight}.
5898 */
5899 public WearableExtender setCustomContentHeight(int height) {
5900 mCustomContentHeight = height;
5901 return this;
5902 }
5903
5904 /**
5905 * Get the custom height in pixels for the display of this notification's content.
5906 * <p>This option is only available for custom display notifications created
5907 * using {@link #setDisplayIntent}. See also {@link #setCustomSizePreset} and
5908 * {@link #setCustomContentHeight}.
5909 */
5910 public int getCustomContentHeight() {
5911 return mCustomContentHeight;
5912 }
5913
5914 /**
5915 * Set whether the scrolling position for the contents of this notification should start
5916 * at the bottom of the contents instead of the top when the contents are too long to
5917 * display within the screen. Default is false (start scroll at the top).
5918 */
5919 public WearableExtender setStartScrollBottom(boolean startScrollBottom) {
5920 setFlag(FLAG_START_SCROLL_BOTTOM, startScrollBottom);
5921 return this;
5922 }
5923
5924 /**
5925 * Get whether the scrolling position for the contents of this notification should start
5926 * at the bottom of the contents instead of the top when the contents are too long to
5927 * display within the screen. Default is false (start scroll at the top).
5928 */
5929 public boolean getStartScrollBottom() {
5930 return (mFlags & FLAG_START_SCROLL_BOTTOM) != 0;
5931 }
5932
5933 /**
5934 * Set whether the content intent is available when the wearable device is not connected
5935 * to a companion device. The user can still trigger this intent when the wearable device
5936 * is offline, but a visual hint will indicate that the content intent may not be available.
5937 * Defaults to true.
5938 */
5939 public WearableExtender setContentIntentAvailableOffline(
5940 boolean contentIntentAvailableOffline) {
5941 setFlag(FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE, contentIntentAvailableOffline);
5942 return this;
5943 }
5944
5945 /**
5946 * Get whether the content intent is available when the wearable device is not connected
5947 * to a companion device. The user can still trigger this intent when the wearable device
5948 * is offline, but a visual hint will indicate that the content intent may not be available.
5949 * Defaults to true.
5950 */
5951 public boolean getContentIntentAvailableOffline() {
5952 return (mFlags & FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE) != 0;
5953 }
5954
5955 /**
5956 * Set a hint that this notification's icon should not be displayed.
5957 * @param hintHideIcon {@code true} to hide the icon, {@code false} otherwise.
5958 * @return this object for method chaining
5959 */
5960 public WearableExtender setHintHideIcon(boolean hintHideIcon) {
5961 setFlag(FLAG_HINT_HIDE_ICON, hintHideIcon);
5962 return this;
5963 }
5964
5965 /**
5966 * Get a hint that this notification's icon should not be displayed.
5967 * @return {@code true} if this icon should not be displayed, false otherwise.
5968 * The default value is {@code false} if this was never set.
5969 */
5970 public boolean getHintHideIcon() {
5971 return (mFlags & FLAG_HINT_HIDE_ICON) != 0;
5972 }
5973
5974 /**
5975 * Set a visual hint that only the background image of this notification should be
5976 * displayed, and other semantic content should be hidden. This hint is only applicable
5977 * to sub-pages added using {@link #addPage}.
5978 */
5979 public WearableExtender setHintShowBackgroundOnly(boolean hintShowBackgroundOnly) {
5980 setFlag(FLAG_HINT_SHOW_BACKGROUND_ONLY, hintShowBackgroundOnly);
5981 return this;
5982 }
5983
5984 /**
5985 * Get a visual hint that only the background image of this notification should be
5986 * displayed, and other semantic content should be hidden. This hint is only applicable
5987 * to sub-pages added using {@link android.app.Notification.WearableExtender#addPage}.
5988 */
5989 public boolean getHintShowBackgroundOnly() {
5990 return (mFlags & FLAG_HINT_SHOW_BACKGROUND_ONLY) != 0;
5991 }
5992
Griff Hazen5f2edfc2014-09-29 16:28:44 -07005993 /**
Griff Hazen9c5be4e2014-11-17 17:47:50 -08005994 * Set a hint that this notification's background should not be clipped if possible,
5995 * and should instead be resized to fully display on the screen, retaining the aspect
5996 * ratio of the image. This can be useful for images like barcodes or qr codes.
Griff Hazen5f2edfc2014-09-29 16:28:44 -07005997 * @param hintAvoidBackgroundClipping {@code true} to avoid clipping if possible.
5998 * @return this object for method chaining
5999 */
6000 public WearableExtender setHintAvoidBackgroundClipping(
6001 boolean hintAvoidBackgroundClipping) {
6002 setFlag(FLAG_HINT_AVOID_BACKGROUND_CLIPPING, hintAvoidBackgroundClipping);
6003 return this;
6004 }
6005
6006 /**
Griff Hazen9c5be4e2014-11-17 17:47:50 -08006007 * Get a hint that this notification's background should not be clipped if possible,
6008 * and should instead be resized to fully display on the screen, retaining the aspect
6009 * ratio of the image. This can be useful for images like barcodes or qr codes.
Griff Hazen5f2edfc2014-09-29 16:28:44 -07006010 * @return {@code true} if it's ok if the background is clipped on the screen, false
6011 * otherwise. The default value is {@code false} if this was never set.
6012 */
6013 public boolean getHintAvoidBackgroundClipping() {
6014 return (mFlags & FLAG_HINT_AVOID_BACKGROUND_CLIPPING) != 0;
6015 }
6016
6017 /**
6018 * Set a hint that the screen should remain on for at least this duration when
6019 * this notification is displayed on the screen.
6020 * @param timeout The requested screen timeout in milliseconds. Can also be either
6021 * {@link #SCREEN_TIMEOUT_SHORT} or {@link #SCREEN_TIMEOUT_LONG}.
6022 * @return this object for method chaining
6023 */
6024 public WearableExtender setHintScreenTimeout(int timeout) {
6025 mHintScreenTimeout = timeout;
6026 return this;
6027 }
6028
6029 /**
6030 * Get the duration, in milliseconds, that the screen should remain on for
6031 * when this notification is displayed.
6032 * @return the duration in milliseconds if > 0, or either one of the sentinel values
6033 * {@link #SCREEN_TIMEOUT_SHORT} or {@link #SCREEN_TIMEOUT_LONG}.
6034 */
6035 public int getHintScreenTimeout() {
6036 return mHintScreenTimeout;
6037 }
6038
Alex Hills9ab3a232016-04-05 14:54:56 -04006039 /**
6040 * Set a hint that this notification's content intent will launch an {@link Activity}
6041 * directly, telling the platform that it can generate the appropriate transitions.
6042 * @param hintContentIntentLaunchesActivity {@code true} if the content intent will launch
6043 * an activity and transitions should be generated, false otherwise.
6044 * @return this object for method chaining
6045 */
6046 public WearableExtender setHintContentIntentLaunchesActivity(
6047 boolean hintContentIntentLaunchesActivity) {
6048 setFlag(FLAG_HINT_CONTENT_INTENT_LAUNCHES_ACTIVITY, hintContentIntentLaunchesActivity);
6049 return this;
6050 }
6051
6052 /**
6053 * Get a hint that this notification's content intent will launch an {@link Activity}
6054 * directly, telling the platform that it can generate the appropriate transitions
6055 * @return {@code true} if the content intent will launch an activity and transitions should
6056 * be generated, false otherwise. The default value is {@code false} if this was never set.
6057 */
6058 public boolean getHintContentIntentLaunchesActivity() {
6059 return (mFlags & FLAG_HINT_CONTENT_INTENT_LAUNCHES_ACTIVITY) != 0;
6060 }
6061
Griff Hazen61a9e862014-05-22 16:05:19 -07006062 private void setFlag(int mask, boolean value) {
6063 if (value) {
6064 mFlags |= mask;
6065 } else {
6066 mFlags &= ~mask;
6067 }
6068 }
6069 }
6070
6071 /**
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08006072 * <p>Helper class to add Android Auto extensions to notifications. To create a notification
6073 * with car extensions:
6074 *
6075 * <ol>
6076 * <li>Create an {@link Notification.Builder}, setting any desired
6077 * properties.
6078 * <li>Create a {@link CarExtender}.
6079 * <li>Set car-specific properties using the {@code add} and {@code set} methods of
6080 * {@link CarExtender}.
6081 * <li>Call {@link Notification.Builder#extend(Notification.Extender)}
6082 * to apply the extensions to a notification.
6083 * </ol>
6084 *
6085 * <pre class="prettyprint">
6086 * Notification notification = new Notification.Builder(context)
6087 * ...
6088 * .extend(new CarExtender()
6089 * .set*(...))
6090 * .build();
6091 * </pre>
6092 *
6093 * <p>Car extensions can be accessed on an existing notification by using the
6094 * {@code CarExtender(Notification)} constructor, and then using the {@code get} methods
6095 * to access values.
6096 */
6097 public static final class CarExtender implements Extender {
6098 private static final String TAG = "CarExtender";
6099
6100 private static final String EXTRA_CAR_EXTENDER = "android.car.EXTENSIONS";
6101 private static final String EXTRA_LARGE_ICON = "large_icon";
6102 private static final String EXTRA_CONVERSATION = "car_conversation";
6103 private static final String EXTRA_COLOR = "app_color";
6104
6105 private Bitmap mLargeIcon;
6106 private UnreadConversation mUnreadConversation;
6107 private int mColor = Notification.COLOR_DEFAULT;
6108
6109 /**
6110 * Create a {@link CarExtender} with default options.
6111 */
6112 public CarExtender() {
6113 }
6114
6115 /**
6116 * Create a {@link CarExtender} from the CarExtender options of an existing Notification.
6117 *
6118 * @param notif The notification from which to copy options.
6119 */
6120 public CarExtender(Notification notif) {
6121 Bundle carBundle = notif.extras == null ?
6122 null : notif.extras.getBundle(EXTRA_CAR_EXTENDER);
6123 if (carBundle != null) {
6124 mLargeIcon = carBundle.getParcelable(EXTRA_LARGE_ICON);
6125 mColor = carBundle.getInt(EXTRA_COLOR, Notification.COLOR_DEFAULT);
6126
6127 Bundle b = carBundle.getBundle(EXTRA_CONVERSATION);
6128 mUnreadConversation = UnreadConversation.getUnreadConversationFromBundle(b);
6129 }
6130 }
6131
6132 /**
6133 * Apply car extensions to a notification that is being built. This is typically called by
6134 * the {@link Notification.Builder#extend(Notification.Extender)}
6135 * method of {@link Notification.Builder}.
6136 */
6137 @Override
6138 public Notification.Builder extend(Notification.Builder builder) {
6139 Bundle carExtensions = new Bundle();
6140
6141 if (mLargeIcon != null) {
6142 carExtensions.putParcelable(EXTRA_LARGE_ICON, mLargeIcon);
6143 }
6144 if (mColor != Notification.COLOR_DEFAULT) {
6145 carExtensions.putInt(EXTRA_COLOR, mColor);
6146 }
6147
6148 if (mUnreadConversation != null) {
6149 Bundle b = mUnreadConversation.getBundleForUnreadConversation();
6150 carExtensions.putBundle(EXTRA_CONVERSATION, b);
6151 }
6152
6153 builder.getExtras().putBundle(EXTRA_CAR_EXTENDER, carExtensions);
6154 return builder;
6155 }
6156
6157 /**
6158 * Sets the accent color to use when Android Auto presents the notification.
6159 *
6160 * Android Auto uses the color set with {@link Notification.Builder#setColor(int)}
6161 * to accent the displayed notification. However, not all colors are acceptable in an
6162 * automotive setting. This method can be used to override the color provided in the
6163 * notification in such a situation.
6164 */
Tor Norbye80756e32015-03-02 09:39:27 -08006165 public CarExtender setColor(@ColorInt int color) {
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08006166 mColor = color;
6167 return this;
6168 }
6169
6170 /**
6171 * Gets the accent color.
6172 *
Julia Reynoldsd9228f12015-10-20 10:37:27 -04006173 * @see #setColor
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08006174 */
Tor Norbye80756e32015-03-02 09:39:27 -08006175 @ColorInt
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08006176 public int getColor() {
6177 return mColor;
6178 }
6179
6180 /**
6181 * Sets the large icon of the car notification.
6182 *
6183 * If no large icon is set in the extender, Android Auto will display the icon
6184 * specified by {@link Notification.Builder#setLargeIcon(android.graphics.Bitmap)}
6185 *
6186 * @param largeIcon The large icon to use in the car notification.
6187 * @return This object for method chaining.
6188 */
6189 public CarExtender setLargeIcon(Bitmap largeIcon) {
6190 mLargeIcon = largeIcon;
6191 return this;
6192 }
6193
6194 /**
6195 * Gets the large icon used in this car notification, or null if no icon has been set.
6196 *
6197 * @return The large icon for the car notification.
6198 * @see CarExtender#setLargeIcon
6199 */
6200 public Bitmap getLargeIcon() {
6201 return mLargeIcon;
6202 }
6203
6204 /**
6205 * Sets the unread conversation in a message notification.
6206 *
6207 * @param unreadConversation The unread part of the conversation this notification conveys.
6208 * @return This object for method chaining.
6209 */
6210 public CarExtender setUnreadConversation(UnreadConversation unreadConversation) {
6211 mUnreadConversation = unreadConversation;
6212 return this;
6213 }
6214
6215 /**
6216 * Returns the unread conversation conveyed by this notification.
6217 * @see #setUnreadConversation(UnreadConversation)
6218 */
6219 public UnreadConversation getUnreadConversation() {
6220 return mUnreadConversation;
6221 }
6222
6223 /**
6224 * A class which holds the unread messages from a conversation.
6225 */
6226 public static class UnreadConversation {
6227 private static final String KEY_AUTHOR = "author";
6228 private static final String KEY_TEXT = "text";
6229 private static final String KEY_MESSAGES = "messages";
6230 private static final String KEY_REMOTE_INPUT = "remote_input";
6231 private static final String KEY_ON_REPLY = "on_reply";
6232 private static final String KEY_ON_READ = "on_read";
6233 private static final String KEY_PARTICIPANTS = "participants";
6234 private static final String KEY_TIMESTAMP = "timestamp";
6235
6236 private final String[] mMessages;
6237 private final RemoteInput mRemoteInput;
6238 private final PendingIntent mReplyPendingIntent;
6239 private final PendingIntent mReadPendingIntent;
6240 private final String[] mParticipants;
6241 private final long mLatestTimestamp;
6242
6243 UnreadConversation(String[] messages, RemoteInput remoteInput,
6244 PendingIntent replyPendingIntent, PendingIntent readPendingIntent,
6245 String[] participants, long latestTimestamp) {
6246 mMessages = messages;
6247 mRemoteInput = remoteInput;
6248 mReadPendingIntent = readPendingIntent;
6249 mReplyPendingIntent = replyPendingIntent;
6250 mParticipants = participants;
6251 mLatestTimestamp = latestTimestamp;
6252 }
6253
6254 /**
6255 * Gets the list of messages conveyed by this notification.
6256 */
6257 public String[] getMessages() {
6258 return mMessages;
6259 }
6260
6261 /**
6262 * Gets the remote input that will be used to convey the response to a message list, or
6263 * null if no such remote input exists.
6264 */
6265 public RemoteInput getRemoteInput() {
6266 return mRemoteInput;
6267 }
6268
6269 /**
6270 * Gets the pending intent that will be triggered when the user replies to this
6271 * notification.
6272 */
6273 public PendingIntent getReplyPendingIntent() {
6274 return mReplyPendingIntent;
6275 }
6276
6277 /**
6278 * Gets the pending intent that Android Auto will send after it reads aloud all messages
6279 * in this object's message list.
6280 */
6281 public PendingIntent getReadPendingIntent() {
6282 return mReadPendingIntent;
6283 }
6284
6285 /**
6286 * Gets the participants in the conversation.
6287 */
6288 public String[] getParticipants() {
6289 return mParticipants;
6290 }
6291
6292 /**
6293 * Gets the firs participant in the conversation.
6294 */
6295 public String getParticipant() {
6296 return mParticipants.length > 0 ? mParticipants[0] : null;
6297 }
6298
6299 /**
6300 * Gets the timestamp of the conversation.
6301 */
6302 public long getLatestTimestamp() {
6303 return mLatestTimestamp;
6304 }
6305
6306 Bundle getBundleForUnreadConversation() {
6307 Bundle b = new Bundle();
6308 String author = null;
6309 if (mParticipants != null && mParticipants.length > 1) {
6310 author = mParticipants[0];
6311 }
6312 Parcelable[] messages = new Parcelable[mMessages.length];
6313 for (int i = 0; i < messages.length; i++) {
6314 Bundle m = new Bundle();
6315 m.putString(KEY_TEXT, mMessages[i]);
6316 m.putString(KEY_AUTHOR, author);
6317 messages[i] = m;
6318 }
6319 b.putParcelableArray(KEY_MESSAGES, messages);
6320 if (mRemoteInput != null) {
6321 b.putParcelable(KEY_REMOTE_INPUT, mRemoteInput);
6322 }
6323 b.putParcelable(KEY_ON_REPLY, mReplyPendingIntent);
6324 b.putParcelable(KEY_ON_READ, mReadPendingIntent);
6325 b.putStringArray(KEY_PARTICIPANTS, mParticipants);
6326 b.putLong(KEY_TIMESTAMP, mLatestTimestamp);
6327 return b;
6328 }
6329
6330 static UnreadConversation getUnreadConversationFromBundle(Bundle b) {
6331 if (b == null) {
6332 return null;
6333 }
6334 Parcelable[] parcelableMessages = b.getParcelableArray(KEY_MESSAGES);
6335 String[] messages = null;
6336 if (parcelableMessages != null) {
6337 String[] tmp = new String[parcelableMessages.length];
6338 boolean success = true;
6339 for (int i = 0; i < tmp.length; i++) {
6340 if (!(parcelableMessages[i] instanceof Bundle)) {
6341 success = false;
6342 break;
6343 }
6344 tmp[i] = ((Bundle) parcelableMessages[i]).getString(KEY_TEXT);
6345 if (tmp[i] == null) {
6346 success = false;
6347 break;
6348 }
6349 }
6350 if (success) {
6351 messages = tmp;
6352 } else {
6353 return null;
6354 }
6355 }
6356
6357 PendingIntent onRead = b.getParcelable(KEY_ON_READ);
6358 PendingIntent onReply = b.getParcelable(KEY_ON_REPLY);
6359
6360 RemoteInput remoteInput = b.getParcelable(KEY_REMOTE_INPUT);
6361
6362 String[] participants = b.getStringArray(KEY_PARTICIPANTS);
6363 if (participants == null || participants.length != 1) {
6364 return null;
6365 }
6366
6367 return new UnreadConversation(messages,
6368 remoteInput,
6369 onReply,
6370 onRead,
6371 participants, b.getLong(KEY_TIMESTAMP));
6372 }
6373 };
6374
6375 /**
6376 * Builder class for {@link CarExtender.UnreadConversation} objects.
6377 */
6378 public static class Builder {
6379 private final List<String> mMessages = new ArrayList<String>();
6380 private final String mParticipant;
6381 private RemoteInput mRemoteInput;
6382 private PendingIntent mReadPendingIntent;
6383 private PendingIntent mReplyPendingIntent;
6384 private long mLatestTimestamp;
6385
6386 /**
6387 * Constructs a new builder for {@link CarExtender.UnreadConversation}.
6388 *
6389 * @param name The name of the other participant in the conversation.
6390 */
6391 public Builder(String name) {
6392 mParticipant = name;
6393 }
6394
6395 /**
6396 * Appends a new unread message to the list of messages for this conversation.
6397 *
6398 * The messages should be added from oldest to newest.
6399 *
6400 * @param message The text of the new unread message.
6401 * @return This object for method chaining.
6402 */
6403 public Builder addMessage(String message) {
6404 mMessages.add(message);
6405 return this;
6406 }
6407
6408 /**
6409 * Sets the pending intent and remote input which will convey the reply to this
6410 * notification.
6411 *
6412 * @param pendingIntent The pending intent which will be triggered on a reply.
6413 * @param remoteInput The remote input parcelable which will carry the reply.
6414 * @return This object for method chaining.
6415 *
6416 * @see CarExtender.UnreadConversation#getRemoteInput
6417 * @see CarExtender.UnreadConversation#getReplyPendingIntent
6418 */
6419 public Builder setReplyAction(
6420 PendingIntent pendingIntent, RemoteInput remoteInput) {
6421 mRemoteInput = remoteInput;
6422 mReplyPendingIntent = pendingIntent;
6423
6424 return this;
6425 }
6426
6427 /**
6428 * Sets the pending intent that will be sent once the messages in this notification
6429 * are read.
6430 *
6431 * @param pendingIntent The pending intent to use.
6432 * @return This object for method chaining.
6433 */
6434 public Builder setReadPendingIntent(PendingIntent pendingIntent) {
6435 mReadPendingIntent = pendingIntent;
6436 return this;
6437 }
6438
6439 /**
6440 * Sets the timestamp of the most recent message in an unread conversation.
6441 *
6442 * If a messaging notification has been posted by your application and has not
6443 * yet been cancelled, posting a later notification with the same id and tag
6444 * but without a newer timestamp may result in Android Auto not displaying a
6445 * heads up notification for the later notification.
6446 *
6447 * @param timestamp The timestamp of the most recent message in the conversation.
6448 * @return This object for method chaining.
6449 */
6450 public Builder setLatestTimestamp(long timestamp) {
6451 mLatestTimestamp = timestamp;
6452 return this;
6453 }
6454
6455 /**
6456 * Builds a new unread conversation object.
6457 *
6458 * @return The new unread conversation object.
6459 */
6460 public UnreadConversation build() {
6461 String[] messages = mMessages.toArray(new String[mMessages.size()]);
6462 String[] participants = { mParticipant };
6463 return new UnreadConversation(messages, mRemoteInput, mReplyPendingIntent,
6464 mReadPendingIntent, participants, mLatestTimestamp);
6465 }
6466 }
6467 }
6468
6469 /**
Griff Hazen61a9e862014-05-22 16:05:19 -07006470 * Get an array of Notification objects from a parcelable array bundle field.
6471 * Update the bundle to have a typed array so fetches in the future don't need
6472 * to do an array copy.
6473 */
6474 private static Notification[] getNotificationArrayFromBundle(Bundle bundle, String key) {
6475 Parcelable[] array = bundle.getParcelableArray(key);
6476 if (array instanceof Notification[] || array == null) {
6477 return (Notification[]) array;
6478 }
6479 Notification[] typedArray = Arrays.copyOf(array, array.length,
6480 Notification[].class);
6481 bundle.putParcelableArray(key, typedArray);
6482 return typedArray;
6483 }
Christoph Studer4600f9b2014-07-22 22:44:43 +02006484
6485 private static class BuilderRemoteViews extends RemoteViews {
6486 public BuilderRemoteViews(Parcel parcel) {
6487 super(parcel);
6488 }
6489
Kenny Guy77320062014-08-27 21:37:15 +01006490 public BuilderRemoteViews(ApplicationInfo appInfo, int layoutId) {
6491 super(appInfo, layoutId);
Christoph Studer4600f9b2014-07-22 22:44:43 +02006492 }
6493
6494 @Override
6495 public BuilderRemoteViews clone() {
6496 Parcel p = Parcel.obtain();
6497 writeToParcel(p, 0);
6498 p.setDataPosition(0);
6499 BuilderRemoteViews brv = new BuilderRemoteViews(p);
6500 p.recycle();
6501 return brv;
6502 }
6503 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006504}