blob: 6fc1820c3c3619470c9ae671774739f989ef4c73 [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 }
Selim Cinek279fa862016-06-14 10:57:25 -07001869 if (mLargeIcon == null && largeIcon != null) {
1870 // you snuck an icon in here without using the builder; let's try to keep it
1871 mLargeIcon = Icon.createWithBitmap(largeIcon);
1872 }
Dan Sandlerd63f9322015-05-06 15:18:49 -04001873 if (mLargeIcon != null) {
Joe Onorato561d3852010-11-20 18:09:34 -08001874 parcel.writeInt(1);
Dan Sandlerd63f9322015-05-06 15:18:49 -04001875 mLargeIcon.writeToParcel(parcel, 0);
Joe Onorato561d3852010-11-20 18:09:34 -08001876 } else {
1877 parcel.writeInt(0);
1878 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001879
1880 parcel.writeInt(defaults);
1881 parcel.writeInt(this.flags);
1882
1883 if (sound != null) {
1884 parcel.writeInt(1);
1885 sound.writeToParcel(parcel, 0);
1886 } else {
1887 parcel.writeInt(0);
1888 }
1889 parcel.writeInt(audioStreamType);
John Spurlockc0650f022014-07-19 13:22:39 -04001890
1891 if (audioAttributes != null) {
1892 parcel.writeInt(1);
1893 audioAttributes.writeToParcel(parcel, 0);
1894 } else {
1895 parcel.writeInt(0);
1896 }
1897
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001898 parcel.writeLongArray(vibrate);
1899 parcel.writeInt(ledARGB);
1900 parcel.writeInt(ledOnMS);
1901 parcel.writeInt(ledOffMS);
1902 parcel.writeInt(iconLevel);
Daniel Sandlere46cbd32010-06-17 10:35:26 -04001903
1904 if (fullScreenIntent != null) {
1905 parcel.writeInt(1);
1906 fullScreenIntent.writeToParcel(parcel, 0);
1907 } else {
1908 parcel.writeInt(0);
1909 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001910
1911 parcel.writeInt(priority);
Joe Malin8d40d042012-11-05 11:36:40 -08001912
John Spurlockfd7f1e02014-03-18 16:41:57 -04001913 parcel.writeString(category);
Joe Malin8d40d042012-11-05 11:36:40 -08001914
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001915 parcel.writeString(mGroupKey);
1916
1917 parcel.writeString(mSortKey);
1918
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001919 parcel.writeBundle(extras); // null ok
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001920
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001921 parcel.writeTypedArray(actions, 0); // null ok
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001922
Daniel Sandlerf3b73432012-03-27 15:01:25 -04001923 if (bigContentView != null) {
1924 parcel.writeInt(1);
1925 bigContentView.writeToParcel(parcel, 0);
1926 } else {
1927 parcel.writeInt(0);
1928 }
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001929
Chris Wren8fd39ec2014-02-27 17:43:26 -05001930 if (headsUpContentView != null) {
1931 parcel.writeInt(1);
1932 headsUpContentView.writeToParcel(parcel, 0);
1933 } else {
1934 parcel.writeInt(0);
1935 }
1936
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001937 parcel.writeInt(visibility);
1938
1939 if (publicVersion != null) {
1940 parcel.writeInt(1);
1941 publicVersion.writeToParcel(parcel, 0);
1942 } else {
1943 parcel.writeInt(0);
1944 }
Dan Sandler26e81cf2014-05-06 10:01:27 -04001945
1946 parcel.writeInt(color);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001947 }
1948
1949 /**
1950 * Parcelable.Creator that instantiates Notification objects
1951 */
1952 public static final Parcelable.Creator<Notification> CREATOR
1953 = new Parcelable.Creator<Notification>()
1954 {
1955 public Notification createFromParcel(Parcel parcel)
1956 {
1957 return new Notification(parcel);
1958 }
1959
1960 public Notification[] newArray(int size)
1961 {
1962 return new Notification[size];
1963 }
1964 };
1965
1966 /**
1967 * Sets the {@link #contentView} field to be a view with the standard "Latest Event"
1968 * layout.
1969 *
1970 * <p>Uses the {@link #icon} and {@link #when} fields to set the icon and time fields
1971 * in the view.</p>
1972 * @param context The context for your application / activity.
1973 * @param contentTitle The title that goes in the expanded entry.
1974 * @param contentText The text that goes in the expanded entry.
1975 * @param contentIntent The intent to launch when the user clicks the expanded notification.
1976 * If this is an activity, it must include the
1977 * {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} flag, which requires
Scott Main7aee61f2011-02-08 11:25:01 -08001978 * that you take care of task management as described in the
1979 * <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back
1980 * Stack</a> document.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001981 *
Joe Onorato46439ce2010-11-19 13:56:21 -08001982 * @deprecated Use {@link Builder} instead.
Chris Wrena05db382015-06-24 15:18:34 -04001983 * @removed
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001984 */
Joe Onorato46439ce2010-11-19 13:56:21 -08001985 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001986 public void setLatestEventInfo(Context context,
1987 CharSequence contentTitle, CharSequence contentText, PendingIntent contentIntent) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04001988 if (context.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1){
1989 Log.e(TAG, "setLatestEventInfo() is deprecated and you should feel deprecated.",
1990 new Throwable());
1991 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001992
Selim Cinek4ac6f602016-06-13 15:47:03 -07001993 if (context.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N) {
1994 extras.putBoolean(EXTRA_SHOW_WHEN, true);
1995 }
1996
Julia Reynoldsd9228f12015-10-20 10:37:27 -04001997 // ensure that any information already set directly is preserved
1998 final Notification.Builder builder = new Notification.Builder(context, this);
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001999
2000 // now apply the latestEventInfo fields
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002001 if (contentTitle != null) {
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002002 builder.setContentTitle(contentTitle);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002003 }
2004 if (contentText != null) {
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002005 builder.setContentText(contentText);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002006 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002007 builder.setContentIntent(contentIntent);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002008
2009 builder.build(); // callers expect this notification to be ready to use
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002010 }
2011
Julia Reynoldsda303542015-11-23 14:00:20 -05002012 /**
2013 * @hide
2014 */
2015 public static void addFieldsFromContext(Context context, Notification notification) {
Jeff Sharkey012bc7b2016-04-11 16:30:27 -06002016 addFieldsFromContext(context.getApplicationInfo(), context.getUserId(), notification);
2017 }
2018
2019 /**
2020 * @hide
2021 */
2022 public static void addFieldsFromContext(ApplicationInfo ai, int userId,
2023 Notification notification) {
2024 notification.extras.putParcelable(EXTRA_BUILDER_APPLICATION_INFO, ai);
2025 notification.extras.putInt(EXTRA_ORIGINATING_USERID, userId);
Julia Reynoldsda303542015-11-23 14:00:20 -05002026 }
2027
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002028 @Override
2029 public String toString() {
2030 StringBuilder sb = new StringBuilder();
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002031 sb.append("Notification(pri=");
2032 sb.append(priority);
2033 sb.append(" contentView=");
Joe Onoratoc9596d62011-01-12 17:03:11 -08002034 if (contentView != null) {
2035 sb.append(contentView.getPackage());
2036 sb.append("/0x");
2037 sb.append(Integer.toHexString(contentView.getLayoutId()));
2038 } else {
2039 sb.append("null");
2040 }
2041 sb.append(" vibrate=");
Daniel Sandler6738eee2012-11-16 12:03:32 -05002042 if ((this.defaults & DEFAULT_VIBRATE) != 0) {
2043 sb.append("default");
2044 } else if (this.vibrate != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002045 int N = this.vibrate.length-1;
2046 sb.append("[");
2047 for (int i=0; i<N; i++) {
2048 sb.append(this.vibrate[i]);
2049 sb.append(',');
2050 }
Simon Schoar8cf97d92009-06-10 22:08:37 +02002051 if (N != -1) {
2052 sb.append(this.vibrate[N]);
2053 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002054 sb.append("]");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002055 } else {
2056 sb.append("null");
2057 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002058 sb.append(" sound=");
Daniel Sandler6738eee2012-11-16 12:03:32 -05002059 if ((this.defaults & DEFAULT_SOUND) != 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002060 sb.append("default");
Daniel Sandler6738eee2012-11-16 12:03:32 -05002061 } else if (this.sound != null) {
2062 sb.append(this.sound.toString());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002063 } else {
2064 sb.append("null");
2065 }
Chris Wren365b6d32015-07-16 10:39:26 -04002066 if (this.tickerText != null) {
2067 sb.append(" tick");
2068 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002069 sb.append(" defaults=0x");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002070 sb.append(Integer.toHexString(this.defaults));
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002071 sb.append(" flags=0x");
Daniel Sandlere46cbd32010-06-17 10:35:26 -04002072 sb.append(Integer.toHexString(this.flags));
Dan Sandler26e81cf2014-05-06 10:01:27 -04002073 sb.append(String.format(" color=0x%08x", this.color));
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002074 if (this.category != null) {
2075 sb.append(" category=");
2076 sb.append(this.category);
2077 }
2078 if (this.mGroupKey != null) {
2079 sb.append(" groupKey=");
2080 sb.append(this.mGroupKey);
2081 }
2082 if (this.mSortKey != null) {
2083 sb.append(" sortKey=");
2084 sb.append(this.mSortKey);
2085 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002086 if (actions != null) {
Dan Sandler1b718782014-07-18 12:43:45 -04002087 sb.append(" actions=");
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002088 sb.append(actions.length);
Dan Sandler1b718782014-07-18 12:43:45 -04002089 }
2090 sb.append(" vis=");
2091 sb.append(visibilityToString(this.visibility));
2092 if (this.publicVersion != null) {
2093 sb.append(" publicVersion=");
2094 sb.append(publicVersion.toString());
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002095 }
2096 sb.append(")");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002097 return sb.toString();
2098 }
Joe Onorato46439ce2010-11-19 13:56:21 -08002099
Dan Sandler1b718782014-07-18 12:43:45 -04002100 /**
2101 * {@hide}
2102 */
2103 public static String visibilityToString(int vis) {
2104 switch (vis) {
2105 case VISIBILITY_PRIVATE:
2106 return "PRIVATE";
2107 case VISIBILITY_PUBLIC:
2108 return "PUBLIC";
2109 case VISIBILITY_SECRET:
2110 return "SECRET";
2111 default:
2112 return "UNKNOWN(" + String.valueOf(vis) + ")";
2113 }
2114 }
2115
Joe Onoratocb109a02011-01-18 17:57:41 -08002116 /**
John Spurlock1d881a12015-03-18 19:21:54 -04002117 * {@hide}
2118 */
2119 public static String priorityToString(@Priority int pri) {
2120 switch (pri) {
2121 case PRIORITY_MIN:
2122 return "MIN";
2123 case PRIORITY_LOW:
2124 return "LOW";
2125 case PRIORITY_DEFAULT:
2126 return "DEFAULT";
2127 case PRIORITY_HIGH:
2128 return "HIGH";
2129 case PRIORITY_MAX:
2130 return "MAX";
2131 default:
2132 return "UNKNOWN(" + String.valueOf(pri) + ")";
2133 }
2134 }
2135
2136 /**
Dan Sandlerd63f9322015-05-06 15:18:49 -04002137 * The small icon representing this notification in the status bar and content view.
2138 *
2139 * @return the small icon representing this notification.
2140 *
2141 * @see Builder#getSmallIcon()
2142 * @see Builder#setSmallIcon(Icon)
2143 */
2144 public Icon getSmallIcon() {
2145 return mSmallIcon;
2146 }
2147
2148 /**
2149 * Used when notifying to clean up legacy small icons.
2150 * @hide
2151 */
2152 public void setSmallIcon(Icon icon) {
2153 mSmallIcon = icon;
2154 }
2155
2156 /**
2157 * The large icon shown in this notification's content view.
2158 * @see Builder#getLargeIcon()
2159 * @see Builder#setLargeIcon(Icon)
2160 */
2161 public Icon getLargeIcon() {
2162 return mLargeIcon;
2163 }
2164
2165 /**
Christoph Studer4600f9b2014-07-22 22:44:43 +02002166 * @hide
2167 */
Christoph Studerc8db24b2014-07-25 17:50:30 +02002168 public boolean isGroupSummary() {
2169 return mGroupKey != null && (flags & FLAG_GROUP_SUMMARY) != 0;
2170 }
2171
2172 /**
2173 * @hide
2174 */
2175 public boolean isGroupChild() {
2176 return mGroupKey != null && (flags & FLAG_GROUP_SUMMARY) == 0;
2177 }
2178
2179 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002180 * Builder class for {@link Notification} objects.
Joe Malin8d40d042012-11-05 11:36:40 -08002181 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002182 * Provides a convenient way to set the various fields of a {@link Notification} and generate
Scott Main183bf112012-08-13 19:12:13 -07002183 * content views using the platform's notification layout template. If your app supports
2184 * versions of Android as old as API level 4, you can instead use
2185 * {@link android.support.v4.app.NotificationCompat.Builder NotificationCompat.Builder},
2186 * available in the <a href="{@docRoot}tools/extras/support-library.html">Android Support
2187 * library</a>.
Joe Malin8d40d042012-11-05 11:36:40 -08002188 *
Scott Main183bf112012-08-13 19:12:13 -07002189 * <p>Example:
Joe Malin8d40d042012-11-05 11:36:40 -08002190 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002191 * <pre class="prettyprint">
Scott Main183bf112012-08-13 19:12:13 -07002192 * Notification noti = new Notification.Builder(mContext)
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002193 * .setContentTitle(&quot;New mail from &quot; + sender.toString())
2194 * .setContentText(subject)
2195 * .setSmallIcon(R.drawable.new_mail)
2196 * .setLargeIcon(aBitmap)
Chris Wrenfbd96ba2012-05-01 12:03:58 -04002197 * .build();
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002198 * </pre>
Joe Onoratocb109a02011-01-18 17:57:41 -08002199 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002200 public static class Builder {
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05002201 /**
2202 * @hide
2203 */
2204 public static final String EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT =
2205 "android.rebuild.contentViewActionCount";
2206 /**
2207 * @hide
2208 */
2209 public static final String EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT
2210 = "android.rebuild.bigViewActionCount";
2211 /**
2212 * @hide
2213 */
2214 public static final String EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT
2215 = "android.rebuild.hudViewActionCount";
2216
Daniel Sandler602ad1c2012-06-12 16:06:27 -04002217 private static final int MAX_ACTION_BUTTONS = 3;
Daniel Sandler8680bf82012-05-15 16:52:52 -04002218
Joe Onorato46439ce2010-11-19 13:56:21 -08002219 private Context mContext;
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002220 private Notification mN;
2221 private Bundle mUserExtras = new Bundle();
Chris Wrenfbd96ba2012-05-01 12:03:58 -04002222 private Style mStyle;
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002223 private ArrayList<Action> mActions = new ArrayList<Action>(MAX_ACTION_BUTTONS);
2224 private ArrayList<String> mPersonList = new ArrayList<String>();
2225 private NotificationColorUtil mColorUtil;
2226 private boolean mColorUtilInited = false;
Christoph Studer7ac80e62014-08-04 16:01:57 +02002227
2228 /**
Adrian Roos4ff3b122016-02-01 12:26:13 -08002229 * Caches a contrast-enhanced version of {@link #mCachedContrastColorIsFor}.
2230 */
2231 private int mCachedContrastColor = COLOR_INVALID;
2232 private int mCachedContrastColorIsFor = COLOR_INVALID;
2233
2234 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002235 * Constructs a new Builder with the defaults:
Joe Onoratocb109a02011-01-18 17:57:41 -08002236 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002237
2238 * <table>
2239 * <tr><th align=right>priority</th>
2240 * <td>{@link #PRIORITY_DEFAULT}</td></tr>
2241 * <tr><th align=right>when</th>
2242 * <td>now ({@link System#currentTimeMillis()})</td></tr>
2243 * <tr><th align=right>audio stream</th>
2244 * <td>{@link #STREAM_DEFAULT}</td></tr>
2245 * </table>
Joe Onoratocb109a02011-01-18 17:57:41 -08002246 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002247
2248 * @param context
2249 * A {@link Context} that will be used by the Builder to construct the
2250 * RemoteViews. The Context will not be held past the lifetime of this Builder
2251 * object.
Joe Onoratocb109a02011-01-18 17:57:41 -08002252 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002253 public Builder(Context context) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002254 this(context, null);
Joe Onorato46439ce2010-11-19 13:56:21 -08002255 }
2256
Joe Onoratocb109a02011-01-18 17:57:41 -08002257 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002258 * @hide
Christoph Studer4600f9b2014-07-22 22:44:43 +02002259 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002260 public Builder(Context context, Notification toAdopt) {
2261 mContext = context;
Christoph Studer4600f9b2014-07-22 22:44:43 +02002262
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002263 if (toAdopt == null) {
2264 mN = new Notification();
Selim Cinek0ff1ce602016-04-05 18:27:16 -07002265 if (context.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N) {
2266 mN.extras.putBoolean(EXTRA_SHOW_WHEN, true);
2267 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002268 mN.priority = PRIORITY_DEFAULT;
2269 mN.visibility = VISIBILITY_PRIVATE;
2270 } else {
2271 mN = toAdopt;
2272 if (mN.actions != null) {
2273 Collections.addAll(mActions, mN.actions);
Christoph Studer4600f9b2014-07-22 22:44:43 +02002274 }
2275
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002276 if (mN.extras.containsKey(EXTRA_PEOPLE)) {
2277 Collections.addAll(mPersonList, mN.extras.getStringArray(EXTRA_PEOPLE));
2278 }
2279
Selim Cinek4ac6f602016-06-13 15:47:03 -07002280 if (mN.getSmallIcon() == null && mN.icon != 0) {
2281 setSmallIcon(mN.icon);
2282 }
2283
2284 if (mN.getLargeIcon() == null && mN.largeIcon != null) {
2285 setLargeIcon(mN.largeIcon);
2286 }
2287
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002288 String templateClass = mN.extras.getString(EXTRA_TEMPLATE);
2289 if (!TextUtils.isEmpty(templateClass)) {
2290 final Class<? extends Style> styleClass
2291 = getNotificationStyleClass(templateClass);
2292 if (styleClass == null) {
2293 Log.d(TAG, "Unknown style class: " + templateClass);
2294 } else {
2295 try {
Adrian Roosc1a80b02016-04-05 14:54:55 -07002296 final Constructor<? extends Style> ctor =
2297 styleClass.getDeclaredConstructor();
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002298 ctor.setAccessible(true);
2299 final Style style = ctor.newInstance();
2300 style.restoreFromExtras(mN.extras);
2301
2302 if (style != null) {
2303 setStyle(style);
2304 }
2305 } catch (Throwable t) {
2306 Log.e(TAG, "Could not create Style", t);
2307 }
2308 }
2309 }
2310
2311 }
2312 }
2313
2314 private NotificationColorUtil getColorUtil() {
2315 if (!mColorUtilInited) {
2316 mColorUtilInited = true;
2317 if (mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.LOLLIPOP) {
2318 mColorUtil = NotificationColorUtil.getInstance(mContext);
Christoph Studer4600f9b2014-07-22 22:44:43 +02002319 }
2320 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002321 return mColorUtil;
Christoph Studer4600f9b2014-07-22 22:44:43 +02002322 }
2323
2324 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002325 * Add a timestamp pertaining to the notification (usually the time the event occurred).
Selim Cinek0ff1ce602016-04-05 18:27:16 -07002326 *
2327 * For apps targeting {@link android.os.Build.VERSION_CODES#N} and above, this time is not
2328 * shown anymore by default and must be opted into by using
2329 * {@link android.app.Notification.Builder#setShowWhen(boolean)}
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002330 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002331 * @see Notification#when
Joe Onoratocb109a02011-01-18 17:57:41 -08002332 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002333 public Builder setWhen(long when) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002334 mN.when = when;
Joe Onorato46439ce2010-11-19 13:56:21 -08002335 return this;
2336 }
2337
Joe Onoratocb109a02011-01-18 17:57:41 -08002338 /**
Griff Hazen50c11652014-05-16 09:46:31 -07002339 * Control whether the timestamp set with {@link #setWhen(long) setWhen} is shown
Daniel Sandler0c890492012-09-12 17:23:10 -07002340 * in the content view.
Selim Cinek0ff1ce602016-04-05 18:27:16 -07002341 * For apps targeting {@link android.os.Build.VERSION_CODES#N} and above, this defaults to
2342 * {@code false}. For earlier apps, the default is {@code true}.
Daniel Sandler0c890492012-09-12 17:23:10 -07002343 */
2344 public Builder setShowWhen(boolean show) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002345 mN.extras.putBoolean(EXTRA_SHOW_WHEN, show);
Daniel Sandler0c890492012-09-12 17:23:10 -07002346 return this;
2347 }
2348
2349 /**
Daniel Sandlerd33b8032012-05-10 11:41:48 -04002350 * Show the {@link Notification#when} field as a stopwatch.
Joe Malin8d40d042012-11-05 11:36:40 -08002351 *
2352 * Instead of presenting <code>when</code> as a timestamp, the notification will show an
Daniel Sandlerd33b8032012-05-10 11:41:48 -04002353 * automatically updating display of the minutes and seconds since <code>when</code>.
Daniel Sandlera2985ed2012-04-03 16:42:00 -04002354 *
Daniel Sandlerd33b8032012-05-10 11:41:48 -04002355 * Useful when showing an elapsed time (like an ongoing phone call).
2356 *
Selim Cinek81c23aa2016-02-25 16:23:13 -08002357 * The counter can also be set to count down to <code>when</code> when using
Adrian Roos96b7e202016-05-17 13:50:38 -07002358 * {@link #setChronometerCountDown(boolean)}.
Selim Cinek81c23aa2016-02-25 16:23:13 -08002359 *
Daniel Sandlerd33b8032012-05-10 11:41:48 -04002360 * @see android.widget.Chronometer
Daniel Sandlera2985ed2012-04-03 16:42:00 -04002361 * @see Notification#when
Adrian Roos96b7e202016-05-17 13:50:38 -07002362 * @see #setChronometerCountDown(boolean)
Daniel Sandlera2985ed2012-04-03 16:42:00 -04002363 */
2364 public Builder setUsesChronometer(boolean b) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002365 mN.extras.putBoolean(EXTRA_SHOW_CHRONOMETER, b);
Daniel Sandlera2985ed2012-04-03 16:42:00 -04002366 return this;
2367 }
2368
2369 /**
Selim Cinek81c23aa2016-02-25 16:23:13 -08002370 * Sets the Chronometer to count down instead of counting up.
2371 *
2372 * <p>This is only relevant if {@link #setUsesChronometer(boolean)} has been set to true.
2373 * If it isn't set the chronometer will count up.
2374 *
2375 * @see #setUsesChronometer(boolean)
2376 */
Adrian Roos96b7e202016-05-17 13:50:38 -07002377 public Builder setChronometerCountDown(boolean countDown) {
2378 mN.extras.putBoolean(EXTRA_CHRONOMETER_COUNT_DOWN, countDown);
Selim Cinek81c23aa2016-02-25 16:23:13 -08002379 return this;
2380 }
2381
2382 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002383 * Set the small icon resource, which will be used to represent the notification in the
2384 * status bar.
Joe Onoratocb109a02011-01-18 17:57:41 -08002385 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002386
2387 * The platform template for the expanded view will draw this icon in the left, unless a
2388 * {@link #setLargeIcon(Bitmap) large icon} has also been specified, in which case the small
2389 * icon will be moved to the right-hand side.
2390 *
2391
2392 * @param icon
2393 * A resource ID in the application's package of the drawable to use.
2394 * @see Notification#icon
Joe Onoratocb109a02011-01-18 17:57:41 -08002395 */
Tor Norbye7b9c9122013-05-30 16:48:33 -07002396 public Builder setSmallIcon(@DrawableRes int icon) {
Dan Sandlerd63f9322015-05-06 15:18:49 -04002397 return setSmallIcon(icon != 0
2398 ? Icon.createWithResource(mContext, icon)
2399 : null);
Joe Onorato46439ce2010-11-19 13:56:21 -08002400 }
2401
Joe Onoratocb109a02011-01-18 17:57:41 -08002402 /**
2403 * A variant of {@link #setSmallIcon(int) setSmallIcon(int)} that takes an additional
2404 * level parameter for when the icon is a {@link android.graphics.drawable.LevelListDrawable
2405 * LevelListDrawable}.
2406 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002407 * @param icon A resource ID in the application's package of the drawable to use.
Joe Onoratocb109a02011-01-18 17:57:41 -08002408 * @param level The level to use for the icon.
2409 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002410 * @see Notification#icon
2411 * @see Notification#iconLevel
Joe Onoratocb109a02011-01-18 17:57:41 -08002412 */
Tor Norbye7b9c9122013-05-30 16:48:33 -07002413 public Builder setSmallIcon(@DrawableRes int icon, int level) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002414 mN.iconLevel = level;
Dan Sandlerd63f9322015-05-06 15:18:49 -04002415 return setSmallIcon(icon);
2416 }
2417
2418 /**
2419 * Set the small icon, which will be used to represent the notification in the
2420 * status bar and content view (unless overriden there by a
2421 * {@link #setLargeIcon(Bitmap) large icon}).
2422 *
2423 * @param icon An Icon object to use.
2424 * @see Notification#icon
2425 */
2426 public Builder setSmallIcon(Icon icon) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002427 mN.setSmallIcon(icon);
2428 if (icon != null && icon.getType() == Icon.TYPE_RESOURCE) {
2429 mN.icon = icon.getResId();
2430 }
Joe Onorato46439ce2010-11-19 13:56:21 -08002431 return this;
2432 }
2433
Joe Onoratocb109a02011-01-18 17:57:41 -08002434 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002435 * Set the first line of text in the platform notification template.
Joe Onoratocb109a02011-01-18 17:57:41 -08002436 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002437 public Builder setContentTitle(CharSequence title) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002438 mN.extras.putCharSequence(EXTRA_TITLE, safeCharSequence(title));
Joe Onorato46439ce2010-11-19 13:56:21 -08002439 return this;
2440 }
2441
Joe Onoratocb109a02011-01-18 17:57:41 -08002442 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002443 * Set the second line of text in the platform notification template.
Joe Onoratocb109a02011-01-18 17:57:41 -08002444 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002445 public Builder setContentText(CharSequence text) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002446 mN.extras.putCharSequence(EXTRA_TEXT, safeCharSequence(text));
Joe Onorato46439ce2010-11-19 13:56:21 -08002447 return this;
2448 }
2449
Joe Onoratocb109a02011-01-18 17:57:41 -08002450 /**
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07002451 * This provides some additional information that is displayed in the notification. No
2452 * guarantees are given where exactly it is displayed.
2453 *
2454 * <p>This information should only be provided if it provides an essential
2455 * benefit to the understanding of the notification. The more text you provide the
2456 * less readable it becomes. For example, an email client should only provide the account
2457 * name here if more than one email account has been added.</p>
2458 *
2459 * <p>As of {@link android.os.Build.VERSION_CODES#N} this information is displayed in the
2460 * notification header area.
2461 *
2462 * On Android versions before {@link android.os.Build.VERSION_CODES#N}
2463 * this will be shown in the third line of text in the platform notification template.
2464 * You should not be using {@link #setProgress(int, int, boolean)} at the
2465 * same time on those versions; they occupy the same place.
2466 * </p>
Daniel Sandlerf3b73432012-03-27 15:01:25 -04002467 */
2468 public Builder setSubText(CharSequence text) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002469 mN.extras.putCharSequence(EXTRA_SUB_TEXT, safeCharSequence(text));
Daniel Sandlerf3b73432012-03-27 15:01:25 -04002470 return this;
2471 }
2472
2473 /**
Adrian Roose458aa82015-12-08 16:17:19 -08002474 * Set the remote input history.
2475 *
2476 * This should be set to the most recent inputs that have been sent
2477 * through a {@link RemoteInput} of this Notification and cleared once the it is no
2478 * longer relevant (e.g. for chat notifications once the other party has responded).
2479 *
2480 * The most recent input must be stored at the 0 index, the second most recent at the
2481 * 1 index, etc. Note that the system will limit both how far back the inputs will be shown
2482 * and how much of each individual input is shown.
2483 *
2484 * <p>Note: The reply text will only be shown on notifications that have least one action
2485 * with a {@code RemoteInput}.</p>
2486 */
2487 public Builder setRemoteInputHistory(CharSequence[] text) {
2488 if (text == null) {
2489 mN.extras.putCharSequenceArray(EXTRA_REMOTE_INPUT_HISTORY, null);
2490 } else {
2491 final int N = Math.min(MAX_REPLY_HISTORY, text.length);
2492 CharSequence[] safe = new CharSequence[N];
2493 for (int i = 0; i < N; i++) {
2494 safe[i] = safeCharSequence(text[i]);
2495 }
2496 mN.extras.putCharSequenceArray(EXTRA_REMOTE_INPUT_HISTORY, safe);
2497 }
2498 return this;
2499 }
2500
2501 /**
Joe Onoratocb109a02011-01-18 17:57:41 -08002502 * Set the large number at the right-hand side of the notification. This is
2503 * equivalent to setContentInfo, although it might show the number in a different
2504 * font size for readability.
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07002505 *
2506 * @deprecated this number is not shown anywhere anymore
Joe Onoratocb109a02011-01-18 17:57:41 -08002507 */
Joe Onorato8595a3d2010-11-19 18:12:07 -08002508 public Builder setNumber(int number) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002509 mN.number = number;
Joe Onorato8595a3d2010-11-19 18:12:07 -08002510 return this;
2511 }
2512
Joe Onoratocb109a02011-01-18 17:57:41 -08002513 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002514 * A small piece of additional information pertaining to this notification.
2515 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002516 * The platform template will draw this on the last line of the notification, at the far
2517 * right (to the right of a smallIcon if it has been placed there).
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07002518 *
2519 * @deprecated use {@link #setSubText(CharSequence)} instead to set a text in the header.
2520 * For legacy apps targeting a version below {@link android.os.Build.VERSION_CODES#N} this
2521 * field will still show up, but the subtext will take precedence.
Joe Onoratocb109a02011-01-18 17:57:41 -08002522 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002523 public Builder setContentInfo(CharSequence info) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002524 mN.extras.putCharSequence(EXTRA_INFO_TEXT, safeCharSequence(info));
Joe Onorato46439ce2010-11-19 13:56:21 -08002525 return this;
2526 }
2527
Joe Onoratocb109a02011-01-18 17:57:41 -08002528 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002529 * Set the progress this notification represents.
2530 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002531 * The platform template will represent this using a {@link ProgressBar}.
Jeff Sharkey1c400132011-08-05 14:50:13 -07002532 */
2533 public Builder setProgress(int max, int progress, boolean indeterminate) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002534 mN.extras.putInt(EXTRA_PROGRESS, progress);
2535 mN.extras.putInt(EXTRA_PROGRESS_MAX, max);
2536 mN.extras.putBoolean(EXTRA_PROGRESS_INDETERMINATE, indeterminate);
Jeff Sharkey1c400132011-08-05 14:50:13 -07002537 return this;
2538 }
2539
2540 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002541 * Supply a custom RemoteViews to use instead of the platform template.
2542 *
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002543 * Use {@link #setCustomContentView(RemoteViews)} instead.
Joe Onoratocb109a02011-01-18 17:57:41 -08002544 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002545 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08002546 public Builder setContent(RemoteViews views) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002547 return setCustomContentView(views);
2548 }
2549
2550 /**
2551 * Supply custom RemoteViews to use instead of the platform template.
2552 *
2553 * This will override the layout that would otherwise be constructed by this Builder
2554 * object.
2555 */
2556 public Builder setCustomContentView(RemoteViews contentView) {
2557 mN.contentView = contentView;
2558 return this;
2559 }
2560
2561 /**
2562 * Supply custom RemoteViews to use instead of the platform template in the expanded form.
2563 *
2564 * This will override the expanded layout that would otherwise be constructed by this
2565 * Builder object.
2566 */
2567 public Builder setCustomBigContentView(RemoteViews contentView) {
2568 mN.bigContentView = contentView;
2569 return this;
2570 }
2571
2572 /**
2573 * Supply custom RemoteViews to use instead of the platform template in the heads up dialog.
2574 *
2575 * This will override the heads-up layout that would otherwise be constructed by this
2576 * Builder object.
2577 */
2578 public Builder setCustomHeadsUpContentView(RemoteViews contentView) {
2579 mN.headsUpContentView = contentView;
Joe Onorato46439ce2010-11-19 13:56:21 -08002580 return this;
2581 }
2582
Joe Onoratocb109a02011-01-18 17:57:41 -08002583 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002584 * Supply a {@link PendingIntent} to be sent when the notification is clicked.
2585 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002586 * As of {@link android.os.Build.VERSION_CODES#HONEYCOMB}, if this field is unset and you
2587 * have specified a custom RemoteViews with {@link #setContent(RemoteViews)}, you can use
2588 * {@link RemoteViews#setOnClickPendingIntent RemoteViews.setOnClickPendingIntent(int,PendingIntent)}
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002589 * to assign PendingIntents to individual views in that custom layout (i.e., to create
Daniel Sandlerf3b73432012-03-27 15:01:25 -04002590 * clickable buttons inside the notification view).
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002591 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002592 * @see Notification#contentIntent Notification.contentIntent
Joe Onoratocb109a02011-01-18 17:57:41 -08002593 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002594 public Builder setContentIntent(PendingIntent intent) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002595 mN.contentIntent = intent;
Joe Onorato46439ce2010-11-19 13:56:21 -08002596 return this;
2597 }
2598
Joe Onoratocb109a02011-01-18 17:57:41 -08002599 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002600 * Supply a {@link PendingIntent} to send when the notification is cleared explicitly by the user.
2601 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002602 * @see Notification#deleteIntent
Joe Onoratocb109a02011-01-18 17:57:41 -08002603 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002604 public Builder setDeleteIntent(PendingIntent intent) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002605 mN.deleteIntent = intent;
Joe Onorato46439ce2010-11-19 13:56:21 -08002606 return this;
2607 }
2608
Joe Onoratocb109a02011-01-18 17:57:41 -08002609 /**
2610 * An intent to launch instead of posting the notification to the status bar.
2611 * Only for use with extremely high-priority notifications demanding the user's
2612 * <strong>immediate</strong> attention, such as an incoming phone call or
2613 * alarm clock that the user has explicitly set to a particular time.
2614 * If this facility is used for something else, please give the user an option
2615 * to turn it off and use a normal notification, as this can be extremely
2616 * disruptive.
2617 *
Chris Wren47c20a12014-06-18 17:27:29 -04002618 * <p>
2619 * The system UI may choose to display a heads-up notification, instead of
2620 * launching this intent, while the user is using the device.
2621 * </p>
2622 *
Joe Onoratocb109a02011-01-18 17:57:41 -08002623 * @param intent The pending intent to launch.
2624 * @param highPriority Passing true will cause this notification to be sent
2625 * even if other notifications are suppressed.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002626 *
2627 * @see Notification#fullScreenIntent
Joe Onoratocb109a02011-01-18 17:57:41 -08002628 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002629 public Builder setFullScreenIntent(PendingIntent intent, boolean highPriority) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002630 mN.fullScreenIntent = intent;
Joe Onorato46439ce2010-11-19 13:56:21 -08002631 setFlag(FLAG_HIGH_PRIORITY, highPriority);
2632 return this;
2633 }
2634
Joe Onoratocb109a02011-01-18 17:57:41 -08002635 /**
Dan Sandler5fcdf6e2014-07-18 11:31:15 -04002636 * Set the "ticker" text which is sent to accessibility services.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002637 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002638 * @see Notification#tickerText
Joe Onoratocb109a02011-01-18 17:57:41 -08002639 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002640 public Builder setTicker(CharSequence tickerText) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002641 mN.tickerText = safeCharSequence(tickerText);
Joe Onorato46439ce2010-11-19 13:56:21 -08002642 return this;
2643 }
2644
Joe Onoratocb109a02011-01-18 17:57:41 -08002645 /**
Dan Sandler5fcdf6e2014-07-18 11:31:15 -04002646 * Obsolete version of {@link #setTicker(CharSequence)}.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002647 *
Joe Onoratocb109a02011-01-18 17:57:41 -08002648 */
Dan Sandler5fcdf6e2014-07-18 11:31:15 -04002649 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08002650 public Builder setTicker(CharSequence tickerText, RemoteViews views) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002651 setTicker(tickerText);
2652 // views is ignored
Joe Onorato46439ce2010-11-19 13:56:21 -08002653 return this;
2654 }
2655
Joe Onoratocb109a02011-01-18 17:57:41 -08002656 /**
Dan Sandlerd63f9322015-05-06 15:18:49 -04002657 * Add a large icon to the notification content view.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002658 *
2659 * In the platform template, this image will be shown on the left of the notification view
Dan Sandlerd63f9322015-05-06 15:18:49 -04002660 * in place of the {@link #setSmallIcon(Icon) small icon} (which will be placed in a small
2661 * badge atop the large icon).
Dan Sandler08a04c12015-05-06 15:18:49 -04002662 */
Dan Sandlerd63f9322015-05-06 15:18:49 -04002663 public Builder setLargeIcon(Bitmap b) {
2664 return setLargeIcon(b != null ? Icon.createWithBitmap(b) : null);
2665 }
2666
2667 /**
2668 * Add a large icon to the notification content view.
2669 *
2670 * In the platform template, this image will be shown on the left of the notification view
2671 * in place of the {@link #setSmallIcon(Icon) small icon} (which will be placed in a small
2672 * badge atop the large icon).
2673 */
2674 public Builder setLargeIcon(Icon icon) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002675 mN.mLargeIcon = icon;
2676 mN.extras.putParcelable(EXTRA_LARGE_ICON, icon);
Joe Onorato46439ce2010-11-19 13:56:21 -08002677 return this;
2678 }
2679
Joe Onoratocb109a02011-01-18 17:57:41 -08002680 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002681 * Set the sound to play.
2682 *
John Spurlockc0650f022014-07-19 13:22:39 -04002683 * It will be played using the {@link #AUDIO_ATTRIBUTES_DEFAULT default audio attributes}
2684 * for notifications.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002685 *
Chris Wren47c20a12014-06-18 17:27:29 -04002686 * <p>
2687 * A notification that is noisy is more likely to be presented as a heads-up notification.
2688 * </p>
2689 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002690 * @see Notification#sound
Joe Onoratocb109a02011-01-18 17:57:41 -08002691 */
Joe Onorato52f80cd2010-11-21 15:34:48 -08002692 public Builder setSound(Uri sound) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002693 mN.sound = sound;
2694 mN.audioAttributes = AUDIO_ATTRIBUTES_DEFAULT;
Joe Onorato52f80cd2010-11-21 15:34:48 -08002695 return this;
2696 }
2697
Joe Onoratocb109a02011-01-18 17:57:41 -08002698 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002699 * Set the sound to play, along with a specific stream on which to play it.
Joe Onoratocb109a02011-01-18 17:57:41 -08002700 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002701 * See {@link android.media.AudioManager} for the <code>STREAM_</code> constants.
2702 *
Chris Wren47c20a12014-06-18 17:27:29 -04002703 * <p>
2704 * A notification that is noisy is more likely to be presented as a heads-up notification.
2705 * </p>
John Spurlockc0650f022014-07-19 13:22:39 -04002706 * @deprecated use {@link #setSound(Uri, AudioAttributes)} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002707 * @see Notification#sound
Joe Onoratocb109a02011-01-18 17:57:41 -08002708 */
Jean-Michel Trivi81f871e2014-08-06 16:32:38 -07002709 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08002710 public Builder setSound(Uri sound, int streamType) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002711 mN.sound = sound;
2712 mN.audioStreamType = streamType;
Joe Onorato46439ce2010-11-19 13:56:21 -08002713 return this;
2714 }
2715
Joe Onoratocb109a02011-01-18 17:57:41 -08002716 /**
John Spurlockc0650f022014-07-19 13:22:39 -04002717 * Set the sound to play, along with specific {@link AudioAttributes audio attributes} to
2718 * use during playback.
2719 *
2720 * <p>
2721 * A notification that is noisy is more likely to be presented as a heads-up notification.
2722 * </p>
2723 *
2724 * @see Notification#sound
2725 */
2726 public Builder setSound(Uri sound, AudioAttributes audioAttributes) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002727 mN.sound = sound;
2728 mN.audioAttributes = audioAttributes;
John Spurlockc0650f022014-07-19 13:22:39 -04002729 return this;
2730 }
2731
2732 /**
Joe Onoratocb109a02011-01-18 17:57:41 -08002733 * Set the vibration pattern to use.
2734 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002735 * See {@link android.os.Vibrator#vibrate(long[], int)} for a discussion of the
2736 * <code>pattern</code> parameter.
2737 *
Chris Wren47c20a12014-06-18 17:27:29 -04002738 * <p>
2739 * A notification that vibrates is more likely to be presented as a heads-up notification.
2740 * </p>
2741 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002742 * @see Notification#vibrate
Joe Onoratocb109a02011-01-18 17:57:41 -08002743 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002744 public Builder setVibrate(long[] pattern) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002745 mN.vibrate = pattern;
Joe Onorato46439ce2010-11-19 13:56:21 -08002746 return this;
2747 }
2748
Joe Onoratocb109a02011-01-18 17:57:41 -08002749 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002750 * Set the desired color for the indicator LED on the device, as well as the
2751 * blink duty cycle (specified in milliseconds).
2752 *
2753
2754 * Not all devices will honor all (or even any) of these values.
2755 *
2756
2757 * @see Notification#ledARGB
2758 * @see Notification#ledOnMS
2759 * @see Notification#ledOffMS
Joe Onoratocb109a02011-01-18 17:57:41 -08002760 */
Tor Norbye80756e32015-03-02 09:39:27 -08002761 public Builder setLights(@ColorInt int argb, int onMs, int offMs) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002762 mN.ledARGB = argb;
2763 mN.ledOnMS = onMs;
2764 mN.ledOffMS = offMs;
Julia Reynolds10ee1fc2015-11-09 11:04:55 -05002765 if (onMs != 0 || offMs != 0) {
2766 mN.flags |= FLAG_SHOW_LIGHTS;
2767 }
Joe Onorato46439ce2010-11-19 13:56:21 -08002768 return this;
2769 }
2770
Joe Onoratocb109a02011-01-18 17:57:41 -08002771 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002772 * Set whether this is an "ongoing" notification.
Joe Onoratocb109a02011-01-18 17:57:41 -08002773 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002774
2775 * Ongoing notifications cannot be dismissed by the user, so your application or service
2776 * must take care of canceling them.
2777 *
2778
2779 * They are typically used to indicate a background task that the user is actively engaged
2780 * with (e.g., playing music) or is pending in some way and therefore occupying the device
2781 * (e.g., a file download, sync operation, active network connection).
2782 *
2783
2784 * @see Notification#FLAG_ONGOING_EVENT
2785 * @see Service#setForeground(boolean)
Joe Onoratocb109a02011-01-18 17:57:41 -08002786 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002787 public Builder setOngoing(boolean ongoing) {
2788 setFlag(FLAG_ONGOING_EVENT, ongoing);
2789 return this;
2790 }
2791
Joe Onoratocb109a02011-01-18 17:57:41 -08002792 /**
2793 * Set this flag if you would only like the sound, vibrate
2794 * and ticker to be played if the notification is not already showing.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002795 *
2796 * @see Notification#FLAG_ONLY_ALERT_ONCE
Joe Onoratocb109a02011-01-18 17:57:41 -08002797 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002798 public Builder setOnlyAlertOnce(boolean onlyAlertOnce) {
2799 setFlag(FLAG_ONLY_ALERT_ONCE, onlyAlertOnce);
2800 return this;
2801 }
2802
Joe Onoratocb109a02011-01-18 17:57:41 -08002803 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002804 * Make this notification automatically dismissed when the user touches it. The
2805 * PendingIntent set with {@link #setDeleteIntent} will be sent when this happens.
2806 *
2807 * @see Notification#FLAG_AUTO_CANCEL
Joe Onoratocb109a02011-01-18 17:57:41 -08002808 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002809 public Builder setAutoCancel(boolean autoCancel) {
Joe Onorato281d83f2011-01-04 17:13:10 -08002810 setFlag(FLAG_AUTO_CANCEL, autoCancel);
Joe Onorato46439ce2010-11-19 13:56:21 -08002811 return this;
2812 }
2813
Joe Onoratocb109a02011-01-18 17:57:41 -08002814 /**
Griff Hazendfcb0802014-02-11 12:00:00 -08002815 * Set whether or not this notification should not bridge to other devices.
2816 *
2817 * <p>Some notifications can be bridged to other devices for remote display.
2818 * This hint can be set to recommend this notification not be bridged.
2819 */
2820 public Builder setLocalOnly(boolean localOnly) {
2821 setFlag(FLAG_LOCAL_ONLY, localOnly);
2822 return this;
2823 }
2824
2825 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002826 * Set which notification properties will be inherited from system defaults.
Joe Onoratocb109a02011-01-18 17:57:41 -08002827 * <p>
2828 * The value should be one or more of the following fields combined with
2829 * bitwise-or:
2830 * {@link #DEFAULT_SOUND}, {@link #DEFAULT_VIBRATE}, {@link #DEFAULT_LIGHTS}.
2831 * <p>
2832 * For all default values, use {@link #DEFAULT_ALL}.
2833 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002834 public Builder setDefaults(int defaults) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002835 mN.defaults = defaults;
Joe Onorato46439ce2010-11-19 13:56:21 -08002836 return this;
2837 }
2838
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002839 /**
2840 * Set the priority of this notification.
2841 *
2842 * @see Notification#priority
2843 */
Tor Norbyed9273d62013-05-30 15:59:53 -07002844 public Builder setPriority(@Priority int pri) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002845 mN.priority = pri;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002846 return this;
2847 }
Joe Malin8d40d042012-11-05 11:36:40 -08002848
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002849 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -04002850 * Set the notification category.
Joe Malin8d40d042012-11-05 11:36:40 -08002851 *
John Spurlockfd7f1e02014-03-18 16:41:57 -04002852 * @see Notification#category
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002853 */
John Spurlockfd7f1e02014-03-18 16:41:57 -04002854 public Builder setCategory(String category) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002855 mN.category = category;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002856 return this;
2857 }
2858
2859 /**
Chris Wrendde75302014-03-26 17:24:15 -04002860 * Add a person that is relevant to this notification.
2861 *
Chris Wrene6c48932014-09-29 17:19:27 -04002862 * <P>
2863 * Depending on user preferences, this annotation may allow the notification to pass
2864 * through interruption filters, and to appear more prominently in the user interface.
2865 * </P>
2866 *
2867 * <P>
2868 * The person should be specified by the {@code String} representation of a
2869 * {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI}.
2870 * </P>
2871 *
2872 * <P>The system will also attempt to resolve {@code mailto:} and {@code tel:} schema
2873 * URIs. The path part of these URIs must exist in the contacts database, in the
2874 * appropriate column, or the reference will be discarded as invalid. Telephone schema
2875 * URIs will be resolved by {@link android.provider.ContactsContract.PhoneLookup}.
2876 * </P>
2877 *
2878 * @param uri A URI for the person.
Chris Wrendde75302014-03-26 17:24:15 -04002879 * @see Notification#EXTRA_PEOPLE
2880 */
Chris Wrene6c48932014-09-29 17:19:27 -04002881 public Builder addPerson(String uri) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002882 mPersonList.add(uri);
Chris Wrendde75302014-03-26 17:24:15 -04002883 return this;
2884 }
2885
2886 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002887 * Set this notification to be part of a group of notifications sharing the same key.
2888 * Grouped notifications may display in a cluster or stack on devices which
2889 * support such rendering.
2890 *
2891 * <p>To make this notification the summary for its group, also call
2892 * {@link #setGroupSummary}. A sort order can be specified for group members by using
2893 * {@link #setSortKey}.
2894 * @param groupKey The group key of the group.
2895 * @return this object for method chaining
2896 */
2897 public Builder setGroup(String groupKey) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002898 mN.mGroupKey = groupKey;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002899 return this;
2900 }
2901
2902 /**
2903 * Set this notification to be the group summary for a group of notifications.
2904 * Grouped notifications may display in a cluster or stack on devices which
2905 * support such rendering. Requires a group key also be set using {@link #setGroup}.
2906 * @param isGroupSummary Whether this notification should be a group summary.
2907 * @return this object for method chaining
2908 */
2909 public Builder setGroupSummary(boolean isGroupSummary) {
2910 setFlag(FLAG_GROUP_SUMMARY, isGroupSummary);
2911 return this;
2912 }
2913
2914 /**
2915 * Set a sort key that orders this notification among other notifications from the
2916 * same package. This can be useful if an external sort was already applied and an app
2917 * would like to preserve this. Notifications will be sorted lexicographically using this
2918 * value, although providing different priorities in addition to providing sort key may
2919 * cause this value to be ignored.
2920 *
2921 * <p>This sort key can also be used to order members of a notification group. See
Griff Hazen9e1379f2014-05-20 12:50:51 -07002922 * {@link #setGroup}.
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002923 *
2924 * @see String#compareTo(String)
2925 */
2926 public Builder setSortKey(String sortKey) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002927 mN.mSortKey = sortKey;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002928 return this;
2929 }
2930
2931 /**
Griff Hazen720042b2014-02-24 15:46:56 -08002932 * Merge additional metadata into this notification.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002933 *
Griff Hazen720042b2014-02-24 15:46:56 -08002934 * <p>Values within the Bundle will replace existing extras values in this Builder.
2935 *
2936 * @see Notification#extras
2937 */
Griff Hazen959591e2014-05-15 22:26:18 -07002938 public Builder addExtras(Bundle extras) {
2939 if (extras != null) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002940 mUserExtras.putAll(extras);
Griff Hazen720042b2014-02-24 15:46:56 -08002941 }
2942 return this;
2943 }
2944
2945 /**
2946 * Set metadata for this notification.
2947 *
2948 * <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 -04002949 * current contents are copied into the Notification each time {@link #build()} is
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002950 * called.
2951 *
Griff Hazen720042b2014-02-24 15:46:56 -08002952 * <p>Replaces any existing extras values with those from the provided Bundle.
2953 * Use {@link #addExtras} to merge in metadata instead.
2954 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002955 * @see Notification#extras
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002956 */
Griff Hazen959591e2014-05-15 22:26:18 -07002957 public Builder setExtras(Bundle extras) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002958 if (extras != null) {
2959 mUserExtras = extras;
2960 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002961 return this;
2962 }
2963
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002964 /**
Griff Hazen720042b2014-02-24 15:46:56 -08002965 * Get the current metadata Bundle used by this notification Builder.
2966 *
2967 * <p>The returned Bundle is shared with this Builder.
2968 *
2969 * <p>The current contents of this Bundle are copied into the Notification each time
2970 * {@link #build()} is called.
2971 *
2972 * @see Notification#extras
2973 */
2974 public Bundle getExtras() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002975 return mUserExtras;
2976 }
2977
2978 private Bundle getAllExtras() {
2979 final Bundle saveExtras = (Bundle) mUserExtras.clone();
2980 saveExtras.putAll(mN.extras);
2981 return saveExtras;
Griff Hazen720042b2014-02-24 15:46:56 -08002982 }
2983
2984 /**
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002985 * Add an action to this notification. Actions are typically displayed by
2986 * the system as a button adjacent to the notification content.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04002987 * <p>
2988 * Every action must have an icon (32dp square and matching the
2989 * <a href="{@docRoot}design/style/iconography.html#action-bar">Holo
2990 * Dark action bar</a> visual style), a textual label, and a {@link PendingIntent}.
2991 * <p>
2992 * A notification in its expanded form can display up to 3 actions, from left to right in
2993 * the order they were added. Actions will not be displayed when the notification is
2994 * collapsed, however, so be sure that any essential functions may be accessed by the user
2995 * in some other way (for example, in the Activity pointed to by {@link #contentIntent}).
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002996 *
2997 * @param icon Resource ID of a drawable that represents the action.
2998 * @param title Text describing the action.
2999 * @param intent PendingIntent to be fired when the action is invoked.
Dan Sandler86647982015-05-13 23:41:13 -04003000 *
3001 * @deprecated Use {@link #addAction(Action)} instead.
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003002 */
Dan Sandler86647982015-05-13 23:41:13 -04003003 @Deprecated
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003004 public Builder addAction(int icon, CharSequence title, PendingIntent intent) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04003005 mActions.add(new Action(icon, safeCharSequence(title), intent));
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003006 return this;
3007 }
3008
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003009 /**
Griff Hazen959591e2014-05-15 22:26:18 -07003010 * Add an action to this notification. Actions are typically displayed by
3011 * the system as a button adjacent to the notification content.
3012 * <p>
3013 * Every action must have an icon (32dp square and matching the
3014 * <a href="{@docRoot}design/style/iconography.html#action-bar">Holo
3015 * Dark action bar</a> visual style), a textual label, and a {@link PendingIntent}.
3016 * <p>
3017 * A notification in its expanded form can display up to 3 actions, from left to right in
3018 * the order they were added. Actions will not be displayed when the notification is
3019 * collapsed, however, so be sure that any essential functions may be accessed by the user
3020 * in some other way (for example, in the Activity pointed to by {@link #contentIntent}).
3021 *
3022 * @param action The action to add.
3023 */
3024 public Builder addAction(Action action) {
3025 mActions.add(action);
3026 return this;
3027 }
3028
3029 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003030 * Alter the complete list of actions attached to this notification.
3031 * @see #addAction(Action).
3032 *
3033 * @param actions
3034 * @return
3035 */
3036 public Builder setActions(Action... actions) {
3037 mActions.clear();
3038 for (int i = 0; i < actions.length; i++) {
3039 mActions.add(actions[i]);
3040 }
3041 return this;
3042 }
3043
3044 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003045 * Add a rich notification style to be applied at build time.
3046 *
3047 * @param style Object responsible for modifying the notification style.
3048 */
3049 public Builder setStyle(Style style) {
3050 if (mStyle != style) {
3051 mStyle = style;
Daniel Sandlerc08dea22012-06-28 08:35:24 -07003052 if (mStyle != null) {
3053 mStyle.setBuilder(this);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003054 mN.extras.putString(EXTRA_TEMPLATE, style.getClass().getName());
3055 } else {
3056 mN.extras.remove(EXTRA_TEMPLATE);
Daniel Sandlerc08dea22012-06-28 08:35:24 -07003057 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003058 }
3059 return this;
3060 }
3061
Dan Sandler0bf2ed82013-12-21 23:33:41 -06003062 /**
3063 * Specify the value of {@link #visibility}.
Griff Hazenb720abe2014-05-20 13:15:30 -07003064 *
Dan Sandler0bf2ed82013-12-21 23:33:41 -06003065 * @param visibility One of {@link #VISIBILITY_PRIVATE} (the default),
3066 * {@link #VISIBILITY_SECRET}, or {@link #VISIBILITY_PUBLIC}.
3067 *
3068 * @return The same Builder.
3069 */
3070 public Builder setVisibility(int visibility) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003071 mN.visibility = visibility;
Dan Sandler0bf2ed82013-12-21 23:33:41 -06003072 return this;
3073 }
3074
3075 /**
3076 * Supply a replacement Notification whose contents should be shown in insecure contexts
3077 * (i.e. atop the secure lockscreen). See {@link #visibility} and {@link #VISIBILITY_PUBLIC}.
3078 * @param n A replacement notification, presumably with some or all info redacted.
3079 * @return The same Builder.
3080 */
3081 public Builder setPublicVersion(Notification n) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003082 if (n != null) {
3083 mN.publicVersion = new Notification();
3084 n.cloneInto(mN.publicVersion, /*heavy=*/ true);
3085 } else {
3086 mN.publicVersion = null;
3087 }
Dan Sandler0bf2ed82013-12-21 23:33:41 -06003088 return this;
3089 }
3090
Griff Hazenb720abe2014-05-20 13:15:30 -07003091 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003092 * Apply an extender to this notification builder. Extenders may be used to add
3093 * metadata or change options on this builder.
3094 */
Griff Hazen61a9e862014-05-22 16:05:19 -07003095 public Builder extend(Extender extender) {
3096 extender.extend(this);
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003097 return this;
3098 }
3099
Dan Sandler4e787062015-06-17 15:09:48 -04003100 /**
3101 * @hide
3102 */
Julia Reynoldse46bb372016-03-17 11:05:58 -04003103 public Builder setFlag(int mask, boolean value) {
Joe Onorato46439ce2010-11-19 13:56:21 -08003104 if (value) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003105 mN.flags |= mask;
Joe Onorato46439ce2010-11-19 13:56:21 -08003106 } else {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003107 mN.flags &= ~mask;
Joe Onorato46439ce2010-11-19 13:56:21 -08003108 }
Julia Reynoldse46bb372016-03-17 11:05:58 -04003109 return this;
Joe Onorato46439ce2010-11-19 13:56:21 -08003110 }
3111
Dan Sandler26e81cf2014-05-06 10:01:27 -04003112 /**
3113 * Sets {@link Notification#color}.
3114 *
3115 * @param argb The accent color to use
3116 *
3117 * @return The same Builder.
3118 */
Tor Norbye80756e32015-03-02 09:39:27 -08003119 public Builder setColor(@ColorInt int argb) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003120 mN.color = argb;
Julia Reynolds10ee1fc2015-11-09 11:04:55 -05003121 sanitizeColor();
Dan Sandler26e81cf2014-05-06 10:01:27 -04003122 return this;
3123 }
3124
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003125 private Drawable getProfileBadgeDrawable() {
Chris Wren66619a22016-05-12 16:42:37 -04003126 if (mContext.getUserId() == UserHandle.USER_SYSTEM) {
3127 // This user can never be a badged profile,
3128 // and also includes USER_ALL system notifications.
3129 return null;
3130 }
Christoph Studer7ac80e62014-08-04 16:01:57 +02003131 // Note: This assumes that the current user can read the profile badge of the
3132 // originating user.
Selim Cineke6ff9462016-01-15 15:07:06 -08003133 return mContext.getPackageManager().getUserBadgeForDensityNoBackground(
Julia Reynoldsda303542015-11-23 14:00:20 -05003134 new UserHandle(mContext.getUserId()), 0);
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003135 }
3136
3137 private Bitmap getProfileBadge() {
3138 Drawable badge = getProfileBadgeDrawable();
Kenny Guy8a0101b2014-05-08 23:34:12 +01003139 if (badge == null) {
3140 return null;
3141 }
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003142 final int size = mContext.getResources().getDimensionPixelSize(
3143 R.dimen.notification_badge_size);
3144 Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
Kenny Guy8a0101b2014-05-08 23:34:12 +01003145 Canvas canvas = new Canvas(bitmap);
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003146 badge.setBounds(0, 0, size, size);
Kenny Guy8a0101b2014-05-08 23:34:12 +01003147 badge.draw(canvas);
3148 return bitmap;
3149 }
3150
Selim Cinekc848c3a2016-01-13 15:27:30 -08003151 private void bindProfileBadge(RemoteViews contentView) {
Kenny Guy98193ea2014-07-24 19:54:37 +01003152 Bitmap profileBadge = getProfileBadge();
3153
Kenny Guy98193ea2014-07-24 19:54:37 +01003154 if (profileBadge != null) {
Selim Cinekc848c3a2016-01-13 15:27:30 -08003155 contentView.setImageViewBitmap(R.id.profile_badge, profileBadge);
3156 contentView.setViewVisibility(R.id.profile_badge, View.VISIBLE);
Kenny Guy98193ea2014-07-24 19:54:37 +01003157 }
Kenny Guy98193ea2014-07-24 19:54:37 +01003158 }
3159
Christoph Studerfe718432014-09-01 18:21:18 +02003160 private void resetStandardTemplate(RemoteViews contentView) {
Selim Cinekeaa29ca2015-11-23 13:51:13 -08003161 resetNotificationHeader(contentView);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003162 resetContentMargins(contentView);
Christoph Studerfe718432014-09-01 18:21:18 +02003163 contentView.setViewVisibility(R.id.right_icon, View.GONE);
Selim Cinek860b6da2015-12-16 19:02:19 -08003164 contentView.setViewVisibility(R.id.title, View.GONE);
Christoph Studerfe718432014-09-01 18:21:18 +02003165 contentView.setTextViewText(R.id.title, null);
Selim Cinek41598732016-01-11 16:58:37 -08003166 contentView.setViewVisibility(R.id.text, View.GONE);
Christoph Studerfe718432014-09-01 18:21:18 +02003167 contentView.setTextViewText(R.id.text, null);
Selim Cinek29603462015-11-17 19:04:39 -08003168 contentView.setViewVisibility(R.id.text_line_1, View.GONE);
Selim Cinek41598732016-01-11 16:58:37 -08003169 contentView.setTextViewText(R.id.text_line_1, null);
Christoph Studerfe718432014-09-01 18:21:18 +02003170 contentView.setViewVisibility(R.id.progress, View.GONE);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003171 }
3172
Selim Cinekeaa29ca2015-11-23 13:51:13 -08003173 /**
3174 * Resets the notification header to its original state
3175 */
3176 private void resetNotificationHeader(RemoteViews contentView) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003177 contentView.setImageViewResource(R.id.icon, 0);
Selim Cinek7b836392015-12-04 20:02:59 -08003178 contentView.setBoolean(R.id.notification_header, "setExpanded", false);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003179 contentView.setTextViewText(R.id.app_name_text, null);
Christoph Studerca1db712014-09-10 17:31:33 +02003180 contentView.setViewVisibility(R.id.chronometer, View.GONE);
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07003181 contentView.setViewVisibility(R.id.header_text, View.GONE);
3182 contentView.setViewVisibility(R.id.header_text_divider, View.GONE);
Selim Cinek29603462015-11-17 19:04:39 -08003183 contentView.setViewVisibility(R.id.time_divider, View.GONE);
Selim Cinekb85f36fd2016-04-20 18:46:36 -07003184 contentView.setViewVisibility(R.id.time, View.GONE);
Selim Cinekc848c3a2016-01-13 15:27:30 -08003185 contentView.setImageViewIcon(R.id.profile_badge, null);
3186 contentView.setViewVisibility(R.id.profile_badge, View.GONE);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003187 }
3188
3189 private void resetContentMargins(RemoteViews contentView) {
Adrian Roos2d5dbba2016-06-08 17:11:53 -07003190 contentView.setViewLayoutMarginEndDimen(R.id.line1, 0);
3191 contentView.setViewLayoutMarginEndDimen(R.id.text, 0);
Christoph Studerfe718432014-09-01 18:21:18 +02003192 }
3193
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003194 private RemoteViews applyStandardTemplate(int resId) {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02003195 return applyStandardTemplate(resId, true /* hasProgress */);
3196 }
3197
3198 /**
3199 * @param hasProgress whether the progress bar should be shown and set
3200 */
3201 private RemoteViews applyStandardTemplate(int resId, boolean hasProgress) {
Adrian Roosc1a80b02016-04-05 14:54:55 -07003202 final Bundle ex = mN.extras;
3203
3204 CharSequence title = processLegacyText(ex.getCharSequence(EXTRA_TITLE));
3205 CharSequence text = processLegacyText(ex.getCharSequence(EXTRA_TEXT));
3206 return applyStandardTemplate(resId, hasProgress, title, text);
3207 }
3208
3209 /**
3210 * @param hasProgress whether the progress bar should be shown and set
3211 */
3212 private RemoteViews applyStandardTemplate(int resId, boolean hasProgress,
3213 CharSequence title, CharSequence text) {
Kenny Guy77320062014-08-27 21:37:15 +01003214 RemoteViews contentView = new BuilderRemoteViews(mContext.getApplicationInfo(), resId);
Dan Sandler539aad42014-08-04 00:43:39 -04003215
Christoph Studerfe718432014-09-01 18:21:18 +02003216 resetStandardTemplate(contentView);
3217
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003218 final Bundle ex = mN.extras;
Dan Sandler190d58d2014-05-15 09:33:39 -04003219
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003220 bindNotificationHeader(contentView);
3221 bindLargeIcon(contentView);
Selim Cinek954cc232016-05-20 13:29:23 -07003222 boolean showProgress = handleProgressBar(hasProgress, contentView, ex);
Adrian Roosc1a80b02016-04-05 14:54:55 -07003223 if (title != null) {
Selim Cinek860b6da2015-12-16 19:02:19 -08003224 contentView.setViewVisibility(R.id.title, View.VISIBLE);
Adrian Roosc1a80b02016-04-05 14:54:55 -07003225 contentView.setTextViewText(R.id.title, title);
Selim Cinek954cc232016-05-20 13:29:23 -07003226 contentView.setViewLayoutWidth(R.id.title, showProgress
3227 ? ViewGroup.LayoutParams.WRAP_CONTENT
3228 : ViewGroup.LayoutParams.MATCH_PARENT);
Joe Onorato561d3852010-11-20 18:09:34 -08003229 }
Adrian Roosc1a80b02016-04-05 14:54:55 -07003230 if (text != null) {
Selim Cinek41598732016-01-11 16:58:37 -08003231 int textId = showProgress ? com.android.internal.R.id.text_line_1
3232 : com.android.internal.R.id.text;
Adrian Roosc1a80b02016-04-05 14:54:55 -07003233 contentView.setTextViewText(textId, text);
Selim Cinek41598732016-01-11 16:58:37 -08003234 contentView.setViewVisibility(textId, View.VISIBLE);
Joe Onorato561d3852010-11-20 18:09:34 -08003235 }
Selim Cinekc848c3a2016-01-13 15:27:30 -08003236
Selim Cinek279fa862016-06-14 10:57:25 -07003237 setContentMinHeight(contentView, showProgress || mN.hasLargeIcon());
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003238
Selim Cinek29603462015-11-17 19:04:39 -08003239 return contentView;
3240 }
3241
Selim Cinek860b6da2015-12-16 19:02:19 -08003242 /**
3243 * @param remoteView the remote view to update the minheight in
3244 * @param hasMinHeight does it have a mimHeight
3245 * @hide
3246 */
3247 void setContentMinHeight(RemoteViews remoteView, boolean hasMinHeight) {
3248 int minHeight = 0;
3249 if (hasMinHeight) {
3250 // we need to set the minHeight of the notification
3251 minHeight = mContext.getResources().getDimensionPixelSize(
3252 com.android.internal.R.dimen.notification_min_content_height);
3253 }
3254 remoteView.setInt(R.id.notification_main_column, "setMinimumHeight", minHeight);
3255 }
3256
Selim Cinek29603462015-11-17 19:04:39 -08003257 private boolean handleProgressBar(boolean hasProgress, RemoteViews contentView, Bundle ex) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003258 final int max = ex.getInt(EXTRA_PROGRESS_MAX, 0);
3259 final int progress = ex.getInt(EXTRA_PROGRESS, 0);
3260 final boolean ind = ex.getBoolean(EXTRA_PROGRESS_INDETERMINATE);
3261 if (hasProgress && (max != 0 || ind)) {
Selim Cinek29603462015-11-17 19:04:39 -08003262 contentView.setViewVisibility(com.android.internal.R.id.progress, View.VISIBLE);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003263 contentView.setProgressBar(
Selim Cinek29603462015-11-17 19:04:39 -08003264 R.id.progress, max, progress, ind);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003265 contentView.setProgressBackgroundTintList(
3266 R.id.progress, ColorStateList.valueOf(mContext.getColor(
3267 R.color.notification_progress_background_color)));
Selim Cinek29603462015-11-17 19:04:39 -08003268 if (mN.color != COLOR_DEFAULT) {
Adrian Roos4ff3b122016-02-01 12:26:13 -08003269 ColorStateList colorStateList = ColorStateList.valueOf(resolveContrastColor());
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003270 contentView.setProgressTintList(R.id.progress, colorStateList);
3271 contentView.setProgressIndeterminateTintList(R.id.progress, colorStateList);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003272 }
Selim Cinek29603462015-11-17 19:04:39 -08003273 return true;
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003274 } else {
3275 contentView.setViewVisibility(R.id.progress, View.GONE);
Selim Cinek29603462015-11-17 19:04:39 -08003276 return false;
Jeff Sharkey1c400132011-08-05 14:50:13 -07003277 }
Joe Onorato561d3852010-11-20 18:09:34 -08003278 }
3279
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003280 private void bindLargeIcon(RemoteViews contentView) {
Selim Cinek279fa862016-06-14 10:57:25 -07003281 if (mN.mLargeIcon == null && mN.largeIcon != null) {
3282 mN.mLargeIcon = Icon.createWithBitmap(mN.largeIcon);
3283 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003284 if (mN.mLargeIcon != null) {
3285 contentView.setViewVisibility(R.id.right_icon, View.VISIBLE);
3286 contentView.setImageViewIcon(R.id.right_icon, mN.mLargeIcon);
3287 processLargeLegacyIcon(mN.mLargeIcon, contentView);
Adrian Roos2d5dbba2016-06-08 17:11:53 -07003288 int endMargin = R.dimen.notification_content_picture_margin;
3289 contentView.setViewLayoutMarginEndDimen(R.id.line1, endMargin);
3290 contentView.setViewLayoutMarginEndDimen(R.id.text, endMargin);
3291 contentView.setViewLayoutMarginEndDimen(R.id.progress, endMargin);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003292 }
3293 }
3294
3295 private void bindNotificationHeader(RemoteViews contentView) {
3296 bindSmallIcon(contentView);
3297 bindHeaderAppName(contentView);
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07003298 bindHeaderText(contentView);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003299 bindHeaderChronometerAndTime(contentView);
3300 bindExpandButton(contentView);
Selim Cinekc848c3a2016-01-13 15:27:30 -08003301 bindProfileBadge(contentView);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003302 }
3303
3304 private void bindExpandButton(RemoteViews contentView) {
Adrian Roos4ff3b122016-02-01 12:26:13 -08003305 contentView.setDrawableParameters(R.id.expand_button, false, -1, resolveContrastColor(),
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003306 PorterDuff.Mode.SRC_ATOP, -1);
Selim Cinekea4bef72015-12-02 15:51:10 -08003307 contentView.setInt(R.id.notification_header, "setOriginalNotificationColor",
Adrian Roos4ff3b122016-02-01 12:26:13 -08003308 resolveContrastColor());
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003309 }
3310
3311 private void bindHeaderChronometerAndTime(RemoteViews contentView) {
3312 if (showsTimeOrChronometer()) {
Selim Cinek29603462015-11-17 19:04:39 -08003313 contentView.setViewVisibility(R.id.time_divider, View.VISIBLE);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003314 if (mN.extras.getBoolean(EXTRA_SHOW_CHRONOMETER)) {
3315 contentView.setViewVisibility(R.id.chronometer, View.VISIBLE);
3316 contentView.setLong(R.id.chronometer, "setBase",
3317 mN.when + (SystemClock.elapsedRealtime() - System.currentTimeMillis()));
3318 contentView.setBoolean(R.id.chronometer, "setStarted", true);
Adrian Roos96b7e202016-05-17 13:50:38 -07003319 boolean countsDown = mN.extras.getBoolean(EXTRA_CHRONOMETER_COUNT_DOWN);
Selim Cinekc3b752e2016-04-20 16:13:59 -07003320 contentView.setChronometerCountDown(R.id.chronometer, countsDown);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003321 } else {
3322 contentView.setViewVisibility(R.id.time, View.VISIBLE);
3323 contentView.setLong(R.id.time, "setTime", mN.when);
3324 }
Selim Cinekb85f36fd2016-04-20 18:46:36 -07003325 } else {
3326 // We still want a time to be set but gone, such that we can show and hide it
3327 // on demand in case it's a child notification without anything in the header
3328 contentView.setLong(R.id.time, "setTime", mN.when != 0 ? mN.when : mN.creationTime);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003329 }
3330 }
3331
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07003332 private void bindHeaderText(RemoteViews contentView) {
3333 CharSequence headerText = mN.extras.getCharSequence(EXTRA_SUB_TEXT);
3334 if (headerText == null && mStyle != null && mStyle.mSummaryTextSet
Selim Cinek03d0d652015-11-13 13:18:09 -05003335 && mStyle.hasSummaryInHeader()) {
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07003336 headerText = mStyle.mSummaryText;
Selim Cinek03d0d652015-11-13 13:18:09 -05003337 }
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07003338 if (headerText == null
3339 && mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N
3340 && mN.extras.getCharSequence(EXTRA_INFO_TEXT) != null) {
3341 headerText = mN.extras.getCharSequence(EXTRA_INFO_TEXT);
3342 }
3343 if (headerText != null) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003344 // TODO: Remove the span entirely to only have the string with propper formating.
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07003345 contentView.setTextViewText(R.id.header_text, processLegacyText(headerText));
3346 contentView.setViewVisibility(R.id.header_text, View.VISIBLE);
3347 contentView.setViewVisibility(R.id.header_text_divider, View.VISIBLE);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003348 }
3349 }
3350
Adrian Rooseba05822016-04-22 17:09:27 -07003351 /**
3352 * @hide
3353 */
3354 public String loadHeaderAppName() {
Dan Sandler732bd6c2016-04-12 14:20:32 -04003355 CharSequence name = null;
3356 final PackageManager pm = mContext.getPackageManager();
3357 if (mN.extras.containsKey(EXTRA_SUBSTITUTE_APP_NAME)) {
3358 // only system packages which lump together a bunch of unrelated stuff
3359 // may substitute a different name to make the purpose of the
3360 // notification more clear. the correct package label should always
3361 // be accessible via SystemUI.
3362 final String pkg = mContext.getPackageName();
3363 final String subName = mN.extras.getString(EXTRA_SUBSTITUTE_APP_NAME);
3364 if (PackageManager.PERMISSION_GRANTED == pm.checkPermission(
3365 android.Manifest.permission.SUBSTITUTE_NOTIFICATION_APP_NAME, pkg)) {
3366 name = subName;
3367 } else {
3368 Log.w(TAG, "warning: pkg "
3369 + pkg + " attempting to substitute app name '" + subName
3370 + "' without holding perm "
3371 + android.Manifest.permission.SUBSTITUTE_NOTIFICATION_APP_NAME);
3372 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003373 }
Dan Sandler732bd6c2016-04-12 14:20:32 -04003374 if (TextUtils.isEmpty(name)) {
3375 name = pm.getApplicationLabel(mContext.getApplicationInfo());
3376 }
3377 if (TextUtils.isEmpty(name)) {
3378 // still nothing?
3379 return null;
3380 }
3381
3382 return String.valueOf(name);
3383 }
3384 private void bindHeaderAppName(RemoteViews contentView) {
3385 contentView.setTextViewText(R.id.app_name_text, loadHeaderAppName());
Adrian Roos4ff3b122016-02-01 12:26:13 -08003386 contentView.setTextColor(R.id.app_name_text, resolveContrastColor());
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003387 }
3388
3389 private void bindSmallIcon(RemoteViews contentView) {
Selim Cinek279fa862016-06-14 10:57:25 -07003390 if (mN.mSmallIcon == null && mN.icon != 0) {
3391 mN.mSmallIcon = Icon.createWithResource(mContext, mN.icon);
3392 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003393 contentView.setImageViewIcon(R.id.icon, mN.mSmallIcon);
3394 processSmallIconColor(mN.mSmallIcon, contentView);
3395 }
3396
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003397 /**
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02003398 * @return true if the built notification will show the time or the chronometer; false
3399 * otherwise
3400 */
3401 private boolean showsTimeOrChronometer() {
Selim Cinekc2c0b042016-05-18 17:13:46 -07003402 return mN.showsTime() || mN.showsChronometer();
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02003403 }
3404
Christoph Studerfe718432014-09-01 18:21:18 +02003405 private void resetStandardTemplateWithActions(RemoteViews big) {
Adrian Roos4c1fcc82016-03-31 14:39:39 -07003406 // actions_container is only reset when there are no actions to avoid focus issues with
3407 // remote inputs.
Christoph Studerfe718432014-09-01 18:21:18 +02003408 big.setViewVisibility(R.id.actions, View.GONE);
Christoph Studerfe718432014-09-01 18:21:18 +02003409 big.removeAllViews(R.id.actions);
Adrian Roose458aa82015-12-08 16:17:19 -08003410
3411 big.setViewVisibility(R.id.notification_material_reply_container, View.GONE);
3412 big.setTextViewText(R.id.notification_material_reply_text_1, null);
3413
3414 big.setViewVisibility(R.id.notification_material_reply_text_2, View.GONE);
3415 big.setTextViewText(R.id.notification_material_reply_text_2, null);
3416 big.setViewVisibility(R.id.notification_material_reply_text_3, View.GONE);
3417 big.setTextViewText(R.id.notification_material_reply_text_3, null);
Adrian Roosf852a422016-06-03 13:33:43 -07003418
3419 big.setViewLayoutMarginBottomDimen(R.id.notification_action_list_margin_target, 0);
Christoph Studerfe718432014-09-01 18:21:18 +02003420 }
3421
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003422 private RemoteViews applyStandardTemplateWithActions(int layoutId) {
Adrian Roos48d746a2016-04-12 14:57:28 -07003423 final Bundle ex = mN.extras;
3424
3425 CharSequence title = processLegacyText(ex.getCharSequence(EXTRA_TITLE));
3426 CharSequence text = processLegacyText(ex.getCharSequence(EXTRA_TEXT));
3427 return applyStandardTemplateWithActions(layoutId, true /* hasProgress */, title, text);
3428 }
3429
3430 private RemoteViews applyStandardTemplateWithActions(int layoutId, boolean hasProgress,
3431 CharSequence title, CharSequence text) {
3432 RemoteViews big = applyStandardTemplate(layoutId, hasProgress, title, text);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003433
Christoph Studerfe718432014-09-01 18:21:18 +02003434 resetStandardTemplateWithActions(big);
3435
Adrian Roose458aa82015-12-08 16:17:19 -08003436 boolean validRemoteInput = false;
3437
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003438 int N = mActions.size();
3439 if (N > 0) {
Adrian Roos7052de52016-03-03 15:53:34 -08003440 big.setViewVisibility(R.id.actions_container, View.VISIBLE);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003441 big.setViewVisibility(R.id.actions, View.VISIBLE);
Adrian Roosf852a422016-06-03 13:33:43 -07003442 big.setViewLayoutMarginBottomDimen(R.id.notification_action_list_margin_target,
3443 R.dimen.notification_action_list_height);
Daniel Sandler8680bf82012-05-15 16:52:52 -04003444 if (N>MAX_ACTION_BUTTONS) N=MAX_ACTION_BUTTONS;
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003445 for (int i=0; i<N; i++) {
Adrian Roose458aa82015-12-08 16:17:19 -08003446 Action action = mActions.get(i);
3447 validRemoteInput |= hasValidRemoteInput(action);
3448
3449 final RemoteViews button = generateActionButton(action);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003450 big.addView(R.id.actions, button);
3451 }
Adrian Roos4c1fcc82016-03-31 14:39:39 -07003452 } else {
3453 big.setViewVisibility(R.id.actions_container, View.GONE);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003454 }
Adrian Roose458aa82015-12-08 16:17:19 -08003455
3456 CharSequence[] replyText = mN.extras.getCharSequenceArray(EXTRA_REMOTE_INPUT_HISTORY);
3457 if (validRemoteInput && replyText != null
3458 && replyText.length > 0 && !TextUtils.isEmpty(replyText[0])) {
3459 big.setViewVisibility(R.id.notification_material_reply_container, View.VISIBLE);
3460 big.setTextViewText(R.id.notification_material_reply_text_1, replyText[0]);
3461
3462 if (replyText.length > 1 && !TextUtils.isEmpty(replyText[1])) {
3463 big.setViewVisibility(R.id.notification_material_reply_text_2, View.VISIBLE);
3464 big.setTextViewText(R.id.notification_material_reply_text_2, replyText[1]);
3465
3466 if (replyText.length > 2 && !TextUtils.isEmpty(replyText[2])) {
3467 big.setViewVisibility(
3468 R.id.notification_material_reply_text_3, View.VISIBLE);
3469 big.setTextViewText(R.id.notification_material_reply_text_3, replyText[2]);
3470 }
3471 }
3472 }
3473
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003474 return big;
3475 }
3476
Adrian Roose458aa82015-12-08 16:17:19 -08003477 private boolean hasValidRemoteInput(Action action) {
3478 if (TextUtils.isEmpty(action.title) || action.actionIntent == null) {
3479 // Weird actions
3480 return false;
3481 }
3482
3483 RemoteInput[] remoteInputs = action.getRemoteInputs();
3484 if (remoteInputs == null) {
3485 return false;
3486 }
3487
3488 for (RemoteInput r : remoteInputs) {
3489 CharSequence[] choices = r.getChoices();
3490 if (r.getAllowFreeFormInput() || (choices != null && choices.length != 0)) {
3491 return true;
3492 }
3493 }
3494 return false;
3495 }
3496
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003497 /**
3498 * Construct a RemoteViews for the final 1U notification layout. In order:
3499 * 1. Custom contentView from the caller
3500 * 2. Style's proposed content view
3501 * 3. Standard template view
3502 */
Julia Reynolds3b848122016-02-26 10:45:32 -05003503 public RemoteViews createContentView() {
Selim Cinek593610c2016-02-16 18:42:57 -08003504 if (mN.contentView != null && (mStyle == null || !mStyle.displayCustomViewInline())) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003505 return mN.contentView;
3506 } else if (mStyle != null) {
3507 final RemoteViews styleView = mStyle.makeContentView();
3508 if (styleView != null) {
3509 return styleView;
3510 }
Joe Onorato46439ce2010-11-19 13:56:21 -08003511 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003512 return applyStandardTemplate(getBaseLayoutResource());
Joe Onorato46439ce2010-11-19 13:56:21 -08003513 }
3514
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003515 /**
3516 * Construct a RemoteViews for the final big notification layout.
3517 */
Julia Reynolds3b848122016-02-26 10:45:32 -05003518 public RemoteViews createBigContentView() {
Selim Cinek850a8542015-11-11 11:48:36 -05003519 RemoteViews result = null;
Selim Cinek593610c2016-02-16 18:42:57 -08003520 if (mN.bigContentView != null
3521 && (mStyle == null || !mStyle.displayCustomViewInline())) {
Julia Reynolds089e3e42015-11-18 09:59:57 -05003522 return mN.bigContentView;
3523 } else if (mStyle != null) {
Selim Cinek850a8542015-11-11 11:48:36 -05003524 result = mStyle.makeBigContentView();
Selim Cinek90dcf6d2015-11-18 20:24:13 -08003525 hideLine1Text(result);
Selim Cinekcc10bfb2016-02-10 16:24:21 -08003526 } else if (mActions.size() != 0) {
3527 result = applyStandardTemplateWithActions(getBigBaseLayoutResource());
Selim Cinek850a8542015-11-11 11:48:36 -05003528 }
3529 adaptNotificationHeaderForBigContentView(result);
3530 return result;
3531 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003532
Selim Cinekeaa29ca2015-11-23 13:51:13 -08003533 /**
3534 * Construct a RemoteViews for the final notification header only
3535 *
3536 * @hide
3537 */
3538 public RemoteViews makeNotificationHeader() {
3539 RemoteViews header = new BuilderRemoteViews(mContext.getApplicationInfo(),
3540 R.layout.notification_template_header);
3541 resetNotificationHeader(header);
3542 bindNotificationHeader(header);
3543 return header;
3544 }
3545
Selim Cinek29603462015-11-17 19:04:39 -08003546 private void hideLine1Text(RemoteViews result) {
Selim Cinekcc10bfb2016-02-10 16:24:21 -08003547 if (result != null) {
3548 result.setViewVisibility(R.id.text_line_1, View.GONE);
3549 }
Selim Cinek29603462015-11-17 19:04:39 -08003550 }
3551
Selim Cinek850a8542015-11-11 11:48:36 -05003552 private void adaptNotificationHeaderForBigContentView(RemoteViews result) {
Selim Cinekcc10bfb2016-02-10 16:24:21 -08003553 if (result != null) {
3554 result.setBoolean(R.id.notification_header, "setExpanded", true);
3555 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003556 }
3557
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003558 /**
3559 * Construct a RemoteViews for the final heads-up notification layout.
3560 */
Julia Reynolds3b848122016-02-26 10:45:32 -05003561 public RemoteViews createHeadsUpContentView() {
Selim Cinek593610c2016-02-16 18:42:57 -08003562 if (mN.headsUpContentView != null
3563 && (mStyle == null || !mStyle.displayCustomViewInline())) {
Julia Reynolds089e3e42015-11-18 09:59:57 -05003564 return mN.headsUpContentView;
3565 } else if (mStyle != null) {
3566 final RemoteViews styleView = mStyle.makeHeadsUpContentView();
3567 if (styleView != null) {
3568 return styleView;
3569 }
3570 } else if (mActions.size() == 0) {
3571 return null;
3572 }
3573
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003574 return applyStandardTemplateWithActions(getBigBaseLayoutResource());
Chris Wren8fd39ec2014-02-27 17:43:26 -05003575 }
3576
Selim Cinek624c02db2015-12-14 21:00:02 -08003577 /**
3578 * Construct a RemoteViews for the display in public contexts like on the lockscreen.
3579 *
3580 * @hide
3581 */
3582 public RemoteViews makePublicContentView() {
3583 if (mN.publicVersion != null) {
3584 final Builder builder = recoverBuilder(mContext, mN.publicVersion);
Julia Reynolds3b848122016-02-26 10:45:32 -05003585 return builder.createContentView();
Selim Cinek624c02db2015-12-14 21:00:02 -08003586 }
3587 Bundle savedBundle = mN.extras;
3588 Style style = mStyle;
3589 mStyle = null;
3590 Icon largeIcon = mN.mLargeIcon;
3591 mN.mLargeIcon = null;
Selim Cinek279fa862016-06-14 10:57:25 -07003592 Bitmap largeIconLegacy = mN.largeIcon;
3593 mN.largeIcon = null;
Selim Cinek624c02db2015-12-14 21:00:02 -08003594 Bundle publicExtras = new Bundle();
3595 publicExtras.putBoolean(EXTRA_SHOW_WHEN,
3596 savedBundle.getBoolean(EXTRA_SHOW_WHEN));
3597 publicExtras.putBoolean(EXTRA_SHOW_CHRONOMETER,
3598 savedBundle.getBoolean(EXTRA_SHOW_CHRONOMETER));
Adrian Roos96b7e202016-05-17 13:50:38 -07003599 publicExtras.putBoolean(EXTRA_CHRONOMETER_COUNT_DOWN,
3600 savedBundle.getBoolean(EXTRA_CHRONOMETER_COUNT_DOWN));
Selim Cinek624c02db2015-12-14 21:00:02 -08003601 publicExtras.putCharSequence(EXTRA_TITLE,
3602 mContext.getString(R.string.notification_hidden_text));
3603 mN.extras = publicExtras;
3604 final RemoteViews publicView = applyStandardTemplate(getBaseLayoutResource());
3605 mN.extras = savedBundle;
3606 mN.mLargeIcon = largeIcon;
Selim Cinek279fa862016-06-14 10:57:25 -07003607 mN.largeIcon = largeIconLegacy;
Selim Cinek624c02db2015-12-14 21:00:02 -08003608 mStyle = style;
3609 return publicView;
3610 }
3611
3612
Chris Wren8fd39ec2014-02-27 17:43:26 -05003613
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003614 private RemoteViews generateActionButton(Action action) {
Daniel Sandler8680bf82012-05-15 16:52:52 -04003615 final boolean tombstone = (action.actionIntent == null);
Selim Cinekf33b1112015-07-15 17:45:11 -07003616 RemoteViews button = new BuilderRemoteViews(mContext.getApplicationInfo(),
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003617 tombstone ? getActionTombstoneLayoutResource()
3618 : getActionLayoutResource());
Dan Sandler68079d52015-07-22 10:45:30 -04003619 final Icon ai = action.getIcon();
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003620 button.setTextViewText(R.id.action0, processLegacyText(action.title));
Daniel Sandler8680bf82012-05-15 16:52:52 -04003621 if (!tombstone) {
Daniel Sandlere5518842012-05-10 16:20:40 -04003622 button.setOnClickPendingIntent(R.id.action0, action.actionIntent);
Daniel Sandlere5518842012-05-10 16:20:40 -04003623 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003624 button.setContentDescription(R.id.action0, action.title);
Adrian Roosfe84e1f2015-11-04 15:55:39 -08003625 if (action.mRemoteInputs != null) {
3626 button.setRemoteInputs(R.id.action0, action.mRemoteInputs);
3627 }
3628 if (mN.color != COLOR_DEFAULT) {
Adrian Roos4ff3b122016-02-01 12:26:13 -08003629 button.setTextColor(R.id.action0, resolveContrastColor());
Adrian Roosfe84e1f2015-11-04 15:55:39 -08003630 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003631 return button;
3632 }
3633
Joe Onoratocb109a02011-01-18 17:57:41 -08003634 /**
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003635 * @return Whether we are currently building a notification from a legacy (an app that
Alan Viverette3cb07a462014-06-06 14:19:53 -07003636 * doesn't create material notifications by itself) app.
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003637 */
3638 private boolean isLegacy() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003639 return getColorUtil() != null;
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003640 }
3641
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003642 private CharSequence processLegacyText(CharSequence charSequence) {
3643 if (isLegacy()) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003644 return getColorUtil().invertCharSequenceColors(charSequence);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003645 } else {
3646 return charSequence;
3647 }
3648 }
3649
Dan Sandler26e81cf2014-05-06 10:01:27 -04003650 /**
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003651 * Apply any necessariy colors to the small icon
Dan Sandler26e81cf2014-05-06 10:01:27 -04003652 */
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003653 private void processSmallIconColor(Icon smallIcon, RemoteViews contentView) {
Selim Cinekea4bef72015-12-02 15:51:10 -08003654 boolean colorable = !isLegacy() || getColorUtil().isGrayscaleIcon(mContext, smallIcon);
3655 if (colorable) {
Adrian Roos4ff3b122016-02-01 12:26:13 -08003656 contentView.setDrawableParameters(R.id.icon, false, -1, resolveContrastColor(),
Jorim Jaggi92df1f22014-12-16 19:44:41 +01003657 PorterDuff.Mode.SRC_ATOP, -1);
Selim Cinekea4bef72015-12-02 15:51:10 -08003658
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003659 }
Selim Cinekea4bef72015-12-02 15:51:10 -08003660 contentView.setInt(R.id.notification_header, "setOriginalIconColor",
Adrian Roos4ff3b122016-02-01 12:26:13 -08003661 colorable ? resolveContrastColor() : NotificationHeaderView.NO_COLOR);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003662 }
3663
Dan Sandler26e81cf2014-05-06 10:01:27 -04003664 /**
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003665 * Make the largeIcon dark if it's a fake smallIcon (that is,
Dan Sandler26e81cf2014-05-06 10:01:27 -04003666 * if it's grayscale).
3667 */
3668 // TODO: also check bounds, transparency, that sort of thing.
Dan Sandlerd63f9322015-05-06 15:18:49 -04003669 private void processLargeLegacyIcon(Icon largeIcon, RemoteViews contentView) {
3670 if (largeIcon != null && isLegacy()
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003671 && getColorUtil().isGrayscaleIcon(mContext, largeIcon)) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003672 // resolve color will fall back to the default when legacy
Adrian Roos4ff3b122016-02-01 12:26:13 -08003673 contentView.setDrawableParameters(R.id.icon, false, -1, resolveContrastColor(),
Dan Sandler190d58d2014-05-15 09:33:39 -04003674 PorterDuff.Mode.SRC_ATOP, -1);
Jorim Jaggi92df1f22014-12-16 19:44:41 +01003675 }
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003676 }
3677
Julia Reynolds10ee1fc2015-11-09 11:04:55 -05003678 private void sanitizeColor() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003679 if (mN.color != COLOR_DEFAULT) {
3680 mN.color |= 0xFF000000; // no alpha for custom colors
Jorim Jaggi74419312014-06-10 20:57:21 +02003681 }
Jorim Jaggi74419312014-06-10 20:57:21 +02003682 }
3683
Adrian Roos4ff3b122016-02-01 12:26:13 -08003684 int resolveContrastColor() {
3685 if (mCachedContrastColorIsFor == mN.color && mCachedContrastColor != COLOR_INVALID) {
3686 return mCachedContrastColor;
Dan Sandler26e81cf2014-05-06 10:01:27 -04003687 }
Adrian Roos4ff3b122016-02-01 12:26:13 -08003688 final int contrasted = NotificationColorUtil.resolveContrastColor(mContext, mN.color);
3689
3690 mCachedContrastColorIsFor = mN.color;
3691 return mCachedContrastColor = contrasted;
Dan Sandler26e81cf2014-05-06 10:01:27 -04003692 }
3693
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003694 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003695 * Apply the unstyled operations and return a new {@link Notification} object.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04003696 * @hide
Joe Onoratocb109a02011-01-18 17:57:41 -08003697 */
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04003698 public Notification buildUnstyled() {
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003699 if (mActions.size() > 0) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003700 mN.actions = new Action[mActions.size()];
3701 mActions.toArray(mN.actions);
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003702 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003703 if (!mPersonList.isEmpty()) {
3704 mN.extras.putStringArray(EXTRA_PEOPLE,
3705 mPersonList.toArray(new String[mPersonList.size()]));
Dan Sandler0bf2ed82013-12-21 23:33:41 -06003706 }
Selim Cinek247fa012016-02-18 09:50:48 -08003707 if (mN.bigContentView != null || mN.contentView != null
3708 || mN.headsUpContentView != null) {
3709 mN.extras.putBoolean(EXTRA_CONTAINS_CUSTOM_VIEW, true);
3710 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003711 return mN;
Joe Onorato46439ce2010-11-19 13:56:21 -08003712 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003713
Julia Reynolds3b848122016-02-26 10:45:32 -05003714 /**
3715 * Creates a Builder from an existing notification so further changes can be made.
3716 * @param context The context for your application / activity.
3717 * @param n The notification to create a Builder from.
3718 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003719 public static Notification.Builder recoverBuilder(Context context, Notification n) {
Christoph Studer4600f9b2014-07-22 22:44:43 +02003720 // Re-create notification context so we can access app resources.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003721 ApplicationInfo applicationInfo = n.extras.getParcelable(
3722 EXTRA_BUILDER_APPLICATION_INFO);
Christoph Studer4600f9b2014-07-22 22:44:43 +02003723 Context builderContext;
Julia Reynoldsda303542015-11-23 14:00:20 -05003724 if (applicationInfo != null) {
3725 try {
3726 builderContext = context.createApplicationContext(applicationInfo,
3727 Context.CONTEXT_RESTRICTED);
3728 } catch (NameNotFoundException e) {
3729 Log.e(TAG, "ApplicationInfo " + applicationInfo + " not found");
3730 builderContext = context; // try with our context
3731 }
3732 } else {
3733 builderContext = context; // try with given context
Christoph Studer4600f9b2014-07-22 22:44:43 +02003734 }
3735
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003736 return new Builder(builderContext, n);
Christoph Studer4600f9b2014-07-22 22:44:43 +02003737 }
3738
3739 private static Class<? extends Style> getNotificationStyleClass(String templateClass) {
Selim Cinek593610c2016-02-16 18:42:57 -08003740 Class<? extends Style>[] classes = new Class[] {
3741 BigTextStyle.class, BigPictureStyle.class, InboxStyle.class, MediaStyle.class,
Alex Hillsfc737de2016-03-23 17:33:02 -04003742 DecoratedCustomViewStyle.class, DecoratedMediaCustomViewStyle.class,
3743 MessagingStyle.class };
Christoph Studer4600f9b2014-07-22 22:44:43 +02003744 for (Class<? extends Style> innerClass : classes) {
3745 if (templateClass.equals(innerClass.getName())) {
3746 return innerClass;
3747 }
3748 }
3749 return null;
3750 }
3751
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003752 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003753 * @deprecated Use {@link #build()} instead.
3754 */
3755 @Deprecated
3756 public Notification getNotification() {
3757 return build();
3758 }
3759
3760 /**
3761 * Combine all of the options that have been set and return a new {@link Notification}
3762 * object.
3763 */
3764 public Notification build() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003765 // first, add any extras from the calling code
3766 if (mUserExtras != null) {
3767 mN.extras = getAllExtras();
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07003768 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003769
Selim Cinekb85f36fd2016-04-20 18:46:36 -07003770 mN.creationTime = System.currentTimeMillis();
3771
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003772 // lazy stuff from mContext; see comment in Builder(Context, Notification)
Julia Reynoldsda303542015-11-23 14:00:20 -05003773 Notification.addFieldsFromContext(mContext, mN);
Christoph Studer943aa672014-08-03 20:31:16 +02003774
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003775 buildUnstyled();
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003776
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003777 if (mStyle != null) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003778 mStyle.buildStyled(mN);
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003779 }
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003780
Adrian Roos5081c0d2016-02-26 16:04:19 -08003781 if (mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N
3782 && (mStyle == null || !mStyle.displayCustomViewInline())) {
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05003783 if (mN.contentView == null) {
Julia Reynolds3b848122016-02-26 10:45:32 -05003784 mN.contentView = createContentView();
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05003785 mN.extras.putInt(EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT,
3786 mN.contentView.getSequenceNumber());
3787 }
3788 if (mN.bigContentView == null) {
Julia Reynolds3b848122016-02-26 10:45:32 -05003789 mN.bigContentView = createBigContentView();
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05003790 if (mN.bigContentView != null) {
3791 mN.extras.putInt(EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT,
3792 mN.bigContentView.getSequenceNumber());
3793 }
3794 }
3795 if (mN.headsUpContentView == null) {
Julia Reynolds3b848122016-02-26 10:45:32 -05003796 mN.headsUpContentView = createHeadsUpContentView();
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05003797 if (mN.headsUpContentView != null) {
3798 mN.extras.putInt(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT,
3799 mN.headsUpContentView.getSequenceNumber());
3800 }
3801 }
3802 }
3803
Julia Reynolds4c0c2022016-02-02 15:11:59 -05003804 if ((mN.defaults & DEFAULT_LIGHTS) != 0) {
3805 mN.flags |= FLAG_SHOW_LIGHTS;
3806 }
3807
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003808 return mN;
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003809 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05003810
3811 /**
3812 * Apply this Builder to an existing {@link Notification} object.
3813 *
3814 * @hide
3815 */
3816 public Notification buildInto(Notification n) {
Daniel Sandler1a497d32013-04-18 14:52:45 -04003817 build().cloneInto(n, true);
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05003818 return n;
3819 }
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003820
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05003821 /**
Adrian Roos184bfe022016-03-03 13:41:44 -08003822 * Removes RemoteViews that were created for compatibility from {@param n}, if they did not
3823 * change.
3824 *
3825 * @return {@param n}, if no stripping is needed, otherwise a stripped clone of {@param n}.
3826 *
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05003827 * @hide
3828 */
Adrian Roos184bfe022016-03-03 13:41:44 -08003829 public static Notification maybeCloneStrippedForDelivery(Notification n) {
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05003830 String templateClass = n.extras.getString(EXTRA_TEMPLATE);
Adrian Roos184bfe022016-03-03 13:41:44 -08003831
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05003832 // Only strip views for known Styles because we won't know how to
3833 // re-create them otherwise.
Adrian Roos184bfe022016-03-03 13:41:44 -08003834 if (!TextUtils.isEmpty(templateClass)
3835 && getNotificationStyleClass(templateClass) == null) {
3836 return n;
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05003837 }
Adrian Roos184bfe022016-03-03 13:41:44 -08003838
3839 // Only strip unmodified BuilderRemoteViews.
3840 boolean stripContentView = n.contentView instanceof BuilderRemoteViews &&
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05003841 n.extras.getInt(EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT, -1) ==
Adrian Roos184bfe022016-03-03 13:41:44 -08003842 n.contentView.getSequenceNumber();
3843 boolean stripBigContentView = n.bigContentView instanceof BuilderRemoteViews &&
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05003844 n.extras.getInt(EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT, -1) ==
Adrian Roos184bfe022016-03-03 13:41:44 -08003845 n.bigContentView.getSequenceNumber();
3846 boolean stripHeadsUpContentView = n.headsUpContentView instanceof BuilderRemoteViews &&
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05003847 n.extras.getInt(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT, -1) ==
Adrian Roos184bfe022016-03-03 13:41:44 -08003848 n.headsUpContentView.getSequenceNumber();
3849
3850 // Nothing to do here, no need to clone.
3851 if (!stripContentView && !stripBigContentView && !stripHeadsUpContentView) {
3852 return n;
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05003853 }
Adrian Roos184bfe022016-03-03 13:41:44 -08003854
3855 Notification clone = n.clone();
3856 if (stripContentView) {
3857 clone.contentView = null;
3858 clone.extras.remove(EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT);
3859 }
3860 if (stripBigContentView) {
3861 clone.bigContentView = null;
3862 clone.extras.remove(EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT);
3863 }
3864 if (stripHeadsUpContentView) {
3865 clone.headsUpContentView = null;
3866 clone.extras.remove(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT);
3867 }
3868 return clone;
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05003869 }
3870
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003871 private int getBaseLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07003872 return R.layout.notification_template_material_base;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003873 }
3874
3875 private int getBigBaseLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07003876 return R.layout.notification_template_material_big_base;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003877 }
3878
3879 private int getBigPictureLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07003880 return R.layout.notification_template_material_big_picture;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003881 }
3882
3883 private int getBigTextLayoutResource() {
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003884 return R.layout.notification_template_material_big_text;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003885 }
3886
3887 private int getInboxLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07003888 return R.layout.notification_template_material_inbox;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003889 }
3890
Adrian Roosc1a80b02016-04-05 14:54:55 -07003891 private int getMessagingLayoutResource() {
3892 return R.layout.notification_template_material_messaging;
3893 }
3894
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003895 private int getActionLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07003896 return R.layout.notification_material_action;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003897 }
3898
3899 private int getActionTombstoneLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07003900 return R.layout.notification_material_action_tombstone;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003901 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003902 }
3903
Selim Cinek279fa862016-06-14 10:57:25 -07003904 private boolean hasLargeIcon() {
3905 return mLargeIcon != null || largeIcon != null;
3906 }
3907
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003908 /**
Selim Cinekc2c0b042016-05-18 17:13:46 -07003909 * @return true if the notification will show the time; false otherwise
Selim Cinekb85f36fd2016-04-20 18:46:36 -07003910 * @hide
3911 */
Selim Cinekc2c0b042016-05-18 17:13:46 -07003912 public boolean showsTime() {
Selim Cinekb85f36fd2016-04-20 18:46:36 -07003913 return when != 0 && extras.getBoolean(EXTRA_SHOW_WHEN);
3914 }
3915
3916 /**
Selim Cinekc2c0b042016-05-18 17:13:46 -07003917 * @return true if the notification will show a chronometer; false otherwise
3918 * @hide
3919 */
3920 public boolean showsChronometer() {
3921 return when != 0 && extras.getBoolean(EXTRA_SHOW_CHRONOMETER);
3922 }
3923
3924 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003925 * An object that can apply a rich notification style to a {@link Notification.Builder}
3926 * object.
3927 */
Griff Hazendfcb0802014-02-11 12:00:00 -08003928 public static abstract class Style {
Chris Wrend6297db2012-05-03 16:20:13 -04003929 private CharSequence mBigContentTitle;
Jorim Jaggi457a10d2014-09-08 16:18:23 +02003930
3931 /**
3932 * @hide
3933 */
3934 protected CharSequence mSummaryText = null;
3935
3936 /**
3937 * @hide
3938 */
3939 protected boolean mSummaryTextSet = false;
Chris Wrend6297db2012-05-03 16:20:13 -04003940
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003941 protected Builder mBuilder;
3942
Chris Wrend6297db2012-05-03 16:20:13 -04003943 /**
3944 * Overrides ContentTitle in the big form of the template.
3945 * This defaults to the value passed to setContentTitle().
3946 */
3947 protected void internalSetBigContentTitle(CharSequence title) {
3948 mBigContentTitle = title;
3949 }
3950
3951 /**
3952 * Set the first line of text after the detail section in the big form of the template.
3953 */
3954 protected void internalSetSummaryText(CharSequence cs) {
3955 mSummaryText = cs;
Daniel Sandler619738c2012-06-07 16:33:08 -04003956 mSummaryTextSet = true;
Chris Wrend6297db2012-05-03 16:20:13 -04003957 }
3958
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003959 public void setBuilder(Builder builder) {
3960 if (mBuilder != builder) {
3961 mBuilder = builder;
Daniel Sandlerc08dea22012-06-28 08:35:24 -07003962 if (mBuilder != null) {
3963 mBuilder.setStyle(this);
3964 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003965 }
3966 }
3967
Chris Wrend6297db2012-05-03 16:20:13 -04003968 protected void checkBuilder() {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003969 if (mBuilder == null) {
3970 throw new IllegalArgumentException("Style requires a valid Builder object");
3971 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003972 }
Chris Wrend6297db2012-05-03 16:20:13 -04003973
3974 protected RemoteViews getStandardView(int layoutId) {
3975 checkBuilder();
3976
Christoph Studer4600f9b2014-07-22 22:44:43 +02003977 // Nasty.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003978 CharSequence oldBuilderContentTitle =
3979 mBuilder.getAllExtras().getCharSequence(EXTRA_TITLE);
Chris Wrend6297db2012-05-03 16:20:13 -04003980 if (mBigContentTitle != null) {
3981 mBuilder.setContentTitle(mBigContentTitle);
3982 }
3983
Chris Wrend6297db2012-05-03 16:20:13 -04003984 RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(layoutId);
3985
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003986 mBuilder.getAllExtras().putCharSequence(EXTRA_TITLE, oldBuilderContentTitle);
Christoph Studer4600f9b2014-07-22 22:44:43 +02003987
Chris Wrend6297db2012-05-03 16:20:13 -04003988 if (mBigContentTitle != null && mBigContentTitle.equals("")) {
3989 contentView.setViewVisibility(R.id.line1, View.GONE);
Chris Wren67dc9a02012-05-16 01:03:20 -04003990 } else {
3991 contentView.setViewVisibility(R.id.line1, View.VISIBLE);
Chris Wrend6297db2012-05-03 16:20:13 -04003992 }
3993
Chris Wrend6297db2012-05-03 16:20:13 -04003994 return contentView;
3995 }
3996
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003997 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003998 * Construct a Style-specific RemoteViews for the final 1U notification layout.
3999 * The default implementation has nothing additional to add.
4000 * @hide
4001 */
4002 public RemoteViews makeContentView() {
4003 return null;
4004 }
4005
4006 /**
4007 * Construct a Style-specific RemoteViews for the final big notification layout.
4008 * @hide
4009 */
4010 public RemoteViews makeBigContentView() {
4011 return null;
4012 }
4013
4014 /**
4015 * Construct a Style-specific RemoteViews for the final HUN layout.
4016 * @hide
4017 */
4018 public RemoteViews makeHeadsUpContentView() {
4019 return null;
4020 }
4021
4022 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004023 * Apply any style-specific extras to this notification before shipping it out.
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004024 * @hide
4025 */
4026 public void addExtras(Bundle extras) {
4027 if (mSummaryTextSet) {
4028 extras.putCharSequence(EXTRA_SUMMARY_TEXT, mSummaryText);
4029 }
4030 if (mBigContentTitle != null) {
4031 extras.putCharSequence(EXTRA_TITLE_BIG, mBigContentTitle);
4032 }
Chris Wren91ad5632013-06-05 15:05:57 -04004033 extras.putString(EXTRA_TEMPLATE, this.getClass().getName());
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004034 }
4035
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04004036 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004037 * Reconstruct the internal state of this Style object from extras.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04004038 * @hide
4039 */
Christoph Studer4600f9b2014-07-22 22:44:43 +02004040 protected void restoreFromExtras(Bundle extras) {
4041 if (extras.containsKey(EXTRA_SUMMARY_TEXT)) {
4042 mSummaryText = extras.getCharSequence(EXTRA_SUMMARY_TEXT);
4043 mSummaryTextSet = true;
4044 }
4045 if (extras.containsKey(EXTRA_TITLE_BIG)) {
4046 mBigContentTitle = extras.getCharSequence(EXTRA_TITLE_BIG);
4047 }
4048 }
4049
4050
4051 /**
4052 * @hide
4053 */
4054 public Notification buildStyled(Notification wip) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004055 addExtras(wip.extras);
Christoph Studer4600f9b2014-07-22 22:44:43 +02004056 return wip;
4057 }
4058
Daniel Sandler0ec46202015-06-24 01:27:05 -04004059 /**
4060 * @hide
4061 */
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07004062 public void purgeResources() {}
4063
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04004064 /**
4065 * Calls {@link android.app.Notification.Builder#build()} on the Builder this Style is
4066 * attached to.
4067 *
4068 * @return the fully constructed Notification.
4069 */
4070 public Notification build() {
4071 checkBuilder();
4072 return mBuilder.build();
4073 }
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004074
4075 /**
4076 * @hide
4077 * @return true if the style positions the progress bar on the second line; false if the
4078 * style hides the progress bar
4079 */
4080 protected boolean hasProgress() {
4081 return true;
4082 }
Selim Cinek03d0d652015-11-13 13:18:09 -05004083
4084 /**
4085 * @hide
4086 * @return Whether we should put the summary be put into the notification header
4087 */
4088 public boolean hasSummaryInHeader() {
4089 return true;
4090 }
Selim Cinek593610c2016-02-16 18:42:57 -08004091
4092 /**
4093 * @hide
4094 * @return Whether custom content views are displayed inline in the style
4095 */
4096 public boolean displayCustomViewInline() {
4097 return false;
4098 }
Joe Onorato46439ce2010-11-19 13:56:21 -08004099 }
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004100
4101 /**
Daniel Sandler4dfbe832012-04-11 14:51:46 -04004102 * Helper class for generating large-format notifications that include a large image attachment.
Joe Malin8d40d042012-11-05 11:36:40 -08004103 *
Robert Ly91c5ce32014-06-08 15:37:00 -07004104 * Here's how you'd set the <code>BigPictureStyle</code> on a notification:
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004105 * <pre class="prettyprint">
Robert Ly91c5ce32014-06-08 15:37:00 -07004106 * Notification notif = new Notification.Builder(mContext)
4107 * .setContentTitle(&quot;New photo from &quot; + sender.toString())
4108 * .setContentText(subject)
4109 * .setSmallIcon(R.drawable.new_post)
4110 * .setLargeIcon(aBitmap)
4111 * .setStyle(new Notification.BigPictureStyle()
4112 * .bigPicture(aBigBitmap))
4113 * .build();
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004114 * </pre>
Joe Malin8d40d042012-11-05 11:36:40 -08004115 *
Daniel Sandler4dfbe832012-04-11 14:51:46 -04004116 * @see Notification#bigContentView
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004117 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004118 public static class BigPictureStyle extends Style {
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004119 private Bitmap mPicture;
Dan Sandlerd63f9322015-05-06 15:18:49 -04004120 private Icon mBigLargeIcon;
Chris Wren3745a3d2012-05-22 15:11:52 -04004121 private boolean mBigLargeIconSet = false;
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004122
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004123 public BigPictureStyle() {
4124 }
4125
Adrian Roosf5faf9d2016-05-23 13:56:15 -07004126 /**
4127 * @deprecated use {@code BigPictureStyle()}.
4128 */
4129 @Deprecated
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004130 public BigPictureStyle(Builder builder) {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004131 setBuilder(builder);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004132 }
4133
Chris Wrend6297db2012-05-03 16:20:13 -04004134 /**
4135 * Overrides ContentTitle in the big form of the template.
4136 * This defaults to the value passed to setContentTitle().
4137 */
4138 public BigPictureStyle setBigContentTitle(CharSequence title) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04004139 internalSetBigContentTitle(safeCharSequence(title));
Chris Wrend6297db2012-05-03 16:20:13 -04004140 return this;
4141 }
4142
4143 /**
4144 * Set the first line of text after the detail section in the big form of the template.
4145 */
4146 public BigPictureStyle setSummaryText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04004147 internalSetSummaryText(safeCharSequence(cs));
Chris Wrend6297db2012-05-03 16:20:13 -04004148 return this;
4149 }
4150
Chris Wren0bd664d2012-08-01 13:56:56 -04004151 /**
4152 * Provide the bitmap to be used as the payload for the BigPicture notification.
4153 */
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004154 public BigPictureStyle bigPicture(Bitmap b) {
4155 mPicture = b;
4156 return this;
4157 }
4158
Chris Wren3745a3d2012-05-22 15:11:52 -04004159 /**
Chris Wren3745a3d2012-05-22 15:11:52 -04004160 * Override the large icon when the big notification is shown.
4161 */
4162 public BigPictureStyle bigLargeIcon(Bitmap b) {
Dan Sandlerd63f9322015-05-06 15:18:49 -04004163 return bigLargeIcon(b != null ? Icon.createWithBitmap(b) : null);
4164 }
4165
4166 /**
4167 * Override the large icon when the big notification is shown.
4168 */
4169 public BigPictureStyle bigLargeIcon(Icon icon) {
Chris Wren3745a3d2012-05-22 15:11:52 -04004170 mBigLargeIconSet = true;
Dan Sandlerd63f9322015-05-06 15:18:49 -04004171 mBigLargeIcon = icon;
Chris Wren3745a3d2012-05-22 15:11:52 -04004172 return this;
4173 }
4174
Riley Andrews0394a0c2015-11-03 23:36:52 -08004175 /** @hide */
4176 public static final int MIN_ASHMEM_BITMAP_SIZE = 128 * (1 << 10);
4177
Daniel Sandler0ec46202015-06-24 01:27:05 -04004178 /**
4179 * @hide
4180 */
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07004181 @Override
4182 public void purgeResources() {
4183 super.purgeResources();
Riley Andrews8cee7c12015-11-01 23:36:04 -08004184 if (mPicture != null &&
4185 mPicture.isMutable() &&
Riley Andrews0394a0c2015-11-03 23:36:52 -08004186 mPicture.getAllocationByteCount() >= MIN_ASHMEM_BITMAP_SIZE) {
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07004187 mPicture = mPicture.createAshmemBitmap();
4188 }
4189 if (mBigLargeIcon != null) {
4190 mBigLargeIcon.convertToAshmem();
4191 }
4192 }
Christoph Studer5c510ee2014-12-15 16:32:27 +01004193
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004194 /**
4195 * @hide
4196 */
4197 public RemoteViews makeBigContentView() {
4198 // Replace mN.mLargeIcon with mBigLargeIcon if mBigLargeIconSet
Christoph Studer5c510ee2014-12-15 16:32:27 +01004199 // This covers the following cases:
4200 // 1. mBigLargeIconSet -> mBigLargeIcon (null or non-null) applies, overrides
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004201 // mN.mLargeIcon
4202 // 2. !mBigLargeIconSet -> mN.mLargeIcon applies
Dan Sandlerd63f9322015-05-06 15:18:49 -04004203 Icon oldLargeIcon = null;
Christoph Studer5c510ee2014-12-15 16:32:27 +01004204 if (mBigLargeIconSet) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004205 oldLargeIcon = mBuilder.mN.mLargeIcon;
4206 mBuilder.mN.mLargeIcon = mBigLargeIcon;
Christoph Studer5c510ee2014-12-15 16:32:27 +01004207 }
4208
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004209 RemoteViews contentView = getStandardView(mBuilder.getBigPictureLayoutResource());
Selim Cinek03d0d652015-11-13 13:18:09 -05004210 if (mSummaryTextSet) {
4211 contentView.setTextViewText(R.id.text, mBuilder.processLegacyText(mSummaryText));
Selim Cinekc848c3a2016-01-13 15:27:30 -08004212 contentView.setViewVisibility(R.id.text, View.VISIBLE);
Selim Cinek03d0d652015-11-13 13:18:09 -05004213 }
Selim Cinek279fa862016-06-14 10:57:25 -07004214 mBuilder.setContentMinHeight(contentView, mBuilder.mN.hasLargeIcon());
Selim Cinek53e64a42015-11-16 10:40:56 -08004215
Christoph Studer5c510ee2014-12-15 16:32:27 +01004216 if (mBigLargeIconSet) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004217 mBuilder.mN.mLargeIcon = oldLargeIcon;
Christoph Studer5c510ee2014-12-15 16:32:27 +01004218 }
4219
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004220 contentView.setImageViewBitmap(R.id.big_picture, mPicture);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004221 return contentView;
4222 }
4223
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004224 /**
4225 * @hide
4226 */
4227 public void addExtras(Bundle extras) {
4228 super.addExtras(extras);
4229
4230 if (mBigLargeIconSet) {
4231 extras.putParcelable(EXTRA_LARGE_ICON_BIG, mBigLargeIcon);
4232 }
4233 extras.putParcelable(EXTRA_PICTURE, mPicture);
4234 }
4235
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04004236 /**
4237 * @hide
4238 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004239 @Override
Christoph Studer4600f9b2014-07-22 22:44:43 +02004240 protected void restoreFromExtras(Bundle extras) {
4241 super.restoreFromExtras(extras);
4242
4243 if (extras.containsKey(EXTRA_LARGE_ICON_BIG)) {
Christoph Studer5c510ee2014-12-15 16:32:27 +01004244 mBigLargeIconSet = true;
Christoph Studer4600f9b2014-07-22 22:44:43 +02004245 mBigLargeIcon = extras.getParcelable(EXTRA_LARGE_ICON_BIG);
Chris Wren3745a3d2012-05-22 15:11:52 -04004246 }
Christoph Studer4600f9b2014-07-22 22:44:43 +02004247 mPicture = extras.getParcelable(EXTRA_PICTURE);
4248 }
Selim Cinek03d0d652015-11-13 13:18:09 -05004249
4250 /**
4251 * @hide
4252 */
4253 @Override
4254 public boolean hasSummaryInHeader() {
4255 return false;
4256 }
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004257 }
4258
4259 /**
Daniel Sandler4dfbe832012-04-11 14:51:46 -04004260 * Helper class for generating large-format notifications that include a lot of text.
Joe Malin8d40d042012-11-05 11:36:40 -08004261 *
Robert Ly91c5ce32014-06-08 15:37:00 -07004262 * Here's how you'd set the <code>BigTextStyle</code> on a notification:
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004263 * <pre class="prettyprint">
Robert Ly91c5ce32014-06-08 15:37:00 -07004264 * Notification notif = new Notification.Builder(mContext)
4265 * .setContentTitle(&quot;New mail from &quot; + sender.toString())
4266 * .setContentText(subject)
4267 * .setSmallIcon(R.drawable.new_mail)
4268 * .setLargeIcon(aBitmap)
4269 * .setStyle(new Notification.BigTextStyle()
4270 * .bigText(aVeryLongString))
4271 * .build();
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004272 * </pre>
Joe Malin8d40d042012-11-05 11:36:40 -08004273 *
Daniel Sandler4dfbe832012-04-11 14:51:46 -04004274 * @see Notification#bigContentView
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004275 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004276 public static class BigTextStyle extends Style {
Jorim Jaggi457a10d2014-09-08 16:18:23 +02004277
4278 private static final int MAX_LINES = 13;
Selim Cinek3a2c4b92015-12-17 17:01:17 -08004279 private static final int LINES_CONSUMED_BY_ACTIONS = 4;
Jorim Jaggi457a10d2014-09-08 16:18:23 +02004280
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004281 private CharSequence mBigText;
4282
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004283 public BigTextStyle() {
4284 }
4285
Adrian Roosf5faf9d2016-05-23 13:56:15 -07004286 /**
4287 * @deprecated use {@code BigTextStyle()}.
4288 */
4289 @Deprecated
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004290 public BigTextStyle(Builder builder) {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004291 setBuilder(builder);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004292 }
4293
Chris Wrend6297db2012-05-03 16:20:13 -04004294 /**
4295 * Overrides ContentTitle in the big form of the template.
4296 * This defaults to the value passed to setContentTitle().
4297 */
4298 public BigTextStyle setBigContentTitle(CharSequence title) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04004299 internalSetBigContentTitle(safeCharSequence(title));
Chris Wrend6297db2012-05-03 16:20:13 -04004300 return this;
4301 }
4302
4303 /**
4304 * Set the first line of text after the detail section in the big form of the template.
4305 */
4306 public BigTextStyle setSummaryText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04004307 internalSetSummaryText(safeCharSequence(cs));
Chris Wrend6297db2012-05-03 16:20:13 -04004308 return this;
4309 }
4310
Chris Wren0bd664d2012-08-01 13:56:56 -04004311 /**
4312 * Provide the longer text to be displayed in the big form of the
4313 * template in place of the content text.
4314 */
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004315 public BigTextStyle bigText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04004316 mBigText = safeCharSequence(cs);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004317 return this;
4318 }
4319
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004320 /**
4321 * @hide
4322 */
4323 public void addExtras(Bundle extras) {
4324 super.addExtras(extras);
4325
Christoph Studer4600f9b2014-07-22 22:44:43 +02004326 extras.putCharSequence(EXTRA_BIG_TEXT, mBigText);
4327 }
4328
4329 /**
4330 * @hide
4331 */
4332 @Override
4333 protected void restoreFromExtras(Bundle extras) {
4334 super.restoreFromExtras(extras);
4335
4336 mBigText = extras.getCharSequence(EXTRA_BIG_TEXT);
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004337 }
4338
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004339 /**
4340 * @hide
4341 */
4342 public RemoteViews makeBigContentView() {
Christoph Studer4600f9b2014-07-22 22:44:43 +02004343
4344 // Nasty
Selim Cinek75998782016-04-26 10:39:17 -07004345 CharSequence text = mBuilder.getAllExtras().getCharSequence(EXTRA_TEXT);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004346 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, null);
Daniel Sandler916ad912012-06-13 12:17:07 -04004347
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004348 RemoteViews contentView = getStandardView(mBuilder.getBigTextLayoutResource());
Joe Malin8d40d042012-11-05 11:36:40 -08004349
Selim Cinek75998782016-04-26 10:39:17 -07004350 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, text);
Christoph Studer4600f9b2014-07-22 22:44:43 +02004351
Selim Cinek3a2c4b92015-12-17 17:01:17 -08004352 CharSequence bigTextText = mBuilder.processLegacyText(mBigText);
Selim Cinek75998782016-04-26 10:39:17 -07004353 if (TextUtils.isEmpty(bigTextText)) {
4354 // In case the bigtext is null / empty fall back to the normal text to avoid a weird
4355 // experience
4356 bigTextText = mBuilder.processLegacyText(text);
4357 }
Adrian Roosb1f427c2016-05-26 12:27:15 -07004358 applyBigTextContentView(mBuilder, contentView, bigTextText);
Selim Cinek4fb12d32015-11-19 18:10:48 -08004359
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004360 return contentView;
4361 }
4362
Adrian Roosb1f427c2016-05-26 12:27:15 -07004363 static void applyBigTextContentView(Builder builder,
4364 RemoteViews contentView, CharSequence bigTextText) {
4365 contentView.setTextViewText(R.id.big_text, bigTextText);
4366 contentView.setViewVisibility(R.id.big_text,
4367 TextUtils.isEmpty(bigTextText) ? View.GONE : View.VISIBLE);
4368 contentView.setInt(R.id.big_text, "setMaxLines", calculateMaxLines(builder));
Selim Cinek279fa862016-06-14 10:57:25 -07004369 contentView.setBoolean(R.id.big_text, "setHasImage", builder.mN.hasLargeIcon());
Adrian Roosb1f427c2016-05-26 12:27:15 -07004370 }
4371
4372 private static int calculateMaxLines(Builder builder) {
Jorim Jaggi457a10d2014-09-08 16:18:23 +02004373 int lineCount = MAX_LINES;
Adrian Roosb1f427c2016-05-26 12:27:15 -07004374 boolean hasActions = builder.mActions.size() > 0;
Jorim Jaggi457a10d2014-09-08 16:18:23 +02004375 if (hasActions) {
4376 lineCount -= LINES_CONSUMED_BY_ACTIONS;
4377 }
Jorim Jaggi457a10d2014-09-08 16:18:23 +02004378 return lineCount;
4379 }
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004380 }
Daniel Sandler879c5e02012-04-17 16:46:51 -04004381
4382 /**
Alex Hillsfc737de2016-03-23 17:33:02 -04004383 * Helper class for generating large-format notifications that include multiple back-and-forth
4384 * messages of varying types between any number of people.
4385 *
4386 * <br>
4387 * If the platform does not provide large-format notifications, this method has no effect. The
4388 * user will always see the normal notification view.
4389 * <br>
4390 * This class is a "rebuilder": It attaches to a Builder object and modifies its behavior, like
4391 * so:
4392 * <pre class="prettyprint">
4393 *
4394 * Notification noti = new Notification.Builder()
4395 * .setContentTitle(&quot;2 new messages wtih &quot; + sender.toString())
4396 * .setContentText(subject)
4397 * .setSmallIcon(R.drawable.new_message)
4398 * .setLargeIcon(aBitmap)
4399 * .setStyle(new Notification.MessagingStyle(resources.getString(R.string.reply_name))
4400 * .addMessage(messages[0].getText(), messages[0].getTime(), messages[0].getSender())
4401 * .addMessage(messages[1].getText(), messages[1].getTime(), messages[1].getSender()))
4402 * .build();
4403 * </pre>
4404 */
4405 public static class MessagingStyle extends Style {
4406
4407 /**
4408 * The maximum number of messages that will be retained in the Notification itself (the
4409 * number displayed is up to the platform).
4410 */
4411 public static final int MAXIMUM_RETAINED_MESSAGES = 25;
4412
4413 CharSequence mUserDisplayName;
4414 CharSequence mConversationTitle;
Alex Hillsd9b04d92016-04-11 16:38:16 -04004415 List<Message> mMessages = new ArrayList<>();
Alex Hillsfc737de2016-03-23 17:33:02 -04004416
4417 MessagingStyle() {
4418 }
4419
4420 /**
4421 * @param userDisplayName the name to be displayed for any replies sent by the user before the
4422 * posting app reposts the notification with those messages after they've been actually
4423 * sent and in previous messages sent by the user added in
4424 * {@link #addMessage(Notification.MessagingStyle.Message)}
4425 */
4426 public MessagingStyle(CharSequence userDisplayName) {
4427 mUserDisplayName = userDisplayName;
4428 }
4429
4430 /**
4431 * Returns the name to be displayed for any replies sent by the user
4432 */
4433 public CharSequence getUserDisplayName() {
4434 return mUserDisplayName;
4435 }
4436
4437 /**
Alex Hillsfc737de2016-03-23 17:33:02 -04004438 * Sets the title to be displayed on this conversation. This should only be used for
4439 * group messaging and left unset for one-on-one conversations.
4440 * @param conversationTitle
4441 * @return this object for method chaining.
4442 */
4443 public MessagingStyle setConversationTitle(CharSequence conversationTitle) {
4444 mConversationTitle = conversationTitle;
4445 return this;
4446 }
4447
4448 /**
4449 * Return the title to be displayed on this conversation. Can be <code>null</code> and
4450 * should be for one-on-one conversations
4451 */
4452 public CharSequence getConversationTitle() {
4453 return mConversationTitle;
4454 }
4455
4456 /**
4457 * Adds a message for display by this notification. Convenience call for a simple
4458 * {@link Message} in {@link #addMessage(Notification.MessagingStyle.Message)}.
4459 * @param text A {@link CharSequence} to be displayed as the message content
4460 * @param timestamp Time at which the message arrived
4461 * @param sender A {@link CharSequence} to be used for displaying the name of the
4462 * sender. Should be <code>null</code> for messages by the current user, in which case
4463 * the platform will insert {@link #getUserDisplayName()}.
4464 * Should be unique amongst all individuals in the conversation, and should be
4465 * consistent during re-posts of the notification.
4466 *
4467 * @see Message#Message(CharSequence, long, CharSequence)
4468 *
4469 * @return this object for method chaining
4470 */
4471 public MessagingStyle addMessage(CharSequence text, long timestamp, CharSequence sender) {
4472 mMessages.add(new Message(text, timestamp, sender));
4473 if (mMessages.size() > MAXIMUM_RETAINED_MESSAGES) {
4474 mMessages.remove(0);
4475 }
4476 return this;
4477 }
4478
4479 /**
4480 * Adds a {@link Message} for display in this notification.
4481 * @param message The {@link Message} to be displayed
4482 * @return this object for method chaining
4483 */
4484 public MessagingStyle addMessage(Message message) {
4485 mMessages.add(message);
4486 if (mMessages.size() > MAXIMUM_RETAINED_MESSAGES) {
4487 mMessages.remove(0);
4488 }
4489 return this;
4490 }
4491
4492 /**
4493 * Gets the list of {@code Message} objects that represent the notification
4494 */
4495 public List<Message> getMessages() {
4496 return mMessages;
4497 }
4498
4499 /**
4500 * @hide
4501 */
4502 @Override
4503 public void addExtras(Bundle extras) {
4504 super.addExtras(extras);
4505 if (mUserDisplayName != null) {
4506 extras.putCharSequence(EXTRA_SELF_DISPLAY_NAME, mUserDisplayName);
4507 }
4508 if (mConversationTitle != null) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04004509 extras.putCharSequence(EXTRA_CONVERSATION_TITLE, mConversationTitle);
Alex Hillsfc737de2016-03-23 17:33:02 -04004510 }
Alex Hillsd9b04d92016-04-11 16:38:16 -04004511 if (!mMessages.isEmpty()) { extras.putParcelableArray(EXTRA_MESSAGES,
4512 Message.getBundleArrayForMessages(mMessages));
Alex Hillsfc737de2016-03-23 17:33:02 -04004513 }
Adrian Roos33fbd2c2016-05-27 15:35:28 -07004514
4515 fixTitleAndTextExtras(extras);
4516 }
4517
4518 private void fixTitleAndTextExtras(Bundle extras) {
4519 Message m = findLatestIncomingMessage();
4520 CharSequence text = (m == null) ? null : m.mText;
4521 CharSequence sender = m == null ? null
4522 : TextUtils.isEmpty(m.mSender) ? mUserDisplayName : m.mSender;
4523 CharSequence title;
4524 if (!TextUtils.isEmpty(mConversationTitle)) {
4525 if (!TextUtils.isEmpty(sender)) {
4526 BidiFormatter bidi = BidiFormatter.getInstance();
4527 title = mBuilder.mContext.getString(
4528 com.android.internal.R.string.notification_messaging_title_template,
4529 bidi.unicodeWrap(mConversationTitle), bidi.unicodeWrap(m.mSender));
4530 } else {
4531 title = mConversationTitle;
4532 }
4533 } else {
4534 title = sender;
4535 }
4536
4537 if (title != null) {
4538 extras.putCharSequence(EXTRA_TITLE, title);
4539 }
4540 if (text != null) {
4541 extras.putCharSequence(EXTRA_TEXT, text);
4542 }
Alex Hillsfc737de2016-03-23 17:33:02 -04004543 }
4544
4545 /**
4546 * @hide
4547 */
4548 @Override
4549 protected void restoreFromExtras(Bundle extras) {
4550 super.restoreFromExtras(extras);
4551
4552 mMessages.clear();
Adrian Roos96b7e202016-05-17 13:50:38 -07004553 mUserDisplayName = extras.getCharSequence(EXTRA_SELF_DISPLAY_NAME);
4554 mConversationTitle = extras.getCharSequence(EXTRA_CONVERSATION_TITLE);
Alex Hillsd9b04d92016-04-11 16:38:16 -04004555 Parcelable[] parcelables = extras.getParcelableArray(EXTRA_MESSAGES);
Adrian Roosdedd1df2016-04-26 16:38:47 -07004556 if (parcelables != null && parcelables instanceof Parcelable[]) {
4557 mMessages = Message.getMessagesFromBundleArray(parcelables);
Alex Hillsfc737de2016-03-23 17:33:02 -04004558 }
4559 }
4560
4561 /**
4562 * @hide
4563 */
Adrian Roosc1a80b02016-04-05 14:54:55 -07004564 @Override
4565 public RemoteViews makeContentView() {
4566 Message m = findLatestIncomingMessage();
4567 CharSequence title = mConversationTitle != null
4568 ? mConversationTitle
4569 : (m == null) ? null : m.mSender;
4570 CharSequence text = (m == null)
4571 ? null
4572 : mConversationTitle != null ? makeMessageLine(m) : m.mText;
Alex Hillsfc737de2016-03-23 17:33:02 -04004573
Adrian Roosc1a80b02016-04-05 14:54:55 -07004574 return mBuilder.applyStandardTemplate(mBuilder.getBaseLayoutResource(),
4575 false /* hasProgress */,
4576 title,
4577 text);
4578 }
4579
4580 private Message findLatestIncomingMessage() {
4581 for (int i = mMessages.size() - 1; i >= 0; i--) {
4582 Message m = mMessages.get(i);
4583 // Incoming messages have a non-empty sender.
4584 if (!TextUtils.isEmpty(m.mSender)) {
4585 return m;
4586 }
4587 }
Adrian Roos33fbd2c2016-05-27 15:35:28 -07004588 if (!mMessages.isEmpty()) {
4589 // No incoming messages, fall back to outgoing message
4590 return mMessages.get(mMessages.size() - 1);
4591 }
Adrian Roosc1a80b02016-04-05 14:54:55 -07004592 return null;
4593 }
4594
4595 /**
4596 * @hide
4597 */
4598 @Override
4599 public RemoteViews makeBigContentView() {
4600 CharSequence title = !TextUtils.isEmpty(super.mBigContentTitle)
4601 ? super.mBigContentTitle
4602 : mConversationTitle;
4603 boolean hasTitle = !TextUtils.isEmpty(title);
4604
Adrian Roosfeafa052016-06-01 17:09:45 -07004605 if (mMessages.size() == 1) {
4606 // Special case for a single message: Use the big text style
4607 // so the collapsed and expanded versions match nicely.
4608 CharSequence bigTitle;
4609 CharSequence text;
4610 if (hasTitle) {
4611 bigTitle = title;
4612 text = makeMessageLine(mMessages.get(0));
4613 } else {
4614 bigTitle = mMessages.get(0).mSender;
4615 text = mMessages.get(0).mText;
4616 }
Adrian Roosb1f427c2016-05-26 12:27:15 -07004617 RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(
4618 mBuilder.getBigTextLayoutResource(),
Adrian Roosfeafa052016-06-01 17:09:45 -07004619 false /* progress */, bigTitle, null /* text */);
Adrian Roosb1f427c2016-05-26 12:27:15 -07004620 BigTextStyle.applyBigTextContentView(mBuilder, contentView, text);
4621 return contentView;
4622 }
4623
Adrian Roos48d746a2016-04-12 14:57:28 -07004624 RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(
Adrian Roosc1a80b02016-04-05 14:54:55 -07004625 mBuilder.getMessagingLayoutResource(),
4626 false /* hasProgress */,
4627 title,
4628 null /* text */);
4629
4630 int[] rowIds = {R.id.inbox_text0, R.id.inbox_text1, R.id.inbox_text2, R.id.inbox_text3,
4631 R.id.inbox_text4, R.id.inbox_text5, R.id.inbox_text6};
4632
4633 // Make sure all rows are gone in case we reuse a view.
4634 for (int rowId : rowIds) {
4635 contentView.setViewVisibility(rowId, View.GONE);
4636 }
4637
4638 int i=0;
Adrian Roos2d5dbba2016-06-08 17:11:53 -07004639 contentView.setViewLayoutMarginBottomDimen(R.id.line1,
4640 hasTitle ? R.dimen.notification_messaging_spacing : 0);
Adrian Roosc1a80b02016-04-05 14:54:55 -07004641 contentView.setInt(R.id.notification_messaging, "setNumIndentLines",
Selim Cinek279fa862016-06-14 10:57:25 -07004642 !mBuilder.mN.hasLargeIcon() ? 0 : (hasTitle ? 1 : 2));
Adrian Roosc1a80b02016-04-05 14:54:55 -07004643
Adrian Roosfeafa052016-06-01 17:09:45 -07004644 int contractedChildId = View.NO_ID;
4645 Message contractedMessage = findLatestIncomingMessage();
Adrian Roosc1a80b02016-04-05 14:54:55 -07004646 int firstMessage = Math.max(0, mMessages.size() - rowIds.length);
4647 while (firstMessage + i < mMessages.size() && i < rowIds.length) {
4648 Message m = mMessages.get(firstMessage + i);
4649 int rowId = rowIds[i];
4650
4651 contentView.setViewVisibility(rowId, View.VISIBLE);
4652 contentView.setTextViewText(rowId, makeMessageLine(m));
4653
Adrian Roosfeafa052016-06-01 17:09:45 -07004654 if (contractedMessage == m) {
4655 contractedChildId = rowId;
4656 }
4657
Adrian Roosc1a80b02016-04-05 14:54:55 -07004658 i++;
4659 }
Adrian Roosfeafa052016-06-01 17:09:45 -07004660 // Record this here to allow transformation between the contracted and expanded views.
4661 contentView.setInt(R.id.notification_messaging, "setContractedChildId",
4662 contractedChildId);
Alex Hillsfc737de2016-03-23 17:33:02 -04004663 return contentView;
4664 }
4665
Adrian Roosc1a80b02016-04-05 14:54:55 -07004666 private CharSequence makeMessageLine(Message m) {
4667 BidiFormatter bidi = BidiFormatter.getInstance();
4668 SpannableStringBuilder sb = new SpannableStringBuilder();
4669 if (TextUtils.isEmpty(m.mSender)) {
4670 CharSequence replyName = mUserDisplayName == null ? "" : mUserDisplayName;
4671 sb.append(bidi.unicodeWrap(replyName),
4672 makeFontColorSpan(mBuilder.resolveContrastColor()),
4673 0 /* flags */);
4674 } else {
4675 sb.append(bidi.unicodeWrap(m.mSender),
4676 makeFontColorSpan(Color.BLACK),
4677 0 /* flags */);
4678 }
4679 CharSequence text = m.mText == null ? "" : m.mText;
4680 sb.append(" ").append(bidi.unicodeWrap(text));
4681 return sb;
4682 }
4683
Adrian Roosdedd1df2016-04-26 16:38:47 -07004684 /**
4685 * @hide
4686 */
4687 @Override
4688 public RemoteViews makeHeadsUpContentView() {
4689 Message m = findLatestIncomingMessage();
4690 CharSequence title = mConversationTitle != null
4691 ? mConversationTitle
4692 : (m == null) ? null : m.mSender;
4693 CharSequence text = (m == null)
4694 ? null
4695 : mConversationTitle != null ? makeMessageLine(m) : m.mText;
4696
4697 return mBuilder.applyStandardTemplateWithActions(mBuilder.getBigBaseLayoutResource(),
4698 false /* hasProgress */,
4699 title,
4700 text);
4701 }
4702
Adrian Roosc1a80b02016-04-05 14:54:55 -07004703 private static TextAppearanceSpan makeFontColorSpan(int color) {
4704 return new TextAppearanceSpan(null, 0, 0,
4705 ColorStateList.valueOf(color), null);
4706 }
4707
Alex Hillsd9b04d92016-04-11 16:38:16 -04004708 public static final class Message {
4709
4710 static final String KEY_TEXT = "text";
4711 static final String KEY_TIMESTAMP = "time";
4712 static final String KEY_SENDER = "sender";
4713 static final String KEY_DATA_MIME_TYPE = "type";
4714 static final String KEY_DATA_URI= "uri";
Alex Hillsfc737de2016-03-23 17:33:02 -04004715
4716 private final CharSequence mText;
4717 private final long mTimestamp;
4718 private final CharSequence mSender;
4719
4720 private String mDataMimeType;
4721 private Uri mDataUri;
4722
4723 /**
4724 * Constructor
4725 * @param text A {@link CharSequence} to be displayed as the message content
4726 * @param timestamp Time at which the message arrived
4727 * @param sender A {@link CharSequence} to be used for displaying the name of the
4728 * sender. Should be <code>null</code> for messages by the current user, in which case
4729 * the platform will insert {@link MessagingStyle#getUserDisplayName()}.
4730 * Should be unique amongst all individuals in the conversation, and should be
4731 * consistent during re-posts of the notification.
4732 */
4733 public Message(CharSequence text, long timestamp, CharSequence sender){
4734 mText = text;
4735 mTimestamp = timestamp;
4736 mSender = sender;
4737 }
4738
4739 /**
4740 * Sets a binary blob of data and an associated MIME type for a message. In the case
4741 * where the platform doesn't support the MIME type, the original text provided in the
4742 * constructor will be used.
4743 * @param dataMimeType The MIME type of the content. See
4744 * <a href="{@docRoot}notifications/messaging.html"> for the list of supported MIME
4745 * types on Android and Android Wear.
4746 * @param dataUri The uri containing the content whose type is given by the MIME type.
4747 * <p class="note">
4748 * <ol>
4749 * <li>Notification Listeners including the System UI need permission to access the
4750 * data the Uri points to. The recommended ways to do this are:</li>
4751 * <li>Store the data in your own ContentProvider, making sure that other apps have
4752 * the correct permission to access your provider. The preferred mechanism for
4753 * providing access is to use per-URI permissions which are temporary and only
4754 * grant access to the receiving application. An easy way to create a
4755 * ContentProvider like this is to use the FileProvider helper class.</li>
4756 * <li>Use the system MediaStore. The MediaStore is primarily aimed at video, audio
4757 * and image MIME types, however beginning with Android 3.0 (API level 11) it can
4758 * also store non-media types (see MediaStore.Files for more info). Files can be
4759 * inserted into the MediaStore using scanFile() after which a content:// style
4760 * Uri suitable for sharing is passed to the provided onScanCompleted() callback.
4761 * Note that once added to the system MediaStore the content is accessible to any
4762 * app on the device.</li>
4763 * </ol>
4764 * @return this object for method chaining
4765 */
4766 public Message setData(String dataMimeType, Uri dataUri) {
4767 mDataMimeType = dataMimeType;
4768 mDataUri = dataUri;
4769 return this;
4770 }
4771
Alex Hillsfc737de2016-03-23 17:33:02 -04004772 /**
4773 * Get the text to be used for this message, or the fallback text if a type and content
4774 * Uri have been set
4775 */
4776 public CharSequence getText() {
4777 return mText;
4778 }
4779
4780 /**
4781 * Get the time at which this message arrived
4782 */
4783 public long getTimestamp() {
4784 return mTimestamp;
4785 }
4786
4787 /**
4788 * Get the text used to display the contact's name in the messaging experience
4789 */
4790 public CharSequence getSender() {
4791 return mSender;
4792 }
4793
4794 /**
4795 * Get the MIME type of the data pointed to by the Uri
4796 */
4797 public String getDataMimeType() {
4798 return mDataMimeType;
4799 }
4800
4801 /**
4802 * Get the the Uri pointing to the content of the message. Can be null, in which case
4803 * {@see #getText()} is used.
4804 */
4805 public Uri getDataUri() {
4806 return mDataUri;
4807 }
4808
Alex Hillsd9b04d92016-04-11 16:38:16 -04004809 private Bundle toBundle() {
4810 Bundle bundle = new Bundle();
Alex Hillsfc737de2016-03-23 17:33:02 -04004811 if (mText != null) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04004812 bundle.putCharSequence(KEY_TEXT, mText);
Alex Hillsfc737de2016-03-23 17:33:02 -04004813 }
Alex Hillsd9b04d92016-04-11 16:38:16 -04004814 bundle.putLong(KEY_TIMESTAMP, mTimestamp);
Alex Hillsfc737de2016-03-23 17:33:02 -04004815 if (mSender != null) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04004816 bundle.putCharSequence(KEY_SENDER, mSender);
Alex Hillsfc737de2016-03-23 17:33:02 -04004817 }
4818 if (mDataMimeType != null) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04004819 bundle.putString(KEY_DATA_MIME_TYPE, mDataMimeType);
Alex Hillsfc737de2016-03-23 17:33:02 -04004820 }
4821 if (mDataUri != null) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04004822 bundle.putParcelable(KEY_DATA_URI, mDataUri);
Alex Hillsfc737de2016-03-23 17:33:02 -04004823 }
Alex Hillsd9b04d92016-04-11 16:38:16 -04004824 return bundle;
Alex Hillsfc737de2016-03-23 17:33:02 -04004825 }
4826
Alex Hillsd9b04d92016-04-11 16:38:16 -04004827 static Bundle[] getBundleArrayForMessages(List<Message> messages) {
4828 Bundle[] bundles = new Bundle[messages.size()];
4829 final int N = messages.size();
4830 for (int i = 0; i < N; i++) {
4831 bundles[i] = messages.get(i).toBundle();
4832 }
4833 return bundles;
4834 }
4835
Adrian Roosdedd1df2016-04-26 16:38:47 -07004836 static List<Message> getMessagesFromBundleArray(Parcelable[] bundles) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04004837 List<Message> messages = new ArrayList<>(bundles.length);
4838 for (int i = 0; i < bundles.length; i++) {
Adrian Roosdedd1df2016-04-26 16:38:47 -07004839 if (bundles[i] instanceof Bundle) {
4840 Message message = getMessageFromBundle((Bundle)bundles[i]);
4841 if (message != null) {
4842 messages.add(message);
4843 }
Alex Hillsd9b04d92016-04-11 16:38:16 -04004844 }
4845 }
4846 return messages;
4847 }
4848
4849 static Message getMessageFromBundle(Bundle bundle) {
4850 try {
Adrian Roosfbddd2c2016-05-13 12:57:20 -07004851 if (!bundle.containsKey(KEY_TEXT) || !bundle.containsKey(KEY_TIMESTAMP)) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04004852 return null;
4853 } else {
4854 Message message = new Message(bundle.getCharSequence(KEY_TEXT),
4855 bundle.getLong(KEY_TIMESTAMP), bundle.getCharSequence(KEY_SENDER));
4856 if (bundle.containsKey(KEY_DATA_MIME_TYPE) &&
4857 bundle.containsKey(KEY_DATA_URI)) {
4858
4859 message.setData(bundle.getString(KEY_DATA_MIME_TYPE),
4860 (Uri) bundle.getParcelable(KEY_DATA_URI));
Alex Hillsfc737de2016-03-23 17:33:02 -04004861 }
Alex Hillsd9b04d92016-04-11 16:38:16 -04004862 return message;
4863 }
4864 } catch (ClassCastException e) {
4865 return null;
4866 }
4867 }
Alex Hillsfc737de2016-03-23 17:33:02 -04004868 }
4869 }
4870
4871 /**
Daniel Sandler879c5e02012-04-17 16:46:51 -04004872 * Helper class for generating large-format notifications that include a list of (up to 5) strings.
Joe Malin8d40d042012-11-05 11:36:40 -08004873 *
Robert Ly91c5ce32014-06-08 15:37:00 -07004874 * Here's how you'd set the <code>InboxStyle</code> on a notification:
Daniel Sandler879c5e02012-04-17 16:46:51 -04004875 * <pre class="prettyprint">
Robert Ly91c5ce32014-06-08 15:37:00 -07004876 * Notification notif = new Notification.Builder(mContext)
4877 * .setContentTitle(&quot;5 New mails from &quot; + sender.toString())
4878 * .setContentText(subject)
4879 * .setSmallIcon(R.drawable.new_mail)
4880 * .setLargeIcon(aBitmap)
4881 * .setStyle(new Notification.InboxStyle()
4882 * .addLine(str1)
4883 * .addLine(str2)
4884 * .setContentTitle(&quot;&quot;)
4885 * .setSummaryText(&quot;+3 more&quot;))
4886 * .build();
Daniel Sandler879c5e02012-04-17 16:46:51 -04004887 * </pre>
Joe Malin8d40d042012-11-05 11:36:40 -08004888 *
Daniel Sandler879c5e02012-04-17 16:46:51 -04004889 * @see Notification#bigContentView
4890 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004891 public static class InboxStyle extends Style {
Daniel Sandler879c5e02012-04-17 16:46:51 -04004892 private ArrayList<CharSequence> mTexts = new ArrayList<CharSequence>(5);
4893
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004894 public InboxStyle() {
4895 }
4896
Adrian Roosf5faf9d2016-05-23 13:56:15 -07004897 /**
4898 * @deprecated use {@code InboxStyle()}.
4899 */
4900 @Deprecated
Daniel Sandler879c5e02012-04-17 16:46:51 -04004901 public InboxStyle(Builder builder) {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004902 setBuilder(builder);
Daniel Sandler879c5e02012-04-17 16:46:51 -04004903 }
4904
Chris Wrend6297db2012-05-03 16:20:13 -04004905 /**
4906 * Overrides ContentTitle in the big form of the template.
4907 * This defaults to the value passed to setContentTitle().
4908 */
4909 public InboxStyle setBigContentTitle(CharSequence title) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04004910 internalSetBigContentTitle(safeCharSequence(title));
Chris Wrend6297db2012-05-03 16:20:13 -04004911 return this;
4912 }
4913
4914 /**
4915 * Set the first line of text after the detail section in the big form of the template.
4916 */
4917 public InboxStyle setSummaryText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04004918 internalSetSummaryText(safeCharSequence(cs));
Chris Wrend6297db2012-05-03 16:20:13 -04004919 return this;
4920 }
4921
Chris Wren0bd664d2012-08-01 13:56:56 -04004922 /**
4923 * Append a line to the digest section of the Inbox notification.
4924 */
Daniel Sandler879c5e02012-04-17 16:46:51 -04004925 public InboxStyle addLine(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04004926 mTexts.add(safeCharSequence(cs));
Daniel Sandler879c5e02012-04-17 16:46:51 -04004927 return this;
4928 }
4929
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004930 /**
4931 * @hide
4932 */
4933 public void addExtras(Bundle extras) {
4934 super.addExtras(extras);
Christoph Studer4600f9b2014-07-22 22:44:43 +02004935
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004936 CharSequence[] a = new CharSequence[mTexts.size()];
4937 extras.putCharSequenceArray(EXTRA_TEXT_LINES, mTexts.toArray(a));
4938 }
4939
Christoph Studer4600f9b2014-07-22 22:44:43 +02004940 /**
4941 * @hide
4942 */
4943 @Override
4944 protected void restoreFromExtras(Bundle extras) {
4945 super.restoreFromExtras(extras);
4946
4947 mTexts.clear();
4948 if (extras.containsKey(EXTRA_TEXT_LINES)) {
4949 Collections.addAll(mTexts, extras.getCharSequenceArray(EXTRA_TEXT_LINES));
4950 }
4951 }
4952
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004953 /**
4954 * @hide
4955 */
4956 public RemoteViews makeBigContentView() {
Selim Cinekc848c3a2016-01-13 15:27:30 -08004957 // Remove the content text so it disappears unless you have a summary
Christoph Studer4600f9b2014-07-22 22:44:43 +02004958 // Nasty
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004959 CharSequence oldBuilderContentText = mBuilder.mN.extras.getCharSequence(EXTRA_TEXT);
4960 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, null);
Christoph Studer4600f9b2014-07-22 22:44:43 +02004961
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004962 RemoteViews contentView = getStandardView(mBuilder.getInboxLayoutResource());
Daniel Sandler619738c2012-06-07 16:33:08 -04004963
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004964 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, oldBuilderContentText);
Christoph Studer4600f9b2014-07-22 22:44:43 +02004965
Chris Wrend6297db2012-05-03 16:20:13 -04004966 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 -04004967 R.id.inbox_text4, R.id.inbox_text5, R.id.inbox_text6};
Chris Wrend6297db2012-05-03 16:20:13 -04004968
Chris Wren4ed80d52012-05-17 09:30:03 -04004969 // Make sure all rows are gone in case we reuse a view.
4970 for (int rowId : rowIds) {
4971 contentView.setViewVisibility(rowId, View.GONE);
4972 }
4973
Daniel Sandler879c5e02012-04-17 16:46:51 -04004974 int i=0;
Selim Cinek07c80172016-04-21 16:40:47 -07004975 int topPadding = mBuilder.mContext.getResources().getDimensionPixelSize(
4976 R.dimen.notification_inbox_item_top_padding);
Selim Cinek247fa012016-02-18 09:50:48 -08004977 boolean first = true;
Selim Cinek07c80172016-04-21 16:40:47 -07004978 int onlyViewId = 0;
4979 int maxRows = rowIds.length;
4980 if (mBuilder.mActions.size() > 0) {
4981 maxRows--;
4982 }
4983 while (i < mTexts.size() && i < maxRows) {
Daniel Sandler879c5e02012-04-17 16:46:51 -04004984 CharSequence str = mTexts.get(i);
Selim Cinek07c80172016-04-21 16:40:47 -07004985 if (!TextUtils.isEmpty(str)) {
Daniel Sandler879c5e02012-04-17 16:46:51 -04004986 contentView.setViewVisibility(rowIds[i], View.VISIBLE);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004987 contentView.setTextViewText(rowIds[i], mBuilder.processLegacyText(str));
Selim Cinek07c80172016-04-21 16:40:47 -07004988 contentView.setViewPadding(rowIds[i], 0, topPadding, 0, 0);
Selim Cinek247fa012016-02-18 09:50:48 -08004989 handleInboxImageMargin(contentView, rowIds[i], first);
Selim Cinek07c80172016-04-21 16:40:47 -07004990 if (first) {
4991 onlyViewId = rowIds[i];
4992 } else {
4993 onlyViewId = 0;
4994 }
Selim Cinek247fa012016-02-18 09:50:48 -08004995 first = false;
Daniel Sandler879c5e02012-04-17 16:46:51 -04004996 }
4997 i++;
4998 }
Selim Cinek07c80172016-04-21 16:40:47 -07004999 if (onlyViewId != 0) {
5000 // We only have 1 entry, lets make it look like the normal Text of a Bigtext
5001 topPadding = mBuilder.mContext.getResources().getDimensionPixelSize(
5002 R.dimen.notification_text_margin_top);
5003 contentView.setViewPadding(onlyViewId, 0, topPadding, 0, 0);
5004 }
Selim Cinek1e0bf612015-11-20 15:57:26 -08005005
Daniel Sandler879c5e02012-04-17 16:46:51 -04005006 return contentView;
5007 }
Selim Cinek1e0bf612015-11-20 15:57:26 -08005008
Selim Cinek247fa012016-02-18 09:50:48 -08005009 private void handleInboxImageMargin(RemoteViews contentView, int id, boolean first) {
Selim Cinek1e0bf612015-11-20 15:57:26 -08005010 int endMargin = 0;
Selim Cinek247fa012016-02-18 09:50:48 -08005011 if (first) {
5012 final int max = mBuilder.mN.extras.getInt(EXTRA_PROGRESS_MAX, 0);
5013 final boolean ind = mBuilder.mN.extras.getBoolean(EXTRA_PROGRESS_INDETERMINATE);
5014 boolean hasProgress = max != 0 || ind;
Selim Cinek279fa862016-06-14 10:57:25 -07005015 if (mBuilder.mN.hasLargeIcon() && !hasProgress) {
Adrian Roos2d5dbba2016-06-08 17:11:53 -07005016 endMargin = R.dimen.notification_content_picture_margin;
Selim Cinek247fa012016-02-18 09:50:48 -08005017 }
Selim Cinek1e0bf612015-11-20 15:57:26 -08005018 }
Adrian Roos2d5dbba2016-06-08 17:11:53 -07005019 contentView.setViewLayoutMarginEndDimen(id, endMargin);
Selim Cinek1e0bf612015-11-20 15:57:26 -08005020 }
Daniel Sandler879c5e02012-04-17 16:46:51 -04005021 }
Dan Sandler842dd772014-05-15 09:36:47 -04005022
5023 /**
5024 * Notification style for media playback notifications.
5025 *
5026 * In the expanded form, {@link Notification#bigContentView}, up to 5
5027 * {@link Notification.Action}s specified with
Dan Sandler86647982015-05-13 23:41:13 -04005028 * {@link Notification.Builder#addAction(Action) addAction} will be
Dan Sandler842dd772014-05-15 09:36:47 -04005029 * shown as icon-only pushbuttons, suitable for transport controls. The Bitmap given to
5030 * {@link Notification.Builder#setLargeIcon(android.graphics.Bitmap) setLargeIcon()} will be
5031 * treated as album artwork.
5032 *
5033 * Unlike the other styles provided here, MediaStyle can also modify the standard-size
5034 * {@link Notification#contentView}; by providing action indices to
Christoph Studerfde6f4d2014-12-12 13:23:26 +01005035 * {@link #setShowActionsInCompactView(int...)} you can promote up to 3 actions to be displayed
Dan Sandler842dd772014-05-15 09:36:47 -04005036 * in the standard view alongside the usual content.
5037 *
Bryan Mawhinney6be8de32014-07-18 10:35:12 +01005038 * Notifications created with MediaStyle will have their category set to
5039 * {@link Notification#CATEGORY_TRANSPORT CATEGORY_TRANSPORT} unless you set a different
5040 * category using {@link Notification.Builder#setCategory(String) setCategory()}.
5041 *
Jeff Browndba34ba2014-06-24 20:46:03 -07005042 * Finally, if you attach a {@link android.media.session.MediaSession.Token} using
5043 * {@link android.app.Notification.MediaStyle#setMediaSession(MediaSession.Token)},
Dan Sandler842dd772014-05-15 09:36:47 -04005044 * the System UI can identify this as a notification representing an active media session
5045 * and respond accordingly (by showing album artwork in the lockscreen, for example).
5046 *
5047 * To use this style with your Notification, feed it to
5048 * {@link Notification.Builder#setStyle(android.app.Notification.Style)} like so:
5049 * <pre class="prettyprint">
5050 * Notification noti = new Notification.Builder()
5051 * .setSmallIcon(R.drawable.ic_stat_player)
Christoph Studere935fe92014-11-24 14:18:06 +01005052 * .setContentTitle(&quot;Track title&quot;)
5053 * .setContentText(&quot;Artist - Album&quot;)
5054 * .setLargeIcon(albumArtBitmap))
Bryan Mawhinney6be8de32014-07-18 10:35:12 +01005055 * .setStyle(<b>new Notification.MediaStyle()</b>
5056 * .setMediaSession(mySession))
Dan Sandler842dd772014-05-15 09:36:47 -04005057 * .build();
5058 * </pre>
5059 *
5060 * @see Notification#bigContentView
5061 */
5062 public static class MediaStyle extends Style {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02005063 static final int MAX_MEDIA_BUTTONS_IN_COMPACT = 3;
Dan Sandler842dd772014-05-15 09:36:47 -04005064 static final int MAX_MEDIA_BUTTONS = 5;
5065
5066 private int[] mActionsToShowInCompact = null;
Jeff Browndba34ba2014-06-24 20:46:03 -07005067 private MediaSession.Token mToken;
Dan Sandler842dd772014-05-15 09:36:47 -04005068
5069 public MediaStyle() {
5070 }
5071
Adrian Roosf5faf9d2016-05-23 13:56:15 -07005072 /**
5073 * @deprecated use {@code MediaStyle()}.
5074 */
5075 @Deprecated
Dan Sandler842dd772014-05-15 09:36:47 -04005076 public MediaStyle(Builder builder) {
5077 setBuilder(builder);
5078 }
5079
5080 /**
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02005081 * 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 -04005082 * notification view.
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02005083 *
5084 * @param actions the indices of the actions to show in the compact notification view
Dan Sandler842dd772014-05-15 09:36:47 -04005085 */
5086 public MediaStyle setShowActionsInCompactView(int...actions) {
5087 mActionsToShowInCompact = actions;
5088 return this;
5089 }
5090
5091 /**
Jeff Browndba34ba2014-06-24 20:46:03 -07005092 * Attach a {@link android.media.session.MediaSession.Token} to this Notification
5093 * to provide additional playback information and control to the SystemUI.
Dan Sandler842dd772014-05-15 09:36:47 -04005094 */
Jeff Browndba34ba2014-06-24 20:46:03 -07005095 public MediaStyle setMediaSession(MediaSession.Token token) {
Dan Sandler842dd772014-05-15 09:36:47 -04005096 mToken = token;
5097 return this;
5098 }
5099
Christoph Studer4600f9b2014-07-22 22:44:43 +02005100 /**
5101 * @hide
5102 */
Dan Sandler842dd772014-05-15 09:36:47 -04005103 @Override
5104 public Notification buildStyled(Notification wip) {
Christoph Studer4600f9b2014-07-22 22:44:43 +02005105 super.buildStyled(wip);
Bryan Mawhinney6be8de32014-07-18 10:35:12 +01005106 if (wip.category == null) {
5107 wip.category = Notification.CATEGORY_TRANSPORT;
5108 }
Dan Sandler842dd772014-05-15 09:36:47 -04005109 return wip;
5110 }
5111
Christoph Studer4600f9b2014-07-22 22:44:43 +02005112 /**
5113 * @hide
5114 */
5115 @Override
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005116 public RemoteViews makeContentView() {
5117 return makeMediaContentView();
Christoph Studer4600f9b2014-07-22 22:44:43 +02005118 }
5119
5120 /**
5121 * @hide
5122 */
5123 @Override
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005124 public RemoteViews makeBigContentView() {
5125 return makeMediaBigContentView();
Christoph Studer4600f9b2014-07-22 22:44:43 +02005126 }
5127
Selim Cinekcc10bfb2016-02-10 16:24:21 -08005128 /**
5129 * @hide
5130 */
5131 @Override
5132 public RemoteViews makeHeadsUpContentView() {
5133 RemoteViews expanded = makeMediaBigContentView();
5134 return expanded != null ? expanded : makeMediaContentView();
5135 }
5136
Dan Sandler842dd772014-05-15 09:36:47 -04005137 /** @hide */
5138 @Override
5139 public void addExtras(Bundle extras) {
5140 super.addExtras(extras);
5141
5142 if (mToken != null) {
5143 extras.putParcelable(EXTRA_MEDIA_SESSION, mToken);
5144 }
Bryan Mawhinneye191f902014-07-22 12:50:09 +01005145 if (mActionsToShowInCompact != null) {
5146 extras.putIntArray(EXTRA_COMPACT_ACTIONS, mActionsToShowInCompact);
5147 }
Dan Sandler842dd772014-05-15 09:36:47 -04005148 }
5149
Christoph Studer4600f9b2014-07-22 22:44:43 +02005150 /**
5151 * @hide
5152 */
5153 @Override
5154 protected void restoreFromExtras(Bundle extras) {
5155 super.restoreFromExtras(extras);
5156
5157 if (extras.containsKey(EXTRA_MEDIA_SESSION)) {
5158 mToken = extras.getParcelable(EXTRA_MEDIA_SESSION);
5159 }
5160 if (extras.containsKey(EXTRA_COMPACT_ACTIONS)) {
5161 mActionsToShowInCompact = extras.getIntArray(EXTRA_COMPACT_ACTIONS);
5162 }
5163 }
5164
Selim Cinek5bf069a2015-11-10 19:14:27 -05005165 private RemoteViews generateMediaActionButton(Action action, int color) {
Dan Sandler842dd772014-05-15 09:36:47 -04005166 final boolean tombstone = (action.actionIntent == null);
Selim Cinekf33b1112015-07-15 17:45:11 -07005167 RemoteViews button = new BuilderRemoteViews(mBuilder.mContext.getApplicationInfo(),
Alan Viverette3cb07a462014-06-06 14:19:53 -07005168 R.layout.notification_material_media_action);
Dan Sandler68079d52015-07-22 10:45:30 -04005169 button.setImageViewIcon(R.id.action0, action.getIcon());
Selim Cinek5bf069a2015-11-10 19:14:27 -05005170 button.setDrawableParameters(R.id.action0, false, -1, color, PorterDuff.Mode.SRC_ATOP,
5171 -1);
Dan Sandler842dd772014-05-15 09:36:47 -04005172 if (!tombstone) {
5173 button.setOnClickPendingIntent(R.id.action0, action.actionIntent);
5174 }
5175 button.setContentDescription(R.id.action0, action.title);
5176 return button;
5177 }
5178
5179 private RemoteViews makeMediaContentView() {
5180 RemoteViews view = mBuilder.applyStandardTemplate(
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02005181 R.layout.notification_template_material_media, false /* hasProgress */);
Dan Sandler842dd772014-05-15 09:36:47 -04005182
5183 final int numActions = mBuilder.mActions.size();
5184 final int N = mActionsToShowInCompact == null
5185 ? 0
5186 : Math.min(mActionsToShowInCompact.length, MAX_MEDIA_BUTTONS_IN_COMPACT);
5187 if (N > 0) {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02005188 view.removeAllViews(com.android.internal.R.id.media_actions);
Dan Sandler842dd772014-05-15 09:36:47 -04005189 for (int i = 0; i < N; i++) {
5190 if (i >= numActions) {
5191 throw new IllegalArgumentException(String.format(
5192 "setShowActionsInCompactView: action %d out of bounds (max %d)",
5193 i, numActions - 1));
5194 }
5195
5196 final Action action = mBuilder.mActions.get(mActionsToShowInCompact[i]);
Selim Cinek5bf069a2015-11-10 19:14:27 -05005197 final RemoteViews button = generateMediaActionButton(action,
Adrian Roos4ff3b122016-02-01 12:26:13 -08005198 mBuilder.resolveContrastColor());
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02005199 view.addView(com.android.internal.R.id.media_actions, button);
Dan Sandler842dd772014-05-15 09:36:47 -04005200 }
5201 }
Selim Cinekfdc738f2016-01-27 20:04:27 -08005202 handleImage(view);
5203 // handle the content margin
Adrian Roos2d5dbba2016-06-08 17:11:53 -07005204 int endMargin = R.dimen.notification_content_margin_end;
Selim Cinek279fa862016-06-14 10:57:25 -07005205 if (mBuilder.mN.hasLargeIcon()) {
Adrian Roos2d5dbba2016-06-08 17:11:53 -07005206 endMargin = R.dimen.notification_content_plus_picture_margin_end;
Selim Cinekfdc738f2016-01-27 20:04:27 -08005207 }
Adrian Roos2d5dbba2016-06-08 17:11:53 -07005208 view.setViewLayoutMarginEndDimen(R.id.notification_main_column, endMargin);
Dan Sandler842dd772014-05-15 09:36:47 -04005209 return view;
5210 }
5211
5212 private RemoteViews makeMediaBigContentView() {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02005213 final int actionCount = Math.min(mBuilder.mActions.size(), MAX_MEDIA_BUTTONS);
Selim Cinekcc10bfb2016-02-10 16:24:21 -08005214 // Dont add an expanded view if there is no more content to be revealed
5215 int actionsInCompact = mActionsToShowInCompact == null
5216 ? 0
5217 : Math.min(mActionsToShowInCompact.length, MAX_MEDIA_BUTTONS_IN_COMPACT);
Selim Cinek279fa862016-06-14 10:57:25 -07005218 if (!mBuilder.mN.hasLargeIcon() && actionCount <= actionsInCompact) {
Selim Cinekcc10bfb2016-02-10 16:24:21 -08005219 return null;
5220 }
Selim Cinek5bf069a2015-11-10 19:14:27 -05005221 RemoteViews big = mBuilder.applyStandardTemplate(
5222 R.layout.notification_template_material_big_media,
5223 false);
Dan Sandler842dd772014-05-15 09:36:47 -04005224
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02005225 if (actionCount > 0) {
5226 big.removeAllViews(com.android.internal.R.id.media_actions);
5227 for (int i = 0; i < actionCount; i++) {
Selim Cinek5bf069a2015-11-10 19:14:27 -05005228 final RemoteViews button = generateMediaActionButton(mBuilder.mActions.get(i),
Adrian Roos4ff3b122016-02-01 12:26:13 -08005229 mBuilder.resolveContrastColor());
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02005230 big.addView(com.android.internal.R.id.media_actions, button);
Dan Sandler842dd772014-05-15 09:36:47 -04005231 }
5232 }
Selim Cinek5bf069a2015-11-10 19:14:27 -05005233 handleImage(big);
Dan Sandler842dd772014-05-15 09:36:47 -04005234 return big;
5235 }
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02005236
Selim Cinek5bf069a2015-11-10 19:14:27 -05005237 private void handleImage(RemoteViews contentView) {
Selim Cinek279fa862016-06-14 10:57:25 -07005238 if (mBuilder.mN.hasLargeIcon()) {
Adrian Roos2d5dbba2016-06-08 17:11:53 -07005239 contentView.setViewLayoutMarginEndDimen(R.id.line1, 0);
5240 contentView.setViewLayoutMarginEndDimen(R.id.text, 0);
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02005241 }
5242 }
5243
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02005244 /**
5245 * @hide
5246 */
5247 @Override
5248 protected boolean hasProgress() {
5249 return false;
5250 }
Dan Sandler842dd772014-05-15 09:36:47 -04005251 }
Griff Hazen61a9e862014-05-22 16:05:19 -07005252
Selim Cinek593610c2016-02-16 18:42:57 -08005253 /**
5254 * Notification style for custom views that are decorated by the system
5255 *
5256 * <p>Instead of providing a notification that is completely custom, a developer can set this
5257 * style and still obtain system decorations like the notification header with the expand
5258 * affordance and actions.
5259 *
5260 * <p>Use {@link android.app.Notification.Builder#setCustomContentView(RemoteViews)},
5261 * {@link android.app.Notification.Builder#setCustomBigContentView(RemoteViews)} and
5262 * {@link android.app.Notification.Builder#setCustomHeadsUpContentView(RemoteViews)} to set the
5263 * corresponding custom views to display.
5264 *
5265 * To use this style with your Notification, feed it to
5266 * {@link Notification.Builder#setStyle(android.app.Notification.Style)} like so:
5267 * <pre class="prettyprint">
5268 * Notification noti = new Notification.Builder()
5269 * .setSmallIcon(R.drawable.ic_stat_player)
5270 * .setLargeIcon(albumArtBitmap))
5271 * .setCustomContentView(contentView);
5272 * .setStyle(<b>new Notification.DecoratedCustomViewStyle()</b>)
5273 * .build();
5274 * </pre>
5275 */
5276 public static class DecoratedCustomViewStyle extends Style {
5277
5278 public DecoratedCustomViewStyle() {
5279 }
5280
Selim Cinek593610c2016-02-16 18:42:57 -08005281 /**
5282 * @hide
5283 */
5284 public boolean displayCustomViewInline() {
5285 return true;
5286 }
5287
5288 /**
5289 * @hide
5290 */
5291 @Override
5292 public RemoteViews makeContentView() {
5293 return makeStandardTemplateWithCustomContent(mBuilder.mN.contentView);
5294 }
5295
5296 /**
5297 * @hide
5298 */
5299 @Override
5300 public RemoteViews makeBigContentView() {
5301 return makeDecoratedBigContentView();
5302 }
5303
5304 /**
5305 * @hide
5306 */
5307 @Override
5308 public RemoteViews makeHeadsUpContentView() {
5309 return makeDecoratedHeadsUpContentView();
5310 }
5311
Selim Cinek593610c2016-02-16 18:42:57 -08005312 private RemoteViews makeDecoratedHeadsUpContentView() {
5313 RemoteViews headsUpContentView = mBuilder.mN.headsUpContentView == null
5314 ? mBuilder.mN.contentView
5315 : mBuilder.mN.headsUpContentView;
5316 if (mBuilder.mActions.size() == 0) {
5317 return makeStandardTemplateWithCustomContent(headsUpContentView);
5318 }
5319 RemoteViews remoteViews = mBuilder.applyStandardTemplateWithActions(
5320 mBuilder.getBigBaseLayoutResource());
Selim Cinek247fa012016-02-18 09:50:48 -08005321 buildIntoRemoteViewContent(remoteViews, headsUpContentView);
Selim Cinek593610c2016-02-16 18:42:57 -08005322 return remoteViews;
5323 }
5324
Selim Cinek593610c2016-02-16 18:42:57 -08005325 private RemoteViews makeStandardTemplateWithCustomContent(RemoteViews customContent) {
5326 RemoteViews remoteViews = mBuilder.applyStandardTemplate(
5327 mBuilder.getBaseLayoutResource());
Selim Cinek247fa012016-02-18 09:50:48 -08005328 buildIntoRemoteViewContent(remoteViews, customContent);
Selim Cinek593610c2016-02-16 18:42:57 -08005329 return remoteViews;
5330 }
5331
Selim Cinek593610c2016-02-16 18:42:57 -08005332 private RemoteViews makeDecoratedBigContentView() {
5333 RemoteViews bigContentView = mBuilder.mN.bigContentView == null
5334 ? mBuilder.mN.contentView
5335 : mBuilder.mN.bigContentView;
5336 if (mBuilder.mActions.size() == 0) {
5337 return makeStandardTemplateWithCustomContent(bigContentView);
5338 }
5339 RemoteViews remoteViews = mBuilder.applyStandardTemplateWithActions(
5340 mBuilder.getBigBaseLayoutResource());
Selim Cinek247fa012016-02-18 09:50:48 -08005341 buildIntoRemoteViewContent(remoteViews, bigContentView);
Selim Cinek593610c2016-02-16 18:42:57 -08005342 return remoteViews;
5343 }
Selim Cinek247fa012016-02-18 09:50:48 -08005344
5345 private void buildIntoRemoteViewContent(RemoteViews remoteViews,
5346 RemoteViews customContent) {
Adrian Roos5081c0d2016-02-26 16:04:19 -08005347 if (customContent != null) {
Selim Cinekf91017e2016-03-14 12:25:09 -07005348 // Need to clone customContent before adding, because otherwise it can no longer be
5349 // parceled independently of remoteViews.
Adrian Roos5081c0d2016-02-26 16:04:19 -08005350 customContent = customContent.clone();
Selim Cinekf91017e2016-03-14 12:25:09 -07005351 remoteViews.removeAllViews(R.id.notification_main_column);
5352 remoteViews.addView(R.id.notification_main_column, customContent);
Adrian Roos5081c0d2016-02-26 16:04:19 -08005353 }
Selim Cinek247fa012016-02-18 09:50:48 -08005354 // also update the end margin if there is an image
Adrian Roos2d5dbba2016-06-08 17:11:53 -07005355 int endMargin = R.dimen.notification_content_margin_end;
Selim Cinek279fa862016-06-14 10:57:25 -07005356 if (mBuilder.mN.hasLargeIcon()) {
Adrian Roos2d5dbba2016-06-08 17:11:53 -07005357 endMargin = R.dimen.notification_content_plus_picture_margin_end;
Selim Cinek247fa012016-02-18 09:50:48 -08005358 }
Adrian Roos2d5dbba2016-06-08 17:11:53 -07005359 remoteViews.setViewLayoutMarginEndDimen(R.id.notification_main_column, endMargin);
Selim Cinek247fa012016-02-18 09:50:48 -08005360 }
Selim Cinek593610c2016-02-16 18:42:57 -08005361 }
5362
Selim Cinek03eb3b72016-02-18 10:39:45 -08005363 /**
5364 * Notification style for media custom views that are decorated by the system
5365 *
5366 * <p>Instead of providing a media notification that is completely custom, a developer can set
5367 * this style and still obtain system decorations like the notification header with the expand
5368 * affordance and actions.
5369 *
5370 * <p>Use {@link android.app.Notification.Builder#setCustomContentView(RemoteViews)},
5371 * {@link android.app.Notification.Builder#setCustomBigContentView(RemoteViews)} and
5372 * {@link android.app.Notification.Builder#setCustomHeadsUpContentView(RemoteViews)} to set the
5373 * corresponding custom views to display.
5374 *
5375 * To use this style with your Notification, feed it to
5376 * {@link Notification.Builder#setStyle(android.app.Notification.Style)} like so:
5377 * <pre class="prettyprint">
5378 * Notification noti = new Notification.Builder()
5379 * .setSmallIcon(R.drawable.ic_stat_player)
5380 * .setLargeIcon(albumArtBitmap))
5381 * .setCustomContentView(contentView);
5382 * .setStyle(<b>new Notification.DecoratedMediaCustomViewStyle()</b>
5383 * .setMediaSession(mySession))
5384 * .build();
5385 * </pre>
5386 *
5387 * @see android.app.Notification.DecoratedCustomViewStyle
5388 * @see android.app.Notification.MediaStyle
5389 */
5390 public static class DecoratedMediaCustomViewStyle extends MediaStyle {
5391
5392 public DecoratedMediaCustomViewStyle() {
5393 }
5394
Selim Cinek03eb3b72016-02-18 10:39:45 -08005395 /**
5396 * @hide
5397 */
5398 public boolean displayCustomViewInline() {
5399 return true;
5400 }
5401
5402 /**
5403 * @hide
5404 */
5405 @Override
5406 public RemoteViews makeContentView() {
5407 RemoteViews remoteViews = super.makeContentView();
5408 return buildIntoRemoteView(remoteViews, R.id.notification_content_container,
5409 mBuilder.mN.contentView);
5410 }
5411
5412 /**
5413 * @hide
5414 */
5415 @Override
5416 public RemoteViews makeBigContentView() {
5417 RemoteViews customRemoteView = mBuilder.mN.bigContentView != null
5418 ? mBuilder.mN.bigContentView
5419 : mBuilder.mN.contentView;
5420 return makeBigContentViewWithCustomContent(customRemoteView);
5421 }
5422
5423 private RemoteViews makeBigContentViewWithCustomContent(RemoteViews customRemoteView) {
5424 RemoteViews remoteViews = super.makeBigContentView();
5425 if (remoteViews != null) {
5426 return buildIntoRemoteView(remoteViews, R.id.notification_main_column,
5427 customRemoteView);
5428 } else if (customRemoteView != mBuilder.mN.contentView){
5429 remoteViews = super.makeContentView();
5430 return buildIntoRemoteView(remoteViews, R.id.notification_content_container,
5431 customRemoteView);
5432 } else {
5433 return null;
5434 }
5435 }
5436
5437 /**
5438 * @hide
5439 */
5440 @Override
5441 public RemoteViews makeHeadsUpContentView() {
5442 RemoteViews customRemoteView = mBuilder.mN.headsUpContentView != null
5443 ? mBuilder.mN.headsUpContentView
5444 : mBuilder.mN.contentView;
5445 return makeBigContentViewWithCustomContent(customRemoteView);
5446 }
5447
5448 private RemoteViews buildIntoRemoteView(RemoteViews remoteViews, int id,
5449 RemoteViews customContent) {
Adrian Roos5081c0d2016-02-26 16:04:19 -08005450 if (customContent != null) {
Selim Cinekf91017e2016-03-14 12:25:09 -07005451 // Need to clone customContent before adding, because otherwise it can no longer be
5452 // parceled independently of remoteViews.
Adrian Roos5081c0d2016-02-26 16:04:19 -08005453 customContent = customContent.clone();
Selim Cinekf91017e2016-03-14 12:25:09 -07005454 remoteViews.removeAllViews(id);
5455 remoteViews.addView(id, customContent);
Adrian Roos5081c0d2016-02-26 16:04:19 -08005456 }
Selim Cinek03eb3b72016-02-18 10:39:45 -08005457 return remoteViews;
5458 }
5459 }
5460
Christoph Studer4600f9b2014-07-22 22:44:43 +02005461 // When adding a new Style subclass here, don't forget to update
5462 // Builder.getNotificationStyleClass.
5463
Griff Hazen61a9e862014-05-22 16:05:19 -07005464 /**
5465 * Extender interface for use with {@link Builder#extend}. Extenders may be used to add
5466 * metadata or change options on a notification builder.
5467 */
5468 public interface Extender {
5469 /**
5470 * Apply this extender to a notification builder.
5471 * @param builder the builder to be modified.
5472 * @return the build object for chaining.
5473 */
5474 public Builder extend(Builder builder);
5475 }
5476
5477 /**
5478 * Helper class to add wearable extensions to notifications.
5479 * <p class="note"> See
5480 * <a href="{@docRoot}wear/notifications/creating.html">Creating Notifications
5481 * for Android Wear</a> for more information on how to use this class.
5482 * <p>
5483 * To create a notification with wearable extensions:
5484 * <ol>
5485 * <li>Create a {@link android.app.Notification.Builder}, setting any desired
5486 * properties.
5487 * <li>Create a {@link android.app.Notification.WearableExtender}.
5488 * <li>Set wearable-specific properties using the
5489 * {@code add} and {@code set} methods of {@link android.app.Notification.WearableExtender}.
5490 * <li>Call {@link android.app.Notification.Builder#extend} to apply the extensions to a
5491 * notification.
5492 * <li>Post the notification to the notification system with the
5493 * {@code NotificationManager.notify(...)} methods.
5494 * </ol>
5495 *
5496 * <pre class="prettyprint">
5497 * Notification notif = new Notification.Builder(mContext)
5498 * .setContentTitle(&quot;New mail from &quot; + sender.toString())
5499 * .setContentText(subject)
5500 * .setSmallIcon(R.drawable.new_mail)
5501 * .extend(new Notification.WearableExtender()
5502 * .setContentIcon(R.drawable.new_mail))
5503 * .build();
5504 * NotificationManager notificationManger =
5505 * (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
5506 * notificationManger.notify(0, notif);</pre>
5507 *
5508 * <p>Wearable extensions can be accessed on an existing notification by using the
5509 * {@code WearableExtender(Notification)} constructor,
5510 * and then using the {@code get} methods to access values.
5511 *
5512 * <pre class="prettyprint">
5513 * Notification.WearableExtender wearableExtender = new Notification.WearableExtender(
5514 * notification);
Griff Hazen14f57992014-05-26 09:07:14 -07005515 * List&lt;Notification&gt; pages = wearableExtender.getPages();</pre>
Griff Hazen61a9e862014-05-22 16:05:19 -07005516 */
5517 public static final class WearableExtender implements Extender {
5518 /**
5519 * Sentinel value for an action index that is unset.
5520 */
5521 public static final int UNSET_ACTION_INDEX = -1;
5522
5523 /**
5524 * Size value for use with {@link #setCustomSizePreset} to show this notification with
5525 * default sizing.
5526 * <p>For custom display notifications created using {@link #setDisplayIntent},
Paul Soulosaa4f4bf2015-08-04 11:59:45 -07005527 * the default is {@link #SIZE_MEDIUM}. All other notifications size automatically based
Griff Hazen61a9e862014-05-22 16:05:19 -07005528 * on their content.
5529 */
5530 public static final int SIZE_DEFAULT = 0;
5531
5532 /**
5533 * Size value for use with {@link #setCustomSizePreset} to show this notification
5534 * with an extra small size.
5535 * <p>This value is only applicable for custom display notifications created using
5536 * {@link #setDisplayIntent}.
5537 */
5538 public static final int SIZE_XSMALL = 1;
5539
5540 /**
5541 * Size value for use with {@link #setCustomSizePreset} to show this notification
5542 * with a small size.
5543 * <p>This value is only applicable for custom display notifications created using
5544 * {@link #setDisplayIntent}.
5545 */
5546 public static final int SIZE_SMALL = 2;
5547
5548 /**
5549 * Size value for use with {@link #setCustomSizePreset} to show this notification
5550 * with a medium size.
5551 * <p>This value is only applicable for custom display notifications created using
5552 * {@link #setDisplayIntent}.
5553 */
5554 public static final int SIZE_MEDIUM = 3;
5555
5556 /**
5557 * Size value for use with {@link #setCustomSizePreset} to show this notification
5558 * with a large size.
5559 * <p>This value is only applicable for custom display notifications created using
5560 * {@link #setDisplayIntent}.
5561 */
5562 public static final int SIZE_LARGE = 4;
5563
Griff Hazend5f11f92014-05-27 15:40:09 -07005564 /**
5565 * Size value for use with {@link #setCustomSizePreset} to show this notification
5566 * full screen.
5567 * <p>This value is only applicable for custom display notifications created using
5568 * {@link #setDisplayIntent}.
5569 */
5570 public static final int SIZE_FULL_SCREEN = 5;
5571
Griff Hazen5f2edfc2014-09-29 16:28:44 -07005572 /**
5573 * Sentinel value for use with {@link #setHintScreenTimeout} to keep the screen on for a
5574 * short amount of time when this notification is displayed on the screen. This
5575 * is the default value.
5576 */
5577 public static final int SCREEN_TIMEOUT_SHORT = 0;
5578
5579 /**
5580 * Sentinel value for use with {@link #setHintScreenTimeout} to keep the screen on
5581 * for a longer amount of time when this notification is displayed on the screen.
5582 */
5583 public static final int SCREEN_TIMEOUT_LONG = -1;
5584
Griff Hazen61a9e862014-05-22 16:05:19 -07005585 /** Notification extra which contains wearable extensions */
5586 private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS";
5587
Pete Gastaf6781d2014-10-07 15:17:05 -04005588 // Keys within EXTRA_WEARABLE_EXTENSIONS for wearable options.
Griff Hazen61a9e862014-05-22 16:05:19 -07005589 private static final String KEY_ACTIONS = "actions";
5590 private static final String KEY_FLAGS = "flags";
5591 private static final String KEY_DISPLAY_INTENT = "displayIntent";
5592 private static final String KEY_PAGES = "pages";
5593 private static final String KEY_BACKGROUND = "background";
5594 private static final String KEY_CONTENT_ICON = "contentIcon";
5595 private static final String KEY_CONTENT_ICON_GRAVITY = "contentIconGravity";
5596 private static final String KEY_CONTENT_ACTION_INDEX = "contentActionIndex";
5597 private static final String KEY_CUSTOM_SIZE_PRESET = "customSizePreset";
5598 private static final String KEY_CUSTOM_CONTENT_HEIGHT = "customContentHeight";
5599 private static final String KEY_GRAVITY = "gravity";
Griff Hazen5f2edfc2014-09-29 16:28:44 -07005600 private static final String KEY_HINT_SCREEN_TIMEOUT = "hintScreenTimeout";
Nadia Benbernou948627e2016-04-14 14:41:08 -04005601 private static final String KEY_DISMISSAL_ID = "dismissalId";
Griff Hazen61a9e862014-05-22 16:05:19 -07005602
5603 // Flags bitwise-ored to mFlags
5604 private static final int FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE = 0x1;
5605 private static final int FLAG_HINT_HIDE_ICON = 1 << 1;
5606 private static final int FLAG_HINT_SHOW_BACKGROUND_ONLY = 1 << 2;
5607 private static final int FLAG_START_SCROLL_BOTTOM = 1 << 3;
Griff Hazen5f2edfc2014-09-29 16:28:44 -07005608 private static final int FLAG_HINT_AVOID_BACKGROUND_CLIPPING = 1 << 4;
Alex Hills4bcb06b2016-04-05 14:26:25 -04005609 private static final int FLAG_BIG_PICTURE_AMBIENT = 1 << 5;
Alex Hills9ab3a232016-04-05 14:54:56 -04005610 private static final int FLAG_HINT_CONTENT_INTENT_LAUNCHES_ACTIVITY = 1 << 6;
Griff Hazen61a9e862014-05-22 16:05:19 -07005611
5612 // Default value for flags integer
5613 private static final int DEFAULT_FLAGS = FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE;
5614
5615 private static final int DEFAULT_CONTENT_ICON_GRAVITY = Gravity.END;
5616 private static final int DEFAULT_GRAVITY = Gravity.BOTTOM;
5617
5618 private ArrayList<Action> mActions = new ArrayList<Action>();
5619 private int mFlags = DEFAULT_FLAGS;
5620 private PendingIntent mDisplayIntent;
5621 private ArrayList<Notification> mPages = new ArrayList<Notification>();
5622 private Bitmap mBackground;
5623 private int mContentIcon;
5624 private int mContentIconGravity = DEFAULT_CONTENT_ICON_GRAVITY;
5625 private int mContentActionIndex = UNSET_ACTION_INDEX;
5626 private int mCustomSizePreset = SIZE_DEFAULT;
5627 private int mCustomContentHeight;
5628 private int mGravity = DEFAULT_GRAVITY;
Griff Hazen5f2edfc2014-09-29 16:28:44 -07005629 private int mHintScreenTimeout;
Nadia Benbernou948627e2016-04-14 14:41:08 -04005630 private String mDismissalId;
Griff Hazen61a9e862014-05-22 16:05:19 -07005631
5632 /**
5633 * Create a {@link android.app.Notification.WearableExtender} with default
5634 * options.
5635 */
5636 public WearableExtender() {
5637 }
5638
5639 public WearableExtender(Notification notif) {
5640 Bundle wearableBundle = notif.extras.getBundle(EXTRA_WEARABLE_EXTENSIONS);
5641 if (wearableBundle != null) {
5642 List<Action> actions = wearableBundle.getParcelableArrayList(KEY_ACTIONS);
5643 if (actions != null) {
5644 mActions.addAll(actions);
5645 }
5646
5647 mFlags = wearableBundle.getInt(KEY_FLAGS, DEFAULT_FLAGS);
5648 mDisplayIntent = wearableBundle.getParcelable(KEY_DISPLAY_INTENT);
5649
5650 Notification[] pages = getNotificationArrayFromBundle(
5651 wearableBundle, KEY_PAGES);
5652 if (pages != null) {
5653 Collections.addAll(mPages, pages);
5654 }
5655
5656 mBackground = wearableBundle.getParcelable(KEY_BACKGROUND);
5657 mContentIcon = wearableBundle.getInt(KEY_CONTENT_ICON);
5658 mContentIconGravity = wearableBundle.getInt(KEY_CONTENT_ICON_GRAVITY,
5659 DEFAULT_CONTENT_ICON_GRAVITY);
5660 mContentActionIndex = wearableBundle.getInt(KEY_CONTENT_ACTION_INDEX,
5661 UNSET_ACTION_INDEX);
5662 mCustomSizePreset = wearableBundle.getInt(KEY_CUSTOM_SIZE_PRESET,
5663 SIZE_DEFAULT);
5664 mCustomContentHeight = wearableBundle.getInt(KEY_CUSTOM_CONTENT_HEIGHT);
5665 mGravity = wearableBundle.getInt(KEY_GRAVITY, DEFAULT_GRAVITY);
Griff Hazen5f2edfc2014-09-29 16:28:44 -07005666 mHintScreenTimeout = wearableBundle.getInt(KEY_HINT_SCREEN_TIMEOUT);
Nadia Benbernou948627e2016-04-14 14:41:08 -04005667 mDismissalId = wearableBundle.getString(KEY_DISMISSAL_ID);
Griff Hazen61a9e862014-05-22 16:05:19 -07005668 }
5669 }
5670
5671 /**
5672 * Apply wearable extensions to a notification that is being built. This is typically
5673 * called by the {@link android.app.Notification.Builder#extend} method of
5674 * {@link android.app.Notification.Builder}.
5675 */
5676 @Override
5677 public Notification.Builder extend(Notification.Builder builder) {
5678 Bundle wearableBundle = new Bundle();
5679
5680 if (!mActions.isEmpty()) {
5681 wearableBundle.putParcelableArrayList(KEY_ACTIONS, mActions);
5682 }
5683 if (mFlags != DEFAULT_FLAGS) {
5684 wearableBundle.putInt(KEY_FLAGS, mFlags);
5685 }
5686 if (mDisplayIntent != null) {
5687 wearableBundle.putParcelable(KEY_DISPLAY_INTENT, mDisplayIntent);
5688 }
5689 if (!mPages.isEmpty()) {
5690 wearableBundle.putParcelableArray(KEY_PAGES, mPages.toArray(
5691 new Notification[mPages.size()]));
5692 }
5693 if (mBackground != null) {
5694 wearableBundle.putParcelable(KEY_BACKGROUND, mBackground);
5695 }
5696 if (mContentIcon != 0) {
5697 wearableBundle.putInt(KEY_CONTENT_ICON, mContentIcon);
5698 }
5699 if (mContentIconGravity != DEFAULT_CONTENT_ICON_GRAVITY) {
5700 wearableBundle.putInt(KEY_CONTENT_ICON_GRAVITY, mContentIconGravity);
5701 }
5702 if (mContentActionIndex != UNSET_ACTION_INDEX) {
5703 wearableBundle.putInt(KEY_CONTENT_ACTION_INDEX,
5704 mContentActionIndex);
5705 }
5706 if (mCustomSizePreset != SIZE_DEFAULT) {
5707 wearableBundle.putInt(KEY_CUSTOM_SIZE_PRESET, mCustomSizePreset);
5708 }
5709 if (mCustomContentHeight != 0) {
5710 wearableBundle.putInt(KEY_CUSTOM_CONTENT_HEIGHT, mCustomContentHeight);
5711 }
5712 if (mGravity != DEFAULT_GRAVITY) {
5713 wearableBundle.putInt(KEY_GRAVITY, mGravity);
5714 }
Griff Hazen5f2edfc2014-09-29 16:28:44 -07005715 if (mHintScreenTimeout != 0) {
5716 wearableBundle.putInt(KEY_HINT_SCREEN_TIMEOUT, mHintScreenTimeout);
5717 }
Nadia Benbernou948627e2016-04-14 14:41:08 -04005718 if (mDismissalId != null) {
5719 wearableBundle.putString(KEY_DISMISSAL_ID, mDismissalId);
5720 }
Griff Hazen61a9e862014-05-22 16:05:19 -07005721
5722 builder.getExtras().putBundle(EXTRA_WEARABLE_EXTENSIONS, wearableBundle);
5723 return builder;
5724 }
5725
5726 @Override
5727 public WearableExtender clone() {
5728 WearableExtender that = new WearableExtender();
5729 that.mActions = new ArrayList<Action>(this.mActions);
5730 that.mFlags = this.mFlags;
5731 that.mDisplayIntent = this.mDisplayIntent;
5732 that.mPages = new ArrayList<Notification>(this.mPages);
5733 that.mBackground = this.mBackground;
5734 that.mContentIcon = this.mContentIcon;
5735 that.mContentIconGravity = this.mContentIconGravity;
5736 that.mContentActionIndex = this.mContentActionIndex;
5737 that.mCustomSizePreset = this.mCustomSizePreset;
5738 that.mCustomContentHeight = this.mCustomContentHeight;
5739 that.mGravity = this.mGravity;
Griff Hazen5f2edfc2014-09-29 16:28:44 -07005740 that.mHintScreenTimeout = this.mHintScreenTimeout;
Nadia Benbernou948627e2016-04-14 14:41:08 -04005741 that.mDismissalId = this.mDismissalId;
Griff Hazen61a9e862014-05-22 16:05:19 -07005742 return that;
5743 }
5744
5745 /**
5746 * Add a wearable action to this notification.
5747 *
5748 * <p>When wearable actions are added using this method, the set of actions that
5749 * show on a wearable device splits from devices that only show actions added
5750 * using {@link android.app.Notification.Builder#addAction}. This allows for customization
5751 * of which actions display on different devices.
5752 *
5753 * @param action the action to add to this notification
5754 * @return this object for method chaining
5755 * @see android.app.Notification.Action
5756 */
5757 public WearableExtender addAction(Action action) {
5758 mActions.add(action);
5759 return this;
5760 }
5761
5762 /**
5763 * Adds wearable actions to this notification.
5764 *
5765 * <p>When wearable actions are added using this method, the set of actions that
5766 * show on a wearable device splits from devices that only show actions added
5767 * using {@link android.app.Notification.Builder#addAction}. This allows for customization
5768 * of which actions display on different devices.
5769 *
5770 * @param actions the actions to add to this notification
5771 * @return this object for method chaining
5772 * @see android.app.Notification.Action
5773 */
5774 public WearableExtender addActions(List<Action> actions) {
5775 mActions.addAll(actions);
5776 return this;
5777 }
5778
5779 /**
5780 * Clear all wearable actions present on this builder.
5781 * @return this object for method chaining.
5782 * @see #addAction
5783 */
5784 public WearableExtender clearActions() {
5785 mActions.clear();
5786 return this;
5787 }
5788
5789 /**
5790 * Get the wearable actions present on this notification.
5791 */
5792 public List<Action> getActions() {
5793 return mActions;
5794 }
5795
5796 /**
5797 * Set an intent to launch inside of an activity view when displaying
Griff Hazen14f57992014-05-26 09:07:14 -07005798 * this notification. The {@link PendingIntent} provided should be for an activity.
5799 *
5800 * <pre class="prettyprint">
5801 * Intent displayIntent = new Intent(context, MyDisplayActivity.class);
5802 * PendingIntent displayPendingIntent = PendingIntent.getActivity(context,
5803 * 0, displayIntent, PendingIntent.FLAG_UPDATE_CURRENT);
5804 * Notification notif = new Notification.Builder(context)
5805 * .extend(new Notification.WearableExtender()
5806 * .setDisplayIntent(displayPendingIntent)
5807 * .setCustomSizePreset(Notification.WearableExtender.SIZE_MEDIUM))
5808 * .build();</pre>
5809 *
5810 * <p>The activity to launch needs to allow embedding, must be exported, and
Griff Hazen831ca9d2014-06-17 00:38:38 -07005811 * should have an empty task affinity. It is also recommended to use the device
5812 * default light theme.
Griff Hazen14f57992014-05-26 09:07:14 -07005813 *
5814 * <p>Example AndroidManifest.xml entry:
5815 * <pre class="prettyprint">
5816 * &lt;activity android:name=&quot;com.example.MyDisplayActivity&quot;
5817 * android:exported=&quot;true&quot;
5818 * android:allowEmbedded=&quot;true&quot;
Griff Hazen831ca9d2014-06-17 00:38:38 -07005819 * android:taskAffinity=&quot;&quot;
5820 * android:theme=&quot;@android:style/Theme.DeviceDefault.Light&quot; /&gt;</pre>
Griff Hazen61a9e862014-05-22 16:05:19 -07005821 *
5822 * @param intent the {@link PendingIntent} for an activity
5823 * @return this object for method chaining
5824 * @see android.app.Notification.WearableExtender#getDisplayIntent
5825 */
5826 public WearableExtender setDisplayIntent(PendingIntent intent) {
5827 mDisplayIntent = intent;
5828 return this;
5829 }
5830
5831 /**
5832 * Get the intent to launch inside of an activity view when displaying this
5833 * notification. This {@code PendingIntent} should be for an activity.
5834 */
5835 public PendingIntent getDisplayIntent() {
5836 return mDisplayIntent;
5837 }
5838
5839 /**
5840 * Add an additional page of content to display with this notification. The current
5841 * notification forms the first page, and pages added using this function form
5842 * subsequent pages. This field can be used to separate a notification into multiple
5843 * sections.
5844 *
5845 * @param page the notification to add as another page
5846 * @return this object for method chaining
5847 * @see android.app.Notification.WearableExtender#getPages
5848 */
5849 public WearableExtender addPage(Notification page) {
5850 mPages.add(page);
5851 return this;
5852 }
5853
5854 /**
5855 * Add additional pages of content to display with this notification. The current
5856 * notification forms the first page, and pages added using this function form
5857 * subsequent pages. This field can be used to separate a notification into multiple
5858 * sections.
5859 *
5860 * @param pages a list of notifications
5861 * @return this object for method chaining
5862 * @see android.app.Notification.WearableExtender#getPages
5863 */
5864 public WearableExtender addPages(List<Notification> pages) {
5865 mPages.addAll(pages);
5866 return this;
5867 }
5868
5869 /**
5870 * Clear all additional pages present on this builder.
5871 * @return this object for method chaining.
5872 * @see #addPage
5873 */
5874 public WearableExtender clearPages() {
5875 mPages.clear();
5876 return this;
5877 }
5878
5879 /**
5880 * Get the array of additional pages of content for displaying this notification. The
5881 * current notification forms the first page, and elements within this array form
5882 * subsequent pages. This field can be used to separate a notification into multiple
5883 * sections.
5884 * @return the pages for this notification
5885 */
5886 public List<Notification> getPages() {
5887 return mPages;
5888 }
5889
5890 /**
5891 * Set a background image to be displayed behind the notification content.
5892 * Contrary to the {@link android.app.Notification.BigPictureStyle}, this background
5893 * will work with any notification style.
5894 *
5895 * @param background the background bitmap
5896 * @return this object for method chaining
5897 * @see android.app.Notification.WearableExtender#getBackground
5898 */
5899 public WearableExtender setBackground(Bitmap background) {
5900 mBackground = background;
5901 return this;
5902 }
5903
5904 /**
5905 * Get a background image to be displayed behind the notification content.
5906 * Contrary to the {@link android.app.Notification.BigPictureStyle}, this background
5907 * will work with any notification style.
5908 *
5909 * @return the background image
5910 * @see android.app.Notification.WearableExtender#setBackground
5911 */
5912 public Bitmap getBackground() {
5913 return mBackground;
5914 }
5915
5916 /**
5917 * Set an icon that goes with the content of this notification.
5918 */
5919 public WearableExtender setContentIcon(int icon) {
5920 mContentIcon = icon;
5921 return this;
5922 }
5923
5924 /**
5925 * Get an icon that goes with the content of this notification.
5926 */
5927 public int getContentIcon() {
5928 return mContentIcon;
5929 }
5930
5931 /**
5932 * Set the gravity that the content icon should have within the notification display.
5933 * Supported values include {@link android.view.Gravity#START} and
5934 * {@link android.view.Gravity#END}. The default value is {@link android.view.Gravity#END}.
5935 * @see #setContentIcon
5936 */
5937 public WearableExtender setContentIconGravity(int contentIconGravity) {
5938 mContentIconGravity = contentIconGravity;
5939 return this;
5940 }
5941
5942 /**
5943 * Get the gravity that the content icon should have within the notification display.
5944 * Supported values include {@link android.view.Gravity#START} and
5945 * {@link android.view.Gravity#END}. The default value is {@link android.view.Gravity#END}.
5946 * @see #getContentIcon
5947 */
5948 public int getContentIconGravity() {
5949 return mContentIconGravity;
5950 }
5951
5952 /**
5953 * Set an action from this notification's actions to be clickable with the content of
Griff Hazen14f57992014-05-26 09:07:14 -07005954 * this notification. This action will no longer display separately from the
5955 * notification's content.
5956 *
Griff Hazenca48d352014-05-28 22:37:13 -07005957 * <p>For notifications with multiple pages, child pages can also have content actions
Griff Hazen14f57992014-05-26 09:07:14 -07005958 * set, although the list of available actions comes from the main notification and not
5959 * from the child page's notification.
5960 *
5961 * @param actionIndex The index of the action to hoist onto the current notification page.
5962 * If wearable actions were added to the main notification, this index
5963 * will apply to that list, otherwise it will apply to the regular
5964 * actions list.
Griff Hazen61a9e862014-05-22 16:05:19 -07005965 */
5966 public WearableExtender setContentAction(int actionIndex) {
5967 mContentActionIndex = actionIndex;
5968 return this;
5969 }
5970
5971 /**
Griff Hazenca48d352014-05-28 22:37:13 -07005972 * Get the index of the notification action, if any, that was specified as being clickable
5973 * with the content of this notification. This action will no longer display separately
Griff Hazen14f57992014-05-26 09:07:14 -07005974 * from the notification's content.
Griff Hazen61a9e862014-05-22 16:05:19 -07005975 *
Griff Hazenca48d352014-05-28 22:37:13 -07005976 * <p>For notifications with multiple pages, child pages can also have content actions
Griff Hazen14f57992014-05-26 09:07:14 -07005977 * set, although the list of available actions comes from the main notification and not
5978 * from the child page's notification.
5979 *
5980 * <p>If wearable specific actions were added to the main notification, this index will
5981 * apply to that list, otherwise it will apply to the regular actions list.
Griff Hazenca48d352014-05-28 22:37:13 -07005982 *
5983 * @return the action index or {@link #UNSET_ACTION_INDEX} if no action was selected.
Griff Hazen61a9e862014-05-22 16:05:19 -07005984 */
5985 public int getContentAction() {
5986 return mContentActionIndex;
5987 }
5988
5989 /**
5990 * Set the gravity that this notification should have within the available viewport space.
5991 * Supported values include {@link android.view.Gravity#TOP},
5992 * {@link android.view.Gravity#CENTER_VERTICAL} and {@link android.view.Gravity#BOTTOM}.
5993 * The default value is {@link android.view.Gravity#BOTTOM}.
5994 */
5995 public WearableExtender setGravity(int gravity) {
5996 mGravity = gravity;
5997 return this;
5998 }
5999
6000 /**
6001 * Get the gravity that this notification should have within the available viewport space.
6002 * Supported values include {@link android.view.Gravity#TOP},
6003 * {@link android.view.Gravity#CENTER_VERTICAL} and {@link android.view.Gravity#BOTTOM}.
6004 * The default value is {@link android.view.Gravity#BOTTOM}.
6005 */
6006 public int getGravity() {
6007 return mGravity;
6008 }
6009
6010 /**
6011 * Set the custom size preset for the display of this notification out of the available
6012 * presets found in {@link android.app.Notification.WearableExtender}, e.g.
6013 * {@link #SIZE_LARGE}.
6014 * <p>Some custom size presets are only applicable for custom display notifications created
6015 * using {@link android.app.Notification.WearableExtender#setDisplayIntent}. Check the
6016 * documentation for the preset in question. See also
6017 * {@link #setCustomContentHeight} and {@link #getCustomSizePreset}.
6018 */
6019 public WearableExtender setCustomSizePreset(int sizePreset) {
6020 mCustomSizePreset = sizePreset;
6021 return this;
6022 }
6023
6024 /**
6025 * Get the custom size preset for the display of this notification out of the available
6026 * presets found in {@link android.app.Notification.WearableExtender}, e.g.
6027 * {@link #SIZE_LARGE}.
6028 * <p>Some custom size presets are only applicable for custom display notifications created
6029 * using {@link #setDisplayIntent}. Check the documentation for the preset in question.
6030 * See also {@link #setCustomContentHeight} and {@link #setCustomSizePreset}.
6031 */
6032 public int getCustomSizePreset() {
6033 return mCustomSizePreset;
6034 }
6035
6036 /**
6037 * Set the custom height in pixels for the display of this notification's content.
6038 * <p>This option is only available for custom display notifications created
6039 * using {@link android.app.Notification.WearableExtender#setDisplayIntent}. See also
6040 * {@link android.app.Notification.WearableExtender#setCustomSizePreset} and
6041 * {@link #getCustomContentHeight}.
6042 */
6043 public WearableExtender setCustomContentHeight(int height) {
6044 mCustomContentHeight = height;
6045 return this;
6046 }
6047
6048 /**
6049 * Get the custom height in pixels for the display of this notification's content.
6050 * <p>This option is only available for custom display notifications created
6051 * using {@link #setDisplayIntent}. See also {@link #setCustomSizePreset} and
6052 * {@link #setCustomContentHeight}.
6053 */
6054 public int getCustomContentHeight() {
6055 return mCustomContentHeight;
6056 }
6057
6058 /**
6059 * Set whether the scrolling position for the contents of this notification should start
6060 * at the bottom of the contents instead of the top when the contents are too long to
6061 * display within the screen. Default is false (start scroll at the top).
6062 */
6063 public WearableExtender setStartScrollBottom(boolean startScrollBottom) {
6064 setFlag(FLAG_START_SCROLL_BOTTOM, startScrollBottom);
6065 return this;
6066 }
6067
6068 /**
6069 * Get whether the scrolling position for the contents of this notification should start
6070 * at the bottom of the contents instead of the top when the contents are too long to
6071 * display within the screen. Default is false (start scroll at the top).
6072 */
6073 public boolean getStartScrollBottom() {
6074 return (mFlags & FLAG_START_SCROLL_BOTTOM) != 0;
6075 }
6076
6077 /**
6078 * Set whether the content intent is available when the wearable device is not connected
6079 * to a companion device. The user can still trigger this intent when the wearable device
6080 * is offline, but a visual hint will indicate that the content intent may not be available.
6081 * Defaults to true.
6082 */
6083 public WearableExtender setContentIntentAvailableOffline(
6084 boolean contentIntentAvailableOffline) {
6085 setFlag(FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE, contentIntentAvailableOffline);
6086 return this;
6087 }
6088
6089 /**
6090 * Get whether the content intent is available when the wearable device is not connected
6091 * to a companion device. The user can still trigger this intent when the wearable device
6092 * is offline, but a visual hint will indicate that the content intent may not be available.
6093 * Defaults to true.
6094 */
6095 public boolean getContentIntentAvailableOffline() {
6096 return (mFlags & FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE) != 0;
6097 }
6098
6099 /**
6100 * Set a hint that this notification's icon should not be displayed.
6101 * @param hintHideIcon {@code true} to hide the icon, {@code false} otherwise.
6102 * @return this object for method chaining
6103 */
6104 public WearableExtender setHintHideIcon(boolean hintHideIcon) {
6105 setFlag(FLAG_HINT_HIDE_ICON, hintHideIcon);
6106 return this;
6107 }
6108
6109 /**
6110 * Get a hint that this notification's icon should not be displayed.
6111 * @return {@code true} if this icon should not be displayed, false otherwise.
6112 * The default value is {@code false} if this was never set.
6113 */
6114 public boolean getHintHideIcon() {
6115 return (mFlags & FLAG_HINT_HIDE_ICON) != 0;
6116 }
6117
6118 /**
6119 * Set a visual hint that only the background image of this notification should be
6120 * displayed, and other semantic content should be hidden. This hint is only applicable
6121 * to sub-pages added using {@link #addPage}.
6122 */
6123 public WearableExtender setHintShowBackgroundOnly(boolean hintShowBackgroundOnly) {
6124 setFlag(FLAG_HINT_SHOW_BACKGROUND_ONLY, hintShowBackgroundOnly);
6125 return this;
6126 }
6127
6128 /**
6129 * Get a visual hint that only the background image of this notification should be
6130 * displayed, and other semantic content should be hidden. This hint is only applicable
6131 * to sub-pages added using {@link android.app.Notification.WearableExtender#addPage}.
6132 */
6133 public boolean getHintShowBackgroundOnly() {
6134 return (mFlags & FLAG_HINT_SHOW_BACKGROUND_ONLY) != 0;
6135 }
6136
Griff Hazen5f2edfc2014-09-29 16:28:44 -07006137 /**
Griff Hazen9c5be4e2014-11-17 17:47:50 -08006138 * Set a hint that this notification's background should not be clipped if possible,
6139 * and should instead be resized to fully display on the screen, retaining the aspect
6140 * ratio of the image. This can be useful for images like barcodes or qr codes.
Griff Hazen5f2edfc2014-09-29 16:28:44 -07006141 * @param hintAvoidBackgroundClipping {@code true} to avoid clipping if possible.
6142 * @return this object for method chaining
6143 */
6144 public WearableExtender setHintAvoidBackgroundClipping(
6145 boolean hintAvoidBackgroundClipping) {
6146 setFlag(FLAG_HINT_AVOID_BACKGROUND_CLIPPING, hintAvoidBackgroundClipping);
6147 return this;
6148 }
6149
6150 /**
Griff Hazen9c5be4e2014-11-17 17:47:50 -08006151 * Get a hint that this notification's background should not be clipped if possible,
6152 * and should instead be resized to fully display on the screen, retaining the aspect
6153 * ratio of the image. This can be useful for images like barcodes or qr codes.
Griff Hazen5f2edfc2014-09-29 16:28:44 -07006154 * @return {@code true} if it's ok if the background is clipped on the screen, false
6155 * otherwise. The default value is {@code false} if this was never set.
6156 */
6157 public boolean getHintAvoidBackgroundClipping() {
6158 return (mFlags & FLAG_HINT_AVOID_BACKGROUND_CLIPPING) != 0;
6159 }
6160
6161 /**
6162 * Set a hint that the screen should remain on for at least this duration when
6163 * this notification is displayed on the screen.
6164 * @param timeout The requested screen timeout in milliseconds. Can also be either
6165 * {@link #SCREEN_TIMEOUT_SHORT} or {@link #SCREEN_TIMEOUT_LONG}.
6166 * @return this object for method chaining
6167 */
6168 public WearableExtender setHintScreenTimeout(int timeout) {
6169 mHintScreenTimeout = timeout;
6170 return this;
6171 }
6172
6173 /**
6174 * Get the duration, in milliseconds, that the screen should remain on for
6175 * when this notification is displayed.
6176 * @return the duration in milliseconds if > 0, or either one of the sentinel values
6177 * {@link #SCREEN_TIMEOUT_SHORT} or {@link #SCREEN_TIMEOUT_LONG}.
6178 */
6179 public int getHintScreenTimeout() {
6180 return mHintScreenTimeout;
6181 }
6182
Alex Hills9ab3a232016-04-05 14:54:56 -04006183 /**
Alex Hills4bcb06b2016-04-05 14:26:25 -04006184 * Set a hint that this notification's {@link BigPictureStyle} (if present) should be
6185 * converted to low-bit and displayed in ambient mode, especially useful for barcodes and
6186 * qr codes, as well as other simple black-and-white tickets.
6187 * @param hintAmbientBigPicture {@code true} to enable converstion and ambient.
6188 * @return this object for method chaining
6189 */
6190 public WearableExtender setHintAmbientBigPicture(boolean hintAmbientBigPicture) {
6191 setFlag(FLAG_BIG_PICTURE_AMBIENT, hintAmbientBigPicture);
6192 return this;
6193 }
6194
6195 /**
6196 * Get a hint that this notification's {@link BigPictureStyle} (if present) should be
6197 * converted to low-bit and displayed in ambient mode, especially useful for barcodes and
6198 * qr codes, as well as other simple black-and-white tickets.
6199 * @return {@code true} if it should be displayed in ambient, false otherwise
6200 * otherwise. The default value is {@code false} if this was never set.
6201 */
6202 public boolean getHintAmbientBigPicture() {
6203 return (mFlags & FLAG_BIG_PICTURE_AMBIENT) != 0;
6204 }
6205
6206 /**
Alex Hills9ab3a232016-04-05 14:54:56 -04006207 * Set a hint that this notification's content intent will launch an {@link Activity}
6208 * directly, telling the platform that it can generate the appropriate transitions.
6209 * @param hintContentIntentLaunchesActivity {@code true} if the content intent will launch
6210 * an activity and transitions should be generated, false otherwise.
6211 * @return this object for method chaining
6212 */
6213 public WearableExtender setHintContentIntentLaunchesActivity(
6214 boolean hintContentIntentLaunchesActivity) {
6215 setFlag(FLAG_HINT_CONTENT_INTENT_LAUNCHES_ACTIVITY, hintContentIntentLaunchesActivity);
6216 return this;
6217 }
6218
6219 /**
6220 * Get a hint that this notification's content intent will launch an {@link Activity}
6221 * directly, telling the platform that it can generate the appropriate transitions
6222 * @return {@code true} if the content intent will launch an activity and transitions should
6223 * be generated, false otherwise. The default value is {@code false} if this was never set.
6224 */
6225 public boolean getHintContentIntentLaunchesActivity() {
6226 return (mFlags & FLAG_HINT_CONTENT_INTENT_LAUNCHES_ACTIVITY) != 0;
6227 }
6228
Nadia Benbernou948627e2016-04-14 14:41:08 -04006229 /**
6230 * When you post a notification, if you set the dismissal id field, then when that
6231 * notification is canceled, notifications on other wearables and the paired Android phone
6232 * having that same dismissal id will also be canceled. Note that this only works if you
6233 * have notification bridge mode set to NO_BRIDGING in your Wear app manifest. See
6234 * <a href="{@docRoot}wear/notifications/index.html">Adding Wearable Features to
6235 * Notifications</a> for more information on how to use the bridge mode feature.
6236 * @param dismissalId the dismissal id of the notification.
6237 * @return this object for method chaining
6238 */
6239 public WearableExtender setDismissalId(String dismissalId) {
6240 mDismissalId = dismissalId;
6241 return this;
6242 }
6243
6244 /**
6245 * Returns the dismissal id of the notification.
6246 * @return the dismissal id of the notification or null if it has not been set.
6247 */
6248 public String getDismissalId() {
6249 return mDismissalId;
6250 }
6251
Griff Hazen61a9e862014-05-22 16:05:19 -07006252 private void setFlag(int mask, boolean value) {
6253 if (value) {
6254 mFlags |= mask;
6255 } else {
6256 mFlags &= ~mask;
6257 }
6258 }
6259 }
6260
6261 /**
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08006262 * <p>Helper class to add Android Auto extensions to notifications. To create a notification
6263 * with car extensions:
6264 *
6265 * <ol>
6266 * <li>Create an {@link Notification.Builder}, setting any desired
6267 * properties.
6268 * <li>Create a {@link CarExtender}.
6269 * <li>Set car-specific properties using the {@code add} and {@code set} methods of
6270 * {@link CarExtender}.
6271 * <li>Call {@link Notification.Builder#extend(Notification.Extender)}
6272 * to apply the extensions to a notification.
6273 * </ol>
6274 *
6275 * <pre class="prettyprint">
6276 * Notification notification = new Notification.Builder(context)
6277 * ...
6278 * .extend(new CarExtender()
6279 * .set*(...))
6280 * .build();
6281 * </pre>
6282 *
6283 * <p>Car extensions can be accessed on an existing notification by using the
6284 * {@code CarExtender(Notification)} constructor, and then using the {@code get} methods
6285 * to access values.
6286 */
6287 public static final class CarExtender implements Extender {
6288 private static final String TAG = "CarExtender";
6289
6290 private static final String EXTRA_CAR_EXTENDER = "android.car.EXTENSIONS";
6291 private static final String EXTRA_LARGE_ICON = "large_icon";
6292 private static final String EXTRA_CONVERSATION = "car_conversation";
6293 private static final String EXTRA_COLOR = "app_color";
6294
6295 private Bitmap mLargeIcon;
6296 private UnreadConversation mUnreadConversation;
6297 private int mColor = Notification.COLOR_DEFAULT;
6298
6299 /**
6300 * Create a {@link CarExtender} with default options.
6301 */
6302 public CarExtender() {
6303 }
6304
6305 /**
6306 * Create a {@link CarExtender} from the CarExtender options of an existing Notification.
6307 *
6308 * @param notif The notification from which to copy options.
6309 */
6310 public CarExtender(Notification notif) {
6311 Bundle carBundle = notif.extras == null ?
6312 null : notif.extras.getBundle(EXTRA_CAR_EXTENDER);
6313 if (carBundle != null) {
6314 mLargeIcon = carBundle.getParcelable(EXTRA_LARGE_ICON);
6315 mColor = carBundle.getInt(EXTRA_COLOR, Notification.COLOR_DEFAULT);
6316
6317 Bundle b = carBundle.getBundle(EXTRA_CONVERSATION);
6318 mUnreadConversation = UnreadConversation.getUnreadConversationFromBundle(b);
6319 }
6320 }
6321
6322 /**
6323 * Apply car extensions to a notification that is being built. This is typically called by
6324 * the {@link Notification.Builder#extend(Notification.Extender)}
6325 * method of {@link Notification.Builder}.
6326 */
6327 @Override
6328 public Notification.Builder extend(Notification.Builder builder) {
6329 Bundle carExtensions = new Bundle();
6330
6331 if (mLargeIcon != null) {
6332 carExtensions.putParcelable(EXTRA_LARGE_ICON, mLargeIcon);
6333 }
6334 if (mColor != Notification.COLOR_DEFAULT) {
6335 carExtensions.putInt(EXTRA_COLOR, mColor);
6336 }
6337
6338 if (mUnreadConversation != null) {
6339 Bundle b = mUnreadConversation.getBundleForUnreadConversation();
6340 carExtensions.putBundle(EXTRA_CONVERSATION, b);
6341 }
6342
6343 builder.getExtras().putBundle(EXTRA_CAR_EXTENDER, carExtensions);
6344 return builder;
6345 }
6346
6347 /**
6348 * Sets the accent color to use when Android Auto presents the notification.
6349 *
6350 * Android Auto uses the color set with {@link Notification.Builder#setColor(int)}
6351 * to accent the displayed notification. However, not all colors are acceptable in an
6352 * automotive setting. This method can be used to override the color provided in the
6353 * notification in such a situation.
6354 */
Tor Norbye80756e32015-03-02 09:39:27 -08006355 public CarExtender setColor(@ColorInt int color) {
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08006356 mColor = color;
6357 return this;
6358 }
6359
6360 /**
6361 * Gets the accent color.
6362 *
Julia Reynoldsd9228f12015-10-20 10:37:27 -04006363 * @see #setColor
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08006364 */
Tor Norbye80756e32015-03-02 09:39:27 -08006365 @ColorInt
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08006366 public int getColor() {
6367 return mColor;
6368 }
6369
6370 /**
6371 * Sets the large icon of the car notification.
6372 *
6373 * If no large icon is set in the extender, Android Auto will display the icon
6374 * specified by {@link Notification.Builder#setLargeIcon(android.graphics.Bitmap)}
6375 *
6376 * @param largeIcon The large icon to use in the car notification.
6377 * @return This object for method chaining.
6378 */
6379 public CarExtender setLargeIcon(Bitmap largeIcon) {
6380 mLargeIcon = largeIcon;
6381 return this;
6382 }
6383
6384 /**
6385 * Gets the large icon used in this car notification, or null if no icon has been set.
6386 *
6387 * @return The large icon for the car notification.
6388 * @see CarExtender#setLargeIcon
6389 */
6390 public Bitmap getLargeIcon() {
6391 return mLargeIcon;
6392 }
6393
6394 /**
6395 * Sets the unread conversation in a message notification.
6396 *
6397 * @param unreadConversation The unread part of the conversation this notification conveys.
6398 * @return This object for method chaining.
6399 */
6400 public CarExtender setUnreadConversation(UnreadConversation unreadConversation) {
6401 mUnreadConversation = unreadConversation;
6402 return this;
6403 }
6404
6405 /**
6406 * Returns the unread conversation conveyed by this notification.
6407 * @see #setUnreadConversation(UnreadConversation)
6408 */
6409 public UnreadConversation getUnreadConversation() {
6410 return mUnreadConversation;
6411 }
6412
6413 /**
6414 * A class which holds the unread messages from a conversation.
6415 */
6416 public static class UnreadConversation {
6417 private static final String KEY_AUTHOR = "author";
6418 private static final String KEY_TEXT = "text";
6419 private static final String KEY_MESSAGES = "messages";
6420 private static final String KEY_REMOTE_INPUT = "remote_input";
6421 private static final String KEY_ON_REPLY = "on_reply";
6422 private static final String KEY_ON_READ = "on_read";
6423 private static final String KEY_PARTICIPANTS = "participants";
6424 private static final String KEY_TIMESTAMP = "timestamp";
6425
6426 private final String[] mMessages;
6427 private final RemoteInput mRemoteInput;
6428 private final PendingIntent mReplyPendingIntent;
6429 private final PendingIntent mReadPendingIntent;
6430 private final String[] mParticipants;
6431 private final long mLatestTimestamp;
6432
6433 UnreadConversation(String[] messages, RemoteInput remoteInput,
6434 PendingIntent replyPendingIntent, PendingIntent readPendingIntent,
6435 String[] participants, long latestTimestamp) {
6436 mMessages = messages;
6437 mRemoteInput = remoteInput;
6438 mReadPendingIntent = readPendingIntent;
6439 mReplyPendingIntent = replyPendingIntent;
6440 mParticipants = participants;
6441 mLatestTimestamp = latestTimestamp;
6442 }
6443
6444 /**
6445 * Gets the list of messages conveyed by this notification.
6446 */
6447 public String[] getMessages() {
6448 return mMessages;
6449 }
6450
6451 /**
6452 * Gets the remote input that will be used to convey the response to a message list, or
6453 * null if no such remote input exists.
6454 */
6455 public RemoteInput getRemoteInput() {
6456 return mRemoteInput;
6457 }
6458
6459 /**
6460 * Gets the pending intent that will be triggered when the user replies to this
6461 * notification.
6462 */
6463 public PendingIntent getReplyPendingIntent() {
6464 return mReplyPendingIntent;
6465 }
6466
6467 /**
6468 * Gets the pending intent that Android Auto will send after it reads aloud all messages
6469 * in this object's message list.
6470 */
6471 public PendingIntent getReadPendingIntent() {
6472 return mReadPendingIntent;
6473 }
6474
6475 /**
6476 * Gets the participants in the conversation.
6477 */
6478 public String[] getParticipants() {
6479 return mParticipants;
6480 }
6481
6482 /**
6483 * Gets the firs participant in the conversation.
6484 */
6485 public String getParticipant() {
6486 return mParticipants.length > 0 ? mParticipants[0] : null;
6487 }
6488
6489 /**
6490 * Gets the timestamp of the conversation.
6491 */
6492 public long getLatestTimestamp() {
6493 return mLatestTimestamp;
6494 }
6495
6496 Bundle getBundleForUnreadConversation() {
6497 Bundle b = new Bundle();
6498 String author = null;
6499 if (mParticipants != null && mParticipants.length > 1) {
6500 author = mParticipants[0];
6501 }
6502 Parcelable[] messages = new Parcelable[mMessages.length];
6503 for (int i = 0; i < messages.length; i++) {
6504 Bundle m = new Bundle();
6505 m.putString(KEY_TEXT, mMessages[i]);
6506 m.putString(KEY_AUTHOR, author);
6507 messages[i] = m;
6508 }
6509 b.putParcelableArray(KEY_MESSAGES, messages);
6510 if (mRemoteInput != null) {
6511 b.putParcelable(KEY_REMOTE_INPUT, mRemoteInput);
6512 }
6513 b.putParcelable(KEY_ON_REPLY, mReplyPendingIntent);
6514 b.putParcelable(KEY_ON_READ, mReadPendingIntent);
6515 b.putStringArray(KEY_PARTICIPANTS, mParticipants);
6516 b.putLong(KEY_TIMESTAMP, mLatestTimestamp);
6517 return b;
6518 }
6519
6520 static UnreadConversation getUnreadConversationFromBundle(Bundle b) {
6521 if (b == null) {
6522 return null;
6523 }
6524 Parcelable[] parcelableMessages = b.getParcelableArray(KEY_MESSAGES);
6525 String[] messages = null;
6526 if (parcelableMessages != null) {
6527 String[] tmp = new String[parcelableMessages.length];
6528 boolean success = true;
6529 for (int i = 0; i < tmp.length; i++) {
6530 if (!(parcelableMessages[i] instanceof Bundle)) {
6531 success = false;
6532 break;
6533 }
6534 tmp[i] = ((Bundle) parcelableMessages[i]).getString(KEY_TEXT);
6535 if (tmp[i] == null) {
6536 success = false;
6537 break;
6538 }
6539 }
6540 if (success) {
6541 messages = tmp;
6542 } else {
6543 return null;
6544 }
6545 }
6546
6547 PendingIntent onRead = b.getParcelable(KEY_ON_READ);
6548 PendingIntent onReply = b.getParcelable(KEY_ON_REPLY);
6549
6550 RemoteInput remoteInput = b.getParcelable(KEY_REMOTE_INPUT);
6551
6552 String[] participants = b.getStringArray(KEY_PARTICIPANTS);
6553 if (participants == null || participants.length != 1) {
6554 return null;
6555 }
6556
6557 return new UnreadConversation(messages,
6558 remoteInput,
6559 onReply,
6560 onRead,
6561 participants, b.getLong(KEY_TIMESTAMP));
6562 }
6563 };
6564
6565 /**
6566 * Builder class for {@link CarExtender.UnreadConversation} objects.
6567 */
6568 public static class Builder {
6569 private final List<String> mMessages = new ArrayList<String>();
6570 private final String mParticipant;
6571 private RemoteInput mRemoteInput;
6572 private PendingIntent mReadPendingIntent;
6573 private PendingIntent mReplyPendingIntent;
6574 private long mLatestTimestamp;
6575
6576 /**
6577 * Constructs a new builder for {@link CarExtender.UnreadConversation}.
6578 *
6579 * @param name The name of the other participant in the conversation.
6580 */
6581 public Builder(String name) {
6582 mParticipant = name;
6583 }
6584
6585 /**
6586 * Appends a new unread message to the list of messages for this conversation.
6587 *
6588 * The messages should be added from oldest to newest.
6589 *
6590 * @param message The text of the new unread message.
6591 * @return This object for method chaining.
6592 */
6593 public Builder addMessage(String message) {
6594 mMessages.add(message);
6595 return this;
6596 }
6597
6598 /**
6599 * Sets the pending intent and remote input which will convey the reply to this
6600 * notification.
6601 *
6602 * @param pendingIntent The pending intent which will be triggered on a reply.
6603 * @param remoteInput The remote input parcelable which will carry the reply.
6604 * @return This object for method chaining.
6605 *
6606 * @see CarExtender.UnreadConversation#getRemoteInput
6607 * @see CarExtender.UnreadConversation#getReplyPendingIntent
6608 */
6609 public Builder setReplyAction(
6610 PendingIntent pendingIntent, RemoteInput remoteInput) {
6611 mRemoteInput = remoteInput;
6612 mReplyPendingIntent = pendingIntent;
6613
6614 return this;
6615 }
6616
6617 /**
6618 * Sets the pending intent that will be sent once the messages in this notification
6619 * are read.
6620 *
6621 * @param pendingIntent The pending intent to use.
6622 * @return This object for method chaining.
6623 */
6624 public Builder setReadPendingIntent(PendingIntent pendingIntent) {
6625 mReadPendingIntent = pendingIntent;
6626 return this;
6627 }
6628
6629 /**
6630 * Sets the timestamp of the most recent message in an unread conversation.
6631 *
6632 * If a messaging notification has been posted by your application and has not
6633 * yet been cancelled, posting a later notification with the same id and tag
6634 * but without a newer timestamp may result in Android Auto not displaying a
6635 * heads up notification for the later notification.
6636 *
6637 * @param timestamp The timestamp of the most recent message in the conversation.
6638 * @return This object for method chaining.
6639 */
6640 public Builder setLatestTimestamp(long timestamp) {
6641 mLatestTimestamp = timestamp;
6642 return this;
6643 }
6644
6645 /**
6646 * Builds a new unread conversation object.
6647 *
6648 * @return The new unread conversation object.
6649 */
6650 public UnreadConversation build() {
6651 String[] messages = mMessages.toArray(new String[mMessages.size()]);
6652 String[] participants = { mParticipant };
6653 return new UnreadConversation(messages, mRemoteInput, mReplyPendingIntent,
6654 mReadPendingIntent, participants, mLatestTimestamp);
6655 }
6656 }
6657 }
6658
6659 /**
Griff Hazen61a9e862014-05-22 16:05:19 -07006660 * Get an array of Notification objects from a parcelable array bundle field.
6661 * Update the bundle to have a typed array so fetches in the future don't need
6662 * to do an array copy.
6663 */
6664 private static Notification[] getNotificationArrayFromBundle(Bundle bundle, String key) {
6665 Parcelable[] array = bundle.getParcelableArray(key);
6666 if (array instanceof Notification[] || array == null) {
6667 return (Notification[]) array;
6668 }
6669 Notification[] typedArray = Arrays.copyOf(array, array.length,
6670 Notification[].class);
6671 bundle.putParcelableArray(key, typedArray);
6672 return typedArray;
6673 }
Christoph Studer4600f9b2014-07-22 22:44:43 +02006674
6675 private static class BuilderRemoteViews extends RemoteViews {
6676 public BuilderRemoteViews(Parcel parcel) {
6677 super(parcel);
6678 }
6679
Kenny Guy77320062014-08-27 21:37:15 +01006680 public BuilderRemoteViews(ApplicationInfo appInfo, int layoutId) {
6681 super(appInfo, layoutId);
Christoph Studer4600f9b2014-07-22 22:44:43 +02006682 }
6683
6684 @Override
6685 public BuilderRemoteViews clone() {
6686 Parcel p = Parcel.obtain();
6687 writeToParcel(p, 0);
6688 p.setDataPosition(0);
6689 BuilderRemoteViews brv = new BuilderRemoteViews(p);
6690 p.recycle();
6691 return brv;
6692 }
6693 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006694}