blob: e4d8dec6423ffa48e0d24da79a1a5cf8b973a4c7 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.app;
18
Tor Norbye80756e32015-03-02 09:39:27 -080019import android.annotation.ColorInt;
Tor Norbye7b9c9122013-05-30 16:48:33 -070020import android.annotation.DrawableRes;
Tor Norbyed9273d62013-05-30 15:59:53 -070021import android.annotation.IntDef;
Daniel Sandler01df1c62014-06-09 10:54:01 -040022import android.annotation.SdkConstant;
23import android.annotation.SdkConstant.SdkConstantType;
Julia Reynoldse46bb372016-03-17 11:05:58 -040024import android.annotation.SystemApi;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080025import android.content.Context;
26import android.content.Intent;
Kenny Guy77320062014-08-27 21:37:15 +010027import android.content.pm.ApplicationInfo;
Dan Sandler732bd6c2016-04-12 14:20:32 -040028import android.content.pm.PackageManager;
Christoph Studer4600f9b2014-07-22 22:44:43 +020029import android.content.pm.PackageManager.NameNotFoundException;
Jorim Jaggief72a192014-08-26 21:57:46 +020030import android.content.res.ColorStateList;
Joe Onoratoef1e7762010-09-17 18:38:38 -040031import android.graphics.Bitmap;
Kenny Guy8a0101b2014-05-08 23:34:12 +010032import android.graphics.Canvas;
Adrian Roosc1a80b02016-04-05 14:54:55 -070033import android.graphics.Color;
Jorim Jaggi5c2d8462014-03-21 17:37:00 +010034import android.graphics.PorterDuff;
Kenny Guy8a0101b2014-05-08 23:34:12 +010035import android.graphics.drawable.Drawable;
Dan Sandlerd63f9322015-05-06 15:18:49 -040036import android.graphics.drawable.Icon;
John Spurlockc0650f022014-07-19 13:22:39 -040037import android.media.AudioAttributes;
Jeff Sharkey098d5802012-04-26 17:30:34 -070038import android.media.AudioManager;
Jeff Browndba34ba2014-06-24 20:46:03 -070039import android.media.session.MediaSession;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080040import android.net.Uri;
Daniel Sandlerdcbaf662013-04-26 16:23:09 -040041import android.os.BadParcelableException;
Christoph Studer239f8352014-08-25 15:13:18 +020042import android.os.Build;
Daniel Sandler2561b0b2012-02-13 21:04:12 -050043import android.os.Bundle;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080044import android.os.Parcel;
45import android.os.Parcelable;
Daniel Sandlera2985ed2012-04-03 16:42:00 -040046import android.os.SystemClock;
Jeff Sharkey6d515712012-09-20 16:06:08 -070047import android.os.UserHandle;
Adrian Roosc1a80b02016-04-05 14:54:55 -070048import android.text.BidiFormatter;
Selim Cinek60a54252016-02-26 17:03:25 -080049import android.text.SpannableStringBuilder;
50import android.text.Spanned;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080051import android.text.TextUtils;
Selim Cinek60a54252016-02-26 17:03:25 -080052import android.text.style.AbsoluteSizeSpan;
Selim Cinek89991a22016-03-07 19:51:54 -080053import android.text.style.CharacterStyle;
Selim Cinek60a54252016-02-26 17:03:25 -080054import android.text.style.RelativeSizeSpan;
55import android.text.style.TextAppearanceSpan;
Daniel Sandlerdcbaf662013-04-26 16:23:09 -040056import android.util.Log;
Dan Sandler50128532015-12-08 15:42:41 -050057import android.util.SparseArray;
Daniel Sandler9f7936a2012-05-21 16:14:28 -040058import android.util.TypedValue;
Griff Hazen61a9e862014-05-22 16:05:19 -070059import android.view.Gravity;
Selim Cinekea4bef72015-12-02 15:51:10 -080060import android.view.NotificationHeaderView;
Joe Onorato8595a3d2010-11-19 18:12:07 -080061import android.view.View;
Adrian Roos9b123cf2016-02-04 14:55:57 -080062import android.view.ViewGroup;
Jeff Sharkey1c400132011-08-05 14:50:13 -070063import android.widget.ProgressBar;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080064import android.widget.RemoteViews;
65
Griff Hazen959591e2014-05-15 22:26:18 -070066import com.android.internal.R;
Griff Hazenc091ba82014-05-16 10:13:26 -070067import com.android.internal.util.NotificationColorUtil;
Griff Hazen959591e2014-05-15 22:26:18 -070068
Tor Norbyed9273d62013-05-30 15:59:53 -070069import java.lang.annotation.Retention;
70import java.lang.annotation.RetentionPolicy;
Christoph Studer4600f9b2014-07-22 22:44:43 +020071import java.lang.reflect.Constructor;
Daniel Sandler2561b0b2012-02-13 21:04:12 -050072import java.util.ArrayList;
Griff Hazen61a9e862014-05-22 16:05:19 -070073import java.util.Arrays;
Griff Hazen5cadc3b2014-05-20 09:55:39 -070074import java.util.Collections;
Griff Hazen61a9e862014-05-22 16:05:19 -070075import java.util.List;
Dan Sandler50128532015-12-08 15:42:41 -050076import java.util.Set;
Joe Onorato561d3852010-11-20 18:09:34 -080077
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080078/**
79 * A class that represents how a persistent notification is to be presented to
80 * the user using the {@link android.app.NotificationManager}.
81 *
Joe Onoratocb109a02011-01-18 17:57:41 -080082 * <p>The {@link Notification.Builder Notification.Builder} has been added to make it
83 * easier to construct Notifications.</p>
84 *
Joe Fernandez558459f2011-10-13 16:47:36 -070085 * <div class="special reference">
86 * <h3>Developer Guides</h3>
87 * <p>For a guide to creating notifications, read the
88 * <a href="{@docRoot}guide/topics/ui/notifiers/notifications.html">Status Bar Notifications</a>
89 * developer guide.</p>
90 * </div>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080091 */
92public class Notification implements Parcelable
93{
Daniel Sandlerdcbaf662013-04-26 16:23:09 -040094 private static final String TAG = "Notification";
95
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080096 /**
Daniel Sandler01df1c62014-06-09 10:54:01 -040097 * An activity that provides a user interface for adjusting notification preferences for its
98 * containing application. Optional but recommended for apps that post
99 * {@link android.app.Notification Notifications}.
100 */
101 @SdkConstant(SdkConstantType.INTENT_CATEGORY)
102 public static final String INTENT_CATEGORY_NOTIFICATION_PREFERENCES
103 = "android.intent.category.NOTIFICATION_PREFERENCES";
104
105 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800106 * Use all default values (where applicable).
107 */
108 public static final int DEFAULT_ALL = ~0;
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500109
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800110 /**
111 * Use the default notification sound. This will ignore any given
112 * {@link #sound}.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500113 *
Chris Wren47c20a12014-06-18 17:27:29 -0400114 * <p>
115 * A notification that is noisy is more likely to be presented as a heads-up notification.
116 * </p>
117 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800118 * @see #defaults
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500119 */
120
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800121 public static final int DEFAULT_SOUND = 1;
122
123 /**
124 * Use the default notification vibrate. This will ignore any given
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500125 * {@link #vibrate}. Using phone vibration requires the
Scott Mainb8b36452009-04-26 15:50:49 -0700126 * {@link android.Manifest.permission#VIBRATE VIBRATE} permission.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500127 *
Chris Wren47c20a12014-06-18 17:27:29 -0400128 * <p>
129 * A notification that vibrates is more likely to be presented as a heads-up notification.
130 * </p>
131 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800132 * @see #defaults
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500133 */
134
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800135 public static final int DEFAULT_VIBRATE = 2;
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500136
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800137 /**
138 * Use the default notification lights. This will ignore the
139 * {@link #FLAG_SHOW_LIGHTS} bit, and {@link #ledARGB}, {@link #ledOffMS}, or
140 * {@link #ledOnMS}.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500141 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800142 * @see #defaults
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500143 */
144
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800145 public static final int DEFAULT_LIGHTS = 4;
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500146
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800147 /**
Christoph Studer535ec612014-09-03 15:47:47 +0200148 * Maximum length of CharSequences accepted by Builder and friends.
149 *
150 * <p>
151 * Avoids spamming the system with overly large strings such as full e-mails.
152 */
153 private static final int MAX_CHARSEQUENCE_LENGTH = 5 * 1024;
154
155 /**
Adrian Roose458aa82015-12-08 16:17:19 -0800156 * Maximum entries of reply text that are accepted by Builder and friends.
157 */
158 private static final int MAX_REPLY_HISTORY = 5;
159
160 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500161 * A timestamp related to this notification, in milliseconds since the epoch.
Joe Malin8d40d042012-11-05 11:36:40 -0800162 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500163 * Default value: {@link System#currentTimeMillis() Now}.
164 *
165 * Choose a timestamp that will be most relevant to the user. For most finite events, this
166 * corresponds to the time the event happened (or will happen, in the case of events that have
167 * yet to occur but about which the user is being informed). Indefinite events should be
Joe Malin8d40d042012-11-05 11:36:40 -0800168 * timestamped according to when the activity began.
169 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500170 * Some examples:
Joe Malin8d40d042012-11-05 11:36:40 -0800171 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500172 * <ul>
173 * <li>Notification of a new chat message should be stamped when the message was received.</li>
174 * <li>Notification of an ongoing file download (with a progress bar, for example) should be stamped when the download started.</li>
175 * <li>Notification of a completed file download should be stamped when the download finished.</li>
176 * <li>Notification of an upcoming meeting should be stamped with the time the meeting will begin (that is, in the future).</li>
177 * <li>Notification of an ongoing stopwatch (increasing timer) should be stamped with the watch's start time.
178 * <li>Notification of an ongoing countdown timer should be stamped with the timer's end time.
Joe Malin8d40d042012-11-05 11:36:40 -0800179 * </ul>
180 *
Selim Cinek0ff1ce602016-04-05 18:27:16 -0700181 * For apps targeting {@link android.os.Build.VERSION_CODES#N} and above, this time is not shown
182 * anymore by default and must be opted into by using
183 * {@link android.app.Notification.Builder#setShowWhen(boolean)}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800184 */
185 public long when;
186
187 /**
Selim Cinekb85f36fd2016-04-20 18:46:36 -0700188 * The creation time of the notification
189 */
190 private long creationTime;
191
192 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800193 * The resource id of a drawable to use as the icon in the status bar.
Dan Sandler86647982015-05-13 23:41:13 -0400194 *
195 * @deprecated Use {@link Builder#setSmallIcon(Icon)} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800196 */
Dan Sandler86647982015-05-13 23:41:13 -0400197 @Deprecated
Tor Norbye7b9c9122013-05-30 16:48:33 -0700198 @DrawableRes
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800199 public int icon;
200
201 /**
Joe Onorato46439ce2010-11-19 13:56:21 -0800202 * If the icon in the status bar is to have more than one level, you can set this. Otherwise,
203 * leave it at its default value of 0.
204 *
205 * @see android.widget.ImageView#setImageLevel
Griff Hazen959591e2014-05-15 22:26:18 -0700206 * @see android.graphics.drawable.Drawable#setLevel
Joe Onorato46439ce2010-11-19 13:56:21 -0800207 */
208 public int iconLevel;
209
210 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500211 * The number of events that this notification represents. For example, in a new mail
212 * notification, this could be the number of unread messages.
Joe Malin8d40d042012-11-05 11:36:40 -0800213 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500214 * The system may or may not use this field to modify the appearance of the notification. For
215 * example, before {@link android.os.Build.VERSION_CODES#HONEYCOMB}, this number was
216 * superimposed over the icon in the status bar. Starting with
217 * {@link android.os.Build.VERSION_CODES#HONEYCOMB}, the template used by
218 * {@link Notification.Builder} has displayed the number in the expanded notification view.
Joe Malin8d40d042012-11-05 11:36:40 -0800219 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500220 * If the number is 0 or negative, it is never shown.
Selim Cinek0f9dd1e2016-04-05 17:03:40 -0700221 *
222 * @deprecated this number is not shown anymore
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800223 */
224 public int number;
225
226 /**
227 * The intent to execute when the expanded status entry is clicked. If
228 * this is an activity, it must include the
229 * {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} flag, which requires
Scott Main7aee61f2011-02-08 11:25:01 -0800230 * that you take care of task management as described in the
231 * <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back
Dianne Hackborn6ceca582012-01-10 15:24:26 -0800232 * Stack</a> document. In particular, make sure to read the notification section
233 * <a href="{@docRoot}guide/topics/ui/notifiers/notifications.html#HandlingNotifications">Handling
234 * Notifications</a> for the correct ways to launch an application from a
235 * notification.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800236 */
237 public PendingIntent contentIntent;
238
239 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500240 * The intent to execute when the notification is explicitly dismissed by the user, either with
241 * the "Clear All" button or by swiping it away individually.
242 *
243 * This probably shouldn't be launching an activity since several of those will be sent
244 * at the same time.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800245 */
246 public PendingIntent deleteIntent;
247
248 /**
Dianne Hackborn170bae72010-09-03 15:14:28 -0700249 * An intent to launch instead of posting the notification to the status bar.
Joe Onoratocb109a02011-01-18 17:57:41 -0800250 *
Chris Wren47c20a12014-06-18 17:27:29 -0400251 * <p>
252 * The system UI may choose to display a heads-up notification, instead of
253 * launching this intent, while the user is using the device.
254 * </p>
255 *
Joe Onoratocb109a02011-01-18 17:57:41 -0800256 * @see Notification.Builder#setFullScreenIntent
Daniel Sandlere46cbd32010-06-17 10:35:26 -0400257 */
258 public PendingIntent fullScreenIntent;
259
260 /**
Dan Sandler5fcdf6e2014-07-18 11:31:15 -0400261 * Text that summarizes this notification for accessibility services.
262 *
263 * As of the L release, this text is no longer shown on screen, but it is still useful to
264 * accessibility services (where it serves as an audible announcement of the notification's
265 * appearance).
Joe Onoratoef1e7762010-09-17 18:38:38 -0400266 *
Joe Onorato46439ce2010-11-19 13:56:21 -0800267 * @see #tickerView
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800268 */
269 public CharSequence tickerText;
270
271 /**
Dan Sandler5fcdf6e2014-07-18 11:31:15 -0400272 * Formerly, a view showing the {@link #tickerText}.
273 *
274 * No longer displayed in the status bar as of API 21.
Joe Onoratoef1e7762010-09-17 18:38:38 -0400275 */
Dan Sandler5fcdf6e2014-07-18 11:31:15 -0400276 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -0800277 public RemoteViews tickerView;
Joe Onoratoef1e7762010-09-17 18:38:38 -0400278
279 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400280 * The view that will represent this notification in the notification list (which is pulled
281 * down from the status bar).
282 *
Julia Reynoldsd4ea7412016-02-17 14:00:56 -0500283 * As of N, this field may be null. The notification view is determined by the inputs
284 * to {@link Notification.Builder}; a custom RemoteViews can optionally be
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400285 * supplied with {@link Notification.Builder#setCustomContentView(RemoteViews)}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800286 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400287 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800288 public RemoteViews contentView;
289
Daniel Sandlera0a938c2012-03-15 08:42:37 -0400290 /**
Daniel Sandler4dfbe832012-04-11 14:51:46 -0400291 * A large-format version of {@link #contentView}, giving the Notification an
Daniel Sandlerf3b73432012-03-27 15:01:25 -0400292 * opportunity to show more detail. The system UI may choose to show this
293 * instead of the normal content view at its discretion.
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400294 *
Julia Reynoldsd4ea7412016-02-17 14:00:56 -0500295 * As of N, this field may be null. The expanded notification view is determined by the
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400296 * inputs to {@link Notification.Builder}; a custom RemoteViews can optionally be
297 * supplied with {@link Notification.Builder#setCustomBigContentView(RemoteViews)}.
Daniel Sandlerf3b73432012-03-27 15:01:25 -0400298 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400299 @Deprecated
Daniel Sandlerf3b73432012-03-27 15:01:25 -0400300 public RemoteViews bigContentView;
301
Chris Wren8fd39ec2014-02-27 17:43:26 -0500302
303 /**
Chris Wren47c20a12014-06-18 17:27:29 -0400304 * A medium-format version of {@link #contentView}, providing the Notification an
305 * opportunity to add action buttons to contentView. At its discretion, the system UI may
306 * choose to show this as a heads-up notification, which will pop up so the user can see
307 * it without leaving their current activity.
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400308 *
Julia Reynoldsd4ea7412016-02-17 14:00:56 -0500309 * As of N, this field may be null. The heads-up notification view is determined by the
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400310 * inputs to {@link Notification.Builder}; a custom RemoteViews can optionally be
311 * supplied with {@link Notification.Builder#setCustomHeadsUpContentView(RemoteViews)}.
Chris Wren8fd39ec2014-02-27 17:43:26 -0500312 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400313 @Deprecated
Chris Wren8fd39ec2014-02-27 17:43:26 -0500314 public RemoteViews headsUpContentView;
315
Daniel Sandlerf3b73432012-03-27 15:01:25 -0400316 /**
Dan Sandler86647982015-05-13 23:41:13 -0400317 * A large bitmap to be shown in the notification content area.
318 *
319 * @deprecated Use {@link Builder#setLargeIcon(Icon)} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800320 */
Dan Sandler86647982015-05-13 23:41:13 -0400321 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -0800322 public Bitmap largeIcon;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800323
324 /**
325 * The sound to play.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500326 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800327 * <p>
Chris Wren47c20a12014-06-18 17:27:29 -0400328 * A notification that is noisy is more likely to be presented as a heads-up notification.
329 * </p>
330 *
331 * <p>
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500332 * To play the default notification sound, see {@link #defaults}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800333 * </p>
334 */
335 public Uri sound;
336
337 /**
338 * Use this constant as the value for audioStreamType to request that
339 * the default stream type for notifications be used. Currently the
Jeff Sharkey098d5802012-04-26 17:30:34 -0700340 * default stream type is {@link AudioManager#STREAM_NOTIFICATION}.
John Spurlockc0650f022014-07-19 13:22:39 -0400341 *
342 * @deprecated Use {@link #audioAttributes} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800343 */
Jean-Michel Trivi81f871e2014-08-06 16:32:38 -0700344 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800345 public static final int STREAM_DEFAULT = -1;
346
347 /**
348 * The audio stream type to use when playing the sound.
349 * Should be one of the STREAM_ constants from
350 * {@link android.media.AudioManager}.
John Spurlockc0650f022014-07-19 13:22:39 -0400351 *
352 * @deprecated Use {@link #audioAttributes} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800353 */
Jean-Michel Trivi81f871e2014-08-06 16:32:38 -0700354 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800355 public int audioStreamType = STREAM_DEFAULT;
356
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800357 /**
John Spurlockc0650f022014-07-19 13:22:39 -0400358 * The default value of {@link #audioAttributes}.
359 */
360 public static final AudioAttributes AUDIO_ATTRIBUTES_DEFAULT = new AudioAttributes.Builder()
361 .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
362 .setUsage(AudioAttributes.USAGE_NOTIFICATION)
363 .build();
364
365 /**
366 * The {@link AudioAttributes audio attributes} to use when playing the sound.
367 */
368 public AudioAttributes audioAttributes = AUDIO_ATTRIBUTES_DEFAULT;
369
370 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500371 * The pattern with which to vibrate.
372 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800373 * <p>
374 * To vibrate the default pattern, see {@link #defaults}.
375 * </p>
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500376 *
Chris Wren47c20a12014-06-18 17:27:29 -0400377 * <p>
378 * A notification that vibrates is more likely to be presented as a heads-up notification.
379 * </p>
380 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800381 * @see android.os.Vibrator#vibrate(long[],int)
382 */
383 public long[] vibrate;
384
385 /**
386 * The color of the led. The hardware will do its best approximation.
387 *
388 * @see #FLAG_SHOW_LIGHTS
389 * @see #flags
390 */
Tor Norbye80756e32015-03-02 09:39:27 -0800391 @ColorInt
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800392 public int ledARGB;
393
394 /**
395 * The number of milliseconds for the LED to be on while it's flashing.
396 * The hardware will do its best approximation.
397 *
398 * @see #FLAG_SHOW_LIGHTS
399 * @see #flags
400 */
401 public int ledOnMS;
402
403 /**
404 * The number of milliseconds for the LED to be off while it's flashing.
405 * The hardware will do its best approximation.
406 *
407 * @see #FLAG_SHOW_LIGHTS
408 * @see #flags
409 */
410 public int ledOffMS;
411
412 /**
413 * Specifies which values should be taken from the defaults.
414 * <p>
415 * To set, OR the desired from {@link #DEFAULT_SOUND},
416 * {@link #DEFAULT_VIBRATE}, {@link #DEFAULT_LIGHTS}. For all default
417 * values, use {@link #DEFAULT_ALL}.
418 * </p>
419 */
420 public int defaults;
421
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800422 /**
423 * Bit to be bitwise-ored into the {@link #flags} field that should be
424 * set if you want the LED on for this notification.
425 * <ul>
426 * <li>To turn the LED off, pass 0 in the alpha channel for colorARGB
427 * or 0 for both ledOnMS and ledOffMS.</li>
428 * <li>To turn the LED on, pass 1 for ledOnMS and 0 for ledOffMS.</li>
429 * <li>To flash the LED, pass the number of milliseconds that it should
430 * be on and off to ledOnMS and ledOffMS.</li>
431 * </ul>
432 * <p>
433 * Since hardware varies, you are not guaranteed that any of the values
434 * you pass are honored exactly. Use the system defaults (TODO) if possible
435 * because they will be set to values that work on any given hardware.
436 * <p>
437 * The alpha channel must be set for forward compatibility.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500438 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800439 */
440 public static final int FLAG_SHOW_LIGHTS = 0x00000001;
441
442 /**
443 * Bit to be bitwise-ored into the {@link #flags} field that should be
444 * set if this notification is in reference to something that is ongoing,
445 * like a phone call. It should not be set if this notification is in
446 * reference to something that happened at a particular point in time,
447 * like a missed phone call.
448 */
449 public static final int FLAG_ONGOING_EVENT = 0x00000002;
450
451 /**
452 * Bit to be bitwise-ored into the {@link #flags} field that if set,
Scott Mainb8b36452009-04-26 15:50:49 -0700453 * the audio will be repeated until the notification is
454 * cancelled or the notification window is opened.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800455 */
456 public static final int FLAG_INSISTENT = 0x00000004;
457
458 /**
459 * Bit to be bitwise-ored into the {@link #flags} field that should be
Griff Hazen293977b2014-04-28 08:37:20 -0700460 * set if you would only like the sound, vibrate and ticker to be played
461 * if the notification was not already showing.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800462 */
463 public static final int FLAG_ONLY_ALERT_ONCE = 0x00000008;
464
465 /**
466 * Bit to be bitwise-ored into the {@link #flags} field that should be
467 * set if the notification should be canceled when it is clicked by the
Daniel Sandler8aa9ae62012-12-04 23:31:47 -0500468 * user.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800469 */
470 public static final int FLAG_AUTO_CANCEL = 0x00000010;
471
472 /**
473 * Bit to be bitwise-ored into the {@link #flags} field that should be
474 * set if the notification should not be canceled when the user clicks
475 * the Clear all button.
476 */
477 public static final int FLAG_NO_CLEAR = 0x00000020;
478
Dianne Hackbornd8a43f62009-08-17 23:33:56 -0700479 /**
480 * Bit to be bitwise-ored into the {@link #flags} field that should be
481 * set if this notification represents a currently running service. This
482 * will normally be set for you by {@link Service#startForeground}.
483 */
484 public static final int FLAG_FOREGROUND_SERVICE = 0x00000040;
485
Daniel Sandlere46cbd32010-06-17 10:35:26 -0400486 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500487 * Obsolete flag indicating high-priority notifications; use the priority field instead.
Joe Malin8d40d042012-11-05 11:36:40 -0800488 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500489 * @deprecated Use {@link #priority} with a positive value.
Daniel Sandlere46cbd32010-06-17 10:35:26 -0400490 */
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500491 public static final int FLAG_HIGH_PRIORITY = 0x00000080;
Daniel Sandlere46cbd32010-06-17 10:35:26 -0400492
Griff Hazendfcb0802014-02-11 12:00:00 -0800493 /**
494 * Bit to be bitswise-ored into the {@link #flags} field that should be
495 * set if this notification is relevant to the current device only
496 * and it is not recommended that it bridge to other devices.
497 */
498 public static final int FLAG_LOCAL_ONLY = 0x00000100;
499
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700500 /**
501 * Bit to be bitswise-ored into the {@link #flags} field that should be
502 * set if this notification is the group summary for a group of notifications.
503 * Grouped notifications may display in a cluster or stack on devices which
504 * support such rendering. Requires a group key also be set using {@link Builder#setGroup}.
505 */
506 public static final int FLAG_GROUP_SUMMARY = 0x00000200;
507
Julia Reynoldse46bb372016-03-17 11:05:58 -0400508 /**
509 * Bit to be bitswise-ored into the {@link #flags} field that should be
510 * set if this notification is the group summary for an auto-group of notifications.
511 *
512 * @hide
513 */
514 @SystemApi
515 public static final int FLAG_AUTOGROUP_SUMMARY = 0x00000400;
516
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800517 public int flags;
518
Tor Norbyed9273d62013-05-30 15:59:53 -0700519 /** @hide */
520 @IntDef({PRIORITY_DEFAULT,PRIORITY_LOW,PRIORITY_MIN,PRIORITY_HIGH,PRIORITY_MAX})
521 @Retention(RetentionPolicy.SOURCE)
522 public @interface Priority {}
523
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800524 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500525 * Default notification {@link #priority}. If your application does not prioritize its own
526 * notifications, use this value for all notifications.
527 */
528 public static final int PRIORITY_DEFAULT = 0;
529
530 /**
531 * Lower {@link #priority}, for items that are less important. The UI may choose to show these
532 * items smaller, or at a different position in the list, compared with your app's
533 * {@link #PRIORITY_DEFAULT} items.
534 */
535 public static final int PRIORITY_LOW = -1;
536
537 /**
538 * Lowest {@link #priority}; these items might not be shown to the user except under special
539 * circumstances, such as detailed notification logs.
540 */
541 public static final int PRIORITY_MIN = -2;
542
543 /**
544 * Higher {@link #priority}, for more important notifications or alerts. The UI may choose to
545 * show these items larger, or at a different position in notification lists, compared with
546 * your app's {@link #PRIORITY_DEFAULT} items.
547 */
548 public static final int PRIORITY_HIGH = 1;
549
550 /**
551 * Highest {@link #priority}, for your application's most important items that require the
552 * user's prompt attention or input.
553 */
554 public static final int PRIORITY_MAX = 2;
555
556 /**
557 * Relative priority for this notification.
Joe Malin8d40d042012-11-05 11:36:40 -0800558 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500559 * Priority is an indication of how much of the user's valuable attention should be consumed by
560 * this notification. Low-priority notifications may be hidden from the user in certain
561 * situations, while the user might be interrupted for a higher-priority notification. The
Daniel Sandler6738eee2012-11-16 12:03:32 -0500562 * system will make a determination about how to interpret this priority when presenting
563 * the notification.
Chris Wren47c20a12014-06-18 17:27:29 -0400564 *
565 * <p>
566 * A notification that is at least {@link #PRIORITY_HIGH} is more likely to be presented
567 * as a heads-up notification.
568 * </p>
569 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500570 */
Tor Norbyed9273d62013-05-30 15:59:53 -0700571 @Priority
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500572 public int priority;
Joe Malin8d40d042012-11-05 11:36:40 -0800573
Dan Sandler26e81cf2014-05-06 10:01:27 -0400574 /**
575 * Accent color (an ARGB integer like the constants in {@link android.graphics.Color})
576 * to be applied by the standard Style templates when presenting this notification.
577 *
578 * The current template design constructs a colorful header image by overlaying the
579 * {@link #icon} image (stenciled in white) atop a field of this color. Alpha components are
580 * ignored.
581 */
Tor Norbye80756e32015-03-02 09:39:27 -0800582 @ColorInt
Dan Sandler26e81cf2014-05-06 10:01:27 -0400583 public int color = COLOR_DEFAULT;
584
585 /**
586 * Special value of {@link #color} telling the system not to decorate this notification with
587 * any special color but instead use default colors when presenting this notification.
588 */
Tor Norbye80756e32015-03-02 09:39:27 -0800589 @ColorInt
Dan Sandler26e81cf2014-05-06 10:01:27 -0400590 public static final int COLOR_DEFAULT = 0; // AKA Color.TRANSPARENT
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600591
592 /**
Adrian Roos4ff3b122016-02-01 12:26:13 -0800593 * Special value of {@link #color} used as a place holder for an invalid color.
594 */
595 @ColorInt
596 private static final int COLOR_INVALID = 1;
597
598 /**
Adrian Roosc1a80b02016-04-05 14:54:55 -0700599 * Sphere of visibility of this notification, which affects how and when the SystemUI reveals
600 * the notification's presence and contents in untrusted situations (namely, on the secure
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600601 * lockscreen).
602 *
603 * The default level, {@link #VISIBILITY_PRIVATE}, behaves exactly as notifications have always
604 * done on Android: The notification's {@link #icon} and {@link #tickerText} (if available) are
605 * shown in all situations, but the contents are only available if the device is unlocked for
606 * the appropriate user.
607 *
608 * A more permissive policy can be expressed by {@link #VISIBILITY_PUBLIC}; such a notification
609 * can be read even in an "insecure" context (that is, above a secure lockscreen).
610 * To modify the public version of this notification—for example, to redact some portions—see
611 * {@link Builder#setPublicVersion(Notification)}.
612 *
613 * Finally, a notification can be made {@link #VISIBILITY_SECRET}, which will suppress its icon
614 * and ticker until the user has bypassed the lockscreen.
615 */
616 public int visibility;
617
Griff Hazenfc3922d2014-08-20 11:56:44 -0700618 /**
619 * Notification visibility: Show this notification in its entirety on all lockscreens.
620 *
621 * {@see #visibility}
622 */
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600623 public static final int VISIBILITY_PUBLIC = 1;
Griff Hazenfc3922d2014-08-20 11:56:44 -0700624
625 /**
626 * Notification visibility: Show this notification on all lockscreens, but conceal sensitive or
627 * private information on secure lockscreens.
628 *
629 * {@see #visibility}
630 */
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600631 public static final int VISIBILITY_PRIVATE = 0;
Griff Hazenfc3922d2014-08-20 11:56:44 -0700632
633 /**
634 * Notification visibility: Do not reveal any part of this notification on a secure lockscreen.
635 *
636 * {@see #visibility}
637 */
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600638 public static final int VISIBILITY_SECRET = -1;
639
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500640 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400641 * Notification category: incoming call (voice or video) or similar synchronous communication request.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500642 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400643 public static final String CATEGORY_CALL = "call";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500644
645 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400646 * Notification category: incoming direct message (SMS, instant message, etc.).
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500647 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400648 public static final String CATEGORY_MESSAGE = "msg";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500649
650 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400651 * Notification category: asynchronous bulk message (email).
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500652 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400653 public static final String CATEGORY_EMAIL = "email";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500654
655 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400656 * Notification category: calendar event.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500657 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400658 public static final String CATEGORY_EVENT = "event";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500659
660 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400661 * Notification category: promotion or advertisement.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500662 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400663 public static final String CATEGORY_PROMO = "promo";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500664
665 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400666 * Notification category: alarm or timer.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500667 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400668 public static final String CATEGORY_ALARM = "alarm";
669
670 /**
671 * Notification category: progress of a long-running background operation.
672 */
673 public static final String CATEGORY_PROGRESS = "progress";
674
675 /**
676 * Notification category: social network or sharing update.
677 */
678 public static final String CATEGORY_SOCIAL = "social";
679
680 /**
681 * Notification category: error in background operation or authentication status.
682 */
683 public static final String CATEGORY_ERROR = "err";
684
685 /**
686 * Notification category: media transport control for playback.
687 */
688 public static final String CATEGORY_TRANSPORT = "transport";
689
690 /**
691 * Notification category: system or device status update. Reserved for system use.
692 */
693 public static final String CATEGORY_SYSTEM = "sys";
694
695 /**
696 * Notification category: indication of running background service.
697 */
698 public static final String CATEGORY_SERVICE = "service";
699
700 /**
John Spurlock0a69c8c2014-03-21 13:30:57 -0400701 * Notification category: a specific, timely recommendation for a single thing.
702 * For example, a news app might want to recommend a news story it believes the user will
703 * want to read next.
704 */
705 public static final String CATEGORY_RECOMMENDATION = "recommendation";
706
707 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400708 * Notification category: ongoing information about device or contextual status.
709 */
710 public static final String CATEGORY_STATUS = "status";
711
712 /**
John Spurlock24d3dad2015-04-02 12:24:02 -0400713 * Notification category: user-scheduled reminder.
714 */
715 public static final String CATEGORY_REMINDER = "reminder";
716
717 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400718 * One of the predefined notification categories (see the <code>CATEGORY_*</code> constants)
719 * that best describes this Notification. May be used by the system for ranking and filtering.
720 */
721 public String category;
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500722
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700723 private String mGroupKey;
724
725 /**
726 * Get the key used to group this notification into a cluster or stack
727 * with other notifications on devices which support such rendering.
728 */
729 public String getGroup() {
730 return mGroupKey;
731 }
732
733 private String mSortKey;
734
735 /**
736 * Get a sort key that orders this notification among other notifications from the
737 * same package. This can be useful if an external sort was already applied and an app
738 * would like to preserve this. Notifications will be sorted lexicographically using this
739 * value, although providing different priorities in addition to providing sort key may
740 * cause this value to be ignored.
741 *
742 * <p>This sort key can also be used to order members of a notification group. See
743 * {@link Builder#setGroup}.
744 *
745 * @see String#compareTo(String)
746 */
747 public String getSortKey() {
748 return mSortKey;
749 }
750
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500751 /**
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400752 * Additional semantic data to be carried around with this Notification.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400753 * <p>
754 * The extras keys defined here are intended to capture the original inputs to {@link Builder}
755 * APIs, and are intended to be used by
756 * {@link android.service.notification.NotificationListenerService} implementations to extract
757 * detailed information from notification objects.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500758 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400759 public Bundle extras = new Bundle();
760
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400761 /**
762 * {@link #extras} key: this is the title of the notification,
763 * as supplied to {@link Builder#setContentTitle(CharSequence)}.
764 */
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -0500765 public static final String EXTRA_TITLE = "android.title";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400766
767 /**
768 * {@link #extras} key: this is the title of the notification when shown in expanded form,
769 * e.g. as supplied to {@link BigTextStyle#setBigContentTitle(CharSequence)}.
770 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400771 public static final String EXTRA_TITLE_BIG = EXTRA_TITLE + ".big";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400772
773 /**
774 * {@link #extras} key: this is the main text payload, as supplied to
775 * {@link Builder#setContentText(CharSequence)}.
776 */
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -0500777 public static final String EXTRA_TEXT = "android.text";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400778
779 /**
780 * {@link #extras} key: this is a third line of text, as supplied to
781 * {@link Builder#setSubText(CharSequence)}.
782 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400783 public static final String EXTRA_SUB_TEXT = "android.subText";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400784
785 /**
Adrian Roose458aa82015-12-08 16:17:19 -0800786 * {@link #extras} key: this is the remote input history, as supplied to
787 * {@link Builder#setRemoteInputHistory(CharSequence[])}.
Adrian Roos005e7742016-04-13 15:02:20 -0700788 *
789 * Apps can fill this through {@link Builder#setRemoteInputHistory(CharSequence[])}
790 * with the most recent inputs that have been sent through a {@link RemoteInput} of this
791 * Notification and are expected to clear it once the it is no longer relevant (e.g. for chat
792 * notifications once the other party has responded).
793 *
794 * The extra with this key is of type CharSequence[] and contains the most recent entry at
795 * the 0 index, the second most recent at the 1 index, etc.
796 *
797 * @see Builder#setRemoteInputHistory(CharSequence[])
Adrian Roose458aa82015-12-08 16:17:19 -0800798 */
799 public static final String EXTRA_REMOTE_INPUT_HISTORY = "android.remoteInputHistory";
800
801 /**
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400802 * {@link #extras} key: this is a small piece of additional text as supplied to
803 * {@link Builder#setContentInfo(CharSequence)}.
804 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400805 public static final String EXTRA_INFO_TEXT = "android.infoText";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400806
807 /**
808 * {@link #extras} key: this is a line of summary information intended to be shown
809 * alongside expanded notifications, as supplied to (e.g.)
810 * {@link BigTextStyle#setSummaryText(CharSequence)}.
811 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400812 public static final String EXTRA_SUMMARY_TEXT = "android.summaryText";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400813
814 /**
Christoph Studer4600f9b2014-07-22 22:44:43 +0200815 * {@link #extras} key: this is the longer text shown in the big form of a
816 * {@link BigTextStyle} notification, as supplied to
817 * {@link BigTextStyle#bigText(CharSequence)}.
818 */
819 public static final String EXTRA_BIG_TEXT = "android.bigText";
820
821 /**
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400822 * {@link #extras} key: this is the resource ID of the notification's main small icon, as
823 * supplied to {@link Builder#setSmallIcon(int)}.
824 */
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -0500825 public static final String EXTRA_SMALL_ICON = "android.icon";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400826
827 /**
828 * {@link #extras} key: this is a bitmap to be used instead of the small icon when showing the
829 * notification payload, as
830 * supplied to {@link Builder#setLargeIcon(android.graphics.Bitmap)}.
831 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400832 public static final String EXTRA_LARGE_ICON = "android.largeIcon";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400833
834 /**
835 * {@link #extras} key: this is a bitmap to be used instead of the one from
836 * {@link Builder#setLargeIcon(android.graphics.Bitmap)} when the notification is
837 * shown in its expanded form, as supplied to
838 * {@link BigPictureStyle#bigLargeIcon(android.graphics.Bitmap)}.
839 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400840 public static final String EXTRA_LARGE_ICON_BIG = EXTRA_LARGE_ICON + ".big";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400841
842 /**
843 * {@link #extras} key: this is the progress value supplied to
844 * {@link Builder#setProgress(int, int, boolean)}.
845 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400846 public static final String EXTRA_PROGRESS = "android.progress";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400847
848 /**
849 * {@link #extras} key: this is the maximum value supplied to
850 * {@link Builder#setProgress(int, int, boolean)}.
851 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400852 public static final String EXTRA_PROGRESS_MAX = "android.progressMax";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400853
854 /**
855 * {@link #extras} key: whether the progress bar is indeterminate, supplied to
856 * {@link Builder#setProgress(int, int, boolean)}.
857 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400858 public static final String EXTRA_PROGRESS_INDETERMINATE = "android.progressIndeterminate";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400859
860 /**
861 * {@link #extras} key: whether {@link #when} should be shown as a count-up timer (specifically
862 * a {@link android.widget.Chronometer}) instead of a timestamp, as supplied to
863 * {@link Builder#setUsesChronometer(boolean)}.
864 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400865 public static final String EXTRA_SHOW_CHRONOMETER = "android.showChronometer";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400866
867 /**
Selim Cinek81c23aa2016-02-25 16:23:13 -0800868 * {@link #extras} key: whether the chronometer set on the notification should count down
869 * instead of counting up. Is only relevant if key {@link #EXTRA_SHOW_CHRONOMETER} is present.
870 */
871 public static final String EXTRA_CHRONOMETER_COUNTS_DOWN = "android.chronometerCountsDown";
872
873 /**
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400874 * {@link #extras} key: whether {@link #when} should be shown,
875 * as supplied to {@link Builder#setShowWhen(boolean)}.
876 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400877 public static final String EXTRA_SHOW_WHEN = "android.showWhen";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400878
879 /**
880 * {@link #extras} key: this is a bitmap to be shown in {@link BigPictureStyle} expanded
881 * notifications, supplied to {@link BigPictureStyle#bigPicture(android.graphics.Bitmap)}.
882 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400883 public static final String EXTRA_PICTURE = "android.picture";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400884
885 /**
886 * {@link #extras} key: An array of CharSequences to show in {@link InboxStyle} expanded
887 * notifications, each of which was supplied to {@link InboxStyle#addLine(CharSequence)}.
888 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400889 public static final String EXTRA_TEXT_LINES = "android.textLines";
Dan Sandler842dd772014-05-15 09:36:47 -0400890
891 /**
892 * {@link #extras} key: A string representing the name of the specific
893 * {@link android.app.Notification.Style} used to create this notification.
894 */
Chris Wren91ad5632013-06-05 15:05:57 -0400895 public static final String EXTRA_TEMPLATE = "android.template";
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400896
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400897 /**
Chris Wrene6c48932014-09-29 17:19:27 -0400898 * {@link #extras} key: A String array containing the people that this notification relates to,
899 * each of which was supplied to {@link Builder#addPerson(String)}.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400900 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400901 public static final String EXTRA_PEOPLE = "android.people";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500902
903 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400904 * Allow certain system-generated notifications to appear before the device is provisioned.
905 * Only available to notifications coming from the android package.
906 * @hide
907 */
908 public static final String EXTRA_ALLOW_DURING_SETUP = "android.allowDuringSetup";
909
910 /**
Jose Limae9e3b3b2014-05-18 23:44:50 -0700911 * {@link #extras} key: A
912 * {@link android.content.ContentUris content URI} pointing to an image that can be displayed
913 * in the background when the notification is selected. The URI must point to an image stream
914 * suitable for passing into
915 * {@link android.graphics.BitmapFactory#decodeStream(java.io.InputStream)
916 * BitmapFactory.decodeStream}; all other content types will be ignored. The content provider
917 * URI used for this purpose must require no permissions to read the image data.
918 */
919 public static final String EXTRA_BACKGROUND_IMAGE_URI = "android.backgroundImageUri";
920
921 /**
Dan Sandler842dd772014-05-15 09:36:47 -0400922 * {@link #extras} key: A
Jeff Browndba34ba2014-06-24 20:46:03 -0700923 * {@link android.media.session.MediaSession.Token} associated with a
Dan Sandler842dd772014-05-15 09:36:47 -0400924 * {@link android.app.Notification.MediaStyle} notification.
925 */
926 public static final String EXTRA_MEDIA_SESSION = "android.mediaSession";
927
928 /**
Bryan Mawhinneye191f902014-07-22 12:50:09 +0100929 * {@link #extras} key: the indices of actions to be shown in the compact view,
930 * as supplied to (e.g.) {@link MediaStyle#setShowActionsInCompactView(int...)}.
931 */
932 public static final String EXTRA_COMPACT_ACTIONS = "android.compactActions";
933
Christoph Studer943aa672014-08-03 20:31:16 +0200934 /**
Alex Hillsfc737de2016-03-23 17:33:02 -0400935 * {@link #extras} key: the username to be displayed for all messages sent by the user including
936 * direct replies
937 * {@link android.app.Notification.MessagingStyle} notification.
938 */
939 public static final String EXTRA_SELF_DISPLAY_NAME = "android.selfDisplayName";
940
941 /**
942 * {@link #extras} key: a boolean describing whether the platform should automatically
943 * generate possible replies to
944 * {@link android.app.Notification.MessagingStyle.Message} objects provided by a
945 * {@link android.app.Notification.MessagingStyle} notification.
946 */
947 public static final String EXTRA_ALLOW_GENERATED_REPLIES = "android.allowGeneratedReplies";
948
949 /**
Alex Hillsd9b04d92016-04-11 16:38:16 -0400950 * {@link #extras} key: a {@link String} to be displayed as the title to a conversation
951 * represented by a {@link android.app.Notification.MessagingStyle}
Alex Hillsfc737de2016-03-23 17:33:02 -0400952 */
Alex Hillsd9b04d92016-04-11 16:38:16 -0400953 public static final String EXTRA_CONVERSATION_TITLE = "android.conversationTitle";
Alex Hillsfc737de2016-03-23 17:33:02 -0400954
955 /**
956 * {@link #extras} key: an array of {@link android.app.Notification.MessagingStyle.Message}
957 * bundles provided by a
958 * {@link android.app.Notification.MessagingStyle} notification.
959 */
960 public static final String EXTRA_MESSAGES = "android.messages";
961
962 /**
Kenny Guy8942bcd2014-09-08 21:09:47 +0100963 * {@link #extras} key: the user that built the notification.
964 *
965 * @hide
966 */
967 public static final String EXTRA_ORIGINATING_USERID = "android.originatingUserId";
968
969 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400970 * @hide
971 */
972 public static final String EXTRA_BUILDER_APPLICATION_INFO = "android.appInfo";
973
Selim Cinek247fa012016-02-18 09:50:48 -0800974 /**
975 * @hide
976 */
977 public static final String EXTRA_CONTAINS_CUSTOM_VIEW = "android.contains.customView";
978
Dan Sandler80eaa592016-04-14 23:34:54 -0400979 /** @hide */
980 @SystemApi
Dan Sandler732bd6c2016-04-12 14:20:32 -0400981 public static final String EXTRA_SUBSTITUTE_APP_NAME = "android.substName";
982
Dan Sandlerd63f9322015-05-06 15:18:49 -0400983 private Icon mSmallIcon;
984 private Icon mLargeIcon;
985
Chris Wren51c75102013-07-16 20:49:17 -0400986 /**
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400987 * Structure to encapsulate a named action that can be shown as part of this notification.
988 * It must include an icon, a label, and a {@link PendingIntent} to be fired when the action is
989 * selected by the user.
990 * <p>
Griff Hazen959591e2014-05-15 22:26:18 -0700991 * Apps should use {@link Notification.Builder#addAction(int, CharSequence, PendingIntent)}
992 * or {@link Notification.Builder#addAction(Notification.Action)}
993 * to attach actions.
Daniel Sandlera0a938c2012-03-15 08:42:37 -0400994 */
Daniel Sandlerea2a3172013-02-20 22:24:20 -0500995 public static class Action implements Parcelable {
Griff Hazen959591e2014-05-15 22:26:18 -0700996 private final Bundle mExtras;
Dan Sandler86647982015-05-13 23:41:13 -0400997 private Icon mIcon;
Griff Hazen61a9e862014-05-22 16:05:19 -0700998 private final RemoteInput[] mRemoteInputs;
Griff Hazen959591e2014-05-15 22:26:18 -0700999
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001000 /**
1001 * Small icon representing the action.
Dan Sandler86647982015-05-13 23:41:13 -04001002 *
1003 * @deprecated Use {@link Action#getIcon()} instead.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001004 */
Dan Sandler86647982015-05-13 23:41:13 -04001005 @Deprecated
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001006 public int icon;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001007
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001008 /**
1009 * Title of the action.
1010 */
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001011 public CharSequence title;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001012
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001013 /**
1014 * Intent to send when the user invokes this action. May be null, in which case the action
1015 * may be rendered in a disabled presentation by the system UI.
1016 */
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001017 public PendingIntent actionIntent;
Griff Hazen959591e2014-05-15 22:26:18 -07001018
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001019 private Action(Parcel in) {
Dan Sandler86647982015-05-13 23:41:13 -04001020 if (in.readInt() != 0) {
1021 mIcon = Icon.CREATOR.createFromParcel(in);
Dan Sandler68079d52015-07-22 10:45:30 -04001022 if (mIcon.getType() == Icon.TYPE_RESOURCE) {
1023 icon = mIcon.getResId();
1024 }
Dan Sandler86647982015-05-13 23:41:13 -04001025 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001026 title = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
1027 if (in.readInt() == 1) {
1028 actionIntent = PendingIntent.CREATOR.createFromParcel(in);
1029 }
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001030 mExtras = Bundle.setDefusable(in.readBundle(), true);
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001031 mRemoteInputs = in.createTypedArray(RemoteInput.CREATOR);
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001032 }
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001033
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001034 /**
Dan Sandler86647982015-05-13 23:41:13 -04001035 * @deprecated Use {@link android.app.Notification.Action.Builder}.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001036 */
Dan Sandler86647982015-05-13 23:41:13 -04001037 @Deprecated
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001038 public Action(int icon, CharSequence title, PendingIntent intent) {
Dan Sandler86647982015-05-13 23:41:13 -04001039 this(Icon.createWithResource("", icon), title, intent, new Bundle(), null);
Griff Hazen959591e2014-05-15 22:26:18 -07001040 }
1041
Dan Sandler86647982015-05-13 23:41:13 -04001042 private Action(Icon icon, CharSequence title, PendingIntent intent, Bundle extras,
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001043 RemoteInput[] remoteInputs) {
Dan Sandler86647982015-05-13 23:41:13 -04001044 this.mIcon = icon;
Gus Prevasf5bff46f2015-08-24 10:34:28 -04001045 if (icon != null && icon.getType() == Icon.TYPE_RESOURCE) {
1046 this.icon = icon.getResId();
1047 }
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001048 this.title = title;
1049 this.actionIntent = intent;
Griff Hazen959591e2014-05-15 22:26:18 -07001050 this.mExtras = extras != null ? extras : new Bundle();
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001051 this.mRemoteInputs = remoteInputs;
Griff Hazen959591e2014-05-15 22:26:18 -07001052 }
1053
1054 /**
Dan Sandler86647982015-05-13 23:41:13 -04001055 * Return an icon representing the action.
1056 */
1057 public Icon getIcon() {
1058 if (mIcon == null && icon != 0) {
1059 // you snuck an icon in here without using the builder; let's try to keep it
1060 mIcon = Icon.createWithResource("", icon);
1061 }
1062 return mIcon;
1063 }
1064
1065 /**
Griff Hazen959591e2014-05-15 22:26:18 -07001066 * Get additional metadata carried around with this Action.
1067 */
1068 public Bundle getExtras() {
1069 return mExtras;
1070 }
1071
1072 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001073 * Get the list of inputs to be collected from the user when this action is sent.
1074 * May return null if no remote inputs were added.
1075 */
1076 public RemoteInput[] getRemoteInputs() {
1077 return mRemoteInputs;
1078 }
1079
1080 /**
Griff Hazen959591e2014-05-15 22:26:18 -07001081 * Builder class for {@link Action} objects.
1082 */
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001083 public static final class Builder {
Dan Sandler86647982015-05-13 23:41:13 -04001084 private final Icon mIcon;
Griff Hazen959591e2014-05-15 22:26:18 -07001085 private final CharSequence mTitle;
1086 private final PendingIntent mIntent;
1087 private final Bundle mExtras;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001088 private ArrayList<RemoteInput> mRemoteInputs;
Griff Hazen959591e2014-05-15 22:26:18 -07001089
1090 /**
1091 * Construct a new builder for {@link Action} object.
1092 * @param icon icon to show for this action
1093 * @param title the title of the action
1094 * @param intent the {@link PendingIntent} to fire when users trigger this action
1095 */
Dan Sandler86647982015-05-13 23:41:13 -04001096 @Deprecated
Griff Hazen959591e2014-05-15 22:26:18 -07001097 public Builder(int icon, CharSequence title, PendingIntent intent) {
Dan Sandler86647982015-05-13 23:41:13 -04001098 this(Icon.createWithResource("", icon), title, intent, new Bundle(), null);
1099 }
1100
1101 /**
1102 * Construct a new builder for {@link Action} object.
1103 * @param icon icon to show for this action
1104 * @param title the title of the action
1105 * @param intent the {@link PendingIntent} to fire when users trigger this action
1106 */
1107 public Builder(Icon icon, CharSequence title, PendingIntent intent) {
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001108 this(icon, title, intent, new Bundle(), null);
Griff Hazen959591e2014-05-15 22:26:18 -07001109 }
1110
1111 /**
1112 * Construct a new builder for {@link Action} object using the fields from an
1113 * {@link Action}.
1114 * @param action the action to read fields from.
1115 */
1116 public Builder(Action action) {
Dan Sandler86647982015-05-13 23:41:13 -04001117 this(action.getIcon(), action.title, action.actionIntent, new Bundle(action.mExtras),
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001118 action.getRemoteInputs());
Griff Hazen959591e2014-05-15 22:26:18 -07001119 }
1120
Dan Sandler86647982015-05-13 23:41:13 -04001121 private Builder(Icon icon, CharSequence title, PendingIntent intent, Bundle extras,
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001122 RemoteInput[] remoteInputs) {
Griff Hazen959591e2014-05-15 22:26:18 -07001123 mIcon = icon;
1124 mTitle = title;
1125 mIntent = intent;
1126 mExtras = extras;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001127 if (remoteInputs != null) {
1128 mRemoteInputs = new ArrayList<RemoteInput>(remoteInputs.length);
1129 Collections.addAll(mRemoteInputs, remoteInputs);
1130 }
Griff Hazen959591e2014-05-15 22:26:18 -07001131 }
1132
1133 /**
1134 * Merge additional metadata into this builder.
1135 *
1136 * <p>Values within the Bundle will replace existing extras values in this Builder.
1137 *
1138 * @see Notification.Action#extras
1139 */
1140 public Builder addExtras(Bundle extras) {
1141 if (extras != null) {
1142 mExtras.putAll(extras);
1143 }
1144 return this;
1145 }
1146
1147 /**
1148 * Get the metadata Bundle used by this Builder.
1149 *
1150 * <p>The returned Bundle is shared with this Builder.
1151 */
1152 public Bundle getExtras() {
1153 return mExtras;
1154 }
1155
1156 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001157 * Add an input to be collected from the user when this action is sent.
1158 * Response values can be retrieved from the fired intent by using the
1159 * {@link RemoteInput#getResultsFromIntent} function.
1160 * @param remoteInput a {@link RemoteInput} to add to the action
1161 * @return this object for method chaining
1162 */
1163 public Builder addRemoteInput(RemoteInput remoteInput) {
1164 if (mRemoteInputs == null) {
1165 mRemoteInputs = new ArrayList<RemoteInput>();
1166 }
1167 mRemoteInputs.add(remoteInput);
1168 return this;
1169 }
1170
1171 /**
1172 * Apply an extender to this action builder. Extenders may be used to add
1173 * metadata or change options on this builder.
1174 */
Griff Hazen61a9e862014-05-22 16:05:19 -07001175 public Builder extend(Extender extender) {
1176 extender.extend(this);
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001177 return this;
1178 }
1179
1180 /**
Griff Hazen959591e2014-05-15 22:26:18 -07001181 * Combine all of the options that have been set and return a new {@link Action}
1182 * object.
1183 * @return the built action
1184 */
1185 public Action build() {
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001186 RemoteInput[] remoteInputs = mRemoteInputs != null
1187 ? mRemoteInputs.toArray(new RemoteInput[mRemoteInputs.size()]) : null;
1188 return new Action(mIcon, mTitle, mIntent, mExtras, remoteInputs);
Griff Hazen959591e2014-05-15 22:26:18 -07001189 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001190 }
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001191
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001192 @Override
1193 public Action clone() {
1194 return new Action(
Dan Sandler86647982015-05-13 23:41:13 -04001195 getIcon(),
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001196 title,
1197 actionIntent, // safe to alias
1198 new Bundle(mExtras),
1199 getRemoteInputs());
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001200 }
1201 @Override
1202 public int describeContents() {
1203 return 0;
1204 }
1205 @Override
1206 public void writeToParcel(Parcel out, int flags) {
Dan Sandler86647982015-05-13 23:41:13 -04001207 final Icon ic = getIcon();
1208 if (ic != null) {
1209 out.writeInt(1);
1210 ic.writeToParcel(out, 0);
1211 } else {
1212 out.writeInt(0);
1213 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001214 TextUtils.writeToParcel(title, out, flags);
1215 if (actionIntent != null) {
1216 out.writeInt(1);
1217 actionIntent.writeToParcel(out, flags);
1218 } else {
1219 out.writeInt(0);
1220 }
Griff Hazen959591e2014-05-15 22:26:18 -07001221 out.writeBundle(mExtras);
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001222 out.writeTypedArray(mRemoteInputs, flags);
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001223 }
Griff Hazen959591e2014-05-15 22:26:18 -07001224 public static final Parcelable.Creator<Action> CREATOR =
1225 new Parcelable.Creator<Action>() {
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001226 public Action createFromParcel(Parcel in) {
1227 return new Action(in);
1228 }
1229 public Action[] newArray(int size) {
1230 return new Action[size];
1231 }
1232 };
Griff Hazen61a9e862014-05-22 16:05:19 -07001233
1234 /**
1235 * Extender interface for use with {@link Builder#extend}. Extenders may be used to add
1236 * metadata or change options on an action builder.
1237 */
1238 public interface Extender {
1239 /**
1240 * Apply this extender to a notification action builder.
1241 * @param builder the builder to be modified.
1242 * @return the build object for chaining.
1243 */
1244 public Builder extend(Builder builder);
1245 }
1246
1247 /**
1248 * Wearable extender for notification actions. To add extensions to an action,
1249 * create a new {@link android.app.Notification.Action.WearableExtender} object using
1250 * the {@code WearableExtender()} constructor and apply it to a
1251 * {@link android.app.Notification.Action.Builder} using
1252 * {@link android.app.Notification.Action.Builder#extend}.
1253 *
1254 * <pre class="prettyprint">
1255 * Notification.Action action = new Notification.Action.Builder(
1256 * R.drawable.archive_all, "Archive all", actionIntent)
Griff Hazen14f57992014-05-26 09:07:14 -07001257 * .extend(new Notification.Action.WearableExtender()
Griff Hazen61a9e862014-05-22 16:05:19 -07001258 * .setAvailableOffline(false))
Griff Hazen14f57992014-05-26 09:07:14 -07001259 * .build();</pre>
Griff Hazen61a9e862014-05-22 16:05:19 -07001260 */
1261 public static final class WearableExtender implements Extender {
1262 /** Notification action extra which contains wearable extensions */
1263 private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS";
1264
Pete Gastaf6781d2014-10-07 15:17:05 -04001265 // Keys within EXTRA_WEARABLE_EXTENSIONS for wearable options.
Griff Hazen61a9e862014-05-22 16:05:19 -07001266 private static final String KEY_FLAGS = "flags";
Pete Gastaf6781d2014-10-07 15:17:05 -04001267 private static final String KEY_IN_PROGRESS_LABEL = "inProgressLabel";
1268 private static final String KEY_CONFIRM_LABEL = "confirmLabel";
1269 private static final String KEY_CANCEL_LABEL = "cancelLabel";
Griff Hazen61a9e862014-05-22 16:05:19 -07001270
1271 // Flags bitwise-ored to mFlags
1272 private static final int FLAG_AVAILABLE_OFFLINE = 0x1;
Alex Hills9ab3a232016-04-05 14:54:56 -04001273 private static final int FLAG_HINT_LAUNCHES_ACTIVITY = 1 << 1;
Griff Hazen61a9e862014-05-22 16:05:19 -07001274
1275 // Default value for flags integer
1276 private static final int DEFAULT_FLAGS = FLAG_AVAILABLE_OFFLINE;
1277
1278 private int mFlags = DEFAULT_FLAGS;
1279
Pete Gastaf6781d2014-10-07 15:17:05 -04001280 private CharSequence mInProgressLabel;
1281 private CharSequence mConfirmLabel;
1282 private CharSequence mCancelLabel;
1283
Griff Hazen61a9e862014-05-22 16:05:19 -07001284 /**
1285 * Create a {@link android.app.Notification.Action.WearableExtender} with default
1286 * options.
1287 */
1288 public WearableExtender() {
1289 }
1290
1291 /**
1292 * Create a {@link android.app.Notification.Action.WearableExtender} by reading
1293 * wearable options present in an existing notification action.
1294 * @param action the notification action to inspect.
1295 */
1296 public WearableExtender(Action action) {
1297 Bundle wearableBundle = action.getExtras().getBundle(EXTRA_WEARABLE_EXTENSIONS);
1298 if (wearableBundle != null) {
1299 mFlags = wearableBundle.getInt(KEY_FLAGS, DEFAULT_FLAGS);
Pete Gastaf6781d2014-10-07 15:17:05 -04001300 mInProgressLabel = wearableBundle.getCharSequence(KEY_IN_PROGRESS_LABEL);
1301 mConfirmLabel = wearableBundle.getCharSequence(KEY_CONFIRM_LABEL);
1302 mCancelLabel = wearableBundle.getCharSequence(KEY_CANCEL_LABEL);
Griff Hazen61a9e862014-05-22 16:05:19 -07001303 }
1304 }
1305
1306 /**
1307 * Apply wearable extensions to a notification action that is being built. This is
1308 * typically called by the {@link android.app.Notification.Action.Builder#extend}
1309 * method of {@link android.app.Notification.Action.Builder}.
1310 */
1311 @Override
1312 public Action.Builder extend(Action.Builder builder) {
1313 Bundle wearableBundle = new Bundle();
1314
1315 if (mFlags != DEFAULT_FLAGS) {
1316 wearableBundle.putInt(KEY_FLAGS, mFlags);
1317 }
Pete Gastaf6781d2014-10-07 15:17:05 -04001318 if (mInProgressLabel != null) {
1319 wearableBundle.putCharSequence(KEY_IN_PROGRESS_LABEL, mInProgressLabel);
1320 }
1321 if (mConfirmLabel != null) {
1322 wearableBundle.putCharSequence(KEY_CONFIRM_LABEL, mConfirmLabel);
1323 }
1324 if (mCancelLabel != null) {
1325 wearableBundle.putCharSequence(KEY_CANCEL_LABEL, mCancelLabel);
1326 }
Griff Hazen61a9e862014-05-22 16:05:19 -07001327
1328 builder.getExtras().putBundle(EXTRA_WEARABLE_EXTENSIONS, wearableBundle);
1329 return builder;
1330 }
1331
1332 @Override
1333 public WearableExtender clone() {
1334 WearableExtender that = new WearableExtender();
1335 that.mFlags = this.mFlags;
Pete Gastaf6781d2014-10-07 15:17:05 -04001336 that.mInProgressLabel = this.mInProgressLabel;
1337 that.mConfirmLabel = this.mConfirmLabel;
1338 that.mCancelLabel = this.mCancelLabel;
Griff Hazen61a9e862014-05-22 16:05:19 -07001339 return that;
1340 }
1341
1342 /**
1343 * Set whether this action is available when the wearable device is not connected to
1344 * a companion device. The user can still trigger this action when the wearable device is
1345 * offline, but a visual hint will indicate that the action may not be available.
1346 * Defaults to true.
1347 */
1348 public WearableExtender setAvailableOffline(boolean availableOffline) {
1349 setFlag(FLAG_AVAILABLE_OFFLINE, availableOffline);
1350 return this;
1351 }
1352
1353 /**
1354 * Get whether this action is available when the wearable device is not connected to
1355 * a companion device. The user can still trigger this action when the wearable device is
1356 * offline, but a visual hint will indicate that the action may not be available.
1357 * Defaults to true.
1358 */
1359 public boolean isAvailableOffline() {
1360 return (mFlags & FLAG_AVAILABLE_OFFLINE) != 0;
1361 }
1362
1363 private void setFlag(int mask, boolean value) {
1364 if (value) {
1365 mFlags |= mask;
1366 } else {
1367 mFlags &= ~mask;
1368 }
1369 }
Pete Gastaf6781d2014-10-07 15:17:05 -04001370
1371 /**
1372 * Set a label to display while the wearable is preparing to automatically execute the
1373 * action. This is usually a 'ing' verb ending in ellipsis like "Sending..."
1374 *
1375 * @param label the label to display while the action is being prepared to execute
1376 * @return this object for method chaining
1377 */
1378 public WearableExtender setInProgressLabel(CharSequence label) {
1379 mInProgressLabel = label;
1380 return this;
1381 }
1382
1383 /**
1384 * Get the label to display while the wearable is preparing to automatically execute
1385 * the action. This is usually a 'ing' verb ending in ellipsis like "Sending..."
1386 *
1387 * @return the label to display while the action is being prepared to execute
1388 */
1389 public CharSequence getInProgressLabel() {
1390 return mInProgressLabel;
1391 }
1392
1393 /**
1394 * Set a label to display to confirm that the action should be executed.
1395 * This is usually an imperative verb like "Send".
1396 *
1397 * @param label the label to confirm the action should be executed
1398 * @return this object for method chaining
1399 */
1400 public WearableExtender setConfirmLabel(CharSequence label) {
1401 mConfirmLabel = label;
1402 return this;
1403 }
1404
1405 /**
1406 * Get the label to display to confirm that the action should be executed.
1407 * This is usually an imperative verb like "Send".
1408 *
1409 * @return the label to confirm the action should be executed
1410 */
1411 public CharSequence getConfirmLabel() {
1412 return mConfirmLabel;
1413 }
1414
1415 /**
1416 * Set a label to display to cancel the action.
1417 * This is usually an imperative verb, like "Cancel".
1418 *
1419 * @param label the label to display to cancel the action
1420 * @return this object for method chaining
1421 */
1422 public WearableExtender setCancelLabel(CharSequence label) {
1423 mCancelLabel = label;
1424 return this;
1425 }
1426
1427 /**
1428 * Get the label to display to cancel the action.
1429 * This is usually an imperative verb like "Cancel".
1430 *
1431 * @return the label to display to cancel the action
1432 */
1433 public CharSequence getCancelLabel() {
1434 return mCancelLabel;
1435 }
Alex Hills9ab3a232016-04-05 14:54:56 -04001436
1437 /**
1438 * Set a hint that this Action will launch an {@link Activity} directly, telling the
1439 * platform that it can generate the appropriate transitions.
1440 * @param hintLaunchesActivity {@code true} if the content intent will launch
1441 * an activity and transitions should be generated, false otherwise.
1442 * @return this object for method chaining
1443 */
Alex Hills4ec3ff42016-04-12 11:36:18 -04001444 public WearableExtender setHintLaunchesActivity(
Alex Hills9ab3a232016-04-05 14:54:56 -04001445 boolean hintLaunchesActivity) {
1446 setFlag(FLAG_HINT_LAUNCHES_ACTIVITY, hintLaunchesActivity);
1447 return this;
1448 }
1449
1450 /**
1451 * Get a hint that this Action will launch an {@link Activity} directly, telling the
1452 * platform that it can generate the appropriate transitions
1453 * @return {@code true} if the content intent will launch an activity and transitions
1454 * should be generated, false otherwise. The default value is {@code false} if this was
1455 * never set.
1456 */
Alex Hills4ec3ff42016-04-12 11:36:18 -04001457 public boolean getHintLaunchesActivity() {
Alex Hills9ab3a232016-04-05 14:54:56 -04001458 return (mFlags & FLAG_HINT_LAUNCHES_ACTIVITY) != 0;
1459 }
Griff Hazen61a9e862014-05-22 16:05:19 -07001460 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001461 }
1462
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001463 /**
1464 * Array of all {@link Action} structures attached to this notification by
1465 * {@link Builder#addAction(int, CharSequence, PendingIntent)}. Mostly useful for instances of
1466 * {@link android.service.notification.NotificationListenerService} that provide an alternative
1467 * interface for invoking actions.
1468 */
Daniel Sandlerea2a3172013-02-20 22:24:20 -05001469 public Action[] actions;
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001470
1471 /**
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001472 * Replacement version of this notification whose content will be shown
1473 * in an insecure context such as atop a secure keyguard. See {@link #visibility}
1474 * and {@link #VISIBILITY_PUBLIC}.
1475 */
1476 public Notification publicVersion;
1477
1478 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001479 * Constructs a Notification object with default values.
Joe Onorato46439ce2010-11-19 13:56:21 -08001480 * You might want to consider using {@link Builder} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001481 */
1482 public Notification()
1483 {
1484 this.when = System.currentTimeMillis();
Selim Cinekb85f36fd2016-04-20 18:46:36 -07001485 this.creationTime = System.currentTimeMillis();
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001486 this.priority = PRIORITY_DEFAULT;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001487 }
1488
1489 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001490 * @hide
1491 */
1492 public Notification(Context context, int icon, CharSequence tickerText, long when,
1493 CharSequence contentTitle, CharSequence contentText, Intent contentIntent)
1494 {
Chris Wren1ce4b6d2015-06-11 10:19:43 -04001495 new Builder(context)
1496 .setWhen(when)
1497 .setSmallIcon(icon)
1498 .setTicker(tickerText)
1499 .setContentTitle(contentTitle)
1500 .setContentText(contentText)
1501 .setContentIntent(PendingIntent.getActivity(context, 0, contentIntent, 0))
1502 .buildInto(this);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001503 }
1504
1505 /**
1506 * Constructs a Notification object with the information needed to
1507 * have a status bar icon without the standard expanded view.
1508 *
1509 * @param icon The resource id of the icon to put in the status bar.
1510 * @param tickerText The text that flows by in the status bar when the notification first
1511 * activates.
1512 * @param when The time to show in the time field. In the System.currentTimeMillis
1513 * timebase.
Joe Onorato46439ce2010-11-19 13:56:21 -08001514 *
1515 * @deprecated Use {@link Builder} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001516 */
Joe Onorato46439ce2010-11-19 13:56:21 -08001517 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001518 public Notification(int icon, CharSequence tickerText, long when)
1519 {
1520 this.icon = icon;
1521 this.tickerText = tickerText;
1522 this.when = when;
Selim Cinekb85f36fd2016-04-20 18:46:36 -07001523 this.creationTime = System.currentTimeMillis();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001524 }
1525
1526 /**
1527 * Unflatten the notification from a parcel.
1528 */
1529 public Notification(Parcel parcel)
1530 {
1531 int version = parcel.readInt();
1532
1533 when = parcel.readLong();
Selim Cinekb85f36fd2016-04-20 18:46:36 -07001534 creationTime = parcel.readLong();
Dan Sandler3936e7a2015-05-19 20:59:12 -04001535 if (parcel.readInt() != 0) {
1536 mSmallIcon = Icon.CREATOR.createFromParcel(parcel);
Dan Sandler4e787062015-06-17 15:09:48 -04001537 if (mSmallIcon.getType() == Icon.TYPE_RESOURCE) {
1538 icon = mSmallIcon.getResId();
1539 }
Dan Sandler3936e7a2015-05-19 20:59:12 -04001540 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001541 number = parcel.readInt();
1542 if (parcel.readInt() != 0) {
1543 contentIntent = PendingIntent.CREATOR.createFromParcel(parcel);
1544 }
1545 if (parcel.readInt() != 0) {
1546 deleteIntent = PendingIntent.CREATOR.createFromParcel(parcel);
1547 }
1548 if (parcel.readInt() != 0) {
1549 tickerText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
1550 }
1551 if (parcel.readInt() != 0) {
Joe Onorato46439ce2010-11-19 13:56:21 -08001552 tickerView = RemoteViews.CREATOR.createFromParcel(parcel);
Joe Onoratoef1e7762010-09-17 18:38:38 -04001553 }
1554 if (parcel.readInt() != 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001555 contentView = RemoteViews.CREATOR.createFromParcel(parcel);
1556 }
Joe Onorato561d3852010-11-20 18:09:34 -08001557 if (parcel.readInt() != 0) {
Dan Sandlerd63f9322015-05-06 15:18:49 -04001558 mLargeIcon = Icon.CREATOR.createFromParcel(parcel);
Joe Onorato561d3852010-11-20 18:09:34 -08001559 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001560 defaults = parcel.readInt();
1561 flags = parcel.readInt();
1562 if (parcel.readInt() != 0) {
1563 sound = Uri.CREATOR.createFromParcel(parcel);
1564 }
1565
1566 audioStreamType = parcel.readInt();
John Spurlockc0650f022014-07-19 13:22:39 -04001567 if (parcel.readInt() != 0) {
1568 audioAttributes = AudioAttributes.CREATOR.createFromParcel(parcel);
1569 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001570 vibrate = parcel.createLongArray();
1571 ledARGB = parcel.readInt();
1572 ledOnMS = parcel.readInt();
1573 ledOffMS = parcel.readInt();
1574 iconLevel = parcel.readInt();
Daniel Sandlere46cbd32010-06-17 10:35:26 -04001575
1576 if (parcel.readInt() != 0) {
1577 fullScreenIntent = PendingIntent.CREATOR.createFromParcel(parcel);
1578 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001579
1580 priority = parcel.readInt();
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001581
John Spurlockfd7f1e02014-03-18 16:41:57 -04001582 category = parcel.readString();
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001583
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001584 mGroupKey = parcel.readString();
1585
1586 mSortKey = parcel.readString();
1587
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001588 extras = Bundle.setDefusable(parcel.readBundle(), true); // may be null
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001589
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001590 actions = parcel.createTypedArray(Action.CREATOR); // may be null
1591
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001592 if (parcel.readInt() != 0) {
Daniel Sandlerf3b73432012-03-27 15:01:25 -04001593 bigContentView = RemoteViews.CREATOR.createFromParcel(parcel);
1594 }
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001595
Chris Wren8fd39ec2014-02-27 17:43:26 -05001596 if (parcel.readInt() != 0) {
1597 headsUpContentView = RemoteViews.CREATOR.createFromParcel(parcel);
1598 }
1599
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001600 visibility = parcel.readInt();
1601
1602 if (parcel.readInt() != 0) {
1603 publicVersion = Notification.CREATOR.createFromParcel(parcel);
1604 }
Dan Sandler26e81cf2014-05-06 10:01:27 -04001605
1606 color = parcel.readInt();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001607 }
1608
Andy Stadler110988c2010-12-03 14:29:16 -08001609 @Override
Joe Onorato18e69df2010-05-17 22:26:12 -07001610 public Notification clone() {
1611 Notification that = new Notification();
Daniel Sandler1a497d32013-04-18 14:52:45 -04001612 cloneInto(that, true);
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001613 return that;
1614 }
Joe Onorato18e69df2010-05-17 22:26:12 -07001615
Daniel Sandler1a497d32013-04-18 14:52:45 -04001616 /**
1617 * Copy all (or if heavy is false, all except Bitmaps and RemoteViews) members
1618 * of this into that.
1619 * @hide
1620 */
1621 public void cloneInto(Notification that, boolean heavy) {
Joe Onorato18e69df2010-05-17 22:26:12 -07001622 that.when = this.when;
Selim Cinekb85f36fd2016-04-20 18:46:36 -07001623 that.creationTime = this.creationTime;
Dan Sandlerd63f9322015-05-06 15:18:49 -04001624 that.mSmallIcon = this.mSmallIcon;
Joe Onorato18e69df2010-05-17 22:26:12 -07001625 that.number = this.number;
1626
1627 // PendingIntents are global, so there's no reason (or way) to clone them.
1628 that.contentIntent = this.contentIntent;
1629 that.deleteIntent = this.deleteIntent;
Daniel Sandlere46cbd32010-06-17 10:35:26 -04001630 that.fullScreenIntent = this.fullScreenIntent;
Joe Onorato18e69df2010-05-17 22:26:12 -07001631
1632 if (this.tickerText != null) {
1633 that.tickerText = this.tickerText.toString();
1634 }
Daniel Sandler1a497d32013-04-18 14:52:45 -04001635 if (heavy && this.tickerView != null) {
Joe Onorato46439ce2010-11-19 13:56:21 -08001636 that.tickerView = this.tickerView.clone();
Joe Onoratoef1e7762010-09-17 18:38:38 -04001637 }
Daniel Sandler1a497d32013-04-18 14:52:45 -04001638 if (heavy && this.contentView != null) {
Joe Onorato18e69df2010-05-17 22:26:12 -07001639 that.contentView = this.contentView.clone();
1640 }
Dan Sandlerd63f9322015-05-06 15:18:49 -04001641 if (heavy && this.mLargeIcon != null) {
1642 that.mLargeIcon = this.mLargeIcon;
Joe Onorato561d3852010-11-20 18:09:34 -08001643 }
Jozef BABJAKa8b91832011-02-22 08:05:08 +01001644 that.iconLevel = this.iconLevel;
Joe Onorato18e69df2010-05-17 22:26:12 -07001645 that.sound = this.sound; // android.net.Uri is immutable
1646 that.audioStreamType = this.audioStreamType;
John Spurlockc0650f022014-07-19 13:22:39 -04001647 if (this.audioAttributes != null) {
1648 that.audioAttributes = new AudioAttributes.Builder(this.audioAttributes).build();
1649 }
Joe Onorato18e69df2010-05-17 22:26:12 -07001650
1651 final long[] vibrate = this.vibrate;
1652 if (vibrate != null) {
1653 final int N = vibrate.length;
1654 final long[] vib = that.vibrate = new long[N];
1655 System.arraycopy(vibrate, 0, vib, 0, N);
1656 }
1657
1658 that.ledARGB = this.ledARGB;
1659 that.ledOnMS = this.ledOnMS;
1660 that.ledOffMS = this.ledOffMS;
1661 that.defaults = this.defaults;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001662
Joe Onorato18e69df2010-05-17 22:26:12 -07001663 that.flags = this.flags;
1664
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001665 that.priority = this.priority;
Joe Malin8d40d042012-11-05 11:36:40 -08001666
John Spurlockfd7f1e02014-03-18 16:41:57 -04001667 that.category = this.category;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001668
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001669 that.mGroupKey = this.mGroupKey;
1670
1671 that.mSortKey = this.mSortKey;
1672
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001673 if (this.extras != null) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04001674 try {
1675 that.extras = new Bundle(this.extras);
1676 // will unparcel
1677 that.extras.size();
1678 } catch (BadParcelableException e) {
1679 Log.e(TAG, "could not unparcel extras from notification: " + this, e);
1680 that.extras = null;
1681 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001682 }
1683
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001684 if (this.actions != null) {
1685 that.actions = new Action[this.actions.length];
1686 for(int i=0; i<this.actions.length; i++) {
1687 that.actions[i] = this.actions[i].clone();
1688 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001689 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001690
Daniel Sandler1a497d32013-04-18 14:52:45 -04001691 if (heavy && this.bigContentView != null) {
Daniel Sandlerf3b73432012-03-27 15:01:25 -04001692 that.bigContentView = this.bigContentView.clone();
1693 }
Daniel Sandler1a497d32013-04-18 14:52:45 -04001694
Chris Wren8fd39ec2014-02-27 17:43:26 -05001695 if (heavy && this.headsUpContentView != null) {
1696 that.headsUpContentView = this.headsUpContentView.clone();
1697 }
1698
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001699 that.visibility = this.visibility;
1700
1701 if (this.publicVersion != null) {
1702 that.publicVersion = new Notification();
1703 this.publicVersion.cloneInto(that.publicVersion, heavy);
1704 }
1705
Dan Sandler26e81cf2014-05-06 10:01:27 -04001706 that.color = this.color;
1707
Daniel Sandler1a497d32013-04-18 14:52:45 -04001708 if (!heavy) {
1709 that.lightenPayload(); // will clean out extras
1710 }
1711 }
1712
1713 /**
1714 * Removes heavyweight parts of the Notification object for archival or for sending to
1715 * listeners when the full contents are not necessary.
1716 * @hide
1717 */
1718 public final void lightenPayload() {
1719 tickerView = null;
1720 contentView = null;
1721 bigContentView = null;
Chris Wren8fd39ec2014-02-27 17:43:26 -05001722 headsUpContentView = null;
Dan Sandlerd63f9322015-05-06 15:18:49 -04001723 mLargeIcon = null;
Dan Sandler50128532015-12-08 15:42:41 -05001724 if (extras != null && !extras.isEmpty()) {
1725 final Set<String> keyset = extras.keySet();
1726 final int N = keyset.size();
1727 final String[] keys = keyset.toArray(new String[N]);
1728 for (int i=0; i<N; i++) {
1729 final String key = keys[i];
1730 final Object obj = extras.get(key);
1731 if (obj != null &&
1732 ( obj instanceof Parcelable
1733 || obj instanceof Parcelable[]
1734 || obj instanceof SparseArray
1735 || obj instanceof ArrayList)) {
1736 extras.remove(key);
1737 }
1738 }
Daniel Sandler1a497d32013-04-18 14:52:45 -04001739 }
Joe Onorato18e69df2010-05-17 22:26:12 -07001740 }
1741
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04001742 /**
1743 * Make sure this CharSequence is safe to put into a bundle, which basically
1744 * means it had better not be some custom Parcelable implementation.
1745 * @hide
1746 */
1747 public static CharSequence safeCharSequence(CharSequence cs) {
Christoph Studer535ec612014-09-03 15:47:47 +02001748 if (cs == null) return cs;
1749 if (cs.length() > MAX_CHARSEQUENCE_LENGTH) {
1750 cs = cs.subSequence(0, MAX_CHARSEQUENCE_LENGTH);
1751 }
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04001752 if (cs instanceof Parcelable) {
1753 Log.e(TAG, "warning: " + cs.getClass().getCanonicalName()
1754 + " instance is a custom Parcelable and not allowed in Notification");
1755 return cs.toString();
1756 }
Selim Cinek60a54252016-02-26 17:03:25 -08001757 return removeTextSizeSpans(cs);
1758 }
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04001759
Selim Cinek60a54252016-02-26 17:03:25 -08001760 private static CharSequence removeTextSizeSpans(CharSequence charSequence) {
1761 if (charSequence instanceof Spanned) {
1762 Spanned ss = (Spanned) charSequence;
1763 Object[] spans = ss.getSpans(0, ss.length(), Object.class);
1764 SpannableStringBuilder builder = new SpannableStringBuilder(ss.toString());
1765 for (Object span : spans) {
1766 Object resultSpan = span;
Selim Cinek89991a22016-03-07 19:51:54 -08001767 if (resultSpan instanceof CharacterStyle) {
1768 resultSpan = ((CharacterStyle) span).getUnderlying();
1769 }
1770 if (resultSpan instanceof TextAppearanceSpan) {
1771 TextAppearanceSpan originalSpan = (TextAppearanceSpan) resultSpan;
Selim Cinek60a54252016-02-26 17:03:25 -08001772 resultSpan = new TextAppearanceSpan(
1773 originalSpan.getFamily(),
1774 originalSpan.getTextStyle(),
1775 -1,
1776 originalSpan.getTextColor(),
1777 originalSpan.getLinkTextColor());
Selim Cinek89991a22016-03-07 19:51:54 -08001778 } else if (resultSpan instanceof RelativeSizeSpan
1779 || resultSpan instanceof AbsoluteSizeSpan) {
Selim Cinek60a54252016-02-26 17:03:25 -08001780 continue;
Selim Cinek89991a22016-03-07 19:51:54 -08001781 } else {
1782 resultSpan = span;
Selim Cinek60a54252016-02-26 17:03:25 -08001783 }
1784 builder.setSpan(resultSpan, ss.getSpanStart(span), ss.getSpanEnd(span),
1785 ss.getSpanFlags(span));
1786 }
1787 return builder;
1788 }
1789 return charSequence;
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04001790 }
1791
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001792 public int describeContents() {
1793 return 0;
1794 }
1795
1796 /**
Dan Sandler4e787062015-06-17 15:09:48 -04001797 * Flatten this notification into a parcel.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001798 */
1799 public void writeToParcel(Parcel parcel, int flags)
1800 {
1801 parcel.writeInt(1);
1802
1803 parcel.writeLong(when);
Selim Cinekb85f36fd2016-04-20 18:46:36 -07001804 parcel.writeLong(creationTime);
Dan Sandler4e787062015-06-17 15:09:48 -04001805 if (mSmallIcon == null && icon != 0) {
1806 // you snuck an icon in here without using the builder; let's try to keep it
1807 mSmallIcon = Icon.createWithResource("", icon);
1808 }
Dan Sandler3936e7a2015-05-19 20:59:12 -04001809 if (mSmallIcon != null) {
1810 parcel.writeInt(1);
1811 mSmallIcon.writeToParcel(parcel, 0);
1812 } else {
1813 parcel.writeInt(0);
1814 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001815 parcel.writeInt(number);
1816 if (contentIntent != null) {
1817 parcel.writeInt(1);
1818 contentIntent.writeToParcel(parcel, 0);
1819 } else {
1820 parcel.writeInt(0);
1821 }
1822 if (deleteIntent != null) {
1823 parcel.writeInt(1);
1824 deleteIntent.writeToParcel(parcel, 0);
1825 } else {
1826 parcel.writeInt(0);
1827 }
1828 if (tickerText != null) {
1829 parcel.writeInt(1);
1830 TextUtils.writeToParcel(tickerText, parcel, flags);
1831 } else {
1832 parcel.writeInt(0);
1833 }
Joe Onorato46439ce2010-11-19 13:56:21 -08001834 if (tickerView != null) {
Joe Onoratoef1e7762010-09-17 18:38:38 -04001835 parcel.writeInt(1);
Joe Onorato46439ce2010-11-19 13:56:21 -08001836 tickerView.writeToParcel(parcel, 0);
Joe Onoratoef1e7762010-09-17 18:38:38 -04001837 } else {
1838 parcel.writeInt(0);
1839 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001840 if (contentView != null) {
1841 parcel.writeInt(1);
1842 contentView.writeToParcel(parcel, 0);
1843 } else {
1844 parcel.writeInt(0);
1845 }
Dan Sandlerd63f9322015-05-06 15:18:49 -04001846 if (mLargeIcon != null) {
Joe Onorato561d3852010-11-20 18:09:34 -08001847 parcel.writeInt(1);
Dan Sandlerd63f9322015-05-06 15:18:49 -04001848 mLargeIcon.writeToParcel(parcel, 0);
Joe Onorato561d3852010-11-20 18:09:34 -08001849 } else {
1850 parcel.writeInt(0);
1851 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001852
1853 parcel.writeInt(defaults);
1854 parcel.writeInt(this.flags);
1855
1856 if (sound != null) {
1857 parcel.writeInt(1);
1858 sound.writeToParcel(parcel, 0);
1859 } else {
1860 parcel.writeInt(0);
1861 }
1862 parcel.writeInt(audioStreamType);
John Spurlockc0650f022014-07-19 13:22:39 -04001863
1864 if (audioAttributes != null) {
1865 parcel.writeInt(1);
1866 audioAttributes.writeToParcel(parcel, 0);
1867 } else {
1868 parcel.writeInt(0);
1869 }
1870
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001871 parcel.writeLongArray(vibrate);
1872 parcel.writeInt(ledARGB);
1873 parcel.writeInt(ledOnMS);
1874 parcel.writeInt(ledOffMS);
1875 parcel.writeInt(iconLevel);
Daniel Sandlere46cbd32010-06-17 10:35:26 -04001876
1877 if (fullScreenIntent != null) {
1878 parcel.writeInt(1);
1879 fullScreenIntent.writeToParcel(parcel, 0);
1880 } else {
1881 parcel.writeInt(0);
1882 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001883
1884 parcel.writeInt(priority);
Joe Malin8d40d042012-11-05 11:36:40 -08001885
John Spurlockfd7f1e02014-03-18 16:41:57 -04001886 parcel.writeString(category);
Joe Malin8d40d042012-11-05 11:36:40 -08001887
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001888 parcel.writeString(mGroupKey);
1889
1890 parcel.writeString(mSortKey);
1891
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001892 parcel.writeBundle(extras); // null ok
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001893
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001894 parcel.writeTypedArray(actions, 0); // null ok
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001895
Daniel Sandlerf3b73432012-03-27 15:01:25 -04001896 if (bigContentView != null) {
1897 parcel.writeInt(1);
1898 bigContentView.writeToParcel(parcel, 0);
1899 } else {
1900 parcel.writeInt(0);
1901 }
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001902
Chris Wren8fd39ec2014-02-27 17:43:26 -05001903 if (headsUpContentView != null) {
1904 parcel.writeInt(1);
1905 headsUpContentView.writeToParcel(parcel, 0);
1906 } else {
1907 parcel.writeInt(0);
1908 }
1909
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001910 parcel.writeInt(visibility);
1911
1912 if (publicVersion != null) {
1913 parcel.writeInt(1);
1914 publicVersion.writeToParcel(parcel, 0);
1915 } else {
1916 parcel.writeInt(0);
1917 }
Dan Sandler26e81cf2014-05-06 10:01:27 -04001918
1919 parcel.writeInt(color);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001920 }
1921
1922 /**
1923 * Parcelable.Creator that instantiates Notification objects
1924 */
1925 public static final Parcelable.Creator<Notification> CREATOR
1926 = new Parcelable.Creator<Notification>()
1927 {
1928 public Notification createFromParcel(Parcel parcel)
1929 {
1930 return new Notification(parcel);
1931 }
1932
1933 public Notification[] newArray(int size)
1934 {
1935 return new Notification[size];
1936 }
1937 };
1938
1939 /**
1940 * Sets the {@link #contentView} field to be a view with the standard "Latest Event"
1941 * layout.
1942 *
1943 * <p>Uses the {@link #icon} and {@link #when} fields to set the icon and time fields
1944 * in the view.</p>
1945 * @param context The context for your application / activity.
1946 * @param contentTitle The title that goes in the expanded entry.
1947 * @param contentText The text that goes in the expanded entry.
1948 * @param contentIntent The intent to launch when the user clicks the expanded notification.
1949 * If this is an activity, it must include the
1950 * {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} flag, which requires
Scott Main7aee61f2011-02-08 11:25:01 -08001951 * that you take care of task management as described in the
1952 * <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back
1953 * Stack</a> document.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001954 *
Joe Onorato46439ce2010-11-19 13:56:21 -08001955 * @deprecated Use {@link Builder} instead.
Chris Wrena05db382015-06-24 15:18:34 -04001956 * @removed
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001957 */
Joe Onorato46439ce2010-11-19 13:56:21 -08001958 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001959 public void setLatestEventInfo(Context context,
1960 CharSequence contentTitle, CharSequence contentText, PendingIntent contentIntent) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04001961 if (context.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1){
1962 Log.e(TAG, "setLatestEventInfo() is deprecated and you should feel deprecated.",
1963 new Throwable());
1964 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001965
Julia Reynoldsd9228f12015-10-20 10:37:27 -04001966 // ensure that any information already set directly is preserved
1967 final Notification.Builder builder = new Notification.Builder(context, this);
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001968
1969 // now apply the latestEventInfo fields
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001970 if (contentTitle != null) {
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001971 builder.setContentTitle(contentTitle);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001972 }
1973 if (contentText != null) {
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001974 builder.setContentText(contentText);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001975 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001976 builder.setContentIntent(contentIntent);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04001977
1978 builder.build(); // callers expect this notification to be ready to use
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001979 }
1980
Julia Reynoldsda303542015-11-23 14:00:20 -05001981 /**
1982 * @hide
1983 */
1984 public static void addFieldsFromContext(Context context, Notification notification) {
Jeff Sharkey012bc7b2016-04-11 16:30:27 -06001985 addFieldsFromContext(context.getApplicationInfo(), context.getUserId(), notification);
1986 }
1987
1988 /**
1989 * @hide
1990 */
1991 public static void addFieldsFromContext(ApplicationInfo ai, int userId,
1992 Notification notification) {
1993 notification.extras.putParcelable(EXTRA_BUILDER_APPLICATION_INFO, ai);
1994 notification.extras.putInt(EXTRA_ORIGINATING_USERID, userId);
Julia Reynoldsda303542015-11-23 14:00:20 -05001995 }
1996
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001997 @Override
1998 public String toString() {
1999 StringBuilder sb = new StringBuilder();
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002000 sb.append("Notification(pri=");
2001 sb.append(priority);
2002 sb.append(" contentView=");
Joe Onoratoc9596d62011-01-12 17:03:11 -08002003 if (contentView != null) {
2004 sb.append(contentView.getPackage());
2005 sb.append("/0x");
2006 sb.append(Integer.toHexString(contentView.getLayoutId()));
2007 } else {
2008 sb.append("null");
2009 }
2010 sb.append(" vibrate=");
Daniel Sandler6738eee2012-11-16 12:03:32 -05002011 if ((this.defaults & DEFAULT_VIBRATE) != 0) {
2012 sb.append("default");
2013 } else if (this.vibrate != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002014 int N = this.vibrate.length-1;
2015 sb.append("[");
2016 for (int i=0; i<N; i++) {
2017 sb.append(this.vibrate[i]);
2018 sb.append(',');
2019 }
Simon Schoar8cf97d92009-06-10 22:08:37 +02002020 if (N != -1) {
2021 sb.append(this.vibrate[N]);
2022 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002023 sb.append("]");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002024 } else {
2025 sb.append("null");
2026 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002027 sb.append(" sound=");
Daniel Sandler6738eee2012-11-16 12:03:32 -05002028 if ((this.defaults & DEFAULT_SOUND) != 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002029 sb.append("default");
Daniel Sandler6738eee2012-11-16 12:03:32 -05002030 } else if (this.sound != null) {
2031 sb.append(this.sound.toString());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002032 } else {
2033 sb.append("null");
2034 }
Chris Wren365b6d32015-07-16 10:39:26 -04002035 if (this.tickerText != null) {
2036 sb.append(" tick");
2037 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002038 sb.append(" defaults=0x");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002039 sb.append(Integer.toHexString(this.defaults));
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002040 sb.append(" flags=0x");
Daniel Sandlere46cbd32010-06-17 10:35:26 -04002041 sb.append(Integer.toHexString(this.flags));
Dan Sandler26e81cf2014-05-06 10:01:27 -04002042 sb.append(String.format(" color=0x%08x", this.color));
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002043 if (this.category != null) {
2044 sb.append(" category=");
2045 sb.append(this.category);
2046 }
2047 if (this.mGroupKey != null) {
2048 sb.append(" groupKey=");
2049 sb.append(this.mGroupKey);
2050 }
2051 if (this.mSortKey != null) {
2052 sb.append(" sortKey=");
2053 sb.append(this.mSortKey);
2054 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002055 if (actions != null) {
Dan Sandler1b718782014-07-18 12:43:45 -04002056 sb.append(" actions=");
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002057 sb.append(actions.length);
Dan Sandler1b718782014-07-18 12:43:45 -04002058 }
2059 sb.append(" vis=");
2060 sb.append(visibilityToString(this.visibility));
2061 if (this.publicVersion != null) {
2062 sb.append(" publicVersion=");
2063 sb.append(publicVersion.toString());
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002064 }
2065 sb.append(")");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002066 return sb.toString();
2067 }
Joe Onorato46439ce2010-11-19 13:56:21 -08002068
Dan Sandler1b718782014-07-18 12:43:45 -04002069 /**
2070 * {@hide}
2071 */
2072 public static String visibilityToString(int vis) {
2073 switch (vis) {
2074 case VISIBILITY_PRIVATE:
2075 return "PRIVATE";
2076 case VISIBILITY_PUBLIC:
2077 return "PUBLIC";
2078 case VISIBILITY_SECRET:
2079 return "SECRET";
2080 default:
2081 return "UNKNOWN(" + String.valueOf(vis) + ")";
2082 }
2083 }
2084
Joe Onoratocb109a02011-01-18 17:57:41 -08002085 /**
John Spurlock1d881a12015-03-18 19:21:54 -04002086 * {@hide}
2087 */
2088 public static String priorityToString(@Priority int pri) {
2089 switch (pri) {
2090 case PRIORITY_MIN:
2091 return "MIN";
2092 case PRIORITY_LOW:
2093 return "LOW";
2094 case PRIORITY_DEFAULT:
2095 return "DEFAULT";
2096 case PRIORITY_HIGH:
2097 return "HIGH";
2098 case PRIORITY_MAX:
2099 return "MAX";
2100 default:
2101 return "UNKNOWN(" + String.valueOf(pri) + ")";
2102 }
2103 }
2104
2105 /**
Dan Sandlerd63f9322015-05-06 15:18:49 -04002106 * The small icon representing this notification in the status bar and content view.
2107 *
2108 * @return the small icon representing this notification.
2109 *
2110 * @see Builder#getSmallIcon()
2111 * @see Builder#setSmallIcon(Icon)
2112 */
2113 public Icon getSmallIcon() {
2114 return mSmallIcon;
2115 }
2116
2117 /**
2118 * Used when notifying to clean up legacy small icons.
2119 * @hide
2120 */
2121 public void setSmallIcon(Icon icon) {
2122 mSmallIcon = icon;
2123 }
2124
2125 /**
2126 * The large icon shown in this notification's content view.
2127 * @see Builder#getLargeIcon()
2128 * @see Builder#setLargeIcon(Icon)
2129 */
2130 public Icon getLargeIcon() {
2131 return mLargeIcon;
2132 }
2133
2134 /**
Christoph Studer4600f9b2014-07-22 22:44:43 +02002135 * @hide
2136 */
Christoph Studerc8db24b2014-07-25 17:50:30 +02002137 public boolean isGroupSummary() {
2138 return mGroupKey != null && (flags & FLAG_GROUP_SUMMARY) != 0;
2139 }
2140
2141 /**
2142 * @hide
2143 */
2144 public boolean isGroupChild() {
2145 return mGroupKey != null && (flags & FLAG_GROUP_SUMMARY) == 0;
2146 }
2147
2148 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002149 * Builder class for {@link Notification} objects.
Joe Malin8d40d042012-11-05 11:36:40 -08002150 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002151 * Provides a convenient way to set the various fields of a {@link Notification} and generate
Scott Main183bf112012-08-13 19:12:13 -07002152 * content views using the platform's notification layout template. If your app supports
2153 * versions of Android as old as API level 4, you can instead use
2154 * {@link android.support.v4.app.NotificationCompat.Builder NotificationCompat.Builder},
2155 * available in the <a href="{@docRoot}tools/extras/support-library.html">Android Support
2156 * library</a>.
Joe Malin8d40d042012-11-05 11:36:40 -08002157 *
Scott Main183bf112012-08-13 19:12:13 -07002158 * <p>Example:
Joe Malin8d40d042012-11-05 11:36:40 -08002159 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002160 * <pre class="prettyprint">
Scott Main183bf112012-08-13 19:12:13 -07002161 * Notification noti = new Notification.Builder(mContext)
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002162 * .setContentTitle(&quot;New mail from &quot; + sender.toString())
2163 * .setContentText(subject)
2164 * .setSmallIcon(R.drawable.new_mail)
2165 * .setLargeIcon(aBitmap)
Chris Wrenfbd96ba2012-05-01 12:03:58 -04002166 * .build();
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002167 * </pre>
Joe Onoratocb109a02011-01-18 17:57:41 -08002168 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002169 public static class Builder {
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05002170 /**
2171 * @hide
2172 */
2173 public static final String EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT =
2174 "android.rebuild.contentViewActionCount";
2175 /**
2176 * @hide
2177 */
2178 public static final String EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT
2179 = "android.rebuild.bigViewActionCount";
2180 /**
2181 * @hide
2182 */
2183 public static final String EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT
2184 = "android.rebuild.hudViewActionCount";
2185
Daniel Sandler602ad1c2012-06-12 16:06:27 -04002186 private static final int MAX_ACTION_BUTTONS = 3;
Daniel Sandler8680bf82012-05-15 16:52:52 -04002187
Joe Onorato46439ce2010-11-19 13:56:21 -08002188 private Context mContext;
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002189 private Notification mN;
2190 private Bundle mUserExtras = new Bundle();
Chris Wrenfbd96ba2012-05-01 12:03:58 -04002191 private Style mStyle;
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002192 private ArrayList<Action> mActions = new ArrayList<Action>(MAX_ACTION_BUTTONS);
2193 private ArrayList<String> mPersonList = new ArrayList<String>();
2194 private NotificationColorUtil mColorUtil;
2195 private boolean mColorUtilInited = false;
Christoph Studer7ac80e62014-08-04 16:01:57 +02002196
2197 /**
Adrian Roos4ff3b122016-02-01 12:26:13 -08002198 * Caches a contrast-enhanced version of {@link #mCachedContrastColorIsFor}.
2199 */
2200 private int mCachedContrastColor = COLOR_INVALID;
2201 private int mCachedContrastColorIsFor = COLOR_INVALID;
2202
2203 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002204 * Constructs a new Builder with the defaults:
Joe Onoratocb109a02011-01-18 17:57:41 -08002205 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002206
2207 * <table>
2208 * <tr><th align=right>priority</th>
2209 * <td>{@link #PRIORITY_DEFAULT}</td></tr>
2210 * <tr><th align=right>when</th>
2211 * <td>now ({@link System#currentTimeMillis()})</td></tr>
2212 * <tr><th align=right>audio stream</th>
2213 * <td>{@link #STREAM_DEFAULT}</td></tr>
2214 * </table>
Joe Onoratocb109a02011-01-18 17:57:41 -08002215 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002216
2217 * @param context
2218 * A {@link Context} that will be used by the Builder to construct the
2219 * RemoteViews. The Context will not be held past the lifetime of this Builder
2220 * object.
Joe Onoratocb109a02011-01-18 17:57:41 -08002221 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002222 public Builder(Context context) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002223 this(context, null);
Joe Onorato46439ce2010-11-19 13:56:21 -08002224 }
2225
Joe Onoratocb109a02011-01-18 17:57:41 -08002226 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002227 * @hide
Christoph Studer4600f9b2014-07-22 22:44:43 +02002228 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002229 public Builder(Context context, Notification toAdopt) {
2230 mContext = context;
Christoph Studer4600f9b2014-07-22 22:44:43 +02002231
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002232 if (toAdopt == null) {
2233 mN = new Notification();
Selim Cinek0ff1ce602016-04-05 18:27:16 -07002234 if (context.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N) {
2235 mN.extras.putBoolean(EXTRA_SHOW_WHEN, true);
2236 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002237 mN.priority = PRIORITY_DEFAULT;
2238 mN.visibility = VISIBILITY_PRIVATE;
2239 } else {
2240 mN = toAdopt;
2241 if (mN.actions != null) {
2242 Collections.addAll(mActions, mN.actions);
Christoph Studer4600f9b2014-07-22 22:44:43 +02002243 }
2244
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002245 if (mN.extras.containsKey(EXTRA_PEOPLE)) {
2246 Collections.addAll(mPersonList, mN.extras.getStringArray(EXTRA_PEOPLE));
2247 }
2248
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002249 String templateClass = mN.extras.getString(EXTRA_TEMPLATE);
2250 if (!TextUtils.isEmpty(templateClass)) {
2251 final Class<? extends Style> styleClass
2252 = getNotificationStyleClass(templateClass);
2253 if (styleClass == null) {
2254 Log.d(TAG, "Unknown style class: " + templateClass);
2255 } else {
2256 try {
Adrian Roosc1a80b02016-04-05 14:54:55 -07002257 final Constructor<? extends Style> ctor =
2258 styleClass.getDeclaredConstructor();
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002259 ctor.setAccessible(true);
2260 final Style style = ctor.newInstance();
2261 style.restoreFromExtras(mN.extras);
2262
2263 if (style != null) {
2264 setStyle(style);
2265 }
2266 } catch (Throwable t) {
2267 Log.e(TAG, "Could not create Style", t);
2268 }
2269 }
2270 }
2271
2272 }
2273 }
2274
2275 private NotificationColorUtil getColorUtil() {
2276 if (!mColorUtilInited) {
2277 mColorUtilInited = true;
2278 if (mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.LOLLIPOP) {
2279 mColorUtil = NotificationColorUtil.getInstance(mContext);
Christoph Studer4600f9b2014-07-22 22:44:43 +02002280 }
2281 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002282 return mColorUtil;
Christoph Studer4600f9b2014-07-22 22:44:43 +02002283 }
2284
2285 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002286 * Add a timestamp pertaining to the notification (usually the time the event occurred).
Selim Cinek0ff1ce602016-04-05 18:27:16 -07002287 *
2288 * For apps targeting {@link android.os.Build.VERSION_CODES#N} and above, this time is not
2289 * shown anymore by default and must be opted into by using
2290 * {@link android.app.Notification.Builder#setShowWhen(boolean)}
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002291 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002292 * @see Notification#when
Joe Onoratocb109a02011-01-18 17:57:41 -08002293 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002294 public Builder setWhen(long when) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002295 mN.when = when;
Joe Onorato46439ce2010-11-19 13:56:21 -08002296 return this;
2297 }
2298
Joe Onoratocb109a02011-01-18 17:57:41 -08002299 /**
Griff Hazen50c11652014-05-16 09:46:31 -07002300 * Control whether the timestamp set with {@link #setWhen(long) setWhen} is shown
Daniel Sandler0c890492012-09-12 17:23:10 -07002301 * in the content view.
Selim Cinek0ff1ce602016-04-05 18:27:16 -07002302 * For apps targeting {@link android.os.Build.VERSION_CODES#N} and above, this defaults to
2303 * {@code false}. For earlier apps, the default is {@code true}.
Daniel Sandler0c890492012-09-12 17:23:10 -07002304 */
2305 public Builder setShowWhen(boolean show) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002306 mN.extras.putBoolean(EXTRA_SHOW_WHEN, show);
Daniel Sandler0c890492012-09-12 17:23:10 -07002307 return this;
2308 }
2309
2310 /**
Daniel Sandlerd33b8032012-05-10 11:41:48 -04002311 * Show the {@link Notification#when} field as a stopwatch.
Joe Malin8d40d042012-11-05 11:36:40 -08002312 *
2313 * Instead of presenting <code>when</code> as a timestamp, the notification will show an
Daniel Sandlerd33b8032012-05-10 11:41:48 -04002314 * automatically updating display of the minutes and seconds since <code>when</code>.
Daniel Sandlera2985ed2012-04-03 16:42:00 -04002315 *
Daniel Sandlerd33b8032012-05-10 11:41:48 -04002316 * Useful when showing an elapsed time (like an ongoing phone call).
2317 *
Selim Cinek81c23aa2016-02-25 16:23:13 -08002318 * The counter can also be set to count down to <code>when</code> when using
2319 * {@link #setChronometerCountsDown(boolean)}.
2320 *
Daniel Sandlerd33b8032012-05-10 11:41:48 -04002321 * @see android.widget.Chronometer
Daniel Sandlera2985ed2012-04-03 16:42:00 -04002322 * @see Notification#when
Selim Cinek81c23aa2016-02-25 16:23:13 -08002323 * @see #setChronometerCountsDown(boolean)
Daniel Sandlera2985ed2012-04-03 16:42:00 -04002324 */
2325 public Builder setUsesChronometer(boolean b) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002326 mN.extras.putBoolean(EXTRA_SHOW_CHRONOMETER, b);
Daniel Sandlera2985ed2012-04-03 16:42:00 -04002327 return this;
2328 }
2329
2330 /**
Selim Cinek81c23aa2016-02-25 16:23:13 -08002331 * Sets the Chronometer to count down instead of counting up.
2332 *
2333 * <p>This is only relevant if {@link #setUsesChronometer(boolean)} has been set to true.
2334 * If it isn't set the chronometer will count up.
2335 *
2336 * @see #setUsesChronometer(boolean)
2337 */
2338 public Builder setChronometerCountsDown(boolean countsDown) {
2339 mN.extras.putBoolean(EXTRA_CHRONOMETER_COUNTS_DOWN, countsDown);
2340 return this;
2341 }
2342
2343 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002344 * Set the small icon resource, which will be used to represent the notification in the
2345 * status bar.
Joe Onoratocb109a02011-01-18 17:57:41 -08002346 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002347
2348 * The platform template for the expanded view will draw this icon in the left, unless a
2349 * {@link #setLargeIcon(Bitmap) large icon} has also been specified, in which case the small
2350 * icon will be moved to the right-hand side.
2351 *
2352
2353 * @param icon
2354 * A resource ID in the application's package of the drawable to use.
2355 * @see Notification#icon
Joe Onoratocb109a02011-01-18 17:57:41 -08002356 */
Tor Norbye7b9c9122013-05-30 16:48:33 -07002357 public Builder setSmallIcon(@DrawableRes int icon) {
Dan Sandlerd63f9322015-05-06 15:18:49 -04002358 return setSmallIcon(icon != 0
2359 ? Icon.createWithResource(mContext, icon)
2360 : null);
Joe Onorato46439ce2010-11-19 13:56:21 -08002361 }
2362
Joe Onoratocb109a02011-01-18 17:57:41 -08002363 /**
2364 * A variant of {@link #setSmallIcon(int) setSmallIcon(int)} that takes an additional
2365 * level parameter for when the icon is a {@link android.graphics.drawable.LevelListDrawable
2366 * LevelListDrawable}.
2367 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002368 * @param icon A resource ID in the application's package of the drawable to use.
Joe Onoratocb109a02011-01-18 17:57:41 -08002369 * @param level The level to use for the icon.
2370 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002371 * @see Notification#icon
2372 * @see Notification#iconLevel
Joe Onoratocb109a02011-01-18 17:57:41 -08002373 */
Tor Norbye7b9c9122013-05-30 16:48:33 -07002374 public Builder setSmallIcon(@DrawableRes int icon, int level) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002375 mN.iconLevel = level;
Dan Sandlerd63f9322015-05-06 15:18:49 -04002376 return setSmallIcon(icon);
2377 }
2378
2379 /**
2380 * Set the small icon, which will be used to represent the notification in the
2381 * status bar and content view (unless overriden there by a
2382 * {@link #setLargeIcon(Bitmap) large icon}).
2383 *
2384 * @param icon An Icon object to use.
2385 * @see Notification#icon
2386 */
2387 public Builder setSmallIcon(Icon icon) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002388 mN.setSmallIcon(icon);
2389 if (icon != null && icon.getType() == Icon.TYPE_RESOURCE) {
2390 mN.icon = icon.getResId();
2391 }
Joe Onorato46439ce2010-11-19 13:56:21 -08002392 return this;
2393 }
2394
Joe Onoratocb109a02011-01-18 17:57:41 -08002395 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002396 * Set the first line of text in the platform notification template.
Joe Onoratocb109a02011-01-18 17:57:41 -08002397 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002398 public Builder setContentTitle(CharSequence title) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002399 mN.extras.putCharSequence(EXTRA_TITLE, safeCharSequence(title));
Joe Onorato46439ce2010-11-19 13:56:21 -08002400 return this;
2401 }
2402
Joe Onoratocb109a02011-01-18 17:57:41 -08002403 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002404 * Set the second line of text in the platform notification template.
Joe Onoratocb109a02011-01-18 17:57:41 -08002405 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002406 public Builder setContentText(CharSequence text) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002407 mN.extras.putCharSequence(EXTRA_TEXT, safeCharSequence(text));
Joe Onorato46439ce2010-11-19 13:56:21 -08002408 return this;
2409 }
2410
Joe Onoratocb109a02011-01-18 17:57:41 -08002411 /**
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07002412 * This provides some additional information that is displayed in the notification. No
2413 * guarantees are given where exactly it is displayed.
2414 *
2415 * <p>This information should only be provided if it provides an essential
2416 * benefit to the understanding of the notification. The more text you provide the
2417 * less readable it becomes. For example, an email client should only provide the account
2418 * name here if more than one email account has been added.</p>
2419 *
2420 * <p>As of {@link android.os.Build.VERSION_CODES#N} this information is displayed in the
2421 * notification header area.
2422 *
2423 * On Android versions before {@link android.os.Build.VERSION_CODES#N}
2424 * this will be shown in the third line of text in the platform notification template.
2425 * You should not be using {@link #setProgress(int, int, boolean)} at the
2426 * same time on those versions; they occupy the same place.
2427 * </p>
Daniel Sandlerf3b73432012-03-27 15:01:25 -04002428 */
2429 public Builder setSubText(CharSequence text) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002430 mN.extras.putCharSequence(EXTRA_SUB_TEXT, safeCharSequence(text));
Daniel Sandlerf3b73432012-03-27 15:01:25 -04002431 return this;
2432 }
2433
2434 /**
Adrian Roose458aa82015-12-08 16:17:19 -08002435 * Set the remote input history.
2436 *
2437 * This should be set to the most recent inputs that have been sent
2438 * through a {@link RemoteInput} of this Notification and cleared once the it is no
2439 * longer relevant (e.g. for chat notifications once the other party has responded).
2440 *
2441 * The most recent input must be stored at the 0 index, the second most recent at the
2442 * 1 index, etc. Note that the system will limit both how far back the inputs will be shown
2443 * and how much of each individual input is shown.
2444 *
2445 * <p>Note: The reply text will only be shown on notifications that have least one action
2446 * with a {@code RemoteInput}.</p>
2447 */
2448 public Builder setRemoteInputHistory(CharSequence[] text) {
2449 if (text == null) {
2450 mN.extras.putCharSequenceArray(EXTRA_REMOTE_INPUT_HISTORY, null);
2451 } else {
2452 final int N = Math.min(MAX_REPLY_HISTORY, text.length);
2453 CharSequence[] safe = new CharSequence[N];
2454 for (int i = 0; i < N; i++) {
2455 safe[i] = safeCharSequence(text[i]);
2456 }
2457 mN.extras.putCharSequenceArray(EXTRA_REMOTE_INPUT_HISTORY, safe);
2458 }
2459 return this;
2460 }
2461
2462 /**
Joe Onoratocb109a02011-01-18 17:57:41 -08002463 * Set the large number at the right-hand side of the notification. This is
2464 * equivalent to setContentInfo, although it might show the number in a different
2465 * font size for readability.
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07002466 *
2467 * @deprecated this number is not shown anywhere anymore
Joe Onoratocb109a02011-01-18 17:57:41 -08002468 */
Joe Onorato8595a3d2010-11-19 18:12:07 -08002469 public Builder setNumber(int number) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002470 mN.number = number;
Joe Onorato8595a3d2010-11-19 18:12:07 -08002471 return this;
2472 }
2473
Joe Onoratocb109a02011-01-18 17:57:41 -08002474 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002475 * A small piece of additional information pertaining to this notification.
2476 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002477 * The platform template will draw this on the last line of the notification, at the far
2478 * right (to the right of a smallIcon if it has been placed there).
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07002479 *
2480 * @deprecated use {@link #setSubText(CharSequence)} instead to set a text in the header.
2481 * For legacy apps targeting a version below {@link android.os.Build.VERSION_CODES#N} this
2482 * field will still show up, but the subtext will take precedence.
Joe Onoratocb109a02011-01-18 17:57:41 -08002483 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002484 public Builder setContentInfo(CharSequence info) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002485 mN.extras.putCharSequence(EXTRA_INFO_TEXT, safeCharSequence(info));
Joe Onorato46439ce2010-11-19 13:56:21 -08002486 return this;
2487 }
2488
Joe Onoratocb109a02011-01-18 17:57:41 -08002489 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002490 * Set the progress this notification represents.
2491 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002492 * The platform template will represent this using a {@link ProgressBar}.
Jeff Sharkey1c400132011-08-05 14:50:13 -07002493 */
2494 public Builder setProgress(int max, int progress, boolean indeterminate) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002495 mN.extras.putInt(EXTRA_PROGRESS, progress);
2496 mN.extras.putInt(EXTRA_PROGRESS_MAX, max);
2497 mN.extras.putBoolean(EXTRA_PROGRESS_INDETERMINATE, indeterminate);
Jeff Sharkey1c400132011-08-05 14:50:13 -07002498 return this;
2499 }
2500
2501 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002502 * Supply a custom RemoteViews to use instead of the platform template.
2503 *
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002504 * Use {@link #setCustomContentView(RemoteViews)} instead.
Joe Onoratocb109a02011-01-18 17:57:41 -08002505 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002506 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08002507 public Builder setContent(RemoteViews views) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002508 return setCustomContentView(views);
2509 }
2510
2511 /**
2512 * Supply custom RemoteViews to use instead of the platform template.
2513 *
2514 * This will override the layout that would otherwise be constructed by this Builder
2515 * object.
2516 */
2517 public Builder setCustomContentView(RemoteViews contentView) {
2518 mN.contentView = contentView;
2519 return this;
2520 }
2521
2522 /**
2523 * Supply custom RemoteViews to use instead of the platform template in the expanded form.
2524 *
2525 * This will override the expanded layout that would otherwise be constructed by this
2526 * Builder object.
2527 */
2528 public Builder setCustomBigContentView(RemoteViews contentView) {
2529 mN.bigContentView = contentView;
2530 return this;
2531 }
2532
2533 /**
2534 * Supply custom RemoteViews to use instead of the platform template in the heads up dialog.
2535 *
2536 * This will override the heads-up layout that would otherwise be constructed by this
2537 * Builder object.
2538 */
2539 public Builder setCustomHeadsUpContentView(RemoteViews contentView) {
2540 mN.headsUpContentView = contentView;
Joe Onorato46439ce2010-11-19 13:56:21 -08002541 return this;
2542 }
2543
Joe Onoratocb109a02011-01-18 17:57:41 -08002544 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002545 * Supply a {@link PendingIntent} to be sent when the notification is clicked.
2546 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002547 * As of {@link android.os.Build.VERSION_CODES#HONEYCOMB}, if this field is unset and you
2548 * have specified a custom RemoteViews with {@link #setContent(RemoteViews)}, you can use
2549 * {@link RemoteViews#setOnClickPendingIntent RemoteViews.setOnClickPendingIntent(int,PendingIntent)}
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002550 * to assign PendingIntents to individual views in that custom layout (i.e., to create
Daniel Sandlerf3b73432012-03-27 15:01:25 -04002551 * clickable buttons inside the notification view).
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002552 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002553 * @see Notification#contentIntent Notification.contentIntent
Joe Onoratocb109a02011-01-18 17:57:41 -08002554 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002555 public Builder setContentIntent(PendingIntent intent) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002556 mN.contentIntent = intent;
Joe Onorato46439ce2010-11-19 13:56:21 -08002557 return this;
2558 }
2559
Joe Onoratocb109a02011-01-18 17:57:41 -08002560 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002561 * Supply a {@link PendingIntent} to send when the notification is cleared explicitly by the user.
2562 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002563 * @see Notification#deleteIntent
Joe Onoratocb109a02011-01-18 17:57:41 -08002564 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002565 public Builder setDeleteIntent(PendingIntent intent) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002566 mN.deleteIntent = intent;
Joe Onorato46439ce2010-11-19 13:56:21 -08002567 return this;
2568 }
2569
Joe Onoratocb109a02011-01-18 17:57:41 -08002570 /**
2571 * An intent to launch instead of posting the notification to the status bar.
2572 * Only for use with extremely high-priority notifications demanding the user's
2573 * <strong>immediate</strong> attention, such as an incoming phone call or
2574 * alarm clock that the user has explicitly set to a particular time.
2575 * If this facility is used for something else, please give the user an option
2576 * to turn it off and use a normal notification, as this can be extremely
2577 * disruptive.
2578 *
Chris Wren47c20a12014-06-18 17:27:29 -04002579 * <p>
2580 * The system UI may choose to display a heads-up notification, instead of
2581 * launching this intent, while the user is using the device.
2582 * </p>
2583 *
Joe Onoratocb109a02011-01-18 17:57:41 -08002584 * @param intent The pending intent to launch.
2585 * @param highPriority Passing true will cause this notification to be sent
2586 * even if other notifications are suppressed.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002587 *
2588 * @see Notification#fullScreenIntent
Joe Onoratocb109a02011-01-18 17:57:41 -08002589 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002590 public Builder setFullScreenIntent(PendingIntent intent, boolean highPriority) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002591 mN.fullScreenIntent = intent;
Joe Onorato46439ce2010-11-19 13:56:21 -08002592 setFlag(FLAG_HIGH_PRIORITY, highPriority);
2593 return this;
2594 }
2595
Joe Onoratocb109a02011-01-18 17:57:41 -08002596 /**
Dan Sandler5fcdf6e2014-07-18 11:31:15 -04002597 * Set the "ticker" text which is sent to accessibility services.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002598 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002599 * @see Notification#tickerText
Joe Onoratocb109a02011-01-18 17:57:41 -08002600 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002601 public Builder setTicker(CharSequence tickerText) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002602 mN.tickerText = safeCharSequence(tickerText);
Joe Onorato46439ce2010-11-19 13:56:21 -08002603 return this;
2604 }
2605
Joe Onoratocb109a02011-01-18 17:57:41 -08002606 /**
Dan Sandler5fcdf6e2014-07-18 11:31:15 -04002607 * Obsolete version of {@link #setTicker(CharSequence)}.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002608 *
Joe Onoratocb109a02011-01-18 17:57:41 -08002609 */
Dan Sandler5fcdf6e2014-07-18 11:31:15 -04002610 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08002611 public Builder setTicker(CharSequence tickerText, RemoteViews views) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002612 setTicker(tickerText);
2613 // views is ignored
Joe Onorato46439ce2010-11-19 13:56:21 -08002614 return this;
2615 }
2616
Joe Onoratocb109a02011-01-18 17:57:41 -08002617 /**
Dan Sandlerd63f9322015-05-06 15:18:49 -04002618 * Add a large icon to the notification content view.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002619 *
2620 * In the platform template, this image will be shown on the left of the notification view
Dan Sandlerd63f9322015-05-06 15:18:49 -04002621 * in place of the {@link #setSmallIcon(Icon) small icon} (which will be placed in a small
2622 * badge atop the large icon).
Dan Sandler08a04c12015-05-06 15:18:49 -04002623 */
Dan Sandlerd63f9322015-05-06 15:18:49 -04002624 public Builder setLargeIcon(Bitmap b) {
2625 return setLargeIcon(b != null ? Icon.createWithBitmap(b) : null);
2626 }
2627
2628 /**
2629 * Add a large icon to the notification content view.
2630 *
2631 * In the platform template, this image will be shown on the left of the notification view
2632 * in place of the {@link #setSmallIcon(Icon) small icon} (which will be placed in a small
2633 * badge atop the large icon).
2634 */
2635 public Builder setLargeIcon(Icon icon) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002636 mN.mLargeIcon = icon;
2637 mN.extras.putParcelable(EXTRA_LARGE_ICON, icon);
Joe Onorato46439ce2010-11-19 13:56:21 -08002638 return this;
2639 }
2640
Joe Onoratocb109a02011-01-18 17:57:41 -08002641 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002642 * Set the sound to play.
2643 *
John Spurlockc0650f022014-07-19 13:22:39 -04002644 * It will be played using the {@link #AUDIO_ATTRIBUTES_DEFAULT default audio attributes}
2645 * for notifications.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002646 *
Chris Wren47c20a12014-06-18 17:27:29 -04002647 * <p>
2648 * A notification that is noisy is more likely to be presented as a heads-up notification.
2649 * </p>
2650 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002651 * @see Notification#sound
Joe Onoratocb109a02011-01-18 17:57:41 -08002652 */
Joe Onorato52f80cd2010-11-21 15:34:48 -08002653 public Builder setSound(Uri sound) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002654 mN.sound = sound;
2655 mN.audioAttributes = AUDIO_ATTRIBUTES_DEFAULT;
Joe Onorato52f80cd2010-11-21 15:34:48 -08002656 return this;
2657 }
2658
Joe Onoratocb109a02011-01-18 17:57:41 -08002659 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002660 * Set the sound to play, along with a specific stream on which to play it.
Joe Onoratocb109a02011-01-18 17:57:41 -08002661 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002662 * See {@link android.media.AudioManager} for the <code>STREAM_</code> constants.
2663 *
Chris Wren47c20a12014-06-18 17:27:29 -04002664 * <p>
2665 * A notification that is noisy is more likely to be presented as a heads-up notification.
2666 * </p>
John Spurlockc0650f022014-07-19 13:22:39 -04002667 * @deprecated use {@link #setSound(Uri, AudioAttributes)} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002668 * @see Notification#sound
Joe Onoratocb109a02011-01-18 17:57:41 -08002669 */
Jean-Michel Trivi81f871e2014-08-06 16:32:38 -07002670 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08002671 public Builder setSound(Uri sound, int streamType) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002672 mN.sound = sound;
2673 mN.audioStreamType = streamType;
Joe Onorato46439ce2010-11-19 13:56:21 -08002674 return this;
2675 }
2676
Joe Onoratocb109a02011-01-18 17:57:41 -08002677 /**
John Spurlockc0650f022014-07-19 13:22:39 -04002678 * Set the sound to play, along with specific {@link AudioAttributes audio attributes} to
2679 * use during playback.
2680 *
2681 * <p>
2682 * A notification that is noisy is more likely to be presented as a heads-up notification.
2683 * </p>
2684 *
2685 * @see Notification#sound
2686 */
2687 public Builder setSound(Uri sound, AudioAttributes audioAttributes) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002688 mN.sound = sound;
2689 mN.audioAttributes = audioAttributes;
John Spurlockc0650f022014-07-19 13:22:39 -04002690 return this;
2691 }
2692
2693 /**
Joe Onoratocb109a02011-01-18 17:57:41 -08002694 * Set the vibration pattern to use.
2695 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002696 * See {@link android.os.Vibrator#vibrate(long[], int)} for a discussion of the
2697 * <code>pattern</code> parameter.
2698 *
Chris Wren47c20a12014-06-18 17:27:29 -04002699 * <p>
2700 * A notification that vibrates is more likely to be presented as a heads-up notification.
2701 * </p>
2702 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002703 * @see Notification#vibrate
Joe Onoratocb109a02011-01-18 17:57:41 -08002704 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002705 public Builder setVibrate(long[] pattern) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002706 mN.vibrate = pattern;
Joe Onorato46439ce2010-11-19 13:56:21 -08002707 return this;
2708 }
2709
Joe Onoratocb109a02011-01-18 17:57:41 -08002710 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002711 * Set the desired color for the indicator LED on the device, as well as the
2712 * blink duty cycle (specified in milliseconds).
2713 *
2714
2715 * Not all devices will honor all (or even any) of these values.
2716 *
2717
2718 * @see Notification#ledARGB
2719 * @see Notification#ledOnMS
2720 * @see Notification#ledOffMS
Joe Onoratocb109a02011-01-18 17:57:41 -08002721 */
Tor Norbye80756e32015-03-02 09:39:27 -08002722 public Builder setLights(@ColorInt int argb, int onMs, int offMs) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002723 mN.ledARGB = argb;
2724 mN.ledOnMS = onMs;
2725 mN.ledOffMS = offMs;
Julia Reynolds10ee1fc2015-11-09 11:04:55 -05002726 if (onMs != 0 || offMs != 0) {
2727 mN.flags |= FLAG_SHOW_LIGHTS;
2728 }
Joe Onorato46439ce2010-11-19 13:56:21 -08002729 return this;
2730 }
2731
Joe Onoratocb109a02011-01-18 17:57:41 -08002732 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002733 * Set whether this is an "ongoing" notification.
Joe Onoratocb109a02011-01-18 17:57:41 -08002734 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002735
2736 * Ongoing notifications cannot be dismissed by the user, so your application or service
2737 * must take care of canceling them.
2738 *
2739
2740 * They are typically used to indicate a background task that the user is actively engaged
2741 * with (e.g., playing music) or is pending in some way and therefore occupying the device
2742 * (e.g., a file download, sync operation, active network connection).
2743 *
2744
2745 * @see Notification#FLAG_ONGOING_EVENT
2746 * @see Service#setForeground(boolean)
Joe Onoratocb109a02011-01-18 17:57:41 -08002747 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002748 public Builder setOngoing(boolean ongoing) {
2749 setFlag(FLAG_ONGOING_EVENT, ongoing);
2750 return this;
2751 }
2752
Joe Onoratocb109a02011-01-18 17:57:41 -08002753 /**
2754 * Set this flag if you would only like the sound, vibrate
2755 * and ticker to be played if the notification is not already showing.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002756 *
2757 * @see Notification#FLAG_ONLY_ALERT_ONCE
Joe Onoratocb109a02011-01-18 17:57:41 -08002758 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002759 public Builder setOnlyAlertOnce(boolean onlyAlertOnce) {
2760 setFlag(FLAG_ONLY_ALERT_ONCE, onlyAlertOnce);
2761 return this;
2762 }
2763
Joe Onoratocb109a02011-01-18 17:57:41 -08002764 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002765 * Make this notification automatically dismissed when the user touches it. The
2766 * PendingIntent set with {@link #setDeleteIntent} will be sent when this happens.
2767 *
2768 * @see Notification#FLAG_AUTO_CANCEL
Joe Onoratocb109a02011-01-18 17:57:41 -08002769 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002770 public Builder setAutoCancel(boolean autoCancel) {
Joe Onorato281d83f2011-01-04 17:13:10 -08002771 setFlag(FLAG_AUTO_CANCEL, autoCancel);
Joe Onorato46439ce2010-11-19 13:56:21 -08002772 return this;
2773 }
2774
Joe Onoratocb109a02011-01-18 17:57:41 -08002775 /**
Griff Hazendfcb0802014-02-11 12:00:00 -08002776 * Set whether or not this notification should not bridge to other devices.
2777 *
2778 * <p>Some notifications can be bridged to other devices for remote display.
2779 * This hint can be set to recommend this notification not be bridged.
2780 */
2781 public Builder setLocalOnly(boolean localOnly) {
2782 setFlag(FLAG_LOCAL_ONLY, localOnly);
2783 return this;
2784 }
2785
2786 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002787 * Set which notification properties will be inherited from system defaults.
Joe Onoratocb109a02011-01-18 17:57:41 -08002788 * <p>
2789 * The value should be one or more of the following fields combined with
2790 * bitwise-or:
2791 * {@link #DEFAULT_SOUND}, {@link #DEFAULT_VIBRATE}, {@link #DEFAULT_LIGHTS}.
2792 * <p>
2793 * For all default values, use {@link #DEFAULT_ALL}.
2794 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002795 public Builder setDefaults(int defaults) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002796 mN.defaults = defaults;
Joe Onorato46439ce2010-11-19 13:56:21 -08002797 return this;
2798 }
2799
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002800 /**
2801 * Set the priority of this notification.
2802 *
2803 * @see Notification#priority
2804 */
Tor Norbyed9273d62013-05-30 15:59:53 -07002805 public Builder setPriority(@Priority int pri) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002806 mN.priority = pri;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002807 return this;
2808 }
Joe Malin8d40d042012-11-05 11:36:40 -08002809
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002810 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -04002811 * Set the notification category.
Joe Malin8d40d042012-11-05 11:36:40 -08002812 *
John Spurlockfd7f1e02014-03-18 16:41:57 -04002813 * @see Notification#category
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002814 */
John Spurlockfd7f1e02014-03-18 16:41:57 -04002815 public Builder setCategory(String category) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002816 mN.category = category;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002817 return this;
2818 }
2819
2820 /**
Chris Wrendde75302014-03-26 17:24:15 -04002821 * Add a person that is relevant to this notification.
2822 *
Chris Wrene6c48932014-09-29 17:19:27 -04002823 * <P>
2824 * Depending on user preferences, this annotation may allow the notification to pass
2825 * through interruption filters, and to appear more prominently in the user interface.
2826 * </P>
2827 *
2828 * <P>
2829 * The person should be specified by the {@code String} representation of a
2830 * {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI}.
2831 * </P>
2832 *
2833 * <P>The system will also attempt to resolve {@code mailto:} and {@code tel:} schema
2834 * URIs. The path part of these URIs must exist in the contacts database, in the
2835 * appropriate column, or the reference will be discarded as invalid. Telephone schema
2836 * URIs will be resolved by {@link android.provider.ContactsContract.PhoneLookup}.
2837 * </P>
2838 *
2839 * @param uri A URI for the person.
Chris Wrendde75302014-03-26 17:24:15 -04002840 * @see Notification#EXTRA_PEOPLE
2841 */
Chris Wrene6c48932014-09-29 17:19:27 -04002842 public Builder addPerson(String uri) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002843 mPersonList.add(uri);
Chris Wrendde75302014-03-26 17:24:15 -04002844 return this;
2845 }
2846
2847 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002848 * Set this notification to be part of a group of notifications sharing the same key.
2849 * Grouped notifications may display in a cluster or stack on devices which
2850 * support such rendering.
2851 *
2852 * <p>To make this notification the summary for its group, also call
2853 * {@link #setGroupSummary}. A sort order can be specified for group members by using
2854 * {@link #setSortKey}.
2855 * @param groupKey The group key of the group.
2856 * @return this object for method chaining
2857 */
2858 public Builder setGroup(String groupKey) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002859 mN.mGroupKey = groupKey;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002860 return this;
2861 }
2862
2863 /**
2864 * Set this notification to be the group summary for a group of notifications.
2865 * Grouped notifications may display in a cluster or stack on devices which
2866 * support such rendering. Requires a group key also be set using {@link #setGroup}.
2867 * @param isGroupSummary Whether this notification should be a group summary.
2868 * @return this object for method chaining
2869 */
2870 public Builder setGroupSummary(boolean isGroupSummary) {
2871 setFlag(FLAG_GROUP_SUMMARY, isGroupSummary);
2872 return this;
2873 }
2874
2875 /**
2876 * Set a sort key that orders this notification among other notifications from the
2877 * same package. This can be useful if an external sort was already applied and an app
2878 * would like to preserve this. Notifications will be sorted lexicographically using this
2879 * value, although providing different priorities in addition to providing sort key may
2880 * cause this value to be ignored.
2881 *
2882 * <p>This sort key can also be used to order members of a notification group. See
Griff Hazen9e1379f2014-05-20 12:50:51 -07002883 * {@link #setGroup}.
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002884 *
2885 * @see String#compareTo(String)
2886 */
2887 public Builder setSortKey(String sortKey) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002888 mN.mSortKey = sortKey;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002889 return this;
2890 }
2891
2892 /**
Griff Hazen720042b2014-02-24 15:46:56 -08002893 * Merge additional metadata into this notification.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002894 *
Griff Hazen720042b2014-02-24 15:46:56 -08002895 * <p>Values within the Bundle will replace existing extras values in this Builder.
2896 *
2897 * @see Notification#extras
2898 */
Griff Hazen959591e2014-05-15 22:26:18 -07002899 public Builder addExtras(Bundle extras) {
2900 if (extras != null) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002901 mUserExtras.putAll(extras);
Griff Hazen720042b2014-02-24 15:46:56 -08002902 }
2903 return this;
2904 }
2905
2906 /**
2907 * Set metadata for this notification.
2908 *
2909 * <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 -04002910 * current contents are copied into the Notification each time {@link #build()} is
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002911 * called.
2912 *
Griff Hazen720042b2014-02-24 15:46:56 -08002913 * <p>Replaces any existing extras values with those from the provided Bundle.
2914 * Use {@link #addExtras} to merge in metadata instead.
2915 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002916 * @see Notification#extras
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002917 */
Griff Hazen959591e2014-05-15 22:26:18 -07002918 public Builder setExtras(Bundle extras) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002919 if (extras != null) {
2920 mUserExtras = extras;
2921 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002922 return this;
2923 }
2924
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002925 /**
Griff Hazen720042b2014-02-24 15:46:56 -08002926 * Get the current metadata Bundle used by this notification Builder.
2927 *
2928 * <p>The returned Bundle is shared with this Builder.
2929 *
2930 * <p>The current contents of this Bundle are copied into the Notification each time
2931 * {@link #build()} is called.
2932 *
2933 * @see Notification#extras
2934 */
2935 public Bundle getExtras() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002936 return mUserExtras;
2937 }
2938
2939 private Bundle getAllExtras() {
2940 final Bundle saveExtras = (Bundle) mUserExtras.clone();
2941 saveExtras.putAll(mN.extras);
2942 return saveExtras;
Griff Hazen720042b2014-02-24 15:46:56 -08002943 }
2944
2945 /**
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002946 * Add an action to this notification. Actions are typically displayed by
2947 * the system as a button adjacent to the notification content.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04002948 * <p>
2949 * Every action must have an icon (32dp square and matching the
2950 * <a href="{@docRoot}design/style/iconography.html#action-bar">Holo
2951 * Dark action bar</a> visual style), a textual label, and a {@link PendingIntent}.
2952 * <p>
2953 * A notification in its expanded form can display up to 3 actions, from left to right in
2954 * the order they were added. Actions will not be displayed when the notification is
2955 * collapsed, however, so be sure that any essential functions may be accessed by the user
2956 * in some other way (for example, in the Activity pointed to by {@link #contentIntent}).
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002957 *
2958 * @param icon Resource ID of a drawable that represents the action.
2959 * @param title Text describing the action.
2960 * @param intent PendingIntent to be fired when the action is invoked.
Dan Sandler86647982015-05-13 23:41:13 -04002961 *
2962 * @deprecated Use {@link #addAction(Action)} instead.
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002963 */
Dan Sandler86647982015-05-13 23:41:13 -04002964 @Deprecated
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002965 public Builder addAction(int icon, CharSequence title, PendingIntent intent) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04002966 mActions.add(new Action(icon, safeCharSequence(title), intent));
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002967 return this;
2968 }
2969
Chris Wrenfbd96ba2012-05-01 12:03:58 -04002970 /**
Griff Hazen959591e2014-05-15 22:26:18 -07002971 * Add an action to this notification. Actions are typically displayed by
2972 * the system as a button adjacent to the notification content.
2973 * <p>
2974 * Every action must have an icon (32dp square and matching the
2975 * <a href="{@docRoot}design/style/iconography.html#action-bar">Holo
2976 * Dark action bar</a> visual style), a textual label, and a {@link PendingIntent}.
2977 * <p>
2978 * A notification in its expanded form can display up to 3 actions, from left to right in
2979 * the order they were added. Actions will not be displayed when the notification is
2980 * collapsed, however, so be sure that any essential functions may be accessed by the user
2981 * in some other way (for example, in the Activity pointed to by {@link #contentIntent}).
2982 *
2983 * @param action The action to add.
2984 */
2985 public Builder addAction(Action action) {
2986 mActions.add(action);
2987 return this;
2988 }
2989
2990 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002991 * Alter the complete list of actions attached to this notification.
2992 * @see #addAction(Action).
2993 *
2994 * @param actions
2995 * @return
2996 */
2997 public Builder setActions(Action... actions) {
2998 mActions.clear();
2999 for (int i = 0; i < actions.length; i++) {
3000 mActions.add(actions[i]);
3001 }
3002 return this;
3003 }
3004
3005 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003006 * Add a rich notification style to be applied at build time.
3007 *
3008 * @param style Object responsible for modifying the notification style.
3009 */
3010 public Builder setStyle(Style style) {
3011 if (mStyle != style) {
3012 mStyle = style;
Daniel Sandlerc08dea22012-06-28 08:35:24 -07003013 if (mStyle != null) {
3014 mStyle.setBuilder(this);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003015 mN.extras.putString(EXTRA_TEMPLATE, style.getClass().getName());
3016 } else {
3017 mN.extras.remove(EXTRA_TEMPLATE);
Daniel Sandlerc08dea22012-06-28 08:35:24 -07003018 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003019 }
3020 return this;
3021 }
3022
Dan Sandler0bf2ed82013-12-21 23:33:41 -06003023 /**
3024 * Specify the value of {@link #visibility}.
Griff Hazenb720abe2014-05-20 13:15:30 -07003025 *
Dan Sandler0bf2ed82013-12-21 23:33:41 -06003026 * @param visibility One of {@link #VISIBILITY_PRIVATE} (the default),
3027 * {@link #VISIBILITY_SECRET}, or {@link #VISIBILITY_PUBLIC}.
3028 *
3029 * @return The same Builder.
3030 */
3031 public Builder setVisibility(int visibility) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003032 mN.visibility = visibility;
Dan Sandler0bf2ed82013-12-21 23:33:41 -06003033 return this;
3034 }
3035
3036 /**
3037 * Supply a replacement Notification whose contents should be shown in insecure contexts
3038 * (i.e. atop the secure lockscreen). See {@link #visibility} and {@link #VISIBILITY_PUBLIC}.
3039 * @param n A replacement notification, presumably with some or all info redacted.
3040 * @return The same Builder.
3041 */
3042 public Builder setPublicVersion(Notification n) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003043 if (n != null) {
3044 mN.publicVersion = new Notification();
3045 n.cloneInto(mN.publicVersion, /*heavy=*/ true);
3046 } else {
3047 mN.publicVersion = null;
3048 }
Dan Sandler0bf2ed82013-12-21 23:33:41 -06003049 return this;
3050 }
3051
Griff Hazenb720abe2014-05-20 13:15:30 -07003052 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003053 * Apply an extender to this notification builder. Extenders may be used to add
3054 * metadata or change options on this builder.
3055 */
Griff Hazen61a9e862014-05-22 16:05:19 -07003056 public Builder extend(Extender extender) {
3057 extender.extend(this);
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003058 return this;
3059 }
3060
Dan Sandler4e787062015-06-17 15:09:48 -04003061 /**
3062 * @hide
3063 */
Julia Reynoldse46bb372016-03-17 11:05:58 -04003064 public Builder setFlag(int mask, boolean value) {
Joe Onorato46439ce2010-11-19 13:56:21 -08003065 if (value) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003066 mN.flags |= mask;
Joe Onorato46439ce2010-11-19 13:56:21 -08003067 } else {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003068 mN.flags &= ~mask;
Joe Onorato46439ce2010-11-19 13:56:21 -08003069 }
Julia Reynoldse46bb372016-03-17 11:05:58 -04003070 return this;
Joe Onorato46439ce2010-11-19 13:56:21 -08003071 }
3072
Dan Sandler26e81cf2014-05-06 10:01:27 -04003073 /**
3074 * Sets {@link Notification#color}.
3075 *
3076 * @param argb The accent color to use
3077 *
3078 * @return The same Builder.
3079 */
Tor Norbye80756e32015-03-02 09:39:27 -08003080 public Builder setColor(@ColorInt int argb) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003081 mN.color = argb;
Julia Reynolds10ee1fc2015-11-09 11:04:55 -05003082 sanitizeColor();
Dan Sandler26e81cf2014-05-06 10:01:27 -04003083 return this;
3084 }
3085
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003086 private Drawable getProfileBadgeDrawable() {
Christoph Studer7ac80e62014-08-04 16:01:57 +02003087 // Note: This assumes that the current user can read the profile badge of the
3088 // originating user.
Selim Cineke6ff9462016-01-15 15:07:06 -08003089 return mContext.getPackageManager().getUserBadgeForDensityNoBackground(
Julia Reynoldsda303542015-11-23 14:00:20 -05003090 new UserHandle(mContext.getUserId()), 0);
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003091 }
3092
3093 private Bitmap getProfileBadge() {
3094 Drawable badge = getProfileBadgeDrawable();
Kenny Guy8a0101b2014-05-08 23:34:12 +01003095 if (badge == null) {
3096 return null;
3097 }
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003098 final int size = mContext.getResources().getDimensionPixelSize(
3099 R.dimen.notification_badge_size);
3100 Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
Kenny Guy8a0101b2014-05-08 23:34:12 +01003101 Canvas canvas = new Canvas(bitmap);
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003102 badge.setBounds(0, 0, size, size);
Kenny Guy8a0101b2014-05-08 23:34:12 +01003103 badge.draw(canvas);
3104 return bitmap;
3105 }
3106
Selim Cinekc848c3a2016-01-13 15:27:30 -08003107 private void bindProfileBadge(RemoteViews contentView) {
Kenny Guy98193ea2014-07-24 19:54:37 +01003108 Bitmap profileBadge = getProfileBadge();
3109
Kenny Guy98193ea2014-07-24 19:54:37 +01003110 if (profileBadge != null) {
Selim Cinekc848c3a2016-01-13 15:27:30 -08003111 contentView.setImageViewBitmap(R.id.profile_badge, profileBadge);
3112 contentView.setViewVisibility(R.id.profile_badge, View.VISIBLE);
Kenny Guy98193ea2014-07-24 19:54:37 +01003113 }
Kenny Guy98193ea2014-07-24 19:54:37 +01003114 }
3115
Christoph Studerfe718432014-09-01 18:21:18 +02003116 private void resetStandardTemplate(RemoteViews contentView) {
Selim Cinekeaa29ca2015-11-23 13:51:13 -08003117 resetNotificationHeader(contentView);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003118 resetContentMargins(contentView);
Christoph Studerfe718432014-09-01 18:21:18 +02003119 contentView.setViewVisibility(R.id.right_icon, View.GONE);
Selim Cinek860b6da2015-12-16 19:02:19 -08003120 contentView.setViewVisibility(R.id.title, View.GONE);
Christoph Studerfe718432014-09-01 18:21:18 +02003121 contentView.setTextViewText(R.id.title, null);
Selim Cinek41598732016-01-11 16:58:37 -08003122 contentView.setViewVisibility(R.id.text, View.GONE);
Christoph Studerfe718432014-09-01 18:21:18 +02003123 contentView.setTextViewText(R.id.text, null);
Selim Cinek29603462015-11-17 19:04:39 -08003124 contentView.setViewVisibility(R.id.text_line_1, View.GONE);
Selim Cinek41598732016-01-11 16:58:37 -08003125 contentView.setTextViewText(R.id.text_line_1, null);
Christoph Studerfe718432014-09-01 18:21:18 +02003126 contentView.setViewVisibility(R.id.progress, View.GONE);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003127 }
3128
Selim Cinekeaa29ca2015-11-23 13:51:13 -08003129 /**
3130 * Resets the notification header to its original state
3131 */
3132 private void resetNotificationHeader(RemoteViews contentView) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003133 contentView.setImageViewResource(R.id.icon, 0);
Selim Cinek7b836392015-12-04 20:02:59 -08003134 contentView.setBoolean(R.id.notification_header, "setExpanded", false);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003135 contentView.setTextViewText(R.id.app_name_text, null);
Christoph Studerca1db712014-09-10 17:31:33 +02003136 contentView.setViewVisibility(R.id.chronometer, View.GONE);
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07003137 contentView.setViewVisibility(R.id.header_text, View.GONE);
3138 contentView.setViewVisibility(R.id.header_text_divider, View.GONE);
Selim Cinek29603462015-11-17 19:04:39 -08003139 contentView.setViewVisibility(R.id.time_divider, View.GONE);
Selim Cinekb85f36fd2016-04-20 18:46:36 -07003140 contentView.setViewVisibility(R.id.time, View.GONE);
Selim Cinekc848c3a2016-01-13 15:27:30 -08003141 contentView.setImageViewIcon(R.id.profile_badge, null);
3142 contentView.setViewVisibility(R.id.profile_badge, View.GONE);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003143 }
3144
3145 private void resetContentMargins(RemoteViews contentView) {
3146 contentView.setViewLayoutMarginEnd(R.id.line1, 0);
Selim Cinekc848c3a2016-01-13 15:27:30 -08003147 contentView.setViewLayoutMarginEnd(R.id.text, 0);
Christoph Studerfe718432014-09-01 18:21:18 +02003148 }
3149
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003150 private RemoteViews applyStandardTemplate(int resId) {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02003151 return applyStandardTemplate(resId, true /* hasProgress */);
3152 }
3153
3154 /**
3155 * @param hasProgress whether the progress bar should be shown and set
3156 */
3157 private RemoteViews applyStandardTemplate(int resId, boolean hasProgress) {
Adrian Roosc1a80b02016-04-05 14:54:55 -07003158 final Bundle ex = mN.extras;
3159
3160 CharSequence title = processLegacyText(ex.getCharSequence(EXTRA_TITLE));
3161 CharSequence text = processLegacyText(ex.getCharSequence(EXTRA_TEXT));
3162 return applyStandardTemplate(resId, hasProgress, title, text);
3163 }
3164
3165 /**
3166 * @param hasProgress whether the progress bar should be shown and set
3167 */
3168 private RemoteViews applyStandardTemplate(int resId, boolean hasProgress,
3169 CharSequence title, CharSequence text) {
Kenny Guy77320062014-08-27 21:37:15 +01003170 RemoteViews contentView = new BuilderRemoteViews(mContext.getApplicationInfo(), resId);
Dan Sandler539aad42014-08-04 00:43:39 -04003171
Christoph Studerfe718432014-09-01 18:21:18 +02003172 resetStandardTemplate(contentView);
3173
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003174 final Bundle ex = mN.extras;
Dan Sandler190d58d2014-05-15 09:33:39 -04003175
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003176 bindNotificationHeader(contentView);
3177 bindLargeIcon(contentView);
Adrian Roosc1a80b02016-04-05 14:54:55 -07003178 if (title != null) {
Selim Cinek860b6da2015-12-16 19:02:19 -08003179 contentView.setViewVisibility(R.id.title, View.VISIBLE);
Adrian Roosc1a80b02016-04-05 14:54:55 -07003180 contentView.setTextViewText(R.id.title, title);
Joe Onorato561d3852010-11-20 18:09:34 -08003181 }
Selim Cinek29603462015-11-17 19:04:39 -08003182 boolean showProgress = handleProgressBar(hasProgress, contentView, ex);
Adrian Roosc1a80b02016-04-05 14:54:55 -07003183 if (text != null) {
Selim Cinek41598732016-01-11 16:58:37 -08003184 int textId = showProgress ? com.android.internal.R.id.text_line_1
3185 : com.android.internal.R.id.text;
Adrian Roosc1a80b02016-04-05 14:54:55 -07003186 contentView.setTextViewText(textId, text);
Selim Cinek41598732016-01-11 16:58:37 -08003187 contentView.setViewVisibility(textId, View.VISIBLE);
Joe Onorato561d3852010-11-20 18:09:34 -08003188 }
Selim Cinekc848c3a2016-01-13 15:27:30 -08003189
Selim Cinek860b6da2015-12-16 19:02:19 -08003190 setContentMinHeight(contentView, showProgress || mN.mLargeIcon != null);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003191
Selim Cinek29603462015-11-17 19:04:39 -08003192 return contentView;
3193 }
3194
Selim Cinek860b6da2015-12-16 19:02:19 -08003195 /**
3196 * @param remoteView the remote view to update the minheight in
3197 * @param hasMinHeight does it have a mimHeight
3198 * @hide
3199 */
3200 void setContentMinHeight(RemoteViews remoteView, boolean hasMinHeight) {
3201 int minHeight = 0;
3202 if (hasMinHeight) {
3203 // we need to set the minHeight of the notification
3204 minHeight = mContext.getResources().getDimensionPixelSize(
3205 com.android.internal.R.dimen.notification_min_content_height);
3206 }
3207 remoteView.setInt(R.id.notification_main_column, "setMinimumHeight", minHeight);
3208 }
3209
Selim Cinek29603462015-11-17 19:04:39 -08003210 private boolean handleProgressBar(boolean hasProgress, RemoteViews contentView, Bundle ex) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003211 final int max = ex.getInt(EXTRA_PROGRESS_MAX, 0);
3212 final int progress = ex.getInt(EXTRA_PROGRESS, 0);
3213 final boolean ind = ex.getBoolean(EXTRA_PROGRESS_INDETERMINATE);
3214 if (hasProgress && (max != 0 || ind)) {
Selim Cinek29603462015-11-17 19:04:39 -08003215 contentView.setViewVisibility(com.android.internal.R.id.progress, View.VISIBLE);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003216 contentView.setProgressBar(
Selim Cinek29603462015-11-17 19:04:39 -08003217 R.id.progress, max, progress, ind);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003218 contentView.setProgressBackgroundTintList(
3219 R.id.progress, ColorStateList.valueOf(mContext.getColor(
3220 R.color.notification_progress_background_color)));
Selim Cinek29603462015-11-17 19:04:39 -08003221 if (mN.color != COLOR_DEFAULT) {
Adrian Roos4ff3b122016-02-01 12:26:13 -08003222 ColorStateList colorStateList = ColorStateList.valueOf(resolveContrastColor());
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003223 contentView.setProgressTintList(R.id.progress, colorStateList);
3224 contentView.setProgressIndeterminateTintList(R.id.progress, colorStateList);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003225 }
Selim Cinek29603462015-11-17 19:04:39 -08003226 return true;
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003227 } else {
3228 contentView.setViewVisibility(R.id.progress, View.GONE);
Selim Cinek29603462015-11-17 19:04:39 -08003229 return false;
Jeff Sharkey1c400132011-08-05 14:50:13 -07003230 }
Joe Onorato561d3852010-11-20 18:09:34 -08003231 }
3232
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003233 private void bindLargeIcon(RemoteViews contentView) {
3234 if (mN.mLargeIcon != null) {
3235 contentView.setViewVisibility(R.id.right_icon, View.VISIBLE);
3236 contentView.setImageViewIcon(R.id.right_icon, mN.mLargeIcon);
3237 processLargeLegacyIcon(mN.mLargeIcon, contentView);
3238 int endMargin = mContext.getResources().getDimensionPixelSize(
3239 R.dimen.notification_content_picture_margin);
3240 contentView.setViewLayoutMarginEnd(R.id.line1, endMargin);
Selim Cinekc848c3a2016-01-13 15:27:30 -08003241 contentView.setViewLayoutMarginEnd(R.id.text, endMargin);
Selim Cinek29603462015-11-17 19:04:39 -08003242 contentView.setViewLayoutMarginEnd(R.id.progress, endMargin);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003243 }
3244 }
3245
3246 private void bindNotificationHeader(RemoteViews contentView) {
3247 bindSmallIcon(contentView);
3248 bindHeaderAppName(contentView);
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07003249 bindHeaderText(contentView);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003250 bindHeaderChronometerAndTime(contentView);
3251 bindExpandButton(contentView);
Selim Cinekc848c3a2016-01-13 15:27:30 -08003252 bindProfileBadge(contentView);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003253 }
3254
3255 private void bindExpandButton(RemoteViews contentView) {
Adrian Roos4ff3b122016-02-01 12:26:13 -08003256 contentView.setDrawableParameters(R.id.expand_button, false, -1, resolveContrastColor(),
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003257 PorterDuff.Mode.SRC_ATOP, -1);
Selim Cinekea4bef72015-12-02 15:51:10 -08003258 contentView.setInt(R.id.notification_header, "setOriginalNotificationColor",
Adrian Roos4ff3b122016-02-01 12:26:13 -08003259 resolveContrastColor());
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003260 }
3261
3262 private void bindHeaderChronometerAndTime(RemoteViews contentView) {
3263 if (showsTimeOrChronometer()) {
Selim Cinek29603462015-11-17 19:04:39 -08003264 contentView.setViewVisibility(R.id.time_divider, View.VISIBLE);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003265 if (mN.extras.getBoolean(EXTRA_SHOW_CHRONOMETER)) {
3266 contentView.setViewVisibility(R.id.chronometer, View.VISIBLE);
3267 contentView.setLong(R.id.chronometer, "setBase",
3268 mN.when + (SystemClock.elapsedRealtime() - System.currentTimeMillis()));
3269 contentView.setBoolean(R.id.chronometer, "setStarted", true);
Selim Cinek81c23aa2016-02-25 16:23:13 -08003270 boolean countsDown = mN.extras.getBoolean(EXTRA_CHRONOMETER_COUNTS_DOWN);
Selim Cinekc3b752e2016-04-20 16:13:59 -07003271 contentView.setChronometerCountDown(R.id.chronometer, countsDown);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003272 } else {
3273 contentView.setViewVisibility(R.id.time, View.VISIBLE);
3274 contentView.setLong(R.id.time, "setTime", mN.when);
3275 }
Selim Cinekb85f36fd2016-04-20 18:46:36 -07003276 } else {
3277 // We still want a time to be set but gone, such that we can show and hide it
3278 // on demand in case it's a child notification without anything in the header
3279 contentView.setLong(R.id.time, "setTime", mN.when != 0 ? mN.when : mN.creationTime);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003280 }
3281 }
3282
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07003283 private void bindHeaderText(RemoteViews contentView) {
3284 CharSequence headerText = mN.extras.getCharSequence(EXTRA_SUB_TEXT);
3285 if (headerText == null && mStyle != null && mStyle.mSummaryTextSet
Selim Cinek03d0d652015-11-13 13:18:09 -05003286 && mStyle.hasSummaryInHeader()) {
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07003287 headerText = mStyle.mSummaryText;
Selim Cinek03d0d652015-11-13 13:18:09 -05003288 }
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07003289 if (headerText == null
3290 && mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N
3291 && mN.extras.getCharSequence(EXTRA_INFO_TEXT) != null) {
3292 headerText = mN.extras.getCharSequence(EXTRA_INFO_TEXT);
3293 }
3294 if (headerText != null) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003295 // TODO: Remove the span entirely to only have the string with propper formating.
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07003296 contentView.setTextViewText(R.id.header_text, processLegacyText(headerText));
3297 contentView.setViewVisibility(R.id.header_text, View.VISIBLE);
3298 contentView.setViewVisibility(R.id.header_text_divider, View.VISIBLE);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003299 }
3300 }
3301
Adrian Rooseba05822016-04-22 17:09:27 -07003302 /**
3303 * @hide
3304 */
3305 public String loadHeaderAppName() {
Dan Sandler732bd6c2016-04-12 14:20:32 -04003306 CharSequence name = null;
3307 final PackageManager pm = mContext.getPackageManager();
3308 if (mN.extras.containsKey(EXTRA_SUBSTITUTE_APP_NAME)) {
3309 // only system packages which lump together a bunch of unrelated stuff
3310 // may substitute a different name to make the purpose of the
3311 // notification more clear. the correct package label should always
3312 // be accessible via SystemUI.
3313 final String pkg = mContext.getPackageName();
3314 final String subName = mN.extras.getString(EXTRA_SUBSTITUTE_APP_NAME);
3315 if (PackageManager.PERMISSION_GRANTED == pm.checkPermission(
3316 android.Manifest.permission.SUBSTITUTE_NOTIFICATION_APP_NAME, pkg)) {
3317 name = subName;
3318 } else {
3319 Log.w(TAG, "warning: pkg "
3320 + pkg + " attempting to substitute app name '" + subName
3321 + "' without holding perm "
3322 + android.Manifest.permission.SUBSTITUTE_NOTIFICATION_APP_NAME);
3323 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003324 }
Dan Sandler732bd6c2016-04-12 14:20:32 -04003325 if (TextUtils.isEmpty(name)) {
3326 name = pm.getApplicationLabel(mContext.getApplicationInfo());
3327 }
3328 if (TextUtils.isEmpty(name)) {
3329 // still nothing?
3330 return null;
3331 }
3332
3333 return String.valueOf(name);
3334 }
3335 private void bindHeaderAppName(RemoteViews contentView) {
3336 contentView.setTextViewText(R.id.app_name_text, loadHeaderAppName());
Adrian Roos4ff3b122016-02-01 12:26:13 -08003337 contentView.setTextColor(R.id.app_name_text, resolveContrastColor());
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003338 }
3339
3340 private void bindSmallIcon(RemoteViews contentView) {
3341 contentView.setImageViewIcon(R.id.icon, mN.mSmallIcon);
3342 processSmallIconColor(mN.mSmallIcon, contentView);
3343 }
3344
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003345 /**
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02003346 * @return true if the built notification will show the time or the chronometer; false
3347 * otherwise
3348 */
3349 private boolean showsTimeOrChronometer() {
Selim Cinekb85f36fd2016-04-20 18:46:36 -07003350 return mN.showsTimeOrChronometer();
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02003351 }
3352
Christoph Studerfe718432014-09-01 18:21:18 +02003353 private void resetStandardTemplateWithActions(RemoteViews big) {
Adrian Roos7052de52016-03-03 15:53:34 -08003354 big.setViewVisibility(R.id.actions_container, View.GONE);
Christoph Studerfe718432014-09-01 18:21:18 +02003355 big.setViewVisibility(R.id.actions, View.GONE);
Christoph Studerfe718432014-09-01 18:21:18 +02003356 big.removeAllViews(R.id.actions);
Adrian Roose458aa82015-12-08 16:17:19 -08003357
3358 big.setViewVisibility(R.id.notification_material_reply_container, View.GONE);
3359 big.setTextViewText(R.id.notification_material_reply_text_1, null);
3360
3361 big.setViewVisibility(R.id.notification_material_reply_text_2, View.GONE);
3362 big.setTextViewText(R.id.notification_material_reply_text_2, null);
3363 big.setViewVisibility(R.id.notification_material_reply_text_3, View.GONE);
3364 big.setTextViewText(R.id.notification_material_reply_text_3, null);
Christoph Studerfe718432014-09-01 18:21:18 +02003365 }
3366
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003367 private RemoteViews applyStandardTemplateWithActions(int layoutId) {
Adrian Roos48d746a2016-04-12 14:57:28 -07003368 final Bundle ex = mN.extras;
3369
3370 CharSequence title = processLegacyText(ex.getCharSequence(EXTRA_TITLE));
3371 CharSequence text = processLegacyText(ex.getCharSequence(EXTRA_TEXT));
3372 return applyStandardTemplateWithActions(layoutId, true /* hasProgress */, title, text);
3373 }
3374
3375 private RemoteViews applyStandardTemplateWithActions(int layoutId, boolean hasProgress,
3376 CharSequence title, CharSequence text) {
3377 RemoteViews big = applyStandardTemplate(layoutId, hasProgress, title, text);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003378
Christoph Studerfe718432014-09-01 18:21:18 +02003379 resetStandardTemplateWithActions(big);
3380
Adrian Roose458aa82015-12-08 16:17:19 -08003381 boolean validRemoteInput = false;
3382
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003383 int N = mActions.size();
3384 if (N > 0) {
Adrian Roos7052de52016-03-03 15:53:34 -08003385 big.setViewVisibility(R.id.actions_container, View.VISIBLE);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003386 big.setViewVisibility(R.id.actions, View.VISIBLE);
Daniel Sandler8680bf82012-05-15 16:52:52 -04003387 if (N>MAX_ACTION_BUTTONS) N=MAX_ACTION_BUTTONS;
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003388 for (int i=0; i<N; i++) {
Adrian Roose458aa82015-12-08 16:17:19 -08003389 Action action = mActions.get(i);
3390 validRemoteInput |= hasValidRemoteInput(action);
3391
3392 final RemoteViews button = generateActionButton(action);
Adrian Roos9b123cf2016-02-04 14:55:57 -08003393 if (i == N - 1) {
3394 button.setViewLayoutWidth(com.android.internal.R.id.action0,
3395 ViewGroup.LayoutParams.MATCH_PARENT);
3396 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003397 big.addView(R.id.actions, button);
3398 }
3399 }
Adrian Roose458aa82015-12-08 16:17:19 -08003400
3401 CharSequence[] replyText = mN.extras.getCharSequenceArray(EXTRA_REMOTE_INPUT_HISTORY);
3402 if (validRemoteInput && replyText != null
3403 && replyText.length > 0 && !TextUtils.isEmpty(replyText[0])) {
3404 big.setViewVisibility(R.id.notification_material_reply_container, View.VISIBLE);
3405 big.setTextViewText(R.id.notification_material_reply_text_1, replyText[0]);
3406
3407 if (replyText.length > 1 && !TextUtils.isEmpty(replyText[1])) {
3408 big.setViewVisibility(R.id.notification_material_reply_text_2, View.VISIBLE);
3409 big.setTextViewText(R.id.notification_material_reply_text_2, replyText[1]);
3410
3411 if (replyText.length > 2 && !TextUtils.isEmpty(replyText[2])) {
3412 big.setViewVisibility(
3413 R.id.notification_material_reply_text_3, View.VISIBLE);
3414 big.setTextViewText(R.id.notification_material_reply_text_3, replyText[2]);
3415 }
3416 }
3417 }
3418
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003419 return big;
3420 }
3421
Adrian Roose458aa82015-12-08 16:17:19 -08003422 private boolean hasValidRemoteInput(Action action) {
3423 if (TextUtils.isEmpty(action.title) || action.actionIntent == null) {
3424 // Weird actions
3425 return false;
3426 }
3427
3428 RemoteInput[] remoteInputs = action.getRemoteInputs();
3429 if (remoteInputs == null) {
3430 return false;
3431 }
3432
3433 for (RemoteInput r : remoteInputs) {
3434 CharSequence[] choices = r.getChoices();
3435 if (r.getAllowFreeFormInput() || (choices != null && choices.length != 0)) {
3436 return true;
3437 }
3438 }
3439 return false;
3440 }
3441
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003442 /**
3443 * Construct a RemoteViews for the final 1U notification layout. In order:
3444 * 1. Custom contentView from the caller
3445 * 2. Style's proposed content view
3446 * 3. Standard template view
3447 */
Julia Reynolds3b848122016-02-26 10:45:32 -05003448 public RemoteViews createContentView() {
Selim Cinek593610c2016-02-16 18:42:57 -08003449 if (mN.contentView != null && (mStyle == null || !mStyle.displayCustomViewInline())) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003450 return mN.contentView;
3451 } else if (mStyle != null) {
3452 final RemoteViews styleView = mStyle.makeContentView();
3453 if (styleView != null) {
3454 return styleView;
3455 }
Joe Onorato46439ce2010-11-19 13:56:21 -08003456 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003457 return applyStandardTemplate(getBaseLayoutResource());
Joe Onorato46439ce2010-11-19 13:56:21 -08003458 }
3459
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003460 /**
3461 * Construct a RemoteViews for the final big notification layout.
3462 */
Julia Reynolds3b848122016-02-26 10:45:32 -05003463 public RemoteViews createBigContentView() {
Selim Cinek850a8542015-11-11 11:48:36 -05003464 RemoteViews result = null;
Selim Cinek593610c2016-02-16 18:42:57 -08003465 if (mN.bigContentView != null
3466 && (mStyle == null || !mStyle.displayCustomViewInline())) {
Julia Reynolds089e3e42015-11-18 09:59:57 -05003467 return mN.bigContentView;
3468 } else if (mStyle != null) {
Selim Cinek850a8542015-11-11 11:48:36 -05003469 result = mStyle.makeBigContentView();
Selim Cinek90dcf6d2015-11-18 20:24:13 -08003470 hideLine1Text(result);
Selim Cinekcc10bfb2016-02-10 16:24:21 -08003471 } else if (mActions.size() != 0) {
3472 result = applyStandardTemplateWithActions(getBigBaseLayoutResource());
Selim Cinek850a8542015-11-11 11:48:36 -05003473 }
3474 adaptNotificationHeaderForBigContentView(result);
3475 return result;
3476 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003477
Selim Cinekeaa29ca2015-11-23 13:51:13 -08003478 /**
3479 * Construct a RemoteViews for the final notification header only
3480 *
3481 * @hide
3482 */
3483 public RemoteViews makeNotificationHeader() {
3484 RemoteViews header = new BuilderRemoteViews(mContext.getApplicationInfo(),
3485 R.layout.notification_template_header);
3486 resetNotificationHeader(header);
3487 bindNotificationHeader(header);
3488 return header;
3489 }
3490
Selim Cinek29603462015-11-17 19:04:39 -08003491 private void hideLine1Text(RemoteViews result) {
Selim Cinekcc10bfb2016-02-10 16:24:21 -08003492 if (result != null) {
3493 result.setViewVisibility(R.id.text_line_1, View.GONE);
3494 }
Selim Cinek29603462015-11-17 19:04:39 -08003495 }
3496
Selim Cinek850a8542015-11-11 11:48:36 -05003497 private void adaptNotificationHeaderForBigContentView(RemoteViews result) {
Selim Cinekcc10bfb2016-02-10 16:24:21 -08003498 if (result != null) {
3499 result.setBoolean(R.id.notification_header, "setExpanded", true);
3500 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003501 }
3502
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003503 /**
3504 * Construct a RemoteViews for the final heads-up notification layout.
3505 */
Julia Reynolds3b848122016-02-26 10:45:32 -05003506 public RemoteViews createHeadsUpContentView() {
Selim Cinek593610c2016-02-16 18:42:57 -08003507 if (mN.headsUpContentView != null
3508 && (mStyle == null || !mStyle.displayCustomViewInline())) {
Julia Reynolds089e3e42015-11-18 09:59:57 -05003509 return mN.headsUpContentView;
3510 } else if (mStyle != null) {
3511 final RemoteViews styleView = mStyle.makeHeadsUpContentView();
3512 if (styleView != null) {
3513 return styleView;
3514 }
3515 } else if (mActions.size() == 0) {
3516 return null;
3517 }
3518
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003519 return applyStandardTemplateWithActions(getBigBaseLayoutResource());
Chris Wren8fd39ec2014-02-27 17:43:26 -05003520 }
3521
Selim Cinek624c02db2015-12-14 21:00:02 -08003522 /**
3523 * Construct a RemoteViews for the display in public contexts like on the lockscreen.
3524 *
3525 * @hide
3526 */
3527 public RemoteViews makePublicContentView() {
3528 if (mN.publicVersion != null) {
3529 final Builder builder = recoverBuilder(mContext, mN.publicVersion);
Julia Reynolds3b848122016-02-26 10:45:32 -05003530 return builder.createContentView();
Selim Cinek624c02db2015-12-14 21:00:02 -08003531 }
3532 Bundle savedBundle = mN.extras;
3533 Style style = mStyle;
3534 mStyle = null;
3535 Icon largeIcon = mN.mLargeIcon;
3536 mN.mLargeIcon = null;
3537 Bundle publicExtras = new Bundle();
3538 publicExtras.putBoolean(EXTRA_SHOW_WHEN,
3539 savedBundle.getBoolean(EXTRA_SHOW_WHEN));
3540 publicExtras.putBoolean(EXTRA_SHOW_CHRONOMETER,
3541 savedBundle.getBoolean(EXTRA_SHOW_CHRONOMETER));
Selim Cinek81c23aa2016-02-25 16:23:13 -08003542 publicExtras.putBoolean(EXTRA_CHRONOMETER_COUNTS_DOWN,
3543 savedBundle.getBoolean(EXTRA_CHRONOMETER_COUNTS_DOWN));
Selim Cinek624c02db2015-12-14 21:00:02 -08003544 publicExtras.putCharSequence(EXTRA_TITLE,
3545 mContext.getString(R.string.notification_hidden_text));
3546 mN.extras = publicExtras;
3547 final RemoteViews publicView = applyStandardTemplate(getBaseLayoutResource());
3548 mN.extras = savedBundle;
3549 mN.mLargeIcon = largeIcon;
3550 mStyle = style;
3551 return publicView;
3552 }
3553
3554
Chris Wren8fd39ec2014-02-27 17:43:26 -05003555
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003556 private RemoteViews generateActionButton(Action action) {
Daniel Sandler8680bf82012-05-15 16:52:52 -04003557 final boolean tombstone = (action.actionIntent == null);
Selim Cinekf33b1112015-07-15 17:45:11 -07003558 RemoteViews button = new BuilderRemoteViews(mContext.getApplicationInfo(),
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003559 tombstone ? getActionTombstoneLayoutResource()
3560 : getActionLayoutResource());
Dan Sandler68079d52015-07-22 10:45:30 -04003561 final Icon ai = action.getIcon();
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003562 button.setTextViewText(R.id.action0, processLegacyText(action.title));
Daniel Sandler8680bf82012-05-15 16:52:52 -04003563 if (!tombstone) {
Daniel Sandlere5518842012-05-10 16:20:40 -04003564 button.setOnClickPendingIntent(R.id.action0, action.actionIntent);
Daniel Sandlere5518842012-05-10 16:20:40 -04003565 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003566 button.setContentDescription(R.id.action0, action.title);
Adrian Roosfe84e1f2015-11-04 15:55:39 -08003567 if (action.mRemoteInputs != null) {
3568 button.setRemoteInputs(R.id.action0, action.mRemoteInputs);
3569 }
3570 if (mN.color != COLOR_DEFAULT) {
Adrian Roos4ff3b122016-02-01 12:26:13 -08003571 button.setTextColor(R.id.action0, resolveContrastColor());
Adrian Roosfe84e1f2015-11-04 15:55:39 -08003572 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003573 return button;
3574 }
3575
Joe Onoratocb109a02011-01-18 17:57:41 -08003576 /**
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003577 * @return Whether we are currently building a notification from a legacy (an app that
Alan Viverette3cb07a462014-06-06 14:19:53 -07003578 * doesn't create material notifications by itself) app.
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003579 */
3580 private boolean isLegacy() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003581 return getColorUtil() != null;
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003582 }
3583
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003584 private CharSequence processLegacyText(CharSequence charSequence) {
3585 if (isLegacy()) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003586 return getColorUtil().invertCharSequenceColors(charSequence);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003587 } else {
3588 return charSequence;
3589 }
3590 }
3591
Dan Sandler26e81cf2014-05-06 10:01:27 -04003592 /**
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003593 * Apply any necessariy colors to the small icon
Dan Sandler26e81cf2014-05-06 10:01:27 -04003594 */
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003595 private void processSmallIconColor(Icon smallIcon, RemoteViews contentView) {
Selim Cinekea4bef72015-12-02 15:51:10 -08003596 boolean colorable = !isLegacy() || getColorUtil().isGrayscaleIcon(mContext, smallIcon);
3597 if (colorable) {
Adrian Roos4ff3b122016-02-01 12:26:13 -08003598 contentView.setDrawableParameters(R.id.icon, false, -1, resolveContrastColor(),
Jorim Jaggi92df1f22014-12-16 19:44:41 +01003599 PorterDuff.Mode.SRC_ATOP, -1);
Selim Cinekea4bef72015-12-02 15:51:10 -08003600
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003601 }
Selim Cinekea4bef72015-12-02 15:51:10 -08003602 contentView.setInt(R.id.notification_header, "setOriginalIconColor",
Adrian Roos4ff3b122016-02-01 12:26:13 -08003603 colorable ? resolveContrastColor() : NotificationHeaderView.NO_COLOR);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003604 }
3605
Dan Sandler26e81cf2014-05-06 10:01:27 -04003606 /**
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003607 * Make the largeIcon dark if it's a fake smallIcon (that is,
Dan Sandler26e81cf2014-05-06 10:01:27 -04003608 * if it's grayscale).
3609 */
3610 // TODO: also check bounds, transparency, that sort of thing.
Dan Sandlerd63f9322015-05-06 15:18:49 -04003611 private void processLargeLegacyIcon(Icon largeIcon, RemoteViews contentView) {
3612 if (largeIcon != null && isLegacy()
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003613 && getColorUtil().isGrayscaleIcon(mContext, largeIcon)) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003614 // resolve color will fall back to the default when legacy
Adrian Roos4ff3b122016-02-01 12:26:13 -08003615 contentView.setDrawableParameters(R.id.icon, false, -1, resolveContrastColor(),
Dan Sandler190d58d2014-05-15 09:33:39 -04003616 PorterDuff.Mode.SRC_ATOP, -1);
Jorim Jaggi92df1f22014-12-16 19:44:41 +01003617 }
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003618 }
3619
Julia Reynolds10ee1fc2015-11-09 11:04:55 -05003620 private void sanitizeColor() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003621 if (mN.color != COLOR_DEFAULT) {
3622 mN.color |= 0xFF000000; // no alpha for custom colors
Jorim Jaggi74419312014-06-10 20:57:21 +02003623 }
Jorim Jaggi74419312014-06-10 20:57:21 +02003624 }
3625
Adrian Roos4ff3b122016-02-01 12:26:13 -08003626 int resolveContrastColor() {
3627 if (mCachedContrastColorIsFor == mN.color && mCachedContrastColor != COLOR_INVALID) {
3628 return mCachedContrastColor;
Dan Sandler26e81cf2014-05-06 10:01:27 -04003629 }
Adrian Roos4ff3b122016-02-01 12:26:13 -08003630 final int contrasted = NotificationColorUtil.resolveContrastColor(mContext, mN.color);
3631
3632 mCachedContrastColorIsFor = mN.color;
3633 return mCachedContrastColor = contrasted;
Dan Sandler26e81cf2014-05-06 10:01:27 -04003634 }
3635
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003636 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003637 * Apply the unstyled operations and return a new {@link Notification} object.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04003638 * @hide
Joe Onoratocb109a02011-01-18 17:57:41 -08003639 */
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04003640 public Notification buildUnstyled() {
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003641 if (mActions.size() > 0) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003642 mN.actions = new Action[mActions.size()];
3643 mActions.toArray(mN.actions);
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003644 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003645 if (!mPersonList.isEmpty()) {
3646 mN.extras.putStringArray(EXTRA_PEOPLE,
3647 mPersonList.toArray(new String[mPersonList.size()]));
Dan Sandler0bf2ed82013-12-21 23:33:41 -06003648 }
Selim Cinek247fa012016-02-18 09:50:48 -08003649 if (mN.bigContentView != null || mN.contentView != null
3650 || mN.headsUpContentView != null) {
3651 mN.extras.putBoolean(EXTRA_CONTAINS_CUSTOM_VIEW, true);
3652 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003653 return mN;
Joe Onorato46439ce2010-11-19 13:56:21 -08003654 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003655
Julia Reynolds3b848122016-02-26 10:45:32 -05003656 /**
3657 * Creates a Builder from an existing notification so further changes can be made.
3658 * @param context The context for your application / activity.
3659 * @param n The notification to create a Builder from.
3660 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003661 public static Notification.Builder recoverBuilder(Context context, Notification n) {
Christoph Studer4600f9b2014-07-22 22:44:43 +02003662 // Re-create notification context so we can access app resources.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003663 ApplicationInfo applicationInfo = n.extras.getParcelable(
3664 EXTRA_BUILDER_APPLICATION_INFO);
Christoph Studer4600f9b2014-07-22 22:44:43 +02003665 Context builderContext;
Julia Reynoldsda303542015-11-23 14:00:20 -05003666 if (applicationInfo != null) {
3667 try {
3668 builderContext = context.createApplicationContext(applicationInfo,
3669 Context.CONTEXT_RESTRICTED);
3670 } catch (NameNotFoundException e) {
3671 Log.e(TAG, "ApplicationInfo " + applicationInfo + " not found");
3672 builderContext = context; // try with our context
3673 }
3674 } else {
3675 builderContext = context; // try with given context
Christoph Studer4600f9b2014-07-22 22:44:43 +02003676 }
3677
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003678 return new Builder(builderContext, n);
Christoph Studer4600f9b2014-07-22 22:44:43 +02003679 }
3680
3681 private static Class<? extends Style> getNotificationStyleClass(String templateClass) {
Selim Cinek593610c2016-02-16 18:42:57 -08003682 Class<? extends Style>[] classes = new Class[] {
3683 BigTextStyle.class, BigPictureStyle.class, InboxStyle.class, MediaStyle.class,
Alex Hillsfc737de2016-03-23 17:33:02 -04003684 DecoratedCustomViewStyle.class, DecoratedMediaCustomViewStyle.class,
3685 MessagingStyle.class };
Christoph Studer4600f9b2014-07-22 22:44:43 +02003686 for (Class<? extends Style> innerClass : classes) {
3687 if (templateClass.equals(innerClass.getName())) {
3688 return innerClass;
3689 }
3690 }
3691 return null;
3692 }
3693
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003694 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003695 * @deprecated Use {@link #build()} instead.
3696 */
3697 @Deprecated
3698 public Notification getNotification() {
3699 return build();
3700 }
3701
3702 /**
3703 * Combine all of the options that have been set and return a new {@link Notification}
3704 * object.
3705 */
3706 public Notification build() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003707 // first, add any extras from the calling code
3708 if (mUserExtras != null) {
3709 mN.extras = getAllExtras();
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07003710 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003711
Selim Cinekb85f36fd2016-04-20 18:46:36 -07003712 mN.creationTime = System.currentTimeMillis();
3713
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003714 // lazy stuff from mContext; see comment in Builder(Context, Notification)
Julia Reynoldsda303542015-11-23 14:00:20 -05003715 Notification.addFieldsFromContext(mContext, mN);
Christoph Studer943aa672014-08-03 20:31:16 +02003716
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003717 buildUnstyled();
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003718
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003719 if (mStyle != null) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003720 mStyle.buildStyled(mN);
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003721 }
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003722
Adrian Roos5081c0d2016-02-26 16:04:19 -08003723 if (mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N
3724 && (mStyle == null || !mStyle.displayCustomViewInline())) {
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05003725 if (mN.contentView == null) {
Julia Reynolds3b848122016-02-26 10:45:32 -05003726 mN.contentView = createContentView();
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05003727 mN.extras.putInt(EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT,
3728 mN.contentView.getSequenceNumber());
3729 }
3730 if (mN.bigContentView == null) {
Julia Reynolds3b848122016-02-26 10:45:32 -05003731 mN.bigContentView = createBigContentView();
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05003732 if (mN.bigContentView != null) {
3733 mN.extras.putInt(EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT,
3734 mN.bigContentView.getSequenceNumber());
3735 }
3736 }
3737 if (mN.headsUpContentView == null) {
Julia Reynolds3b848122016-02-26 10:45:32 -05003738 mN.headsUpContentView = createHeadsUpContentView();
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05003739 if (mN.headsUpContentView != null) {
3740 mN.extras.putInt(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT,
3741 mN.headsUpContentView.getSequenceNumber());
3742 }
3743 }
3744 }
3745
Julia Reynolds4c0c2022016-02-02 15:11:59 -05003746 if ((mN.defaults & DEFAULT_LIGHTS) != 0) {
3747 mN.flags |= FLAG_SHOW_LIGHTS;
3748 }
3749
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003750 return mN;
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003751 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05003752
3753 /**
3754 * Apply this Builder to an existing {@link Notification} object.
3755 *
3756 * @hide
3757 */
3758 public Notification buildInto(Notification n) {
Daniel Sandler1a497d32013-04-18 14:52:45 -04003759 build().cloneInto(n, true);
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05003760 return n;
3761 }
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003762
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05003763 /**
Adrian Roos184bfe022016-03-03 13:41:44 -08003764 * Removes RemoteViews that were created for compatibility from {@param n}, if they did not
3765 * change.
3766 *
3767 * @return {@param n}, if no stripping is needed, otherwise a stripped clone of {@param n}.
3768 *
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05003769 * @hide
3770 */
Adrian Roos184bfe022016-03-03 13:41:44 -08003771 public static Notification maybeCloneStrippedForDelivery(Notification n) {
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05003772 String templateClass = n.extras.getString(EXTRA_TEMPLATE);
Adrian Roos184bfe022016-03-03 13:41:44 -08003773
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05003774 // Only strip views for known Styles because we won't know how to
3775 // re-create them otherwise.
Adrian Roos184bfe022016-03-03 13:41:44 -08003776 if (!TextUtils.isEmpty(templateClass)
3777 && getNotificationStyleClass(templateClass) == null) {
3778 return n;
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05003779 }
Adrian Roos184bfe022016-03-03 13:41:44 -08003780
3781 // Only strip unmodified BuilderRemoteViews.
3782 boolean stripContentView = n.contentView instanceof BuilderRemoteViews &&
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05003783 n.extras.getInt(EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT, -1) ==
Adrian Roos184bfe022016-03-03 13:41:44 -08003784 n.contentView.getSequenceNumber();
3785 boolean stripBigContentView = n.bigContentView instanceof BuilderRemoteViews &&
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05003786 n.extras.getInt(EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT, -1) ==
Adrian Roos184bfe022016-03-03 13:41:44 -08003787 n.bigContentView.getSequenceNumber();
3788 boolean stripHeadsUpContentView = n.headsUpContentView instanceof BuilderRemoteViews &&
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05003789 n.extras.getInt(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT, -1) ==
Adrian Roos184bfe022016-03-03 13:41:44 -08003790 n.headsUpContentView.getSequenceNumber();
3791
3792 // Nothing to do here, no need to clone.
3793 if (!stripContentView && !stripBigContentView && !stripHeadsUpContentView) {
3794 return n;
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05003795 }
Adrian Roos184bfe022016-03-03 13:41:44 -08003796
3797 Notification clone = n.clone();
3798 if (stripContentView) {
3799 clone.contentView = null;
3800 clone.extras.remove(EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT);
3801 }
3802 if (stripBigContentView) {
3803 clone.bigContentView = null;
3804 clone.extras.remove(EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT);
3805 }
3806 if (stripHeadsUpContentView) {
3807 clone.headsUpContentView = null;
3808 clone.extras.remove(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT);
3809 }
3810 return clone;
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05003811 }
3812
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003813 private int getBaseLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07003814 return R.layout.notification_template_material_base;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003815 }
3816
3817 private int getBigBaseLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07003818 return R.layout.notification_template_material_big_base;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003819 }
3820
3821 private int getBigPictureLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07003822 return R.layout.notification_template_material_big_picture;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003823 }
3824
3825 private int getBigTextLayoutResource() {
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003826 return R.layout.notification_template_material_big_text;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003827 }
3828
3829 private int getInboxLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07003830 return R.layout.notification_template_material_inbox;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003831 }
3832
Adrian Roosc1a80b02016-04-05 14:54:55 -07003833 private int getMessagingLayoutResource() {
3834 return R.layout.notification_template_material_messaging;
3835 }
3836
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003837 private int getActionLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07003838 return R.layout.notification_material_action;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003839 }
3840
3841 private int getActionTombstoneLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07003842 return R.layout.notification_material_action_tombstone;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003843 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003844 }
3845
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003846 /**
Selim Cinekb85f36fd2016-04-20 18:46:36 -07003847 * @return true if the notification will show the time or the chronometer; false
3848 * otherwise
3849 * @hide
3850 */
3851 public boolean showsTimeOrChronometer() {
3852 return when != 0 && extras.getBoolean(EXTRA_SHOW_WHEN);
3853 }
3854
3855 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003856 * An object that can apply a rich notification style to a {@link Notification.Builder}
3857 * object.
3858 */
Griff Hazendfcb0802014-02-11 12:00:00 -08003859 public static abstract class Style {
Chris Wrend6297db2012-05-03 16:20:13 -04003860 private CharSequence mBigContentTitle;
Jorim Jaggi457a10d2014-09-08 16:18:23 +02003861
3862 /**
3863 * @hide
3864 */
3865 protected CharSequence mSummaryText = null;
3866
3867 /**
3868 * @hide
3869 */
3870 protected boolean mSummaryTextSet = false;
Chris Wrend6297db2012-05-03 16:20:13 -04003871
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003872 protected Builder mBuilder;
3873
Chris Wrend6297db2012-05-03 16:20:13 -04003874 /**
3875 * Overrides ContentTitle in the big form of the template.
3876 * This defaults to the value passed to setContentTitle().
3877 */
3878 protected void internalSetBigContentTitle(CharSequence title) {
3879 mBigContentTitle = title;
3880 }
3881
3882 /**
3883 * Set the first line of text after the detail section in the big form of the template.
3884 */
3885 protected void internalSetSummaryText(CharSequence cs) {
3886 mSummaryText = cs;
Daniel Sandler619738c2012-06-07 16:33:08 -04003887 mSummaryTextSet = true;
Chris Wrend6297db2012-05-03 16:20:13 -04003888 }
3889
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003890 public void setBuilder(Builder builder) {
3891 if (mBuilder != builder) {
3892 mBuilder = builder;
Daniel Sandlerc08dea22012-06-28 08:35:24 -07003893 if (mBuilder != null) {
3894 mBuilder.setStyle(this);
3895 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003896 }
3897 }
3898
Chris Wrend6297db2012-05-03 16:20:13 -04003899 protected void checkBuilder() {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003900 if (mBuilder == null) {
3901 throw new IllegalArgumentException("Style requires a valid Builder object");
3902 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003903 }
Chris Wrend6297db2012-05-03 16:20:13 -04003904
3905 protected RemoteViews getStandardView(int layoutId) {
3906 checkBuilder();
3907
Christoph Studer4600f9b2014-07-22 22:44:43 +02003908 // Nasty.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003909 CharSequence oldBuilderContentTitle =
3910 mBuilder.getAllExtras().getCharSequence(EXTRA_TITLE);
Chris Wrend6297db2012-05-03 16:20:13 -04003911 if (mBigContentTitle != null) {
3912 mBuilder.setContentTitle(mBigContentTitle);
3913 }
3914
Chris Wrend6297db2012-05-03 16:20:13 -04003915 RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(layoutId);
3916
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003917 mBuilder.getAllExtras().putCharSequence(EXTRA_TITLE, oldBuilderContentTitle);
Christoph Studer4600f9b2014-07-22 22:44:43 +02003918
Chris Wrend6297db2012-05-03 16:20:13 -04003919 if (mBigContentTitle != null && mBigContentTitle.equals("")) {
3920 contentView.setViewVisibility(R.id.line1, View.GONE);
Chris Wren67dc9a02012-05-16 01:03:20 -04003921 } else {
3922 contentView.setViewVisibility(R.id.line1, View.VISIBLE);
Chris Wrend6297db2012-05-03 16:20:13 -04003923 }
3924
Chris Wrend6297db2012-05-03 16:20:13 -04003925 return contentView;
3926 }
3927
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003928 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003929 * Construct a Style-specific RemoteViews for the final 1U notification layout.
3930 * The default implementation has nothing additional to add.
3931 * @hide
3932 */
3933 public RemoteViews makeContentView() {
3934 return null;
3935 }
3936
3937 /**
3938 * Construct a Style-specific RemoteViews for the final big notification layout.
3939 * @hide
3940 */
3941 public RemoteViews makeBigContentView() {
3942 return null;
3943 }
3944
3945 /**
3946 * Construct a Style-specific RemoteViews for the final HUN layout.
3947 * @hide
3948 */
3949 public RemoteViews makeHeadsUpContentView() {
3950 return null;
3951 }
3952
3953 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003954 * Apply any style-specific extras to this notification before shipping it out.
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003955 * @hide
3956 */
3957 public void addExtras(Bundle extras) {
3958 if (mSummaryTextSet) {
3959 extras.putCharSequence(EXTRA_SUMMARY_TEXT, mSummaryText);
3960 }
3961 if (mBigContentTitle != null) {
3962 extras.putCharSequence(EXTRA_TITLE_BIG, mBigContentTitle);
3963 }
Chris Wren91ad5632013-06-05 15:05:57 -04003964 extras.putString(EXTRA_TEMPLATE, this.getClass().getName());
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003965 }
3966
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04003967 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003968 * Reconstruct the internal state of this Style object from extras.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04003969 * @hide
3970 */
Christoph Studer4600f9b2014-07-22 22:44:43 +02003971 protected void restoreFromExtras(Bundle extras) {
3972 if (extras.containsKey(EXTRA_SUMMARY_TEXT)) {
3973 mSummaryText = extras.getCharSequence(EXTRA_SUMMARY_TEXT);
3974 mSummaryTextSet = true;
3975 }
3976 if (extras.containsKey(EXTRA_TITLE_BIG)) {
3977 mBigContentTitle = extras.getCharSequence(EXTRA_TITLE_BIG);
3978 }
3979 }
3980
3981
3982 /**
3983 * @hide
3984 */
3985 public Notification buildStyled(Notification wip) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003986 addExtras(wip.extras);
Christoph Studer4600f9b2014-07-22 22:44:43 +02003987 return wip;
3988 }
3989
Daniel Sandler0ec46202015-06-24 01:27:05 -04003990 /**
3991 * @hide
3992 */
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07003993 public void purgeResources() {}
3994
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04003995 /**
3996 * Calls {@link android.app.Notification.Builder#build()} on the Builder this Style is
3997 * attached to.
3998 *
3999 * @return the fully constructed Notification.
4000 */
4001 public Notification build() {
4002 checkBuilder();
4003 return mBuilder.build();
4004 }
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004005
4006 /**
4007 * @hide
4008 * @return true if the style positions the progress bar on the second line; false if the
4009 * style hides the progress bar
4010 */
4011 protected boolean hasProgress() {
4012 return true;
4013 }
Selim Cinek03d0d652015-11-13 13:18:09 -05004014
4015 /**
4016 * @hide
4017 * @return Whether we should put the summary be put into the notification header
4018 */
4019 public boolean hasSummaryInHeader() {
4020 return true;
4021 }
Selim Cinek593610c2016-02-16 18:42:57 -08004022
4023 /**
4024 * @hide
4025 * @return Whether custom content views are displayed inline in the style
4026 */
4027 public boolean displayCustomViewInline() {
4028 return false;
4029 }
Joe Onorato46439ce2010-11-19 13:56:21 -08004030 }
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004031
4032 /**
Daniel Sandler4dfbe832012-04-11 14:51:46 -04004033 * Helper class for generating large-format notifications that include a large image attachment.
Joe Malin8d40d042012-11-05 11:36:40 -08004034 *
Robert Ly91c5ce32014-06-08 15:37:00 -07004035 * Here's how you'd set the <code>BigPictureStyle</code> on a notification:
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004036 * <pre class="prettyprint">
Robert Ly91c5ce32014-06-08 15:37:00 -07004037 * Notification notif = new Notification.Builder(mContext)
4038 * .setContentTitle(&quot;New photo from &quot; + sender.toString())
4039 * .setContentText(subject)
4040 * .setSmallIcon(R.drawable.new_post)
4041 * .setLargeIcon(aBitmap)
4042 * .setStyle(new Notification.BigPictureStyle()
4043 * .bigPicture(aBigBitmap))
4044 * .build();
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004045 * </pre>
Joe Malin8d40d042012-11-05 11:36:40 -08004046 *
Daniel Sandler4dfbe832012-04-11 14:51:46 -04004047 * @see Notification#bigContentView
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004048 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004049 public static class BigPictureStyle extends Style {
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004050 private Bitmap mPicture;
Dan Sandlerd63f9322015-05-06 15:18:49 -04004051 private Icon mBigLargeIcon;
Chris Wren3745a3d2012-05-22 15:11:52 -04004052 private boolean mBigLargeIconSet = false;
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004053
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004054 public BigPictureStyle() {
4055 }
4056
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004057 public BigPictureStyle(Builder builder) {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004058 setBuilder(builder);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004059 }
4060
Chris Wrend6297db2012-05-03 16:20:13 -04004061 /**
4062 * Overrides ContentTitle in the big form of the template.
4063 * This defaults to the value passed to setContentTitle().
4064 */
4065 public BigPictureStyle setBigContentTitle(CharSequence title) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04004066 internalSetBigContentTitle(safeCharSequence(title));
Chris Wrend6297db2012-05-03 16:20:13 -04004067 return this;
4068 }
4069
4070 /**
4071 * Set the first line of text after the detail section in the big form of the template.
4072 */
4073 public BigPictureStyle setSummaryText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04004074 internalSetSummaryText(safeCharSequence(cs));
Chris Wrend6297db2012-05-03 16:20:13 -04004075 return this;
4076 }
4077
Chris Wren0bd664d2012-08-01 13:56:56 -04004078 /**
4079 * Provide the bitmap to be used as the payload for the BigPicture notification.
4080 */
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004081 public BigPictureStyle bigPicture(Bitmap b) {
4082 mPicture = b;
4083 return this;
4084 }
4085
Chris Wren3745a3d2012-05-22 15:11:52 -04004086 /**
Chris Wren3745a3d2012-05-22 15:11:52 -04004087 * Override the large icon when the big notification is shown.
4088 */
4089 public BigPictureStyle bigLargeIcon(Bitmap b) {
Dan Sandlerd63f9322015-05-06 15:18:49 -04004090 return bigLargeIcon(b != null ? Icon.createWithBitmap(b) : null);
4091 }
4092
4093 /**
4094 * Override the large icon when the big notification is shown.
4095 */
4096 public BigPictureStyle bigLargeIcon(Icon icon) {
Chris Wren3745a3d2012-05-22 15:11:52 -04004097 mBigLargeIconSet = true;
Dan Sandlerd63f9322015-05-06 15:18:49 -04004098 mBigLargeIcon = icon;
Chris Wren3745a3d2012-05-22 15:11:52 -04004099 return this;
4100 }
4101
Riley Andrews0394a0c2015-11-03 23:36:52 -08004102 /** @hide */
4103 public static final int MIN_ASHMEM_BITMAP_SIZE = 128 * (1 << 10);
4104
Daniel Sandler0ec46202015-06-24 01:27:05 -04004105 /**
4106 * @hide
4107 */
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07004108 @Override
4109 public void purgeResources() {
4110 super.purgeResources();
Riley Andrews8cee7c12015-11-01 23:36:04 -08004111 if (mPicture != null &&
4112 mPicture.isMutable() &&
Riley Andrews0394a0c2015-11-03 23:36:52 -08004113 mPicture.getAllocationByteCount() >= MIN_ASHMEM_BITMAP_SIZE) {
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07004114 mPicture = mPicture.createAshmemBitmap();
4115 }
4116 if (mBigLargeIcon != null) {
4117 mBigLargeIcon.convertToAshmem();
4118 }
4119 }
Christoph Studer5c510ee2014-12-15 16:32:27 +01004120
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004121 /**
4122 * @hide
4123 */
4124 public RemoteViews makeBigContentView() {
4125 // Replace mN.mLargeIcon with mBigLargeIcon if mBigLargeIconSet
Christoph Studer5c510ee2014-12-15 16:32:27 +01004126 // This covers the following cases:
4127 // 1. mBigLargeIconSet -> mBigLargeIcon (null or non-null) applies, overrides
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004128 // mN.mLargeIcon
4129 // 2. !mBigLargeIconSet -> mN.mLargeIcon applies
Dan Sandlerd63f9322015-05-06 15:18:49 -04004130 Icon oldLargeIcon = null;
Christoph Studer5c510ee2014-12-15 16:32:27 +01004131 if (mBigLargeIconSet) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004132 oldLargeIcon = mBuilder.mN.mLargeIcon;
4133 mBuilder.mN.mLargeIcon = mBigLargeIcon;
Christoph Studer5c510ee2014-12-15 16:32:27 +01004134 }
4135
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004136 RemoteViews contentView = getStandardView(mBuilder.getBigPictureLayoutResource());
Selim Cinek03d0d652015-11-13 13:18:09 -05004137 if (mSummaryTextSet) {
4138 contentView.setTextViewText(R.id.text, mBuilder.processLegacyText(mSummaryText));
Selim Cinekc848c3a2016-01-13 15:27:30 -08004139 contentView.setViewVisibility(R.id.text, View.VISIBLE);
Selim Cinek03d0d652015-11-13 13:18:09 -05004140 }
Selim Cinek860b6da2015-12-16 19:02:19 -08004141 mBuilder.setContentMinHeight(contentView, mBuilder.mN.mLargeIcon != null);
Selim Cinek53e64a42015-11-16 10:40:56 -08004142
Christoph Studer5c510ee2014-12-15 16:32:27 +01004143 if (mBigLargeIconSet) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004144 mBuilder.mN.mLargeIcon = oldLargeIcon;
Christoph Studer5c510ee2014-12-15 16:32:27 +01004145 }
4146
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004147 contentView.setImageViewBitmap(R.id.big_picture, mPicture);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004148 return contentView;
4149 }
4150
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004151 /**
4152 * @hide
4153 */
4154 public void addExtras(Bundle extras) {
4155 super.addExtras(extras);
4156
4157 if (mBigLargeIconSet) {
4158 extras.putParcelable(EXTRA_LARGE_ICON_BIG, mBigLargeIcon);
4159 }
4160 extras.putParcelable(EXTRA_PICTURE, mPicture);
4161 }
4162
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04004163 /**
4164 * @hide
4165 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004166 @Override
Christoph Studer4600f9b2014-07-22 22:44:43 +02004167 protected void restoreFromExtras(Bundle extras) {
4168 super.restoreFromExtras(extras);
4169
4170 if (extras.containsKey(EXTRA_LARGE_ICON_BIG)) {
Christoph Studer5c510ee2014-12-15 16:32:27 +01004171 mBigLargeIconSet = true;
Christoph Studer4600f9b2014-07-22 22:44:43 +02004172 mBigLargeIcon = extras.getParcelable(EXTRA_LARGE_ICON_BIG);
Chris Wren3745a3d2012-05-22 15:11:52 -04004173 }
Christoph Studer4600f9b2014-07-22 22:44:43 +02004174 mPicture = extras.getParcelable(EXTRA_PICTURE);
4175 }
Selim Cinek03d0d652015-11-13 13:18:09 -05004176
4177 /**
4178 * @hide
4179 */
4180 @Override
4181 public boolean hasSummaryInHeader() {
4182 return false;
4183 }
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004184 }
4185
4186 /**
Daniel Sandler4dfbe832012-04-11 14:51:46 -04004187 * Helper class for generating large-format notifications that include a lot of text.
Joe Malin8d40d042012-11-05 11:36:40 -08004188 *
Robert Ly91c5ce32014-06-08 15:37:00 -07004189 * Here's how you'd set the <code>BigTextStyle</code> on a notification:
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004190 * <pre class="prettyprint">
Robert Ly91c5ce32014-06-08 15:37:00 -07004191 * Notification notif = new Notification.Builder(mContext)
4192 * .setContentTitle(&quot;New mail from &quot; + sender.toString())
4193 * .setContentText(subject)
4194 * .setSmallIcon(R.drawable.new_mail)
4195 * .setLargeIcon(aBitmap)
4196 * .setStyle(new Notification.BigTextStyle()
4197 * .bigText(aVeryLongString))
4198 * .build();
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004199 * </pre>
Joe Malin8d40d042012-11-05 11:36:40 -08004200 *
Daniel Sandler4dfbe832012-04-11 14:51:46 -04004201 * @see Notification#bigContentView
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004202 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004203 public static class BigTextStyle extends Style {
Jorim Jaggi457a10d2014-09-08 16:18:23 +02004204
4205 private static final int MAX_LINES = 13;
Selim Cinek3a2c4b92015-12-17 17:01:17 -08004206 private static final int LINES_CONSUMED_BY_ACTIONS = 4;
Jorim Jaggi457a10d2014-09-08 16:18:23 +02004207
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004208 private CharSequence mBigText;
4209
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004210 public BigTextStyle() {
4211 }
4212
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004213 public BigTextStyle(Builder builder) {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004214 setBuilder(builder);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004215 }
4216
Chris Wrend6297db2012-05-03 16:20:13 -04004217 /**
4218 * Overrides ContentTitle in the big form of the template.
4219 * This defaults to the value passed to setContentTitle().
4220 */
4221 public BigTextStyle setBigContentTitle(CharSequence title) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04004222 internalSetBigContentTitle(safeCharSequence(title));
Chris Wrend6297db2012-05-03 16:20:13 -04004223 return this;
4224 }
4225
4226 /**
4227 * Set the first line of text after the detail section in the big form of the template.
4228 */
4229 public BigTextStyle setSummaryText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04004230 internalSetSummaryText(safeCharSequence(cs));
Chris Wrend6297db2012-05-03 16:20:13 -04004231 return this;
4232 }
4233
Chris Wren0bd664d2012-08-01 13:56:56 -04004234 /**
4235 * Provide the longer text to be displayed in the big form of the
4236 * template in place of the content text.
4237 */
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004238 public BigTextStyle bigText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04004239 mBigText = safeCharSequence(cs);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004240 return this;
4241 }
4242
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004243 /**
4244 * @hide
4245 */
4246 public void addExtras(Bundle extras) {
4247 super.addExtras(extras);
4248
Christoph Studer4600f9b2014-07-22 22:44:43 +02004249 extras.putCharSequence(EXTRA_BIG_TEXT, mBigText);
4250 }
4251
4252 /**
4253 * @hide
4254 */
4255 @Override
4256 protected void restoreFromExtras(Bundle extras) {
4257 super.restoreFromExtras(extras);
4258
4259 mBigText = extras.getCharSequence(EXTRA_BIG_TEXT);
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004260 }
4261
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004262 /**
4263 * @hide
4264 */
4265 public RemoteViews makeBigContentView() {
Christoph Studer4600f9b2014-07-22 22:44:43 +02004266
4267 // Nasty
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004268 CharSequence oldBuilderContentText =
4269 mBuilder.getAllExtras().getCharSequence(EXTRA_TEXT);
4270 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, null);
Daniel Sandler916ad912012-06-13 12:17:07 -04004271
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004272 RemoteViews contentView = getStandardView(mBuilder.getBigTextLayoutResource());
Joe Malin8d40d042012-11-05 11:36:40 -08004273
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004274 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, oldBuilderContentText);
Christoph Studer4600f9b2014-07-22 22:44:43 +02004275
Selim Cinek3a2c4b92015-12-17 17:01:17 -08004276 CharSequence bigTextText = mBuilder.processLegacyText(mBigText);
4277 contentView.setTextViewText(R.id.big_text, bigTextText);
4278 contentView.setViewVisibility(R.id.big_text,
4279 TextUtils.isEmpty(bigTextText) ? View.GONE : View.VISIBLE);
Jorim Jaggi457a10d2014-09-08 16:18:23 +02004280 contentView.setInt(R.id.big_text, "setMaxLines", calculateMaxLines());
Selim Cinek4fb12d32015-11-19 18:10:48 -08004281 contentView.setBoolean(R.id.big_text, "setHasImage", mBuilder.mN.mLargeIcon != null);
4282
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004283 return contentView;
4284 }
4285
Jorim Jaggi457a10d2014-09-08 16:18:23 +02004286 private int calculateMaxLines() {
4287 int lineCount = MAX_LINES;
4288 boolean hasActions = mBuilder.mActions.size() > 0;
Jorim Jaggi457a10d2014-09-08 16:18:23 +02004289 if (hasActions) {
4290 lineCount -= LINES_CONSUMED_BY_ACTIONS;
4291 }
Jorim Jaggi457a10d2014-09-08 16:18:23 +02004292 return lineCount;
4293 }
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004294 }
Daniel Sandler879c5e02012-04-17 16:46:51 -04004295
4296 /**
Alex Hillsfc737de2016-03-23 17:33:02 -04004297 * Helper class for generating large-format notifications that include multiple back-and-forth
4298 * messages of varying types between any number of people.
4299 *
4300 * <br>
4301 * If the platform does not provide large-format notifications, this method has no effect. The
4302 * user will always see the normal notification view.
4303 * <br>
4304 * This class is a "rebuilder": It attaches to a Builder object and modifies its behavior, like
4305 * so:
4306 * <pre class="prettyprint">
4307 *
4308 * Notification noti = new Notification.Builder()
4309 * .setContentTitle(&quot;2 new messages wtih &quot; + sender.toString())
4310 * .setContentText(subject)
4311 * .setSmallIcon(R.drawable.new_message)
4312 * .setLargeIcon(aBitmap)
4313 * .setStyle(new Notification.MessagingStyle(resources.getString(R.string.reply_name))
4314 * .addMessage(messages[0].getText(), messages[0].getTime(), messages[0].getSender())
4315 * .addMessage(messages[1].getText(), messages[1].getTime(), messages[1].getSender()))
4316 * .build();
4317 * </pre>
4318 */
4319 public static class MessagingStyle extends Style {
4320
4321 /**
4322 * The maximum number of messages that will be retained in the Notification itself (the
4323 * number displayed is up to the platform).
4324 */
4325 public static final int MAXIMUM_RETAINED_MESSAGES = 25;
4326
4327 CharSequence mUserDisplayName;
4328 CharSequence mConversationTitle;
4329 boolean mAllowGeneratedReplies = true;
Alex Hillsd9b04d92016-04-11 16:38:16 -04004330 List<Message> mMessages = new ArrayList<>();
Alex Hillsfc737de2016-03-23 17:33:02 -04004331
4332 MessagingStyle() {
4333 }
4334
4335 /**
4336 * @param userDisplayName the name to be displayed for any replies sent by the user before the
4337 * posting app reposts the notification with those messages after they've been actually
4338 * sent and in previous messages sent by the user added in
4339 * {@link #addMessage(Notification.MessagingStyle.Message)}
4340 */
4341 public MessagingStyle(CharSequence userDisplayName) {
4342 mUserDisplayName = userDisplayName;
4343 }
4344
4345 /**
4346 * Returns the name to be displayed for any replies sent by the user
4347 */
4348 public CharSequence getUserDisplayName() {
4349 return mUserDisplayName;
4350 }
4351
4352 /**
4353 * Set whether the platform should automatically generate possible replies from messages.
4354 * @param allowGeneratedReplies {@code true} to allow generated replies, {@code false}
4355 * otherwise
4356 * @return this object for method chaining
4357 * The default value is {@code true}
4358 */
4359 public MessagingStyle setAllowGeneratedReplies(boolean allowGeneratedReplies) {
4360 mAllowGeneratedReplies = allowGeneratedReplies;
4361 return this;
4362 }
4363
4364 /**
4365 * Return whether the platform should automatically generate possible replies from messages.
4366 */
4367 public boolean getAllowGeneratedReplies() {
4368 return mAllowGeneratedReplies;
4369 }
4370
4371 /**
4372 * Sets the title to be displayed on this conversation. This should only be used for
4373 * group messaging and left unset for one-on-one conversations.
4374 * @param conversationTitle
4375 * @return this object for method chaining.
4376 */
4377 public MessagingStyle setConversationTitle(CharSequence conversationTitle) {
4378 mConversationTitle = conversationTitle;
4379 return this;
4380 }
4381
4382 /**
4383 * Return the title to be displayed on this conversation. Can be <code>null</code> and
4384 * should be for one-on-one conversations
4385 */
4386 public CharSequence getConversationTitle() {
4387 return mConversationTitle;
4388 }
4389
4390 /**
4391 * Adds a message for display by this notification. Convenience call for a simple
4392 * {@link Message} in {@link #addMessage(Notification.MessagingStyle.Message)}.
4393 * @param text A {@link CharSequence} to be displayed as the message content
4394 * @param timestamp Time at which the message arrived
4395 * @param sender A {@link CharSequence} to be used for displaying the name of the
4396 * sender. Should be <code>null</code> for messages by the current user, in which case
4397 * the platform will insert {@link #getUserDisplayName()}.
4398 * Should be unique amongst all individuals in the conversation, and should be
4399 * consistent during re-posts of the notification.
4400 *
4401 * @see Message#Message(CharSequence, long, CharSequence)
4402 *
4403 * @return this object for method chaining
4404 */
4405 public MessagingStyle addMessage(CharSequence text, long timestamp, CharSequence sender) {
4406 mMessages.add(new Message(text, timestamp, sender));
4407 if (mMessages.size() > MAXIMUM_RETAINED_MESSAGES) {
4408 mMessages.remove(0);
4409 }
4410 return this;
4411 }
4412
4413 /**
4414 * Adds a {@link Message} for display in this notification.
4415 * @param message The {@link Message} to be displayed
4416 * @return this object for method chaining
4417 */
4418 public MessagingStyle addMessage(Message message) {
4419 mMessages.add(message);
4420 if (mMessages.size() > MAXIMUM_RETAINED_MESSAGES) {
4421 mMessages.remove(0);
4422 }
4423 return this;
4424 }
4425
4426 /**
4427 * Gets the list of {@code Message} objects that represent the notification
4428 */
4429 public List<Message> getMessages() {
4430 return mMessages;
4431 }
4432
4433 /**
4434 * @hide
4435 */
4436 @Override
4437 public void addExtras(Bundle extras) {
4438 super.addExtras(extras);
4439 if (mUserDisplayName != null) {
4440 extras.putCharSequence(EXTRA_SELF_DISPLAY_NAME, mUserDisplayName);
4441 }
4442 if (mConversationTitle != null) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04004443 extras.putCharSequence(EXTRA_CONVERSATION_TITLE, mConversationTitle);
Alex Hillsfc737de2016-03-23 17:33:02 -04004444 }
4445 extras.putBoolean(EXTRA_ALLOW_GENERATED_REPLIES, mAllowGeneratedReplies);
Alex Hillsd9b04d92016-04-11 16:38:16 -04004446 if (!mMessages.isEmpty()) { extras.putParcelableArray(EXTRA_MESSAGES,
4447 Message.getBundleArrayForMessages(mMessages));
Alex Hillsfc737de2016-03-23 17:33:02 -04004448 }
4449 }
4450
4451 /**
4452 * @hide
4453 */
4454 @Override
4455 protected void restoreFromExtras(Bundle extras) {
4456 super.restoreFromExtras(extras);
4457
4458 mMessages.clear();
4459 mUserDisplayName = extras.getString(EXTRA_SELF_DISPLAY_NAME);
Alex Hillsd9b04d92016-04-11 16:38:16 -04004460 mConversationTitle = extras.getString(EXTRA_CONVERSATION_TITLE);
Alex Hillsfc737de2016-03-23 17:33:02 -04004461 mAllowGeneratedReplies = extras.getBoolean(EXTRA_ALLOW_GENERATED_REPLIES,
4462 mAllowGeneratedReplies);
Alex Hillsd9b04d92016-04-11 16:38:16 -04004463 Parcelable[] parcelables = extras.getParcelableArray(EXTRA_MESSAGES);
Adrian Roosdedd1df2016-04-26 16:38:47 -07004464 if (parcelables != null && parcelables instanceof Parcelable[]) {
4465 mMessages = Message.getMessagesFromBundleArray(parcelables);
Alex Hillsfc737de2016-03-23 17:33:02 -04004466 }
4467 }
4468
4469 /**
4470 * @hide
4471 */
Adrian Roosc1a80b02016-04-05 14:54:55 -07004472 @Override
4473 public RemoteViews makeContentView() {
4474 Message m = findLatestIncomingMessage();
4475 CharSequence title = mConversationTitle != null
4476 ? mConversationTitle
4477 : (m == null) ? null : m.mSender;
4478 CharSequence text = (m == null)
4479 ? null
4480 : mConversationTitle != null ? makeMessageLine(m) : m.mText;
Alex Hillsfc737de2016-03-23 17:33:02 -04004481
Adrian Roosc1a80b02016-04-05 14:54:55 -07004482 return mBuilder.applyStandardTemplate(mBuilder.getBaseLayoutResource(),
4483 false /* hasProgress */,
4484 title,
4485 text);
4486 }
4487
4488 private Message findLatestIncomingMessage() {
4489 for (int i = mMessages.size() - 1; i >= 0; i--) {
4490 Message m = mMessages.get(i);
4491 // Incoming messages have a non-empty sender.
4492 if (!TextUtils.isEmpty(m.mSender)) {
4493 return m;
4494 }
4495 }
4496 return null;
4497 }
4498
4499 /**
4500 * @hide
4501 */
4502 @Override
4503 public RemoteViews makeBigContentView() {
4504 CharSequence title = !TextUtils.isEmpty(super.mBigContentTitle)
4505 ? super.mBigContentTitle
4506 : mConversationTitle;
4507 boolean hasTitle = !TextUtils.isEmpty(title);
4508
Adrian Roos48d746a2016-04-12 14:57:28 -07004509 RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(
Adrian Roosc1a80b02016-04-05 14:54:55 -07004510 mBuilder.getMessagingLayoutResource(),
4511 false /* hasProgress */,
4512 title,
4513 null /* text */);
4514
4515 int[] rowIds = {R.id.inbox_text0, R.id.inbox_text1, R.id.inbox_text2, R.id.inbox_text3,
4516 R.id.inbox_text4, R.id.inbox_text5, R.id.inbox_text6};
4517
4518 // Make sure all rows are gone in case we reuse a view.
4519 for (int rowId : rowIds) {
4520 contentView.setViewVisibility(rowId, View.GONE);
4521 }
4522
4523 int i=0;
4524 int titlePadding = mBuilder.mContext.getResources().getDimensionPixelSize(
4525 R.dimen.notification_messaging_spacing);
4526 contentView.setViewLayoutMarginBottom(R.id.line1, hasTitle ? titlePadding : 0);
4527 contentView.setInt(R.id.notification_messaging, "setNumIndentLines",
4528 mBuilder.mN.mLargeIcon == null ? 0 : (hasTitle ? 1 : 2));
4529
4530 int firstMessage = Math.max(0, mMessages.size() - rowIds.length);
4531 while (firstMessage + i < mMessages.size() && i < rowIds.length) {
4532 Message m = mMessages.get(firstMessage + i);
4533 int rowId = rowIds[i];
4534
4535 contentView.setViewVisibility(rowId, View.VISIBLE);
4536 contentView.setTextViewText(rowId, makeMessageLine(m));
4537
4538 i++;
4539 }
Alex Hillsfc737de2016-03-23 17:33:02 -04004540 return contentView;
4541 }
4542
Adrian Roosc1a80b02016-04-05 14:54:55 -07004543 private CharSequence makeMessageLine(Message m) {
4544 BidiFormatter bidi = BidiFormatter.getInstance();
4545 SpannableStringBuilder sb = new SpannableStringBuilder();
4546 if (TextUtils.isEmpty(m.mSender)) {
4547 CharSequence replyName = mUserDisplayName == null ? "" : mUserDisplayName;
4548 sb.append(bidi.unicodeWrap(replyName),
4549 makeFontColorSpan(mBuilder.resolveContrastColor()),
4550 0 /* flags */);
4551 } else {
4552 sb.append(bidi.unicodeWrap(m.mSender),
4553 makeFontColorSpan(Color.BLACK),
4554 0 /* flags */);
4555 }
4556 CharSequence text = m.mText == null ? "" : m.mText;
4557 sb.append(" ").append(bidi.unicodeWrap(text));
4558 return sb;
4559 }
4560
Adrian Roosdedd1df2016-04-26 16:38:47 -07004561 /**
4562 * @hide
4563 */
4564 @Override
4565 public RemoteViews makeHeadsUpContentView() {
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;
4573
4574 return mBuilder.applyStandardTemplateWithActions(mBuilder.getBigBaseLayoutResource(),
4575 false /* hasProgress */,
4576 title,
4577 text);
4578 }
4579
Adrian Roosc1a80b02016-04-05 14:54:55 -07004580 private static TextAppearanceSpan makeFontColorSpan(int color) {
4581 return new TextAppearanceSpan(null, 0, 0,
4582 ColorStateList.valueOf(color), null);
4583 }
4584
Alex Hillsd9b04d92016-04-11 16:38:16 -04004585 public static final class Message {
4586
4587 static final String KEY_TEXT = "text";
4588 static final String KEY_TIMESTAMP = "time";
4589 static final String KEY_SENDER = "sender";
4590 static final String KEY_DATA_MIME_TYPE = "type";
4591 static final String KEY_DATA_URI= "uri";
Alex Hillsfc737de2016-03-23 17:33:02 -04004592
4593 private final CharSequence mText;
4594 private final long mTimestamp;
4595 private final CharSequence mSender;
4596
4597 private String mDataMimeType;
4598 private Uri mDataUri;
4599
4600 /**
4601 * Constructor
4602 * @param text A {@link CharSequence} to be displayed as the message content
4603 * @param timestamp Time at which the message arrived
4604 * @param sender A {@link CharSequence} to be used for displaying the name of the
4605 * sender. Should be <code>null</code> for messages by the current user, in which case
4606 * the platform will insert {@link MessagingStyle#getUserDisplayName()}.
4607 * Should be unique amongst all individuals in the conversation, and should be
4608 * consistent during re-posts of the notification.
4609 */
4610 public Message(CharSequence text, long timestamp, CharSequence sender){
4611 mText = text;
4612 mTimestamp = timestamp;
4613 mSender = sender;
4614 }
4615
4616 /**
4617 * Sets a binary blob of data and an associated MIME type for a message. In the case
4618 * where the platform doesn't support the MIME type, the original text provided in the
4619 * constructor will be used.
4620 * @param dataMimeType The MIME type of the content. See
4621 * <a href="{@docRoot}notifications/messaging.html"> for the list of supported MIME
4622 * types on Android and Android Wear.
4623 * @param dataUri The uri containing the content whose type is given by the MIME type.
4624 * <p class="note">
4625 * <ol>
4626 * <li>Notification Listeners including the System UI need permission to access the
4627 * data the Uri points to. The recommended ways to do this are:</li>
4628 * <li>Store the data in your own ContentProvider, making sure that other apps have
4629 * the correct permission to access your provider. The preferred mechanism for
4630 * providing access is to use per-URI permissions which are temporary and only
4631 * grant access to the receiving application. An easy way to create a
4632 * ContentProvider like this is to use the FileProvider helper class.</li>
4633 * <li>Use the system MediaStore. The MediaStore is primarily aimed at video, audio
4634 * and image MIME types, however beginning with Android 3.0 (API level 11) it can
4635 * also store non-media types (see MediaStore.Files for more info). Files can be
4636 * inserted into the MediaStore using scanFile() after which a content:// style
4637 * Uri suitable for sharing is passed to the provided onScanCompleted() callback.
4638 * Note that once added to the system MediaStore the content is accessible to any
4639 * app on the device.</li>
4640 * </ol>
4641 * @return this object for method chaining
4642 */
4643 public Message setData(String dataMimeType, Uri dataUri) {
4644 mDataMimeType = dataMimeType;
4645 mDataUri = dataUri;
4646 return this;
4647 }
4648
Alex Hillsfc737de2016-03-23 17:33:02 -04004649 /**
4650 * Get the text to be used for this message, or the fallback text if a type and content
4651 * Uri have been set
4652 */
4653 public CharSequence getText() {
4654 return mText;
4655 }
4656
4657 /**
4658 * Get the time at which this message arrived
4659 */
4660 public long getTimestamp() {
4661 return mTimestamp;
4662 }
4663
4664 /**
4665 * Get the text used to display the contact's name in the messaging experience
4666 */
4667 public CharSequence getSender() {
4668 return mSender;
4669 }
4670
4671 /**
4672 * Get the MIME type of the data pointed to by the Uri
4673 */
4674 public String getDataMimeType() {
4675 return mDataMimeType;
4676 }
4677
4678 /**
4679 * Get the the Uri pointing to the content of the message. Can be null, in which case
4680 * {@see #getText()} is used.
4681 */
4682 public Uri getDataUri() {
4683 return mDataUri;
4684 }
4685
Alex Hillsd9b04d92016-04-11 16:38:16 -04004686 private Bundle toBundle() {
4687 Bundle bundle = new Bundle();
Alex Hillsfc737de2016-03-23 17:33:02 -04004688 if (mText != null) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04004689 bundle.putCharSequence(KEY_TEXT, mText);
Alex Hillsfc737de2016-03-23 17:33:02 -04004690 }
Alex Hillsd9b04d92016-04-11 16:38:16 -04004691 bundle.putLong(KEY_TIMESTAMP, mTimestamp);
Alex Hillsfc737de2016-03-23 17:33:02 -04004692 if (mSender != null) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04004693 bundle.putCharSequence(KEY_SENDER, mSender);
Alex Hillsfc737de2016-03-23 17:33:02 -04004694 }
4695 if (mDataMimeType != null) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04004696 bundle.putString(KEY_DATA_MIME_TYPE, mDataMimeType);
Alex Hillsfc737de2016-03-23 17:33:02 -04004697 }
4698 if (mDataUri != null) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04004699 bundle.putParcelable(KEY_DATA_URI, mDataUri);
Alex Hillsfc737de2016-03-23 17:33:02 -04004700 }
Alex Hillsd9b04d92016-04-11 16:38:16 -04004701 return bundle;
Alex Hillsfc737de2016-03-23 17:33:02 -04004702 }
4703
Alex Hillsd9b04d92016-04-11 16:38:16 -04004704 static Bundle[] getBundleArrayForMessages(List<Message> messages) {
4705 Bundle[] bundles = new Bundle[messages.size()];
4706 final int N = messages.size();
4707 for (int i = 0; i < N; i++) {
4708 bundles[i] = messages.get(i).toBundle();
4709 }
4710 return bundles;
4711 }
4712
Adrian Roosdedd1df2016-04-26 16:38:47 -07004713 static List<Message> getMessagesFromBundleArray(Parcelable[] bundles) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04004714 List<Message> messages = new ArrayList<>(bundles.length);
4715 for (int i = 0; i < bundles.length; i++) {
Adrian Roosdedd1df2016-04-26 16:38:47 -07004716 if (bundles[i] instanceof Bundle) {
4717 Message message = getMessageFromBundle((Bundle)bundles[i]);
4718 if (message != null) {
4719 messages.add(message);
4720 }
Alex Hillsd9b04d92016-04-11 16:38:16 -04004721 }
4722 }
4723 return messages;
4724 }
4725
4726 static Message getMessageFromBundle(Bundle bundle) {
4727 try {
4728 if (!bundle.containsKey(KEY_TEXT) || !bundle.containsKey(KEY_TIMESTAMP) ||
4729 !bundle.containsKey(KEY_SENDER)) {
4730 return null;
4731 } else {
4732 Message message = new Message(bundle.getCharSequence(KEY_TEXT),
4733 bundle.getLong(KEY_TIMESTAMP), bundle.getCharSequence(KEY_SENDER));
4734 if (bundle.containsKey(KEY_DATA_MIME_TYPE) &&
4735 bundle.containsKey(KEY_DATA_URI)) {
4736
4737 message.setData(bundle.getString(KEY_DATA_MIME_TYPE),
4738 (Uri) bundle.getParcelable(KEY_DATA_URI));
Alex Hillsfc737de2016-03-23 17:33:02 -04004739 }
Alex Hillsd9b04d92016-04-11 16:38:16 -04004740 return message;
4741 }
4742 } catch (ClassCastException e) {
4743 return null;
4744 }
4745 }
Alex Hillsfc737de2016-03-23 17:33:02 -04004746 }
4747 }
4748
4749 /**
Daniel Sandler879c5e02012-04-17 16:46:51 -04004750 * Helper class for generating large-format notifications that include a list of (up to 5) strings.
Joe Malin8d40d042012-11-05 11:36:40 -08004751 *
Robert Ly91c5ce32014-06-08 15:37:00 -07004752 * Here's how you'd set the <code>InboxStyle</code> on a notification:
Daniel Sandler879c5e02012-04-17 16:46:51 -04004753 * <pre class="prettyprint">
Robert Ly91c5ce32014-06-08 15:37:00 -07004754 * Notification notif = new Notification.Builder(mContext)
4755 * .setContentTitle(&quot;5 New mails from &quot; + sender.toString())
4756 * .setContentText(subject)
4757 * .setSmallIcon(R.drawable.new_mail)
4758 * .setLargeIcon(aBitmap)
4759 * .setStyle(new Notification.InboxStyle()
4760 * .addLine(str1)
4761 * .addLine(str2)
4762 * .setContentTitle(&quot;&quot;)
4763 * .setSummaryText(&quot;+3 more&quot;))
4764 * .build();
Daniel Sandler879c5e02012-04-17 16:46:51 -04004765 * </pre>
Joe Malin8d40d042012-11-05 11:36:40 -08004766 *
Daniel Sandler879c5e02012-04-17 16:46:51 -04004767 * @see Notification#bigContentView
4768 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004769 public static class InboxStyle extends Style {
Daniel Sandler879c5e02012-04-17 16:46:51 -04004770 private ArrayList<CharSequence> mTexts = new ArrayList<CharSequence>(5);
4771
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004772 public InboxStyle() {
4773 }
4774
Daniel Sandler879c5e02012-04-17 16:46:51 -04004775 public InboxStyle(Builder builder) {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004776 setBuilder(builder);
Daniel Sandler879c5e02012-04-17 16:46:51 -04004777 }
4778
Chris Wrend6297db2012-05-03 16:20:13 -04004779 /**
4780 * Overrides ContentTitle in the big form of the template.
4781 * This defaults to the value passed to setContentTitle().
4782 */
4783 public InboxStyle setBigContentTitle(CharSequence title) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04004784 internalSetBigContentTitle(safeCharSequence(title));
Chris Wrend6297db2012-05-03 16:20:13 -04004785 return this;
4786 }
4787
4788 /**
4789 * Set the first line of text after the detail section in the big form of the template.
4790 */
4791 public InboxStyle setSummaryText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04004792 internalSetSummaryText(safeCharSequence(cs));
Chris Wrend6297db2012-05-03 16:20:13 -04004793 return this;
4794 }
4795
Chris Wren0bd664d2012-08-01 13:56:56 -04004796 /**
4797 * Append a line to the digest section of the Inbox notification.
4798 */
Daniel Sandler879c5e02012-04-17 16:46:51 -04004799 public InboxStyle addLine(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04004800 mTexts.add(safeCharSequence(cs));
Daniel Sandler879c5e02012-04-17 16:46:51 -04004801 return this;
4802 }
4803
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004804 /**
4805 * @hide
4806 */
4807 public void addExtras(Bundle extras) {
4808 super.addExtras(extras);
Christoph Studer4600f9b2014-07-22 22:44:43 +02004809
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004810 CharSequence[] a = new CharSequence[mTexts.size()];
4811 extras.putCharSequenceArray(EXTRA_TEXT_LINES, mTexts.toArray(a));
4812 }
4813
Christoph Studer4600f9b2014-07-22 22:44:43 +02004814 /**
4815 * @hide
4816 */
4817 @Override
4818 protected void restoreFromExtras(Bundle extras) {
4819 super.restoreFromExtras(extras);
4820
4821 mTexts.clear();
4822 if (extras.containsKey(EXTRA_TEXT_LINES)) {
4823 Collections.addAll(mTexts, extras.getCharSequenceArray(EXTRA_TEXT_LINES));
4824 }
4825 }
4826
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004827 /**
4828 * @hide
4829 */
4830 public RemoteViews makeBigContentView() {
Selim Cinekc848c3a2016-01-13 15:27:30 -08004831 // Remove the content text so it disappears unless you have a summary
Christoph Studer4600f9b2014-07-22 22:44:43 +02004832 // Nasty
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004833 CharSequence oldBuilderContentText = mBuilder.mN.extras.getCharSequence(EXTRA_TEXT);
4834 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, null);
Christoph Studer4600f9b2014-07-22 22:44:43 +02004835
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004836 RemoteViews contentView = getStandardView(mBuilder.getInboxLayoutResource());
Daniel Sandler619738c2012-06-07 16:33:08 -04004837
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004838 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, oldBuilderContentText);
Christoph Studer4600f9b2014-07-22 22:44:43 +02004839
Chris Wrend6297db2012-05-03 16:20:13 -04004840 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 -04004841 R.id.inbox_text4, R.id.inbox_text5, R.id.inbox_text6};
Chris Wrend6297db2012-05-03 16:20:13 -04004842
Chris Wren4ed80d52012-05-17 09:30:03 -04004843 // Make sure all rows are gone in case we reuse a view.
4844 for (int rowId : rowIds) {
4845 contentView.setViewVisibility(rowId, View.GONE);
4846 }
4847
Daniel Sandler879c5e02012-04-17 16:46:51 -04004848 int i=0;
Selim Cinek07c80172016-04-21 16:40:47 -07004849 int topPadding = mBuilder.mContext.getResources().getDimensionPixelSize(
4850 R.dimen.notification_inbox_item_top_padding);
Selim Cinek247fa012016-02-18 09:50:48 -08004851 boolean first = true;
Selim Cinek07c80172016-04-21 16:40:47 -07004852 int onlyViewId = 0;
4853 int maxRows = rowIds.length;
4854 if (mBuilder.mActions.size() > 0) {
4855 maxRows--;
4856 }
4857 while (i < mTexts.size() && i < maxRows) {
Daniel Sandler879c5e02012-04-17 16:46:51 -04004858 CharSequence str = mTexts.get(i);
Selim Cinek07c80172016-04-21 16:40:47 -07004859 if (!TextUtils.isEmpty(str)) {
Daniel Sandler879c5e02012-04-17 16:46:51 -04004860 contentView.setViewVisibility(rowIds[i], View.VISIBLE);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004861 contentView.setTextViewText(rowIds[i], mBuilder.processLegacyText(str));
Selim Cinek07c80172016-04-21 16:40:47 -07004862 contentView.setViewPadding(rowIds[i], 0, topPadding, 0, 0);
Selim Cinek247fa012016-02-18 09:50:48 -08004863 handleInboxImageMargin(contentView, rowIds[i], first);
Selim Cinek07c80172016-04-21 16:40:47 -07004864 if (first) {
4865 onlyViewId = rowIds[i];
4866 } else {
4867 onlyViewId = 0;
4868 }
Selim Cinek247fa012016-02-18 09:50:48 -08004869 first = false;
Daniel Sandler879c5e02012-04-17 16:46:51 -04004870 }
4871 i++;
4872 }
Selim Cinek07c80172016-04-21 16:40:47 -07004873 if (onlyViewId != 0) {
4874 // We only have 1 entry, lets make it look like the normal Text of a Bigtext
4875 topPadding = mBuilder.mContext.getResources().getDimensionPixelSize(
4876 R.dimen.notification_text_margin_top);
4877 contentView.setViewPadding(onlyViewId, 0, topPadding, 0, 0);
4878 }
Selim Cinek1e0bf612015-11-20 15:57:26 -08004879
Daniel Sandler879c5e02012-04-17 16:46:51 -04004880 return contentView;
4881 }
Selim Cinek1e0bf612015-11-20 15:57:26 -08004882
Selim Cinek247fa012016-02-18 09:50:48 -08004883 private void handleInboxImageMargin(RemoteViews contentView, int id, boolean first) {
Selim Cinek1e0bf612015-11-20 15:57:26 -08004884 int endMargin = 0;
Selim Cinek247fa012016-02-18 09:50:48 -08004885 if (first) {
4886 final int max = mBuilder.mN.extras.getInt(EXTRA_PROGRESS_MAX, 0);
4887 final boolean ind = mBuilder.mN.extras.getBoolean(EXTRA_PROGRESS_INDETERMINATE);
4888 boolean hasProgress = max != 0 || ind;
4889 if (mBuilder.mN.mLargeIcon != null && !hasProgress) {
4890 endMargin = mBuilder.mContext.getResources().getDimensionPixelSize(
4891 R.dimen.notification_content_picture_margin);
4892 }
Selim Cinek1e0bf612015-11-20 15:57:26 -08004893 }
4894 contentView.setViewLayoutMarginEnd(id, endMargin);
4895 }
Daniel Sandler879c5e02012-04-17 16:46:51 -04004896 }
Dan Sandler842dd772014-05-15 09:36:47 -04004897
4898 /**
4899 * Notification style for media playback notifications.
4900 *
4901 * In the expanded form, {@link Notification#bigContentView}, up to 5
4902 * {@link Notification.Action}s specified with
Dan Sandler86647982015-05-13 23:41:13 -04004903 * {@link Notification.Builder#addAction(Action) addAction} will be
Dan Sandler842dd772014-05-15 09:36:47 -04004904 * shown as icon-only pushbuttons, suitable for transport controls. The Bitmap given to
4905 * {@link Notification.Builder#setLargeIcon(android.graphics.Bitmap) setLargeIcon()} will be
4906 * treated as album artwork.
4907 *
4908 * Unlike the other styles provided here, MediaStyle can also modify the standard-size
4909 * {@link Notification#contentView}; by providing action indices to
Christoph Studerfde6f4d2014-12-12 13:23:26 +01004910 * {@link #setShowActionsInCompactView(int...)} you can promote up to 3 actions to be displayed
Dan Sandler842dd772014-05-15 09:36:47 -04004911 * in the standard view alongside the usual content.
4912 *
Bryan Mawhinney6be8de32014-07-18 10:35:12 +01004913 * Notifications created with MediaStyle will have their category set to
4914 * {@link Notification#CATEGORY_TRANSPORT CATEGORY_TRANSPORT} unless you set a different
4915 * category using {@link Notification.Builder#setCategory(String) setCategory()}.
4916 *
Jeff Browndba34ba2014-06-24 20:46:03 -07004917 * Finally, if you attach a {@link android.media.session.MediaSession.Token} using
4918 * {@link android.app.Notification.MediaStyle#setMediaSession(MediaSession.Token)},
Dan Sandler842dd772014-05-15 09:36:47 -04004919 * the System UI can identify this as a notification representing an active media session
4920 * and respond accordingly (by showing album artwork in the lockscreen, for example).
4921 *
4922 * To use this style with your Notification, feed it to
4923 * {@link Notification.Builder#setStyle(android.app.Notification.Style)} like so:
4924 * <pre class="prettyprint">
4925 * Notification noti = new Notification.Builder()
4926 * .setSmallIcon(R.drawable.ic_stat_player)
Christoph Studere935fe92014-11-24 14:18:06 +01004927 * .setContentTitle(&quot;Track title&quot;)
4928 * .setContentText(&quot;Artist - Album&quot;)
4929 * .setLargeIcon(albumArtBitmap))
Bryan Mawhinney6be8de32014-07-18 10:35:12 +01004930 * .setStyle(<b>new Notification.MediaStyle()</b>
4931 * .setMediaSession(mySession))
Dan Sandler842dd772014-05-15 09:36:47 -04004932 * .build();
4933 * </pre>
4934 *
4935 * @see Notification#bigContentView
4936 */
4937 public static class MediaStyle extends Style {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004938 static final int MAX_MEDIA_BUTTONS_IN_COMPACT = 3;
Dan Sandler842dd772014-05-15 09:36:47 -04004939 static final int MAX_MEDIA_BUTTONS = 5;
4940
4941 private int[] mActionsToShowInCompact = null;
Jeff Browndba34ba2014-06-24 20:46:03 -07004942 private MediaSession.Token mToken;
Dan Sandler842dd772014-05-15 09:36:47 -04004943
4944 public MediaStyle() {
4945 }
4946
4947 public MediaStyle(Builder builder) {
4948 setBuilder(builder);
4949 }
4950
4951 /**
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004952 * 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 -04004953 * notification view.
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004954 *
4955 * @param actions the indices of the actions to show in the compact notification view
Dan Sandler842dd772014-05-15 09:36:47 -04004956 */
4957 public MediaStyle setShowActionsInCompactView(int...actions) {
4958 mActionsToShowInCompact = actions;
4959 return this;
4960 }
4961
4962 /**
Jeff Browndba34ba2014-06-24 20:46:03 -07004963 * Attach a {@link android.media.session.MediaSession.Token} to this Notification
4964 * to provide additional playback information and control to the SystemUI.
Dan Sandler842dd772014-05-15 09:36:47 -04004965 */
Jeff Browndba34ba2014-06-24 20:46:03 -07004966 public MediaStyle setMediaSession(MediaSession.Token token) {
Dan Sandler842dd772014-05-15 09:36:47 -04004967 mToken = token;
4968 return this;
4969 }
4970
Christoph Studer4600f9b2014-07-22 22:44:43 +02004971 /**
4972 * @hide
4973 */
Dan Sandler842dd772014-05-15 09:36:47 -04004974 @Override
4975 public Notification buildStyled(Notification wip) {
Christoph Studer4600f9b2014-07-22 22:44:43 +02004976 super.buildStyled(wip);
Bryan Mawhinney6be8de32014-07-18 10:35:12 +01004977 if (wip.category == null) {
4978 wip.category = Notification.CATEGORY_TRANSPORT;
4979 }
Dan Sandler842dd772014-05-15 09:36:47 -04004980 return wip;
4981 }
4982
Christoph Studer4600f9b2014-07-22 22:44:43 +02004983 /**
4984 * @hide
4985 */
4986 @Override
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004987 public RemoteViews makeContentView() {
4988 return makeMediaContentView();
Christoph Studer4600f9b2014-07-22 22:44:43 +02004989 }
4990
4991 /**
4992 * @hide
4993 */
4994 @Override
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004995 public RemoteViews makeBigContentView() {
4996 return makeMediaBigContentView();
Christoph Studer4600f9b2014-07-22 22:44:43 +02004997 }
4998
Selim Cinekcc10bfb2016-02-10 16:24:21 -08004999 /**
5000 * @hide
5001 */
5002 @Override
5003 public RemoteViews makeHeadsUpContentView() {
5004 RemoteViews expanded = makeMediaBigContentView();
5005 return expanded != null ? expanded : makeMediaContentView();
5006 }
5007
Dan Sandler842dd772014-05-15 09:36:47 -04005008 /** @hide */
5009 @Override
5010 public void addExtras(Bundle extras) {
5011 super.addExtras(extras);
5012
5013 if (mToken != null) {
5014 extras.putParcelable(EXTRA_MEDIA_SESSION, mToken);
5015 }
Bryan Mawhinneye191f902014-07-22 12:50:09 +01005016 if (mActionsToShowInCompact != null) {
5017 extras.putIntArray(EXTRA_COMPACT_ACTIONS, mActionsToShowInCompact);
5018 }
Dan Sandler842dd772014-05-15 09:36:47 -04005019 }
5020
Christoph Studer4600f9b2014-07-22 22:44:43 +02005021 /**
5022 * @hide
5023 */
5024 @Override
5025 protected void restoreFromExtras(Bundle extras) {
5026 super.restoreFromExtras(extras);
5027
5028 if (extras.containsKey(EXTRA_MEDIA_SESSION)) {
5029 mToken = extras.getParcelable(EXTRA_MEDIA_SESSION);
5030 }
5031 if (extras.containsKey(EXTRA_COMPACT_ACTIONS)) {
5032 mActionsToShowInCompact = extras.getIntArray(EXTRA_COMPACT_ACTIONS);
5033 }
5034 }
5035
Selim Cinek5bf069a2015-11-10 19:14:27 -05005036 private RemoteViews generateMediaActionButton(Action action, int color) {
Dan Sandler842dd772014-05-15 09:36:47 -04005037 final boolean tombstone = (action.actionIntent == null);
Selim Cinekf33b1112015-07-15 17:45:11 -07005038 RemoteViews button = new BuilderRemoteViews(mBuilder.mContext.getApplicationInfo(),
Alan Viverette3cb07a462014-06-06 14:19:53 -07005039 R.layout.notification_material_media_action);
Dan Sandler68079d52015-07-22 10:45:30 -04005040 button.setImageViewIcon(R.id.action0, action.getIcon());
Selim Cinek5bf069a2015-11-10 19:14:27 -05005041 button.setDrawableParameters(R.id.action0, false, -1, color, PorterDuff.Mode.SRC_ATOP,
5042 -1);
Dan Sandler842dd772014-05-15 09:36:47 -04005043 if (!tombstone) {
5044 button.setOnClickPendingIntent(R.id.action0, action.actionIntent);
5045 }
5046 button.setContentDescription(R.id.action0, action.title);
5047 return button;
5048 }
5049
5050 private RemoteViews makeMediaContentView() {
5051 RemoteViews view = mBuilder.applyStandardTemplate(
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02005052 R.layout.notification_template_material_media, false /* hasProgress */);
Dan Sandler842dd772014-05-15 09:36:47 -04005053
5054 final int numActions = mBuilder.mActions.size();
5055 final int N = mActionsToShowInCompact == null
5056 ? 0
5057 : Math.min(mActionsToShowInCompact.length, MAX_MEDIA_BUTTONS_IN_COMPACT);
5058 if (N > 0) {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02005059 view.removeAllViews(com.android.internal.R.id.media_actions);
Dan Sandler842dd772014-05-15 09:36:47 -04005060 for (int i = 0; i < N; i++) {
5061 if (i >= numActions) {
5062 throw new IllegalArgumentException(String.format(
5063 "setShowActionsInCompactView: action %d out of bounds (max %d)",
5064 i, numActions - 1));
5065 }
5066
5067 final Action action = mBuilder.mActions.get(mActionsToShowInCompact[i]);
Selim Cinek5bf069a2015-11-10 19:14:27 -05005068 final RemoteViews button = generateMediaActionButton(action,
Adrian Roos4ff3b122016-02-01 12:26:13 -08005069 mBuilder.resolveContrastColor());
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02005070 view.addView(com.android.internal.R.id.media_actions, button);
Dan Sandler842dd772014-05-15 09:36:47 -04005071 }
5072 }
Selim Cinekfdc738f2016-01-27 20:04:27 -08005073 handleImage(view);
5074 // handle the content margin
Selim Cinek247fa012016-02-18 09:50:48 -08005075 int endMargin = mBuilder.mContext.getResources().getDimensionPixelSize(
5076 R.dimen.notification_content_margin_end);;
Selim Cinekfdc738f2016-01-27 20:04:27 -08005077 if (mBuilder.mN.mLargeIcon != null) {
Selim Cinek247fa012016-02-18 09:50:48 -08005078 endMargin += mBuilder.mContext.getResources().getDimensionPixelSize(
5079 R.dimen.notification_content_picture_margin);
Selim Cinekfdc738f2016-01-27 20:04:27 -08005080 }
5081 view.setViewLayoutMarginEnd(R.id.notification_main_column, endMargin);
Dan Sandler842dd772014-05-15 09:36:47 -04005082 return view;
5083 }
5084
5085 private RemoteViews makeMediaBigContentView() {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02005086 final int actionCount = Math.min(mBuilder.mActions.size(), MAX_MEDIA_BUTTONS);
Selim Cinekcc10bfb2016-02-10 16:24:21 -08005087 // Dont add an expanded view if there is no more content to be revealed
5088 int actionsInCompact = mActionsToShowInCompact == null
5089 ? 0
5090 : Math.min(mActionsToShowInCompact.length, MAX_MEDIA_BUTTONS_IN_COMPACT);
5091 if (mBuilder.mN.mLargeIcon == null && actionCount <= actionsInCompact) {
5092 return null;
5093 }
Selim Cinek5bf069a2015-11-10 19:14:27 -05005094 RemoteViews big = mBuilder.applyStandardTemplate(
5095 R.layout.notification_template_material_big_media,
5096 false);
Dan Sandler842dd772014-05-15 09:36:47 -04005097
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02005098 if (actionCount > 0) {
5099 big.removeAllViews(com.android.internal.R.id.media_actions);
5100 for (int i = 0; i < actionCount; i++) {
Selim Cinek5bf069a2015-11-10 19:14:27 -05005101 final RemoteViews button = generateMediaActionButton(mBuilder.mActions.get(i),
Adrian Roos4ff3b122016-02-01 12:26:13 -08005102 mBuilder.resolveContrastColor());
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02005103 big.addView(com.android.internal.R.id.media_actions, button);
Dan Sandler842dd772014-05-15 09:36:47 -04005104 }
5105 }
Selim Cinek5bf069a2015-11-10 19:14:27 -05005106 handleImage(big);
Dan Sandler842dd772014-05-15 09:36:47 -04005107 return big;
5108 }
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02005109
Selim Cinek5bf069a2015-11-10 19:14:27 -05005110 private void handleImage(RemoteViews contentView) {
5111 if (mBuilder.mN.mLargeIcon != null) {
5112 contentView.setViewLayoutMarginEnd(R.id.line1, 0);
Selim Cinekc848c3a2016-01-13 15:27:30 -08005113 contentView.setViewLayoutMarginEnd(R.id.text, 0);
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02005114 }
5115 }
5116
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02005117 /**
5118 * @hide
5119 */
5120 @Override
5121 protected boolean hasProgress() {
5122 return false;
5123 }
Dan Sandler842dd772014-05-15 09:36:47 -04005124 }
Griff Hazen61a9e862014-05-22 16:05:19 -07005125
Selim Cinek593610c2016-02-16 18:42:57 -08005126 /**
5127 * Notification style for custom views that are decorated by the system
5128 *
5129 * <p>Instead of providing a notification that is completely custom, a developer can set this
5130 * style and still obtain system decorations like the notification header with the expand
5131 * affordance and actions.
5132 *
5133 * <p>Use {@link android.app.Notification.Builder#setCustomContentView(RemoteViews)},
5134 * {@link android.app.Notification.Builder#setCustomBigContentView(RemoteViews)} and
5135 * {@link android.app.Notification.Builder#setCustomHeadsUpContentView(RemoteViews)} to set the
5136 * corresponding custom views to display.
5137 *
5138 * To use this style with your Notification, feed it to
5139 * {@link Notification.Builder#setStyle(android.app.Notification.Style)} like so:
5140 * <pre class="prettyprint">
5141 * Notification noti = new Notification.Builder()
5142 * .setSmallIcon(R.drawable.ic_stat_player)
5143 * .setLargeIcon(albumArtBitmap))
5144 * .setCustomContentView(contentView);
5145 * .setStyle(<b>new Notification.DecoratedCustomViewStyle()</b>)
5146 * .build();
5147 * </pre>
5148 */
5149 public static class DecoratedCustomViewStyle extends Style {
5150
5151 public DecoratedCustomViewStyle() {
5152 }
5153
5154 public DecoratedCustomViewStyle(Builder builder) {
5155 setBuilder(builder);
5156 }
5157
5158 /**
5159 * @hide
5160 */
5161 public boolean displayCustomViewInline() {
5162 return true;
5163 }
5164
5165 /**
5166 * @hide
5167 */
5168 @Override
5169 public RemoteViews makeContentView() {
5170 return makeStandardTemplateWithCustomContent(mBuilder.mN.contentView);
5171 }
5172
5173 /**
5174 * @hide
5175 */
5176 @Override
5177 public RemoteViews makeBigContentView() {
5178 return makeDecoratedBigContentView();
5179 }
5180
5181 /**
5182 * @hide
5183 */
5184 @Override
5185 public RemoteViews makeHeadsUpContentView() {
5186 return makeDecoratedHeadsUpContentView();
5187 }
5188
Selim Cinek593610c2016-02-16 18:42:57 -08005189 private RemoteViews makeDecoratedHeadsUpContentView() {
5190 RemoteViews headsUpContentView = mBuilder.mN.headsUpContentView == null
5191 ? mBuilder.mN.contentView
5192 : mBuilder.mN.headsUpContentView;
5193 if (mBuilder.mActions.size() == 0) {
5194 return makeStandardTemplateWithCustomContent(headsUpContentView);
5195 }
5196 RemoteViews remoteViews = mBuilder.applyStandardTemplateWithActions(
5197 mBuilder.getBigBaseLayoutResource());
Selim Cinek247fa012016-02-18 09:50:48 -08005198 buildIntoRemoteViewContent(remoteViews, headsUpContentView);
Selim Cinek593610c2016-02-16 18:42:57 -08005199 return remoteViews;
5200 }
5201
Selim Cinek593610c2016-02-16 18:42:57 -08005202 private RemoteViews makeStandardTemplateWithCustomContent(RemoteViews customContent) {
5203 RemoteViews remoteViews = mBuilder.applyStandardTemplate(
5204 mBuilder.getBaseLayoutResource());
Selim Cinek247fa012016-02-18 09:50:48 -08005205 buildIntoRemoteViewContent(remoteViews, customContent);
Selim Cinek593610c2016-02-16 18:42:57 -08005206 return remoteViews;
5207 }
5208
Selim Cinek593610c2016-02-16 18:42:57 -08005209 private RemoteViews makeDecoratedBigContentView() {
5210 RemoteViews bigContentView = mBuilder.mN.bigContentView == null
5211 ? mBuilder.mN.contentView
5212 : mBuilder.mN.bigContentView;
5213 if (mBuilder.mActions.size() == 0) {
5214 return makeStandardTemplateWithCustomContent(bigContentView);
5215 }
5216 RemoteViews remoteViews = mBuilder.applyStandardTemplateWithActions(
5217 mBuilder.getBigBaseLayoutResource());
Selim Cinek247fa012016-02-18 09:50:48 -08005218 buildIntoRemoteViewContent(remoteViews, bigContentView);
Selim Cinek593610c2016-02-16 18:42:57 -08005219 return remoteViews;
5220 }
Selim Cinek247fa012016-02-18 09:50:48 -08005221
5222 private void buildIntoRemoteViewContent(RemoteViews remoteViews,
5223 RemoteViews customContent) {
Adrian Roos5081c0d2016-02-26 16:04:19 -08005224 if (customContent != null) {
Selim Cinekf91017e2016-03-14 12:25:09 -07005225 // Need to clone customContent before adding, because otherwise it can no longer be
5226 // parceled independently of remoteViews.
Adrian Roos5081c0d2016-02-26 16:04:19 -08005227 customContent = customContent.clone();
Selim Cinekf91017e2016-03-14 12:25:09 -07005228 remoteViews.removeAllViews(R.id.notification_main_column);
5229 remoteViews.addView(R.id.notification_main_column, customContent);
Adrian Roos5081c0d2016-02-26 16:04:19 -08005230 }
Selim Cinek247fa012016-02-18 09:50:48 -08005231 // also update the end margin if there is an image
5232 int endMargin = mBuilder.mContext.getResources().getDimensionPixelSize(
5233 R.dimen.notification_content_margin_end);
5234 if (mBuilder.mN.mLargeIcon != null) {
5235 endMargin += mBuilder.mContext.getResources().getDimensionPixelSize(
5236 R.dimen.notification_content_picture_margin);
5237 }
5238 remoteViews.setViewLayoutMarginEnd(R.id.notification_main_column, endMargin);
5239 }
Selim Cinek593610c2016-02-16 18:42:57 -08005240 }
5241
Selim Cinek03eb3b72016-02-18 10:39:45 -08005242 /**
5243 * Notification style for media custom views that are decorated by the system
5244 *
5245 * <p>Instead of providing a media notification that is completely custom, a developer can set
5246 * this style and still obtain system decorations like the notification header with the expand
5247 * affordance and actions.
5248 *
5249 * <p>Use {@link android.app.Notification.Builder#setCustomContentView(RemoteViews)},
5250 * {@link android.app.Notification.Builder#setCustomBigContentView(RemoteViews)} and
5251 * {@link android.app.Notification.Builder#setCustomHeadsUpContentView(RemoteViews)} to set the
5252 * corresponding custom views to display.
5253 *
5254 * To use this style with your Notification, feed it to
5255 * {@link Notification.Builder#setStyle(android.app.Notification.Style)} like so:
5256 * <pre class="prettyprint">
5257 * Notification noti = new Notification.Builder()
5258 * .setSmallIcon(R.drawable.ic_stat_player)
5259 * .setLargeIcon(albumArtBitmap))
5260 * .setCustomContentView(contentView);
5261 * .setStyle(<b>new Notification.DecoratedMediaCustomViewStyle()</b>
5262 * .setMediaSession(mySession))
5263 * .build();
5264 * </pre>
5265 *
5266 * @see android.app.Notification.DecoratedCustomViewStyle
5267 * @see android.app.Notification.MediaStyle
5268 */
5269 public static class DecoratedMediaCustomViewStyle extends MediaStyle {
5270
5271 public DecoratedMediaCustomViewStyle() {
5272 }
5273
5274 public DecoratedMediaCustomViewStyle(Builder builder) {
5275 setBuilder(builder);
5276 }
5277
5278 /**
5279 * @hide
5280 */
5281 public boolean displayCustomViewInline() {
5282 return true;
5283 }
5284
5285 /**
5286 * @hide
5287 */
5288 @Override
5289 public RemoteViews makeContentView() {
5290 RemoteViews remoteViews = super.makeContentView();
5291 return buildIntoRemoteView(remoteViews, R.id.notification_content_container,
5292 mBuilder.mN.contentView);
5293 }
5294
5295 /**
5296 * @hide
5297 */
5298 @Override
5299 public RemoteViews makeBigContentView() {
5300 RemoteViews customRemoteView = mBuilder.mN.bigContentView != null
5301 ? mBuilder.mN.bigContentView
5302 : mBuilder.mN.contentView;
5303 return makeBigContentViewWithCustomContent(customRemoteView);
5304 }
5305
5306 private RemoteViews makeBigContentViewWithCustomContent(RemoteViews customRemoteView) {
5307 RemoteViews remoteViews = super.makeBigContentView();
5308 if (remoteViews != null) {
5309 return buildIntoRemoteView(remoteViews, R.id.notification_main_column,
5310 customRemoteView);
5311 } else if (customRemoteView != mBuilder.mN.contentView){
5312 remoteViews = super.makeContentView();
5313 return buildIntoRemoteView(remoteViews, R.id.notification_content_container,
5314 customRemoteView);
5315 } else {
5316 return null;
5317 }
5318 }
5319
5320 /**
5321 * @hide
5322 */
5323 @Override
5324 public RemoteViews makeHeadsUpContentView() {
5325 RemoteViews customRemoteView = mBuilder.mN.headsUpContentView != null
5326 ? mBuilder.mN.headsUpContentView
5327 : mBuilder.mN.contentView;
5328 return makeBigContentViewWithCustomContent(customRemoteView);
5329 }
5330
5331 private RemoteViews buildIntoRemoteView(RemoteViews remoteViews, int id,
5332 RemoteViews customContent) {
Adrian Roos5081c0d2016-02-26 16:04:19 -08005333 if (customContent != null) {
Selim Cinekf91017e2016-03-14 12:25:09 -07005334 // Need to clone customContent before adding, because otherwise it can no longer be
5335 // parceled independently of remoteViews.
Adrian Roos5081c0d2016-02-26 16:04:19 -08005336 customContent = customContent.clone();
Selim Cinekf91017e2016-03-14 12:25:09 -07005337 remoteViews.removeAllViews(id);
5338 remoteViews.addView(id, customContent);
Adrian Roos5081c0d2016-02-26 16:04:19 -08005339 }
Selim Cinek03eb3b72016-02-18 10:39:45 -08005340 return remoteViews;
5341 }
5342 }
5343
Christoph Studer4600f9b2014-07-22 22:44:43 +02005344 // When adding a new Style subclass here, don't forget to update
5345 // Builder.getNotificationStyleClass.
5346
Griff Hazen61a9e862014-05-22 16:05:19 -07005347 /**
5348 * Extender interface for use with {@link Builder#extend}. Extenders may be used to add
5349 * metadata or change options on a notification builder.
5350 */
5351 public interface Extender {
5352 /**
5353 * Apply this extender to a notification builder.
5354 * @param builder the builder to be modified.
5355 * @return the build object for chaining.
5356 */
5357 public Builder extend(Builder builder);
5358 }
5359
5360 /**
5361 * Helper class to add wearable extensions to notifications.
5362 * <p class="note"> See
5363 * <a href="{@docRoot}wear/notifications/creating.html">Creating Notifications
5364 * for Android Wear</a> for more information on how to use this class.
5365 * <p>
5366 * To create a notification with wearable extensions:
5367 * <ol>
5368 * <li>Create a {@link android.app.Notification.Builder}, setting any desired
5369 * properties.
5370 * <li>Create a {@link android.app.Notification.WearableExtender}.
5371 * <li>Set wearable-specific properties using the
5372 * {@code add} and {@code set} methods of {@link android.app.Notification.WearableExtender}.
5373 * <li>Call {@link android.app.Notification.Builder#extend} to apply the extensions to a
5374 * notification.
5375 * <li>Post the notification to the notification system with the
5376 * {@code NotificationManager.notify(...)} methods.
5377 * </ol>
5378 *
5379 * <pre class="prettyprint">
5380 * Notification notif = new Notification.Builder(mContext)
5381 * .setContentTitle(&quot;New mail from &quot; + sender.toString())
5382 * .setContentText(subject)
5383 * .setSmallIcon(R.drawable.new_mail)
5384 * .extend(new Notification.WearableExtender()
5385 * .setContentIcon(R.drawable.new_mail))
5386 * .build();
5387 * NotificationManager notificationManger =
5388 * (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
5389 * notificationManger.notify(0, notif);</pre>
5390 *
5391 * <p>Wearable extensions can be accessed on an existing notification by using the
5392 * {@code WearableExtender(Notification)} constructor,
5393 * and then using the {@code get} methods to access values.
5394 *
5395 * <pre class="prettyprint">
5396 * Notification.WearableExtender wearableExtender = new Notification.WearableExtender(
5397 * notification);
Griff Hazen14f57992014-05-26 09:07:14 -07005398 * List&lt;Notification&gt; pages = wearableExtender.getPages();</pre>
Griff Hazen61a9e862014-05-22 16:05:19 -07005399 */
5400 public static final class WearableExtender implements Extender {
5401 /**
5402 * Sentinel value for an action index that is unset.
5403 */
5404 public static final int UNSET_ACTION_INDEX = -1;
5405
5406 /**
5407 * Size value for use with {@link #setCustomSizePreset} to show this notification with
5408 * default sizing.
5409 * <p>For custom display notifications created using {@link #setDisplayIntent},
Paul Soulosaa4f4bf2015-08-04 11:59:45 -07005410 * the default is {@link #SIZE_MEDIUM}. All other notifications size automatically based
Griff Hazen61a9e862014-05-22 16:05:19 -07005411 * on their content.
5412 */
5413 public static final int SIZE_DEFAULT = 0;
5414
5415 /**
5416 * Size value for use with {@link #setCustomSizePreset} to show this notification
5417 * with an extra small size.
5418 * <p>This value is only applicable for custom display notifications created using
5419 * {@link #setDisplayIntent}.
5420 */
5421 public static final int SIZE_XSMALL = 1;
5422
5423 /**
5424 * Size value for use with {@link #setCustomSizePreset} to show this notification
5425 * with a small size.
5426 * <p>This value is only applicable for custom display notifications created using
5427 * {@link #setDisplayIntent}.
5428 */
5429 public static final int SIZE_SMALL = 2;
5430
5431 /**
5432 * Size value for use with {@link #setCustomSizePreset} to show this notification
5433 * with a medium size.
5434 * <p>This value is only applicable for custom display notifications created using
5435 * {@link #setDisplayIntent}.
5436 */
5437 public static final int SIZE_MEDIUM = 3;
5438
5439 /**
5440 * Size value for use with {@link #setCustomSizePreset} to show this notification
5441 * with a large size.
5442 * <p>This value is only applicable for custom display notifications created using
5443 * {@link #setDisplayIntent}.
5444 */
5445 public static final int SIZE_LARGE = 4;
5446
Griff Hazend5f11f92014-05-27 15:40:09 -07005447 /**
5448 * Size value for use with {@link #setCustomSizePreset} to show this notification
5449 * full screen.
5450 * <p>This value is only applicable for custom display notifications created using
5451 * {@link #setDisplayIntent}.
5452 */
5453 public static final int SIZE_FULL_SCREEN = 5;
5454
Griff Hazen5f2edfc2014-09-29 16:28:44 -07005455 /**
5456 * Sentinel value for use with {@link #setHintScreenTimeout} to keep the screen on for a
5457 * short amount of time when this notification is displayed on the screen. This
5458 * is the default value.
5459 */
5460 public static final int SCREEN_TIMEOUT_SHORT = 0;
5461
5462 /**
5463 * Sentinel value for use with {@link #setHintScreenTimeout} to keep the screen on
5464 * for a longer amount of time when this notification is displayed on the screen.
5465 */
5466 public static final int SCREEN_TIMEOUT_LONG = -1;
5467
Griff Hazen61a9e862014-05-22 16:05:19 -07005468 /** Notification extra which contains wearable extensions */
5469 private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS";
5470
Pete Gastaf6781d2014-10-07 15:17:05 -04005471 // Keys within EXTRA_WEARABLE_EXTENSIONS for wearable options.
Griff Hazen61a9e862014-05-22 16:05:19 -07005472 private static final String KEY_ACTIONS = "actions";
5473 private static final String KEY_FLAGS = "flags";
5474 private static final String KEY_DISPLAY_INTENT = "displayIntent";
5475 private static final String KEY_PAGES = "pages";
5476 private static final String KEY_BACKGROUND = "background";
5477 private static final String KEY_CONTENT_ICON = "contentIcon";
5478 private static final String KEY_CONTENT_ICON_GRAVITY = "contentIconGravity";
5479 private static final String KEY_CONTENT_ACTION_INDEX = "contentActionIndex";
5480 private static final String KEY_CUSTOM_SIZE_PRESET = "customSizePreset";
5481 private static final String KEY_CUSTOM_CONTENT_HEIGHT = "customContentHeight";
5482 private static final String KEY_GRAVITY = "gravity";
Griff Hazen5f2edfc2014-09-29 16:28:44 -07005483 private static final String KEY_HINT_SCREEN_TIMEOUT = "hintScreenTimeout";
Griff Hazen61a9e862014-05-22 16:05:19 -07005484
5485 // Flags bitwise-ored to mFlags
5486 private static final int FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE = 0x1;
5487 private static final int FLAG_HINT_HIDE_ICON = 1 << 1;
5488 private static final int FLAG_HINT_SHOW_BACKGROUND_ONLY = 1 << 2;
5489 private static final int FLAG_START_SCROLL_BOTTOM = 1 << 3;
Griff Hazen5f2edfc2014-09-29 16:28:44 -07005490 private static final int FLAG_HINT_AVOID_BACKGROUND_CLIPPING = 1 << 4;
Alex Hills4bcb06b2016-04-05 14:26:25 -04005491 private static final int FLAG_BIG_PICTURE_AMBIENT = 1 << 5;
Alex Hills9ab3a232016-04-05 14:54:56 -04005492 private static final int FLAG_HINT_CONTENT_INTENT_LAUNCHES_ACTIVITY = 1 << 6;
Griff Hazen61a9e862014-05-22 16:05:19 -07005493
5494 // Default value for flags integer
5495 private static final int DEFAULT_FLAGS = FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE;
5496
5497 private static final int DEFAULT_CONTENT_ICON_GRAVITY = Gravity.END;
5498 private static final int DEFAULT_GRAVITY = Gravity.BOTTOM;
5499
5500 private ArrayList<Action> mActions = new ArrayList<Action>();
5501 private int mFlags = DEFAULT_FLAGS;
5502 private PendingIntent mDisplayIntent;
5503 private ArrayList<Notification> mPages = new ArrayList<Notification>();
5504 private Bitmap mBackground;
5505 private int mContentIcon;
5506 private int mContentIconGravity = DEFAULT_CONTENT_ICON_GRAVITY;
5507 private int mContentActionIndex = UNSET_ACTION_INDEX;
5508 private int mCustomSizePreset = SIZE_DEFAULT;
5509 private int mCustomContentHeight;
5510 private int mGravity = DEFAULT_GRAVITY;
Griff Hazen5f2edfc2014-09-29 16:28:44 -07005511 private int mHintScreenTimeout;
Griff Hazen61a9e862014-05-22 16:05:19 -07005512
5513 /**
5514 * Create a {@link android.app.Notification.WearableExtender} with default
5515 * options.
5516 */
5517 public WearableExtender() {
5518 }
5519
5520 public WearableExtender(Notification notif) {
5521 Bundle wearableBundle = notif.extras.getBundle(EXTRA_WEARABLE_EXTENSIONS);
5522 if (wearableBundle != null) {
5523 List<Action> actions = wearableBundle.getParcelableArrayList(KEY_ACTIONS);
5524 if (actions != null) {
5525 mActions.addAll(actions);
5526 }
5527
5528 mFlags = wearableBundle.getInt(KEY_FLAGS, DEFAULT_FLAGS);
5529 mDisplayIntent = wearableBundle.getParcelable(KEY_DISPLAY_INTENT);
5530
5531 Notification[] pages = getNotificationArrayFromBundle(
5532 wearableBundle, KEY_PAGES);
5533 if (pages != null) {
5534 Collections.addAll(mPages, pages);
5535 }
5536
5537 mBackground = wearableBundle.getParcelable(KEY_BACKGROUND);
5538 mContentIcon = wearableBundle.getInt(KEY_CONTENT_ICON);
5539 mContentIconGravity = wearableBundle.getInt(KEY_CONTENT_ICON_GRAVITY,
5540 DEFAULT_CONTENT_ICON_GRAVITY);
5541 mContentActionIndex = wearableBundle.getInt(KEY_CONTENT_ACTION_INDEX,
5542 UNSET_ACTION_INDEX);
5543 mCustomSizePreset = wearableBundle.getInt(KEY_CUSTOM_SIZE_PRESET,
5544 SIZE_DEFAULT);
5545 mCustomContentHeight = wearableBundle.getInt(KEY_CUSTOM_CONTENT_HEIGHT);
5546 mGravity = wearableBundle.getInt(KEY_GRAVITY, DEFAULT_GRAVITY);
Griff Hazen5f2edfc2014-09-29 16:28:44 -07005547 mHintScreenTimeout = wearableBundle.getInt(KEY_HINT_SCREEN_TIMEOUT);
Griff Hazen61a9e862014-05-22 16:05:19 -07005548 }
5549 }
5550
5551 /**
5552 * Apply wearable extensions to a notification that is being built. This is typically
5553 * called by the {@link android.app.Notification.Builder#extend} method of
5554 * {@link android.app.Notification.Builder}.
5555 */
5556 @Override
5557 public Notification.Builder extend(Notification.Builder builder) {
5558 Bundle wearableBundle = new Bundle();
5559
5560 if (!mActions.isEmpty()) {
5561 wearableBundle.putParcelableArrayList(KEY_ACTIONS, mActions);
5562 }
5563 if (mFlags != DEFAULT_FLAGS) {
5564 wearableBundle.putInt(KEY_FLAGS, mFlags);
5565 }
5566 if (mDisplayIntent != null) {
5567 wearableBundle.putParcelable(KEY_DISPLAY_INTENT, mDisplayIntent);
5568 }
5569 if (!mPages.isEmpty()) {
5570 wearableBundle.putParcelableArray(KEY_PAGES, mPages.toArray(
5571 new Notification[mPages.size()]));
5572 }
5573 if (mBackground != null) {
5574 wearableBundle.putParcelable(KEY_BACKGROUND, mBackground);
5575 }
5576 if (mContentIcon != 0) {
5577 wearableBundle.putInt(KEY_CONTENT_ICON, mContentIcon);
5578 }
5579 if (mContentIconGravity != DEFAULT_CONTENT_ICON_GRAVITY) {
5580 wearableBundle.putInt(KEY_CONTENT_ICON_GRAVITY, mContentIconGravity);
5581 }
5582 if (mContentActionIndex != UNSET_ACTION_INDEX) {
5583 wearableBundle.putInt(KEY_CONTENT_ACTION_INDEX,
5584 mContentActionIndex);
5585 }
5586 if (mCustomSizePreset != SIZE_DEFAULT) {
5587 wearableBundle.putInt(KEY_CUSTOM_SIZE_PRESET, mCustomSizePreset);
5588 }
5589 if (mCustomContentHeight != 0) {
5590 wearableBundle.putInt(KEY_CUSTOM_CONTENT_HEIGHT, mCustomContentHeight);
5591 }
5592 if (mGravity != DEFAULT_GRAVITY) {
5593 wearableBundle.putInt(KEY_GRAVITY, mGravity);
5594 }
Griff Hazen5f2edfc2014-09-29 16:28:44 -07005595 if (mHintScreenTimeout != 0) {
5596 wearableBundle.putInt(KEY_HINT_SCREEN_TIMEOUT, mHintScreenTimeout);
5597 }
Griff Hazen61a9e862014-05-22 16:05:19 -07005598
5599 builder.getExtras().putBundle(EXTRA_WEARABLE_EXTENSIONS, wearableBundle);
5600 return builder;
5601 }
5602
5603 @Override
5604 public WearableExtender clone() {
5605 WearableExtender that = new WearableExtender();
5606 that.mActions = new ArrayList<Action>(this.mActions);
5607 that.mFlags = this.mFlags;
5608 that.mDisplayIntent = this.mDisplayIntent;
5609 that.mPages = new ArrayList<Notification>(this.mPages);
5610 that.mBackground = this.mBackground;
5611 that.mContentIcon = this.mContentIcon;
5612 that.mContentIconGravity = this.mContentIconGravity;
5613 that.mContentActionIndex = this.mContentActionIndex;
5614 that.mCustomSizePreset = this.mCustomSizePreset;
5615 that.mCustomContentHeight = this.mCustomContentHeight;
5616 that.mGravity = this.mGravity;
Griff Hazen5f2edfc2014-09-29 16:28:44 -07005617 that.mHintScreenTimeout = this.mHintScreenTimeout;
Griff Hazen61a9e862014-05-22 16:05:19 -07005618 return that;
5619 }
5620
5621 /**
5622 * Add a wearable action to this notification.
5623 *
5624 * <p>When wearable actions are added using this method, the set of actions that
5625 * show on a wearable device splits from devices that only show actions added
5626 * using {@link android.app.Notification.Builder#addAction}. This allows for customization
5627 * of which actions display on different devices.
5628 *
5629 * @param action the action to add to this notification
5630 * @return this object for method chaining
5631 * @see android.app.Notification.Action
5632 */
5633 public WearableExtender addAction(Action action) {
5634 mActions.add(action);
5635 return this;
5636 }
5637
5638 /**
5639 * Adds wearable actions to this notification.
5640 *
5641 * <p>When wearable actions are added using this method, the set of actions that
5642 * show on a wearable device splits from devices that only show actions added
5643 * using {@link android.app.Notification.Builder#addAction}. This allows for customization
5644 * of which actions display on different devices.
5645 *
5646 * @param actions the actions to add to this notification
5647 * @return this object for method chaining
5648 * @see android.app.Notification.Action
5649 */
5650 public WearableExtender addActions(List<Action> actions) {
5651 mActions.addAll(actions);
5652 return this;
5653 }
5654
5655 /**
5656 * Clear all wearable actions present on this builder.
5657 * @return this object for method chaining.
5658 * @see #addAction
5659 */
5660 public WearableExtender clearActions() {
5661 mActions.clear();
5662 return this;
5663 }
5664
5665 /**
5666 * Get the wearable actions present on this notification.
5667 */
5668 public List<Action> getActions() {
5669 return mActions;
5670 }
5671
5672 /**
5673 * Set an intent to launch inside of an activity view when displaying
Griff Hazen14f57992014-05-26 09:07:14 -07005674 * this notification. The {@link PendingIntent} provided should be for an activity.
5675 *
5676 * <pre class="prettyprint">
5677 * Intent displayIntent = new Intent(context, MyDisplayActivity.class);
5678 * PendingIntent displayPendingIntent = PendingIntent.getActivity(context,
5679 * 0, displayIntent, PendingIntent.FLAG_UPDATE_CURRENT);
5680 * Notification notif = new Notification.Builder(context)
5681 * .extend(new Notification.WearableExtender()
5682 * .setDisplayIntent(displayPendingIntent)
5683 * .setCustomSizePreset(Notification.WearableExtender.SIZE_MEDIUM))
5684 * .build();</pre>
5685 *
5686 * <p>The activity to launch needs to allow embedding, must be exported, and
Griff Hazen831ca9d2014-06-17 00:38:38 -07005687 * should have an empty task affinity. It is also recommended to use the device
5688 * default light theme.
Griff Hazen14f57992014-05-26 09:07:14 -07005689 *
5690 * <p>Example AndroidManifest.xml entry:
5691 * <pre class="prettyprint">
5692 * &lt;activity android:name=&quot;com.example.MyDisplayActivity&quot;
5693 * android:exported=&quot;true&quot;
5694 * android:allowEmbedded=&quot;true&quot;
Griff Hazen831ca9d2014-06-17 00:38:38 -07005695 * android:taskAffinity=&quot;&quot;
5696 * android:theme=&quot;@android:style/Theme.DeviceDefault.Light&quot; /&gt;</pre>
Griff Hazen61a9e862014-05-22 16:05:19 -07005697 *
5698 * @param intent the {@link PendingIntent} for an activity
5699 * @return this object for method chaining
5700 * @see android.app.Notification.WearableExtender#getDisplayIntent
5701 */
5702 public WearableExtender setDisplayIntent(PendingIntent intent) {
5703 mDisplayIntent = intent;
5704 return this;
5705 }
5706
5707 /**
5708 * Get the intent to launch inside of an activity view when displaying this
5709 * notification. This {@code PendingIntent} should be for an activity.
5710 */
5711 public PendingIntent getDisplayIntent() {
5712 return mDisplayIntent;
5713 }
5714
5715 /**
5716 * Add an additional page of content to display with this notification. The current
5717 * notification forms the first page, and pages added using this function form
5718 * subsequent pages. This field can be used to separate a notification into multiple
5719 * sections.
5720 *
5721 * @param page the notification to add as another page
5722 * @return this object for method chaining
5723 * @see android.app.Notification.WearableExtender#getPages
5724 */
5725 public WearableExtender addPage(Notification page) {
5726 mPages.add(page);
5727 return this;
5728 }
5729
5730 /**
5731 * Add additional pages of content to display with this notification. The current
5732 * notification forms the first page, and pages added using this function form
5733 * subsequent pages. This field can be used to separate a notification into multiple
5734 * sections.
5735 *
5736 * @param pages a list of notifications
5737 * @return this object for method chaining
5738 * @see android.app.Notification.WearableExtender#getPages
5739 */
5740 public WearableExtender addPages(List<Notification> pages) {
5741 mPages.addAll(pages);
5742 return this;
5743 }
5744
5745 /**
5746 * Clear all additional pages present on this builder.
5747 * @return this object for method chaining.
5748 * @see #addPage
5749 */
5750 public WearableExtender clearPages() {
5751 mPages.clear();
5752 return this;
5753 }
5754
5755 /**
5756 * Get the array of additional pages of content for displaying this notification. The
5757 * current notification forms the first page, and elements within this array form
5758 * subsequent pages. This field can be used to separate a notification into multiple
5759 * sections.
5760 * @return the pages for this notification
5761 */
5762 public List<Notification> getPages() {
5763 return mPages;
5764 }
5765
5766 /**
5767 * Set a background image to be displayed behind the notification content.
5768 * Contrary to the {@link android.app.Notification.BigPictureStyle}, this background
5769 * will work with any notification style.
5770 *
5771 * @param background the background bitmap
5772 * @return this object for method chaining
5773 * @see android.app.Notification.WearableExtender#getBackground
5774 */
5775 public WearableExtender setBackground(Bitmap background) {
5776 mBackground = background;
5777 return this;
5778 }
5779
5780 /**
5781 * Get a background image to be displayed behind the notification content.
5782 * Contrary to the {@link android.app.Notification.BigPictureStyle}, this background
5783 * will work with any notification style.
5784 *
5785 * @return the background image
5786 * @see android.app.Notification.WearableExtender#setBackground
5787 */
5788 public Bitmap getBackground() {
5789 return mBackground;
5790 }
5791
5792 /**
5793 * Set an icon that goes with the content of this notification.
5794 */
5795 public WearableExtender setContentIcon(int icon) {
5796 mContentIcon = icon;
5797 return this;
5798 }
5799
5800 /**
5801 * Get an icon that goes with the content of this notification.
5802 */
5803 public int getContentIcon() {
5804 return mContentIcon;
5805 }
5806
5807 /**
5808 * Set the gravity that the content icon should have within the notification display.
5809 * Supported values include {@link android.view.Gravity#START} and
5810 * {@link android.view.Gravity#END}. The default value is {@link android.view.Gravity#END}.
5811 * @see #setContentIcon
5812 */
5813 public WearableExtender setContentIconGravity(int contentIconGravity) {
5814 mContentIconGravity = contentIconGravity;
5815 return this;
5816 }
5817
5818 /**
5819 * Get the gravity that the content icon should have within the notification display.
5820 * Supported values include {@link android.view.Gravity#START} and
5821 * {@link android.view.Gravity#END}. The default value is {@link android.view.Gravity#END}.
5822 * @see #getContentIcon
5823 */
5824 public int getContentIconGravity() {
5825 return mContentIconGravity;
5826 }
5827
5828 /**
5829 * Set an action from this notification's actions to be clickable with the content of
Griff Hazen14f57992014-05-26 09:07:14 -07005830 * this notification. This action will no longer display separately from the
5831 * notification's content.
5832 *
Griff Hazenca48d352014-05-28 22:37:13 -07005833 * <p>For notifications with multiple pages, child pages can also have content actions
Griff Hazen14f57992014-05-26 09:07:14 -07005834 * set, although the list of available actions comes from the main notification and not
5835 * from the child page's notification.
5836 *
5837 * @param actionIndex The index of the action to hoist onto the current notification page.
5838 * If wearable actions were added to the main notification, this index
5839 * will apply to that list, otherwise it will apply to the regular
5840 * actions list.
Griff Hazen61a9e862014-05-22 16:05:19 -07005841 */
5842 public WearableExtender setContentAction(int actionIndex) {
5843 mContentActionIndex = actionIndex;
5844 return this;
5845 }
5846
5847 /**
Griff Hazenca48d352014-05-28 22:37:13 -07005848 * Get the index of the notification action, if any, that was specified as being clickable
5849 * with the content of this notification. This action will no longer display separately
Griff Hazen14f57992014-05-26 09:07:14 -07005850 * from the notification's content.
Griff Hazen61a9e862014-05-22 16:05:19 -07005851 *
Griff Hazenca48d352014-05-28 22:37:13 -07005852 * <p>For notifications with multiple pages, child pages can also have content actions
Griff Hazen14f57992014-05-26 09:07:14 -07005853 * set, although the list of available actions comes from the main notification and not
5854 * from the child page's notification.
5855 *
5856 * <p>If wearable specific actions were added to the main notification, this index will
5857 * apply to that list, otherwise it will apply to the regular actions list.
Griff Hazenca48d352014-05-28 22:37:13 -07005858 *
5859 * @return the action index or {@link #UNSET_ACTION_INDEX} if no action was selected.
Griff Hazen61a9e862014-05-22 16:05:19 -07005860 */
5861 public int getContentAction() {
5862 return mContentActionIndex;
5863 }
5864
5865 /**
5866 * Set the gravity that this notification should have within the available viewport space.
5867 * Supported values include {@link android.view.Gravity#TOP},
5868 * {@link android.view.Gravity#CENTER_VERTICAL} and {@link android.view.Gravity#BOTTOM}.
5869 * The default value is {@link android.view.Gravity#BOTTOM}.
5870 */
5871 public WearableExtender setGravity(int gravity) {
5872 mGravity = gravity;
5873 return this;
5874 }
5875
5876 /**
5877 * Get the gravity that this notification should have within the available viewport space.
5878 * Supported values include {@link android.view.Gravity#TOP},
5879 * {@link android.view.Gravity#CENTER_VERTICAL} and {@link android.view.Gravity#BOTTOM}.
5880 * The default value is {@link android.view.Gravity#BOTTOM}.
5881 */
5882 public int getGravity() {
5883 return mGravity;
5884 }
5885
5886 /**
5887 * Set the custom size preset for the display of this notification out of the available
5888 * presets found in {@link android.app.Notification.WearableExtender}, e.g.
5889 * {@link #SIZE_LARGE}.
5890 * <p>Some custom size presets are only applicable for custom display notifications created
5891 * using {@link android.app.Notification.WearableExtender#setDisplayIntent}. Check the
5892 * documentation for the preset in question. See also
5893 * {@link #setCustomContentHeight} and {@link #getCustomSizePreset}.
5894 */
5895 public WearableExtender setCustomSizePreset(int sizePreset) {
5896 mCustomSizePreset = sizePreset;
5897 return this;
5898 }
5899
5900 /**
5901 * Get the custom size preset for the display of this notification out of the available
5902 * presets found in {@link android.app.Notification.WearableExtender}, e.g.
5903 * {@link #SIZE_LARGE}.
5904 * <p>Some custom size presets are only applicable for custom display notifications created
5905 * using {@link #setDisplayIntent}. Check the documentation for the preset in question.
5906 * See also {@link #setCustomContentHeight} and {@link #setCustomSizePreset}.
5907 */
5908 public int getCustomSizePreset() {
5909 return mCustomSizePreset;
5910 }
5911
5912 /**
5913 * Set the custom height in pixels for the display of this notification's content.
5914 * <p>This option is only available for custom display notifications created
5915 * using {@link android.app.Notification.WearableExtender#setDisplayIntent}. See also
5916 * {@link android.app.Notification.WearableExtender#setCustomSizePreset} and
5917 * {@link #getCustomContentHeight}.
5918 */
5919 public WearableExtender setCustomContentHeight(int height) {
5920 mCustomContentHeight = height;
5921 return this;
5922 }
5923
5924 /**
5925 * Get the custom height in pixels for the display of this notification's content.
5926 * <p>This option is only available for custom display notifications created
5927 * using {@link #setDisplayIntent}. See also {@link #setCustomSizePreset} and
5928 * {@link #setCustomContentHeight}.
5929 */
5930 public int getCustomContentHeight() {
5931 return mCustomContentHeight;
5932 }
5933
5934 /**
5935 * Set whether the scrolling position for the contents of this notification should start
5936 * at the bottom of the contents instead of the top when the contents are too long to
5937 * display within the screen. Default is false (start scroll at the top).
5938 */
5939 public WearableExtender setStartScrollBottom(boolean startScrollBottom) {
5940 setFlag(FLAG_START_SCROLL_BOTTOM, startScrollBottom);
5941 return this;
5942 }
5943
5944 /**
5945 * Get whether the scrolling position for the contents of this notification should start
5946 * at the bottom of the contents instead of the top when the contents are too long to
5947 * display within the screen. Default is false (start scroll at the top).
5948 */
5949 public boolean getStartScrollBottom() {
5950 return (mFlags & FLAG_START_SCROLL_BOTTOM) != 0;
5951 }
5952
5953 /**
5954 * Set whether the content intent is available when the wearable device is not connected
5955 * to a companion device. The user can still trigger this intent when the wearable device
5956 * is offline, but a visual hint will indicate that the content intent may not be available.
5957 * Defaults to true.
5958 */
5959 public WearableExtender setContentIntentAvailableOffline(
5960 boolean contentIntentAvailableOffline) {
5961 setFlag(FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE, contentIntentAvailableOffline);
5962 return this;
5963 }
5964
5965 /**
5966 * Get whether the content intent is available when the wearable device is not connected
5967 * to a companion device. The user can still trigger this intent when the wearable device
5968 * is offline, but a visual hint will indicate that the content intent may not be available.
5969 * Defaults to true.
5970 */
5971 public boolean getContentIntentAvailableOffline() {
5972 return (mFlags & FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE) != 0;
5973 }
5974
5975 /**
5976 * Set a hint that this notification's icon should not be displayed.
5977 * @param hintHideIcon {@code true} to hide the icon, {@code false} otherwise.
5978 * @return this object for method chaining
5979 */
5980 public WearableExtender setHintHideIcon(boolean hintHideIcon) {
5981 setFlag(FLAG_HINT_HIDE_ICON, hintHideIcon);
5982 return this;
5983 }
5984
5985 /**
5986 * Get a hint that this notification's icon should not be displayed.
5987 * @return {@code true} if this icon should not be displayed, false otherwise.
5988 * The default value is {@code false} if this was never set.
5989 */
5990 public boolean getHintHideIcon() {
5991 return (mFlags & FLAG_HINT_HIDE_ICON) != 0;
5992 }
5993
5994 /**
5995 * Set a visual hint that only the background image of this notification should be
5996 * displayed, and other semantic content should be hidden. This hint is only applicable
5997 * to sub-pages added using {@link #addPage}.
5998 */
5999 public WearableExtender setHintShowBackgroundOnly(boolean hintShowBackgroundOnly) {
6000 setFlag(FLAG_HINT_SHOW_BACKGROUND_ONLY, hintShowBackgroundOnly);
6001 return this;
6002 }
6003
6004 /**
6005 * Get a visual hint that only the background image of this notification should be
6006 * displayed, and other semantic content should be hidden. This hint is only applicable
6007 * to sub-pages added using {@link android.app.Notification.WearableExtender#addPage}.
6008 */
6009 public boolean getHintShowBackgroundOnly() {
6010 return (mFlags & FLAG_HINT_SHOW_BACKGROUND_ONLY) != 0;
6011 }
6012
Griff Hazen5f2edfc2014-09-29 16:28:44 -07006013 /**
Griff Hazen9c5be4e2014-11-17 17:47:50 -08006014 * Set a hint that this notification's background should not be clipped if possible,
6015 * and should instead be resized to fully display on the screen, retaining the aspect
6016 * ratio of the image. This can be useful for images like barcodes or qr codes.
Griff Hazen5f2edfc2014-09-29 16:28:44 -07006017 * @param hintAvoidBackgroundClipping {@code true} to avoid clipping if possible.
6018 * @return this object for method chaining
6019 */
6020 public WearableExtender setHintAvoidBackgroundClipping(
6021 boolean hintAvoidBackgroundClipping) {
6022 setFlag(FLAG_HINT_AVOID_BACKGROUND_CLIPPING, hintAvoidBackgroundClipping);
6023 return this;
6024 }
6025
6026 /**
Griff Hazen9c5be4e2014-11-17 17:47:50 -08006027 * Get a hint that this notification's background should not be clipped if possible,
6028 * and should instead be resized to fully display on the screen, retaining the aspect
6029 * ratio of the image. This can be useful for images like barcodes or qr codes.
Griff Hazen5f2edfc2014-09-29 16:28:44 -07006030 * @return {@code true} if it's ok if the background is clipped on the screen, false
6031 * otherwise. The default value is {@code false} if this was never set.
6032 */
6033 public boolean getHintAvoidBackgroundClipping() {
6034 return (mFlags & FLAG_HINT_AVOID_BACKGROUND_CLIPPING) != 0;
6035 }
6036
6037 /**
6038 * Set a hint that the screen should remain on for at least this duration when
6039 * this notification is displayed on the screen.
6040 * @param timeout The requested screen timeout in milliseconds. Can also be either
6041 * {@link #SCREEN_TIMEOUT_SHORT} or {@link #SCREEN_TIMEOUT_LONG}.
6042 * @return this object for method chaining
6043 */
6044 public WearableExtender setHintScreenTimeout(int timeout) {
6045 mHintScreenTimeout = timeout;
6046 return this;
6047 }
6048
6049 /**
6050 * Get the duration, in milliseconds, that the screen should remain on for
6051 * when this notification is displayed.
6052 * @return the duration in milliseconds if > 0, or either one of the sentinel values
6053 * {@link #SCREEN_TIMEOUT_SHORT} or {@link #SCREEN_TIMEOUT_LONG}.
6054 */
6055 public int getHintScreenTimeout() {
6056 return mHintScreenTimeout;
6057 }
6058
Alex Hills9ab3a232016-04-05 14:54:56 -04006059 /**
Alex Hills4bcb06b2016-04-05 14:26:25 -04006060 * Set a hint that this notification's {@link BigPictureStyle} (if present) should be
6061 * converted to low-bit and displayed in ambient mode, especially useful for barcodes and
6062 * qr codes, as well as other simple black-and-white tickets.
6063 * @param hintAmbientBigPicture {@code true} to enable converstion and ambient.
6064 * @return this object for method chaining
6065 */
6066 public WearableExtender setHintAmbientBigPicture(boolean hintAmbientBigPicture) {
6067 setFlag(FLAG_BIG_PICTURE_AMBIENT, hintAmbientBigPicture);
6068 return this;
6069 }
6070
6071 /**
6072 * Get a hint that this notification's {@link BigPictureStyle} (if present) should be
6073 * converted to low-bit and displayed in ambient mode, especially useful for barcodes and
6074 * qr codes, as well as other simple black-and-white tickets.
6075 * @return {@code true} if it should be displayed in ambient, false otherwise
6076 * otherwise. The default value is {@code false} if this was never set.
6077 */
6078 public boolean getHintAmbientBigPicture() {
6079 return (mFlags & FLAG_BIG_PICTURE_AMBIENT) != 0;
6080 }
6081
6082 /**
Alex Hills9ab3a232016-04-05 14:54:56 -04006083 * Set a hint that this notification's content intent will launch an {@link Activity}
6084 * directly, telling the platform that it can generate the appropriate transitions.
6085 * @param hintContentIntentLaunchesActivity {@code true} if the content intent will launch
6086 * an activity and transitions should be generated, false otherwise.
6087 * @return this object for method chaining
6088 */
6089 public WearableExtender setHintContentIntentLaunchesActivity(
6090 boolean hintContentIntentLaunchesActivity) {
6091 setFlag(FLAG_HINT_CONTENT_INTENT_LAUNCHES_ACTIVITY, hintContentIntentLaunchesActivity);
6092 return this;
6093 }
6094
6095 /**
6096 * Get a hint that this notification's content intent will launch an {@link Activity}
6097 * directly, telling the platform that it can generate the appropriate transitions
6098 * @return {@code true} if the content intent will launch an activity and transitions should
6099 * be generated, false otherwise. The default value is {@code false} if this was never set.
6100 */
6101 public boolean getHintContentIntentLaunchesActivity() {
6102 return (mFlags & FLAG_HINT_CONTENT_INTENT_LAUNCHES_ACTIVITY) != 0;
6103 }
6104
Griff Hazen61a9e862014-05-22 16:05:19 -07006105 private void setFlag(int mask, boolean value) {
6106 if (value) {
6107 mFlags |= mask;
6108 } else {
6109 mFlags &= ~mask;
6110 }
6111 }
6112 }
6113
6114 /**
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08006115 * <p>Helper class to add Android Auto extensions to notifications. To create a notification
6116 * with car extensions:
6117 *
6118 * <ol>
6119 * <li>Create an {@link Notification.Builder}, setting any desired
6120 * properties.
6121 * <li>Create a {@link CarExtender}.
6122 * <li>Set car-specific properties using the {@code add} and {@code set} methods of
6123 * {@link CarExtender}.
6124 * <li>Call {@link Notification.Builder#extend(Notification.Extender)}
6125 * to apply the extensions to a notification.
6126 * </ol>
6127 *
6128 * <pre class="prettyprint">
6129 * Notification notification = new Notification.Builder(context)
6130 * ...
6131 * .extend(new CarExtender()
6132 * .set*(...))
6133 * .build();
6134 * </pre>
6135 *
6136 * <p>Car extensions can be accessed on an existing notification by using the
6137 * {@code CarExtender(Notification)} constructor, and then using the {@code get} methods
6138 * to access values.
6139 */
6140 public static final class CarExtender implements Extender {
6141 private static final String TAG = "CarExtender";
6142
6143 private static final String EXTRA_CAR_EXTENDER = "android.car.EXTENSIONS";
6144 private static final String EXTRA_LARGE_ICON = "large_icon";
6145 private static final String EXTRA_CONVERSATION = "car_conversation";
6146 private static final String EXTRA_COLOR = "app_color";
6147
6148 private Bitmap mLargeIcon;
6149 private UnreadConversation mUnreadConversation;
6150 private int mColor = Notification.COLOR_DEFAULT;
6151
6152 /**
6153 * Create a {@link CarExtender} with default options.
6154 */
6155 public CarExtender() {
6156 }
6157
6158 /**
6159 * Create a {@link CarExtender} from the CarExtender options of an existing Notification.
6160 *
6161 * @param notif The notification from which to copy options.
6162 */
6163 public CarExtender(Notification notif) {
6164 Bundle carBundle = notif.extras == null ?
6165 null : notif.extras.getBundle(EXTRA_CAR_EXTENDER);
6166 if (carBundle != null) {
6167 mLargeIcon = carBundle.getParcelable(EXTRA_LARGE_ICON);
6168 mColor = carBundle.getInt(EXTRA_COLOR, Notification.COLOR_DEFAULT);
6169
6170 Bundle b = carBundle.getBundle(EXTRA_CONVERSATION);
6171 mUnreadConversation = UnreadConversation.getUnreadConversationFromBundle(b);
6172 }
6173 }
6174
6175 /**
6176 * Apply car extensions to a notification that is being built. This is typically called by
6177 * the {@link Notification.Builder#extend(Notification.Extender)}
6178 * method of {@link Notification.Builder}.
6179 */
6180 @Override
6181 public Notification.Builder extend(Notification.Builder builder) {
6182 Bundle carExtensions = new Bundle();
6183
6184 if (mLargeIcon != null) {
6185 carExtensions.putParcelable(EXTRA_LARGE_ICON, mLargeIcon);
6186 }
6187 if (mColor != Notification.COLOR_DEFAULT) {
6188 carExtensions.putInt(EXTRA_COLOR, mColor);
6189 }
6190
6191 if (mUnreadConversation != null) {
6192 Bundle b = mUnreadConversation.getBundleForUnreadConversation();
6193 carExtensions.putBundle(EXTRA_CONVERSATION, b);
6194 }
6195
6196 builder.getExtras().putBundle(EXTRA_CAR_EXTENDER, carExtensions);
6197 return builder;
6198 }
6199
6200 /**
6201 * Sets the accent color to use when Android Auto presents the notification.
6202 *
6203 * Android Auto uses the color set with {@link Notification.Builder#setColor(int)}
6204 * to accent the displayed notification. However, not all colors are acceptable in an
6205 * automotive setting. This method can be used to override the color provided in the
6206 * notification in such a situation.
6207 */
Tor Norbye80756e32015-03-02 09:39:27 -08006208 public CarExtender setColor(@ColorInt int color) {
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08006209 mColor = color;
6210 return this;
6211 }
6212
6213 /**
6214 * Gets the accent color.
6215 *
Julia Reynoldsd9228f12015-10-20 10:37:27 -04006216 * @see #setColor
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08006217 */
Tor Norbye80756e32015-03-02 09:39:27 -08006218 @ColorInt
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08006219 public int getColor() {
6220 return mColor;
6221 }
6222
6223 /**
6224 * Sets the large icon of the car notification.
6225 *
6226 * If no large icon is set in the extender, Android Auto will display the icon
6227 * specified by {@link Notification.Builder#setLargeIcon(android.graphics.Bitmap)}
6228 *
6229 * @param largeIcon The large icon to use in the car notification.
6230 * @return This object for method chaining.
6231 */
6232 public CarExtender setLargeIcon(Bitmap largeIcon) {
6233 mLargeIcon = largeIcon;
6234 return this;
6235 }
6236
6237 /**
6238 * Gets the large icon used in this car notification, or null if no icon has been set.
6239 *
6240 * @return The large icon for the car notification.
6241 * @see CarExtender#setLargeIcon
6242 */
6243 public Bitmap getLargeIcon() {
6244 return mLargeIcon;
6245 }
6246
6247 /**
6248 * Sets the unread conversation in a message notification.
6249 *
6250 * @param unreadConversation The unread part of the conversation this notification conveys.
6251 * @return This object for method chaining.
6252 */
6253 public CarExtender setUnreadConversation(UnreadConversation unreadConversation) {
6254 mUnreadConversation = unreadConversation;
6255 return this;
6256 }
6257
6258 /**
6259 * Returns the unread conversation conveyed by this notification.
6260 * @see #setUnreadConversation(UnreadConversation)
6261 */
6262 public UnreadConversation getUnreadConversation() {
6263 return mUnreadConversation;
6264 }
6265
6266 /**
6267 * A class which holds the unread messages from a conversation.
6268 */
6269 public static class UnreadConversation {
6270 private static final String KEY_AUTHOR = "author";
6271 private static final String KEY_TEXT = "text";
6272 private static final String KEY_MESSAGES = "messages";
6273 private static final String KEY_REMOTE_INPUT = "remote_input";
6274 private static final String KEY_ON_REPLY = "on_reply";
6275 private static final String KEY_ON_READ = "on_read";
6276 private static final String KEY_PARTICIPANTS = "participants";
6277 private static final String KEY_TIMESTAMP = "timestamp";
6278
6279 private final String[] mMessages;
6280 private final RemoteInput mRemoteInput;
6281 private final PendingIntent mReplyPendingIntent;
6282 private final PendingIntent mReadPendingIntent;
6283 private final String[] mParticipants;
6284 private final long mLatestTimestamp;
6285
6286 UnreadConversation(String[] messages, RemoteInput remoteInput,
6287 PendingIntent replyPendingIntent, PendingIntent readPendingIntent,
6288 String[] participants, long latestTimestamp) {
6289 mMessages = messages;
6290 mRemoteInput = remoteInput;
6291 mReadPendingIntent = readPendingIntent;
6292 mReplyPendingIntent = replyPendingIntent;
6293 mParticipants = participants;
6294 mLatestTimestamp = latestTimestamp;
6295 }
6296
6297 /**
6298 * Gets the list of messages conveyed by this notification.
6299 */
6300 public String[] getMessages() {
6301 return mMessages;
6302 }
6303
6304 /**
6305 * Gets the remote input that will be used to convey the response to a message list, or
6306 * null if no such remote input exists.
6307 */
6308 public RemoteInput getRemoteInput() {
6309 return mRemoteInput;
6310 }
6311
6312 /**
6313 * Gets the pending intent that will be triggered when the user replies to this
6314 * notification.
6315 */
6316 public PendingIntent getReplyPendingIntent() {
6317 return mReplyPendingIntent;
6318 }
6319
6320 /**
6321 * Gets the pending intent that Android Auto will send after it reads aloud all messages
6322 * in this object's message list.
6323 */
6324 public PendingIntent getReadPendingIntent() {
6325 return mReadPendingIntent;
6326 }
6327
6328 /**
6329 * Gets the participants in the conversation.
6330 */
6331 public String[] getParticipants() {
6332 return mParticipants;
6333 }
6334
6335 /**
6336 * Gets the firs participant in the conversation.
6337 */
6338 public String getParticipant() {
6339 return mParticipants.length > 0 ? mParticipants[0] : null;
6340 }
6341
6342 /**
6343 * Gets the timestamp of the conversation.
6344 */
6345 public long getLatestTimestamp() {
6346 return mLatestTimestamp;
6347 }
6348
6349 Bundle getBundleForUnreadConversation() {
6350 Bundle b = new Bundle();
6351 String author = null;
6352 if (mParticipants != null && mParticipants.length > 1) {
6353 author = mParticipants[0];
6354 }
6355 Parcelable[] messages = new Parcelable[mMessages.length];
6356 for (int i = 0; i < messages.length; i++) {
6357 Bundle m = new Bundle();
6358 m.putString(KEY_TEXT, mMessages[i]);
6359 m.putString(KEY_AUTHOR, author);
6360 messages[i] = m;
6361 }
6362 b.putParcelableArray(KEY_MESSAGES, messages);
6363 if (mRemoteInput != null) {
6364 b.putParcelable(KEY_REMOTE_INPUT, mRemoteInput);
6365 }
6366 b.putParcelable(KEY_ON_REPLY, mReplyPendingIntent);
6367 b.putParcelable(KEY_ON_READ, mReadPendingIntent);
6368 b.putStringArray(KEY_PARTICIPANTS, mParticipants);
6369 b.putLong(KEY_TIMESTAMP, mLatestTimestamp);
6370 return b;
6371 }
6372
6373 static UnreadConversation getUnreadConversationFromBundle(Bundle b) {
6374 if (b == null) {
6375 return null;
6376 }
6377 Parcelable[] parcelableMessages = b.getParcelableArray(KEY_MESSAGES);
6378 String[] messages = null;
6379 if (parcelableMessages != null) {
6380 String[] tmp = new String[parcelableMessages.length];
6381 boolean success = true;
6382 for (int i = 0; i < tmp.length; i++) {
6383 if (!(parcelableMessages[i] instanceof Bundle)) {
6384 success = false;
6385 break;
6386 }
6387 tmp[i] = ((Bundle) parcelableMessages[i]).getString(KEY_TEXT);
6388 if (tmp[i] == null) {
6389 success = false;
6390 break;
6391 }
6392 }
6393 if (success) {
6394 messages = tmp;
6395 } else {
6396 return null;
6397 }
6398 }
6399
6400 PendingIntent onRead = b.getParcelable(KEY_ON_READ);
6401 PendingIntent onReply = b.getParcelable(KEY_ON_REPLY);
6402
6403 RemoteInput remoteInput = b.getParcelable(KEY_REMOTE_INPUT);
6404
6405 String[] participants = b.getStringArray(KEY_PARTICIPANTS);
6406 if (participants == null || participants.length != 1) {
6407 return null;
6408 }
6409
6410 return new UnreadConversation(messages,
6411 remoteInput,
6412 onReply,
6413 onRead,
6414 participants, b.getLong(KEY_TIMESTAMP));
6415 }
6416 };
6417
6418 /**
6419 * Builder class for {@link CarExtender.UnreadConversation} objects.
6420 */
6421 public static class Builder {
6422 private final List<String> mMessages = new ArrayList<String>();
6423 private final String mParticipant;
6424 private RemoteInput mRemoteInput;
6425 private PendingIntent mReadPendingIntent;
6426 private PendingIntent mReplyPendingIntent;
6427 private long mLatestTimestamp;
6428
6429 /**
6430 * Constructs a new builder for {@link CarExtender.UnreadConversation}.
6431 *
6432 * @param name The name of the other participant in the conversation.
6433 */
6434 public Builder(String name) {
6435 mParticipant = name;
6436 }
6437
6438 /**
6439 * Appends a new unread message to the list of messages for this conversation.
6440 *
6441 * The messages should be added from oldest to newest.
6442 *
6443 * @param message The text of the new unread message.
6444 * @return This object for method chaining.
6445 */
6446 public Builder addMessage(String message) {
6447 mMessages.add(message);
6448 return this;
6449 }
6450
6451 /**
6452 * Sets the pending intent and remote input which will convey the reply to this
6453 * notification.
6454 *
6455 * @param pendingIntent The pending intent which will be triggered on a reply.
6456 * @param remoteInput The remote input parcelable which will carry the reply.
6457 * @return This object for method chaining.
6458 *
6459 * @see CarExtender.UnreadConversation#getRemoteInput
6460 * @see CarExtender.UnreadConversation#getReplyPendingIntent
6461 */
6462 public Builder setReplyAction(
6463 PendingIntent pendingIntent, RemoteInput remoteInput) {
6464 mRemoteInput = remoteInput;
6465 mReplyPendingIntent = pendingIntent;
6466
6467 return this;
6468 }
6469
6470 /**
6471 * Sets the pending intent that will be sent once the messages in this notification
6472 * are read.
6473 *
6474 * @param pendingIntent The pending intent to use.
6475 * @return This object for method chaining.
6476 */
6477 public Builder setReadPendingIntent(PendingIntent pendingIntent) {
6478 mReadPendingIntent = pendingIntent;
6479 return this;
6480 }
6481
6482 /**
6483 * Sets the timestamp of the most recent message in an unread conversation.
6484 *
6485 * If a messaging notification has been posted by your application and has not
6486 * yet been cancelled, posting a later notification with the same id and tag
6487 * but without a newer timestamp may result in Android Auto not displaying a
6488 * heads up notification for the later notification.
6489 *
6490 * @param timestamp The timestamp of the most recent message in the conversation.
6491 * @return This object for method chaining.
6492 */
6493 public Builder setLatestTimestamp(long timestamp) {
6494 mLatestTimestamp = timestamp;
6495 return this;
6496 }
6497
6498 /**
6499 * Builds a new unread conversation object.
6500 *
6501 * @return The new unread conversation object.
6502 */
6503 public UnreadConversation build() {
6504 String[] messages = mMessages.toArray(new String[mMessages.size()]);
6505 String[] participants = { mParticipant };
6506 return new UnreadConversation(messages, mRemoteInput, mReplyPendingIntent,
6507 mReadPendingIntent, participants, mLatestTimestamp);
6508 }
6509 }
6510 }
6511
6512 /**
Griff Hazen61a9e862014-05-22 16:05:19 -07006513 * Get an array of Notification objects from a parcelable array bundle field.
6514 * Update the bundle to have a typed array so fetches in the future don't need
6515 * to do an array copy.
6516 */
6517 private static Notification[] getNotificationArrayFromBundle(Bundle bundle, String key) {
6518 Parcelable[] array = bundle.getParcelableArray(key);
6519 if (array instanceof Notification[] || array == null) {
6520 return (Notification[]) array;
6521 }
6522 Notification[] typedArray = Arrays.copyOf(array, array.length,
6523 Notification[].class);
6524 bundle.putParcelableArray(key, typedArray);
6525 return typedArray;
6526 }
Christoph Studer4600f9b2014-07-22 22:44:43 +02006527
6528 private static class BuilderRemoteViews extends RemoteViews {
6529 public BuilderRemoteViews(Parcel parcel) {
6530 super(parcel);
6531 }
6532
Kenny Guy77320062014-08-27 21:37:15 +01006533 public BuilderRemoteViews(ApplicationInfo appInfo, int layoutId) {
6534 super(appInfo, layoutId);
Christoph Studer4600f9b2014-07-22 22:44:43 +02006535 }
6536
6537 @Override
6538 public BuilderRemoteViews clone() {
6539 Parcel p = Parcel.obtain();
6540 writeToParcel(p, 0);
6541 p.setDataPosition(0);
6542 BuilderRemoteViews brv = new BuilderRemoteViews(p);
6543 p.recycle();
6544 return brv;
6545 }
6546 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006547}