blob: 5a448f03a118e9bbdc9e296751cfe3e19d4e78b6 [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;
Griff Hazen61a9e862014-05-22 16:05:19 -070058import android.view.Gravity;
Selim Cinekea4bef72015-12-02 15:51:10 -080059import android.view.NotificationHeaderView;
Joe Onorato8595a3d2010-11-19 18:12:07 -080060import android.view.View;
Selim Cinek954cc232016-05-20 13:29:23 -070061import android.view.ViewGroup;
Jeff Sharkey1c400132011-08-05 14:50:13 -070062import android.widget.ProgressBar;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080063import android.widget.RemoteViews;
64
Griff Hazen959591e2014-05-15 22:26:18 -070065import com.android.internal.R;
Griff Hazenc091ba82014-05-16 10:13:26 -070066import com.android.internal.util.NotificationColorUtil;
Griff Hazen959591e2014-05-15 22:26:18 -070067
Tor Norbyed9273d62013-05-30 15:59:53 -070068import java.lang.annotation.Retention;
69import java.lang.annotation.RetentionPolicy;
Christoph Studer4600f9b2014-07-22 22:44:43 +020070import java.lang.reflect.Constructor;
Daniel Sandler2561b0b2012-02-13 21:04:12 -050071import java.util.ArrayList;
Griff Hazen61a9e862014-05-22 16:05:19 -070072import java.util.Arrays;
Griff Hazen5cadc3b2014-05-20 09:55:39 -070073import java.util.Collections;
Griff Hazen61a9e862014-05-22 16:05:19 -070074import java.util.List;
Dan Sandler50128532015-12-08 15:42:41 -050075import java.util.Set;
Joe Onorato561d3852010-11-20 18:09:34 -080076
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080077/**
78 * A class that represents how a persistent notification is to be presented to
79 * the user using the {@link android.app.NotificationManager}.
80 *
Joe Onoratocb109a02011-01-18 17:57:41 -080081 * <p>The {@link Notification.Builder Notification.Builder} has been added to make it
82 * easier to construct Notifications.</p>
83 *
Joe Fernandez558459f2011-10-13 16:47:36 -070084 * <div class="special reference">
85 * <h3>Developer Guides</h3>
86 * <p>For a guide to creating notifications, read the
87 * <a href="{@docRoot}guide/topics/ui/notifiers/notifications.html">Status Bar Notifications</a>
88 * developer guide.</p>
89 * </div>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080090 */
91public class Notification implements Parcelable
92{
Daniel Sandlerdcbaf662013-04-26 16:23:09 -040093 private static final String TAG = "Notification";
94
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080095 /**
Daniel Sandler01df1c62014-06-09 10:54:01 -040096 * An activity that provides a user interface for adjusting notification preferences for its
97 * containing application. Optional but recommended for apps that post
98 * {@link android.app.Notification Notifications}.
99 */
100 @SdkConstant(SdkConstantType.INTENT_CATEGORY)
101 public static final String INTENT_CATEGORY_NOTIFICATION_PREFERENCES
102 = "android.intent.category.NOTIFICATION_PREFERENCES";
103
104 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800105 * Use all default values (where applicable).
106 */
107 public static final int DEFAULT_ALL = ~0;
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500108
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800109 /**
110 * Use the default notification sound. This will ignore any given
111 * {@link #sound}.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500112 *
Chris Wren47c20a12014-06-18 17:27:29 -0400113 * <p>
114 * A notification that is noisy is more likely to be presented as a heads-up notification.
115 * </p>
116 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800117 * @see #defaults
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500118 */
119
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800120 public static final int DEFAULT_SOUND = 1;
121
122 /**
123 * Use the default notification vibrate. This will ignore any given
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500124 * {@link #vibrate}. Using phone vibration requires the
Scott Mainb8b36452009-04-26 15:50:49 -0700125 * {@link android.Manifest.permission#VIBRATE VIBRATE} permission.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500126 *
Chris Wren47c20a12014-06-18 17:27:29 -0400127 * <p>
128 * A notification that vibrates is more likely to be presented as a heads-up notification.
129 * </p>
130 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800131 * @see #defaults
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500132 */
133
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800134 public static final int DEFAULT_VIBRATE = 2;
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500135
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800136 /**
137 * Use the default notification lights. This will ignore the
138 * {@link #FLAG_SHOW_LIGHTS} bit, and {@link #ledARGB}, {@link #ledOffMS}, or
139 * {@link #ledOnMS}.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500140 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800141 * @see #defaults
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500142 */
143
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800144 public static final int DEFAULT_LIGHTS = 4;
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500145
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800146 /**
Christoph Studer535ec612014-09-03 15:47:47 +0200147 * Maximum length of CharSequences accepted by Builder and friends.
148 *
149 * <p>
150 * Avoids spamming the system with overly large strings such as full e-mails.
151 */
152 private static final int MAX_CHARSEQUENCE_LENGTH = 5 * 1024;
153
154 /**
Adrian Roose458aa82015-12-08 16:17:19 -0800155 * Maximum entries of reply text that are accepted by Builder and friends.
156 */
157 private static final int MAX_REPLY_HISTORY = 5;
158
159 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500160 * A timestamp related to this notification, in milliseconds since the epoch.
Joe Malin8d40d042012-11-05 11:36:40 -0800161 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500162 * Default value: {@link System#currentTimeMillis() Now}.
163 *
164 * Choose a timestamp that will be most relevant to the user. For most finite events, this
165 * corresponds to the time the event happened (or will happen, in the case of events that have
166 * yet to occur but about which the user is being informed). Indefinite events should be
Joe Malin8d40d042012-11-05 11:36:40 -0800167 * timestamped according to when the activity began.
168 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500169 * Some examples:
Joe Malin8d40d042012-11-05 11:36:40 -0800170 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500171 * <ul>
172 * <li>Notification of a new chat message should be stamped when the message was received.</li>
173 * <li>Notification of an ongoing file download (with a progress bar, for example) should be stamped when the download started.</li>
174 * <li>Notification of a completed file download should be stamped when the download finished.</li>
175 * <li>Notification of an upcoming meeting should be stamped with the time the meeting will begin (that is, in the future).</li>
176 * <li>Notification of an ongoing stopwatch (increasing timer) should be stamped with the watch's start time.
177 * <li>Notification of an ongoing countdown timer should be stamped with the timer's end time.
Joe Malin8d40d042012-11-05 11:36:40 -0800178 * </ul>
179 *
Selim Cinek0ff1ce602016-04-05 18:27:16 -0700180 * For apps targeting {@link android.os.Build.VERSION_CODES#N} and above, this time is not shown
181 * anymore by default and must be opted into by using
182 * {@link android.app.Notification.Builder#setShowWhen(boolean)}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800183 */
184 public long when;
185
186 /**
Selim Cinekb85f36fd2016-04-20 18:46:36 -0700187 * The creation time of the notification
188 */
189 private long creationTime;
190
191 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800192 * The resource id of a drawable to use as the icon in the status bar.
Dan Sandler86647982015-05-13 23:41:13 -0400193 *
194 * @deprecated Use {@link Builder#setSmallIcon(Icon)} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800195 */
Dan Sandler86647982015-05-13 23:41:13 -0400196 @Deprecated
Tor Norbye7b9c9122013-05-30 16:48:33 -0700197 @DrawableRes
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800198 public int icon;
199
200 /**
Joe Onorato46439ce2010-11-19 13:56:21 -0800201 * If the icon in the status bar is to have more than one level, you can set this. Otherwise,
202 * leave it at its default value of 0.
203 *
204 * @see android.widget.ImageView#setImageLevel
Griff Hazen959591e2014-05-15 22:26:18 -0700205 * @see android.graphics.drawable.Drawable#setLevel
Joe Onorato46439ce2010-11-19 13:56:21 -0800206 */
207 public int iconLevel;
208
209 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500210 * The number of events that this notification represents. For example, in a new mail
211 * notification, this could be the number of unread messages.
Joe Malin8d40d042012-11-05 11:36:40 -0800212 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500213 * The system may or may not use this field to modify the appearance of the notification. For
214 * example, before {@link android.os.Build.VERSION_CODES#HONEYCOMB}, this number was
215 * superimposed over the icon in the status bar. Starting with
216 * {@link android.os.Build.VERSION_CODES#HONEYCOMB}, the template used by
217 * {@link Notification.Builder} has displayed the number in the expanded notification view.
Joe Malin8d40d042012-11-05 11:36:40 -0800218 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500219 * If the number is 0 or negative, it is never shown.
Selim Cinek0f9dd1e2016-04-05 17:03:40 -0700220 *
221 * @deprecated this number is not shown anymore
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800222 */
223 public int number;
224
225 /**
226 * The intent to execute when the expanded status entry is clicked. If
227 * this is an activity, it must include the
228 * {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} flag, which requires
Scott Main7aee61f2011-02-08 11:25:01 -0800229 * that you take care of task management as described in the
230 * <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back
Dianne Hackborn6ceca582012-01-10 15:24:26 -0800231 * Stack</a> document. In particular, make sure to read the notification section
232 * <a href="{@docRoot}guide/topics/ui/notifiers/notifications.html#HandlingNotifications">Handling
233 * Notifications</a> for the correct ways to launch an application from a
234 * notification.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800235 */
236 public PendingIntent contentIntent;
237
238 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500239 * The intent to execute when the notification is explicitly dismissed by the user, either with
240 * the "Clear All" button or by swiping it away individually.
241 *
242 * This probably shouldn't be launching an activity since several of those will be sent
243 * at the same time.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800244 */
245 public PendingIntent deleteIntent;
246
247 /**
Dianne Hackborn170bae72010-09-03 15:14:28 -0700248 * An intent to launch instead of posting the notification to the status bar.
Joe Onoratocb109a02011-01-18 17:57:41 -0800249 *
Chris Wren47c20a12014-06-18 17:27:29 -0400250 * <p>
251 * The system UI may choose to display a heads-up notification, instead of
252 * launching this intent, while the user is using the device.
253 * </p>
254 *
Joe Onoratocb109a02011-01-18 17:57:41 -0800255 * @see Notification.Builder#setFullScreenIntent
Daniel Sandlere46cbd32010-06-17 10:35:26 -0400256 */
257 public PendingIntent fullScreenIntent;
258
259 /**
Dan Sandler5fcdf6e2014-07-18 11:31:15 -0400260 * Text that summarizes this notification for accessibility services.
261 *
262 * As of the L release, this text is no longer shown on screen, but it is still useful to
263 * accessibility services (where it serves as an audible announcement of the notification's
264 * appearance).
Joe Onoratoef1e7762010-09-17 18:38:38 -0400265 *
Joe Onorato46439ce2010-11-19 13:56:21 -0800266 * @see #tickerView
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800267 */
268 public CharSequence tickerText;
269
270 /**
Dan Sandler5fcdf6e2014-07-18 11:31:15 -0400271 * Formerly, a view showing the {@link #tickerText}.
272 *
273 * No longer displayed in the status bar as of API 21.
Joe Onoratoef1e7762010-09-17 18:38:38 -0400274 */
Dan Sandler5fcdf6e2014-07-18 11:31:15 -0400275 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -0800276 public RemoteViews tickerView;
Joe Onoratoef1e7762010-09-17 18:38:38 -0400277
278 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400279 * The view that will represent this notification in the notification list (which is pulled
280 * down from the status bar).
281 *
Julia Reynoldsd4ea7412016-02-17 14:00:56 -0500282 * As of N, this field may be null. The notification view is determined by the inputs
283 * to {@link Notification.Builder}; a custom RemoteViews can optionally be
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400284 * supplied with {@link Notification.Builder#setCustomContentView(RemoteViews)}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800285 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400286 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800287 public RemoteViews contentView;
288
Daniel Sandlera0a938c2012-03-15 08:42:37 -0400289 /**
Daniel Sandler4dfbe832012-04-11 14:51:46 -0400290 * A large-format version of {@link #contentView}, giving the Notification an
Daniel Sandlerf3b73432012-03-27 15:01:25 -0400291 * opportunity to show more detail. The system UI may choose to show this
292 * instead of the normal content view at its discretion.
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400293 *
Julia Reynoldsd4ea7412016-02-17 14:00:56 -0500294 * As of N, this field may be null. The expanded notification view is determined by the
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400295 * inputs to {@link Notification.Builder}; a custom RemoteViews can optionally be
296 * supplied with {@link Notification.Builder#setCustomBigContentView(RemoteViews)}.
Daniel Sandlerf3b73432012-03-27 15:01:25 -0400297 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400298 @Deprecated
Daniel Sandlerf3b73432012-03-27 15:01:25 -0400299 public RemoteViews bigContentView;
300
Chris Wren8fd39ec2014-02-27 17:43:26 -0500301
302 /**
Chris Wren47c20a12014-06-18 17:27:29 -0400303 * A medium-format version of {@link #contentView}, providing the Notification an
304 * opportunity to add action buttons to contentView. At its discretion, the system UI may
305 * choose to show this as a heads-up notification, which will pop up so the user can see
306 * it without leaving their current activity.
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400307 *
Julia Reynoldsd4ea7412016-02-17 14:00:56 -0500308 * As of N, this field may be null. The heads-up notification view is determined by the
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400309 * inputs to {@link Notification.Builder}; a custom RemoteViews can optionally be
310 * supplied with {@link Notification.Builder#setCustomHeadsUpContentView(RemoteViews)}.
Chris Wren8fd39ec2014-02-27 17:43:26 -0500311 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400312 @Deprecated
Chris Wren8fd39ec2014-02-27 17:43:26 -0500313 public RemoteViews headsUpContentView;
314
Daniel Sandlerf3b73432012-03-27 15:01:25 -0400315 /**
Dan Sandler86647982015-05-13 23:41:13 -0400316 * A large bitmap to be shown in the notification content area.
317 *
318 * @deprecated Use {@link Builder#setLargeIcon(Icon)} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800319 */
Dan Sandler86647982015-05-13 23:41:13 -0400320 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -0800321 public Bitmap largeIcon;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800322
323 /**
324 * The sound to play.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500325 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800326 * <p>
Chris Wren47c20a12014-06-18 17:27:29 -0400327 * A notification that is noisy is more likely to be presented as a heads-up notification.
328 * </p>
329 *
330 * <p>
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500331 * To play the default notification sound, see {@link #defaults}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800332 * </p>
333 */
334 public Uri sound;
335
336 /**
337 * Use this constant as the value for audioStreamType to request that
338 * the default stream type for notifications be used. Currently the
Jeff Sharkey098d5802012-04-26 17:30:34 -0700339 * default stream type is {@link AudioManager#STREAM_NOTIFICATION}.
John Spurlockc0650f022014-07-19 13:22:39 -0400340 *
341 * @deprecated Use {@link #audioAttributes} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800342 */
Jean-Michel Trivi81f871e2014-08-06 16:32:38 -0700343 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800344 public static final int STREAM_DEFAULT = -1;
345
346 /**
347 * The audio stream type to use when playing the sound.
348 * Should be one of the STREAM_ constants from
349 * {@link android.media.AudioManager}.
John Spurlockc0650f022014-07-19 13:22:39 -0400350 *
351 * @deprecated Use {@link #audioAttributes} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800352 */
Jean-Michel Trivi81f871e2014-08-06 16:32:38 -0700353 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800354 public int audioStreamType = STREAM_DEFAULT;
355
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800356 /**
John Spurlockc0650f022014-07-19 13:22:39 -0400357 * The default value of {@link #audioAttributes}.
358 */
359 public static final AudioAttributes AUDIO_ATTRIBUTES_DEFAULT = new AudioAttributes.Builder()
360 .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
361 .setUsage(AudioAttributes.USAGE_NOTIFICATION)
362 .build();
363
364 /**
365 * The {@link AudioAttributes audio attributes} to use when playing the sound.
366 */
367 public AudioAttributes audioAttributes = AUDIO_ATTRIBUTES_DEFAULT;
368
369 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500370 * The pattern with which to vibrate.
371 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800372 * <p>
373 * To vibrate the default pattern, see {@link #defaults}.
374 * </p>
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500375 *
Chris Wren47c20a12014-06-18 17:27:29 -0400376 * <p>
377 * A notification that vibrates is more likely to be presented as a heads-up notification.
378 * </p>
379 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800380 * @see android.os.Vibrator#vibrate(long[],int)
381 */
382 public long[] vibrate;
383
384 /**
385 * The color of the led. The hardware will do its best approximation.
386 *
387 * @see #FLAG_SHOW_LIGHTS
388 * @see #flags
389 */
Tor Norbye80756e32015-03-02 09:39:27 -0800390 @ColorInt
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800391 public int ledARGB;
392
393 /**
394 * The number of milliseconds for the LED to be on while it's flashing.
395 * The hardware will do its best approximation.
396 *
397 * @see #FLAG_SHOW_LIGHTS
398 * @see #flags
399 */
400 public int ledOnMS;
401
402 /**
403 * The number of milliseconds for the LED to be off while it's flashing.
404 * The hardware will do its best approximation.
405 *
406 * @see #FLAG_SHOW_LIGHTS
407 * @see #flags
408 */
409 public int ledOffMS;
410
411 /**
412 * Specifies which values should be taken from the defaults.
413 * <p>
414 * To set, OR the desired from {@link #DEFAULT_SOUND},
415 * {@link #DEFAULT_VIBRATE}, {@link #DEFAULT_LIGHTS}. For all default
416 * values, use {@link #DEFAULT_ALL}.
417 * </p>
418 */
419 public int defaults;
420
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800421 /**
422 * Bit to be bitwise-ored into the {@link #flags} field that should be
423 * set if you want the LED on for this notification.
424 * <ul>
425 * <li>To turn the LED off, pass 0 in the alpha channel for colorARGB
426 * or 0 for both ledOnMS and ledOffMS.</li>
427 * <li>To turn the LED on, pass 1 for ledOnMS and 0 for ledOffMS.</li>
428 * <li>To flash the LED, pass the number of milliseconds that it should
429 * be on and off to ledOnMS and ledOffMS.</li>
430 * </ul>
431 * <p>
432 * Since hardware varies, you are not guaranteed that any of the values
433 * you pass are honored exactly. Use the system defaults (TODO) if possible
434 * because they will be set to values that work on any given hardware.
435 * <p>
436 * The alpha channel must be set for forward compatibility.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500437 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800438 */
439 public static final int FLAG_SHOW_LIGHTS = 0x00000001;
440
441 /**
442 * Bit to be bitwise-ored into the {@link #flags} field that should be
443 * set if this notification is in reference to something that is ongoing,
444 * like a phone call. It should not be set if this notification is in
445 * reference to something that happened at a particular point in time,
446 * like a missed phone call.
447 */
448 public static final int FLAG_ONGOING_EVENT = 0x00000002;
449
450 /**
451 * Bit to be bitwise-ored into the {@link #flags} field that if set,
Scott Mainb8b36452009-04-26 15:50:49 -0700452 * the audio will be repeated until the notification is
453 * cancelled or the notification window is opened.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800454 */
455 public static final int FLAG_INSISTENT = 0x00000004;
456
457 /**
458 * Bit to be bitwise-ored into the {@link #flags} field that should be
Griff Hazen293977b2014-04-28 08:37:20 -0700459 * set if you would only like the sound, vibrate and ticker to be played
460 * if the notification was not already showing.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800461 */
462 public static final int FLAG_ONLY_ALERT_ONCE = 0x00000008;
463
464 /**
465 * Bit to be bitwise-ored into the {@link #flags} field that should be
466 * set if the notification should be canceled when it is clicked by the
Daniel Sandler8aa9ae62012-12-04 23:31:47 -0500467 * user.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800468 */
469 public static final int FLAG_AUTO_CANCEL = 0x00000010;
470
471 /**
472 * Bit to be bitwise-ored into the {@link #flags} field that should be
473 * set if the notification should not be canceled when the user clicks
474 * the Clear all button.
475 */
476 public static final int FLAG_NO_CLEAR = 0x00000020;
477
Dianne Hackbornd8a43f62009-08-17 23:33:56 -0700478 /**
479 * Bit to be bitwise-ored into the {@link #flags} field that should be
480 * set if this notification represents a currently running service. This
481 * will normally be set for you by {@link Service#startForeground}.
482 */
483 public static final int FLAG_FOREGROUND_SERVICE = 0x00000040;
484
Daniel Sandlere46cbd32010-06-17 10:35:26 -0400485 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500486 * Obsolete flag indicating high-priority notifications; use the priority field instead.
Joe Malin8d40d042012-11-05 11:36:40 -0800487 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500488 * @deprecated Use {@link #priority} with a positive value.
Daniel Sandlere46cbd32010-06-17 10:35:26 -0400489 */
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500490 public static final int FLAG_HIGH_PRIORITY = 0x00000080;
Daniel Sandlere46cbd32010-06-17 10:35:26 -0400491
Griff Hazendfcb0802014-02-11 12:00:00 -0800492 /**
493 * Bit to be bitswise-ored into the {@link #flags} field that should be
494 * set if this notification is relevant to the current device only
495 * and it is not recommended that it bridge to other devices.
496 */
497 public static final int FLAG_LOCAL_ONLY = 0x00000100;
498
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700499 /**
500 * Bit to be bitswise-ored into the {@link #flags} field that should be
501 * set if this notification is the group summary for a group of notifications.
502 * Grouped notifications may display in a cluster or stack on devices which
503 * support such rendering. Requires a group key also be set using {@link Builder#setGroup}.
504 */
505 public static final int FLAG_GROUP_SUMMARY = 0x00000200;
506
Julia Reynoldse46bb372016-03-17 11:05:58 -0400507 /**
508 * Bit to be bitswise-ored into the {@link #flags} field that should be
509 * set if this notification is the group summary for an auto-group of notifications.
510 *
511 * @hide
512 */
513 @SystemApi
514 public static final int FLAG_AUTOGROUP_SUMMARY = 0x00000400;
515
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800516 public int flags;
517
Tor Norbyed9273d62013-05-30 15:59:53 -0700518 /** @hide */
519 @IntDef({PRIORITY_DEFAULT,PRIORITY_LOW,PRIORITY_MIN,PRIORITY_HIGH,PRIORITY_MAX})
520 @Retention(RetentionPolicy.SOURCE)
521 public @interface Priority {}
522
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800523 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500524 * Default notification {@link #priority}. If your application does not prioritize its own
525 * notifications, use this value for all notifications.
526 */
527 public static final int PRIORITY_DEFAULT = 0;
528
529 /**
530 * Lower {@link #priority}, for items that are less important. The UI may choose to show these
531 * items smaller, or at a different position in the list, compared with your app's
532 * {@link #PRIORITY_DEFAULT} items.
533 */
534 public static final int PRIORITY_LOW = -1;
535
536 /**
537 * Lowest {@link #priority}; these items might not be shown to the user except under special
538 * circumstances, such as detailed notification logs.
539 */
540 public static final int PRIORITY_MIN = -2;
541
542 /**
543 * Higher {@link #priority}, for more important notifications or alerts. The UI may choose to
544 * show these items larger, or at a different position in notification lists, compared with
545 * your app's {@link #PRIORITY_DEFAULT} items.
546 */
547 public static final int PRIORITY_HIGH = 1;
548
549 /**
550 * Highest {@link #priority}, for your application's most important items that require the
551 * user's prompt attention or input.
552 */
553 public static final int PRIORITY_MAX = 2;
554
555 /**
556 * Relative priority for this notification.
Joe Malin8d40d042012-11-05 11:36:40 -0800557 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500558 * Priority is an indication of how much of the user's valuable attention should be consumed by
559 * this notification. Low-priority notifications may be hidden from the user in certain
560 * situations, while the user might be interrupted for a higher-priority notification. The
Daniel Sandler6738eee2012-11-16 12:03:32 -0500561 * system will make a determination about how to interpret this priority when presenting
562 * the notification.
Chris Wren47c20a12014-06-18 17:27:29 -0400563 *
564 * <p>
565 * A notification that is at least {@link #PRIORITY_HIGH} is more likely to be presented
566 * as a heads-up notification.
567 * </p>
568 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500569 */
Tor Norbyed9273d62013-05-30 15:59:53 -0700570 @Priority
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500571 public int priority;
Joe Malin8d40d042012-11-05 11:36:40 -0800572
Dan Sandler26e81cf2014-05-06 10:01:27 -0400573 /**
574 * Accent color (an ARGB integer like the constants in {@link android.graphics.Color})
575 * to be applied by the standard Style templates when presenting this notification.
576 *
577 * The current template design constructs a colorful header image by overlaying the
578 * {@link #icon} image (stenciled in white) atop a field of this color. Alpha components are
579 * ignored.
580 */
Tor Norbye80756e32015-03-02 09:39:27 -0800581 @ColorInt
Dan Sandler26e81cf2014-05-06 10:01:27 -0400582 public int color = COLOR_DEFAULT;
583
584 /**
585 * Special value of {@link #color} telling the system not to decorate this notification with
586 * any special color but instead use default colors when presenting this notification.
587 */
Tor Norbye80756e32015-03-02 09:39:27 -0800588 @ColorInt
Dan Sandler26e81cf2014-05-06 10:01:27 -0400589 public static final int COLOR_DEFAULT = 0; // AKA Color.TRANSPARENT
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600590
591 /**
Adrian Roos4ff3b122016-02-01 12:26:13 -0800592 * Special value of {@link #color} used as a place holder for an invalid color.
593 */
594 @ColorInt
595 private static final int COLOR_INVALID = 1;
596
597 /**
Adrian Roosc1a80b02016-04-05 14:54:55 -0700598 * Sphere of visibility of this notification, which affects how and when the SystemUI reveals
599 * the notification's presence and contents in untrusted situations (namely, on the secure
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600600 * lockscreen).
601 *
602 * The default level, {@link #VISIBILITY_PRIVATE}, behaves exactly as notifications have always
603 * done on Android: The notification's {@link #icon} and {@link #tickerText} (if available) are
604 * shown in all situations, but the contents are only available if the device is unlocked for
605 * the appropriate user.
606 *
607 * A more permissive policy can be expressed by {@link #VISIBILITY_PUBLIC}; such a notification
608 * can be read even in an "insecure" context (that is, above a secure lockscreen).
609 * To modify the public version of this notification—for example, to redact some portions—see
610 * {@link Builder#setPublicVersion(Notification)}.
611 *
612 * Finally, a notification can be made {@link #VISIBILITY_SECRET}, which will suppress its icon
613 * and ticker until the user has bypassed the lockscreen.
614 */
615 public int visibility;
616
Griff Hazenfc3922d2014-08-20 11:56:44 -0700617 /**
618 * Notification visibility: Show this notification in its entirety on all lockscreens.
619 *
620 * {@see #visibility}
621 */
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600622 public static final int VISIBILITY_PUBLIC = 1;
Griff Hazenfc3922d2014-08-20 11:56:44 -0700623
624 /**
625 * Notification visibility: Show this notification on all lockscreens, but conceal sensitive or
626 * private information on secure lockscreens.
627 *
628 * {@see #visibility}
629 */
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600630 public static final int VISIBILITY_PRIVATE = 0;
Griff Hazenfc3922d2014-08-20 11:56:44 -0700631
632 /**
633 * Notification visibility: Do not reveal any part of this notification on a secure lockscreen.
634 *
635 * {@see #visibility}
636 */
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600637 public static final int VISIBILITY_SECRET = -1;
638
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500639 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400640 * Notification category: incoming call (voice or video) or similar synchronous communication request.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500641 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400642 public static final String CATEGORY_CALL = "call";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500643
644 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400645 * Notification category: incoming direct message (SMS, instant message, etc.).
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500646 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400647 public static final String CATEGORY_MESSAGE = "msg";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500648
649 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400650 * Notification category: asynchronous bulk message (email).
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500651 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400652 public static final String CATEGORY_EMAIL = "email";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500653
654 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400655 * Notification category: calendar event.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500656 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400657 public static final String CATEGORY_EVENT = "event";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500658
659 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400660 * Notification category: promotion or advertisement.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500661 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400662 public static final String CATEGORY_PROMO = "promo";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500663
664 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400665 * Notification category: alarm or timer.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500666 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400667 public static final String CATEGORY_ALARM = "alarm";
668
669 /**
670 * Notification category: progress of a long-running background operation.
671 */
672 public static final String CATEGORY_PROGRESS = "progress";
673
674 /**
675 * Notification category: social network or sharing update.
676 */
677 public static final String CATEGORY_SOCIAL = "social";
678
679 /**
680 * Notification category: error in background operation or authentication status.
681 */
682 public static final String CATEGORY_ERROR = "err";
683
684 /**
685 * Notification category: media transport control for playback.
686 */
687 public static final String CATEGORY_TRANSPORT = "transport";
688
689 /**
690 * Notification category: system or device status update. Reserved for system use.
691 */
692 public static final String CATEGORY_SYSTEM = "sys";
693
694 /**
695 * Notification category: indication of running background service.
696 */
697 public static final String CATEGORY_SERVICE = "service";
698
699 /**
John Spurlock0a69c8c2014-03-21 13:30:57 -0400700 * Notification category: a specific, timely recommendation for a single thing.
701 * For example, a news app might want to recommend a news story it believes the user will
702 * want to read next.
703 */
704 public static final String CATEGORY_RECOMMENDATION = "recommendation";
705
706 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400707 * Notification category: ongoing information about device or contextual status.
708 */
709 public static final String CATEGORY_STATUS = "status";
710
711 /**
John Spurlock24d3dad2015-04-02 12:24:02 -0400712 * Notification category: user-scheduled reminder.
713 */
714 public static final String CATEGORY_REMINDER = "reminder";
715
716 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400717 * One of the predefined notification categories (see the <code>CATEGORY_*</code> constants)
718 * that best describes this Notification. May be used by the system for ranking and filtering.
719 */
720 public String category;
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500721
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700722 private String mGroupKey;
723
724 /**
725 * Get the key used to group this notification into a cluster or stack
726 * with other notifications on devices which support such rendering.
727 */
728 public String getGroup() {
729 return mGroupKey;
730 }
731
732 private String mSortKey;
733
734 /**
735 * Get a sort key that orders this notification among other notifications from the
736 * same package. This can be useful if an external sort was already applied and an app
737 * would like to preserve this. Notifications will be sorted lexicographically using this
738 * value, although providing different priorities in addition to providing sort key may
739 * cause this value to be ignored.
740 *
741 * <p>This sort key can also be used to order members of a notification group. See
742 * {@link Builder#setGroup}.
743 *
744 * @see String#compareTo(String)
745 */
746 public String getSortKey() {
747 return mSortKey;
748 }
749
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500750 /**
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400751 * Additional semantic data to be carried around with this Notification.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400752 * <p>
753 * The extras keys defined here are intended to capture the original inputs to {@link Builder}
754 * APIs, and are intended to be used by
755 * {@link android.service.notification.NotificationListenerService} implementations to extract
756 * detailed information from notification objects.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500757 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400758 public Bundle extras = new Bundle();
759
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400760 /**
761 * {@link #extras} key: this is the title of the notification,
762 * as supplied to {@link Builder#setContentTitle(CharSequence)}.
763 */
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -0500764 public static final String EXTRA_TITLE = "android.title";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400765
766 /**
767 * {@link #extras} key: this is the title of the notification when shown in expanded form,
768 * e.g. as supplied to {@link BigTextStyle#setBigContentTitle(CharSequence)}.
769 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400770 public static final String EXTRA_TITLE_BIG = EXTRA_TITLE + ".big";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400771
772 /**
773 * {@link #extras} key: this is the main text payload, as supplied to
774 * {@link Builder#setContentText(CharSequence)}.
775 */
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -0500776 public static final String EXTRA_TEXT = "android.text";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400777
778 /**
779 * {@link #extras} key: this is a third line of text, as supplied to
780 * {@link Builder#setSubText(CharSequence)}.
781 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400782 public static final String EXTRA_SUB_TEXT = "android.subText";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400783
784 /**
Adrian Roose458aa82015-12-08 16:17:19 -0800785 * {@link #extras} key: this is the remote input history, as supplied to
786 * {@link Builder#setRemoteInputHistory(CharSequence[])}.
Adrian Roos005e7742016-04-13 15:02:20 -0700787 *
788 * Apps can fill this through {@link Builder#setRemoteInputHistory(CharSequence[])}
789 * with the most recent inputs that have been sent through a {@link RemoteInput} of this
790 * Notification and are expected to clear it once the it is no longer relevant (e.g. for chat
791 * notifications once the other party has responded).
792 *
793 * The extra with this key is of type CharSequence[] and contains the most recent entry at
794 * the 0 index, the second most recent at the 1 index, etc.
795 *
796 * @see Builder#setRemoteInputHistory(CharSequence[])
Adrian Roose458aa82015-12-08 16:17:19 -0800797 */
798 public static final String EXTRA_REMOTE_INPUT_HISTORY = "android.remoteInputHistory";
799
800 /**
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400801 * {@link #extras} key: this is a small piece of additional text as supplied to
802 * {@link Builder#setContentInfo(CharSequence)}.
803 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400804 public static final String EXTRA_INFO_TEXT = "android.infoText";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400805
806 /**
807 * {@link #extras} key: this is a line of summary information intended to be shown
808 * alongside expanded notifications, as supplied to (e.g.)
809 * {@link BigTextStyle#setSummaryText(CharSequence)}.
810 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400811 public static final String EXTRA_SUMMARY_TEXT = "android.summaryText";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400812
813 /**
Christoph Studer4600f9b2014-07-22 22:44:43 +0200814 * {@link #extras} key: this is the longer text shown in the big form of a
815 * {@link BigTextStyle} notification, as supplied to
816 * {@link BigTextStyle#bigText(CharSequence)}.
817 */
818 public static final String EXTRA_BIG_TEXT = "android.bigText";
819
820 /**
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400821 * {@link #extras} key: this is the resource ID of the notification's main small icon, as
822 * supplied to {@link Builder#setSmallIcon(int)}.
823 */
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -0500824 public static final String EXTRA_SMALL_ICON = "android.icon";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400825
826 /**
827 * {@link #extras} key: this is a bitmap to be used instead of the small icon when showing the
828 * notification payload, as
829 * supplied to {@link Builder#setLargeIcon(android.graphics.Bitmap)}.
830 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400831 public static final String EXTRA_LARGE_ICON = "android.largeIcon";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400832
833 /**
834 * {@link #extras} key: this is a bitmap to be used instead of the one from
835 * {@link Builder#setLargeIcon(android.graphics.Bitmap)} when the notification is
836 * shown in its expanded form, as supplied to
837 * {@link BigPictureStyle#bigLargeIcon(android.graphics.Bitmap)}.
838 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400839 public static final String EXTRA_LARGE_ICON_BIG = EXTRA_LARGE_ICON + ".big";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400840
841 /**
842 * {@link #extras} key: this is the progress value supplied to
843 * {@link Builder#setProgress(int, int, boolean)}.
844 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400845 public static final String EXTRA_PROGRESS = "android.progress";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400846
847 /**
848 * {@link #extras} key: this is the maximum value supplied to
849 * {@link Builder#setProgress(int, int, boolean)}.
850 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400851 public static final String EXTRA_PROGRESS_MAX = "android.progressMax";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400852
853 /**
854 * {@link #extras} key: whether the progress bar is indeterminate, supplied to
855 * {@link Builder#setProgress(int, int, boolean)}.
856 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400857 public static final String EXTRA_PROGRESS_INDETERMINATE = "android.progressIndeterminate";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400858
859 /**
860 * {@link #extras} key: whether {@link #when} should be shown as a count-up timer (specifically
861 * a {@link android.widget.Chronometer}) instead of a timestamp, as supplied to
862 * {@link Builder#setUsesChronometer(boolean)}.
863 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400864 public static final String EXTRA_SHOW_CHRONOMETER = "android.showChronometer";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400865
866 /**
Selim Cinek81c23aa2016-02-25 16:23:13 -0800867 * {@link #extras} key: whether the chronometer set on the notification should count down
868 * instead of counting up. Is only relevant if key {@link #EXTRA_SHOW_CHRONOMETER} is present.
Adrian Roos96b7e202016-05-17 13:50:38 -0700869 * This extra is a boolean. The default is false.
Selim Cinek81c23aa2016-02-25 16:23:13 -0800870 */
Adrian Roos96b7e202016-05-17 13:50:38 -0700871 public static final String EXTRA_CHRONOMETER_COUNT_DOWN = "android.chronometerCountDown";
Selim Cinek81c23aa2016-02-25 16:23:13 -0800872
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
Adrian Roos96b7e202016-05-17 13:50:38 -0700937 * {@link android.app.Notification.MessagingStyle} notification. This extra is a
938 * {@link CharSequence}
Alex Hillsfc737de2016-03-23 17:33:02 -0400939 */
940 public static final String EXTRA_SELF_DISPLAY_NAME = "android.selfDisplayName";
941
942 /**
Adrian Roos96b7e202016-05-17 13:50:38 -0700943 * {@link #extras} key: a {@link CharSequence} to be displayed as the title to a conversation
Alex Hillsd9b04d92016-04-11 16:38:16 -0400944 * represented by a {@link android.app.Notification.MessagingStyle}
Alex Hillsfc737de2016-03-23 17:33:02 -0400945 */
Alex Hillsd9b04d92016-04-11 16:38:16 -0400946 public static final String EXTRA_CONVERSATION_TITLE = "android.conversationTitle";
Alex Hillsfc737de2016-03-23 17:33:02 -0400947
948 /**
949 * {@link #extras} key: an array of {@link android.app.Notification.MessagingStyle.Message}
950 * bundles provided by a
Adrian Roos96b7e202016-05-17 13:50:38 -0700951 * {@link android.app.Notification.MessagingStyle} notification. This extra is a parcelable
952 * array of bundles.
Alex Hillsfc737de2016-03-23 17:33:02 -0400953 */
954 public static final String EXTRA_MESSAGES = "android.messages";
955
956 /**
Kenny Guy8942bcd2014-09-08 21:09:47 +0100957 * {@link #extras} key: the user that built the notification.
958 *
959 * @hide
960 */
961 public static final String EXTRA_ORIGINATING_USERID = "android.originatingUserId";
962
963 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400964 * @hide
965 */
966 public static final String EXTRA_BUILDER_APPLICATION_INFO = "android.appInfo";
967
Selim Cinek247fa012016-02-18 09:50:48 -0800968 /**
969 * @hide
970 */
971 public static final String EXTRA_CONTAINS_CUSTOM_VIEW = "android.contains.customView";
972
Dan Sandler80eaa592016-04-14 23:34:54 -0400973 /** @hide */
974 @SystemApi
Dan Sandler732bd6c2016-04-12 14:20:32 -0400975 public static final String EXTRA_SUBSTITUTE_APP_NAME = "android.substName";
976
Dan Sandlerd63f9322015-05-06 15:18:49 -0400977 private Icon mSmallIcon;
978 private Icon mLargeIcon;
979
Chris Wren51c75102013-07-16 20:49:17 -0400980 /**
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400981 * Structure to encapsulate a named action that can be shown as part of this notification.
982 * It must include an icon, a label, and a {@link PendingIntent} to be fired when the action is
983 * selected by the user.
984 * <p>
Griff Hazen959591e2014-05-15 22:26:18 -0700985 * Apps should use {@link Notification.Builder#addAction(int, CharSequence, PendingIntent)}
986 * or {@link Notification.Builder#addAction(Notification.Action)}
987 * to attach actions.
Daniel Sandlera0a938c2012-03-15 08:42:37 -0400988 */
Daniel Sandlerea2a3172013-02-20 22:24:20 -0500989 public static class Action implements Parcelable {
Griff Hazen959591e2014-05-15 22:26:18 -0700990 private final Bundle mExtras;
Dan Sandler86647982015-05-13 23:41:13 -0400991 private Icon mIcon;
Griff Hazen61a9e862014-05-22 16:05:19 -0700992 private final RemoteInput[] mRemoteInputs;
Alex Hills42b0c4d2016-04-26 13:35:36 -0400993 private boolean mAllowGeneratedReplies = false;
Griff Hazen959591e2014-05-15 22:26:18 -0700994
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400995 /**
996 * Small icon representing the action.
Dan Sandler86647982015-05-13 23:41:13 -0400997 *
998 * @deprecated Use {@link Action#getIcon()} instead.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400999 */
Dan Sandler86647982015-05-13 23:41:13 -04001000 @Deprecated
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001001 public int icon;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001002
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001003 /**
1004 * Title of the action.
1005 */
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001006 public CharSequence title;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001007
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001008 /**
1009 * Intent to send when the user invokes this action. May be null, in which case the action
1010 * may be rendered in a disabled presentation by the system UI.
1011 */
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001012 public PendingIntent actionIntent;
Griff Hazen959591e2014-05-15 22:26:18 -07001013
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001014 private Action(Parcel in) {
Dan Sandler86647982015-05-13 23:41:13 -04001015 if (in.readInt() != 0) {
1016 mIcon = Icon.CREATOR.createFromParcel(in);
Dan Sandler68079d52015-07-22 10:45:30 -04001017 if (mIcon.getType() == Icon.TYPE_RESOURCE) {
1018 icon = mIcon.getResId();
1019 }
Dan Sandler86647982015-05-13 23:41:13 -04001020 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001021 title = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
1022 if (in.readInt() == 1) {
1023 actionIntent = PendingIntent.CREATOR.createFromParcel(in);
1024 }
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001025 mExtras = Bundle.setDefusable(in.readBundle(), true);
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001026 mRemoteInputs = in.createTypedArray(RemoteInput.CREATOR);
Alex Hills42b0c4d2016-04-26 13:35:36 -04001027 mAllowGeneratedReplies = in.readInt() == 1;
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001028 }
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001029
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001030 /**
Dan Sandler86647982015-05-13 23:41:13 -04001031 * @deprecated Use {@link android.app.Notification.Action.Builder}.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001032 */
Dan Sandler86647982015-05-13 23:41:13 -04001033 @Deprecated
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001034 public Action(int icon, CharSequence title, PendingIntent intent) {
Alex Hills42b0c4d2016-04-26 13:35:36 -04001035 this(Icon.createWithResource("", icon), title, intent, new Bundle(), null, false);
Griff Hazen959591e2014-05-15 22:26:18 -07001036 }
1037
Dan Sandler86647982015-05-13 23:41:13 -04001038 private Action(Icon icon, CharSequence title, PendingIntent intent, Bundle extras,
Alex Hills42b0c4d2016-04-26 13:35:36 -04001039 RemoteInput[] remoteInputs, boolean allowGeneratedReplies) {
Dan Sandler86647982015-05-13 23:41:13 -04001040 this.mIcon = icon;
Gus Prevasf5bff46f2015-08-24 10:34:28 -04001041 if (icon != null && icon.getType() == Icon.TYPE_RESOURCE) {
1042 this.icon = icon.getResId();
1043 }
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001044 this.title = title;
1045 this.actionIntent = intent;
Griff Hazen959591e2014-05-15 22:26:18 -07001046 this.mExtras = extras != null ? extras : new Bundle();
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001047 this.mRemoteInputs = remoteInputs;
Alex Hills42b0c4d2016-04-26 13:35:36 -04001048 this.mAllowGeneratedReplies = allowGeneratedReplies;
Griff Hazen959591e2014-05-15 22:26:18 -07001049 }
1050
1051 /**
Dan Sandler86647982015-05-13 23:41:13 -04001052 * Return an icon representing the action.
1053 */
1054 public Icon getIcon() {
1055 if (mIcon == null && icon != 0) {
1056 // you snuck an icon in here without using the builder; let's try to keep it
1057 mIcon = Icon.createWithResource("", icon);
1058 }
1059 return mIcon;
1060 }
1061
1062 /**
Griff Hazen959591e2014-05-15 22:26:18 -07001063 * Get additional metadata carried around with this Action.
1064 */
1065 public Bundle getExtras() {
1066 return mExtras;
1067 }
1068
1069 /**
Alex Hills42b0c4d2016-04-26 13:35:36 -04001070 * Return whether the platform should automatically generate possible replies for this
1071 * {@link Action}
1072 */
1073 public boolean getAllowGeneratedReplies() {
1074 return mAllowGeneratedReplies;
1075 }
1076
1077 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001078 * Get the list of inputs to be collected from the user when this action is sent.
1079 * May return null if no remote inputs were added.
1080 */
1081 public RemoteInput[] getRemoteInputs() {
1082 return mRemoteInputs;
1083 }
1084
1085 /**
Griff Hazen959591e2014-05-15 22:26:18 -07001086 * Builder class for {@link Action} objects.
1087 */
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001088 public static final class Builder {
Dan Sandler86647982015-05-13 23:41:13 -04001089 private final Icon mIcon;
Griff Hazen959591e2014-05-15 22:26:18 -07001090 private final CharSequence mTitle;
1091 private final PendingIntent mIntent;
Alex Hills42b0c4d2016-04-26 13:35:36 -04001092 private boolean mAllowGeneratedReplies;
Griff Hazen959591e2014-05-15 22:26:18 -07001093 private final Bundle mExtras;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001094 private ArrayList<RemoteInput> mRemoteInputs;
Griff Hazen959591e2014-05-15 22:26:18 -07001095
1096 /**
1097 * Construct a new builder for {@link Action} object.
1098 * @param icon icon to show for this action
1099 * @param title the title of the action
1100 * @param intent the {@link PendingIntent} to fire when users trigger this action
1101 */
Dan Sandler86647982015-05-13 23:41:13 -04001102 @Deprecated
Griff Hazen959591e2014-05-15 22:26:18 -07001103 public Builder(int icon, CharSequence title, PendingIntent intent) {
Dan Sandler86647982015-05-13 23:41:13 -04001104 this(Icon.createWithResource("", icon), title, intent, new Bundle(), null);
1105 }
1106
1107 /**
1108 * Construct a new builder for {@link Action} object.
1109 * @param icon icon to show for this action
1110 * @param title the title of the action
1111 * @param intent the {@link PendingIntent} to fire when users trigger this action
1112 */
1113 public Builder(Icon icon, CharSequence title, PendingIntent intent) {
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001114 this(icon, title, intent, new Bundle(), null);
Griff Hazen959591e2014-05-15 22:26:18 -07001115 }
1116
1117 /**
1118 * Construct a new builder for {@link Action} object using the fields from an
1119 * {@link Action}.
1120 * @param action the action to read fields from.
1121 */
1122 public Builder(Action action) {
Dan Sandler86647982015-05-13 23:41:13 -04001123 this(action.getIcon(), action.title, action.actionIntent, new Bundle(action.mExtras),
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001124 action.getRemoteInputs());
Griff Hazen959591e2014-05-15 22:26:18 -07001125 }
1126
Dan Sandler86647982015-05-13 23:41:13 -04001127 private Builder(Icon icon, CharSequence title, PendingIntent intent, Bundle extras,
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001128 RemoteInput[] remoteInputs) {
Griff Hazen959591e2014-05-15 22:26:18 -07001129 mIcon = icon;
1130 mTitle = title;
1131 mIntent = intent;
1132 mExtras = extras;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001133 if (remoteInputs != null) {
1134 mRemoteInputs = new ArrayList<RemoteInput>(remoteInputs.length);
1135 Collections.addAll(mRemoteInputs, remoteInputs);
1136 }
Griff Hazen959591e2014-05-15 22:26:18 -07001137 }
1138
1139 /**
1140 * Merge additional metadata into this builder.
1141 *
1142 * <p>Values within the Bundle will replace existing extras values in this Builder.
1143 *
1144 * @see Notification.Action#extras
1145 */
1146 public Builder addExtras(Bundle extras) {
1147 if (extras != null) {
1148 mExtras.putAll(extras);
1149 }
1150 return this;
1151 }
1152
1153 /**
1154 * Get the metadata Bundle used by this Builder.
1155 *
1156 * <p>The returned Bundle is shared with this Builder.
1157 */
1158 public Bundle getExtras() {
1159 return mExtras;
1160 }
1161
1162 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001163 * Add an input to be collected from the user when this action is sent.
1164 * Response values can be retrieved from the fired intent by using the
1165 * {@link RemoteInput#getResultsFromIntent} function.
1166 * @param remoteInput a {@link RemoteInput} to add to the action
1167 * @return this object for method chaining
1168 */
1169 public Builder addRemoteInput(RemoteInput remoteInput) {
1170 if (mRemoteInputs == null) {
1171 mRemoteInputs = new ArrayList<RemoteInput>();
1172 }
1173 mRemoteInputs.add(remoteInput);
1174 return this;
1175 }
1176
1177 /**
Alex Hills42b0c4d2016-04-26 13:35:36 -04001178 * Set whether the platform should automatically generate possible replies to add to
1179 * {@link RemoteInput#getChoices()}. If the {@link Action} doesn't have a
1180 * {@link RemoteInput}, this has no effect.
1181 * @param allowGeneratedReplies {@code true} to allow generated replies, {@code false}
1182 * otherwise
1183 * @return this object for method chaining
1184 * The default value is {@code false}
1185 */
1186 public Builder setAllowGeneratedReplies(boolean allowGeneratedReplies) {
1187 mAllowGeneratedReplies = allowGeneratedReplies;
1188 return this;
1189 }
1190
1191 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001192 * Apply an extender to this action builder. Extenders may be used to add
1193 * metadata or change options on this builder.
1194 */
Griff Hazen61a9e862014-05-22 16:05:19 -07001195 public Builder extend(Extender extender) {
1196 extender.extend(this);
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001197 return this;
1198 }
1199
1200 /**
Griff Hazen959591e2014-05-15 22:26:18 -07001201 * Combine all of the options that have been set and return a new {@link Action}
1202 * object.
1203 * @return the built action
1204 */
1205 public Action build() {
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001206 RemoteInput[] remoteInputs = mRemoteInputs != null
1207 ? mRemoteInputs.toArray(new RemoteInput[mRemoteInputs.size()]) : null;
Alex Hills42b0c4d2016-04-26 13:35:36 -04001208 return new Action(mIcon, mTitle, mIntent, mExtras, remoteInputs,
1209 mAllowGeneratedReplies);
Griff Hazen959591e2014-05-15 22:26:18 -07001210 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001211 }
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001212
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001213 @Override
1214 public Action clone() {
1215 return new Action(
Dan Sandler86647982015-05-13 23:41:13 -04001216 getIcon(),
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001217 title,
1218 actionIntent, // safe to alias
1219 new Bundle(mExtras),
Alex Hills42b0c4d2016-04-26 13:35:36 -04001220 getRemoteInputs(),
1221 getAllowGeneratedReplies());
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001222 }
1223 @Override
1224 public int describeContents() {
1225 return 0;
1226 }
1227 @Override
1228 public void writeToParcel(Parcel out, int flags) {
Dan Sandler86647982015-05-13 23:41:13 -04001229 final Icon ic = getIcon();
1230 if (ic != null) {
1231 out.writeInt(1);
1232 ic.writeToParcel(out, 0);
1233 } else {
1234 out.writeInt(0);
1235 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001236 TextUtils.writeToParcel(title, out, flags);
1237 if (actionIntent != null) {
1238 out.writeInt(1);
1239 actionIntent.writeToParcel(out, flags);
1240 } else {
1241 out.writeInt(0);
1242 }
Griff Hazen959591e2014-05-15 22:26:18 -07001243 out.writeBundle(mExtras);
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001244 out.writeTypedArray(mRemoteInputs, flags);
Alex Hills42b0c4d2016-04-26 13:35:36 -04001245 out.writeInt(mAllowGeneratedReplies ? 1 : 0);
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001246 }
Griff Hazen959591e2014-05-15 22:26:18 -07001247 public static final Parcelable.Creator<Action> CREATOR =
1248 new Parcelable.Creator<Action>() {
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001249 public Action createFromParcel(Parcel in) {
1250 return new Action(in);
1251 }
1252 public Action[] newArray(int size) {
1253 return new Action[size];
1254 }
1255 };
Griff Hazen61a9e862014-05-22 16:05:19 -07001256
1257 /**
1258 * Extender interface for use with {@link Builder#extend}. Extenders may be used to add
1259 * metadata or change options on an action builder.
1260 */
1261 public interface Extender {
1262 /**
1263 * Apply this extender to a notification action builder.
1264 * @param builder the builder to be modified.
1265 * @return the build object for chaining.
1266 */
1267 public Builder extend(Builder builder);
1268 }
1269
1270 /**
1271 * Wearable extender for notification actions. To add extensions to an action,
1272 * create a new {@link android.app.Notification.Action.WearableExtender} object using
1273 * the {@code WearableExtender()} constructor and apply it to a
1274 * {@link android.app.Notification.Action.Builder} using
1275 * {@link android.app.Notification.Action.Builder#extend}.
1276 *
1277 * <pre class="prettyprint">
1278 * Notification.Action action = new Notification.Action.Builder(
1279 * R.drawable.archive_all, "Archive all", actionIntent)
Griff Hazen14f57992014-05-26 09:07:14 -07001280 * .extend(new Notification.Action.WearableExtender()
Griff Hazen61a9e862014-05-22 16:05:19 -07001281 * .setAvailableOffline(false))
Griff Hazen14f57992014-05-26 09:07:14 -07001282 * .build();</pre>
Griff Hazen61a9e862014-05-22 16:05:19 -07001283 */
1284 public static final class WearableExtender implements Extender {
1285 /** Notification action extra which contains wearable extensions */
1286 private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS";
1287
Pete Gastaf6781d2014-10-07 15:17:05 -04001288 // Keys within EXTRA_WEARABLE_EXTENSIONS for wearable options.
Griff Hazen61a9e862014-05-22 16:05:19 -07001289 private static final String KEY_FLAGS = "flags";
Pete Gastaf6781d2014-10-07 15:17:05 -04001290 private static final String KEY_IN_PROGRESS_LABEL = "inProgressLabel";
1291 private static final String KEY_CONFIRM_LABEL = "confirmLabel";
1292 private static final String KEY_CANCEL_LABEL = "cancelLabel";
Griff Hazen61a9e862014-05-22 16:05:19 -07001293
1294 // Flags bitwise-ored to mFlags
1295 private static final int FLAG_AVAILABLE_OFFLINE = 0x1;
Alex Hills9ab3a232016-04-05 14:54:56 -04001296 private static final int FLAG_HINT_LAUNCHES_ACTIVITY = 1 << 1;
Griff Hazen61a9e862014-05-22 16:05:19 -07001297
1298 // Default value for flags integer
1299 private static final int DEFAULT_FLAGS = FLAG_AVAILABLE_OFFLINE;
1300
1301 private int mFlags = DEFAULT_FLAGS;
1302
Pete Gastaf6781d2014-10-07 15:17:05 -04001303 private CharSequence mInProgressLabel;
1304 private CharSequence mConfirmLabel;
1305 private CharSequence mCancelLabel;
1306
Griff Hazen61a9e862014-05-22 16:05:19 -07001307 /**
1308 * Create a {@link android.app.Notification.Action.WearableExtender} with default
1309 * options.
1310 */
1311 public WearableExtender() {
1312 }
1313
1314 /**
1315 * Create a {@link android.app.Notification.Action.WearableExtender} by reading
1316 * wearable options present in an existing notification action.
1317 * @param action the notification action to inspect.
1318 */
1319 public WearableExtender(Action action) {
1320 Bundle wearableBundle = action.getExtras().getBundle(EXTRA_WEARABLE_EXTENSIONS);
1321 if (wearableBundle != null) {
1322 mFlags = wearableBundle.getInt(KEY_FLAGS, DEFAULT_FLAGS);
Pete Gastaf6781d2014-10-07 15:17:05 -04001323 mInProgressLabel = wearableBundle.getCharSequence(KEY_IN_PROGRESS_LABEL);
1324 mConfirmLabel = wearableBundle.getCharSequence(KEY_CONFIRM_LABEL);
1325 mCancelLabel = wearableBundle.getCharSequence(KEY_CANCEL_LABEL);
Griff Hazen61a9e862014-05-22 16:05:19 -07001326 }
1327 }
1328
1329 /**
1330 * Apply wearable extensions to a notification action that is being built. This is
1331 * typically called by the {@link android.app.Notification.Action.Builder#extend}
1332 * method of {@link android.app.Notification.Action.Builder}.
1333 */
1334 @Override
1335 public Action.Builder extend(Action.Builder builder) {
1336 Bundle wearableBundle = new Bundle();
1337
1338 if (mFlags != DEFAULT_FLAGS) {
1339 wearableBundle.putInt(KEY_FLAGS, mFlags);
1340 }
Pete Gastaf6781d2014-10-07 15:17:05 -04001341 if (mInProgressLabel != null) {
1342 wearableBundle.putCharSequence(KEY_IN_PROGRESS_LABEL, mInProgressLabel);
1343 }
1344 if (mConfirmLabel != null) {
1345 wearableBundle.putCharSequence(KEY_CONFIRM_LABEL, mConfirmLabel);
1346 }
1347 if (mCancelLabel != null) {
1348 wearableBundle.putCharSequence(KEY_CANCEL_LABEL, mCancelLabel);
1349 }
Griff Hazen61a9e862014-05-22 16:05:19 -07001350
1351 builder.getExtras().putBundle(EXTRA_WEARABLE_EXTENSIONS, wearableBundle);
1352 return builder;
1353 }
1354
1355 @Override
1356 public WearableExtender clone() {
1357 WearableExtender that = new WearableExtender();
1358 that.mFlags = this.mFlags;
Pete Gastaf6781d2014-10-07 15:17:05 -04001359 that.mInProgressLabel = this.mInProgressLabel;
1360 that.mConfirmLabel = this.mConfirmLabel;
1361 that.mCancelLabel = this.mCancelLabel;
Griff Hazen61a9e862014-05-22 16:05:19 -07001362 return that;
1363 }
1364
1365 /**
1366 * Set whether this action is available when the wearable device is not connected to
1367 * a companion device. The user can still trigger this action when the wearable device is
1368 * offline, but a visual hint will indicate that the action may not be available.
1369 * Defaults to true.
1370 */
1371 public WearableExtender setAvailableOffline(boolean availableOffline) {
1372 setFlag(FLAG_AVAILABLE_OFFLINE, availableOffline);
1373 return this;
1374 }
1375
1376 /**
1377 * Get whether this action is available when the wearable device is not connected to
1378 * a companion device. The user can still trigger this action when the wearable device is
1379 * offline, but a visual hint will indicate that the action may not be available.
1380 * Defaults to true.
1381 */
1382 public boolean isAvailableOffline() {
1383 return (mFlags & FLAG_AVAILABLE_OFFLINE) != 0;
1384 }
1385
1386 private void setFlag(int mask, boolean value) {
1387 if (value) {
1388 mFlags |= mask;
1389 } else {
1390 mFlags &= ~mask;
1391 }
1392 }
Pete Gastaf6781d2014-10-07 15:17:05 -04001393
1394 /**
1395 * Set a label to display while the wearable is preparing to automatically execute the
1396 * action. This is usually a 'ing' verb ending in ellipsis like "Sending..."
1397 *
1398 * @param label the label to display while the action is being prepared to execute
1399 * @return this object for method chaining
1400 */
1401 public WearableExtender setInProgressLabel(CharSequence label) {
1402 mInProgressLabel = label;
1403 return this;
1404 }
1405
1406 /**
1407 * Get the label to display while the wearable is preparing to automatically execute
1408 * the action. This is usually a 'ing' verb ending in ellipsis like "Sending..."
1409 *
1410 * @return the label to display while the action is being prepared to execute
1411 */
1412 public CharSequence getInProgressLabel() {
1413 return mInProgressLabel;
1414 }
1415
1416 /**
1417 * Set a label to display to confirm that the action should be executed.
1418 * This is usually an imperative verb like "Send".
1419 *
1420 * @param label the label to confirm the action should be executed
1421 * @return this object for method chaining
1422 */
1423 public WearableExtender setConfirmLabel(CharSequence label) {
1424 mConfirmLabel = label;
1425 return this;
1426 }
1427
1428 /**
1429 * Get the label to display to confirm that the action should be executed.
1430 * This is usually an imperative verb like "Send".
1431 *
1432 * @return the label to confirm the action should be executed
1433 */
1434 public CharSequence getConfirmLabel() {
1435 return mConfirmLabel;
1436 }
1437
1438 /**
1439 * Set a label to display to cancel the action.
1440 * This is usually an imperative verb, like "Cancel".
1441 *
1442 * @param label the label to display to cancel the action
1443 * @return this object for method chaining
1444 */
1445 public WearableExtender setCancelLabel(CharSequence label) {
1446 mCancelLabel = label;
1447 return this;
1448 }
1449
1450 /**
1451 * Get the label to display to cancel the action.
1452 * This is usually an imperative verb like "Cancel".
1453 *
1454 * @return the label to display to cancel the action
1455 */
1456 public CharSequence getCancelLabel() {
1457 return mCancelLabel;
1458 }
Alex Hills9ab3a232016-04-05 14:54:56 -04001459
1460 /**
1461 * Set a hint that this Action will launch an {@link Activity} directly, telling the
1462 * platform that it can generate the appropriate transitions.
1463 * @param hintLaunchesActivity {@code true} if the content intent will launch
1464 * an activity and transitions should be generated, false otherwise.
1465 * @return this object for method chaining
1466 */
Alex Hills4ec3ff42016-04-12 11:36:18 -04001467 public WearableExtender setHintLaunchesActivity(
Alex Hills9ab3a232016-04-05 14:54:56 -04001468 boolean hintLaunchesActivity) {
1469 setFlag(FLAG_HINT_LAUNCHES_ACTIVITY, hintLaunchesActivity);
1470 return this;
1471 }
1472
1473 /**
1474 * Get a hint that this Action will launch an {@link Activity} directly, telling the
1475 * platform that it can generate the appropriate transitions
1476 * @return {@code true} if the content intent will launch an activity and transitions
1477 * should be generated, false otherwise. The default value is {@code false} if this was
1478 * never set.
1479 */
Alex Hills4ec3ff42016-04-12 11:36:18 -04001480 public boolean getHintLaunchesActivity() {
Alex Hills9ab3a232016-04-05 14:54:56 -04001481 return (mFlags & FLAG_HINT_LAUNCHES_ACTIVITY) != 0;
1482 }
Griff Hazen61a9e862014-05-22 16:05:19 -07001483 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001484 }
1485
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001486 /**
1487 * Array of all {@link Action} structures attached to this notification by
1488 * {@link Builder#addAction(int, CharSequence, PendingIntent)}. Mostly useful for instances of
1489 * {@link android.service.notification.NotificationListenerService} that provide an alternative
1490 * interface for invoking actions.
1491 */
Daniel Sandlerea2a3172013-02-20 22:24:20 -05001492 public Action[] actions;
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001493
1494 /**
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001495 * Replacement version of this notification whose content will be shown
1496 * in an insecure context such as atop a secure keyguard. See {@link #visibility}
1497 * and {@link #VISIBILITY_PUBLIC}.
1498 */
1499 public Notification publicVersion;
1500
1501 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001502 * Constructs a Notification object with default values.
Joe Onorato46439ce2010-11-19 13:56:21 -08001503 * You might want to consider using {@link Builder} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001504 */
1505 public Notification()
1506 {
1507 this.when = System.currentTimeMillis();
Selim Cinekb85f36fd2016-04-20 18:46:36 -07001508 this.creationTime = System.currentTimeMillis();
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001509 this.priority = PRIORITY_DEFAULT;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001510 }
1511
1512 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001513 * @hide
1514 */
1515 public Notification(Context context, int icon, CharSequence tickerText, long when,
1516 CharSequence contentTitle, CharSequence contentText, Intent contentIntent)
1517 {
Chris Wren1ce4b6d2015-06-11 10:19:43 -04001518 new Builder(context)
1519 .setWhen(when)
1520 .setSmallIcon(icon)
1521 .setTicker(tickerText)
1522 .setContentTitle(contentTitle)
1523 .setContentText(contentText)
1524 .setContentIntent(PendingIntent.getActivity(context, 0, contentIntent, 0))
1525 .buildInto(this);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001526 }
1527
1528 /**
1529 * Constructs a Notification object with the information needed to
1530 * have a status bar icon without the standard expanded view.
1531 *
1532 * @param icon The resource id of the icon to put in the status bar.
1533 * @param tickerText The text that flows by in the status bar when the notification first
1534 * activates.
1535 * @param when The time to show in the time field. In the System.currentTimeMillis
1536 * timebase.
Joe Onorato46439ce2010-11-19 13:56:21 -08001537 *
1538 * @deprecated Use {@link Builder} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001539 */
Joe Onorato46439ce2010-11-19 13:56:21 -08001540 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001541 public Notification(int icon, CharSequence tickerText, long when)
1542 {
1543 this.icon = icon;
1544 this.tickerText = tickerText;
1545 this.when = when;
Selim Cinekb85f36fd2016-04-20 18:46:36 -07001546 this.creationTime = System.currentTimeMillis();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001547 }
1548
1549 /**
1550 * Unflatten the notification from a parcel.
1551 */
1552 public Notification(Parcel parcel)
1553 {
1554 int version = parcel.readInt();
1555
1556 when = parcel.readLong();
Selim Cinekb85f36fd2016-04-20 18:46:36 -07001557 creationTime = parcel.readLong();
Dan Sandler3936e7a2015-05-19 20:59:12 -04001558 if (parcel.readInt() != 0) {
1559 mSmallIcon = Icon.CREATOR.createFromParcel(parcel);
Dan Sandler4e787062015-06-17 15:09:48 -04001560 if (mSmallIcon.getType() == Icon.TYPE_RESOURCE) {
1561 icon = mSmallIcon.getResId();
1562 }
Dan Sandler3936e7a2015-05-19 20:59:12 -04001563 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001564 number = parcel.readInt();
1565 if (parcel.readInt() != 0) {
1566 contentIntent = PendingIntent.CREATOR.createFromParcel(parcel);
1567 }
1568 if (parcel.readInt() != 0) {
1569 deleteIntent = PendingIntent.CREATOR.createFromParcel(parcel);
1570 }
1571 if (parcel.readInt() != 0) {
1572 tickerText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
1573 }
1574 if (parcel.readInt() != 0) {
Joe Onorato46439ce2010-11-19 13:56:21 -08001575 tickerView = RemoteViews.CREATOR.createFromParcel(parcel);
Joe Onoratoef1e7762010-09-17 18:38:38 -04001576 }
1577 if (parcel.readInt() != 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001578 contentView = RemoteViews.CREATOR.createFromParcel(parcel);
1579 }
Joe Onorato561d3852010-11-20 18:09:34 -08001580 if (parcel.readInt() != 0) {
Dan Sandlerd63f9322015-05-06 15:18:49 -04001581 mLargeIcon = Icon.CREATOR.createFromParcel(parcel);
Joe Onorato561d3852010-11-20 18:09:34 -08001582 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001583 defaults = parcel.readInt();
1584 flags = parcel.readInt();
1585 if (parcel.readInt() != 0) {
1586 sound = Uri.CREATOR.createFromParcel(parcel);
1587 }
1588
1589 audioStreamType = parcel.readInt();
John Spurlockc0650f022014-07-19 13:22:39 -04001590 if (parcel.readInt() != 0) {
1591 audioAttributes = AudioAttributes.CREATOR.createFromParcel(parcel);
1592 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001593 vibrate = parcel.createLongArray();
1594 ledARGB = parcel.readInt();
1595 ledOnMS = parcel.readInt();
1596 ledOffMS = parcel.readInt();
1597 iconLevel = parcel.readInt();
Daniel Sandlere46cbd32010-06-17 10:35:26 -04001598
1599 if (parcel.readInt() != 0) {
1600 fullScreenIntent = PendingIntent.CREATOR.createFromParcel(parcel);
1601 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001602
1603 priority = parcel.readInt();
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001604
John Spurlockfd7f1e02014-03-18 16:41:57 -04001605 category = parcel.readString();
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001606
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001607 mGroupKey = parcel.readString();
1608
1609 mSortKey = parcel.readString();
1610
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001611 extras = Bundle.setDefusable(parcel.readBundle(), true); // may be null
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001612
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001613 actions = parcel.createTypedArray(Action.CREATOR); // may be null
1614
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001615 if (parcel.readInt() != 0) {
Daniel Sandlerf3b73432012-03-27 15:01:25 -04001616 bigContentView = RemoteViews.CREATOR.createFromParcel(parcel);
1617 }
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001618
Chris Wren8fd39ec2014-02-27 17:43:26 -05001619 if (parcel.readInt() != 0) {
1620 headsUpContentView = RemoteViews.CREATOR.createFromParcel(parcel);
1621 }
1622
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001623 visibility = parcel.readInt();
1624
1625 if (parcel.readInt() != 0) {
1626 publicVersion = Notification.CREATOR.createFromParcel(parcel);
1627 }
Dan Sandler26e81cf2014-05-06 10:01:27 -04001628
1629 color = parcel.readInt();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001630 }
1631
Andy Stadler110988c2010-12-03 14:29:16 -08001632 @Override
Joe Onorato18e69df2010-05-17 22:26:12 -07001633 public Notification clone() {
1634 Notification that = new Notification();
Daniel Sandler1a497d32013-04-18 14:52:45 -04001635 cloneInto(that, true);
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001636 return that;
1637 }
Joe Onorato18e69df2010-05-17 22:26:12 -07001638
Daniel Sandler1a497d32013-04-18 14:52:45 -04001639 /**
1640 * Copy all (or if heavy is false, all except Bitmaps and RemoteViews) members
1641 * of this into that.
1642 * @hide
1643 */
1644 public void cloneInto(Notification that, boolean heavy) {
Joe Onorato18e69df2010-05-17 22:26:12 -07001645 that.when = this.when;
Selim Cinekb85f36fd2016-04-20 18:46:36 -07001646 that.creationTime = this.creationTime;
Dan Sandlerd63f9322015-05-06 15:18:49 -04001647 that.mSmallIcon = this.mSmallIcon;
Joe Onorato18e69df2010-05-17 22:26:12 -07001648 that.number = this.number;
1649
1650 // PendingIntents are global, so there's no reason (or way) to clone them.
1651 that.contentIntent = this.contentIntent;
1652 that.deleteIntent = this.deleteIntent;
Daniel Sandlere46cbd32010-06-17 10:35:26 -04001653 that.fullScreenIntent = this.fullScreenIntent;
Joe Onorato18e69df2010-05-17 22:26:12 -07001654
1655 if (this.tickerText != null) {
1656 that.tickerText = this.tickerText.toString();
1657 }
Daniel Sandler1a497d32013-04-18 14:52:45 -04001658 if (heavy && this.tickerView != null) {
Joe Onorato46439ce2010-11-19 13:56:21 -08001659 that.tickerView = this.tickerView.clone();
Joe Onoratoef1e7762010-09-17 18:38:38 -04001660 }
Daniel Sandler1a497d32013-04-18 14:52:45 -04001661 if (heavy && this.contentView != null) {
Joe Onorato18e69df2010-05-17 22:26:12 -07001662 that.contentView = this.contentView.clone();
1663 }
Dan Sandlerd63f9322015-05-06 15:18:49 -04001664 if (heavy && this.mLargeIcon != null) {
1665 that.mLargeIcon = this.mLargeIcon;
Joe Onorato561d3852010-11-20 18:09:34 -08001666 }
Jozef BABJAKa8b91832011-02-22 08:05:08 +01001667 that.iconLevel = this.iconLevel;
Joe Onorato18e69df2010-05-17 22:26:12 -07001668 that.sound = this.sound; // android.net.Uri is immutable
1669 that.audioStreamType = this.audioStreamType;
John Spurlockc0650f022014-07-19 13:22:39 -04001670 if (this.audioAttributes != null) {
1671 that.audioAttributes = new AudioAttributes.Builder(this.audioAttributes).build();
1672 }
Joe Onorato18e69df2010-05-17 22:26:12 -07001673
1674 final long[] vibrate = this.vibrate;
1675 if (vibrate != null) {
1676 final int N = vibrate.length;
1677 final long[] vib = that.vibrate = new long[N];
1678 System.arraycopy(vibrate, 0, vib, 0, N);
1679 }
1680
1681 that.ledARGB = this.ledARGB;
1682 that.ledOnMS = this.ledOnMS;
1683 that.ledOffMS = this.ledOffMS;
1684 that.defaults = this.defaults;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001685
Joe Onorato18e69df2010-05-17 22:26:12 -07001686 that.flags = this.flags;
1687
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001688 that.priority = this.priority;
Joe Malin8d40d042012-11-05 11:36:40 -08001689
John Spurlockfd7f1e02014-03-18 16:41:57 -04001690 that.category = this.category;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001691
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001692 that.mGroupKey = this.mGroupKey;
1693
1694 that.mSortKey = this.mSortKey;
1695
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001696 if (this.extras != null) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04001697 try {
1698 that.extras = new Bundle(this.extras);
1699 // will unparcel
1700 that.extras.size();
1701 } catch (BadParcelableException e) {
1702 Log.e(TAG, "could not unparcel extras from notification: " + this, e);
1703 that.extras = null;
1704 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001705 }
1706
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001707 if (this.actions != null) {
1708 that.actions = new Action[this.actions.length];
1709 for(int i=0; i<this.actions.length; i++) {
1710 that.actions[i] = this.actions[i].clone();
1711 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001712 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001713
Daniel Sandler1a497d32013-04-18 14:52:45 -04001714 if (heavy && this.bigContentView != null) {
Daniel Sandlerf3b73432012-03-27 15:01:25 -04001715 that.bigContentView = this.bigContentView.clone();
1716 }
Daniel Sandler1a497d32013-04-18 14:52:45 -04001717
Chris Wren8fd39ec2014-02-27 17:43:26 -05001718 if (heavy && this.headsUpContentView != null) {
1719 that.headsUpContentView = this.headsUpContentView.clone();
1720 }
1721
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001722 that.visibility = this.visibility;
1723
1724 if (this.publicVersion != null) {
1725 that.publicVersion = new Notification();
1726 this.publicVersion.cloneInto(that.publicVersion, heavy);
1727 }
1728
Dan Sandler26e81cf2014-05-06 10:01:27 -04001729 that.color = this.color;
1730
Daniel Sandler1a497d32013-04-18 14:52:45 -04001731 if (!heavy) {
1732 that.lightenPayload(); // will clean out extras
1733 }
1734 }
1735
1736 /**
1737 * Removes heavyweight parts of the Notification object for archival or for sending to
1738 * listeners when the full contents are not necessary.
1739 * @hide
1740 */
1741 public final void lightenPayload() {
1742 tickerView = null;
1743 contentView = null;
1744 bigContentView = null;
Chris Wren8fd39ec2014-02-27 17:43:26 -05001745 headsUpContentView = null;
Dan Sandlerd63f9322015-05-06 15:18:49 -04001746 mLargeIcon = null;
Dan Sandler50128532015-12-08 15:42:41 -05001747 if (extras != null && !extras.isEmpty()) {
1748 final Set<String> keyset = extras.keySet();
1749 final int N = keyset.size();
1750 final String[] keys = keyset.toArray(new String[N]);
1751 for (int i=0; i<N; i++) {
1752 final String key = keys[i];
1753 final Object obj = extras.get(key);
1754 if (obj != null &&
1755 ( obj instanceof Parcelable
1756 || obj instanceof Parcelable[]
1757 || obj instanceof SparseArray
1758 || obj instanceof ArrayList)) {
1759 extras.remove(key);
1760 }
1761 }
Daniel Sandler1a497d32013-04-18 14:52:45 -04001762 }
Joe Onorato18e69df2010-05-17 22:26:12 -07001763 }
1764
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04001765 /**
1766 * Make sure this CharSequence is safe to put into a bundle, which basically
1767 * means it had better not be some custom Parcelable implementation.
1768 * @hide
1769 */
1770 public static CharSequence safeCharSequence(CharSequence cs) {
Christoph Studer535ec612014-09-03 15:47:47 +02001771 if (cs == null) return cs;
1772 if (cs.length() > MAX_CHARSEQUENCE_LENGTH) {
1773 cs = cs.subSequence(0, MAX_CHARSEQUENCE_LENGTH);
1774 }
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04001775 if (cs instanceof Parcelable) {
1776 Log.e(TAG, "warning: " + cs.getClass().getCanonicalName()
1777 + " instance is a custom Parcelable and not allowed in Notification");
1778 return cs.toString();
1779 }
Selim Cinek60a54252016-02-26 17:03:25 -08001780 return removeTextSizeSpans(cs);
1781 }
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04001782
Selim Cinek60a54252016-02-26 17:03:25 -08001783 private static CharSequence removeTextSizeSpans(CharSequence charSequence) {
1784 if (charSequence instanceof Spanned) {
1785 Spanned ss = (Spanned) charSequence;
1786 Object[] spans = ss.getSpans(0, ss.length(), Object.class);
1787 SpannableStringBuilder builder = new SpannableStringBuilder(ss.toString());
1788 for (Object span : spans) {
1789 Object resultSpan = span;
Selim Cinek89991a22016-03-07 19:51:54 -08001790 if (resultSpan instanceof CharacterStyle) {
1791 resultSpan = ((CharacterStyle) span).getUnderlying();
1792 }
1793 if (resultSpan instanceof TextAppearanceSpan) {
1794 TextAppearanceSpan originalSpan = (TextAppearanceSpan) resultSpan;
Selim Cinek60a54252016-02-26 17:03:25 -08001795 resultSpan = new TextAppearanceSpan(
1796 originalSpan.getFamily(),
1797 originalSpan.getTextStyle(),
1798 -1,
1799 originalSpan.getTextColor(),
1800 originalSpan.getLinkTextColor());
Selim Cinek89991a22016-03-07 19:51:54 -08001801 } else if (resultSpan instanceof RelativeSizeSpan
1802 || resultSpan instanceof AbsoluteSizeSpan) {
Selim Cinek60a54252016-02-26 17:03:25 -08001803 continue;
Selim Cinek89991a22016-03-07 19:51:54 -08001804 } else {
1805 resultSpan = span;
Selim Cinek60a54252016-02-26 17:03:25 -08001806 }
1807 builder.setSpan(resultSpan, ss.getSpanStart(span), ss.getSpanEnd(span),
1808 ss.getSpanFlags(span));
1809 }
1810 return builder;
1811 }
1812 return charSequence;
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04001813 }
1814
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001815 public int describeContents() {
1816 return 0;
1817 }
1818
1819 /**
Dan Sandler4e787062015-06-17 15:09:48 -04001820 * Flatten this notification into a parcel.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001821 */
1822 public void writeToParcel(Parcel parcel, int flags)
1823 {
1824 parcel.writeInt(1);
1825
1826 parcel.writeLong(when);
Selim Cinekb85f36fd2016-04-20 18:46:36 -07001827 parcel.writeLong(creationTime);
Dan Sandler4e787062015-06-17 15:09:48 -04001828 if (mSmallIcon == null && icon != 0) {
1829 // you snuck an icon in here without using the builder; let's try to keep it
1830 mSmallIcon = Icon.createWithResource("", icon);
1831 }
Dan Sandler3936e7a2015-05-19 20:59:12 -04001832 if (mSmallIcon != null) {
1833 parcel.writeInt(1);
1834 mSmallIcon.writeToParcel(parcel, 0);
1835 } else {
1836 parcel.writeInt(0);
1837 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001838 parcel.writeInt(number);
1839 if (contentIntent != null) {
1840 parcel.writeInt(1);
1841 contentIntent.writeToParcel(parcel, 0);
1842 } else {
1843 parcel.writeInt(0);
1844 }
1845 if (deleteIntent != null) {
1846 parcel.writeInt(1);
1847 deleteIntent.writeToParcel(parcel, 0);
1848 } else {
1849 parcel.writeInt(0);
1850 }
1851 if (tickerText != null) {
1852 parcel.writeInt(1);
1853 TextUtils.writeToParcel(tickerText, parcel, flags);
1854 } else {
1855 parcel.writeInt(0);
1856 }
Joe Onorato46439ce2010-11-19 13:56:21 -08001857 if (tickerView != null) {
Joe Onoratoef1e7762010-09-17 18:38:38 -04001858 parcel.writeInt(1);
Joe Onorato46439ce2010-11-19 13:56:21 -08001859 tickerView.writeToParcel(parcel, 0);
Joe Onoratoef1e7762010-09-17 18:38:38 -04001860 } else {
1861 parcel.writeInt(0);
1862 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001863 if (contentView != null) {
1864 parcel.writeInt(1);
1865 contentView.writeToParcel(parcel, 0);
1866 } else {
1867 parcel.writeInt(0);
1868 }
Dan Sandlerd63f9322015-05-06 15:18:49 -04001869 if (mLargeIcon != null) {
Joe Onorato561d3852010-11-20 18:09:34 -08001870 parcel.writeInt(1);
Dan Sandlerd63f9322015-05-06 15:18:49 -04001871 mLargeIcon.writeToParcel(parcel, 0);
Joe Onorato561d3852010-11-20 18:09:34 -08001872 } else {
1873 parcel.writeInt(0);
1874 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001875
1876 parcel.writeInt(defaults);
1877 parcel.writeInt(this.flags);
1878
1879 if (sound != null) {
1880 parcel.writeInt(1);
1881 sound.writeToParcel(parcel, 0);
1882 } else {
1883 parcel.writeInt(0);
1884 }
1885 parcel.writeInt(audioStreamType);
John Spurlockc0650f022014-07-19 13:22:39 -04001886
1887 if (audioAttributes != null) {
1888 parcel.writeInt(1);
1889 audioAttributes.writeToParcel(parcel, 0);
1890 } else {
1891 parcel.writeInt(0);
1892 }
1893
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001894 parcel.writeLongArray(vibrate);
1895 parcel.writeInt(ledARGB);
1896 parcel.writeInt(ledOnMS);
1897 parcel.writeInt(ledOffMS);
1898 parcel.writeInt(iconLevel);
Daniel Sandlere46cbd32010-06-17 10:35:26 -04001899
1900 if (fullScreenIntent != null) {
1901 parcel.writeInt(1);
1902 fullScreenIntent.writeToParcel(parcel, 0);
1903 } else {
1904 parcel.writeInt(0);
1905 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001906
1907 parcel.writeInt(priority);
Joe Malin8d40d042012-11-05 11:36:40 -08001908
John Spurlockfd7f1e02014-03-18 16:41:57 -04001909 parcel.writeString(category);
Joe Malin8d40d042012-11-05 11:36:40 -08001910
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001911 parcel.writeString(mGroupKey);
1912
1913 parcel.writeString(mSortKey);
1914
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001915 parcel.writeBundle(extras); // null ok
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001916
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001917 parcel.writeTypedArray(actions, 0); // null ok
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001918
Daniel Sandlerf3b73432012-03-27 15:01:25 -04001919 if (bigContentView != null) {
1920 parcel.writeInt(1);
1921 bigContentView.writeToParcel(parcel, 0);
1922 } else {
1923 parcel.writeInt(0);
1924 }
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001925
Chris Wren8fd39ec2014-02-27 17:43:26 -05001926 if (headsUpContentView != null) {
1927 parcel.writeInt(1);
1928 headsUpContentView.writeToParcel(parcel, 0);
1929 } else {
1930 parcel.writeInt(0);
1931 }
1932
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001933 parcel.writeInt(visibility);
1934
1935 if (publicVersion != null) {
1936 parcel.writeInt(1);
1937 publicVersion.writeToParcel(parcel, 0);
1938 } else {
1939 parcel.writeInt(0);
1940 }
Dan Sandler26e81cf2014-05-06 10:01:27 -04001941
1942 parcel.writeInt(color);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001943 }
1944
1945 /**
1946 * Parcelable.Creator that instantiates Notification objects
1947 */
1948 public static final Parcelable.Creator<Notification> CREATOR
1949 = new Parcelable.Creator<Notification>()
1950 {
1951 public Notification createFromParcel(Parcel parcel)
1952 {
1953 return new Notification(parcel);
1954 }
1955
1956 public Notification[] newArray(int size)
1957 {
1958 return new Notification[size];
1959 }
1960 };
1961
1962 /**
1963 * Sets the {@link #contentView} field to be a view with the standard "Latest Event"
1964 * layout.
1965 *
1966 * <p>Uses the {@link #icon} and {@link #when} fields to set the icon and time fields
1967 * in the view.</p>
1968 * @param context The context for your application / activity.
1969 * @param contentTitle The title that goes in the expanded entry.
1970 * @param contentText The text that goes in the expanded entry.
1971 * @param contentIntent The intent to launch when the user clicks the expanded notification.
1972 * If this is an activity, it must include the
1973 * {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} flag, which requires
Scott Main7aee61f2011-02-08 11:25:01 -08001974 * that you take care of task management as described in the
1975 * <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back
1976 * Stack</a> document.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001977 *
Joe Onorato46439ce2010-11-19 13:56:21 -08001978 * @deprecated Use {@link Builder} instead.
Chris Wrena05db382015-06-24 15:18:34 -04001979 * @removed
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001980 */
Joe Onorato46439ce2010-11-19 13:56:21 -08001981 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001982 public void setLatestEventInfo(Context context,
1983 CharSequence contentTitle, CharSequence contentText, PendingIntent contentIntent) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04001984 if (context.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1){
1985 Log.e(TAG, "setLatestEventInfo() is deprecated and you should feel deprecated.",
1986 new Throwable());
1987 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001988
Selim Cinek4ac6f602016-06-13 15:47:03 -07001989 if (context.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N) {
1990 extras.putBoolean(EXTRA_SHOW_WHEN, true);
1991 }
1992
Julia Reynoldsd9228f12015-10-20 10:37:27 -04001993 // ensure that any information already set directly is preserved
1994 final Notification.Builder builder = new Notification.Builder(context, this);
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001995
1996 // now apply the latestEventInfo fields
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001997 if (contentTitle != null) {
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001998 builder.setContentTitle(contentTitle);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001999 }
2000 if (contentText != null) {
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002001 builder.setContentText(contentText);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002002 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002003 builder.setContentIntent(contentIntent);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002004
2005 builder.build(); // callers expect this notification to be ready to use
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002006 }
2007
Julia Reynoldsda303542015-11-23 14:00:20 -05002008 /**
2009 * @hide
2010 */
2011 public static void addFieldsFromContext(Context context, Notification notification) {
Jeff Sharkey012bc7b2016-04-11 16:30:27 -06002012 addFieldsFromContext(context.getApplicationInfo(), context.getUserId(), notification);
2013 }
2014
2015 /**
2016 * @hide
2017 */
2018 public static void addFieldsFromContext(ApplicationInfo ai, int userId,
2019 Notification notification) {
2020 notification.extras.putParcelable(EXTRA_BUILDER_APPLICATION_INFO, ai);
2021 notification.extras.putInt(EXTRA_ORIGINATING_USERID, userId);
Julia Reynoldsda303542015-11-23 14:00:20 -05002022 }
2023
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002024 @Override
2025 public String toString() {
2026 StringBuilder sb = new StringBuilder();
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002027 sb.append("Notification(pri=");
2028 sb.append(priority);
2029 sb.append(" contentView=");
Joe Onoratoc9596d62011-01-12 17:03:11 -08002030 if (contentView != null) {
2031 sb.append(contentView.getPackage());
2032 sb.append("/0x");
2033 sb.append(Integer.toHexString(contentView.getLayoutId()));
2034 } else {
2035 sb.append("null");
2036 }
2037 sb.append(" vibrate=");
Daniel Sandler6738eee2012-11-16 12:03:32 -05002038 if ((this.defaults & DEFAULT_VIBRATE) != 0) {
2039 sb.append("default");
2040 } else if (this.vibrate != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002041 int N = this.vibrate.length-1;
2042 sb.append("[");
2043 for (int i=0; i<N; i++) {
2044 sb.append(this.vibrate[i]);
2045 sb.append(',');
2046 }
Simon Schoar8cf97d92009-06-10 22:08:37 +02002047 if (N != -1) {
2048 sb.append(this.vibrate[N]);
2049 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002050 sb.append("]");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002051 } else {
2052 sb.append("null");
2053 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002054 sb.append(" sound=");
Daniel Sandler6738eee2012-11-16 12:03:32 -05002055 if ((this.defaults & DEFAULT_SOUND) != 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002056 sb.append("default");
Daniel Sandler6738eee2012-11-16 12:03:32 -05002057 } else if (this.sound != null) {
2058 sb.append(this.sound.toString());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002059 } else {
2060 sb.append("null");
2061 }
Chris Wren365b6d32015-07-16 10:39:26 -04002062 if (this.tickerText != null) {
2063 sb.append(" tick");
2064 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002065 sb.append(" defaults=0x");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002066 sb.append(Integer.toHexString(this.defaults));
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002067 sb.append(" flags=0x");
Daniel Sandlere46cbd32010-06-17 10:35:26 -04002068 sb.append(Integer.toHexString(this.flags));
Dan Sandler26e81cf2014-05-06 10:01:27 -04002069 sb.append(String.format(" color=0x%08x", this.color));
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002070 if (this.category != null) {
2071 sb.append(" category=");
2072 sb.append(this.category);
2073 }
2074 if (this.mGroupKey != null) {
2075 sb.append(" groupKey=");
2076 sb.append(this.mGroupKey);
2077 }
2078 if (this.mSortKey != null) {
2079 sb.append(" sortKey=");
2080 sb.append(this.mSortKey);
2081 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002082 if (actions != null) {
Dan Sandler1b718782014-07-18 12:43:45 -04002083 sb.append(" actions=");
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002084 sb.append(actions.length);
Dan Sandler1b718782014-07-18 12:43:45 -04002085 }
2086 sb.append(" vis=");
2087 sb.append(visibilityToString(this.visibility));
2088 if (this.publicVersion != null) {
2089 sb.append(" publicVersion=");
2090 sb.append(publicVersion.toString());
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002091 }
2092 sb.append(")");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002093 return sb.toString();
2094 }
Joe Onorato46439ce2010-11-19 13:56:21 -08002095
Dan Sandler1b718782014-07-18 12:43:45 -04002096 /**
2097 * {@hide}
2098 */
2099 public static String visibilityToString(int vis) {
2100 switch (vis) {
2101 case VISIBILITY_PRIVATE:
2102 return "PRIVATE";
2103 case VISIBILITY_PUBLIC:
2104 return "PUBLIC";
2105 case VISIBILITY_SECRET:
2106 return "SECRET";
2107 default:
2108 return "UNKNOWN(" + String.valueOf(vis) + ")";
2109 }
2110 }
2111
Joe Onoratocb109a02011-01-18 17:57:41 -08002112 /**
John Spurlock1d881a12015-03-18 19:21:54 -04002113 * {@hide}
2114 */
2115 public static String priorityToString(@Priority int pri) {
2116 switch (pri) {
2117 case PRIORITY_MIN:
2118 return "MIN";
2119 case PRIORITY_LOW:
2120 return "LOW";
2121 case PRIORITY_DEFAULT:
2122 return "DEFAULT";
2123 case PRIORITY_HIGH:
2124 return "HIGH";
2125 case PRIORITY_MAX:
2126 return "MAX";
2127 default:
2128 return "UNKNOWN(" + String.valueOf(pri) + ")";
2129 }
2130 }
2131
2132 /**
Dan Sandlerd63f9322015-05-06 15:18:49 -04002133 * The small icon representing this notification in the status bar and content view.
2134 *
2135 * @return the small icon representing this notification.
2136 *
2137 * @see Builder#getSmallIcon()
2138 * @see Builder#setSmallIcon(Icon)
2139 */
2140 public Icon getSmallIcon() {
2141 return mSmallIcon;
2142 }
2143
2144 /**
2145 * Used when notifying to clean up legacy small icons.
2146 * @hide
2147 */
2148 public void setSmallIcon(Icon icon) {
2149 mSmallIcon = icon;
2150 }
2151
2152 /**
2153 * The large icon shown in this notification's content view.
2154 * @see Builder#getLargeIcon()
2155 * @see Builder#setLargeIcon(Icon)
2156 */
2157 public Icon getLargeIcon() {
2158 return mLargeIcon;
2159 }
2160
2161 /**
Christoph Studer4600f9b2014-07-22 22:44:43 +02002162 * @hide
2163 */
Christoph Studerc8db24b2014-07-25 17:50:30 +02002164 public boolean isGroupSummary() {
2165 return mGroupKey != null && (flags & FLAG_GROUP_SUMMARY) != 0;
2166 }
2167
2168 /**
2169 * @hide
2170 */
2171 public boolean isGroupChild() {
2172 return mGroupKey != null && (flags & FLAG_GROUP_SUMMARY) == 0;
2173 }
2174
2175 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002176 * Builder class for {@link Notification} objects.
Joe Malin8d40d042012-11-05 11:36:40 -08002177 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002178 * Provides a convenient way to set the various fields of a {@link Notification} and generate
Scott Main183bf112012-08-13 19:12:13 -07002179 * content views using the platform's notification layout template. If your app supports
2180 * versions of Android as old as API level 4, you can instead use
2181 * {@link android.support.v4.app.NotificationCompat.Builder NotificationCompat.Builder},
2182 * available in the <a href="{@docRoot}tools/extras/support-library.html">Android Support
2183 * library</a>.
Joe Malin8d40d042012-11-05 11:36:40 -08002184 *
Scott Main183bf112012-08-13 19:12:13 -07002185 * <p>Example:
Joe Malin8d40d042012-11-05 11:36:40 -08002186 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002187 * <pre class="prettyprint">
Scott Main183bf112012-08-13 19:12:13 -07002188 * Notification noti = new Notification.Builder(mContext)
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002189 * .setContentTitle(&quot;New mail from &quot; + sender.toString())
2190 * .setContentText(subject)
2191 * .setSmallIcon(R.drawable.new_mail)
2192 * .setLargeIcon(aBitmap)
Chris Wrenfbd96ba2012-05-01 12:03:58 -04002193 * .build();
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002194 * </pre>
Joe Onoratocb109a02011-01-18 17:57:41 -08002195 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002196 public static class Builder {
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05002197 /**
2198 * @hide
2199 */
2200 public static final String EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT =
2201 "android.rebuild.contentViewActionCount";
2202 /**
2203 * @hide
2204 */
2205 public static final String EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT
2206 = "android.rebuild.bigViewActionCount";
2207 /**
2208 * @hide
2209 */
2210 public static final String EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT
2211 = "android.rebuild.hudViewActionCount";
2212
Daniel Sandler602ad1c2012-06-12 16:06:27 -04002213 private static final int MAX_ACTION_BUTTONS = 3;
Daniel Sandler8680bf82012-05-15 16:52:52 -04002214
Joe Onorato46439ce2010-11-19 13:56:21 -08002215 private Context mContext;
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002216 private Notification mN;
2217 private Bundle mUserExtras = new Bundle();
Chris Wrenfbd96ba2012-05-01 12:03:58 -04002218 private Style mStyle;
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002219 private ArrayList<Action> mActions = new ArrayList<Action>(MAX_ACTION_BUTTONS);
2220 private ArrayList<String> mPersonList = new ArrayList<String>();
2221 private NotificationColorUtil mColorUtil;
2222 private boolean mColorUtilInited = false;
Christoph Studer7ac80e62014-08-04 16:01:57 +02002223
2224 /**
Adrian Roos4ff3b122016-02-01 12:26:13 -08002225 * Caches a contrast-enhanced version of {@link #mCachedContrastColorIsFor}.
2226 */
2227 private int mCachedContrastColor = COLOR_INVALID;
2228 private int mCachedContrastColorIsFor = COLOR_INVALID;
2229
2230 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002231 * Constructs a new Builder with the defaults:
Joe Onoratocb109a02011-01-18 17:57:41 -08002232 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002233
2234 * <table>
2235 * <tr><th align=right>priority</th>
2236 * <td>{@link #PRIORITY_DEFAULT}</td></tr>
2237 * <tr><th align=right>when</th>
2238 * <td>now ({@link System#currentTimeMillis()})</td></tr>
2239 * <tr><th align=right>audio stream</th>
2240 * <td>{@link #STREAM_DEFAULT}</td></tr>
2241 * </table>
Joe Onoratocb109a02011-01-18 17:57:41 -08002242 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002243
2244 * @param context
2245 * A {@link Context} that will be used by the Builder to construct the
2246 * RemoteViews. The Context will not be held past the lifetime of this Builder
2247 * object.
Joe Onoratocb109a02011-01-18 17:57:41 -08002248 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002249 public Builder(Context context) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002250 this(context, null);
Joe Onorato46439ce2010-11-19 13:56:21 -08002251 }
2252
Joe Onoratocb109a02011-01-18 17:57:41 -08002253 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002254 * @hide
Christoph Studer4600f9b2014-07-22 22:44:43 +02002255 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002256 public Builder(Context context, Notification toAdopt) {
2257 mContext = context;
Christoph Studer4600f9b2014-07-22 22:44:43 +02002258
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002259 if (toAdopt == null) {
2260 mN = new Notification();
Selim Cinek0ff1ce602016-04-05 18:27:16 -07002261 if (context.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N) {
2262 mN.extras.putBoolean(EXTRA_SHOW_WHEN, true);
2263 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002264 mN.priority = PRIORITY_DEFAULT;
2265 mN.visibility = VISIBILITY_PRIVATE;
2266 } else {
2267 mN = toAdopt;
2268 if (mN.actions != null) {
2269 Collections.addAll(mActions, mN.actions);
Christoph Studer4600f9b2014-07-22 22:44:43 +02002270 }
2271
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002272 if (mN.extras.containsKey(EXTRA_PEOPLE)) {
2273 Collections.addAll(mPersonList, mN.extras.getStringArray(EXTRA_PEOPLE));
2274 }
2275
Selim Cinek4ac6f602016-06-13 15:47:03 -07002276 if (mN.getSmallIcon() == null && mN.icon != 0) {
2277 setSmallIcon(mN.icon);
2278 }
2279
2280 if (mN.getLargeIcon() == null && mN.largeIcon != null) {
2281 setLargeIcon(mN.largeIcon);
2282 }
2283
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002284 String templateClass = mN.extras.getString(EXTRA_TEMPLATE);
2285 if (!TextUtils.isEmpty(templateClass)) {
2286 final Class<? extends Style> styleClass
2287 = getNotificationStyleClass(templateClass);
2288 if (styleClass == null) {
2289 Log.d(TAG, "Unknown style class: " + templateClass);
2290 } else {
2291 try {
Adrian Roosc1a80b02016-04-05 14:54:55 -07002292 final Constructor<? extends Style> ctor =
2293 styleClass.getDeclaredConstructor();
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002294 ctor.setAccessible(true);
2295 final Style style = ctor.newInstance();
2296 style.restoreFromExtras(mN.extras);
2297
2298 if (style != null) {
2299 setStyle(style);
2300 }
2301 } catch (Throwable t) {
2302 Log.e(TAG, "Could not create Style", t);
2303 }
2304 }
2305 }
2306
2307 }
2308 }
2309
2310 private NotificationColorUtil getColorUtil() {
2311 if (!mColorUtilInited) {
2312 mColorUtilInited = true;
2313 if (mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.LOLLIPOP) {
2314 mColorUtil = NotificationColorUtil.getInstance(mContext);
Christoph Studer4600f9b2014-07-22 22:44:43 +02002315 }
2316 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002317 return mColorUtil;
Christoph Studer4600f9b2014-07-22 22:44:43 +02002318 }
2319
2320 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002321 * Add a timestamp pertaining to the notification (usually the time the event occurred).
Selim Cinek0ff1ce602016-04-05 18:27:16 -07002322 *
2323 * For apps targeting {@link android.os.Build.VERSION_CODES#N} and above, this time is not
2324 * shown anymore by default and must be opted into by using
2325 * {@link android.app.Notification.Builder#setShowWhen(boolean)}
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002326 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002327 * @see Notification#when
Joe Onoratocb109a02011-01-18 17:57:41 -08002328 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002329 public Builder setWhen(long when) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002330 mN.when = when;
Joe Onorato46439ce2010-11-19 13:56:21 -08002331 return this;
2332 }
2333
Joe Onoratocb109a02011-01-18 17:57:41 -08002334 /**
Griff Hazen50c11652014-05-16 09:46:31 -07002335 * Control whether the timestamp set with {@link #setWhen(long) setWhen} is shown
Daniel Sandler0c890492012-09-12 17:23:10 -07002336 * in the content view.
Selim Cinek0ff1ce602016-04-05 18:27:16 -07002337 * For apps targeting {@link android.os.Build.VERSION_CODES#N} and above, this defaults to
2338 * {@code false}. For earlier apps, the default is {@code true}.
Daniel Sandler0c890492012-09-12 17:23:10 -07002339 */
2340 public Builder setShowWhen(boolean show) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002341 mN.extras.putBoolean(EXTRA_SHOW_WHEN, show);
Daniel Sandler0c890492012-09-12 17:23:10 -07002342 return this;
2343 }
2344
2345 /**
Daniel Sandlerd33b8032012-05-10 11:41:48 -04002346 * Show the {@link Notification#when} field as a stopwatch.
Joe Malin8d40d042012-11-05 11:36:40 -08002347 *
2348 * Instead of presenting <code>when</code> as a timestamp, the notification will show an
Daniel Sandlerd33b8032012-05-10 11:41:48 -04002349 * automatically updating display of the minutes and seconds since <code>when</code>.
Daniel Sandlera2985ed2012-04-03 16:42:00 -04002350 *
Daniel Sandlerd33b8032012-05-10 11:41:48 -04002351 * Useful when showing an elapsed time (like an ongoing phone call).
2352 *
Selim Cinek81c23aa2016-02-25 16:23:13 -08002353 * The counter can also be set to count down to <code>when</code> when using
Adrian Roos96b7e202016-05-17 13:50:38 -07002354 * {@link #setChronometerCountDown(boolean)}.
Selim Cinek81c23aa2016-02-25 16:23:13 -08002355 *
Daniel Sandlerd33b8032012-05-10 11:41:48 -04002356 * @see android.widget.Chronometer
Daniel Sandlera2985ed2012-04-03 16:42:00 -04002357 * @see Notification#when
Adrian Roos96b7e202016-05-17 13:50:38 -07002358 * @see #setChronometerCountDown(boolean)
Daniel Sandlera2985ed2012-04-03 16:42:00 -04002359 */
2360 public Builder setUsesChronometer(boolean b) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002361 mN.extras.putBoolean(EXTRA_SHOW_CHRONOMETER, b);
Daniel Sandlera2985ed2012-04-03 16:42:00 -04002362 return this;
2363 }
2364
2365 /**
Selim Cinek81c23aa2016-02-25 16:23:13 -08002366 * Sets the Chronometer to count down instead of counting up.
2367 *
2368 * <p>This is only relevant if {@link #setUsesChronometer(boolean)} has been set to true.
2369 * If it isn't set the chronometer will count up.
2370 *
2371 * @see #setUsesChronometer(boolean)
2372 */
Adrian Roos96b7e202016-05-17 13:50:38 -07002373 public Builder setChronometerCountDown(boolean countDown) {
2374 mN.extras.putBoolean(EXTRA_CHRONOMETER_COUNT_DOWN, countDown);
Selim Cinek81c23aa2016-02-25 16:23:13 -08002375 return this;
2376 }
2377
2378 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002379 * Set the small icon resource, which will be used to represent the notification in the
2380 * status bar.
Joe Onoratocb109a02011-01-18 17:57:41 -08002381 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002382
2383 * The platform template for the expanded view will draw this icon in the left, unless a
2384 * {@link #setLargeIcon(Bitmap) large icon} has also been specified, in which case the small
2385 * icon will be moved to the right-hand side.
2386 *
2387
2388 * @param icon
2389 * A resource ID in the application's package of the drawable to use.
2390 * @see Notification#icon
Joe Onoratocb109a02011-01-18 17:57:41 -08002391 */
Tor Norbye7b9c9122013-05-30 16:48:33 -07002392 public Builder setSmallIcon(@DrawableRes int icon) {
Dan Sandlerd63f9322015-05-06 15:18:49 -04002393 return setSmallIcon(icon != 0
2394 ? Icon.createWithResource(mContext, icon)
2395 : null);
Joe Onorato46439ce2010-11-19 13:56:21 -08002396 }
2397
Joe Onoratocb109a02011-01-18 17:57:41 -08002398 /**
2399 * A variant of {@link #setSmallIcon(int) setSmallIcon(int)} that takes an additional
2400 * level parameter for when the icon is a {@link android.graphics.drawable.LevelListDrawable
2401 * LevelListDrawable}.
2402 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002403 * @param icon A resource ID in the application's package of the drawable to use.
Joe Onoratocb109a02011-01-18 17:57:41 -08002404 * @param level The level to use for the icon.
2405 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002406 * @see Notification#icon
2407 * @see Notification#iconLevel
Joe Onoratocb109a02011-01-18 17:57:41 -08002408 */
Tor Norbye7b9c9122013-05-30 16:48:33 -07002409 public Builder setSmallIcon(@DrawableRes int icon, int level) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002410 mN.iconLevel = level;
Dan Sandlerd63f9322015-05-06 15:18:49 -04002411 return setSmallIcon(icon);
2412 }
2413
2414 /**
2415 * Set the small icon, which will be used to represent the notification in the
2416 * status bar and content view (unless overriden there by a
2417 * {@link #setLargeIcon(Bitmap) large icon}).
2418 *
2419 * @param icon An Icon object to use.
2420 * @see Notification#icon
2421 */
2422 public Builder setSmallIcon(Icon icon) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002423 mN.setSmallIcon(icon);
2424 if (icon != null && icon.getType() == Icon.TYPE_RESOURCE) {
2425 mN.icon = icon.getResId();
2426 }
Joe Onorato46439ce2010-11-19 13:56:21 -08002427 return this;
2428 }
2429
Joe Onoratocb109a02011-01-18 17:57:41 -08002430 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002431 * Set the first line of text in the platform notification template.
Joe Onoratocb109a02011-01-18 17:57:41 -08002432 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002433 public Builder setContentTitle(CharSequence title) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002434 mN.extras.putCharSequence(EXTRA_TITLE, safeCharSequence(title));
Joe Onorato46439ce2010-11-19 13:56:21 -08002435 return this;
2436 }
2437
Joe Onoratocb109a02011-01-18 17:57:41 -08002438 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002439 * Set the second line of text in the platform notification template.
Joe Onoratocb109a02011-01-18 17:57:41 -08002440 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002441 public Builder setContentText(CharSequence text) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002442 mN.extras.putCharSequence(EXTRA_TEXT, safeCharSequence(text));
Joe Onorato46439ce2010-11-19 13:56:21 -08002443 return this;
2444 }
2445
Joe Onoratocb109a02011-01-18 17:57:41 -08002446 /**
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07002447 * This provides some additional information that is displayed in the notification. No
2448 * guarantees are given where exactly it is displayed.
2449 *
2450 * <p>This information should only be provided if it provides an essential
2451 * benefit to the understanding of the notification. The more text you provide the
2452 * less readable it becomes. For example, an email client should only provide the account
2453 * name here if more than one email account has been added.</p>
2454 *
2455 * <p>As of {@link android.os.Build.VERSION_CODES#N} this information is displayed in the
2456 * notification header area.
2457 *
2458 * On Android versions before {@link android.os.Build.VERSION_CODES#N}
2459 * this will be shown in the third line of text in the platform notification template.
2460 * You should not be using {@link #setProgress(int, int, boolean)} at the
2461 * same time on those versions; they occupy the same place.
2462 * </p>
Daniel Sandlerf3b73432012-03-27 15:01:25 -04002463 */
2464 public Builder setSubText(CharSequence text) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002465 mN.extras.putCharSequence(EXTRA_SUB_TEXT, safeCharSequence(text));
Daniel Sandlerf3b73432012-03-27 15:01:25 -04002466 return this;
2467 }
2468
2469 /**
Adrian Roose458aa82015-12-08 16:17:19 -08002470 * Set the remote input history.
2471 *
2472 * This should be set to the most recent inputs that have been sent
2473 * through a {@link RemoteInput} of this Notification and cleared once the it is no
2474 * longer relevant (e.g. for chat notifications once the other party has responded).
2475 *
2476 * The most recent input must be stored at the 0 index, the second most recent at the
2477 * 1 index, etc. Note that the system will limit both how far back the inputs will be shown
2478 * and how much of each individual input is shown.
2479 *
2480 * <p>Note: The reply text will only be shown on notifications that have least one action
2481 * with a {@code RemoteInput}.</p>
2482 */
2483 public Builder setRemoteInputHistory(CharSequence[] text) {
2484 if (text == null) {
2485 mN.extras.putCharSequenceArray(EXTRA_REMOTE_INPUT_HISTORY, null);
2486 } else {
2487 final int N = Math.min(MAX_REPLY_HISTORY, text.length);
2488 CharSequence[] safe = new CharSequence[N];
2489 for (int i = 0; i < N; i++) {
2490 safe[i] = safeCharSequence(text[i]);
2491 }
2492 mN.extras.putCharSequenceArray(EXTRA_REMOTE_INPUT_HISTORY, safe);
2493 }
2494 return this;
2495 }
2496
2497 /**
Joe Onoratocb109a02011-01-18 17:57:41 -08002498 * Set the large number at the right-hand side of the notification. This is
2499 * equivalent to setContentInfo, although it might show the number in a different
2500 * font size for readability.
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07002501 *
2502 * @deprecated this number is not shown anywhere anymore
Joe Onoratocb109a02011-01-18 17:57:41 -08002503 */
Joe Onorato8595a3d2010-11-19 18:12:07 -08002504 public Builder setNumber(int number) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002505 mN.number = number;
Joe Onorato8595a3d2010-11-19 18:12:07 -08002506 return this;
2507 }
2508
Joe Onoratocb109a02011-01-18 17:57:41 -08002509 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002510 * A small piece of additional information pertaining to this notification.
2511 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002512 * The platform template will draw this on the last line of the notification, at the far
2513 * right (to the right of a smallIcon if it has been placed there).
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07002514 *
2515 * @deprecated use {@link #setSubText(CharSequence)} instead to set a text in the header.
2516 * For legacy apps targeting a version below {@link android.os.Build.VERSION_CODES#N} this
2517 * field will still show up, but the subtext will take precedence.
Joe Onoratocb109a02011-01-18 17:57:41 -08002518 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002519 public Builder setContentInfo(CharSequence info) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002520 mN.extras.putCharSequence(EXTRA_INFO_TEXT, safeCharSequence(info));
Joe Onorato46439ce2010-11-19 13:56:21 -08002521 return this;
2522 }
2523
Joe Onoratocb109a02011-01-18 17:57:41 -08002524 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002525 * Set the progress this notification represents.
2526 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002527 * The platform template will represent this using a {@link ProgressBar}.
Jeff Sharkey1c400132011-08-05 14:50:13 -07002528 */
2529 public Builder setProgress(int max, int progress, boolean indeterminate) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002530 mN.extras.putInt(EXTRA_PROGRESS, progress);
2531 mN.extras.putInt(EXTRA_PROGRESS_MAX, max);
2532 mN.extras.putBoolean(EXTRA_PROGRESS_INDETERMINATE, indeterminate);
Jeff Sharkey1c400132011-08-05 14:50:13 -07002533 return this;
2534 }
2535
2536 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002537 * Supply a custom RemoteViews to use instead of the platform template.
2538 *
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002539 * Use {@link #setCustomContentView(RemoteViews)} instead.
Joe Onoratocb109a02011-01-18 17:57:41 -08002540 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002541 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08002542 public Builder setContent(RemoteViews views) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002543 return setCustomContentView(views);
2544 }
2545
2546 /**
2547 * Supply custom RemoteViews to use instead of the platform template.
2548 *
2549 * This will override the layout that would otherwise be constructed by this Builder
2550 * object.
2551 */
2552 public Builder setCustomContentView(RemoteViews contentView) {
2553 mN.contentView = contentView;
2554 return this;
2555 }
2556
2557 /**
2558 * Supply custom RemoteViews to use instead of the platform template in the expanded form.
2559 *
2560 * This will override the expanded layout that would otherwise be constructed by this
2561 * Builder object.
2562 */
2563 public Builder setCustomBigContentView(RemoteViews contentView) {
2564 mN.bigContentView = contentView;
2565 return this;
2566 }
2567
2568 /**
2569 * Supply custom RemoteViews to use instead of the platform template in the heads up dialog.
2570 *
2571 * This will override the heads-up layout that would otherwise be constructed by this
2572 * Builder object.
2573 */
2574 public Builder setCustomHeadsUpContentView(RemoteViews contentView) {
2575 mN.headsUpContentView = contentView;
Joe Onorato46439ce2010-11-19 13:56:21 -08002576 return this;
2577 }
2578
Joe Onoratocb109a02011-01-18 17:57:41 -08002579 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002580 * Supply a {@link PendingIntent} to be sent when the notification is clicked.
2581 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002582 * As of {@link android.os.Build.VERSION_CODES#HONEYCOMB}, if this field is unset and you
2583 * have specified a custom RemoteViews with {@link #setContent(RemoteViews)}, you can use
2584 * {@link RemoteViews#setOnClickPendingIntent RemoteViews.setOnClickPendingIntent(int,PendingIntent)}
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002585 * to assign PendingIntents to individual views in that custom layout (i.e., to create
Daniel Sandlerf3b73432012-03-27 15:01:25 -04002586 * clickable buttons inside the notification view).
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002587 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002588 * @see Notification#contentIntent Notification.contentIntent
Joe Onoratocb109a02011-01-18 17:57:41 -08002589 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002590 public Builder setContentIntent(PendingIntent intent) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002591 mN.contentIntent = intent;
Joe Onorato46439ce2010-11-19 13:56:21 -08002592 return this;
2593 }
2594
Joe Onoratocb109a02011-01-18 17:57:41 -08002595 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002596 * Supply a {@link PendingIntent} to send when the notification is cleared explicitly by the user.
2597 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002598 * @see Notification#deleteIntent
Joe Onoratocb109a02011-01-18 17:57:41 -08002599 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002600 public Builder setDeleteIntent(PendingIntent intent) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002601 mN.deleteIntent = intent;
Joe Onorato46439ce2010-11-19 13:56:21 -08002602 return this;
2603 }
2604
Joe Onoratocb109a02011-01-18 17:57:41 -08002605 /**
2606 * An intent to launch instead of posting the notification to the status bar.
2607 * Only for use with extremely high-priority notifications demanding the user's
2608 * <strong>immediate</strong> attention, such as an incoming phone call or
2609 * alarm clock that the user has explicitly set to a particular time.
2610 * If this facility is used for something else, please give the user an option
2611 * to turn it off and use a normal notification, as this can be extremely
2612 * disruptive.
2613 *
Chris Wren47c20a12014-06-18 17:27:29 -04002614 * <p>
2615 * The system UI may choose to display a heads-up notification, instead of
2616 * launching this intent, while the user is using the device.
2617 * </p>
2618 *
Joe Onoratocb109a02011-01-18 17:57:41 -08002619 * @param intent The pending intent to launch.
2620 * @param highPriority Passing true will cause this notification to be sent
2621 * even if other notifications are suppressed.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002622 *
2623 * @see Notification#fullScreenIntent
Joe Onoratocb109a02011-01-18 17:57:41 -08002624 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002625 public Builder setFullScreenIntent(PendingIntent intent, boolean highPriority) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002626 mN.fullScreenIntent = intent;
Joe Onorato46439ce2010-11-19 13:56:21 -08002627 setFlag(FLAG_HIGH_PRIORITY, highPriority);
2628 return this;
2629 }
2630
Joe Onoratocb109a02011-01-18 17:57:41 -08002631 /**
Dan Sandler5fcdf6e2014-07-18 11:31:15 -04002632 * Set the "ticker" text which is sent to accessibility services.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002633 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002634 * @see Notification#tickerText
Joe Onoratocb109a02011-01-18 17:57:41 -08002635 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002636 public Builder setTicker(CharSequence tickerText) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002637 mN.tickerText = safeCharSequence(tickerText);
Joe Onorato46439ce2010-11-19 13:56:21 -08002638 return this;
2639 }
2640
Joe Onoratocb109a02011-01-18 17:57:41 -08002641 /**
Dan Sandler5fcdf6e2014-07-18 11:31:15 -04002642 * Obsolete version of {@link #setTicker(CharSequence)}.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002643 *
Joe Onoratocb109a02011-01-18 17:57:41 -08002644 */
Dan Sandler5fcdf6e2014-07-18 11:31:15 -04002645 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08002646 public Builder setTicker(CharSequence tickerText, RemoteViews views) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002647 setTicker(tickerText);
2648 // views is ignored
Joe Onorato46439ce2010-11-19 13:56:21 -08002649 return this;
2650 }
2651
Joe Onoratocb109a02011-01-18 17:57:41 -08002652 /**
Dan Sandlerd63f9322015-05-06 15:18:49 -04002653 * Add a large icon to the notification content view.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002654 *
2655 * In the platform template, this image will be shown on the left of the notification view
Dan Sandlerd63f9322015-05-06 15:18:49 -04002656 * in place of the {@link #setSmallIcon(Icon) small icon} (which will be placed in a small
2657 * badge atop the large icon).
Dan Sandler08a04c12015-05-06 15:18:49 -04002658 */
Dan Sandlerd63f9322015-05-06 15:18:49 -04002659 public Builder setLargeIcon(Bitmap b) {
2660 return setLargeIcon(b != null ? Icon.createWithBitmap(b) : null);
2661 }
2662
2663 /**
2664 * Add a large icon to the notification content view.
2665 *
2666 * In the platform template, this image will be shown on the left of the notification view
2667 * in place of the {@link #setSmallIcon(Icon) small icon} (which will be placed in a small
2668 * badge atop the large icon).
2669 */
2670 public Builder setLargeIcon(Icon icon) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002671 mN.mLargeIcon = icon;
2672 mN.extras.putParcelable(EXTRA_LARGE_ICON, icon);
Joe Onorato46439ce2010-11-19 13:56:21 -08002673 return this;
2674 }
2675
Joe Onoratocb109a02011-01-18 17:57:41 -08002676 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002677 * Set the sound to play.
2678 *
John Spurlockc0650f022014-07-19 13:22:39 -04002679 * It will be played using the {@link #AUDIO_ATTRIBUTES_DEFAULT default audio attributes}
2680 * for notifications.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002681 *
Chris Wren47c20a12014-06-18 17:27:29 -04002682 * <p>
2683 * A notification that is noisy is more likely to be presented as a heads-up notification.
2684 * </p>
2685 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002686 * @see Notification#sound
Joe Onoratocb109a02011-01-18 17:57:41 -08002687 */
Joe Onorato52f80cd2010-11-21 15:34:48 -08002688 public Builder setSound(Uri sound) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002689 mN.sound = sound;
2690 mN.audioAttributes = AUDIO_ATTRIBUTES_DEFAULT;
Joe Onorato52f80cd2010-11-21 15:34:48 -08002691 return this;
2692 }
2693
Joe Onoratocb109a02011-01-18 17:57:41 -08002694 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002695 * Set the sound to play, along with a specific stream on which to play it.
Joe Onoratocb109a02011-01-18 17:57:41 -08002696 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002697 * See {@link android.media.AudioManager} for the <code>STREAM_</code> constants.
2698 *
Chris Wren47c20a12014-06-18 17:27:29 -04002699 * <p>
2700 * A notification that is noisy is more likely to be presented as a heads-up notification.
2701 * </p>
John Spurlockc0650f022014-07-19 13:22:39 -04002702 * @deprecated use {@link #setSound(Uri, AudioAttributes)} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002703 * @see Notification#sound
Joe Onoratocb109a02011-01-18 17:57:41 -08002704 */
Jean-Michel Trivi81f871e2014-08-06 16:32:38 -07002705 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08002706 public Builder setSound(Uri sound, int streamType) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002707 mN.sound = sound;
2708 mN.audioStreamType = streamType;
Joe Onorato46439ce2010-11-19 13:56:21 -08002709 return this;
2710 }
2711
Joe Onoratocb109a02011-01-18 17:57:41 -08002712 /**
John Spurlockc0650f022014-07-19 13:22:39 -04002713 * Set the sound to play, along with specific {@link AudioAttributes audio attributes} to
2714 * use during playback.
2715 *
2716 * <p>
2717 * A notification that is noisy is more likely to be presented as a heads-up notification.
2718 * </p>
2719 *
2720 * @see Notification#sound
2721 */
2722 public Builder setSound(Uri sound, AudioAttributes audioAttributes) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002723 mN.sound = sound;
2724 mN.audioAttributes = audioAttributes;
John Spurlockc0650f022014-07-19 13:22:39 -04002725 return this;
2726 }
2727
2728 /**
Joe Onoratocb109a02011-01-18 17:57:41 -08002729 * Set the vibration pattern to use.
2730 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002731 * See {@link android.os.Vibrator#vibrate(long[], int)} for a discussion of the
2732 * <code>pattern</code> parameter.
2733 *
Chris Wren47c20a12014-06-18 17:27:29 -04002734 * <p>
2735 * A notification that vibrates is more likely to be presented as a heads-up notification.
2736 * </p>
2737 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002738 * @see Notification#vibrate
Joe Onoratocb109a02011-01-18 17:57:41 -08002739 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002740 public Builder setVibrate(long[] pattern) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002741 mN.vibrate = pattern;
Joe Onorato46439ce2010-11-19 13:56:21 -08002742 return this;
2743 }
2744
Joe Onoratocb109a02011-01-18 17:57:41 -08002745 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002746 * Set the desired color for the indicator LED on the device, as well as the
2747 * blink duty cycle (specified in milliseconds).
2748 *
2749
2750 * Not all devices will honor all (or even any) of these values.
2751 *
2752
2753 * @see Notification#ledARGB
2754 * @see Notification#ledOnMS
2755 * @see Notification#ledOffMS
Joe Onoratocb109a02011-01-18 17:57:41 -08002756 */
Tor Norbye80756e32015-03-02 09:39:27 -08002757 public Builder setLights(@ColorInt int argb, int onMs, int offMs) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002758 mN.ledARGB = argb;
2759 mN.ledOnMS = onMs;
2760 mN.ledOffMS = offMs;
Julia Reynolds10ee1fc2015-11-09 11:04:55 -05002761 if (onMs != 0 || offMs != 0) {
2762 mN.flags |= FLAG_SHOW_LIGHTS;
2763 }
Joe Onorato46439ce2010-11-19 13:56:21 -08002764 return this;
2765 }
2766
Joe Onoratocb109a02011-01-18 17:57:41 -08002767 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002768 * Set whether this is an "ongoing" notification.
Joe Onoratocb109a02011-01-18 17:57:41 -08002769 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002770
2771 * Ongoing notifications cannot be dismissed by the user, so your application or service
2772 * must take care of canceling them.
2773 *
2774
2775 * They are typically used to indicate a background task that the user is actively engaged
2776 * with (e.g., playing music) or is pending in some way and therefore occupying the device
2777 * (e.g., a file download, sync operation, active network connection).
2778 *
2779
2780 * @see Notification#FLAG_ONGOING_EVENT
2781 * @see Service#setForeground(boolean)
Joe Onoratocb109a02011-01-18 17:57:41 -08002782 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002783 public Builder setOngoing(boolean ongoing) {
2784 setFlag(FLAG_ONGOING_EVENT, ongoing);
2785 return this;
2786 }
2787
Joe Onoratocb109a02011-01-18 17:57:41 -08002788 /**
2789 * Set this flag if you would only like the sound, vibrate
2790 * and ticker to be played if the notification is not already showing.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002791 *
2792 * @see Notification#FLAG_ONLY_ALERT_ONCE
Joe Onoratocb109a02011-01-18 17:57:41 -08002793 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002794 public Builder setOnlyAlertOnce(boolean onlyAlertOnce) {
2795 setFlag(FLAG_ONLY_ALERT_ONCE, onlyAlertOnce);
2796 return this;
2797 }
2798
Joe Onoratocb109a02011-01-18 17:57:41 -08002799 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002800 * Make this notification automatically dismissed when the user touches it. The
2801 * PendingIntent set with {@link #setDeleteIntent} will be sent when this happens.
2802 *
2803 * @see Notification#FLAG_AUTO_CANCEL
Joe Onoratocb109a02011-01-18 17:57:41 -08002804 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002805 public Builder setAutoCancel(boolean autoCancel) {
Joe Onorato281d83f2011-01-04 17:13:10 -08002806 setFlag(FLAG_AUTO_CANCEL, autoCancel);
Joe Onorato46439ce2010-11-19 13:56:21 -08002807 return this;
2808 }
2809
Joe Onoratocb109a02011-01-18 17:57:41 -08002810 /**
Griff Hazendfcb0802014-02-11 12:00:00 -08002811 * Set whether or not this notification should not bridge to other devices.
2812 *
2813 * <p>Some notifications can be bridged to other devices for remote display.
2814 * This hint can be set to recommend this notification not be bridged.
2815 */
2816 public Builder setLocalOnly(boolean localOnly) {
2817 setFlag(FLAG_LOCAL_ONLY, localOnly);
2818 return this;
2819 }
2820
2821 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002822 * Set which notification properties will be inherited from system defaults.
Joe Onoratocb109a02011-01-18 17:57:41 -08002823 * <p>
2824 * The value should be one or more of the following fields combined with
2825 * bitwise-or:
2826 * {@link #DEFAULT_SOUND}, {@link #DEFAULT_VIBRATE}, {@link #DEFAULT_LIGHTS}.
2827 * <p>
2828 * For all default values, use {@link #DEFAULT_ALL}.
2829 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002830 public Builder setDefaults(int defaults) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002831 mN.defaults = defaults;
Joe Onorato46439ce2010-11-19 13:56:21 -08002832 return this;
2833 }
2834
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002835 /**
2836 * Set the priority of this notification.
2837 *
2838 * @see Notification#priority
2839 */
Tor Norbyed9273d62013-05-30 15:59:53 -07002840 public Builder setPriority(@Priority int pri) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002841 mN.priority = pri;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002842 return this;
2843 }
Joe Malin8d40d042012-11-05 11:36:40 -08002844
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002845 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -04002846 * Set the notification category.
Joe Malin8d40d042012-11-05 11:36:40 -08002847 *
John Spurlockfd7f1e02014-03-18 16:41:57 -04002848 * @see Notification#category
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002849 */
John Spurlockfd7f1e02014-03-18 16:41:57 -04002850 public Builder setCategory(String category) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002851 mN.category = category;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002852 return this;
2853 }
2854
2855 /**
Chris Wrendde75302014-03-26 17:24:15 -04002856 * Add a person that is relevant to this notification.
2857 *
Chris Wrene6c48932014-09-29 17:19:27 -04002858 * <P>
2859 * Depending on user preferences, this annotation may allow the notification to pass
2860 * through interruption filters, and to appear more prominently in the user interface.
2861 * </P>
2862 *
2863 * <P>
2864 * The person should be specified by the {@code String} representation of a
2865 * {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI}.
2866 * </P>
2867 *
2868 * <P>The system will also attempt to resolve {@code mailto:} and {@code tel:} schema
2869 * URIs. The path part of these URIs must exist in the contacts database, in the
2870 * appropriate column, or the reference will be discarded as invalid. Telephone schema
2871 * URIs will be resolved by {@link android.provider.ContactsContract.PhoneLookup}.
2872 * </P>
2873 *
2874 * @param uri A URI for the person.
Chris Wrendde75302014-03-26 17:24:15 -04002875 * @see Notification#EXTRA_PEOPLE
2876 */
Chris Wrene6c48932014-09-29 17:19:27 -04002877 public Builder addPerson(String uri) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002878 mPersonList.add(uri);
Chris Wrendde75302014-03-26 17:24:15 -04002879 return this;
2880 }
2881
2882 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002883 * Set this notification to be part of a group of notifications sharing the same key.
2884 * Grouped notifications may display in a cluster or stack on devices which
2885 * support such rendering.
2886 *
2887 * <p>To make this notification the summary for its group, also call
2888 * {@link #setGroupSummary}. A sort order can be specified for group members by using
2889 * {@link #setSortKey}.
2890 * @param groupKey The group key of the group.
2891 * @return this object for method chaining
2892 */
2893 public Builder setGroup(String groupKey) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002894 mN.mGroupKey = groupKey;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002895 return this;
2896 }
2897
2898 /**
2899 * Set this notification to be the group summary for a group of notifications.
2900 * Grouped notifications may display in a cluster or stack on devices which
2901 * support such rendering. Requires a group key also be set using {@link #setGroup}.
2902 * @param isGroupSummary Whether this notification should be a group summary.
2903 * @return this object for method chaining
2904 */
2905 public Builder setGroupSummary(boolean isGroupSummary) {
2906 setFlag(FLAG_GROUP_SUMMARY, isGroupSummary);
2907 return this;
2908 }
2909
2910 /**
2911 * Set a sort key that orders this notification among other notifications from the
2912 * same package. This can be useful if an external sort was already applied and an app
2913 * would like to preserve this. Notifications will be sorted lexicographically using this
2914 * value, although providing different priorities in addition to providing sort key may
2915 * cause this value to be ignored.
2916 *
2917 * <p>This sort key can also be used to order members of a notification group. See
Griff Hazen9e1379f2014-05-20 12:50:51 -07002918 * {@link #setGroup}.
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002919 *
2920 * @see String#compareTo(String)
2921 */
2922 public Builder setSortKey(String sortKey) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002923 mN.mSortKey = sortKey;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002924 return this;
2925 }
2926
2927 /**
Griff Hazen720042b2014-02-24 15:46:56 -08002928 * Merge additional metadata into this notification.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002929 *
Griff Hazen720042b2014-02-24 15:46:56 -08002930 * <p>Values within the Bundle will replace existing extras values in this Builder.
2931 *
2932 * @see Notification#extras
2933 */
Griff Hazen959591e2014-05-15 22:26:18 -07002934 public Builder addExtras(Bundle extras) {
2935 if (extras != null) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002936 mUserExtras.putAll(extras);
Griff Hazen720042b2014-02-24 15:46:56 -08002937 }
2938 return this;
2939 }
2940
2941 /**
2942 * Set metadata for this notification.
2943 *
2944 * <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 -04002945 * current contents are copied into the Notification each time {@link #build()} is
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002946 * called.
2947 *
Griff Hazen720042b2014-02-24 15:46:56 -08002948 * <p>Replaces any existing extras values with those from the provided Bundle.
2949 * Use {@link #addExtras} to merge in metadata instead.
2950 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002951 * @see Notification#extras
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002952 */
Griff Hazen959591e2014-05-15 22:26:18 -07002953 public Builder setExtras(Bundle extras) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002954 if (extras != null) {
2955 mUserExtras = extras;
2956 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002957 return this;
2958 }
2959
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002960 /**
Griff Hazen720042b2014-02-24 15:46:56 -08002961 * Get the current metadata Bundle used by this notification Builder.
2962 *
2963 * <p>The returned Bundle is shared with this Builder.
2964 *
2965 * <p>The current contents of this Bundle are copied into the Notification each time
2966 * {@link #build()} is called.
2967 *
2968 * @see Notification#extras
2969 */
2970 public Bundle getExtras() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002971 return mUserExtras;
2972 }
2973
2974 private Bundle getAllExtras() {
2975 final Bundle saveExtras = (Bundle) mUserExtras.clone();
2976 saveExtras.putAll(mN.extras);
2977 return saveExtras;
Griff Hazen720042b2014-02-24 15:46:56 -08002978 }
2979
2980 /**
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002981 * Add an action to this notification. Actions are typically displayed by
2982 * the system as a button adjacent to the notification content.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04002983 * <p>
2984 * Every action must have an icon (32dp square and matching the
2985 * <a href="{@docRoot}design/style/iconography.html#action-bar">Holo
2986 * Dark action bar</a> visual style), a textual label, and a {@link PendingIntent}.
2987 * <p>
2988 * A notification in its expanded form can display up to 3 actions, from left to right in
2989 * the order they were added. Actions will not be displayed when the notification is
2990 * collapsed, however, so be sure that any essential functions may be accessed by the user
2991 * in some other way (for example, in the Activity pointed to by {@link #contentIntent}).
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002992 *
2993 * @param icon Resource ID of a drawable that represents the action.
2994 * @param title Text describing the action.
2995 * @param intent PendingIntent to be fired when the action is invoked.
Dan Sandler86647982015-05-13 23:41:13 -04002996 *
2997 * @deprecated Use {@link #addAction(Action)} instead.
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002998 */
Dan Sandler86647982015-05-13 23:41:13 -04002999 @Deprecated
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003000 public Builder addAction(int icon, CharSequence title, PendingIntent intent) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04003001 mActions.add(new Action(icon, safeCharSequence(title), intent));
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003002 return this;
3003 }
3004
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003005 /**
Griff Hazen959591e2014-05-15 22:26:18 -07003006 * Add an action to this notification. Actions are typically displayed by
3007 * the system as a button adjacent to the notification content.
3008 * <p>
3009 * Every action must have an icon (32dp square and matching the
3010 * <a href="{@docRoot}design/style/iconography.html#action-bar">Holo
3011 * Dark action bar</a> visual style), a textual label, and a {@link PendingIntent}.
3012 * <p>
3013 * A notification in its expanded form can display up to 3 actions, from left to right in
3014 * the order they were added. Actions will not be displayed when the notification is
3015 * collapsed, however, so be sure that any essential functions may be accessed by the user
3016 * in some other way (for example, in the Activity pointed to by {@link #contentIntent}).
3017 *
3018 * @param action The action to add.
3019 */
3020 public Builder addAction(Action action) {
3021 mActions.add(action);
3022 return this;
3023 }
3024
3025 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003026 * Alter the complete list of actions attached to this notification.
3027 * @see #addAction(Action).
3028 *
3029 * @param actions
3030 * @return
3031 */
3032 public Builder setActions(Action... actions) {
3033 mActions.clear();
3034 for (int i = 0; i < actions.length; i++) {
3035 mActions.add(actions[i]);
3036 }
3037 return this;
3038 }
3039
3040 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003041 * Add a rich notification style to be applied at build time.
3042 *
3043 * @param style Object responsible for modifying the notification style.
3044 */
3045 public Builder setStyle(Style style) {
3046 if (mStyle != style) {
3047 mStyle = style;
Daniel Sandlerc08dea22012-06-28 08:35:24 -07003048 if (mStyle != null) {
3049 mStyle.setBuilder(this);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003050 mN.extras.putString(EXTRA_TEMPLATE, style.getClass().getName());
3051 } else {
3052 mN.extras.remove(EXTRA_TEMPLATE);
Daniel Sandlerc08dea22012-06-28 08:35:24 -07003053 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003054 }
3055 return this;
3056 }
3057
Dan Sandler0bf2ed82013-12-21 23:33:41 -06003058 /**
3059 * Specify the value of {@link #visibility}.
Griff Hazenb720abe2014-05-20 13:15:30 -07003060 *
Dan Sandler0bf2ed82013-12-21 23:33:41 -06003061 * @param visibility One of {@link #VISIBILITY_PRIVATE} (the default),
3062 * {@link #VISIBILITY_SECRET}, or {@link #VISIBILITY_PUBLIC}.
3063 *
3064 * @return The same Builder.
3065 */
3066 public Builder setVisibility(int visibility) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003067 mN.visibility = visibility;
Dan Sandler0bf2ed82013-12-21 23:33:41 -06003068 return this;
3069 }
3070
3071 /**
3072 * Supply a replacement Notification whose contents should be shown in insecure contexts
3073 * (i.e. atop the secure lockscreen). See {@link #visibility} and {@link #VISIBILITY_PUBLIC}.
3074 * @param n A replacement notification, presumably with some or all info redacted.
3075 * @return The same Builder.
3076 */
3077 public Builder setPublicVersion(Notification n) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003078 if (n != null) {
3079 mN.publicVersion = new Notification();
3080 n.cloneInto(mN.publicVersion, /*heavy=*/ true);
3081 } else {
3082 mN.publicVersion = null;
3083 }
Dan Sandler0bf2ed82013-12-21 23:33:41 -06003084 return this;
3085 }
3086
Griff Hazenb720abe2014-05-20 13:15:30 -07003087 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003088 * Apply an extender to this notification builder. Extenders may be used to add
3089 * metadata or change options on this builder.
3090 */
Griff Hazen61a9e862014-05-22 16:05:19 -07003091 public Builder extend(Extender extender) {
3092 extender.extend(this);
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003093 return this;
3094 }
3095
Dan Sandler4e787062015-06-17 15:09:48 -04003096 /**
3097 * @hide
3098 */
Julia Reynoldse46bb372016-03-17 11:05:58 -04003099 public Builder setFlag(int mask, boolean value) {
Joe Onorato46439ce2010-11-19 13:56:21 -08003100 if (value) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003101 mN.flags |= mask;
Joe Onorato46439ce2010-11-19 13:56:21 -08003102 } else {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003103 mN.flags &= ~mask;
Joe Onorato46439ce2010-11-19 13:56:21 -08003104 }
Julia Reynoldse46bb372016-03-17 11:05:58 -04003105 return this;
Joe Onorato46439ce2010-11-19 13:56:21 -08003106 }
3107
Dan Sandler26e81cf2014-05-06 10:01:27 -04003108 /**
3109 * Sets {@link Notification#color}.
3110 *
3111 * @param argb The accent color to use
3112 *
3113 * @return The same Builder.
3114 */
Tor Norbye80756e32015-03-02 09:39:27 -08003115 public Builder setColor(@ColorInt int argb) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003116 mN.color = argb;
Julia Reynolds10ee1fc2015-11-09 11:04:55 -05003117 sanitizeColor();
Dan Sandler26e81cf2014-05-06 10:01:27 -04003118 return this;
3119 }
3120
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003121 private Drawable getProfileBadgeDrawable() {
Chris Wren66619a22016-05-12 16:42:37 -04003122 if (mContext.getUserId() == UserHandle.USER_SYSTEM) {
3123 // This user can never be a badged profile,
3124 // and also includes USER_ALL system notifications.
3125 return null;
3126 }
Christoph Studer7ac80e62014-08-04 16:01:57 +02003127 // Note: This assumes that the current user can read the profile badge of the
3128 // originating user.
Selim Cineke6ff9462016-01-15 15:07:06 -08003129 return mContext.getPackageManager().getUserBadgeForDensityNoBackground(
Julia Reynoldsda303542015-11-23 14:00:20 -05003130 new UserHandle(mContext.getUserId()), 0);
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003131 }
3132
3133 private Bitmap getProfileBadge() {
3134 Drawable badge = getProfileBadgeDrawable();
Kenny Guy8a0101b2014-05-08 23:34:12 +01003135 if (badge == null) {
3136 return null;
3137 }
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003138 final int size = mContext.getResources().getDimensionPixelSize(
3139 R.dimen.notification_badge_size);
3140 Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
Kenny Guy8a0101b2014-05-08 23:34:12 +01003141 Canvas canvas = new Canvas(bitmap);
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003142 badge.setBounds(0, 0, size, size);
Kenny Guy8a0101b2014-05-08 23:34:12 +01003143 badge.draw(canvas);
3144 return bitmap;
3145 }
3146
Selim Cinekc848c3a2016-01-13 15:27:30 -08003147 private void bindProfileBadge(RemoteViews contentView) {
Kenny Guy98193ea2014-07-24 19:54:37 +01003148 Bitmap profileBadge = getProfileBadge();
3149
Kenny Guy98193ea2014-07-24 19:54:37 +01003150 if (profileBadge != null) {
Selim Cinekc848c3a2016-01-13 15:27:30 -08003151 contentView.setImageViewBitmap(R.id.profile_badge, profileBadge);
3152 contentView.setViewVisibility(R.id.profile_badge, View.VISIBLE);
Kenny Guy98193ea2014-07-24 19:54:37 +01003153 }
Kenny Guy98193ea2014-07-24 19:54:37 +01003154 }
3155
Christoph Studerfe718432014-09-01 18:21:18 +02003156 private void resetStandardTemplate(RemoteViews contentView) {
Selim Cinekeaa29ca2015-11-23 13:51:13 -08003157 resetNotificationHeader(contentView);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003158 resetContentMargins(contentView);
Christoph Studerfe718432014-09-01 18:21:18 +02003159 contentView.setViewVisibility(R.id.right_icon, View.GONE);
Selim Cinek860b6da2015-12-16 19:02:19 -08003160 contentView.setViewVisibility(R.id.title, View.GONE);
Christoph Studerfe718432014-09-01 18:21:18 +02003161 contentView.setTextViewText(R.id.title, null);
Selim Cinek41598732016-01-11 16:58:37 -08003162 contentView.setViewVisibility(R.id.text, View.GONE);
Christoph Studerfe718432014-09-01 18:21:18 +02003163 contentView.setTextViewText(R.id.text, null);
Selim Cinek29603462015-11-17 19:04:39 -08003164 contentView.setViewVisibility(R.id.text_line_1, View.GONE);
Selim Cinek41598732016-01-11 16:58:37 -08003165 contentView.setTextViewText(R.id.text_line_1, null);
Christoph Studerfe718432014-09-01 18:21:18 +02003166 contentView.setViewVisibility(R.id.progress, View.GONE);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003167 }
3168
Selim Cinekeaa29ca2015-11-23 13:51:13 -08003169 /**
3170 * Resets the notification header to its original state
3171 */
3172 private void resetNotificationHeader(RemoteViews contentView) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003173 contentView.setImageViewResource(R.id.icon, 0);
Selim Cinek7b836392015-12-04 20:02:59 -08003174 contentView.setBoolean(R.id.notification_header, "setExpanded", false);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003175 contentView.setTextViewText(R.id.app_name_text, null);
Christoph Studerca1db712014-09-10 17:31:33 +02003176 contentView.setViewVisibility(R.id.chronometer, View.GONE);
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07003177 contentView.setViewVisibility(R.id.header_text, View.GONE);
3178 contentView.setViewVisibility(R.id.header_text_divider, View.GONE);
Selim Cinek29603462015-11-17 19:04:39 -08003179 contentView.setViewVisibility(R.id.time_divider, View.GONE);
Selim Cinekb85f36fd2016-04-20 18:46:36 -07003180 contentView.setViewVisibility(R.id.time, View.GONE);
Selim Cinekc848c3a2016-01-13 15:27:30 -08003181 contentView.setImageViewIcon(R.id.profile_badge, null);
3182 contentView.setViewVisibility(R.id.profile_badge, View.GONE);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003183 }
3184
3185 private void resetContentMargins(RemoteViews contentView) {
3186 contentView.setViewLayoutMarginEnd(R.id.line1, 0);
Selim Cinekc848c3a2016-01-13 15:27:30 -08003187 contentView.setViewLayoutMarginEnd(R.id.text, 0);
Christoph Studerfe718432014-09-01 18:21:18 +02003188 }
3189
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003190 private RemoteViews applyStandardTemplate(int resId) {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02003191 return applyStandardTemplate(resId, true /* hasProgress */);
3192 }
3193
3194 /**
3195 * @param hasProgress whether the progress bar should be shown and set
3196 */
3197 private RemoteViews applyStandardTemplate(int resId, boolean hasProgress) {
Adrian Roosc1a80b02016-04-05 14:54:55 -07003198 final Bundle ex = mN.extras;
3199
3200 CharSequence title = processLegacyText(ex.getCharSequence(EXTRA_TITLE));
3201 CharSequence text = processLegacyText(ex.getCharSequence(EXTRA_TEXT));
3202 return applyStandardTemplate(resId, hasProgress, title, text);
3203 }
3204
3205 /**
3206 * @param hasProgress whether the progress bar should be shown and set
3207 */
3208 private RemoteViews applyStandardTemplate(int resId, boolean hasProgress,
3209 CharSequence title, CharSequence text) {
Kenny Guy77320062014-08-27 21:37:15 +01003210 RemoteViews contentView = new BuilderRemoteViews(mContext.getApplicationInfo(), resId);
Dan Sandler539aad42014-08-04 00:43:39 -04003211
Christoph Studerfe718432014-09-01 18:21:18 +02003212 resetStandardTemplate(contentView);
3213
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003214 final Bundle ex = mN.extras;
Dan Sandler190d58d2014-05-15 09:33:39 -04003215
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003216 bindNotificationHeader(contentView);
3217 bindLargeIcon(contentView);
Selim Cinek954cc232016-05-20 13:29:23 -07003218 boolean showProgress = handleProgressBar(hasProgress, contentView, ex);
Adrian Roosc1a80b02016-04-05 14:54:55 -07003219 if (title != null) {
Selim Cinek860b6da2015-12-16 19:02:19 -08003220 contentView.setViewVisibility(R.id.title, View.VISIBLE);
Adrian Roosc1a80b02016-04-05 14:54:55 -07003221 contentView.setTextViewText(R.id.title, title);
Selim Cinek954cc232016-05-20 13:29:23 -07003222 contentView.setViewLayoutWidth(R.id.title, showProgress
3223 ? ViewGroup.LayoutParams.WRAP_CONTENT
3224 : ViewGroup.LayoutParams.MATCH_PARENT);
Joe Onorato561d3852010-11-20 18:09:34 -08003225 }
Adrian Roosc1a80b02016-04-05 14:54:55 -07003226 if (text != null) {
Selim Cinek41598732016-01-11 16:58:37 -08003227 int textId = showProgress ? com.android.internal.R.id.text_line_1
3228 : com.android.internal.R.id.text;
Adrian Roosc1a80b02016-04-05 14:54:55 -07003229 contentView.setTextViewText(textId, text);
Selim Cinek41598732016-01-11 16:58:37 -08003230 contentView.setViewVisibility(textId, View.VISIBLE);
Joe Onorato561d3852010-11-20 18:09:34 -08003231 }
Selim Cinekc848c3a2016-01-13 15:27:30 -08003232
Selim Cinek860b6da2015-12-16 19:02:19 -08003233 setContentMinHeight(contentView, showProgress || mN.mLargeIcon != null);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003234
Selim Cinek29603462015-11-17 19:04:39 -08003235 return contentView;
3236 }
3237
Selim Cinek860b6da2015-12-16 19:02:19 -08003238 /**
3239 * @param remoteView the remote view to update the minheight in
3240 * @param hasMinHeight does it have a mimHeight
3241 * @hide
3242 */
3243 void setContentMinHeight(RemoteViews remoteView, boolean hasMinHeight) {
3244 int minHeight = 0;
3245 if (hasMinHeight) {
3246 // we need to set the minHeight of the notification
3247 minHeight = mContext.getResources().getDimensionPixelSize(
3248 com.android.internal.R.dimen.notification_min_content_height);
3249 }
3250 remoteView.setInt(R.id.notification_main_column, "setMinimumHeight", minHeight);
3251 }
3252
Selim Cinek29603462015-11-17 19:04:39 -08003253 private boolean handleProgressBar(boolean hasProgress, RemoteViews contentView, Bundle ex) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003254 final int max = ex.getInt(EXTRA_PROGRESS_MAX, 0);
3255 final int progress = ex.getInt(EXTRA_PROGRESS, 0);
3256 final boolean ind = ex.getBoolean(EXTRA_PROGRESS_INDETERMINATE);
3257 if (hasProgress && (max != 0 || ind)) {
Selim Cinek29603462015-11-17 19:04:39 -08003258 contentView.setViewVisibility(com.android.internal.R.id.progress, View.VISIBLE);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003259 contentView.setProgressBar(
Selim Cinek29603462015-11-17 19:04:39 -08003260 R.id.progress, max, progress, ind);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003261 contentView.setProgressBackgroundTintList(
3262 R.id.progress, ColorStateList.valueOf(mContext.getColor(
3263 R.color.notification_progress_background_color)));
Selim Cinek29603462015-11-17 19:04:39 -08003264 if (mN.color != COLOR_DEFAULT) {
Adrian Roos4ff3b122016-02-01 12:26:13 -08003265 ColorStateList colorStateList = ColorStateList.valueOf(resolveContrastColor());
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003266 contentView.setProgressTintList(R.id.progress, colorStateList);
3267 contentView.setProgressIndeterminateTintList(R.id.progress, colorStateList);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003268 }
Selim Cinek29603462015-11-17 19:04:39 -08003269 return true;
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003270 } else {
3271 contentView.setViewVisibility(R.id.progress, View.GONE);
Selim Cinek29603462015-11-17 19:04:39 -08003272 return false;
Jeff Sharkey1c400132011-08-05 14:50:13 -07003273 }
Joe Onorato561d3852010-11-20 18:09:34 -08003274 }
3275
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003276 private void bindLargeIcon(RemoteViews contentView) {
3277 if (mN.mLargeIcon != null) {
3278 contentView.setViewVisibility(R.id.right_icon, View.VISIBLE);
3279 contentView.setImageViewIcon(R.id.right_icon, mN.mLargeIcon);
3280 processLargeLegacyIcon(mN.mLargeIcon, contentView);
3281 int endMargin = mContext.getResources().getDimensionPixelSize(
3282 R.dimen.notification_content_picture_margin);
3283 contentView.setViewLayoutMarginEnd(R.id.line1, endMargin);
Selim Cinekc848c3a2016-01-13 15:27:30 -08003284 contentView.setViewLayoutMarginEnd(R.id.text, endMargin);
Selim Cinek29603462015-11-17 19:04:39 -08003285 contentView.setViewLayoutMarginEnd(R.id.progress, endMargin);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003286 }
3287 }
3288
3289 private void bindNotificationHeader(RemoteViews contentView) {
3290 bindSmallIcon(contentView);
3291 bindHeaderAppName(contentView);
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07003292 bindHeaderText(contentView);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003293 bindHeaderChronometerAndTime(contentView);
3294 bindExpandButton(contentView);
Selim Cinekc848c3a2016-01-13 15:27:30 -08003295 bindProfileBadge(contentView);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003296 }
3297
3298 private void bindExpandButton(RemoteViews contentView) {
Adrian Roos4ff3b122016-02-01 12:26:13 -08003299 contentView.setDrawableParameters(R.id.expand_button, false, -1, resolveContrastColor(),
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003300 PorterDuff.Mode.SRC_ATOP, -1);
Selim Cinekea4bef72015-12-02 15:51:10 -08003301 contentView.setInt(R.id.notification_header, "setOriginalNotificationColor",
Adrian Roos4ff3b122016-02-01 12:26:13 -08003302 resolveContrastColor());
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003303 }
3304
3305 private void bindHeaderChronometerAndTime(RemoteViews contentView) {
3306 if (showsTimeOrChronometer()) {
Selim Cinek29603462015-11-17 19:04:39 -08003307 contentView.setViewVisibility(R.id.time_divider, View.VISIBLE);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003308 if (mN.extras.getBoolean(EXTRA_SHOW_CHRONOMETER)) {
3309 contentView.setViewVisibility(R.id.chronometer, View.VISIBLE);
3310 contentView.setLong(R.id.chronometer, "setBase",
3311 mN.when + (SystemClock.elapsedRealtime() - System.currentTimeMillis()));
3312 contentView.setBoolean(R.id.chronometer, "setStarted", true);
Adrian Roos96b7e202016-05-17 13:50:38 -07003313 boolean countsDown = mN.extras.getBoolean(EXTRA_CHRONOMETER_COUNT_DOWN);
Selim Cinekc3b752e2016-04-20 16:13:59 -07003314 contentView.setChronometerCountDown(R.id.chronometer, countsDown);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003315 } else {
3316 contentView.setViewVisibility(R.id.time, View.VISIBLE);
3317 contentView.setLong(R.id.time, "setTime", mN.when);
3318 }
Selim Cinekb85f36fd2016-04-20 18:46:36 -07003319 } else {
3320 // We still want a time to be set but gone, such that we can show and hide it
3321 // on demand in case it's a child notification without anything in the header
3322 contentView.setLong(R.id.time, "setTime", mN.when != 0 ? mN.when : mN.creationTime);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003323 }
3324 }
3325
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07003326 private void bindHeaderText(RemoteViews contentView) {
3327 CharSequence headerText = mN.extras.getCharSequence(EXTRA_SUB_TEXT);
3328 if (headerText == null && mStyle != null && mStyle.mSummaryTextSet
Selim Cinek03d0d652015-11-13 13:18:09 -05003329 && mStyle.hasSummaryInHeader()) {
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07003330 headerText = mStyle.mSummaryText;
Selim Cinek03d0d652015-11-13 13:18:09 -05003331 }
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07003332 if (headerText == null
3333 && mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N
3334 && mN.extras.getCharSequence(EXTRA_INFO_TEXT) != null) {
3335 headerText = mN.extras.getCharSequence(EXTRA_INFO_TEXT);
3336 }
3337 if (headerText != null) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003338 // TODO: Remove the span entirely to only have the string with propper formating.
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07003339 contentView.setTextViewText(R.id.header_text, processLegacyText(headerText));
3340 contentView.setViewVisibility(R.id.header_text, View.VISIBLE);
3341 contentView.setViewVisibility(R.id.header_text_divider, View.VISIBLE);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003342 }
3343 }
3344
Adrian Rooseba05822016-04-22 17:09:27 -07003345 /**
3346 * @hide
3347 */
3348 public String loadHeaderAppName() {
Dan Sandler732bd6c2016-04-12 14:20:32 -04003349 CharSequence name = null;
3350 final PackageManager pm = mContext.getPackageManager();
3351 if (mN.extras.containsKey(EXTRA_SUBSTITUTE_APP_NAME)) {
3352 // only system packages which lump together a bunch of unrelated stuff
3353 // may substitute a different name to make the purpose of the
3354 // notification more clear. the correct package label should always
3355 // be accessible via SystemUI.
3356 final String pkg = mContext.getPackageName();
3357 final String subName = mN.extras.getString(EXTRA_SUBSTITUTE_APP_NAME);
3358 if (PackageManager.PERMISSION_GRANTED == pm.checkPermission(
3359 android.Manifest.permission.SUBSTITUTE_NOTIFICATION_APP_NAME, pkg)) {
3360 name = subName;
3361 } else {
3362 Log.w(TAG, "warning: pkg "
3363 + pkg + " attempting to substitute app name '" + subName
3364 + "' without holding perm "
3365 + android.Manifest.permission.SUBSTITUTE_NOTIFICATION_APP_NAME);
3366 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003367 }
Dan Sandler732bd6c2016-04-12 14:20:32 -04003368 if (TextUtils.isEmpty(name)) {
3369 name = pm.getApplicationLabel(mContext.getApplicationInfo());
3370 }
3371 if (TextUtils.isEmpty(name)) {
3372 // still nothing?
3373 return null;
3374 }
3375
3376 return String.valueOf(name);
3377 }
3378 private void bindHeaderAppName(RemoteViews contentView) {
3379 contentView.setTextViewText(R.id.app_name_text, loadHeaderAppName());
Adrian Roos4ff3b122016-02-01 12:26:13 -08003380 contentView.setTextColor(R.id.app_name_text, resolveContrastColor());
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003381 }
3382
3383 private void bindSmallIcon(RemoteViews contentView) {
3384 contentView.setImageViewIcon(R.id.icon, mN.mSmallIcon);
3385 processSmallIconColor(mN.mSmallIcon, contentView);
3386 }
3387
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003388 /**
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02003389 * @return true if the built notification will show the time or the chronometer; false
3390 * otherwise
3391 */
3392 private boolean showsTimeOrChronometer() {
Selim Cinekc2c0b042016-05-18 17:13:46 -07003393 return mN.showsTime() || mN.showsChronometer();
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02003394 }
3395
Christoph Studerfe718432014-09-01 18:21:18 +02003396 private void resetStandardTemplateWithActions(RemoteViews big) {
Adrian Roos4c1fcc82016-03-31 14:39:39 -07003397 // actions_container is only reset when there are no actions to avoid focus issues with
3398 // remote inputs.
Christoph Studerfe718432014-09-01 18:21:18 +02003399 big.setViewVisibility(R.id.actions, View.GONE);
Christoph Studerfe718432014-09-01 18:21:18 +02003400 big.removeAllViews(R.id.actions);
Adrian Roose458aa82015-12-08 16:17:19 -08003401
3402 big.setViewVisibility(R.id.notification_material_reply_container, View.GONE);
3403 big.setTextViewText(R.id.notification_material_reply_text_1, null);
3404
3405 big.setViewVisibility(R.id.notification_material_reply_text_2, View.GONE);
3406 big.setTextViewText(R.id.notification_material_reply_text_2, null);
3407 big.setViewVisibility(R.id.notification_material_reply_text_3, View.GONE);
3408 big.setTextViewText(R.id.notification_material_reply_text_3, null);
Christoph Studerfe718432014-09-01 18:21:18 +02003409 }
3410
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003411 private RemoteViews applyStandardTemplateWithActions(int layoutId) {
Adrian Roos48d746a2016-04-12 14:57:28 -07003412 final Bundle ex = mN.extras;
3413
3414 CharSequence title = processLegacyText(ex.getCharSequence(EXTRA_TITLE));
3415 CharSequence text = processLegacyText(ex.getCharSequence(EXTRA_TEXT));
3416 return applyStandardTemplateWithActions(layoutId, true /* hasProgress */, title, text);
3417 }
3418
3419 private RemoteViews applyStandardTemplateWithActions(int layoutId, boolean hasProgress,
3420 CharSequence title, CharSequence text) {
3421 RemoteViews big = applyStandardTemplate(layoutId, hasProgress, title, text);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003422
Christoph Studerfe718432014-09-01 18:21:18 +02003423 resetStandardTemplateWithActions(big);
3424
Adrian Roose458aa82015-12-08 16:17:19 -08003425 boolean validRemoteInput = false;
3426
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003427 int N = mActions.size();
3428 if (N > 0) {
Adrian Roos7052de52016-03-03 15:53:34 -08003429 big.setViewVisibility(R.id.actions_container, View.VISIBLE);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003430 big.setViewVisibility(R.id.actions, View.VISIBLE);
Daniel Sandler8680bf82012-05-15 16:52:52 -04003431 if (N>MAX_ACTION_BUTTONS) N=MAX_ACTION_BUTTONS;
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003432 for (int i=0; i<N; i++) {
Adrian Roose458aa82015-12-08 16:17:19 -08003433 Action action = mActions.get(i);
3434 validRemoteInput |= hasValidRemoteInput(action);
3435
3436 final RemoteViews button = generateActionButton(action);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003437 big.addView(R.id.actions, button);
3438 }
Adrian Roos4c1fcc82016-03-31 14:39:39 -07003439 } else {
3440 big.setViewVisibility(R.id.actions_container, View.GONE);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003441 }
Adrian Roose458aa82015-12-08 16:17:19 -08003442
3443 CharSequence[] replyText = mN.extras.getCharSequenceArray(EXTRA_REMOTE_INPUT_HISTORY);
3444 if (validRemoteInput && replyText != null
3445 && replyText.length > 0 && !TextUtils.isEmpty(replyText[0])) {
3446 big.setViewVisibility(R.id.notification_material_reply_container, View.VISIBLE);
3447 big.setTextViewText(R.id.notification_material_reply_text_1, replyText[0]);
3448
3449 if (replyText.length > 1 && !TextUtils.isEmpty(replyText[1])) {
3450 big.setViewVisibility(R.id.notification_material_reply_text_2, View.VISIBLE);
3451 big.setTextViewText(R.id.notification_material_reply_text_2, replyText[1]);
3452
3453 if (replyText.length > 2 && !TextUtils.isEmpty(replyText[2])) {
3454 big.setViewVisibility(
3455 R.id.notification_material_reply_text_3, View.VISIBLE);
3456 big.setTextViewText(R.id.notification_material_reply_text_3, replyText[2]);
3457 }
3458 }
3459 }
3460
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003461 return big;
3462 }
3463
Adrian Roose458aa82015-12-08 16:17:19 -08003464 private boolean hasValidRemoteInput(Action action) {
3465 if (TextUtils.isEmpty(action.title) || action.actionIntent == null) {
3466 // Weird actions
3467 return false;
3468 }
3469
3470 RemoteInput[] remoteInputs = action.getRemoteInputs();
3471 if (remoteInputs == null) {
3472 return false;
3473 }
3474
3475 for (RemoteInput r : remoteInputs) {
3476 CharSequence[] choices = r.getChoices();
3477 if (r.getAllowFreeFormInput() || (choices != null && choices.length != 0)) {
3478 return true;
3479 }
3480 }
3481 return false;
3482 }
3483
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003484 /**
3485 * Construct a RemoteViews for the final 1U notification layout. In order:
3486 * 1. Custom contentView from the caller
3487 * 2. Style's proposed content view
3488 * 3. Standard template view
3489 */
Julia Reynolds3b848122016-02-26 10:45:32 -05003490 public RemoteViews createContentView() {
Selim Cinek593610c2016-02-16 18:42:57 -08003491 if (mN.contentView != null && (mStyle == null || !mStyle.displayCustomViewInline())) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003492 return mN.contentView;
3493 } else if (mStyle != null) {
3494 final RemoteViews styleView = mStyle.makeContentView();
3495 if (styleView != null) {
3496 return styleView;
3497 }
Joe Onorato46439ce2010-11-19 13:56:21 -08003498 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003499 return applyStandardTemplate(getBaseLayoutResource());
Joe Onorato46439ce2010-11-19 13:56:21 -08003500 }
3501
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003502 /**
3503 * Construct a RemoteViews for the final big notification layout.
3504 */
Julia Reynolds3b848122016-02-26 10:45:32 -05003505 public RemoteViews createBigContentView() {
Selim Cinek850a8542015-11-11 11:48:36 -05003506 RemoteViews result = null;
Selim Cinek593610c2016-02-16 18:42:57 -08003507 if (mN.bigContentView != null
3508 && (mStyle == null || !mStyle.displayCustomViewInline())) {
Julia Reynolds089e3e42015-11-18 09:59:57 -05003509 return mN.bigContentView;
3510 } else if (mStyle != null) {
Selim Cinek850a8542015-11-11 11:48:36 -05003511 result = mStyle.makeBigContentView();
Selim Cinek90dcf6d2015-11-18 20:24:13 -08003512 hideLine1Text(result);
Selim Cinekcc10bfb2016-02-10 16:24:21 -08003513 } else if (mActions.size() != 0) {
3514 result = applyStandardTemplateWithActions(getBigBaseLayoutResource());
Selim Cinek850a8542015-11-11 11:48:36 -05003515 }
3516 adaptNotificationHeaderForBigContentView(result);
3517 return result;
3518 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003519
Selim Cinekeaa29ca2015-11-23 13:51:13 -08003520 /**
3521 * Construct a RemoteViews for the final notification header only
3522 *
3523 * @hide
3524 */
3525 public RemoteViews makeNotificationHeader() {
3526 RemoteViews header = new BuilderRemoteViews(mContext.getApplicationInfo(),
3527 R.layout.notification_template_header);
3528 resetNotificationHeader(header);
3529 bindNotificationHeader(header);
3530 return header;
3531 }
3532
Selim Cinek29603462015-11-17 19:04:39 -08003533 private void hideLine1Text(RemoteViews result) {
Selim Cinekcc10bfb2016-02-10 16:24:21 -08003534 if (result != null) {
3535 result.setViewVisibility(R.id.text_line_1, View.GONE);
3536 }
Selim Cinek29603462015-11-17 19:04:39 -08003537 }
3538
Selim Cinek850a8542015-11-11 11:48:36 -05003539 private void adaptNotificationHeaderForBigContentView(RemoteViews result) {
Selim Cinekcc10bfb2016-02-10 16:24:21 -08003540 if (result != null) {
3541 result.setBoolean(R.id.notification_header, "setExpanded", true);
3542 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003543 }
3544
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003545 /**
3546 * Construct a RemoteViews for the final heads-up notification layout.
3547 */
Julia Reynolds3b848122016-02-26 10:45:32 -05003548 public RemoteViews createHeadsUpContentView() {
Selim Cinek593610c2016-02-16 18:42:57 -08003549 if (mN.headsUpContentView != null
3550 && (mStyle == null || !mStyle.displayCustomViewInline())) {
Julia Reynolds089e3e42015-11-18 09:59:57 -05003551 return mN.headsUpContentView;
3552 } else if (mStyle != null) {
3553 final RemoteViews styleView = mStyle.makeHeadsUpContentView();
3554 if (styleView != null) {
3555 return styleView;
3556 }
3557 } else if (mActions.size() == 0) {
3558 return null;
3559 }
3560
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003561 return applyStandardTemplateWithActions(getBigBaseLayoutResource());
Chris Wren8fd39ec2014-02-27 17:43:26 -05003562 }
3563
Selim Cinek624c02db2015-12-14 21:00:02 -08003564 /**
3565 * Construct a RemoteViews for the display in public contexts like on the lockscreen.
3566 *
3567 * @hide
3568 */
3569 public RemoteViews makePublicContentView() {
3570 if (mN.publicVersion != null) {
3571 final Builder builder = recoverBuilder(mContext, mN.publicVersion);
Julia Reynolds3b848122016-02-26 10:45:32 -05003572 return builder.createContentView();
Selim Cinek624c02db2015-12-14 21:00:02 -08003573 }
3574 Bundle savedBundle = mN.extras;
3575 Style style = mStyle;
3576 mStyle = null;
3577 Icon largeIcon = mN.mLargeIcon;
3578 mN.mLargeIcon = null;
3579 Bundle publicExtras = new Bundle();
3580 publicExtras.putBoolean(EXTRA_SHOW_WHEN,
3581 savedBundle.getBoolean(EXTRA_SHOW_WHEN));
3582 publicExtras.putBoolean(EXTRA_SHOW_CHRONOMETER,
3583 savedBundle.getBoolean(EXTRA_SHOW_CHRONOMETER));
Adrian Roos96b7e202016-05-17 13:50:38 -07003584 publicExtras.putBoolean(EXTRA_CHRONOMETER_COUNT_DOWN,
3585 savedBundle.getBoolean(EXTRA_CHRONOMETER_COUNT_DOWN));
Selim Cinek624c02db2015-12-14 21:00:02 -08003586 publicExtras.putCharSequence(EXTRA_TITLE,
3587 mContext.getString(R.string.notification_hidden_text));
3588 mN.extras = publicExtras;
3589 final RemoteViews publicView = applyStandardTemplate(getBaseLayoutResource());
3590 mN.extras = savedBundle;
3591 mN.mLargeIcon = largeIcon;
3592 mStyle = style;
3593 return publicView;
3594 }
3595
3596
Chris Wren8fd39ec2014-02-27 17:43:26 -05003597
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003598 private RemoteViews generateActionButton(Action action) {
Daniel Sandler8680bf82012-05-15 16:52:52 -04003599 final boolean tombstone = (action.actionIntent == null);
Selim Cinekf33b1112015-07-15 17:45:11 -07003600 RemoteViews button = new BuilderRemoteViews(mContext.getApplicationInfo(),
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003601 tombstone ? getActionTombstoneLayoutResource()
3602 : getActionLayoutResource());
Dan Sandler68079d52015-07-22 10:45:30 -04003603 final Icon ai = action.getIcon();
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003604 button.setTextViewText(R.id.action0, processLegacyText(action.title));
Daniel Sandler8680bf82012-05-15 16:52:52 -04003605 if (!tombstone) {
Daniel Sandlere5518842012-05-10 16:20:40 -04003606 button.setOnClickPendingIntent(R.id.action0, action.actionIntent);
Daniel Sandlere5518842012-05-10 16:20:40 -04003607 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003608 button.setContentDescription(R.id.action0, action.title);
Adrian Roosfe84e1f2015-11-04 15:55:39 -08003609 if (action.mRemoteInputs != null) {
3610 button.setRemoteInputs(R.id.action0, action.mRemoteInputs);
3611 }
3612 if (mN.color != COLOR_DEFAULT) {
Adrian Roos4ff3b122016-02-01 12:26:13 -08003613 button.setTextColor(R.id.action0, resolveContrastColor());
Adrian Roosfe84e1f2015-11-04 15:55:39 -08003614 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003615 return button;
3616 }
3617
Joe Onoratocb109a02011-01-18 17:57:41 -08003618 /**
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003619 * @return Whether we are currently building a notification from a legacy (an app that
Alan Viverette3cb07a462014-06-06 14:19:53 -07003620 * doesn't create material notifications by itself) app.
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003621 */
3622 private boolean isLegacy() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003623 return getColorUtil() != null;
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003624 }
3625
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003626 private CharSequence processLegacyText(CharSequence charSequence) {
3627 if (isLegacy()) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003628 return getColorUtil().invertCharSequenceColors(charSequence);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003629 } else {
3630 return charSequence;
3631 }
3632 }
3633
Dan Sandler26e81cf2014-05-06 10:01:27 -04003634 /**
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003635 * Apply any necessariy colors to the small icon
Dan Sandler26e81cf2014-05-06 10:01:27 -04003636 */
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003637 private void processSmallIconColor(Icon smallIcon, RemoteViews contentView) {
Selim Cinekea4bef72015-12-02 15:51:10 -08003638 boolean colorable = !isLegacy() || getColorUtil().isGrayscaleIcon(mContext, smallIcon);
3639 if (colorable) {
Adrian Roos4ff3b122016-02-01 12:26:13 -08003640 contentView.setDrawableParameters(R.id.icon, false, -1, resolveContrastColor(),
Jorim Jaggi92df1f22014-12-16 19:44:41 +01003641 PorterDuff.Mode.SRC_ATOP, -1);
Selim Cinekea4bef72015-12-02 15:51:10 -08003642
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003643 }
Selim Cinekea4bef72015-12-02 15:51:10 -08003644 contentView.setInt(R.id.notification_header, "setOriginalIconColor",
Adrian Roos4ff3b122016-02-01 12:26:13 -08003645 colorable ? resolveContrastColor() : NotificationHeaderView.NO_COLOR);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003646 }
3647
Dan Sandler26e81cf2014-05-06 10:01:27 -04003648 /**
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003649 * Make the largeIcon dark if it's a fake smallIcon (that is,
Dan Sandler26e81cf2014-05-06 10:01:27 -04003650 * if it's grayscale).
3651 */
3652 // TODO: also check bounds, transparency, that sort of thing.
Dan Sandlerd63f9322015-05-06 15:18:49 -04003653 private void processLargeLegacyIcon(Icon largeIcon, RemoteViews contentView) {
3654 if (largeIcon != null && isLegacy()
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003655 && getColorUtil().isGrayscaleIcon(mContext, largeIcon)) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003656 // resolve color will fall back to the default when legacy
Adrian Roos4ff3b122016-02-01 12:26:13 -08003657 contentView.setDrawableParameters(R.id.icon, false, -1, resolveContrastColor(),
Dan Sandler190d58d2014-05-15 09:33:39 -04003658 PorterDuff.Mode.SRC_ATOP, -1);
Jorim Jaggi92df1f22014-12-16 19:44:41 +01003659 }
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003660 }
3661
Julia Reynolds10ee1fc2015-11-09 11:04:55 -05003662 private void sanitizeColor() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003663 if (mN.color != COLOR_DEFAULT) {
3664 mN.color |= 0xFF000000; // no alpha for custom colors
Jorim Jaggi74419312014-06-10 20:57:21 +02003665 }
Jorim Jaggi74419312014-06-10 20:57:21 +02003666 }
3667
Adrian Roos4ff3b122016-02-01 12:26:13 -08003668 int resolveContrastColor() {
3669 if (mCachedContrastColorIsFor == mN.color && mCachedContrastColor != COLOR_INVALID) {
3670 return mCachedContrastColor;
Dan Sandler26e81cf2014-05-06 10:01:27 -04003671 }
Adrian Roos4ff3b122016-02-01 12:26:13 -08003672 final int contrasted = NotificationColorUtil.resolveContrastColor(mContext, mN.color);
3673
3674 mCachedContrastColorIsFor = mN.color;
3675 return mCachedContrastColor = contrasted;
Dan Sandler26e81cf2014-05-06 10:01:27 -04003676 }
3677
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003678 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003679 * Apply the unstyled operations and return a new {@link Notification} object.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04003680 * @hide
Joe Onoratocb109a02011-01-18 17:57:41 -08003681 */
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04003682 public Notification buildUnstyled() {
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003683 if (mActions.size() > 0) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003684 mN.actions = new Action[mActions.size()];
3685 mActions.toArray(mN.actions);
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003686 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003687 if (!mPersonList.isEmpty()) {
3688 mN.extras.putStringArray(EXTRA_PEOPLE,
3689 mPersonList.toArray(new String[mPersonList.size()]));
Dan Sandler0bf2ed82013-12-21 23:33:41 -06003690 }
Selim Cinek247fa012016-02-18 09:50:48 -08003691 if (mN.bigContentView != null || mN.contentView != null
3692 || mN.headsUpContentView != null) {
3693 mN.extras.putBoolean(EXTRA_CONTAINS_CUSTOM_VIEW, true);
3694 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003695 return mN;
Joe Onorato46439ce2010-11-19 13:56:21 -08003696 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003697
Julia Reynolds3b848122016-02-26 10:45:32 -05003698 /**
3699 * Creates a Builder from an existing notification so further changes can be made.
3700 * @param context The context for your application / activity.
3701 * @param n The notification to create a Builder from.
3702 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003703 public static Notification.Builder recoverBuilder(Context context, Notification n) {
Christoph Studer4600f9b2014-07-22 22:44:43 +02003704 // Re-create notification context so we can access app resources.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003705 ApplicationInfo applicationInfo = n.extras.getParcelable(
3706 EXTRA_BUILDER_APPLICATION_INFO);
Christoph Studer4600f9b2014-07-22 22:44:43 +02003707 Context builderContext;
Julia Reynoldsda303542015-11-23 14:00:20 -05003708 if (applicationInfo != null) {
3709 try {
3710 builderContext = context.createApplicationContext(applicationInfo,
3711 Context.CONTEXT_RESTRICTED);
3712 } catch (NameNotFoundException e) {
3713 Log.e(TAG, "ApplicationInfo " + applicationInfo + " not found");
3714 builderContext = context; // try with our context
3715 }
3716 } else {
3717 builderContext = context; // try with given context
Christoph Studer4600f9b2014-07-22 22:44:43 +02003718 }
3719
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003720 return new Builder(builderContext, n);
Christoph Studer4600f9b2014-07-22 22:44:43 +02003721 }
3722
3723 private static Class<? extends Style> getNotificationStyleClass(String templateClass) {
Selim Cinek593610c2016-02-16 18:42:57 -08003724 Class<? extends Style>[] classes = new Class[] {
3725 BigTextStyle.class, BigPictureStyle.class, InboxStyle.class, MediaStyle.class,
Alex Hillsfc737de2016-03-23 17:33:02 -04003726 DecoratedCustomViewStyle.class, DecoratedMediaCustomViewStyle.class,
3727 MessagingStyle.class };
Christoph Studer4600f9b2014-07-22 22:44:43 +02003728 for (Class<? extends Style> innerClass : classes) {
3729 if (templateClass.equals(innerClass.getName())) {
3730 return innerClass;
3731 }
3732 }
3733 return null;
3734 }
3735
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003736 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003737 * @deprecated Use {@link #build()} instead.
3738 */
3739 @Deprecated
3740 public Notification getNotification() {
3741 return build();
3742 }
3743
3744 /**
3745 * Combine all of the options that have been set and return a new {@link Notification}
3746 * object.
3747 */
3748 public Notification build() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003749 // first, add any extras from the calling code
3750 if (mUserExtras != null) {
3751 mN.extras = getAllExtras();
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07003752 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003753
Selim Cinekb85f36fd2016-04-20 18:46:36 -07003754 mN.creationTime = System.currentTimeMillis();
3755
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003756 // lazy stuff from mContext; see comment in Builder(Context, Notification)
Julia Reynoldsda303542015-11-23 14:00:20 -05003757 Notification.addFieldsFromContext(mContext, mN);
Christoph Studer943aa672014-08-03 20:31:16 +02003758
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003759 buildUnstyled();
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003760
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003761 if (mStyle != null) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003762 mStyle.buildStyled(mN);
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003763 }
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003764
Adrian Roos5081c0d2016-02-26 16:04:19 -08003765 if (mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N
3766 && (mStyle == null || !mStyle.displayCustomViewInline())) {
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05003767 if (mN.contentView == null) {
Julia Reynolds3b848122016-02-26 10:45:32 -05003768 mN.contentView = createContentView();
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05003769 mN.extras.putInt(EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT,
3770 mN.contentView.getSequenceNumber());
3771 }
3772 if (mN.bigContentView == null) {
Julia Reynolds3b848122016-02-26 10:45:32 -05003773 mN.bigContentView = createBigContentView();
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05003774 if (mN.bigContentView != null) {
3775 mN.extras.putInt(EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT,
3776 mN.bigContentView.getSequenceNumber());
3777 }
3778 }
3779 if (mN.headsUpContentView == null) {
Julia Reynolds3b848122016-02-26 10:45:32 -05003780 mN.headsUpContentView = createHeadsUpContentView();
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05003781 if (mN.headsUpContentView != null) {
3782 mN.extras.putInt(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT,
3783 mN.headsUpContentView.getSequenceNumber());
3784 }
3785 }
3786 }
3787
Julia Reynolds4c0c2022016-02-02 15:11:59 -05003788 if ((mN.defaults & DEFAULT_LIGHTS) != 0) {
3789 mN.flags |= FLAG_SHOW_LIGHTS;
3790 }
3791
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003792 return mN;
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003793 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05003794
3795 /**
3796 * Apply this Builder to an existing {@link Notification} object.
3797 *
3798 * @hide
3799 */
3800 public Notification buildInto(Notification n) {
Daniel Sandler1a497d32013-04-18 14:52:45 -04003801 build().cloneInto(n, true);
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05003802 return n;
3803 }
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003804
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05003805 /**
Adrian Roos184bfe022016-03-03 13:41:44 -08003806 * Removes RemoteViews that were created for compatibility from {@param n}, if they did not
3807 * change.
3808 *
3809 * @return {@param n}, if no stripping is needed, otherwise a stripped clone of {@param n}.
3810 *
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05003811 * @hide
3812 */
Adrian Roos184bfe022016-03-03 13:41:44 -08003813 public static Notification maybeCloneStrippedForDelivery(Notification n) {
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05003814 String templateClass = n.extras.getString(EXTRA_TEMPLATE);
Adrian Roos184bfe022016-03-03 13:41:44 -08003815
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05003816 // Only strip views for known Styles because we won't know how to
3817 // re-create them otherwise.
Adrian Roos184bfe022016-03-03 13:41:44 -08003818 if (!TextUtils.isEmpty(templateClass)
3819 && getNotificationStyleClass(templateClass) == null) {
3820 return n;
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05003821 }
Adrian Roos184bfe022016-03-03 13:41:44 -08003822
3823 // Only strip unmodified BuilderRemoteViews.
3824 boolean stripContentView = n.contentView instanceof BuilderRemoteViews &&
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05003825 n.extras.getInt(EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT, -1) ==
Adrian Roos184bfe022016-03-03 13:41:44 -08003826 n.contentView.getSequenceNumber();
3827 boolean stripBigContentView = n.bigContentView instanceof BuilderRemoteViews &&
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05003828 n.extras.getInt(EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT, -1) ==
Adrian Roos184bfe022016-03-03 13:41:44 -08003829 n.bigContentView.getSequenceNumber();
3830 boolean stripHeadsUpContentView = n.headsUpContentView instanceof BuilderRemoteViews &&
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05003831 n.extras.getInt(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT, -1) ==
Adrian Roos184bfe022016-03-03 13:41:44 -08003832 n.headsUpContentView.getSequenceNumber();
3833
3834 // Nothing to do here, no need to clone.
3835 if (!stripContentView && !stripBigContentView && !stripHeadsUpContentView) {
3836 return n;
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05003837 }
Adrian Roos184bfe022016-03-03 13:41:44 -08003838
3839 Notification clone = n.clone();
3840 if (stripContentView) {
3841 clone.contentView = null;
3842 clone.extras.remove(EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT);
3843 }
3844 if (stripBigContentView) {
3845 clone.bigContentView = null;
3846 clone.extras.remove(EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT);
3847 }
3848 if (stripHeadsUpContentView) {
3849 clone.headsUpContentView = null;
3850 clone.extras.remove(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT);
3851 }
3852 return clone;
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05003853 }
3854
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003855 private int getBaseLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07003856 return R.layout.notification_template_material_base;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003857 }
3858
3859 private int getBigBaseLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07003860 return R.layout.notification_template_material_big_base;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003861 }
3862
3863 private int getBigPictureLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07003864 return R.layout.notification_template_material_big_picture;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003865 }
3866
3867 private int getBigTextLayoutResource() {
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003868 return R.layout.notification_template_material_big_text;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003869 }
3870
3871 private int getInboxLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07003872 return R.layout.notification_template_material_inbox;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003873 }
3874
Adrian Roosc1a80b02016-04-05 14:54:55 -07003875 private int getMessagingLayoutResource() {
3876 return R.layout.notification_template_material_messaging;
3877 }
3878
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003879 private int getActionLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07003880 return R.layout.notification_material_action;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003881 }
3882
3883 private int getActionTombstoneLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07003884 return R.layout.notification_material_action_tombstone;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003885 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003886 }
3887
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003888 /**
Selim Cinekc2c0b042016-05-18 17:13:46 -07003889 * @return true if the notification will show the time; false otherwise
Selim Cinekb85f36fd2016-04-20 18:46:36 -07003890 * @hide
3891 */
Selim Cinekc2c0b042016-05-18 17:13:46 -07003892 public boolean showsTime() {
Selim Cinekb85f36fd2016-04-20 18:46:36 -07003893 return when != 0 && extras.getBoolean(EXTRA_SHOW_WHEN);
3894 }
3895
3896 /**
Selim Cinekc2c0b042016-05-18 17:13:46 -07003897 * @return true if the notification will show a chronometer; false otherwise
3898 * @hide
3899 */
3900 public boolean showsChronometer() {
3901 return when != 0 && extras.getBoolean(EXTRA_SHOW_CHRONOMETER);
3902 }
3903
3904 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003905 * An object that can apply a rich notification style to a {@link Notification.Builder}
3906 * object.
3907 */
Griff Hazendfcb0802014-02-11 12:00:00 -08003908 public static abstract class Style {
Chris Wrend6297db2012-05-03 16:20:13 -04003909 private CharSequence mBigContentTitle;
Jorim Jaggi457a10d2014-09-08 16:18:23 +02003910
3911 /**
3912 * @hide
3913 */
3914 protected CharSequence mSummaryText = null;
3915
3916 /**
3917 * @hide
3918 */
3919 protected boolean mSummaryTextSet = false;
Chris Wrend6297db2012-05-03 16:20:13 -04003920
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003921 protected Builder mBuilder;
3922
Chris Wrend6297db2012-05-03 16:20:13 -04003923 /**
3924 * Overrides ContentTitle in the big form of the template.
3925 * This defaults to the value passed to setContentTitle().
3926 */
3927 protected void internalSetBigContentTitle(CharSequence title) {
3928 mBigContentTitle = title;
3929 }
3930
3931 /**
3932 * Set the first line of text after the detail section in the big form of the template.
3933 */
3934 protected void internalSetSummaryText(CharSequence cs) {
3935 mSummaryText = cs;
Daniel Sandler619738c2012-06-07 16:33:08 -04003936 mSummaryTextSet = true;
Chris Wrend6297db2012-05-03 16:20:13 -04003937 }
3938
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003939 public void setBuilder(Builder builder) {
3940 if (mBuilder != builder) {
3941 mBuilder = builder;
Daniel Sandlerc08dea22012-06-28 08:35:24 -07003942 if (mBuilder != null) {
3943 mBuilder.setStyle(this);
3944 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003945 }
3946 }
3947
Chris Wrend6297db2012-05-03 16:20:13 -04003948 protected void checkBuilder() {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003949 if (mBuilder == null) {
3950 throw new IllegalArgumentException("Style requires a valid Builder object");
3951 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003952 }
Chris Wrend6297db2012-05-03 16:20:13 -04003953
3954 protected RemoteViews getStandardView(int layoutId) {
3955 checkBuilder();
3956
Christoph Studer4600f9b2014-07-22 22:44:43 +02003957 // Nasty.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003958 CharSequence oldBuilderContentTitle =
3959 mBuilder.getAllExtras().getCharSequence(EXTRA_TITLE);
Chris Wrend6297db2012-05-03 16:20:13 -04003960 if (mBigContentTitle != null) {
3961 mBuilder.setContentTitle(mBigContentTitle);
3962 }
3963
Chris Wrend6297db2012-05-03 16:20:13 -04003964 RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(layoutId);
3965
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003966 mBuilder.getAllExtras().putCharSequence(EXTRA_TITLE, oldBuilderContentTitle);
Christoph Studer4600f9b2014-07-22 22:44:43 +02003967
Chris Wrend6297db2012-05-03 16:20:13 -04003968 if (mBigContentTitle != null && mBigContentTitle.equals("")) {
3969 contentView.setViewVisibility(R.id.line1, View.GONE);
Chris Wren67dc9a02012-05-16 01:03:20 -04003970 } else {
3971 contentView.setViewVisibility(R.id.line1, View.VISIBLE);
Chris Wrend6297db2012-05-03 16:20:13 -04003972 }
3973
Chris Wrend6297db2012-05-03 16:20:13 -04003974 return contentView;
3975 }
3976
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003977 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003978 * Construct a Style-specific RemoteViews for the final 1U notification layout.
3979 * The default implementation has nothing additional to add.
3980 * @hide
3981 */
3982 public RemoteViews makeContentView() {
3983 return null;
3984 }
3985
3986 /**
3987 * Construct a Style-specific RemoteViews for the final big notification layout.
3988 * @hide
3989 */
3990 public RemoteViews makeBigContentView() {
3991 return null;
3992 }
3993
3994 /**
3995 * Construct a Style-specific RemoteViews for the final HUN layout.
3996 * @hide
3997 */
3998 public RemoteViews makeHeadsUpContentView() {
3999 return null;
4000 }
4001
4002 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004003 * Apply any style-specific extras to this notification before shipping it out.
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004004 * @hide
4005 */
4006 public void addExtras(Bundle extras) {
4007 if (mSummaryTextSet) {
4008 extras.putCharSequence(EXTRA_SUMMARY_TEXT, mSummaryText);
4009 }
4010 if (mBigContentTitle != null) {
4011 extras.putCharSequence(EXTRA_TITLE_BIG, mBigContentTitle);
4012 }
Chris Wren91ad5632013-06-05 15:05:57 -04004013 extras.putString(EXTRA_TEMPLATE, this.getClass().getName());
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004014 }
4015
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04004016 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004017 * Reconstruct the internal state of this Style object from extras.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04004018 * @hide
4019 */
Christoph Studer4600f9b2014-07-22 22:44:43 +02004020 protected void restoreFromExtras(Bundle extras) {
4021 if (extras.containsKey(EXTRA_SUMMARY_TEXT)) {
4022 mSummaryText = extras.getCharSequence(EXTRA_SUMMARY_TEXT);
4023 mSummaryTextSet = true;
4024 }
4025 if (extras.containsKey(EXTRA_TITLE_BIG)) {
4026 mBigContentTitle = extras.getCharSequence(EXTRA_TITLE_BIG);
4027 }
4028 }
4029
4030
4031 /**
4032 * @hide
4033 */
4034 public Notification buildStyled(Notification wip) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004035 addExtras(wip.extras);
Christoph Studer4600f9b2014-07-22 22:44:43 +02004036 return wip;
4037 }
4038
Daniel Sandler0ec46202015-06-24 01:27:05 -04004039 /**
4040 * @hide
4041 */
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07004042 public void purgeResources() {}
4043
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04004044 /**
4045 * Calls {@link android.app.Notification.Builder#build()} on the Builder this Style is
4046 * attached to.
4047 *
4048 * @return the fully constructed Notification.
4049 */
4050 public Notification build() {
4051 checkBuilder();
4052 return mBuilder.build();
4053 }
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004054
4055 /**
4056 * @hide
4057 * @return true if the style positions the progress bar on the second line; false if the
4058 * style hides the progress bar
4059 */
4060 protected boolean hasProgress() {
4061 return true;
4062 }
Selim Cinek03d0d652015-11-13 13:18:09 -05004063
4064 /**
4065 * @hide
4066 * @return Whether we should put the summary be put into the notification header
4067 */
4068 public boolean hasSummaryInHeader() {
4069 return true;
4070 }
Selim Cinek593610c2016-02-16 18:42:57 -08004071
4072 /**
4073 * @hide
4074 * @return Whether custom content views are displayed inline in the style
4075 */
4076 public boolean displayCustomViewInline() {
4077 return false;
4078 }
Joe Onorato46439ce2010-11-19 13:56:21 -08004079 }
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004080
4081 /**
Daniel Sandler4dfbe832012-04-11 14:51:46 -04004082 * Helper class for generating large-format notifications that include a large image attachment.
Joe Malin8d40d042012-11-05 11:36:40 -08004083 *
Robert Ly91c5ce32014-06-08 15:37:00 -07004084 * Here's how you'd set the <code>BigPictureStyle</code> on a notification:
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004085 * <pre class="prettyprint">
Robert Ly91c5ce32014-06-08 15:37:00 -07004086 * Notification notif = new Notification.Builder(mContext)
4087 * .setContentTitle(&quot;New photo from &quot; + sender.toString())
4088 * .setContentText(subject)
4089 * .setSmallIcon(R.drawable.new_post)
4090 * .setLargeIcon(aBitmap)
4091 * .setStyle(new Notification.BigPictureStyle()
4092 * .bigPicture(aBigBitmap))
4093 * .build();
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004094 * </pre>
Joe Malin8d40d042012-11-05 11:36:40 -08004095 *
Daniel Sandler4dfbe832012-04-11 14:51:46 -04004096 * @see Notification#bigContentView
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004097 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004098 public static class BigPictureStyle extends Style {
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004099 private Bitmap mPicture;
Dan Sandlerd63f9322015-05-06 15:18:49 -04004100 private Icon mBigLargeIcon;
Chris Wren3745a3d2012-05-22 15:11:52 -04004101 private boolean mBigLargeIconSet = false;
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004102
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004103 public BigPictureStyle() {
4104 }
4105
Adrian Roosf5faf9d2016-05-23 13:56:15 -07004106 /**
4107 * @deprecated use {@code BigPictureStyle()}.
4108 */
4109 @Deprecated
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004110 public BigPictureStyle(Builder builder) {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004111 setBuilder(builder);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004112 }
4113
Chris Wrend6297db2012-05-03 16:20:13 -04004114 /**
4115 * Overrides ContentTitle in the big form of the template.
4116 * This defaults to the value passed to setContentTitle().
4117 */
4118 public BigPictureStyle setBigContentTitle(CharSequence title) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04004119 internalSetBigContentTitle(safeCharSequence(title));
Chris Wrend6297db2012-05-03 16:20:13 -04004120 return this;
4121 }
4122
4123 /**
4124 * Set the first line of text after the detail section in the big form of the template.
4125 */
4126 public BigPictureStyle setSummaryText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04004127 internalSetSummaryText(safeCharSequence(cs));
Chris Wrend6297db2012-05-03 16:20:13 -04004128 return this;
4129 }
4130
Chris Wren0bd664d2012-08-01 13:56:56 -04004131 /**
4132 * Provide the bitmap to be used as the payload for the BigPicture notification.
4133 */
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004134 public BigPictureStyle bigPicture(Bitmap b) {
4135 mPicture = b;
4136 return this;
4137 }
4138
Chris Wren3745a3d2012-05-22 15:11:52 -04004139 /**
Chris Wren3745a3d2012-05-22 15:11:52 -04004140 * Override the large icon when the big notification is shown.
4141 */
4142 public BigPictureStyle bigLargeIcon(Bitmap b) {
Dan Sandlerd63f9322015-05-06 15:18:49 -04004143 return bigLargeIcon(b != null ? Icon.createWithBitmap(b) : null);
4144 }
4145
4146 /**
4147 * Override the large icon when the big notification is shown.
4148 */
4149 public BigPictureStyle bigLargeIcon(Icon icon) {
Chris Wren3745a3d2012-05-22 15:11:52 -04004150 mBigLargeIconSet = true;
Dan Sandlerd63f9322015-05-06 15:18:49 -04004151 mBigLargeIcon = icon;
Chris Wren3745a3d2012-05-22 15:11:52 -04004152 return this;
4153 }
4154
Riley Andrews0394a0c2015-11-03 23:36:52 -08004155 /** @hide */
4156 public static final int MIN_ASHMEM_BITMAP_SIZE = 128 * (1 << 10);
4157
Daniel Sandler0ec46202015-06-24 01:27:05 -04004158 /**
4159 * @hide
4160 */
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07004161 @Override
4162 public void purgeResources() {
4163 super.purgeResources();
Riley Andrews8cee7c12015-11-01 23:36:04 -08004164 if (mPicture != null &&
4165 mPicture.isMutable() &&
Riley Andrews0394a0c2015-11-03 23:36:52 -08004166 mPicture.getAllocationByteCount() >= MIN_ASHMEM_BITMAP_SIZE) {
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07004167 mPicture = mPicture.createAshmemBitmap();
4168 }
4169 if (mBigLargeIcon != null) {
4170 mBigLargeIcon.convertToAshmem();
4171 }
4172 }
Christoph Studer5c510ee2014-12-15 16:32:27 +01004173
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004174 /**
4175 * @hide
4176 */
4177 public RemoteViews makeBigContentView() {
4178 // Replace mN.mLargeIcon with mBigLargeIcon if mBigLargeIconSet
Christoph Studer5c510ee2014-12-15 16:32:27 +01004179 // This covers the following cases:
4180 // 1. mBigLargeIconSet -> mBigLargeIcon (null or non-null) applies, overrides
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004181 // mN.mLargeIcon
4182 // 2. !mBigLargeIconSet -> mN.mLargeIcon applies
Dan Sandlerd63f9322015-05-06 15:18:49 -04004183 Icon oldLargeIcon = null;
Christoph Studer5c510ee2014-12-15 16:32:27 +01004184 if (mBigLargeIconSet) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004185 oldLargeIcon = mBuilder.mN.mLargeIcon;
4186 mBuilder.mN.mLargeIcon = mBigLargeIcon;
Christoph Studer5c510ee2014-12-15 16:32:27 +01004187 }
4188
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004189 RemoteViews contentView = getStandardView(mBuilder.getBigPictureLayoutResource());
Selim Cinek03d0d652015-11-13 13:18:09 -05004190 if (mSummaryTextSet) {
4191 contentView.setTextViewText(R.id.text, mBuilder.processLegacyText(mSummaryText));
Selim Cinekc848c3a2016-01-13 15:27:30 -08004192 contentView.setViewVisibility(R.id.text, View.VISIBLE);
Selim Cinek03d0d652015-11-13 13:18:09 -05004193 }
Selim Cinek860b6da2015-12-16 19:02:19 -08004194 mBuilder.setContentMinHeight(contentView, mBuilder.mN.mLargeIcon != null);
Selim Cinek53e64a42015-11-16 10:40:56 -08004195
Christoph Studer5c510ee2014-12-15 16:32:27 +01004196 if (mBigLargeIconSet) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004197 mBuilder.mN.mLargeIcon = oldLargeIcon;
Christoph Studer5c510ee2014-12-15 16:32:27 +01004198 }
4199
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004200 contentView.setImageViewBitmap(R.id.big_picture, mPicture);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004201 return contentView;
4202 }
4203
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004204 /**
4205 * @hide
4206 */
4207 public void addExtras(Bundle extras) {
4208 super.addExtras(extras);
4209
4210 if (mBigLargeIconSet) {
4211 extras.putParcelable(EXTRA_LARGE_ICON_BIG, mBigLargeIcon);
4212 }
4213 extras.putParcelable(EXTRA_PICTURE, mPicture);
4214 }
4215
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04004216 /**
4217 * @hide
4218 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004219 @Override
Christoph Studer4600f9b2014-07-22 22:44:43 +02004220 protected void restoreFromExtras(Bundle extras) {
4221 super.restoreFromExtras(extras);
4222
4223 if (extras.containsKey(EXTRA_LARGE_ICON_BIG)) {
Christoph Studer5c510ee2014-12-15 16:32:27 +01004224 mBigLargeIconSet = true;
Christoph Studer4600f9b2014-07-22 22:44:43 +02004225 mBigLargeIcon = extras.getParcelable(EXTRA_LARGE_ICON_BIG);
Chris Wren3745a3d2012-05-22 15:11:52 -04004226 }
Christoph Studer4600f9b2014-07-22 22:44:43 +02004227 mPicture = extras.getParcelable(EXTRA_PICTURE);
4228 }
Selim Cinek03d0d652015-11-13 13:18:09 -05004229
4230 /**
4231 * @hide
4232 */
4233 @Override
4234 public boolean hasSummaryInHeader() {
4235 return false;
4236 }
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004237 }
4238
4239 /**
Daniel Sandler4dfbe832012-04-11 14:51:46 -04004240 * Helper class for generating large-format notifications that include a lot of text.
Joe Malin8d40d042012-11-05 11:36:40 -08004241 *
Robert Ly91c5ce32014-06-08 15:37:00 -07004242 * Here's how you'd set the <code>BigTextStyle</code> on a notification:
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004243 * <pre class="prettyprint">
Robert Ly91c5ce32014-06-08 15:37:00 -07004244 * Notification notif = new Notification.Builder(mContext)
4245 * .setContentTitle(&quot;New mail from &quot; + sender.toString())
4246 * .setContentText(subject)
4247 * .setSmallIcon(R.drawable.new_mail)
4248 * .setLargeIcon(aBitmap)
4249 * .setStyle(new Notification.BigTextStyle()
4250 * .bigText(aVeryLongString))
4251 * .build();
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004252 * </pre>
Joe Malin8d40d042012-11-05 11:36:40 -08004253 *
Daniel Sandler4dfbe832012-04-11 14:51:46 -04004254 * @see Notification#bigContentView
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004255 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004256 public static class BigTextStyle extends Style {
Jorim Jaggi457a10d2014-09-08 16:18:23 +02004257
4258 private static final int MAX_LINES = 13;
Selim Cinek3a2c4b92015-12-17 17:01:17 -08004259 private static final int LINES_CONSUMED_BY_ACTIONS = 4;
Jorim Jaggi457a10d2014-09-08 16:18:23 +02004260
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004261 private CharSequence mBigText;
4262
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004263 public BigTextStyle() {
4264 }
4265
Adrian Roosf5faf9d2016-05-23 13:56:15 -07004266 /**
4267 * @deprecated use {@code BigTextStyle()}.
4268 */
4269 @Deprecated
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004270 public BigTextStyle(Builder builder) {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004271 setBuilder(builder);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004272 }
4273
Chris Wrend6297db2012-05-03 16:20:13 -04004274 /**
4275 * Overrides ContentTitle in the big form of the template.
4276 * This defaults to the value passed to setContentTitle().
4277 */
4278 public BigTextStyle setBigContentTitle(CharSequence title) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04004279 internalSetBigContentTitle(safeCharSequence(title));
Chris Wrend6297db2012-05-03 16:20:13 -04004280 return this;
4281 }
4282
4283 /**
4284 * Set the first line of text after the detail section in the big form of the template.
4285 */
4286 public BigTextStyle setSummaryText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04004287 internalSetSummaryText(safeCharSequence(cs));
Chris Wrend6297db2012-05-03 16:20:13 -04004288 return this;
4289 }
4290
Chris Wren0bd664d2012-08-01 13:56:56 -04004291 /**
4292 * Provide the longer text to be displayed in the big form of the
4293 * template in place of the content text.
4294 */
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004295 public BigTextStyle bigText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04004296 mBigText = safeCharSequence(cs);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004297 return this;
4298 }
4299
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004300 /**
4301 * @hide
4302 */
4303 public void addExtras(Bundle extras) {
4304 super.addExtras(extras);
4305
Christoph Studer4600f9b2014-07-22 22:44:43 +02004306 extras.putCharSequence(EXTRA_BIG_TEXT, mBigText);
4307 }
4308
4309 /**
4310 * @hide
4311 */
4312 @Override
4313 protected void restoreFromExtras(Bundle extras) {
4314 super.restoreFromExtras(extras);
4315
4316 mBigText = extras.getCharSequence(EXTRA_BIG_TEXT);
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004317 }
4318
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004319 /**
4320 * @hide
4321 */
4322 public RemoteViews makeBigContentView() {
Christoph Studer4600f9b2014-07-22 22:44:43 +02004323
4324 // Nasty
Selim Cinek75998782016-04-26 10:39:17 -07004325 CharSequence text = mBuilder.getAllExtras().getCharSequence(EXTRA_TEXT);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004326 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, null);
Daniel Sandler916ad912012-06-13 12:17:07 -04004327
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004328 RemoteViews contentView = getStandardView(mBuilder.getBigTextLayoutResource());
Joe Malin8d40d042012-11-05 11:36:40 -08004329
Selim Cinek75998782016-04-26 10:39:17 -07004330 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, text);
Christoph Studer4600f9b2014-07-22 22:44:43 +02004331
Selim Cinek3a2c4b92015-12-17 17:01:17 -08004332 CharSequence bigTextText = mBuilder.processLegacyText(mBigText);
Selim Cinek75998782016-04-26 10:39:17 -07004333 if (TextUtils.isEmpty(bigTextText)) {
4334 // In case the bigtext is null / empty fall back to the normal text to avoid a weird
4335 // experience
4336 bigTextText = mBuilder.processLegacyText(text);
4337 }
Adrian Roosb1f427c2016-05-26 12:27:15 -07004338 applyBigTextContentView(mBuilder, contentView, bigTextText);
Selim Cinek4fb12d32015-11-19 18:10:48 -08004339
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004340 return contentView;
4341 }
4342
Adrian Roosb1f427c2016-05-26 12:27:15 -07004343 static void applyBigTextContentView(Builder builder,
4344 RemoteViews contentView, CharSequence bigTextText) {
4345 contentView.setTextViewText(R.id.big_text, bigTextText);
4346 contentView.setViewVisibility(R.id.big_text,
4347 TextUtils.isEmpty(bigTextText) ? View.GONE : View.VISIBLE);
4348 contentView.setInt(R.id.big_text, "setMaxLines", calculateMaxLines(builder));
4349 contentView.setBoolean(R.id.big_text, "setHasImage", builder.mN.mLargeIcon != null);
4350 }
4351
4352 private static int calculateMaxLines(Builder builder) {
Jorim Jaggi457a10d2014-09-08 16:18:23 +02004353 int lineCount = MAX_LINES;
Adrian Roosb1f427c2016-05-26 12:27:15 -07004354 boolean hasActions = builder.mActions.size() > 0;
Jorim Jaggi457a10d2014-09-08 16:18:23 +02004355 if (hasActions) {
4356 lineCount -= LINES_CONSUMED_BY_ACTIONS;
4357 }
Jorim Jaggi457a10d2014-09-08 16:18:23 +02004358 return lineCount;
4359 }
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004360 }
Daniel Sandler879c5e02012-04-17 16:46:51 -04004361
4362 /**
Alex Hillsfc737de2016-03-23 17:33:02 -04004363 * Helper class for generating large-format notifications that include multiple back-and-forth
4364 * messages of varying types between any number of people.
4365 *
4366 * <br>
4367 * If the platform does not provide large-format notifications, this method has no effect. The
4368 * user will always see the normal notification view.
4369 * <br>
4370 * This class is a "rebuilder": It attaches to a Builder object and modifies its behavior, like
4371 * so:
4372 * <pre class="prettyprint">
4373 *
4374 * Notification noti = new Notification.Builder()
4375 * .setContentTitle(&quot;2 new messages wtih &quot; + sender.toString())
4376 * .setContentText(subject)
4377 * .setSmallIcon(R.drawable.new_message)
4378 * .setLargeIcon(aBitmap)
4379 * .setStyle(new Notification.MessagingStyle(resources.getString(R.string.reply_name))
4380 * .addMessage(messages[0].getText(), messages[0].getTime(), messages[0].getSender())
4381 * .addMessage(messages[1].getText(), messages[1].getTime(), messages[1].getSender()))
4382 * .build();
4383 * </pre>
4384 */
4385 public static class MessagingStyle extends Style {
4386
4387 /**
4388 * The maximum number of messages that will be retained in the Notification itself (the
4389 * number displayed is up to the platform).
4390 */
4391 public static final int MAXIMUM_RETAINED_MESSAGES = 25;
4392
4393 CharSequence mUserDisplayName;
4394 CharSequence mConversationTitle;
Alex Hillsd9b04d92016-04-11 16:38:16 -04004395 List<Message> mMessages = new ArrayList<>();
Alex Hillsfc737de2016-03-23 17:33:02 -04004396
4397 MessagingStyle() {
4398 }
4399
4400 /**
4401 * @param userDisplayName the name to be displayed for any replies sent by the user before the
4402 * posting app reposts the notification with those messages after they've been actually
4403 * sent and in previous messages sent by the user added in
4404 * {@link #addMessage(Notification.MessagingStyle.Message)}
4405 */
4406 public MessagingStyle(CharSequence userDisplayName) {
4407 mUserDisplayName = userDisplayName;
4408 }
4409
4410 /**
4411 * Returns the name to be displayed for any replies sent by the user
4412 */
4413 public CharSequence getUserDisplayName() {
4414 return mUserDisplayName;
4415 }
4416
4417 /**
Alex Hillsfc737de2016-03-23 17:33:02 -04004418 * Sets the title to be displayed on this conversation. This should only be used for
4419 * group messaging and left unset for one-on-one conversations.
4420 * @param conversationTitle
4421 * @return this object for method chaining.
4422 */
4423 public MessagingStyle setConversationTitle(CharSequence conversationTitle) {
4424 mConversationTitle = conversationTitle;
4425 return this;
4426 }
4427
4428 /**
4429 * Return the title to be displayed on this conversation. Can be <code>null</code> and
4430 * should be for one-on-one conversations
4431 */
4432 public CharSequence getConversationTitle() {
4433 return mConversationTitle;
4434 }
4435
4436 /**
4437 * Adds a message for display by this notification. Convenience call for a simple
4438 * {@link Message} in {@link #addMessage(Notification.MessagingStyle.Message)}.
4439 * @param text A {@link CharSequence} to be displayed as the message content
4440 * @param timestamp Time at which the message arrived
4441 * @param sender A {@link CharSequence} to be used for displaying the name of the
4442 * sender. Should be <code>null</code> for messages by the current user, in which case
4443 * the platform will insert {@link #getUserDisplayName()}.
4444 * Should be unique amongst all individuals in the conversation, and should be
4445 * consistent during re-posts of the notification.
4446 *
4447 * @see Message#Message(CharSequence, long, CharSequence)
4448 *
4449 * @return this object for method chaining
4450 */
4451 public MessagingStyle addMessage(CharSequence text, long timestamp, CharSequence sender) {
4452 mMessages.add(new Message(text, timestamp, sender));
4453 if (mMessages.size() > MAXIMUM_RETAINED_MESSAGES) {
4454 mMessages.remove(0);
4455 }
4456 return this;
4457 }
4458
4459 /**
4460 * Adds a {@link Message} for display in this notification.
4461 * @param message The {@link Message} to be displayed
4462 * @return this object for method chaining
4463 */
4464 public MessagingStyle addMessage(Message message) {
4465 mMessages.add(message);
4466 if (mMessages.size() > MAXIMUM_RETAINED_MESSAGES) {
4467 mMessages.remove(0);
4468 }
4469 return this;
4470 }
4471
4472 /**
4473 * Gets the list of {@code Message} objects that represent the notification
4474 */
4475 public List<Message> getMessages() {
4476 return mMessages;
4477 }
4478
4479 /**
4480 * @hide
4481 */
4482 @Override
4483 public void addExtras(Bundle extras) {
4484 super.addExtras(extras);
4485 if (mUserDisplayName != null) {
4486 extras.putCharSequence(EXTRA_SELF_DISPLAY_NAME, mUserDisplayName);
4487 }
4488 if (mConversationTitle != null) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04004489 extras.putCharSequence(EXTRA_CONVERSATION_TITLE, mConversationTitle);
Alex Hillsfc737de2016-03-23 17:33:02 -04004490 }
Alex Hillsd9b04d92016-04-11 16:38:16 -04004491 if (!mMessages.isEmpty()) { extras.putParcelableArray(EXTRA_MESSAGES,
4492 Message.getBundleArrayForMessages(mMessages));
Alex Hillsfc737de2016-03-23 17:33:02 -04004493 }
Adrian Roos33fbd2c2016-05-27 15:35:28 -07004494
4495 fixTitleAndTextExtras(extras);
4496 }
4497
4498 private void fixTitleAndTextExtras(Bundle extras) {
4499 Message m = findLatestIncomingMessage();
4500 CharSequence text = (m == null) ? null : m.mText;
4501 CharSequence sender = m == null ? null
4502 : TextUtils.isEmpty(m.mSender) ? mUserDisplayName : m.mSender;
4503 CharSequence title;
4504 if (!TextUtils.isEmpty(mConversationTitle)) {
4505 if (!TextUtils.isEmpty(sender)) {
4506 BidiFormatter bidi = BidiFormatter.getInstance();
4507 title = mBuilder.mContext.getString(
4508 com.android.internal.R.string.notification_messaging_title_template,
4509 bidi.unicodeWrap(mConversationTitle), bidi.unicodeWrap(m.mSender));
4510 } else {
4511 title = mConversationTitle;
4512 }
4513 } else {
4514 title = sender;
4515 }
4516
4517 if (title != null) {
4518 extras.putCharSequence(EXTRA_TITLE, title);
4519 }
4520 if (text != null) {
4521 extras.putCharSequence(EXTRA_TEXT, text);
4522 }
Alex Hillsfc737de2016-03-23 17:33:02 -04004523 }
4524
4525 /**
4526 * @hide
4527 */
4528 @Override
4529 protected void restoreFromExtras(Bundle extras) {
4530 super.restoreFromExtras(extras);
4531
4532 mMessages.clear();
Adrian Roos96b7e202016-05-17 13:50:38 -07004533 mUserDisplayName = extras.getCharSequence(EXTRA_SELF_DISPLAY_NAME);
4534 mConversationTitle = extras.getCharSequence(EXTRA_CONVERSATION_TITLE);
Alex Hillsd9b04d92016-04-11 16:38:16 -04004535 Parcelable[] parcelables = extras.getParcelableArray(EXTRA_MESSAGES);
Adrian Roosdedd1df2016-04-26 16:38:47 -07004536 if (parcelables != null && parcelables instanceof Parcelable[]) {
4537 mMessages = Message.getMessagesFromBundleArray(parcelables);
Alex Hillsfc737de2016-03-23 17:33:02 -04004538 }
4539 }
4540
4541 /**
4542 * @hide
4543 */
Adrian Roosc1a80b02016-04-05 14:54:55 -07004544 @Override
4545 public RemoteViews makeContentView() {
4546 Message m = findLatestIncomingMessage();
4547 CharSequence title = mConversationTitle != null
4548 ? mConversationTitle
4549 : (m == null) ? null : m.mSender;
4550 CharSequence text = (m == null)
4551 ? null
4552 : mConversationTitle != null ? makeMessageLine(m) : m.mText;
Alex Hillsfc737de2016-03-23 17:33:02 -04004553
Adrian Roosc1a80b02016-04-05 14:54:55 -07004554 return mBuilder.applyStandardTemplate(mBuilder.getBaseLayoutResource(),
4555 false /* hasProgress */,
4556 title,
4557 text);
4558 }
4559
4560 private Message findLatestIncomingMessage() {
4561 for (int i = mMessages.size() - 1; i >= 0; i--) {
4562 Message m = mMessages.get(i);
4563 // Incoming messages have a non-empty sender.
4564 if (!TextUtils.isEmpty(m.mSender)) {
4565 return m;
4566 }
4567 }
Adrian Roos33fbd2c2016-05-27 15:35:28 -07004568 if (!mMessages.isEmpty()) {
4569 // No incoming messages, fall back to outgoing message
4570 return mMessages.get(mMessages.size() - 1);
4571 }
Adrian Roosc1a80b02016-04-05 14:54:55 -07004572 return null;
4573 }
4574
4575 /**
4576 * @hide
4577 */
4578 @Override
4579 public RemoteViews makeBigContentView() {
4580 CharSequence title = !TextUtils.isEmpty(super.mBigContentTitle)
4581 ? super.mBigContentTitle
4582 : mConversationTitle;
4583 boolean hasTitle = !TextUtils.isEmpty(title);
4584
Adrian Roosb1f427c2016-05-26 12:27:15 -07004585 if (!hasTitle && mMessages.size() == 1) {
4586 CharSequence sender = mMessages.get(0).mSender;
4587 CharSequence text = mMessages.get(0).mText;
4588 RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(
4589 mBuilder.getBigTextLayoutResource(),
4590 false /* progress */, sender, null /* text */);
4591 BigTextStyle.applyBigTextContentView(mBuilder, contentView, text);
4592 return contentView;
4593 }
4594
Adrian Roos48d746a2016-04-12 14:57:28 -07004595 RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(
Adrian Roosc1a80b02016-04-05 14:54:55 -07004596 mBuilder.getMessagingLayoutResource(),
4597 false /* hasProgress */,
4598 title,
4599 null /* text */);
4600
4601 int[] rowIds = {R.id.inbox_text0, R.id.inbox_text1, R.id.inbox_text2, R.id.inbox_text3,
4602 R.id.inbox_text4, R.id.inbox_text5, R.id.inbox_text6};
4603
4604 // Make sure all rows are gone in case we reuse a view.
4605 for (int rowId : rowIds) {
4606 contentView.setViewVisibility(rowId, View.GONE);
4607 }
4608
4609 int i=0;
4610 int titlePadding = mBuilder.mContext.getResources().getDimensionPixelSize(
4611 R.dimen.notification_messaging_spacing);
4612 contentView.setViewLayoutMarginBottom(R.id.line1, hasTitle ? titlePadding : 0);
4613 contentView.setInt(R.id.notification_messaging, "setNumIndentLines",
4614 mBuilder.mN.mLargeIcon == null ? 0 : (hasTitle ? 1 : 2));
4615
4616 int firstMessage = Math.max(0, mMessages.size() - rowIds.length);
4617 while (firstMessage + i < mMessages.size() && i < rowIds.length) {
4618 Message m = mMessages.get(firstMessage + i);
4619 int rowId = rowIds[i];
4620
4621 contentView.setViewVisibility(rowId, View.VISIBLE);
4622 contentView.setTextViewText(rowId, makeMessageLine(m));
4623
4624 i++;
4625 }
Alex Hillsfc737de2016-03-23 17:33:02 -04004626 return contentView;
4627 }
4628
Adrian Roosc1a80b02016-04-05 14:54:55 -07004629 private CharSequence makeMessageLine(Message m) {
4630 BidiFormatter bidi = BidiFormatter.getInstance();
4631 SpannableStringBuilder sb = new SpannableStringBuilder();
4632 if (TextUtils.isEmpty(m.mSender)) {
4633 CharSequence replyName = mUserDisplayName == null ? "" : mUserDisplayName;
4634 sb.append(bidi.unicodeWrap(replyName),
4635 makeFontColorSpan(mBuilder.resolveContrastColor()),
4636 0 /* flags */);
4637 } else {
4638 sb.append(bidi.unicodeWrap(m.mSender),
4639 makeFontColorSpan(Color.BLACK),
4640 0 /* flags */);
4641 }
4642 CharSequence text = m.mText == null ? "" : m.mText;
4643 sb.append(" ").append(bidi.unicodeWrap(text));
4644 return sb;
4645 }
4646
Adrian Roosdedd1df2016-04-26 16:38:47 -07004647 /**
4648 * @hide
4649 */
4650 @Override
4651 public RemoteViews makeHeadsUpContentView() {
4652 Message m = findLatestIncomingMessage();
4653 CharSequence title = mConversationTitle != null
4654 ? mConversationTitle
4655 : (m == null) ? null : m.mSender;
4656 CharSequence text = (m == null)
4657 ? null
4658 : mConversationTitle != null ? makeMessageLine(m) : m.mText;
4659
4660 return mBuilder.applyStandardTemplateWithActions(mBuilder.getBigBaseLayoutResource(),
4661 false /* hasProgress */,
4662 title,
4663 text);
4664 }
4665
Adrian Roosc1a80b02016-04-05 14:54:55 -07004666 private static TextAppearanceSpan makeFontColorSpan(int color) {
4667 return new TextAppearanceSpan(null, 0, 0,
4668 ColorStateList.valueOf(color), null);
4669 }
4670
Alex Hillsd9b04d92016-04-11 16:38:16 -04004671 public static final class Message {
4672
4673 static final String KEY_TEXT = "text";
4674 static final String KEY_TIMESTAMP = "time";
4675 static final String KEY_SENDER = "sender";
4676 static final String KEY_DATA_MIME_TYPE = "type";
4677 static final String KEY_DATA_URI= "uri";
Alex Hillsfc737de2016-03-23 17:33:02 -04004678
4679 private final CharSequence mText;
4680 private final long mTimestamp;
4681 private final CharSequence mSender;
4682
4683 private String mDataMimeType;
4684 private Uri mDataUri;
4685
4686 /**
4687 * Constructor
4688 * @param text A {@link CharSequence} to be displayed as the message content
4689 * @param timestamp Time at which the message arrived
4690 * @param sender A {@link CharSequence} to be used for displaying the name of the
4691 * sender. Should be <code>null</code> for messages by the current user, in which case
4692 * the platform will insert {@link MessagingStyle#getUserDisplayName()}.
4693 * Should be unique amongst all individuals in the conversation, and should be
4694 * consistent during re-posts of the notification.
4695 */
4696 public Message(CharSequence text, long timestamp, CharSequence sender){
4697 mText = text;
4698 mTimestamp = timestamp;
4699 mSender = sender;
4700 }
4701
4702 /**
4703 * Sets a binary blob of data and an associated MIME type for a message. In the case
4704 * where the platform doesn't support the MIME type, the original text provided in the
4705 * constructor will be used.
4706 * @param dataMimeType The MIME type of the content. See
4707 * <a href="{@docRoot}notifications/messaging.html"> for the list of supported MIME
4708 * types on Android and Android Wear.
4709 * @param dataUri The uri containing the content whose type is given by the MIME type.
4710 * <p class="note">
4711 * <ol>
4712 * <li>Notification Listeners including the System UI need permission to access the
4713 * data the Uri points to. The recommended ways to do this are:</li>
4714 * <li>Store the data in your own ContentProvider, making sure that other apps have
4715 * the correct permission to access your provider. The preferred mechanism for
4716 * providing access is to use per-URI permissions which are temporary and only
4717 * grant access to the receiving application. An easy way to create a
4718 * ContentProvider like this is to use the FileProvider helper class.</li>
4719 * <li>Use the system MediaStore. The MediaStore is primarily aimed at video, audio
4720 * and image MIME types, however beginning with Android 3.0 (API level 11) it can
4721 * also store non-media types (see MediaStore.Files for more info). Files can be
4722 * inserted into the MediaStore using scanFile() after which a content:// style
4723 * Uri suitable for sharing is passed to the provided onScanCompleted() callback.
4724 * Note that once added to the system MediaStore the content is accessible to any
4725 * app on the device.</li>
4726 * </ol>
4727 * @return this object for method chaining
4728 */
4729 public Message setData(String dataMimeType, Uri dataUri) {
4730 mDataMimeType = dataMimeType;
4731 mDataUri = dataUri;
4732 return this;
4733 }
4734
Alex Hillsfc737de2016-03-23 17:33:02 -04004735 /**
4736 * Get the text to be used for this message, or the fallback text if a type and content
4737 * Uri have been set
4738 */
4739 public CharSequence getText() {
4740 return mText;
4741 }
4742
4743 /**
4744 * Get the time at which this message arrived
4745 */
4746 public long getTimestamp() {
4747 return mTimestamp;
4748 }
4749
4750 /**
4751 * Get the text used to display the contact's name in the messaging experience
4752 */
4753 public CharSequence getSender() {
4754 return mSender;
4755 }
4756
4757 /**
4758 * Get the MIME type of the data pointed to by the Uri
4759 */
4760 public String getDataMimeType() {
4761 return mDataMimeType;
4762 }
4763
4764 /**
4765 * Get the the Uri pointing to the content of the message. Can be null, in which case
4766 * {@see #getText()} is used.
4767 */
4768 public Uri getDataUri() {
4769 return mDataUri;
4770 }
4771
Alex Hillsd9b04d92016-04-11 16:38:16 -04004772 private Bundle toBundle() {
4773 Bundle bundle = new Bundle();
Alex Hillsfc737de2016-03-23 17:33:02 -04004774 if (mText != null) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04004775 bundle.putCharSequence(KEY_TEXT, mText);
Alex Hillsfc737de2016-03-23 17:33:02 -04004776 }
Alex Hillsd9b04d92016-04-11 16:38:16 -04004777 bundle.putLong(KEY_TIMESTAMP, mTimestamp);
Alex Hillsfc737de2016-03-23 17:33:02 -04004778 if (mSender != null) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04004779 bundle.putCharSequence(KEY_SENDER, mSender);
Alex Hillsfc737de2016-03-23 17:33:02 -04004780 }
4781 if (mDataMimeType != null) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04004782 bundle.putString(KEY_DATA_MIME_TYPE, mDataMimeType);
Alex Hillsfc737de2016-03-23 17:33:02 -04004783 }
4784 if (mDataUri != null) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04004785 bundle.putParcelable(KEY_DATA_URI, mDataUri);
Alex Hillsfc737de2016-03-23 17:33:02 -04004786 }
Alex Hillsd9b04d92016-04-11 16:38:16 -04004787 return bundle;
Alex Hillsfc737de2016-03-23 17:33:02 -04004788 }
4789
Alex Hillsd9b04d92016-04-11 16:38:16 -04004790 static Bundle[] getBundleArrayForMessages(List<Message> messages) {
4791 Bundle[] bundles = new Bundle[messages.size()];
4792 final int N = messages.size();
4793 for (int i = 0; i < N; i++) {
4794 bundles[i] = messages.get(i).toBundle();
4795 }
4796 return bundles;
4797 }
4798
Adrian Roosdedd1df2016-04-26 16:38:47 -07004799 static List<Message> getMessagesFromBundleArray(Parcelable[] bundles) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04004800 List<Message> messages = new ArrayList<>(bundles.length);
4801 for (int i = 0; i < bundles.length; i++) {
Adrian Roosdedd1df2016-04-26 16:38:47 -07004802 if (bundles[i] instanceof Bundle) {
4803 Message message = getMessageFromBundle((Bundle)bundles[i]);
4804 if (message != null) {
4805 messages.add(message);
4806 }
Alex Hillsd9b04d92016-04-11 16:38:16 -04004807 }
4808 }
4809 return messages;
4810 }
4811
4812 static Message getMessageFromBundle(Bundle bundle) {
4813 try {
Adrian Roosfbddd2c2016-05-13 12:57:20 -07004814 if (!bundle.containsKey(KEY_TEXT) || !bundle.containsKey(KEY_TIMESTAMP)) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04004815 return null;
4816 } else {
4817 Message message = new Message(bundle.getCharSequence(KEY_TEXT),
4818 bundle.getLong(KEY_TIMESTAMP), bundle.getCharSequence(KEY_SENDER));
4819 if (bundle.containsKey(KEY_DATA_MIME_TYPE) &&
4820 bundle.containsKey(KEY_DATA_URI)) {
4821
4822 message.setData(bundle.getString(KEY_DATA_MIME_TYPE),
4823 (Uri) bundle.getParcelable(KEY_DATA_URI));
Alex Hillsfc737de2016-03-23 17:33:02 -04004824 }
Alex Hillsd9b04d92016-04-11 16:38:16 -04004825 return message;
4826 }
4827 } catch (ClassCastException e) {
4828 return null;
4829 }
4830 }
Alex Hillsfc737de2016-03-23 17:33:02 -04004831 }
4832 }
4833
4834 /**
Daniel Sandler879c5e02012-04-17 16:46:51 -04004835 * Helper class for generating large-format notifications that include a list of (up to 5) strings.
Joe Malin8d40d042012-11-05 11:36:40 -08004836 *
Robert Ly91c5ce32014-06-08 15:37:00 -07004837 * Here's how you'd set the <code>InboxStyle</code> on a notification:
Daniel Sandler879c5e02012-04-17 16:46:51 -04004838 * <pre class="prettyprint">
Robert Ly91c5ce32014-06-08 15:37:00 -07004839 * Notification notif = new Notification.Builder(mContext)
4840 * .setContentTitle(&quot;5 New mails from &quot; + sender.toString())
4841 * .setContentText(subject)
4842 * .setSmallIcon(R.drawable.new_mail)
4843 * .setLargeIcon(aBitmap)
4844 * .setStyle(new Notification.InboxStyle()
4845 * .addLine(str1)
4846 * .addLine(str2)
4847 * .setContentTitle(&quot;&quot;)
4848 * .setSummaryText(&quot;+3 more&quot;))
4849 * .build();
Daniel Sandler879c5e02012-04-17 16:46:51 -04004850 * </pre>
Joe Malin8d40d042012-11-05 11:36:40 -08004851 *
Daniel Sandler879c5e02012-04-17 16:46:51 -04004852 * @see Notification#bigContentView
4853 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004854 public static class InboxStyle extends Style {
Daniel Sandler879c5e02012-04-17 16:46:51 -04004855 private ArrayList<CharSequence> mTexts = new ArrayList<CharSequence>(5);
4856
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004857 public InboxStyle() {
4858 }
4859
Adrian Roosf5faf9d2016-05-23 13:56:15 -07004860 /**
4861 * @deprecated use {@code InboxStyle()}.
4862 */
4863 @Deprecated
Daniel Sandler879c5e02012-04-17 16:46:51 -04004864 public InboxStyle(Builder builder) {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004865 setBuilder(builder);
Daniel Sandler879c5e02012-04-17 16:46:51 -04004866 }
4867
Chris Wrend6297db2012-05-03 16:20:13 -04004868 /**
4869 * Overrides ContentTitle in the big form of the template.
4870 * This defaults to the value passed to setContentTitle().
4871 */
4872 public InboxStyle setBigContentTitle(CharSequence title) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04004873 internalSetBigContentTitle(safeCharSequence(title));
Chris Wrend6297db2012-05-03 16:20:13 -04004874 return this;
4875 }
4876
4877 /**
4878 * Set the first line of text after the detail section in the big form of the template.
4879 */
4880 public InboxStyle setSummaryText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04004881 internalSetSummaryText(safeCharSequence(cs));
Chris Wrend6297db2012-05-03 16:20:13 -04004882 return this;
4883 }
4884
Chris Wren0bd664d2012-08-01 13:56:56 -04004885 /**
4886 * Append a line to the digest section of the Inbox notification.
4887 */
Daniel Sandler879c5e02012-04-17 16:46:51 -04004888 public InboxStyle addLine(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04004889 mTexts.add(safeCharSequence(cs));
Daniel Sandler879c5e02012-04-17 16:46:51 -04004890 return this;
4891 }
4892
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004893 /**
4894 * @hide
4895 */
4896 public void addExtras(Bundle extras) {
4897 super.addExtras(extras);
Christoph Studer4600f9b2014-07-22 22:44:43 +02004898
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004899 CharSequence[] a = new CharSequence[mTexts.size()];
4900 extras.putCharSequenceArray(EXTRA_TEXT_LINES, mTexts.toArray(a));
4901 }
4902
Christoph Studer4600f9b2014-07-22 22:44:43 +02004903 /**
4904 * @hide
4905 */
4906 @Override
4907 protected void restoreFromExtras(Bundle extras) {
4908 super.restoreFromExtras(extras);
4909
4910 mTexts.clear();
4911 if (extras.containsKey(EXTRA_TEXT_LINES)) {
4912 Collections.addAll(mTexts, extras.getCharSequenceArray(EXTRA_TEXT_LINES));
4913 }
4914 }
4915
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004916 /**
4917 * @hide
4918 */
4919 public RemoteViews makeBigContentView() {
Selim Cinekc848c3a2016-01-13 15:27:30 -08004920 // Remove the content text so it disappears unless you have a summary
Christoph Studer4600f9b2014-07-22 22:44:43 +02004921 // Nasty
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004922 CharSequence oldBuilderContentText = mBuilder.mN.extras.getCharSequence(EXTRA_TEXT);
4923 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, null);
Christoph Studer4600f9b2014-07-22 22:44:43 +02004924
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004925 RemoteViews contentView = getStandardView(mBuilder.getInboxLayoutResource());
Daniel Sandler619738c2012-06-07 16:33:08 -04004926
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004927 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, oldBuilderContentText);
Christoph Studer4600f9b2014-07-22 22:44:43 +02004928
Chris Wrend6297db2012-05-03 16:20:13 -04004929 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 -04004930 R.id.inbox_text4, R.id.inbox_text5, R.id.inbox_text6};
Chris Wrend6297db2012-05-03 16:20:13 -04004931
Chris Wren4ed80d52012-05-17 09:30:03 -04004932 // Make sure all rows are gone in case we reuse a view.
4933 for (int rowId : rowIds) {
4934 contentView.setViewVisibility(rowId, View.GONE);
4935 }
4936
Daniel Sandler879c5e02012-04-17 16:46:51 -04004937 int i=0;
Selim Cinek07c80172016-04-21 16:40:47 -07004938 int topPadding = mBuilder.mContext.getResources().getDimensionPixelSize(
4939 R.dimen.notification_inbox_item_top_padding);
Selim Cinek247fa012016-02-18 09:50:48 -08004940 boolean first = true;
Selim Cinek07c80172016-04-21 16:40:47 -07004941 int onlyViewId = 0;
4942 int maxRows = rowIds.length;
4943 if (mBuilder.mActions.size() > 0) {
4944 maxRows--;
4945 }
4946 while (i < mTexts.size() && i < maxRows) {
Daniel Sandler879c5e02012-04-17 16:46:51 -04004947 CharSequence str = mTexts.get(i);
Selim Cinek07c80172016-04-21 16:40:47 -07004948 if (!TextUtils.isEmpty(str)) {
Daniel Sandler879c5e02012-04-17 16:46:51 -04004949 contentView.setViewVisibility(rowIds[i], View.VISIBLE);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004950 contentView.setTextViewText(rowIds[i], mBuilder.processLegacyText(str));
Selim Cinek07c80172016-04-21 16:40:47 -07004951 contentView.setViewPadding(rowIds[i], 0, topPadding, 0, 0);
Selim Cinek247fa012016-02-18 09:50:48 -08004952 handleInboxImageMargin(contentView, rowIds[i], first);
Selim Cinek07c80172016-04-21 16:40:47 -07004953 if (first) {
4954 onlyViewId = rowIds[i];
4955 } else {
4956 onlyViewId = 0;
4957 }
Selim Cinek247fa012016-02-18 09:50:48 -08004958 first = false;
Daniel Sandler879c5e02012-04-17 16:46:51 -04004959 }
4960 i++;
4961 }
Selim Cinek07c80172016-04-21 16:40:47 -07004962 if (onlyViewId != 0) {
4963 // We only have 1 entry, lets make it look like the normal Text of a Bigtext
4964 topPadding = mBuilder.mContext.getResources().getDimensionPixelSize(
4965 R.dimen.notification_text_margin_top);
4966 contentView.setViewPadding(onlyViewId, 0, topPadding, 0, 0);
4967 }
Selim Cinek1e0bf612015-11-20 15:57:26 -08004968
Daniel Sandler879c5e02012-04-17 16:46:51 -04004969 return contentView;
4970 }
Selim Cinek1e0bf612015-11-20 15:57:26 -08004971
Selim Cinek247fa012016-02-18 09:50:48 -08004972 private void handleInboxImageMargin(RemoteViews contentView, int id, boolean first) {
Selim Cinek1e0bf612015-11-20 15:57:26 -08004973 int endMargin = 0;
Selim Cinek247fa012016-02-18 09:50:48 -08004974 if (first) {
4975 final int max = mBuilder.mN.extras.getInt(EXTRA_PROGRESS_MAX, 0);
4976 final boolean ind = mBuilder.mN.extras.getBoolean(EXTRA_PROGRESS_INDETERMINATE);
4977 boolean hasProgress = max != 0 || ind;
4978 if (mBuilder.mN.mLargeIcon != null && !hasProgress) {
4979 endMargin = mBuilder.mContext.getResources().getDimensionPixelSize(
4980 R.dimen.notification_content_picture_margin);
4981 }
Selim Cinek1e0bf612015-11-20 15:57:26 -08004982 }
4983 contentView.setViewLayoutMarginEnd(id, endMargin);
4984 }
Daniel Sandler879c5e02012-04-17 16:46:51 -04004985 }
Dan Sandler842dd772014-05-15 09:36:47 -04004986
4987 /**
4988 * Notification style for media playback notifications.
4989 *
4990 * In the expanded form, {@link Notification#bigContentView}, up to 5
4991 * {@link Notification.Action}s specified with
Dan Sandler86647982015-05-13 23:41:13 -04004992 * {@link Notification.Builder#addAction(Action) addAction} will be
Dan Sandler842dd772014-05-15 09:36:47 -04004993 * shown as icon-only pushbuttons, suitable for transport controls. The Bitmap given to
4994 * {@link Notification.Builder#setLargeIcon(android.graphics.Bitmap) setLargeIcon()} will be
4995 * treated as album artwork.
4996 *
4997 * Unlike the other styles provided here, MediaStyle can also modify the standard-size
4998 * {@link Notification#contentView}; by providing action indices to
Christoph Studerfde6f4d2014-12-12 13:23:26 +01004999 * {@link #setShowActionsInCompactView(int...)} you can promote up to 3 actions to be displayed
Dan Sandler842dd772014-05-15 09:36:47 -04005000 * in the standard view alongside the usual content.
5001 *
Bryan Mawhinney6be8de32014-07-18 10:35:12 +01005002 * Notifications created with MediaStyle will have their category set to
5003 * {@link Notification#CATEGORY_TRANSPORT CATEGORY_TRANSPORT} unless you set a different
5004 * category using {@link Notification.Builder#setCategory(String) setCategory()}.
5005 *
Jeff Browndba34ba2014-06-24 20:46:03 -07005006 * Finally, if you attach a {@link android.media.session.MediaSession.Token} using
5007 * {@link android.app.Notification.MediaStyle#setMediaSession(MediaSession.Token)},
Dan Sandler842dd772014-05-15 09:36:47 -04005008 * the System UI can identify this as a notification representing an active media session
5009 * and respond accordingly (by showing album artwork in the lockscreen, for example).
5010 *
5011 * To use this style with your Notification, feed it to
5012 * {@link Notification.Builder#setStyle(android.app.Notification.Style)} like so:
5013 * <pre class="prettyprint">
5014 * Notification noti = new Notification.Builder()
5015 * .setSmallIcon(R.drawable.ic_stat_player)
Christoph Studere935fe92014-11-24 14:18:06 +01005016 * .setContentTitle(&quot;Track title&quot;)
5017 * .setContentText(&quot;Artist - Album&quot;)
5018 * .setLargeIcon(albumArtBitmap))
Bryan Mawhinney6be8de32014-07-18 10:35:12 +01005019 * .setStyle(<b>new Notification.MediaStyle()</b>
5020 * .setMediaSession(mySession))
Dan Sandler842dd772014-05-15 09:36:47 -04005021 * .build();
5022 * </pre>
5023 *
5024 * @see Notification#bigContentView
5025 */
5026 public static class MediaStyle extends Style {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02005027 static final int MAX_MEDIA_BUTTONS_IN_COMPACT = 3;
Dan Sandler842dd772014-05-15 09:36:47 -04005028 static final int MAX_MEDIA_BUTTONS = 5;
5029
5030 private int[] mActionsToShowInCompact = null;
Jeff Browndba34ba2014-06-24 20:46:03 -07005031 private MediaSession.Token mToken;
Dan Sandler842dd772014-05-15 09:36:47 -04005032
5033 public MediaStyle() {
5034 }
5035
Adrian Roosf5faf9d2016-05-23 13:56:15 -07005036 /**
5037 * @deprecated use {@code MediaStyle()}.
5038 */
5039 @Deprecated
Dan Sandler842dd772014-05-15 09:36:47 -04005040 public MediaStyle(Builder builder) {
5041 setBuilder(builder);
5042 }
5043
5044 /**
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02005045 * 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 -04005046 * notification view.
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02005047 *
5048 * @param actions the indices of the actions to show in the compact notification view
Dan Sandler842dd772014-05-15 09:36:47 -04005049 */
5050 public MediaStyle setShowActionsInCompactView(int...actions) {
5051 mActionsToShowInCompact = actions;
5052 return this;
5053 }
5054
5055 /**
Jeff Browndba34ba2014-06-24 20:46:03 -07005056 * Attach a {@link android.media.session.MediaSession.Token} to this Notification
5057 * to provide additional playback information and control to the SystemUI.
Dan Sandler842dd772014-05-15 09:36:47 -04005058 */
Jeff Browndba34ba2014-06-24 20:46:03 -07005059 public MediaStyle setMediaSession(MediaSession.Token token) {
Dan Sandler842dd772014-05-15 09:36:47 -04005060 mToken = token;
5061 return this;
5062 }
5063
Christoph Studer4600f9b2014-07-22 22:44:43 +02005064 /**
5065 * @hide
5066 */
Dan Sandler842dd772014-05-15 09:36:47 -04005067 @Override
5068 public Notification buildStyled(Notification wip) {
Christoph Studer4600f9b2014-07-22 22:44:43 +02005069 super.buildStyled(wip);
Bryan Mawhinney6be8de32014-07-18 10:35:12 +01005070 if (wip.category == null) {
5071 wip.category = Notification.CATEGORY_TRANSPORT;
5072 }
Dan Sandler842dd772014-05-15 09:36:47 -04005073 return wip;
5074 }
5075
Christoph Studer4600f9b2014-07-22 22:44:43 +02005076 /**
5077 * @hide
5078 */
5079 @Override
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005080 public RemoteViews makeContentView() {
5081 return makeMediaContentView();
Christoph Studer4600f9b2014-07-22 22:44:43 +02005082 }
5083
5084 /**
5085 * @hide
5086 */
5087 @Override
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005088 public RemoteViews makeBigContentView() {
5089 return makeMediaBigContentView();
Christoph Studer4600f9b2014-07-22 22:44:43 +02005090 }
5091
Selim Cinekcc10bfb2016-02-10 16:24:21 -08005092 /**
5093 * @hide
5094 */
5095 @Override
5096 public RemoteViews makeHeadsUpContentView() {
5097 RemoteViews expanded = makeMediaBigContentView();
5098 return expanded != null ? expanded : makeMediaContentView();
5099 }
5100
Dan Sandler842dd772014-05-15 09:36:47 -04005101 /** @hide */
5102 @Override
5103 public void addExtras(Bundle extras) {
5104 super.addExtras(extras);
5105
5106 if (mToken != null) {
5107 extras.putParcelable(EXTRA_MEDIA_SESSION, mToken);
5108 }
Bryan Mawhinneye191f902014-07-22 12:50:09 +01005109 if (mActionsToShowInCompact != null) {
5110 extras.putIntArray(EXTRA_COMPACT_ACTIONS, mActionsToShowInCompact);
5111 }
Dan Sandler842dd772014-05-15 09:36:47 -04005112 }
5113
Christoph Studer4600f9b2014-07-22 22:44:43 +02005114 /**
5115 * @hide
5116 */
5117 @Override
5118 protected void restoreFromExtras(Bundle extras) {
5119 super.restoreFromExtras(extras);
5120
5121 if (extras.containsKey(EXTRA_MEDIA_SESSION)) {
5122 mToken = extras.getParcelable(EXTRA_MEDIA_SESSION);
5123 }
5124 if (extras.containsKey(EXTRA_COMPACT_ACTIONS)) {
5125 mActionsToShowInCompact = extras.getIntArray(EXTRA_COMPACT_ACTIONS);
5126 }
5127 }
5128
Selim Cinek5bf069a2015-11-10 19:14:27 -05005129 private RemoteViews generateMediaActionButton(Action action, int color) {
Dan Sandler842dd772014-05-15 09:36:47 -04005130 final boolean tombstone = (action.actionIntent == null);
Selim Cinekf33b1112015-07-15 17:45:11 -07005131 RemoteViews button = new BuilderRemoteViews(mBuilder.mContext.getApplicationInfo(),
Alan Viverette3cb07a462014-06-06 14:19:53 -07005132 R.layout.notification_material_media_action);
Dan Sandler68079d52015-07-22 10:45:30 -04005133 button.setImageViewIcon(R.id.action0, action.getIcon());
Selim Cinek5bf069a2015-11-10 19:14:27 -05005134 button.setDrawableParameters(R.id.action0, false, -1, color, PorterDuff.Mode.SRC_ATOP,
5135 -1);
Dan Sandler842dd772014-05-15 09:36:47 -04005136 if (!tombstone) {
5137 button.setOnClickPendingIntent(R.id.action0, action.actionIntent);
5138 }
5139 button.setContentDescription(R.id.action0, action.title);
5140 return button;
5141 }
5142
5143 private RemoteViews makeMediaContentView() {
5144 RemoteViews view = mBuilder.applyStandardTemplate(
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02005145 R.layout.notification_template_material_media, false /* hasProgress */);
Dan Sandler842dd772014-05-15 09:36:47 -04005146
5147 final int numActions = mBuilder.mActions.size();
5148 final int N = mActionsToShowInCompact == null
5149 ? 0
5150 : Math.min(mActionsToShowInCompact.length, MAX_MEDIA_BUTTONS_IN_COMPACT);
5151 if (N > 0) {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02005152 view.removeAllViews(com.android.internal.R.id.media_actions);
Dan Sandler842dd772014-05-15 09:36:47 -04005153 for (int i = 0; i < N; i++) {
5154 if (i >= numActions) {
5155 throw new IllegalArgumentException(String.format(
5156 "setShowActionsInCompactView: action %d out of bounds (max %d)",
5157 i, numActions - 1));
5158 }
5159
5160 final Action action = mBuilder.mActions.get(mActionsToShowInCompact[i]);
Selim Cinek5bf069a2015-11-10 19:14:27 -05005161 final RemoteViews button = generateMediaActionButton(action,
Adrian Roos4ff3b122016-02-01 12:26:13 -08005162 mBuilder.resolveContrastColor());
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02005163 view.addView(com.android.internal.R.id.media_actions, button);
Dan Sandler842dd772014-05-15 09:36:47 -04005164 }
5165 }
Selim Cinekfdc738f2016-01-27 20:04:27 -08005166 handleImage(view);
5167 // handle the content margin
Selim Cinek247fa012016-02-18 09:50:48 -08005168 int endMargin = mBuilder.mContext.getResources().getDimensionPixelSize(
5169 R.dimen.notification_content_margin_end);;
Selim Cinekfdc738f2016-01-27 20:04:27 -08005170 if (mBuilder.mN.mLargeIcon != null) {
Selim Cinek247fa012016-02-18 09:50:48 -08005171 endMargin += mBuilder.mContext.getResources().getDimensionPixelSize(
5172 R.dimen.notification_content_picture_margin);
Selim Cinekfdc738f2016-01-27 20:04:27 -08005173 }
5174 view.setViewLayoutMarginEnd(R.id.notification_main_column, endMargin);
Dan Sandler842dd772014-05-15 09:36:47 -04005175 return view;
5176 }
5177
5178 private RemoteViews makeMediaBigContentView() {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02005179 final int actionCount = Math.min(mBuilder.mActions.size(), MAX_MEDIA_BUTTONS);
Selim Cinekcc10bfb2016-02-10 16:24:21 -08005180 // Dont add an expanded view if there is no more content to be revealed
5181 int actionsInCompact = mActionsToShowInCompact == null
5182 ? 0
5183 : Math.min(mActionsToShowInCompact.length, MAX_MEDIA_BUTTONS_IN_COMPACT);
5184 if (mBuilder.mN.mLargeIcon == null && actionCount <= actionsInCompact) {
5185 return null;
5186 }
Selim Cinek5bf069a2015-11-10 19:14:27 -05005187 RemoteViews big = mBuilder.applyStandardTemplate(
5188 R.layout.notification_template_material_big_media,
5189 false);
Dan Sandler842dd772014-05-15 09:36:47 -04005190
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02005191 if (actionCount > 0) {
5192 big.removeAllViews(com.android.internal.R.id.media_actions);
5193 for (int i = 0; i < actionCount; i++) {
Selim Cinek5bf069a2015-11-10 19:14:27 -05005194 final RemoteViews button = generateMediaActionButton(mBuilder.mActions.get(i),
Adrian Roos4ff3b122016-02-01 12:26:13 -08005195 mBuilder.resolveContrastColor());
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02005196 big.addView(com.android.internal.R.id.media_actions, button);
Dan Sandler842dd772014-05-15 09:36:47 -04005197 }
5198 }
Selim Cinek5bf069a2015-11-10 19:14:27 -05005199 handleImage(big);
Dan Sandler842dd772014-05-15 09:36:47 -04005200 return big;
5201 }
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02005202
Selim Cinek5bf069a2015-11-10 19:14:27 -05005203 private void handleImage(RemoteViews contentView) {
5204 if (mBuilder.mN.mLargeIcon != null) {
5205 contentView.setViewLayoutMarginEnd(R.id.line1, 0);
Selim Cinekc848c3a2016-01-13 15:27:30 -08005206 contentView.setViewLayoutMarginEnd(R.id.text, 0);
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02005207 }
5208 }
5209
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02005210 /**
5211 * @hide
5212 */
5213 @Override
5214 protected boolean hasProgress() {
5215 return false;
5216 }
Dan Sandler842dd772014-05-15 09:36:47 -04005217 }
Griff Hazen61a9e862014-05-22 16:05:19 -07005218
Selim Cinek593610c2016-02-16 18:42:57 -08005219 /**
5220 * Notification style for custom views that are decorated by the system
5221 *
5222 * <p>Instead of providing a notification that is completely custom, a developer can set this
5223 * style and still obtain system decorations like the notification header with the expand
5224 * affordance and actions.
5225 *
5226 * <p>Use {@link android.app.Notification.Builder#setCustomContentView(RemoteViews)},
5227 * {@link android.app.Notification.Builder#setCustomBigContentView(RemoteViews)} and
5228 * {@link android.app.Notification.Builder#setCustomHeadsUpContentView(RemoteViews)} to set the
5229 * corresponding custom views to display.
5230 *
5231 * To use this style with your Notification, feed it to
5232 * {@link Notification.Builder#setStyle(android.app.Notification.Style)} like so:
5233 * <pre class="prettyprint">
5234 * Notification noti = new Notification.Builder()
5235 * .setSmallIcon(R.drawable.ic_stat_player)
5236 * .setLargeIcon(albumArtBitmap))
5237 * .setCustomContentView(contentView);
5238 * .setStyle(<b>new Notification.DecoratedCustomViewStyle()</b>)
5239 * .build();
5240 * </pre>
5241 */
5242 public static class DecoratedCustomViewStyle extends Style {
5243
5244 public DecoratedCustomViewStyle() {
5245 }
5246
Selim Cinek593610c2016-02-16 18:42:57 -08005247 /**
5248 * @hide
5249 */
5250 public boolean displayCustomViewInline() {
5251 return true;
5252 }
5253
5254 /**
5255 * @hide
5256 */
5257 @Override
5258 public RemoteViews makeContentView() {
5259 return makeStandardTemplateWithCustomContent(mBuilder.mN.contentView);
5260 }
5261
5262 /**
5263 * @hide
5264 */
5265 @Override
5266 public RemoteViews makeBigContentView() {
5267 return makeDecoratedBigContentView();
5268 }
5269
5270 /**
5271 * @hide
5272 */
5273 @Override
5274 public RemoteViews makeHeadsUpContentView() {
5275 return makeDecoratedHeadsUpContentView();
5276 }
5277
Selim Cinek593610c2016-02-16 18:42:57 -08005278 private RemoteViews makeDecoratedHeadsUpContentView() {
5279 RemoteViews headsUpContentView = mBuilder.mN.headsUpContentView == null
5280 ? mBuilder.mN.contentView
5281 : mBuilder.mN.headsUpContentView;
5282 if (mBuilder.mActions.size() == 0) {
5283 return makeStandardTemplateWithCustomContent(headsUpContentView);
5284 }
5285 RemoteViews remoteViews = mBuilder.applyStandardTemplateWithActions(
5286 mBuilder.getBigBaseLayoutResource());
Selim Cinek247fa012016-02-18 09:50:48 -08005287 buildIntoRemoteViewContent(remoteViews, headsUpContentView);
Selim Cinek593610c2016-02-16 18:42:57 -08005288 return remoteViews;
5289 }
5290
Selim Cinek593610c2016-02-16 18:42:57 -08005291 private RemoteViews makeStandardTemplateWithCustomContent(RemoteViews customContent) {
5292 RemoteViews remoteViews = mBuilder.applyStandardTemplate(
5293 mBuilder.getBaseLayoutResource());
Selim Cinek247fa012016-02-18 09:50:48 -08005294 buildIntoRemoteViewContent(remoteViews, customContent);
Selim Cinek593610c2016-02-16 18:42:57 -08005295 return remoteViews;
5296 }
5297
Selim Cinek593610c2016-02-16 18:42:57 -08005298 private RemoteViews makeDecoratedBigContentView() {
5299 RemoteViews bigContentView = mBuilder.mN.bigContentView == null
5300 ? mBuilder.mN.contentView
5301 : mBuilder.mN.bigContentView;
5302 if (mBuilder.mActions.size() == 0) {
5303 return makeStandardTemplateWithCustomContent(bigContentView);
5304 }
5305 RemoteViews remoteViews = mBuilder.applyStandardTemplateWithActions(
5306 mBuilder.getBigBaseLayoutResource());
Selim Cinek247fa012016-02-18 09:50:48 -08005307 buildIntoRemoteViewContent(remoteViews, bigContentView);
Selim Cinek593610c2016-02-16 18:42:57 -08005308 return remoteViews;
5309 }
Selim Cinek247fa012016-02-18 09:50:48 -08005310
5311 private void buildIntoRemoteViewContent(RemoteViews remoteViews,
5312 RemoteViews customContent) {
Adrian Roos5081c0d2016-02-26 16:04:19 -08005313 if (customContent != null) {
Selim Cinekf91017e2016-03-14 12:25:09 -07005314 // Need to clone customContent before adding, because otherwise it can no longer be
5315 // parceled independently of remoteViews.
Adrian Roos5081c0d2016-02-26 16:04:19 -08005316 customContent = customContent.clone();
Selim Cinekf91017e2016-03-14 12:25:09 -07005317 remoteViews.removeAllViews(R.id.notification_main_column);
5318 remoteViews.addView(R.id.notification_main_column, customContent);
Adrian Roos5081c0d2016-02-26 16:04:19 -08005319 }
Selim Cinek247fa012016-02-18 09:50:48 -08005320 // also update the end margin if there is an image
5321 int endMargin = mBuilder.mContext.getResources().getDimensionPixelSize(
5322 R.dimen.notification_content_margin_end);
5323 if (mBuilder.mN.mLargeIcon != null) {
5324 endMargin += mBuilder.mContext.getResources().getDimensionPixelSize(
5325 R.dimen.notification_content_picture_margin);
5326 }
5327 remoteViews.setViewLayoutMarginEnd(R.id.notification_main_column, endMargin);
5328 }
Selim Cinek593610c2016-02-16 18:42:57 -08005329 }
5330
Selim Cinek03eb3b72016-02-18 10:39:45 -08005331 /**
5332 * Notification style for media custom views that are decorated by the system
5333 *
5334 * <p>Instead of providing a media notification that is completely custom, a developer can set
5335 * this style and still obtain system decorations like the notification header with the expand
5336 * affordance and actions.
5337 *
5338 * <p>Use {@link android.app.Notification.Builder#setCustomContentView(RemoteViews)},
5339 * {@link android.app.Notification.Builder#setCustomBigContentView(RemoteViews)} and
5340 * {@link android.app.Notification.Builder#setCustomHeadsUpContentView(RemoteViews)} to set the
5341 * corresponding custom views to display.
5342 *
5343 * To use this style with your Notification, feed it to
5344 * {@link Notification.Builder#setStyle(android.app.Notification.Style)} like so:
5345 * <pre class="prettyprint">
5346 * Notification noti = new Notification.Builder()
5347 * .setSmallIcon(R.drawable.ic_stat_player)
5348 * .setLargeIcon(albumArtBitmap))
5349 * .setCustomContentView(contentView);
5350 * .setStyle(<b>new Notification.DecoratedMediaCustomViewStyle()</b>
5351 * .setMediaSession(mySession))
5352 * .build();
5353 * </pre>
5354 *
5355 * @see android.app.Notification.DecoratedCustomViewStyle
5356 * @see android.app.Notification.MediaStyle
5357 */
5358 public static class DecoratedMediaCustomViewStyle extends MediaStyle {
5359
5360 public DecoratedMediaCustomViewStyle() {
5361 }
5362
Selim Cinek03eb3b72016-02-18 10:39:45 -08005363 /**
5364 * @hide
5365 */
5366 public boolean displayCustomViewInline() {
5367 return true;
5368 }
5369
5370 /**
5371 * @hide
5372 */
5373 @Override
5374 public RemoteViews makeContentView() {
5375 RemoteViews remoteViews = super.makeContentView();
5376 return buildIntoRemoteView(remoteViews, R.id.notification_content_container,
5377 mBuilder.mN.contentView);
5378 }
5379
5380 /**
5381 * @hide
5382 */
5383 @Override
5384 public RemoteViews makeBigContentView() {
5385 RemoteViews customRemoteView = mBuilder.mN.bigContentView != null
5386 ? mBuilder.mN.bigContentView
5387 : mBuilder.mN.contentView;
5388 return makeBigContentViewWithCustomContent(customRemoteView);
5389 }
5390
5391 private RemoteViews makeBigContentViewWithCustomContent(RemoteViews customRemoteView) {
5392 RemoteViews remoteViews = super.makeBigContentView();
5393 if (remoteViews != null) {
5394 return buildIntoRemoteView(remoteViews, R.id.notification_main_column,
5395 customRemoteView);
5396 } else if (customRemoteView != mBuilder.mN.contentView){
5397 remoteViews = super.makeContentView();
5398 return buildIntoRemoteView(remoteViews, R.id.notification_content_container,
5399 customRemoteView);
5400 } else {
5401 return null;
5402 }
5403 }
5404
5405 /**
5406 * @hide
5407 */
5408 @Override
5409 public RemoteViews makeHeadsUpContentView() {
5410 RemoteViews customRemoteView = mBuilder.mN.headsUpContentView != null
5411 ? mBuilder.mN.headsUpContentView
5412 : mBuilder.mN.contentView;
5413 return makeBigContentViewWithCustomContent(customRemoteView);
5414 }
5415
5416 private RemoteViews buildIntoRemoteView(RemoteViews remoteViews, int id,
5417 RemoteViews customContent) {
Adrian Roos5081c0d2016-02-26 16:04:19 -08005418 if (customContent != null) {
Selim Cinekf91017e2016-03-14 12:25:09 -07005419 // Need to clone customContent before adding, because otherwise it can no longer be
5420 // parceled independently of remoteViews.
Adrian Roos5081c0d2016-02-26 16:04:19 -08005421 customContent = customContent.clone();
Selim Cinekf91017e2016-03-14 12:25:09 -07005422 remoteViews.removeAllViews(id);
5423 remoteViews.addView(id, customContent);
Adrian Roos5081c0d2016-02-26 16:04:19 -08005424 }
Selim Cinek03eb3b72016-02-18 10:39:45 -08005425 return remoteViews;
5426 }
5427 }
5428
Christoph Studer4600f9b2014-07-22 22:44:43 +02005429 // When adding a new Style subclass here, don't forget to update
5430 // Builder.getNotificationStyleClass.
5431
Griff Hazen61a9e862014-05-22 16:05:19 -07005432 /**
5433 * Extender interface for use with {@link Builder#extend}. Extenders may be used to add
5434 * metadata or change options on a notification builder.
5435 */
5436 public interface Extender {
5437 /**
5438 * Apply this extender to a notification builder.
5439 * @param builder the builder to be modified.
5440 * @return the build object for chaining.
5441 */
5442 public Builder extend(Builder builder);
5443 }
5444
5445 /**
5446 * Helper class to add wearable extensions to notifications.
5447 * <p class="note"> See
5448 * <a href="{@docRoot}wear/notifications/creating.html">Creating Notifications
5449 * for Android Wear</a> for more information on how to use this class.
5450 * <p>
5451 * To create a notification with wearable extensions:
5452 * <ol>
5453 * <li>Create a {@link android.app.Notification.Builder}, setting any desired
5454 * properties.
5455 * <li>Create a {@link android.app.Notification.WearableExtender}.
5456 * <li>Set wearable-specific properties using the
5457 * {@code add} and {@code set} methods of {@link android.app.Notification.WearableExtender}.
5458 * <li>Call {@link android.app.Notification.Builder#extend} to apply the extensions to a
5459 * notification.
5460 * <li>Post the notification to the notification system with the
5461 * {@code NotificationManager.notify(...)} methods.
5462 * </ol>
5463 *
5464 * <pre class="prettyprint">
5465 * Notification notif = new Notification.Builder(mContext)
5466 * .setContentTitle(&quot;New mail from &quot; + sender.toString())
5467 * .setContentText(subject)
5468 * .setSmallIcon(R.drawable.new_mail)
5469 * .extend(new Notification.WearableExtender()
5470 * .setContentIcon(R.drawable.new_mail))
5471 * .build();
5472 * NotificationManager notificationManger =
5473 * (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
5474 * notificationManger.notify(0, notif);</pre>
5475 *
5476 * <p>Wearable extensions can be accessed on an existing notification by using the
5477 * {@code WearableExtender(Notification)} constructor,
5478 * and then using the {@code get} methods to access values.
5479 *
5480 * <pre class="prettyprint">
5481 * Notification.WearableExtender wearableExtender = new Notification.WearableExtender(
5482 * notification);
Griff Hazen14f57992014-05-26 09:07:14 -07005483 * List&lt;Notification&gt; pages = wearableExtender.getPages();</pre>
Griff Hazen61a9e862014-05-22 16:05:19 -07005484 */
5485 public static final class WearableExtender implements Extender {
5486 /**
5487 * Sentinel value for an action index that is unset.
5488 */
5489 public static final int UNSET_ACTION_INDEX = -1;
5490
5491 /**
5492 * Size value for use with {@link #setCustomSizePreset} to show this notification with
5493 * default sizing.
5494 * <p>For custom display notifications created using {@link #setDisplayIntent},
Paul Soulosaa4f4bf2015-08-04 11:59:45 -07005495 * the default is {@link #SIZE_MEDIUM}. All other notifications size automatically based
Griff Hazen61a9e862014-05-22 16:05:19 -07005496 * on their content.
5497 */
5498 public static final int SIZE_DEFAULT = 0;
5499
5500 /**
5501 * Size value for use with {@link #setCustomSizePreset} to show this notification
5502 * with an extra small size.
5503 * <p>This value is only applicable for custom display notifications created using
5504 * {@link #setDisplayIntent}.
5505 */
5506 public static final int SIZE_XSMALL = 1;
5507
5508 /**
5509 * Size value for use with {@link #setCustomSizePreset} to show this notification
5510 * with a small size.
5511 * <p>This value is only applicable for custom display notifications created using
5512 * {@link #setDisplayIntent}.
5513 */
5514 public static final int SIZE_SMALL = 2;
5515
5516 /**
5517 * Size value for use with {@link #setCustomSizePreset} to show this notification
5518 * with a medium size.
5519 * <p>This value is only applicable for custom display notifications created using
5520 * {@link #setDisplayIntent}.
5521 */
5522 public static final int SIZE_MEDIUM = 3;
5523
5524 /**
5525 * Size value for use with {@link #setCustomSizePreset} to show this notification
5526 * with a large size.
5527 * <p>This value is only applicable for custom display notifications created using
5528 * {@link #setDisplayIntent}.
5529 */
5530 public static final int SIZE_LARGE = 4;
5531
Griff Hazend5f11f92014-05-27 15:40:09 -07005532 /**
5533 * Size value for use with {@link #setCustomSizePreset} to show this notification
5534 * full screen.
5535 * <p>This value is only applicable for custom display notifications created using
5536 * {@link #setDisplayIntent}.
5537 */
5538 public static final int SIZE_FULL_SCREEN = 5;
5539
Griff Hazen5f2edfc2014-09-29 16:28:44 -07005540 /**
5541 * Sentinel value for use with {@link #setHintScreenTimeout} to keep the screen on for a
5542 * short amount of time when this notification is displayed on the screen. This
5543 * is the default value.
5544 */
5545 public static final int SCREEN_TIMEOUT_SHORT = 0;
5546
5547 /**
5548 * Sentinel value for use with {@link #setHintScreenTimeout} to keep the screen on
5549 * for a longer amount of time when this notification is displayed on the screen.
5550 */
5551 public static final int SCREEN_TIMEOUT_LONG = -1;
5552
Griff Hazen61a9e862014-05-22 16:05:19 -07005553 /** Notification extra which contains wearable extensions */
5554 private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS";
5555
Pete Gastaf6781d2014-10-07 15:17:05 -04005556 // Keys within EXTRA_WEARABLE_EXTENSIONS for wearable options.
Griff Hazen61a9e862014-05-22 16:05:19 -07005557 private static final String KEY_ACTIONS = "actions";
5558 private static final String KEY_FLAGS = "flags";
5559 private static final String KEY_DISPLAY_INTENT = "displayIntent";
5560 private static final String KEY_PAGES = "pages";
5561 private static final String KEY_BACKGROUND = "background";
5562 private static final String KEY_CONTENT_ICON = "contentIcon";
5563 private static final String KEY_CONTENT_ICON_GRAVITY = "contentIconGravity";
5564 private static final String KEY_CONTENT_ACTION_INDEX = "contentActionIndex";
5565 private static final String KEY_CUSTOM_SIZE_PRESET = "customSizePreset";
5566 private static final String KEY_CUSTOM_CONTENT_HEIGHT = "customContentHeight";
5567 private static final String KEY_GRAVITY = "gravity";
Griff Hazen5f2edfc2014-09-29 16:28:44 -07005568 private static final String KEY_HINT_SCREEN_TIMEOUT = "hintScreenTimeout";
Nadia Benbernou948627e2016-04-14 14:41:08 -04005569 private static final String KEY_DISMISSAL_ID = "dismissalId";
Griff Hazen61a9e862014-05-22 16:05:19 -07005570
5571 // Flags bitwise-ored to mFlags
5572 private static final int FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE = 0x1;
5573 private static final int FLAG_HINT_HIDE_ICON = 1 << 1;
5574 private static final int FLAG_HINT_SHOW_BACKGROUND_ONLY = 1 << 2;
5575 private static final int FLAG_START_SCROLL_BOTTOM = 1 << 3;
Griff Hazen5f2edfc2014-09-29 16:28:44 -07005576 private static final int FLAG_HINT_AVOID_BACKGROUND_CLIPPING = 1 << 4;
Alex Hills4bcb06b2016-04-05 14:26:25 -04005577 private static final int FLAG_BIG_PICTURE_AMBIENT = 1 << 5;
Alex Hills9ab3a232016-04-05 14:54:56 -04005578 private static final int FLAG_HINT_CONTENT_INTENT_LAUNCHES_ACTIVITY = 1 << 6;
Griff Hazen61a9e862014-05-22 16:05:19 -07005579
5580 // Default value for flags integer
5581 private static final int DEFAULT_FLAGS = FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE;
5582
5583 private static final int DEFAULT_CONTENT_ICON_GRAVITY = Gravity.END;
5584 private static final int DEFAULT_GRAVITY = Gravity.BOTTOM;
5585
5586 private ArrayList<Action> mActions = new ArrayList<Action>();
5587 private int mFlags = DEFAULT_FLAGS;
5588 private PendingIntent mDisplayIntent;
5589 private ArrayList<Notification> mPages = new ArrayList<Notification>();
5590 private Bitmap mBackground;
5591 private int mContentIcon;
5592 private int mContentIconGravity = DEFAULT_CONTENT_ICON_GRAVITY;
5593 private int mContentActionIndex = UNSET_ACTION_INDEX;
5594 private int mCustomSizePreset = SIZE_DEFAULT;
5595 private int mCustomContentHeight;
5596 private int mGravity = DEFAULT_GRAVITY;
Griff Hazen5f2edfc2014-09-29 16:28:44 -07005597 private int mHintScreenTimeout;
Nadia Benbernou948627e2016-04-14 14:41:08 -04005598 private String mDismissalId;
Griff Hazen61a9e862014-05-22 16:05:19 -07005599
5600 /**
5601 * Create a {@link android.app.Notification.WearableExtender} with default
5602 * options.
5603 */
5604 public WearableExtender() {
5605 }
5606
5607 public WearableExtender(Notification notif) {
5608 Bundle wearableBundle = notif.extras.getBundle(EXTRA_WEARABLE_EXTENSIONS);
5609 if (wearableBundle != null) {
5610 List<Action> actions = wearableBundle.getParcelableArrayList(KEY_ACTIONS);
5611 if (actions != null) {
5612 mActions.addAll(actions);
5613 }
5614
5615 mFlags = wearableBundle.getInt(KEY_FLAGS, DEFAULT_FLAGS);
5616 mDisplayIntent = wearableBundle.getParcelable(KEY_DISPLAY_INTENT);
5617
5618 Notification[] pages = getNotificationArrayFromBundle(
5619 wearableBundle, KEY_PAGES);
5620 if (pages != null) {
5621 Collections.addAll(mPages, pages);
5622 }
5623
5624 mBackground = wearableBundle.getParcelable(KEY_BACKGROUND);
5625 mContentIcon = wearableBundle.getInt(KEY_CONTENT_ICON);
5626 mContentIconGravity = wearableBundle.getInt(KEY_CONTENT_ICON_GRAVITY,
5627 DEFAULT_CONTENT_ICON_GRAVITY);
5628 mContentActionIndex = wearableBundle.getInt(KEY_CONTENT_ACTION_INDEX,
5629 UNSET_ACTION_INDEX);
5630 mCustomSizePreset = wearableBundle.getInt(KEY_CUSTOM_SIZE_PRESET,
5631 SIZE_DEFAULT);
5632 mCustomContentHeight = wearableBundle.getInt(KEY_CUSTOM_CONTENT_HEIGHT);
5633 mGravity = wearableBundle.getInt(KEY_GRAVITY, DEFAULT_GRAVITY);
Griff Hazen5f2edfc2014-09-29 16:28:44 -07005634 mHintScreenTimeout = wearableBundle.getInt(KEY_HINT_SCREEN_TIMEOUT);
Nadia Benbernou948627e2016-04-14 14:41:08 -04005635 mDismissalId = wearableBundle.getString(KEY_DISMISSAL_ID);
Griff Hazen61a9e862014-05-22 16:05:19 -07005636 }
5637 }
5638
5639 /**
5640 * Apply wearable extensions to a notification that is being built. This is typically
5641 * called by the {@link android.app.Notification.Builder#extend} method of
5642 * {@link android.app.Notification.Builder}.
5643 */
5644 @Override
5645 public Notification.Builder extend(Notification.Builder builder) {
5646 Bundle wearableBundle = new Bundle();
5647
5648 if (!mActions.isEmpty()) {
5649 wearableBundle.putParcelableArrayList(KEY_ACTIONS, mActions);
5650 }
5651 if (mFlags != DEFAULT_FLAGS) {
5652 wearableBundle.putInt(KEY_FLAGS, mFlags);
5653 }
5654 if (mDisplayIntent != null) {
5655 wearableBundle.putParcelable(KEY_DISPLAY_INTENT, mDisplayIntent);
5656 }
5657 if (!mPages.isEmpty()) {
5658 wearableBundle.putParcelableArray(KEY_PAGES, mPages.toArray(
5659 new Notification[mPages.size()]));
5660 }
5661 if (mBackground != null) {
5662 wearableBundle.putParcelable(KEY_BACKGROUND, mBackground);
5663 }
5664 if (mContentIcon != 0) {
5665 wearableBundle.putInt(KEY_CONTENT_ICON, mContentIcon);
5666 }
5667 if (mContentIconGravity != DEFAULT_CONTENT_ICON_GRAVITY) {
5668 wearableBundle.putInt(KEY_CONTENT_ICON_GRAVITY, mContentIconGravity);
5669 }
5670 if (mContentActionIndex != UNSET_ACTION_INDEX) {
5671 wearableBundle.putInt(KEY_CONTENT_ACTION_INDEX,
5672 mContentActionIndex);
5673 }
5674 if (mCustomSizePreset != SIZE_DEFAULT) {
5675 wearableBundle.putInt(KEY_CUSTOM_SIZE_PRESET, mCustomSizePreset);
5676 }
5677 if (mCustomContentHeight != 0) {
5678 wearableBundle.putInt(KEY_CUSTOM_CONTENT_HEIGHT, mCustomContentHeight);
5679 }
5680 if (mGravity != DEFAULT_GRAVITY) {
5681 wearableBundle.putInt(KEY_GRAVITY, mGravity);
5682 }
Griff Hazen5f2edfc2014-09-29 16:28:44 -07005683 if (mHintScreenTimeout != 0) {
5684 wearableBundle.putInt(KEY_HINT_SCREEN_TIMEOUT, mHintScreenTimeout);
5685 }
Nadia Benbernou948627e2016-04-14 14:41:08 -04005686 if (mDismissalId != null) {
5687 wearableBundle.putString(KEY_DISMISSAL_ID, mDismissalId);
5688 }
Griff Hazen61a9e862014-05-22 16:05:19 -07005689
5690 builder.getExtras().putBundle(EXTRA_WEARABLE_EXTENSIONS, wearableBundle);
5691 return builder;
5692 }
5693
5694 @Override
5695 public WearableExtender clone() {
5696 WearableExtender that = new WearableExtender();
5697 that.mActions = new ArrayList<Action>(this.mActions);
5698 that.mFlags = this.mFlags;
5699 that.mDisplayIntent = this.mDisplayIntent;
5700 that.mPages = new ArrayList<Notification>(this.mPages);
5701 that.mBackground = this.mBackground;
5702 that.mContentIcon = this.mContentIcon;
5703 that.mContentIconGravity = this.mContentIconGravity;
5704 that.mContentActionIndex = this.mContentActionIndex;
5705 that.mCustomSizePreset = this.mCustomSizePreset;
5706 that.mCustomContentHeight = this.mCustomContentHeight;
5707 that.mGravity = this.mGravity;
Griff Hazen5f2edfc2014-09-29 16:28:44 -07005708 that.mHintScreenTimeout = this.mHintScreenTimeout;
Nadia Benbernou948627e2016-04-14 14:41:08 -04005709 that.mDismissalId = this.mDismissalId;
Griff Hazen61a9e862014-05-22 16:05:19 -07005710 return that;
5711 }
5712
5713 /**
5714 * Add a wearable action to this notification.
5715 *
5716 * <p>When wearable actions are added using this method, the set of actions that
5717 * show on a wearable device splits from devices that only show actions added
5718 * using {@link android.app.Notification.Builder#addAction}. This allows for customization
5719 * of which actions display on different devices.
5720 *
5721 * @param action the action to add to this notification
5722 * @return this object for method chaining
5723 * @see android.app.Notification.Action
5724 */
5725 public WearableExtender addAction(Action action) {
5726 mActions.add(action);
5727 return this;
5728 }
5729
5730 /**
5731 * Adds wearable actions to this notification.
5732 *
5733 * <p>When wearable actions are added using this method, the set of actions that
5734 * show on a wearable device splits from devices that only show actions added
5735 * using {@link android.app.Notification.Builder#addAction}. This allows for customization
5736 * of which actions display on different devices.
5737 *
5738 * @param actions the actions to add to this notification
5739 * @return this object for method chaining
5740 * @see android.app.Notification.Action
5741 */
5742 public WearableExtender addActions(List<Action> actions) {
5743 mActions.addAll(actions);
5744 return this;
5745 }
5746
5747 /**
5748 * Clear all wearable actions present on this builder.
5749 * @return this object for method chaining.
5750 * @see #addAction
5751 */
5752 public WearableExtender clearActions() {
5753 mActions.clear();
5754 return this;
5755 }
5756
5757 /**
5758 * Get the wearable actions present on this notification.
5759 */
5760 public List<Action> getActions() {
5761 return mActions;
5762 }
5763
5764 /**
5765 * Set an intent to launch inside of an activity view when displaying
Griff Hazen14f57992014-05-26 09:07:14 -07005766 * this notification. The {@link PendingIntent} provided should be for an activity.
5767 *
5768 * <pre class="prettyprint">
5769 * Intent displayIntent = new Intent(context, MyDisplayActivity.class);
5770 * PendingIntent displayPendingIntent = PendingIntent.getActivity(context,
5771 * 0, displayIntent, PendingIntent.FLAG_UPDATE_CURRENT);
5772 * Notification notif = new Notification.Builder(context)
5773 * .extend(new Notification.WearableExtender()
5774 * .setDisplayIntent(displayPendingIntent)
5775 * .setCustomSizePreset(Notification.WearableExtender.SIZE_MEDIUM))
5776 * .build();</pre>
5777 *
5778 * <p>The activity to launch needs to allow embedding, must be exported, and
Griff Hazen831ca9d2014-06-17 00:38:38 -07005779 * should have an empty task affinity. It is also recommended to use the device
5780 * default light theme.
Griff Hazen14f57992014-05-26 09:07:14 -07005781 *
5782 * <p>Example AndroidManifest.xml entry:
5783 * <pre class="prettyprint">
5784 * &lt;activity android:name=&quot;com.example.MyDisplayActivity&quot;
5785 * android:exported=&quot;true&quot;
5786 * android:allowEmbedded=&quot;true&quot;
Griff Hazen831ca9d2014-06-17 00:38:38 -07005787 * android:taskAffinity=&quot;&quot;
5788 * android:theme=&quot;@android:style/Theme.DeviceDefault.Light&quot; /&gt;</pre>
Griff Hazen61a9e862014-05-22 16:05:19 -07005789 *
5790 * @param intent the {@link PendingIntent} for an activity
5791 * @return this object for method chaining
5792 * @see android.app.Notification.WearableExtender#getDisplayIntent
5793 */
5794 public WearableExtender setDisplayIntent(PendingIntent intent) {
5795 mDisplayIntent = intent;
5796 return this;
5797 }
5798
5799 /**
5800 * Get the intent to launch inside of an activity view when displaying this
5801 * notification. This {@code PendingIntent} should be for an activity.
5802 */
5803 public PendingIntent getDisplayIntent() {
5804 return mDisplayIntent;
5805 }
5806
5807 /**
5808 * Add an additional page of content to display with this notification. The current
5809 * notification forms the first page, and pages added using this function form
5810 * subsequent pages. This field can be used to separate a notification into multiple
5811 * sections.
5812 *
5813 * @param page the notification to add as another page
5814 * @return this object for method chaining
5815 * @see android.app.Notification.WearableExtender#getPages
5816 */
5817 public WearableExtender addPage(Notification page) {
5818 mPages.add(page);
5819 return this;
5820 }
5821
5822 /**
5823 * Add additional pages of content to display with this notification. The current
5824 * notification forms the first page, and pages added using this function form
5825 * subsequent pages. This field can be used to separate a notification into multiple
5826 * sections.
5827 *
5828 * @param pages a list of notifications
5829 * @return this object for method chaining
5830 * @see android.app.Notification.WearableExtender#getPages
5831 */
5832 public WearableExtender addPages(List<Notification> pages) {
5833 mPages.addAll(pages);
5834 return this;
5835 }
5836
5837 /**
5838 * Clear all additional pages present on this builder.
5839 * @return this object for method chaining.
5840 * @see #addPage
5841 */
5842 public WearableExtender clearPages() {
5843 mPages.clear();
5844 return this;
5845 }
5846
5847 /**
5848 * Get the array of additional pages of content for displaying this notification. The
5849 * current notification forms the first page, and elements within this array form
5850 * subsequent pages. This field can be used to separate a notification into multiple
5851 * sections.
5852 * @return the pages for this notification
5853 */
5854 public List<Notification> getPages() {
5855 return mPages;
5856 }
5857
5858 /**
5859 * Set a background image to be displayed behind the notification content.
5860 * Contrary to the {@link android.app.Notification.BigPictureStyle}, this background
5861 * will work with any notification style.
5862 *
5863 * @param background the background bitmap
5864 * @return this object for method chaining
5865 * @see android.app.Notification.WearableExtender#getBackground
5866 */
5867 public WearableExtender setBackground(Bitmap background) {
5868 mBackground = background;
5869 return this;
5870 }
5871
5872 /**
5873 * Get a background image to be displayed behind the notification content.
5874 * Contrary to the {@link android.app.Notification.BigPictureStyle}, this background
5875 * will work with any notification style.
5876 *
5877 * @return the background image
5878 * @see android.app.Notification.WearableExtender#setBackground
5879 */
5880 public Bitmap getBackground() {
5881 return mBackground;
5882 }
5883
5884 /**
5885 * Set an icon that goes with the content of this notification.
5886 */
5887 public WearableExtender setContentIcon(int icon) {
5888 mContentIcon = icon;
5889 return this;
5890 }
5891
5892 /**
5893 * Get an icon that goes with the content of this notification.
5894 */
5895 public int getContentIcon() {
5896 return mContentIcon;
5897 }
5898
5899 /**
5900 * Set the gravity that the content icon should have within the notification display.
5901 * Supported values include {@link android.view.Gravity#START} and
5902 * {@link android.view.Gravity#END}. The default value is {@link android.view.Gravity#END}.
5903 * @see #setContentIcon
5904 */
5905 public WearableExtender setContentIconGravity(int contentIconGravity) {
5906 mContentIconGravity = contentIconGravity;
5907 return this;
5908 }
5909
5910 /**
5911 * Get the gravity that the content icon should have within the notification display.
5912 * Supported values include {@link android.view.Gravity#START} and
5913 * {@link android.view.Gravity#END}. The default value is {@link android.view.Gravity#END}.
5914 * @see #getContentIcon
5915 */
5916 public int getContentIconGravity() {
5917 return mContentIconGravity;
5918 }
5919
5920 /**
5921 * Set an action from this notification's actions to be clickable with the content of
Griff Hazen14f57992014-05-26 09:07:14 -07005922 * this notification. This action will no longer display separately from the
5923 * notification's content.
5924 *
Griff Hazenca48d352014-05-28 22:37:13 -07005925 * <p>For notifications with multiple pages, child pages can also have content actions
Griff Hazen14f57992014-05-26 09:07:14 -07005926 * set, although the list of available actions comes from the main notification and not
5927 * from the child page's notification.
5928 *
5929 * @param actionIndex The index of the action to hoist onto the current notification page.
5930 * If wearable actions were added to the main notification, this index
5931 * will apply to that list, otherwise it will apply to the regular
5932 * actions list.
Griff Hazen61a9e862014-05-22 16:05:19 -07005933 */
5934 public WearableExtender setContentAction(int actionIndex) {
5935 mContentActionIndex = actionIndex;
5936 return this;
5937 }
5938
5939 /**
Griff Hazenca48d352014-05-28 22:37:13 -07005940 * Get the index of the notification action, if any, that was specified as being clickable
5941 * with the content of this notification. This action will no longer display separately
Griff Hazen14f57992014-05-26 09:07:14 -07005942 * from the notification's content.
Griff Hazen61a9e862014-05-22 16:05:19 -07005943 *
Griff Hazenca48d352014-05-28 22:37:13 -07005944 * <p>For notifications with multiple pages, child pages can also have content actions
Griff Hazen14f57992014-05-26 09:07:14 -07005945 * set, although the list of available actions comes from the main notification and not
5946 * from the child page's notification.
5947 *
5948 * <p>If wearable specific actions were added to the main notification, this index will
5949 * apply to that list, otherwise it will apply to the regular actions list.
Griff Hazenca48d352014-05-28 22:37:13 -07005950 *
5951 * @return the action index or {@link #UNSET_ACTION_INDEX} if no action was selected.
Griff Hazen61a9e862014-05-22 16:05:19 -07005952 */
5953 public int getContentAction() {
5954 return mContentActionIndex;
5955 }
5956
5957 /**
5958 * Set the gravity that this notification should have within the available viewport space.
5959 * Supported values include {@link android.view.Gravity#TOP},
5960 * {@link android.view.Gravity#CENTER_VERTICAL} and {@link android.view.Gravity#BOTTOM}.
5961 * The default value is {@link android.view.Gravity#BOTTOM}.
5962 */
5963 public WearableExtender setGravity(int gravity) {
5964 mGravity = gravity;
5965 return this;
5966 }
5967
5968 /**
5969 * Get the gravity that this notification should have within the available viewport space.
5970 * Supported values include {@link android.view.Gravity#TOP},
5971 * {@link android.view.Gravity#CENTER_VERTICAL} and {@link android.view.Gravity#BOTTOM}.
5972 * The default value is {@link android.view.Gravity#BOTTOM}.
5973 */
5974 public int getGravity() {
5975 return mGravity;
5976 }
5977
5978 /**
5979 * Set the custom size preset for the display of this notification out of the available
5980 * presets found in {@link android.app.Notification.WearableExtender}, e.g.
5981 * {@link #SIZE_LARGE}.
5982 * <p>Some custom size presets are only applicable for custom display notifications created
5983 * using {@link android.app.Notification.WearableExtender#setDisplayIntent}. Check the
5984 * documentation for the preset in question. See also
5985 * {@link #setCustomContentHeight} and {@link #getCustomSizePreset}.
5986 */
5987 public WearableExtender setCustomSizePreset(int sizePreset) {
5988 mCustomSizePreset = sizePreset;
5989 return this;
5990 }
5991
5992 /**
5993 * Get the custom size preset for the display of this notification out of the available
5994 * presets found in {@link android.app.Notification.WearableExtender}, e.g.
5995 * {@link #SIZE_LARGE}.
5996 * <p>Some custom size presets are only applicable for custom display notifications created
5997 * using {@link #setDisplayIntent}. Check the documentation for the preset in question.
5998 * See also {@link #setCustomContentHeight} and {@link #setCustomSizePreset}.
5999 */
6000 public int getCustomSizePreset() {
6001 return mCustomSizePreset;
6002 }
6003
6004 /**
6005 * Set the custom height in pixels for the display of this notification's content.
6006 * <p>This option is only available for custom display notifications created
6007 * using {@link android.app.Notification.WearableExtender#setDisplayIntent}. See also
6008 * {@link android.app.Notification.WearableExtender#setCustomSizePreset} and
6009 * {@link #getCustomContentHeight}.
6010 */
6011 public WearableExtender setCustomContentHeight(int height) {
6012 mCustomContentHeight = height;
6013 return this;
6014 }
6015
6016 /**
6017 * Get the custom height in pixels for the display of this notification's content.
6018 * <p>This option is only available for custom display notifications created
6019 * using {@link #setDisplayIntent}. See also {@link #setCustomSizePreset} and
6020 * {@link #setCustomContentHeight}.
6021 */
6022 public int getCustomContentHeight() {
6023 return mCustomContentHeight;
6024 }
6025
6026 /**
6027 * Set whether the scrolling position for the contents of this notification should start
6028 * at the bottom of the contents instead of the top when the contents are too long to
6029 * display within the screen. Default is false (start scroll at the top).
6030 */
6031 public WearableExtender setStartScrollBottom(boolean startScrollBottom) {
6032 setFlag(FLAG_START_SCROLL_BOTTOM, startScrollBottom);
6033 return this;
6034 }
6035
6036 /**
6037 * Get whether the scrolling position for the contents of this notification should start
6038 * at the bottom of the contents instead of the top when the contents are too long to
6039 * display within the screen. Default is false (start scroll at the top).
6040 */
6041 public boolean getStartScrollBottom() {
6042 return (mFlags & FLAG_START_SCROLL_BOTTOM) != 0;
6043 }
6044
6045 /**
6046 * Set whether the content intent is available when the wearable device is not connected
6047 * to a companion device. The user can still trigger this intent when the wearable device
6048 * is offline, but a visual hint will indicate that the content intent may not be available.
6049 * Defaults to true.
6050 */
6051 public WearableExtender setContentIntentAvailableOffline(
6052 boolean contentIntentAvailableOffline) {
6053 setFlag(FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE, contentIntentAvailableOffline);
6054 return this;
6055 }
6056
6057 /**
6058 * Get whether the content intent is available when the wearable device is not connected
6059 * to a companion device. The user can still trigger this intent when the wearable device
6060 * is offline, but a visual hint will indicate that the content intent may not be available.
6061 * Defaults to true.
6062 */
6063 public boolean getContentIntentAvailableOffline() {
6064 return (mFlags & FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE) != 0;
6065 }
6066
6067 /**
6068 * Set a hint that this notification's icon should not be displayed.
6069 * @param hintHideIcon {@code true} to hide the icon, {@code false} otherwise.
6070 * @return this object for method chaining
6071 */
6072 public WearableExtender setHintHideIcon(boolean hintHideIcon) {
6073 setFlag(FLAG_HINT_HIDE_ICON, hintHideIcon);
6074 return this;
6075 }
6076
6077 /**
6078 * Get a hint that this notification's icon should not be displayed.
6079 * @return {@code true} if this icon should not be displayed, false otherwise.
6080 * The default value is {@code false} if this was never set.
6081 */
6082 public boolean getHintHideIcon() {
6083 return (mFlags & FLAG_HINT_HIDE_ICON) != 0;
6084 }
6085
6086 /**
6087 * Set a visual hint that only the background image of this notification should be
6088 * displayed, and other semantic content should be hidden. This hint is only applicable
6089 * to sub-pages added using {@link #addPage}.
6090 */
6091 public WearableExtender setHintShowBackgroundOnly(boolean hintShowBackgroundOnly) {
6092 setFlag(FLAG_HINT_SHOW_BACKGROUND_ONLY, hintShowBackgroundOnly);
6093 return this;
6094 }
6095
6096 /**
6097 * Get a visual hint that only the background image of this notification should be
6098 * displayed, and other semantic content should be hidden. This hint is only applicable
6099 * to sub-pages added using {@link android.app.Notification.WearableExtender#addPage}.
6100 */
6101 public boolean getHintShowBackgroundOnly() {
6102 return (mFlags & FLAG_HINT_SHOW_BACKGROUND_ONLY) != 0;
6103 }
6104
Griff Hazen5f2edfc2014-09-29 16:28:44 -07006105 /**
Griff Hazen9c5be4e2014-11-17 17:47:50 -08006106 * Set a hint that this notification's background should not be clipped if possible,
6107 * and should instead be resized to fully display on the screen, retaining the aspect
6108 * ratio of the image. This can be useful for images like barcodes or qr codes.
Griff Hazen5f2edfc2014-09-29 16:28:44 -07006109 * @param hintAvoidBackgroundClipping {@code true} to avoid clipping if possible.
6110 * @return this object for method chaining
6111 */
6112 public WearableExtender setHintAvoidBackgroundClipping(
6113 boolean hintAvoidBackgroundClipping) {
6114 setFlag(FLAG_HINT_AVOID_BACKGROUND_CLIPPING, hintAvoidBackgroundClipping);
6115 return this;
6116 }
6117
6118 /**
Griff Hazen9c5be4e2014-11-17 17:47:50 -08006119 * Get a hint that this notification's background should not be clipped if possible,
6120 * and should instead be resized to fully display on the screen, retaining the aspect
6121 * ratio of the image. This can be useful for images like barcodes or qr codes.
Griff Hazen5f2edfc2014-09-29 16:28:44 -07006122 * @return {@code true} if it's ok if the background is clipped on the screen, false
6123 * otherwise. The default value is {@code false} if this was never set.
6124 */
6125 public boolean getHintAvoidBackgroundClipping() {
6126 return (mFlags & FLAG_HINT_AVOID_BACKGROUND_CLIPPING) != 0;
6127 }
6128
6129 /**
6130 * Set a hint that the screen should remain on for at least this duration when
6131 * this notification is displayed on the screen.
6132 * @param timeout The requested screen timeout in milliseconds. Can also be either
6133 * {@link #SCREEN_TIMEOUT_SHORT} or {@link #SCREEN_TIMEOUT_LONG}.
6134 * @return this object for method chaining
6135 */
6136 public WearableExtender setHintScreenTimeout(int timeout) {
6137 mHintScreenTimeout = timeout;
6138 return this;
6139 }
6140
6141 /**
6142 * Get the duration, in milliseconds, that the screen should remain on for
6143 * when this notification is displayed.
6144 * @return the duration in milliseconds if > 0, or either one of the sentinel values
6145 * {@link #SCREEN_TIMEOUT_SHORT} or {@link #SCREEN_TIMEOUT_LONG}.
6146 */
6147 public int getHintScreenTimeout() {
6148 return mHintScreenTimeout;
6149 }
6150
Alex Hills9ab3a232016-04-05 14:54:56 -04006151 /**
Alex Hills4bcb06b2016-04-05 14:26:25 -04006152 * Set a hint that this notification's {@link BigPictureStyle} (if present) should be
6153 * converted to low-bit and displayed in ambient mode, especially useful for barcodes and
6154 * qr codes, as well as other simple black-and-white tickets.
6155 * @param hintAmbientBigPicture {@code true} to enable converstion and ambient.
6156 * @return this object for method chaining
6157 */
6158 public WearableExtender setHintAmbientBigPicture(boolean hintAmbientBigPicture) {
6159 setFlag(FLAG_BIG_PICTURE_AMBIENT, hintAmbientBigPicture);
6160 return this;
6161 }
6162
6163 /**
6164 * Get a hint that this notification's {@link BigPictureStyle} (if present) should be
6165 * converted to low-bit and displayed in ambient mode, especially useful for barcodes and
6166 * qr codes, as well as other simple black-and-white tickets.
6167 * @return {@code true} if it should be displayed in ambient, false otherwise
6168 * otherwise. The default value is {@code false} if this was never set.
6169 */
6170 public boolean getHintAmbientBigPicture() {
6171 return (mFlags & FLAG_BIG_PICTURE_AMBIENT) != 0;
6172 }
6173
6174 /**
Alex Hills9ab3a232016-04-05 14:54:56 -04006175 * Set a hint that this notification's content intent will launch an {@link Activity}
6176 * directly, telling the platform that it can generate the appropriate transitions.
6177 * @param hintContentIntentLaunchesActivity {@code true} if the content intent will launch
6178 * an activity and transitions should be generated, false otherwise.
6179 * @return this object for method chaining
6180 */
6181 public WearableExtender setHintContentIntentLaunchesActivity(
6182 boolean hintContentIntentLaunchesActivity) {
6183 setFlag(FLAG_HINT_CONTENT_INTENT_LAUNCHES_ACTIVITY, hintContentIntentLaunchesActivity);
6184 return this;
6185 }
6186
6187 /**
6188 * Get a hint that this notification's content intent will launch an {@link Activity}
6189 * directly, telling the platform that it can generate the appropriate transitions
6190 * @return {@code true} if the content intent will launch an activity and transitions should
6191 * be generated, false otherwise. The default value is {@code false} if this was never set.
6192 */
6193 public boolean getHintContentIntentLaunchesActivity() {
6194 return (mFlags & FLAG_HINT_CONTENT_INTENT_LAUNCHES_ACTIVITY) != 0;
6195 }
6196
Nadia Benbernou948627e2016-04-14 14:41:08 -04006197 /**
6198 * When you post a notification, if you set the dismissal id field, then when that
6199 * notification is canceled, notifications on other wearables and the paired Android phone
6200 * having that same dismissal id will also be canceled. Note that this only works if you
6201 * have notification bridge mode set to NO_BRIDGING in your Wear app manifest. See
6202 * <a href="{@docRoot}wear/notifications/index.html">Adding Wearable Features to
6203 * Notifications</a> for more information on how to use the bridge mode feature.
6204 * @param dismissalId the dismissal id of the notification.
6205 * @return this object for method chaining
6206 */
6207 public WearableExtender setDismissalId(String dismissalId) {
6208 mDismissalId = dismissalId;
6209 return this;
6210 }
6211
6212 /**
6213 * Returns the dismissal id of the notification.
6214 * @return the dismissal id of the notification or null if it has not been set.
6215 */
6216 public String getDismissalId() {
6217 return mDismissalId;
6218 }
6219
Griff Hazen61a9e862014-05-22 16:05:19 -07006220 private void setFlag(int mask, boolean value) {
6221 if (value) {
6222 mFlags |= mask;
6223 } else {
6224 mFlags &= ~mask;
6225 }
6226 }
6227 }
6228
6229 /**
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08006230 * <p>Helper class to add Android Auto extensions to notifications. To create a notification
6231 * with car extensions:
6232 *
6233 * <ol>
6234 * <li>Create an {@link Notification.Builder}, setting any desired
6235 * properties.
6236 * <li>Create a {@link CarExtender}.
6237 * <li>Set car-specific properties using the {@code add} and {@code set} methods of
6238 * {@link CarExtender}.
6239 * <li>Call {@link Notification.Builder#extend(Notification.Extender)}
6240 * to apply the extensions to a notification.
6241 * </ol>
6242 *
6243 * <pre class="prettyprint">
6244 * Notification notification = new Notification.Builder(context)
6245 * ...
6246 * .extend(new CarExtender()
6247 * .set*(...))
6248 * .build();
6249 * </pre>
6250 *
6251 * <p>Car extensions can be accessed on an existing notification by using the
6252 * {@code CarExtender(Notification)} constructor, and then using the {@code get} methods
6253 * to access values.
6254 */
6255 public static final class CarExtender implements Extender {
6256 private static final String TAG = "CarExtender";
6257
6258 private static final String EXTRA_CAR_EXTENDER = "android.car.EXTENSIONS";
6259 private static final String EXTRA_LARGE_ICON = "large_icon";
6260 private static final String EXTRA_CONVERSATION = "car_conversation";
6261 private static final String EXTRA_COLOR = "app_color";
6262
6263 private Bitmap mLargeIcon;
6264 private UnreadConversation mUnreadConversation;
6265 private int mColor = Notification.COLOR_DEFAULT;
6266
6267 /**
6268 * Create a {@link CarExtender} with default options.
6269 */
6270 public CarExtender() {
6271 }
6272
6273 /**
6274 * Create a {@link CarExtender} from the CarExtender options of an existing Notification.
6275 *
6276 * @param notif The notification from which to copy options.
6277 */
6278 public CarExtender(Notification notif) {
6279 Bundle carBundle = notif.extras == null ?
6280 null : notif.extras.getBundle(EXTRA_CAR_EXTENDER);
6281 if (carBundle != null) {
6282 mLargeIcon = carBundle.getParcelable(EXTRA_LARGE_ICON);
6283 mColor = carBundle.getInt(EXTRA_COLOR, Notification.COLOR_DEFAULT);
6284
6285 Bundle b = carBundle.getBundle(EXTRA_CONVERSATION);
6286 mUnreadConversation = UnreadConversation.getUnreadConversationFromBundle(b);
6287 }
6288 }
6289
6290 /**
6291 * Apply car extensions to a notification that is being built. This is typically called by
6292 * the {@link Notification.Builder#extend(Notification.Extender)}
6293 * method of {@link Notification.Builder}.
6294 */
6295 @Override
6296 public Notification.Builder extend(Notification.Builder builder) {
6297 Bundle carExtensions = new Bundle();
6298
6299 if (mLargeIcon != null) {
6300 carExtensions.putParcelable(EXTRA_LARGE_ICON, mLargeIcon);
6301 }
6302 if (mColor != Notification.COLOR_DEFAULT) {
6303 carExtensions.putInt(EXTRA_COLOR, mColor);
6304 }
6305
6306 if (mUnreadConversation != null) {
6307 Bundle b = mUnreadConversation.getBundleForUnreadConversation();
6308 carExtensions.putBundle(EXTRA_CONVERSATION, b);
6309 }
6310
6311 builder.getExtras().putBundle(EXTRA_CAR_EXTENDER, carExtensions);
6312 return builder;
6313 }
6314
6315 /**
6316 * Sets the accent color to use when Android Auto presents the notification.
6317 *
6318 * Android Auto uses the color set with {@link Notification.Builder#setColor(int)}
6319 * to accent the displayed notification. However, not all colors are acceptable in an
6320 * automotive setting. This method can be used to override the color provided in the
6321 * notification in such a situation.
6322 */
Tor Norbye80756e32015-03-02 09:39:27 -08006323 public CarExtender setColor(@ColorInt int color) {
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08006324 mColor = color;
6325 return this;
6326 }
6327
6328 /**
6329 * Gets the accent color.
6330 *
Julia Reynoldsd9228f12015-10-20 10:37:27 -04006331 * @see #setColor
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08006332 */
Tor Norbye80756e32015-03-02 09:39:27 -08006333 @ColorInt
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08006334 public int getColor() {
6335 return mColor;
6336 }
6337
6338 /**
6339 * Sets the large icon of the car notification.
6340 *
6341 * If no large icon is set in the extender, Android Auto will display the icon
6342 * specified by {@link Notification.Builder#setLargeIcon(android.graphics.Bitmap)}
6343 *
6344 * @param largeIcon The large icon to use in the car notification.
6345 * @return This object for method chaining.
6346 */
6347 public CarExtender setLargeIcon(Bitmap largeIcon) {
6348 mLargeIcon = largeIcon;
6349 return this;
6350 }
6351
6352 /**
6353 * Gets the large icon used in this car notification, or null if no icon has been set.
6354 *
6355 * @return The large icon for the car notification.
6356 * @see CarExtender#setLargeIcon
6357 */
6358 public Bitmap getLargeIcon() {
6359 return mLargeIcon;
6360 }
6361
6362 /**
6363 * Sets the unread conversation in a message notification.
6364 *
6365 * @param unreadConversation The unread part of the conversation this notification conveys.
6366 * @return This object for method chaining.
6367 */
6368 public CarExtender setUnreadConversation(UnreadConversation unreadConversation) {
6369 mUnreadConversation = unreadConversation;
6370 return this;
6371 }
6372
6373 /**
6374 * Returns the unread conversation conveyed by this notification.
6375 * @see #setUnreadConversation(UnreadConversation)
6376 */
6377 public UnreadConversation getUnreadConversation() {
6378 return mUnreadConversation;
6379 }
6380
6381 /**
6382 * A class which holds the unread messages from a conversation.
6383 */
6384 public static class UnreadConversation {
6385 private static final String KEY_AUTHOR = "author";
6386 private static final String KEY_TEXT = "text";
6387 private static final String KEY_MESSAGES = "messages";
6388 private static final String KEY_REMOTE_INPUT = "remote_input";
6389 private static final String KEY_ON_REPLY = "on_reply";
6390 private static final String KEY_ON_READ = "on_read";
6391 private static final String KEY_PARTICIPANTS = "participants";
6392 private static final String KEY_TIMESTAMP = "timestamp";
6393
6394 private final String[] mMessages;
6395 private final RemoteInput mRemoteInput;
6396 private final PendingIntent mReplyPendingIntent;
6397 private final PendingIntent mReadPendingIntent;
6398 private final String[] mParticipants;
6399 private final long mLatestTimestamp;
6400
6401 UnreadConversation(String[] messages, RemoteInput remoteInput,
6402 PendingIntent replyPendingIntent, PendingIntent readPendingIntent,
6403 String[] participants, long latestTimestamp) {
6404 mMessages = messages;
6405 mRemoteInput = remoteInput;
6406 mReadPendingIntent = readPendingIntent;
6407 mReplyPendingIntent = replyPendingIntent;
6408 mParticipants = participants;
6409 mLatestTimestamp = latestTimestamp;
6410 }
6411
6412 /**
6413 * Gets the list of messages conveyed by this notification.
6414 */
6415 public String[] getMessages() {
6416 return mMessages;
6417 }
6418
6419 /**
6420 * Gets the remote input that will be used to convey the response to a message list, or
6421 * null if no such remote input exists.
6422 */
6423 public RemoteInput getRemoteInput() {
6424 return mRemoteInput;
6425 }
6426
6427 /**
6428 * Gets the pending intent that will be triggered when the user replies to this
6429 * notification.
6430 */
6431 public PendingIntent getReplyPendingIntent() {
6432 return mReplyPendingIntent;
6433 }
6434
6435 /**
6436 * Gets the pending intent that Android Auto will send after it reads aloud all messages
6437 * in this object's message list.
6438 */
6439 public PendingIntent getReadPendingIntent() {
6440 return mReadPendingIntent;
6441 }
6442
6443 /**
6444 * Gets the participants in the conversation.
6445 */
6446 public String[] getParticipants() {
6447 return mParticipants;
6448 }
6449
6450 /**
6451 * Gets the firs participant in the conversation.
6452 */
6453 public String getParticipant() {
6454 return mParticipants.length > 0 ? mParticipants[0] : null;
6455 }
6456
6457 /**
6458 * Gets the timestamp of the conversation.
6459 */
6460 public long getLatestTimestamp() {
6461 return mLatestTimestamp;
6462 }
6463
6464 Bundle getBundleForUnreadConversation() {
6465 Bundle b = new Bundle();
6466 String author = null;
6467 if (mParticipants != null && mParticipants.length > 1) {
6468 author = mParticipants[0];
6469 }
6470 Parcelable[] messages = new Parcelable[mMessages.length];
6471 for (int i = 0; i < messages.length; i++) {
6472 Bundle m = new Bundle();
6473 m.putString(KEY_TEXT, mMessages[i]);
6474 m.putString(KEY_AUTHOR, author);
6475 messages[i] = m;
6476 }
6477 b.putParcelableArray(KEY_MESSAGES, messages);
6478 if (mRemoteInput != null) {
6479 b.putParcelable(KEY_REMOTE_INPUT, mRemoteInput);
6480 }
6481 b.putParcelable(KEY_ON_REPLY, mReplyPendingIntent);
6482 b.putParcelable(KEY_ON_READ, mReadPendingIntent);
6483 b.putStringArray(KEY_PARTICIPANTS, mParticipants);
6484 b.putLong(KEY_TIMESTAMP, mLatestTimestamp);
6485 return b;
6486 }
6487
6488 static UnreadConversation getUnreadConversationFromBundle(Bundle b) {
6489 if (b == null) {
6490 return null;
6491 }
6492 Parcelable[] parcelableMessages = b.getParcelableArray(KEY_MESSAGES);
6493 String[] messages = null;
6494 if (parcelableMessages != null) {
6495 String[] tmp = new String[parcelableMessages.length];
6496 boolean success = true;
6497 for (int i = 0; i < tmp.length; i++) {
6498 if (!(parcelableMessages[i] instanceof Bundle)) {
6499 success = false;
6500 break;
6501 }
6502 tmp[i] = ((Bundle) parcelableMessages[i]).getString(KEY_TEXT);
6503 if (tmp[i] == null) {
6504 success = false;
6505 break;
6506 }
6507 }
6508 if (success) {
6509 messages = tmp;
6510 } else {
6511 return null;
6512 }
6513 }
6514
6515 PendingIntent onRead = b.getParcelable(KEY_ON_READ);
6516 PendingIntent onReply = b.getParcelable(KEY_ON_REPLY);
6517
6518 RemoteInput remoteInput = b.getParcelable(KEY_REMOTE_INPUT);
6519
6520 String[] participants = b.getStringArray(KEY_PARTICIPANTS);
6521 if (participants == null || participants.length != 1) {
6522 return null;
6523 }
6524
6525 return new UnreadConversation(messages,
6526 remoteInput,
6527 onReply,
6528 onRead,
6529 participants, b.getLong(KEY_TIMESTAMP));
6530 }
6531 };
6532
6533 /**
6534 * Builder class for {@link CarExtender.UnreadConversation} objects.
6535 */
6536 public static class Builder {
6537 private final List<String> mMessages = new ArrayList<String>();
6538 private final String mParticipant;
6539 private RemoteInput mRemoteInput;
6540 private PendingIntent mReadPendingIntent;
6541 private PendingIntent mReplyPendingIntent;
6542 private long mLatestTimestamp;
6543
6544 /**
6545 * Constructs a new builder for {@link CarExtender.UnreadConversation}.
6546 *
6547 * @param name The name of the other participant in the conversation.
6548 */
6549 public Builder(String name) {
6550 mParticipant = name;
6551 }
6552
6553 /**
6554 * Appends a new unread message to the list of messages for this conversation.
6555 *
6556 * The messages should be added from oldest to newest.
6557 *
6558 * @param message The text of the new unread message.
6559 * @return This object for method chaining.
6560 */
6561 public Builder addMessage(String message) {
6562 mMessages.add(message);
6563 return this;
6564 }
6565
6566 /**
6567 * Sets the pending intent and remote input which will convey the reply to this
6568 * notification.
6569 *
6570 * @param pendingIntent The pending intent which will be triggered on a reply.
6571 * @param remoteInput The remote input parcelable which will carry the reply.
6572 * @return This object for method chaining.
6573 *
6574 * @see CarExtender.UnreadConversation#getRemoteInput
6575 * @see CarExtender.UnreadConversation#getReplyPendingIntent
6576 */
6577 public Builder setReplyAction(
6578 PendingIntent pendingIntent, RemoteInput remoteInput) {
6579 mRemoteInput = remoteInput;
6580 mReplyPendingIntent = pendingIntent;
6581
6582 return this;
6583 }
6584
6585 /**
6586 * Sets the pending intent that will be sent once the messages in this notification
6587 * are read.
6588 *
6589 * @param pendingIntent The pending intent to use.
6590 * @return This object for method chaining.
6591 */
6592 public Builder setReadPendingIntent(PendingIntent pendingIntent) {
6593 mReadPendingIntent = pendingIntent;
6594 return this;
6595 }
6596
6597 /**
6598 * Sets the timestamp of the most recent message in an unread conversation.
6599 *
6600 * If a messaging notification has been posted by your application and has not
6601 * yet been cancelled, posting a later notification with the same id and tag
6602 * but without a newer timestamp may result in Android Auto not displaying a
6603 * heads up notification for the later notification.
6604 *
6605 * @param timestamp The timestamp of the most recent message in the conversation.
6606 * @return This object for method chaining.
6607 */
6608 public Builder setLatestTimestamp(long timestamp) {
6609 mLatestTimestamp = timestamp;
6610 return this;
6611 }
6612
6613 /**
6614 * Builds a new unread conversation object.
6615 *
6616 * @return The new unread conversation object.
6617 */
6618 public UnreadConversation build() {
6619 String[] messages = mMessages.toArray(new String[mMessages.size()]);
6620 String[] participants = { mParticipant };
6621 return new UnreadConversation(messages, mRemoteInput, mReplyPendingIntent,
6622 mReadPendingIntent, participants, mLatestTimestamp);
6623 }
6624 }
6625 }
6626
6627 /**
Griff Hazen61a9e862014-05-22 16:05:19 -07006628 * Get an array of Notification objects from a parcelable array bundle field.
6629 * Update the bundle to have a typed array so fetches in the future don't need
6630 * to do an array copy.
6631 */
6632 private static Notification[] getNotificationArrayFromBundle(Bundle bundle, String key) {
6633 Parcelable[] array = bundle.getParcelableArray(key);
6634 if (array instanceof Notification[] || array == null) {
6635 return (Notification[]) array;
6636 }
6637 Notification[] typedArray = Arrays.copyOf(array, array.length,
6638 Notification[].class);
6639 bundle.putParcelableArray(key, typedArray);
6640 return typedArray;
6641 }
Christoph Studer4600f9b2014-07-22 22:44:43 +02006642
6643 private static class BuilderRemoteViews extends RemoteViews {
6644 public BuilderRemoteViews(Parcel parcel) {
6645 super(parcel);
6646 }
6647
Kenny Guy77320062014-08-27 21:37:15 +01006648 public BuilderRemoteViews(ApplicationInfo appInfo, int layoutId) {
6649 super(appInfo, layoutId);
Christoph Studer4600f9b2014-07-22 22:44:43 +02006650 }
6651
6652 @Override
6653 public BuilderRemoteViews clone() {
6654 Parcel p = Parcel.obtain();
6655 writeToParcel(p, 0);
6656 p.setDataPosition(0);
6657 BuilderRemoteViews brv = new BuilderRemoteViews(p);
6658 p.recycle();
6659 return brv;
6660 }
6661 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006662}