blob: 58927887318c8f43501c91cdfc0fbca2b5ede181 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.app;
18
Tor Norbye80756e32015-03-02 09:39:27 -080019import android.annotation.ColorInt;
Tor Norbye7b9c9122013-05-30 16:48:33 -070020import android.annotation.DrawableRes;
Tor Norbyed9273d62013-05-30 15:59:53 -070021import android.annotation.IntDef;
Alex Hillsfd590442016-10-07 09:52:44 -040022import android.annotation.NonNull;
Daniel Sandler01df1c62014-06-09 10:54:01 -040023import android.annotation.SdkConstant;
24import android.annotation.SdkConstant.SdkConstantType;
Julia Reynoldse46bb372016-03-17 11:05:58 -040025import android.annotation.SystemApi;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080026import android.content.Context;
27import android.content.Intent;
Kenny Guy77320062014-08-27 21:37:15 +010028import android.content.pm.ApplicationInfo;
Dan Sandler732bd6c2016-04-12 14:20:32 -040029import android.content.pm.PackageManager;
Christoph Studer4600f9b2014-07-22 22:44:43 +020030import android.content.pm.PackageManager.NameNotFoundException;
Jorim Jaggief72a192014-08-26 21:57:46 +020031import android.content.res.ColorStateList;
Joe Onoratoef1e7762010-09-17 18:38:38 -040032import android.graphics.Bitmap;
Kenny Guy8a0101b2014-05-08 23:34:12 +010033import android.graphics.Canvas;
Adrian Roosc1a80b02016-04-05 14:54:55 -070034import android.graphics.Color;
Jorim Jaggi5c2d8462014-03-21 17:37:00 +010035import android.graphics.PorterDuff;
Kenny Guy8a0101b2014-05-08 23:34:12 +010036import android.graphics.drawable.Drawable;
Dan Sandlerd63f9322015-05-06 15:18:49 -040037import android.graphics.drawable.Icon;
John Spurlockc0650f022014-07-19 13:22:39 -040038import android.media.AudioAttributes;
Jeff Sharkey098d5802012-04-26 17:30:34 -070039import android.media.AudioManager;
Jean-Michel Trivi2f7511f2016-11-28 15:40:27 -080040import android.media.PlayerBase;
Jeff Browndba34ba2014-06-24 20:46:03 -070041import android.media.session.MediaSession;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080042import android.net.Uri;
Daniel Sandlerdcbaf662013-04-26 16:23:09 -040043import android.os.BadParcelableException;
Christoph Studer239f8352014-08-25 15:13:18 +020044import android.os.Build;
Daniel Sandler2561b0b2012-02-13 21:04:12 -050045import android.os.Bundle;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080046import android.os.Parcel;
47import android.os.Parcelable;
Daniel Sandlera2985ed2012-04-03 16:42:00 -040048import android.os.SystemClock;
Jeff Sharkey6d515712012-09-20 16:06:08 -070049import android.os.UserHandle;
Adrian Roosc1a80b02016-04-05 14:54:55 -070050import android.text.BidiFormatter;
Selim Cinek60a54252016-02-26 17:03:25 -080051import android.text.SpannableStringBuilder;
52import android.text.Spanned;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080053import android.text.TextUtils;
Selim Cinek60a54252016-02-26 17:03:25 -080054import android.text.style.AbsoluteSizeSpan;
Selim Cinek981962e2016-07-20 20:41:58 -070055import android.text.style.BackgroundColorSpan;
Selim Cinek89991a22016-03-07 19:51:54 -080056import android.text.style.CharacterStyle;
Selim Cinek981962e2016-07-20 20:41:58 -070057import android.text.style.ForegroundColorSpan;
Selim Cinek60a54252016-02-26 17:03:25 -080058import android.text.style.RelativeSizeSpan;
59import android.text.style.TextAppearanceSpan;
Svet Ganovddb94882016-06-23 19:55:24 -070060import android.util.ArraySet;
Daniel Sandlerdcbaf662013-04-26 16:23:09 -040061import android.util.Log;
Dan Sandler50128532015-12-08 15:42:41 -050062import android.util.SparseArray;
Griff Hazen61a9e862014-05-22 16:05:19 -070063import android.view.Gravity;
Selim Cinekea4bef72015-12-02 15:51:10 -080064import android.view.NotificationHeaderView;
Joe Onorato8595a3d2010-11-19 18:12:07 -080065import android.view.View;
Selim Cinek954cc232016-05-20 13:29:23 -070066import android.view.ViewGroup;
Jeff Sharkey1c400132011-08-05 14:50:13 -070067import android.widget.ProgressBar;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080068import android.widget.RemoteViews;
69
Griff Hazen959591e2014-05-15 22:26:18 -070070import com.android.internal.R;
Svet Ganovddb94882016-06-23 19:55:24 -070071import com.android.internal.util.ArrayUtils;
Griff Hazenc091ba82014-05-16 10:13:26 -070072import com.android.internal.util.NotificationColorUtil;
Adrian Roos487374f2017-01-11 15:48:14 -080073import com.android.internal.util.Preconditions;
Griff Hazen959591e2014-05-15 22:26:18 -070074
Tor Norbyed9273d62013-05-30 15:59:53 -070075import java.lang.annotation.Retention;
76import java.lang.annotation.RetentionPolicy;
Christoph Studer4600f9b2014-07-22 22:44:43 +020077import java.lang.reflect.Constructor;
Daniel Sandler2561b0b2012-02-13 21:04:12 -050078import java.util.ArrayList;
Griff Hazen61a9e862014-05-22 16:05:19 -070079import java.util.Arrays;
Griff Hazen5cadc3b2014-05-20 09:55:39 -070080import java.util.Collections;
Griff Hazen61a9e862014-05-22 16:05:19 -070081import java.util.List;
Dan Sandler50128532015-12-08 15:42:41 -050082import java.util.Set;
Joe Onorato561d3852010-11-20 18:09:34 -080083
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080084/**
85 * A class that represents how a persistent notification is to be presented to
86 * the user using the {@link android.app.NotificationManager}.
87 *
Joe Onoratocb109a02011-01-18 17:57:41 -080088 * <p>The {@link Notification.Builder Notification.Builder} has been added to make it
89 * easier to construct Notifications.</p>
90 *
Joe Fernandez558459f2011-10-13 16:47:36 -070091 * <div class="special reference">
92 * <h3>Developer Guides</h3>
93 * <p>For a guide to creating notifications, read the
94 * <a href="{@docRoot}guide/topics/ui/notifiers/notifications.html">Status Bar Notifications</a>
95 * developer guide.</p>
96 * </div>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080097 */
98public class Notification implements Parcelable
99{
Daniel Sandlerdcbaf662013-04-26 16:23:09 -0400100 private static final String TAG = "Notification";
101
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800102 /**
Daniel Sandler01df1c62014-06-09 10:54:01 -0400103 * An activity that provides a user interface for adjusting notification preferences for its
104 * containing application. Optional but recommended for apps that post
105 * {@link android.app.Notification Notifications}.
106 */
107 @SdkConstant(SdkConstantType.INTENT_CATEGORY)
108 public static final String INTENT_CATEGORY_NOTIFICATION_PREFERENCES
109 = "android.intent.category.NOTIFICATION_PREFERENCES";
110
111 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800112 * Use all default values (where applicable).
113 */
114 public static final int DEFAULT_ALL = ~0;
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500115
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800116 /**
117 * Use the default notification sound. This will ignore any given
118 * {@link #sound}.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500119 *
Chris Wren47c20a12014-06-18 17:27:29 -0400120 * <p>
121 * A notification that is noisy is more likely to be presented as a heads-up notification.
122 * </p>
123 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800124 * @see #defaults
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500125 */
126
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800127 public static final int DEFAULT_SOUND = 1;
128
129 /**
130 * Use the default notification vibrate. This will ignore any given
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500131 * {@link #vibrate}. Using phone vibration requires the
Scott Mainb8b36452009-04-26 15:50:49 -0700132 * {@link android.Manifest.permission#VIBRATE VIBRATE} permission.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500133 *
Chris Wren47c20a12014-06-18 17:27:29 -0400134 * <p>
135 * A notification that vibrates is more likely to be presented as a heads-up notification.
136 * </p>
137 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800138 * @see #defaults
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500139 */
140
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800141 public static final int DEFAULT_VIBRATE = 2;
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500142
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800143 /**
144 * Use the default notification lights. This will ignore the
145 * {@link #FLAG_SHOW_LIGHTS} bit, and {@link #ledARGB}, {@link #ledOffMS}, or
146 * {@link #ledOnMS}.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500147 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800148 * @see #defaults
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500149 */
150
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800151 public static final int DEFAULT_LIGHTS = 4;
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500152
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800153 /**
Christoph Studer535ec612014-09-03 15:47:47 +0200154 * Maximum length of CharSequences accepted by Builder and friends.
155 *
156 * <p>
157 * Avoids spamming the system with overly large strings such as full e-mails.
158 */
159 private static final int MAX_CHARSEQUENCE_LENGTH = 5 * 1024;
160
161 /**
Adrian Roose458aa82015-12-08 16:17:19 -0800162 * Maximum entries of reply text that are accepted by Builder and friends.
163 */
164 private static final int MAX_REPLY_HISTORY = 5;
165
166 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500167 * A timestamp related to this notification, in milliseconds since the epoch.
Joe Malin8d40d042012-11-05 11:36:40 -0800168 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500169 * Default value: {@link System#currentTimeMillis() Now}.
170 *
171 * Choose a timestamp that will be most relevant to the user. For most finite events, this
172 * corresponds to the time the event happened (or will happen, in the case of events that have
173 * yet to occur but about which the user is being informed). Indefinite events should be
Joe Malin8d40d042012-11-05 11:36:40 -0800174 * timestamped according to when the activity began.
175 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500176 * Some examples:
Joe Malin8d40d042012-11-05 11:36:40 -0800177 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500178 * <ul>
179 * <li>Notification of a new chat message should be stamped when the message was received.</li>
180 * <li>Notification of an ongoing file download (with a progress bar, for example) should be stamped when the download started.</li>
181 * <li>Notification of a completed file download should be stamped when the download finished.</li>
182 * <li>Notification of an upcoming meeting should be stamped with the time the meeting will begin (that is, in the future).</li>
183 * <li>Notification of an ongoing stopwatch (increasing timer) should be stamped with the watch's start time.
184 * <li>Notification of an ongoing countdown timer should be stamped with the timer's end time.
Joe Malin8d40d042012-11-05 11:36:40 -0800185 * </ul>
186 *
Selim Cinek0ff1ce602016-04-05 18:27:16 -0700187 * For apps targeting {@link android.os.Build.VERSION_CODES#N} and above, this time is not shown
188 * anymore by default and must be opted into by using
189 * {@link android.app.Notification.Builder#setShowWhen(boolean)}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800190 */
191 public long when;
192
193 /**
Selim Cinekb85f36fd2016-04-20 18:46:36 -0700194 * The creation time of the notification
195 */
196 private long creationTime;
197
198 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800199 * The resource id of a drawable to use as the icon in the status bar.
Dan Sandler86647982015-05-13 23:41:13 -0400200 *
201 * @deprecated Use {@link Builder#setSmallIcon(Icon)} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800202 */
Dan Sandler86647982015-05-13 23:41:13 -0400203 @Deprecated
Tor Norbye7b9c9122013-05-30 16:48:33 -0700204 @DrawableRes
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800205 public int icon;
206
207 /**
Joe Onorato46439ce2010-11-19 13:56:21 -0800208 * If the icon in the status bar is to have more than one level, you can set this. Otherwise,
209 * leave it at its default value of 0.
210 *
211 * @see android.widget.ImageView#setImageLevel
Griff Hazen959591e2014-05-15 22:26:18 -0700212 * @see android.graphics.drawable.Drawable#setLevel
Joe Onorato46439ce2010-11-19 13:56:21 -0800213 */
214 public int iconLevel;
215
216 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500217 * The number of events that this notification represents. For example, in a new mail
218 * notification, this could be the number of unread messages.
Joe Malin8d40d042012-11-05 11:36:40 -0800219 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500220 * The system may or may not use this field to modify the appearance of the notification. For
221 * example, before {@link android.os.Build.VERSION_CODES#HONEYCOMB}, this number was
222 * superimposed over the icon in the status bar. Starting with
223 * {@link android.os.Build.VERSION_CODES#HONEYCOMB}, the template used by
224 * {@link Notification.Builder} has displayed the number in the expanded notification view.
Joe Malin8d40d042012-11-05 11:36:40 -0800225 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500226 * If the number is 0 or negative, it is never shown.
Selim Cinek0f9dd1e2016-04-05 17:03:40 -0700227 *
228 * @deprecated this number is not shown anymore
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800229 */
Aurimas Liutikas514c5ef2016-05-24 15:22:55 -0700230 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800231 public int number;
232
233 /**
234 * The intent to execute when the expanded status entry is clicked. If
235 * this is an activity, it must include the
236 * {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} flag, which requires
Scott Main7aee61f2011-02-08 11:25:01 -0800237 * that you take care of task management as described in the
238 * <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back
Dianne Hackborn6ceca582012-01-10 15:24:26 -0800239 * Stack</a> document. In particular, make sure to read the notification section
240 * <a href="{@docRoot}guide/topics/ui/notifiers/notifications.html#HandlingNotifications">Handling
241 * Notifications</a> for the correct ways to launch an application from a
242 * notification.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800243 */
244 public PendingIntent contentIntent;
245
246 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500247 * The intent to execute when the notification is explicitly dismissed by the user, either with
248 * the "Clear All" button or by swiping it away individually.
249 *
250 * This probably shouldn't be launching an activity since several of those will be sent
251 * at the same time.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800252 */
253 public PendingIntent deleteIntent;
254
255 /**
Dianne Hackborn170bae72010-09-03 15:14:28 -0700256 * An intent to launch instead of posting the notification to the status bar.
Joe Onoratocb109a02011-01-18 17:57:41 -0800257 *
Chris Wren47c20a12014-06-18 17:27:29 -0400258 * <p>
259 * The system UI may choose to display a heads-up notification, instead of
260 * launching this intent, while the user is using the device.
261 * </p>
262 *
Joe Onoratocb109a02011-01-18 17:57:41 -0800263 * @see Notification.Builder#setFullScreenIntent
Daniel Sandlere46cbd32010-06-17 10:35:26 -0400264 */
265 public PendingIntent fullScreenIntent;
266
267 /**
Dan Sandler5fcdf6e2014-07-18 11:31:15 -0400268 * Text that summarizes this notification for accessibility services.
269 *
270 * As of the L release, this text is no longer shown on screen, but it is still useful to
271 * accessibility services (where it serves as an audible announcement of the notification's
272 * appearance).
Joe Onoratoef1e7762010-09-17 18:38:38 -0400273 *
Joe Onorato46439ce2010-11-19 13:56:21 -0800274 * @see #tickerView
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800275 */
276 public CharSequence tickerText;
277
278 /**
Dan Sandler5fcdf6e2014-07-18 11:31:15 -0400279 * Formerly, a view showing the {@link #tickerText}.
280 *
281 * No longer displayed in the status bar as of API 21.
Joe Onoratoef1e7762010-09-17 18:38:38 -0400282 */
Dan Sandler5fcdf6e2014-07-18 11:31:15 -0400283 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -0800284 public RemoteViews tickerView;
Joe Onoratoef1e7762010-09-17 18:38:38 -0400285
286 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400287 * The view that will represent this notification in the notification list (which is pulled
288 * down from the status bar).
289 *
Julia Reynoldsd4ea7412016-02-17 14:00:56 -0500290 * As of N, this field may be null. The notification view is determined by the inputs
291 * to {@link Notification.Builder}; a custom RemoteViews can optionally be
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400292 * supplied with {@link Notification.Builder#setCustomContentView(RemoteViews)}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800293 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400294 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800295 public RemoteViews contentView;
296
Daniel Sandlera0a938c2012-03-15 08:42:37 -0400297 /**
Daniel Sandler4dfbe832012-04-11 14:51:46 -0400298 * A large-format version of {@link #contentView}, giving the Notification an
Daniel Sandlerf3b73432012-03-27 15:01:25 -0400299 * opportunity to show more detail. The system UI may choose to show this
300 * instead of the normal content view at its discretion.
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400301 *
Julia Reynoldsd4ea7412016-02-17 14:00:56 -0500302 * As of N, this field may be null. The expanded notification view is determined by the
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400303 * inputs to {@link Notification.Builder}; a custom RemoteViews can optionally be
304 * supplied with {@link Notification.Builder#setCustomBigContentView(RemoteViews)}.
Daniel Sandlerf3b73432012-03-27 15:01:25 -0400305 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400306 @Deprecated
Daniel Sandlerf3b73432012-03-27 15:01:25 -0400307 public RemoteViews bigContentView;
308
Chris Wren8fd39ec2014-02-27 17:43:26 -0500309
310 /**
Chris Wren47c20a12014-06-18 17:27:29 -0400311 * A medium-format version of {@link #contentView}, providing the Notification an
312 * opportunity to add action buttons to contentView. At its discretion, the system UI may
313 * choose to show this as a heads-up notification, which will pop up so the user can see
314 * it without leaving their current activity.
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400315 *
Julia Reynoldsd4ea7412016-02-17 14:00:56 -0500316 * As of N, this field may be null. The heads-up notification view is determined by the
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400317 * inputs to {@link Notification.Builder}; a custom RemoteViews can optionally be
318 * supplied with {@link Notification.Builder#setCustomHeadsUpContentView(RemoteViews)}.
Chris Wren8fd39ec2014-02-27 17:43:26 -0500319 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400320 @Deprecated
Chris Wren8fd39ec2014-02-27 17:43:26 -0500321 public RemoteViews headsUpContentView;
322
Daniel Sandlerf3b73432012-03-27 15:01:25 -0400323 /**
Dan Sandler86647982015-05-13 23:41:13 -0400324 * A large bitmap to be shown in the notification content area.
325 *
326 * @deprecated Use {@link Builder#setLargeIcon(Icon)} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800327 */
Dan Sandler86647982015-05-13 23:41:13 -0400328 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -0800329 public Bitmap largeIcon;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800330
331 /**
332 * The sound to play.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500333 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800334 * <p>
Chris Wren47c20a12014-06-18 17:27:29 -0400335 * A notification that is noisy is more likely to be presented as a heads-up notification.
336 * </p>
337 *
338 * <p>
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500339 * To play the default notification sound, see {@link #defaults}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800340 * </p>
341 */
342 public Uri sound;
343
344 /**
345 * Use this constant as the value for audioStreamType to request that
346 * the default stream type for notifications be used. Currently the
Jeff Sharkey098d5802012-04-26 17:30:34 -0700347 * default stream type is {@link AudioManager#STREAM_NOTIFICATION}.
John Spurlockc0650f022014-07-19 13:22:39 -0400348 *
349 * @deprecated Use {@link #audioAttributes} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800350 */
Jean-Michel Trivi81f871e2014-08-06 16:32:38 -0700351 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800352 public static final int STREAM_DEFAULT = -1;
353
354 /**
355 * The audio stream type to use when playing the sound.
356 * Should be one of the STREAM_ constants from
357 * {@link android.media.AudioManager}.
John Spurlockc0650f022014-07-19 13:22:39 -0400358 *
359 * @deprecated Use {@link #audioAttributes} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800360 */
Jean-Michel Trivi81f871e2014-08-06 16:32:38 -0700361 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800362 public int audioStreamType = STREAM_DEFAULT;
363
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800364 /**
John Spurlockc0650f022014-07-19 13:22:39 -0400365 * The default value of {@link #audioAttributes}.
366 */
367 public static final AudioAttributes AUDIO_ATTRIBUTES_DEFAULT = new AudioAttributes.Builder()
368 .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
369 .setUsage(AudioAttributes.USAGE_NOTIFICATION)
370 .build();
371
372 /**
373 * The {@link AudioAttributes audio attributes} to use when playing the sound.
374 */
375 public AudioAttributes audioAttributes = AUDIO_ATTRIBUTES_DEFAULT;
376
377 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500378 * The pattern with which to vibrate.
379 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800380 * <p>
381 * To vibrate the default pattern, see {@link #defaults}.
382 * </p>
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500383 *
Chris Wren47c20a12014-06-18 17:27:29 -0400384 * <p>
385 * A notification that vibrates is more likely to be presented as a heads-up notification.
386 * </p>
387 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800388 * @see android.os.Vibrator#vibrate(long[],int)
389 */
390 public long[] vibrate;
391
392 /**
393 * The color of the led. The hardware will do its best approximation.
394 *
395 * @see #FLAG_SHOW_LIGHTS
396 * @see #flags
397 */
Tor Norbye80756e32015-03-02 09:39:27 -0800398 @ColorInt
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800399 public int ledARGB;
400
401 /**
402 * The number of milliseconds for the LED to be on while it's flashing.
403 * The hardware will do its best approximation.
404 *
405 * @see #FLAG_SHOW_LIGHTS
406 * @see #flags
407 */
408 public int ledOnMS;
409
410 /**
411 * The number of milliseconds for the LED to be off while it's flashing.
412 * The hardware will do its best approximation.
413 *
414 * @see #FLAG_SHOW_LIGHTS
415 * @see #flags
416 */
417 public int ledOffMS;
418
419 /**
420 * Specifies which values should be taken from the defaults.
421 * <p>
422 * To set, OR the desired from {@link #DEFAULT_SOUND},
423 * {@link #DEFAULT_VIBRATE}, {@link #DEFAULT_LIGHTS}. For all default
424 * values, use {@link #DEFAULT_ALL}.
425 * </p>
426 */
427 public int defaults;
428
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800429 /**
430 * Bit to be bitwise-ored into the {@link #flags} field that should be
431 * set if you want the LED on for this notification.
432 * <ul>
433 * <li>To turn the LED off, pass 0 in the alpha channel for colorARGB
434 * or 0 for both ledOnMS and ledOffMS.</li>
435 * <li>To turn the LED on, pass 1 for ledOnMS and 0 for ledOffMS.</li>
436 * <li>To flash the LED, pass the number of milliseconds that it should
437 * be on and off to ledOnMS and ledOffMS.</li>
438 * </ul>
439 * <p>
440 * Since hardware varies, you are not guaranteed that any of the values
441 * you pass are honored exactly. Use the system defaults (TODO) if possible
442 * because they will be set to values that work on any given hardware.
443 * <p>
444 * The alpha channel must be set for forward compatibility.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500445 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800446 */
447 public static final int FLAG_SHOW_LIGHTS = 0x00000001;
448
449 /**
450 * Bit to be bitwise-ored into the {@link #flags} field that should be
451 * set if this notification is in reference to something that is ongoing,
452 * like a phone call. It should not be set if this notification is in
453 * reference to something that happened at a particular point in time,
454 * like a missed phone call.
455 */
456 public static final int FLAG_ONGOING_EVENT = 0x00000002;
457
458 /**
459 * Bit to be bitwise-ored into the {@link #flags} field that if set,
Scott Mainb8b36452009-04-26 15:50:49 -0700460 * the audio will be repeated until the notification is
461 * cancelled or the notification window is opened.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800462 */
463 public static final int FLAG_INSISTENT = 0x00000004;
464
465 /**
466 * Bit to be bitwise-ored into the {@link #flags} field that should be
Griff Hazen293977b2014-04-28 08:37:20 -0700467 * set if you would only like the sound, vibrate and ticker to be played
468 * if the notification was not already showing.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800469 */
470 public static final int FLAG_ONLY_ALERT_ONCE = 0x00000008;
471
472 /**
473 * Bit to be bitwise-ored into the {@link #flags} field that should be
474 * set if the notification should be canceled when it is clicked by the
Daniel Sandler8aa9ae62012-12-04 23:31:47 -0500475 * user.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800476 */
477 public static final int FLAG_AUTO_CANCEL = 0x00000010;
478
479 /**
480 * Bit to be bitwise-ored into the {@link #flags} field that should be
481 * set if the notification should not be canceled when the user clicks
482 * the Clear all button.
483 */
484 public static final int FLAG_NO_CLEAR = 0x00000020;
485
Dianne Hackbornd8a43f62009-08-17 23:33:56 -0700486 /**
487 * Bit to be bitwise-ored into the {@link #flags} field that should be
488 * set if this notification represents a currently running service. This
489 * will normally be set for you by {@link Service#startForeground}.
490 */
491 public static final int FLAG_FOREGROUND_SERVICE = 0x00000040;
492
Daniel Sandlere46cbd32010-06-17 10:35:26 -0400493 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500494 * Obsolete flag indicating high-priority notifications; use the priority field instead.
Joe Malin8d40d042012-11-05 11:36:40 -0800495 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500496 * @deprecated Use {@link #priority} with a positive value.
Daniel Sandlere46cbd32010-06-17 10:35:26 -0400497 */
Aurimas Liutikas514c5ef2016-05-24 15:22:55 -0700498 @Deprecated
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500499 public static final int FLAG_HIGH_PRIORITY = 0x00000080;
Daniel Sandlere46cbd32010-06-17 10:35:26 -0400500
Griff Hazendfcb0802014-02-11 12:00:00 -0800501 /**
502 * Bit to be bitswise-ored into the {@link #flags} field that should be
503 * set if this notification is relevant to the current device only
504 * and it is not recommended that it bridge to other devices.
505 */
506 public static final int FLAG_LOCAL_ONLY = 0x00000100;
507
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700508 /**
509 * Bit to be bitswise-ored into the {@link #flags} field that should be
510 * set if this notification is the group summary for a group of notifications.
511 * Grouped notifications may display in a cluster or stack on devices which
512 * support such rendering. Requires a group key also be set using {@link Builder#setGroup}.
513 */
514 public static final int FLAG_GROUP_SUMMARY = 0x00000200;
515
Julia Reynoldse46bb372016-03-17 11:05:58 -0400516 /**
517 * Bit to be bitswise-ored into the {@link #flags} field that should be
518 * set if this notification is the group summary for an auto-group of notifications.
519 *
520 * @hide
521 */
522 @SystemApi
523 public static final int FLAG_AUTOGROUP_SUMMARY = 0x00000400;
524
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800525 public int flags;
526
Tor Norbyed9273d62013-05-30 15:59:53 -0700527 /** @hide */
528 @IntDef({PRIORITY_DEFAULT,PRIORITY_LOW,PRIORITY_MIN,PRIORITY_HIGH,PRIORITY_MAX})
529 @Retention(RetentionPolicy.SOURCE)
530 public @interface Priority {}
531
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800532 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500533 * Default notification {@link #priority}. If your application does not prioritize its own
534 * notifications, use this value for all notifications.
535 */
536 public static final int PRIORITY_DEFAULT = 0;
537
538 /**
539 * Lower {@link #priority}, for items that are less important. The UI may choose to show these
540 * items smaller, or at a different position in the list, compared with your app's
541 * {@link #PRIORITY_DEFAULT} items.
542 */
543 public static final int PRIORITY_LOW = -1;
544
545 /**
546 * Lowest {@link #priority}; these items might not be shown to the user except under special
547 * circumstances, such as detailed notification logs.
548 */
549 public static final int PRIORITY_MIN = -2;
550
551 /**
552 * Higher {@link #priority}, for more important notifications or alerts. The UI may choose to
553 * show these items larger, or at a different position in notification lists, compared with
554 * your app's {@link #PRIORITY_DEFAULT} items.
555 */
556 public static final int PRIORITY_HIGH = 1;
557
558 /**
559 * Highest {@link #priority}, for your application's most important items that require the
560 * user's prompt attention or input.
561 */
562 public static final int PRIORITY_MAX = 2;
563
564 /**
565 * Relative priority for this notification.
Joe Malin8d40d042012-11-05 11:36:40 -0800566 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500567 * Priority is an indication of how much of the user's valuable attention should be consumed by
568 * this notification. Low-priority notifications may be hidden from the user in certain
569 * situations, while the user might be interrupted for a higher-priority notification. The
Daniel Sandler6738eee2012-11-16 12:03:32 -0500570 * system will make a determination about how to interpret this priority when presenting
571 * the notification.
Chris Wren47c20a12014-06-18 17:27:29 -0400572 *
573 * <p>
574 * A notification that is at least {@link #PRIORITY_HIGH} is more likely to be presented
575 * as a heads-up notification.
576 * </p>
577 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500578 */
Tor Norbyed9273d62013-05-30 15:59:53 -0700579 @Priority
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500580 public int priority;
Joe Malin8d40d042012-11-05 11:36:40 -0800581
Dan Sandler26e81cf2014-05-06 10:01:27 -0400582 /**
583 * Accent color (an ARGB integer like the constants in {@link android.graphics.Color})
584 * to be applied by the standard Style templates when presenting this notification.
585 *
586 * The current template design constructs a colorful header image by overlaying the
587 * {@link #icon} image (stenciled in white) atop a field of this color. Alpha components are
588 * ignored.
589 */
Tor Norbye80756e32015-03-02 09:39:27 -0800590 @ColorInt
Dan Sandler26e81cf2014-05-06 10:01:27 -0400591 public int color = COLOR_DEFAULT;
592
593 /**
594 * Special value of {@link #color} telling the system not to decorate this notification with
595 * any special color but instead use default colors when presenting this notification.
596 */
Tor Norbye80756e32015-03-02 09:39:27 -0800597 @ColorInt
Dan Sandler26e81cf2014-05-06 10:01:27 -0400598 public static final int COLOR_DEFAULT = 0; // AKA Color.TRANSPARENT
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600599
600 /**
Adrian Roos4ff3b122016-02-01 12:26:13 -0800601 * Special value of {@link #color} used as a place holder for an invalid color.
602 */
603 @ColorInt
604 private static final int COLOR_INVALID = 1;
605
606 /**
Adrian Roosc1a80b02016-04-05 14:54:55 -0700607 * Sphere of visibility of this notification, which affects how and when the SystemUI reveals
608 * the notification's presence and contents in untrusted situations (namely, on the secure
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600609 * lockscreen).
610 *
611 * The default level, {@link #VISIBILITY_PRIVATE}, behaves exactly as notifications have always
612 * done on Android: The notification's {@link #icon} and {@link #tickerText} (if available) are
613 * shown in all situations, but the contents are only available if the device is unlocked for
614 * the appropriate user.
615 *
616 * A more permissive policy can be expressed by {@link #VISIBILITY_PUBLIC}; such a notification
617 * can be read even in an "insecure" context (that is, above a secure lockscreen).
618 * To modify the public version of this notification—for example, to redact some portions—see
619 * {@link Builder#setPublicVersion(Notification)}.
620 *
621 * Finally, a notification can be made {@link #VISIBILITY_SECRET}, which will suppress its icon
622 * and ticker until the user has bypassed the lockscreen.
623 */
624 public int visibility;
625
Griff Hazenfc3922d2014-08-20 11:56:44 -0700626 /**
627 * Notification visibility: Show this notification in its entirety on all lockscreens.
628 *
629 * {@see #visibility}
630 */
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600631 public static final int VISIBILITY_PUBLIC = 1;
Griff Hazenfc3922d2014-08-20 11:56:44 -0700632
633 /**
634 * Notification visibility: Show this notification on all lockscreens, but conceal sensitive or
635 * private information on secure lockscreens.
636 *
637 * {@see #visibility}
638 */
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600639 public static final int VISIBILITY_PRIVATE = 0;
Griff Hazenfc3922d2014-08-20 11:56:44 -0700640
641 /**
642 * Notification visibility: Do not reveal any part of this notification on a secure lockscreen.
643 *
644 * {@see #visibility}
645 */
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600646 public static final int VISIBILITY_SECRET = -1;
647
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500648 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400649 * Notification category: incoming call (voice or video) or similar synchronous communication request.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500650 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400651 public static final String CATEGORY_CALL = "call";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500652
653 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400654 * Notification category: incoming direct message (SMS, instant message, etc.).
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500655 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400656 public static final String CATEGORY_MESSAGE = "msg";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500657
658 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400659 * Notification category: asynchronous bulk message (email).
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500660 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400661 public static final String CATEGORY_EMAIL = "email";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500662
663 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400664 * Notification category: calendar event.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500665 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400666 public static final String CATEGORY_EVENT = "event";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500667
668 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400669 * Notification category: promotion or advertisement.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500670 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400671 public static final String CATEGORY_PROMO = "promo";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500672
673 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400674 * Notification category: alarm or timer.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500675 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400676 public static final String CATEGORY_ALARM = "alarm";
677
678 /**
679 * Notification category: progress of a long-running background operation.
680 */
681 public static final String CATEGORY_PROGRESS = "progress";
682
683 /**
684 * Notification category: social network or sharing update.
685 */
686 public static final String CATEGORY_SOCIAL = "social";
687
688 /**
689 * Notification category: error in background operation or authentication status.
690 */
691 public static final String CATEGORY_ERROR = "err";
692
693 /**
694 * Notification category: media transport control for playback.
695 */
696 public static final String CATEGORY_TRANSPORT = "transport";
697
698 /**
699 * Notification category: system or device status update. Reserved for system use.
700 */
701 public static final String CATEGORY_SYSTEM = "sys";
702
703 /**
704 * Notification category: indication of running background service.
705 */
706 public static final String CATEGORY_SERVICE = "service";
707
708 /**
John Spurlock0a69c8c2014-03-21 13:30:57 -0400709 * Notification category: a specific, timely recommendation for a single thing.
710 * For example, a news app might want to recommend a news story it believes the user will
711 * want to read next.
712 */
713 public static final String CATEGORY_RECOMMENDATION = "recommendation";
714
715 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400716 * Notification category: ongoing information about device or contextual status.
717 */
718 public static final String CATEGORY_STATUS = "status";
719
720 /**
John Spurlock24d3dad2015-04-02 12:24:02 -0400721 * Notification category: user-scheduled reminder.
722 */
723 public static final String CATEGORY_REMINDER = "reminder";
724
725 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400726 * One of the predefined notification categories (see the <code>CATEGORY_*</code> constants)
727 * that best describes this Notification. May be used by the system for ranking and filtering.
728 */
729 public String category;
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500730
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700731 private String mGroupKey;
732
733 /**
734 * Get the key used to group this notification into a cluster or stack
735 * with other notifications on devices which support such rendering.
736 */
737 public String getGroup() {
738 return mGroupKey;
739 }
740
741 private String mSortKey;
742
743 /**
744 * Get a sort key that orders this notification among other notifications from the
745 * same package. This can be useful if an external sort was already applied and an app
746 * would like to preserve this. Notifications will be sorted lexicographically using this
747 * value, although providing different priorities in addition to providing sort key may
748 * cause this value to be ignored.
749 *
750 * <p>This sort key can also be used to order members of a notification group. See
751 * {@link Builder#setGroup}.
752 *
753 * @see String#compareTo(String)
754 */
755 public String getSortKey() {
756 return mSortKey;
757 }
758
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500759 /**
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400760 * Additional semantic data to be carried around with this Notification.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400761 * <p>
762 * The extras keys defined here are intended to capture the original inputs to {@link Builder}
763 * APIs, and are intended to be used by
764 * {@link android.service.notification.NotificationListenerService} implementations to extract
765 * detailed information from notification objects.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500766 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400767 public Bundle extras = new Bundle();
768
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400769 /**
Felipe Lemedd85da62016-06-28 11:29:54 -0700770 * All pending intents in the notification as the system needs to be able to access them but
771 * touching the extras bundle in the system process is not safe because the bundle may contain
Svet Ganovddb94882016-06-23 19:55:24 -0700772 * custom parcelable objects.
773 *
774 * @hide
775 */
Felipe Lemedd85da62016-06-28 11:29:54 -0700776 public ArraySet<PendingIntent> allPendingIntents;
Svet Ganovddb94882016-06-23 19:55:24 -0700777
778 /**
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400779 * {@link #extras} key: this is the title of the notification,
780 * as supplied to {@link Builder#setContentTitle(CharSequence)}.
781 */
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -0500782 public static final String EXTRA_TITLE = "android.title";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400783
784 /**
785 * {@link #extras} key: this is the title of the notification when shown in expanded form,
786 * e.g. as supplied to {@link BigTextStyle#setBigContentTitle(CharSequence)}.
787 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400788 public static final String EXTRA_TITLE_BIG = EXTRA_TITLE + ".big";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400789
790 /**
791 * {@link #extras} key: this is the main text payload, as supplied to
792 * {@link Builder#setContentText(CharSequence)}.
793 */
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -0500794 public static final String EXTRA_TEXT = "android.text";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400795
796 /**
797 * {@link #extras} key: this is a third line of text, as supplied to
798 * {@link Builder#setSubText(CharSequence)}.
799 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400800 public static final String EXTRA_SUB_TEXT = "android.subText";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400801
802 /**
Adrian Roose458aa82015-12-08 16:17:19 -0800803 * {@link #extras} key: this is the remote input history, as supplied to
804 * {@link Builder#setRemoteInputHistory(CharSequence[])}.
Adrian Roos005e7742016-04-13 15:02:20 -0700805 *
806 * Apps can fill this through {@link Builder#setRemoteInputHistory(CharSequence[])}
807 * with the most recent inputs that have been sent through a {@link RemoteInput} of this
808 * Notification and are expected to clear it once the it is no longer relevant (e.g. for chat
809 * notifications once the other party has responded).
810 *
811 * The extra with this key is of type CharSequence[] and contains the most recent entry at
812 * the 0 index, the second most recent at the 1 index, etc.
813 *
814 * @see Builder#setRemoteInputHistory(CharSequence[])
Adrian Roose458aa82015-12-08 16:17:19 -0800815 */
816 public static final String EXTRA_REMOTE_INPUT_HISTORY = "android.remoteInputHistory";
817
818 /**
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400819 * {@link #extras} key: this is a small piece of additional text as supplied to
820 * {@link Builder#setContentInfo(CharSequence)}.
821 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400822 public static final String EXTRA_INFO_TEXT = "android.infoText";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400823
824 /**
825 * {@link #extras} key: this is a line of summary information intended to be shown
826 * alongside expanded notifications, as supplied to (e.g.)
827 * {@link BigTextStyle#setSummaryText(CharSequence)}.
828 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400829 public static final String EXTRA_SUMMARY_TEXT = "android.summaryText";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400830
831 /**
Christoph Studer4600f9b2014-07-22 22:44:43 +0200832 * {@link #extras} key: this is the longer text shown in the big form of a
833 * {@link BigTextStyle} notification, as supplied to
834 * {@link BigTextStyle#bigText(CharSequence)}.
835 */
836 public static final String EXTRA_BIG_TEXT = "android.bigText";
837
838 /**
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400839 * {@link #extras} key: this is the resource ID of the notification's main small icon, as
840 * supplied to {@link Builder#setSmallIcon(int)}.
841 */
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -0500842 public static final String EXTRA_SMALL_ICON = "android.icon";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400843
844 /**
845 * {@link #extras} key: this is a bitmap to be used instead of the small icon when showing the
846 * notification payload, as
847 * supplied to {@link Builder#setLargeIcon(android.graphics.Bitmap)}.
848 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400849 public static final String EXTRA_LARGE_ICON = "android.largeIcon";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400850
851 /**
852 * {@link #extras} key: this is a bitmap to be used instead of the one from
853 * {@link Builder#setLargeIcon(android.graphics.Bitmap)} when the notification is
854 * shown in its expanded form, as supplied to
855 * {@link BigPictureStyle#bigLargeIcon(android.graphics.Bitmap)}.
856 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400857 public static final String EXTRA_LARGE_ICON_BIG = EXTRA_LARGE_ICON + ".big";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400858
859 /**
860 * {@link #extras} key: this is the progress value supplied to
861 * {@link Builder#setProgress(int, int, boolean)}.
862 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400863 public static final String EXTRA_PROGRESS = "android.progress";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400864
865 /**
866 * {@link #extras} key: this is the maximum value supplied to
867 * {@link Builder#setProgress(int, int, boolean)}.
868 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400869 public static final String EXTRA_PROGRESS_MAX = "android.progressMax";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400870
871 /**
872 * {@link #extras} key: whether the progress bar is indeterminate, supplied to
873 * {@link Builder#setProgress(int, int, boolean)}.
874 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400875 public static final String EXTRA_PROGRESS_INDETERMINATE = "android.progressIndeterminate";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400876
877 /**
878 * {@link #extras} key: whether {@link #when} should be shown as a count-up timer (specifically
879 * a {@link android.widget.Chronometer}) instead of a timestamp, as supplied to
880 * {@link Builder#setUsesChronometer(boolean)}.
881 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400882 public static final String EXTRA_SHOW_CHRONOMETER = "android.showChronometer";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400883
884 /**
Selim Cinek81c23aa2016-02-25 16:23:13 -0800885 * {@link #extras} key: whether the chronometer set on the notification should count down
886 * instead of counting up. Is only relevant if key {@link #EXTRA_SHOW_CHRONOMETER} is present.
Adrian Roos96b7e202016-05-17 13:50:38 -0700887 * This extra is a boolean. The default is false.
Selim Cinek81c23aa2016-02-25 16:23:13 -0800888 */
Adrian Roos96b7e202016-05-17 13:50:38 -0700889 public static final String EXTRA_CHRONOMETER_COUNT_DOWN = "android.chronometerCountDown";
Selim Cinek81c23aa2016-02-25 16:23:13 -0800890
891 /**
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400892 * {@link #extras} key: whether {@link #when} should be shown,
893 * as supplied to {@link Builder#setShowWhen(boolean)}.
894 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400895 public static final String EXTRA_SHOW_WHEN = "android.showWhen";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400896
897 /**
898 * {@link #extras} key: this is a bitmap to be shown in {@link BigPictureStyle} expanded
899 * notifications, supplied to {@link BigPictureStyle#bigPicture(android.graphics.Bitmap)}.
900 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400901 public static final String EXTRA_PICTURE = "android.picture";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400902
903 /**
904 * {@link #extras} key: An array of CharSequences to show in {@link InboxStyle} expanded
905 * notifications, each of which was supplied to {@link InboxStyle#addLine(CharSequence)}.
906 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400907 public static final String EXTRA_TEXT_LINES = "android.textLines";
Dan Sandler842dd772014-05-15 09:36:47 -0400908
909 /**
910 * {@link #extras} key: A string representing the name of the specific
911 * {@link android.app.Notification.Style} used to create this notification.
912 */
Chris Wren91ad5632013-06-05 15:05:57 -0400913 public static final String EXTRA_TEMPLATE = "android.template";
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400914
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400915 /**
Chris Wrene6c48932014-09-29 17:19:27 -0400916 * {@link #extras} key: A String array containing the people that this notification relates to,
917 * each of which was supplied to {@link Builder#addPerson(String)}.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400918 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400919 public static final String EXTRA_PEOPLE = "android.people";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500920
921 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400922 * Allow certain system-generated notifications to appear before the device is provisioned.
923 * Only available to notifications coming from the android package.
924 * @hide
925 */
926 public static final String EXTRA_ALLOW_DURING_SETUP = "android.allowDuringSetup";
927
928 /**
Jose Limae9e3b3b2014-05-18 23:44:50 -0700929 * {@link #extras} key: A
930 * {@link android.content.ContentUris content URI} pointing to an image that can be displayed
931 * in the background when the notification is selected. The URI must point to an image stream
932 * suitable for passing into
933 * {@link android.graphics.BitmapFactory#decodeStream(java.io.InputStream)
934 * BitmapFactory.decodeStream}; all other content types will be ignored. The content provider
935 * URI used for this purpose must require no permissions to read the image data.
936 */
937 public static final String EXTRA_BACKGROUND_IMAGE_URI = "android.backgroundImageUri";
938
939 /**
Dan Sandler842dd772014-05-15 09:36:47 -0400940 * {@link #extras} key: A
Jeff Browndba34ba2014-06-24 20:46:03 -0700941 * {@link android.media.session.MediaSession.Token} associated with a
Dan Sandler842dd772014-05-15 09:36:47 -0400942 * {@link android.app.Notification.MediaStyle} notification.
943 */
944 public static final String EXTRA_MEDIA_SESSION = "android.mediaSession";
945
946 /**
Bryan Mawhinneye191f902014-07-22 12:50:09 +0100947 * {@link #extras} key: the indices of actions to be shown in the compact view,
948 * as supplied to (e.g.) {@link MediaStyle#setShowActionsInCompactView(int...)}.
949 */
950 public static final String EXTRA_COMPACT_ACTIONS = "android.compactActions";
951
Christoph Studer943aa672014-08-03 20:31:16 +0200952 /**
Alex Hillsfc737de2016-03-23 17:33:02 -0400953 * {@link #extras} key: the username to be displayed for all messages sent by the user including
954 * direct replies
Adrian Roos96b7e202016-05-17 13:50:38 -0700955 * {@link android.app.Notification.MessagingStyle} notification. This extra is a
956 * {@link CharSequence}
Alex Hillsfc737de2016-03-23 17:33:02 -0400957 */
958 public static final String EXTRA_SELF_DISPLAY_NAME = "android.selfDisplayName";
959
960 /**
Adrian Roos96b7e202016-05-17 13:50:38 -0700961 * {@link #extras} key: a {@link CharSequence} to be displayed as the title to a conversation
Alex Hillsd9b04d92016-04-11 16:38:16 -0400962 * represented by a {@link android.app.Notification.MessagingStyle}
Alex Hillsfc737de2016-03-23 17:33:02 -0400963 */
Alex Hillsd9b04d92016-04-11 16:38:16 -0400964 public static final String EXTRA_CONVERSATION_TITLE = "android.conversationTitle";
Alex Hillsfc737de2016-03-23 17:33:02 -0400965
966 /**
967 * {@link #extras} key: an array of {@link android.app.Notification.MessagingStyle.Message}
968 * bundles provided by a
Adrian Roos96b7e202016-05-17 13:50:38 -0700969 * {@link android.app.Notification.MessagingStyle} notification. This extra is a parcelable
970 * array of bundles.
Alex Hillsfc737de2016-03-23 17:33:02 -0400971 */
972 public static final String EXTRA_MESSAGES = "android.messages";
973
974 /**
Adrian Roos437cd562017-01-18 15:47:03 -0800975 * {@link #extras} key: an array of
976 * {@link android.app.Notification.MessagingStyle#addHistoricMessage historic}
977 * {@link android.app.Notification.MessagingStyle.Message} bundles provided by a
978 * {@link android.app.Notification.MessagingStyle} notification. This extra is a parcelable
979 * array of bundles.
980 */
981 public static final String EXTRA_HISTORIC_MESSAGES = "android.messages.historic";
982
983 /**
Kenny Guy8942bcd2014-09-08 21:09:47 +0100984 * {@link #extras} key: the user that built the notification.
985 *
986 * @hide
987 */
988 public static final String EXTRA_ORIGINATING_USERID = "android.originatingUserId";
989
990 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400991 * @hide
992 */
993 public static final String EXTRA_BUILDER_APPLICATION_INFO = "android.appInfo";
994
Selim Cinek247fa012016-02-18 09:50:48 -0800995 /**
996 * @hide
997 */
998 public static final String EXTRA_CONTAINS_CUSTOM_VIEW = "android.contains.customView";
999
Shane Brennan472a3b32016-12-12 15:28:10 -08001000 /**
1001 * {@link #extras} key: the audio contents of this notification.
1002 *
1003 * This is for use when rendering the notification on an audio-focused interface;
1004 * the audio contents are a complete sound sample that contains the contents/body of the
1005 * notification. This may be used in substitute of a Text-to-Speech reading of the
1006 * notification. For example if the notification represents a voice message this should point
1007 * to the audio of that message.
1008 *
1009 * The data stored under this key should be a String representation of a Uri that contains the
1010 * audio contents in one of the following formats: WAV, PCM 16-bit, AMR-WB.
1011 *
1012 * This extra is unnecessary if you are using {@code MessagingStyle} since each {@code Message}
1013 * has a field for holding data URI. That field can be used for audio.
1014 * See {@code Message#setData}.
1015 *
1016 * Example usage:
1017 * <pre>
1018 * {@code
1019 * Notification.Builder myBuilder = (build your Notification as normal);
1020 * myBuilder.getExtras().putString(EXTRA_AUDIO_CONTENTS_URI, myAudioUri.toString());
1021 * }
1022 * </pre>
1023 */
1024 public static final String EXTRA_AUDIO_CONTENTS_URI = "android.audioContents";
1025
Dan Sandler80eaa592016-04-14 23:34:54 -04001026 /** @hide */
1027 @SystemApi
Dan Sandler732bd6c2016-04-12 14:20:32 -04001028 public static final String EXTRA_SUBSTITUTE_APP_NAME = "android.substName";
1029
Dan Sandlerd63f9322015-05-06 15:18:49 -04001030 private Icon mSmallIcon;
1031 private Icon mLargeIcon;
1032
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04001033 private String mChannelId;
1034
Chris Wren51c75102013-07-16 20:49:17 -04001035 /**
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001036 * Structure to encapsulate a named action that can be shown as part of this notification.
1037 * It must include an icon, a label, and a {@link PendingIntent} to be fired when the action is
1038 * selected by the user.
1039 * <p>
Griff Hazen959591e2014-05-15 22:26:18 -07001040 * Apps should use {@link Notification.Builder#addAction(int, CharSequence, PendingIntent)}
1041 * or {@link Notification.Builder#addAction(Notification.Action)}
1042 * to attach actions.
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001043 */
Daniel Sandlerea2a3172013-02-20 22:24:20 -05001044 public static class Action implements Parcelable {
Shane Brennan472a3b32016-12-12 15:28:10 -08001045 /**
1046 * {@link #extras} key: Keys to a {@link Parcelable} {@link ArrayList} of
1047 * {@link RemoteInput}s.
1048 *
1049 * This is intended for {@link RemoteInput}s that only accept data, meaning
1050 * {@link RemoteInput#getAllowFreeFormInput} is false, {@link RemoteInput#getChoices}
1051 * is null or empty, and {@link RemoteInput#getAllowedDataTypes is non-null and not
1052 * empty. These {@link RemoteInput}s will be ignored by devices that do not
1053 * support non-text-based {@link RemoteInput}s. See {@link Builder#build}.
1054 *
1055 * You can test if a RemoteInput matches these constraints using
1056 * {@link RemoteInput#isDataOnly}.
1057 */
1058 private static final String EXTRA_DATA_ONLY_INPUTS = "android.extra.DATA_ONLY_INPUTS";
1059
Griff Hazen959591e2014-05-15 22:26:18 -07001060 private final Bundle mExtras;
Dan Sandler86647982015-05-13 23:41:13 -04001061 private Icon mIcon;
Griff Hazen61a9e862014-05-22 16:05:19 -07001062 private final RemoteInput[] mRemoteInputs;
Alex Hills42b0c4d2016-04-26 13:35:36 -04001063 private boolean mAllowGeneratedReplies = false;
Griff Hazen959591e2014-05-15 22:26:18 -07001064
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001065 /**
1066 * Small icon representing the action.
Dan Sandler86647982015-05-13 23:41:13 -04001067 *
1068 * @deprecated Use {@link Action#getIcon()} instead.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001069 */
Dan Sandler86647982015-05-13 23:41:13 -04001070 @Deprecated
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001071 public int icon;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001072
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001073 /**
1074 * Title of the action.
1075 */
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001076 public CharSequence title;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001077
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001078 /**
1079 * Intent to send when the user invokes this action. May be null, in which case the action
1080 * may be rendered in a disabled presentation by the system UI.
1081 */
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001082 public PendingIntent actionIntent;
Griff Hazen959591e2014-05-15 22:26:18 -07001083
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001084 private Action(Parcel in) {
Dan Sandler86647982015-05-13 23:41:13 -04001085 if (in.readInt() != 0) {
1086 mIcon = Icon.CREATOR.createFromParcel(in);
Dan Sandler68079d52015-07-22 10:45:30 -04001087 if (mIcon.getType() == Icon.TYPE_RESOURCE) {
1088 icon = mIcon.getResId();
1089 }
Dan Sandler86647982015-05-13 23:41:13 -04001090 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001091 title = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
1092 if (in.readInt() == 1) {
1093 actionIntent = PendingIntent.CREATOR.createFromParcel(in);
1094 }
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001095 mExtras = Bundle.setDefusable(in.readBundle(), true);
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001096 mRemoteInputs = in.createTypedArray(RemoteInput.CREATOR);
Alex Hills42b0c4d2016-04-26 13:35:36 -04001097 mAllowGeneratedReplies = in.readInt() == 1;
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001098 }
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001099
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001100 /**
Dan Sandler86647982015-05-13 23:41:13 -04001101 * @deprecated Use {@link android.app.Notification.Action.Builder}.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001102 */
Dan Sandler86647982015-05-13 23:41:13 -04001103 @Deprecated
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001104 public Action(int icon, CharSequence title, PendingIntent intent) {
Alex Hills42b0c4d2016-04-26 13:35:36 -04001105 this(Icon.createWithResource("", icon), title, intent, new Bundle(), null, false);
Griff Hazen959591e2014-05-15 22:26:18 -07001106 }
1107
Adrian Roos7af53622016-10-12 13:44:05 -07001108 /** Keep in sync with {@link Notification.Action.Builder#Builder(Action)}! */
Dan Sandler86647982015-05-13 23:41:13 -04001109 private Action(Icon icon, CharSequence title, PendingIntent intent, Bundle extras,
Alex Hills42b0c4d2016-04-26 13:35:36 -04001110 RemoteInput[] remoteInputs, boolean allowGeneratedReplies) {
Dan Sandler86647982015-05-13 23:41:13 -04001111 this.mIcon = icon;
Gus Prevasf5bff46f2015-08-24 10:34:28 -04001112 if (icon != null && icon.getType() == Icon.TYPE_RESOURCE) {
1113 this.icon = icon.getResId();
1114 }
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001115 this.title = title;
1116 this.actionIntent = intent;
Griff Hazen959591e2014-05-15 22:26:18 -07001117 this.mExtras = extras != null ? extras : new Bundle();
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001118 this.mRemoteInputs = remoteInputs;
Alex Hills42b0c4d2016-04-26 13:35:36 -04001119 this.mAllowGeneratedReplies = allowGeneratedReplies;
Griff Hazen959591e2014-05-15 22:26:18 -07001120 }
1121
1122 /**
Dan Sandler86647982015-05-13 23:41:13 -04001123 * Return an icon representing the action.
1124 */
1125 public Icon getIcon() {
1126 if (mIcon == null && icon != 0) {
1127 // you snuck an icon in here without using the builder; let's try to keep it
1128 mIcon = Icon.createWithResource("", icon);
1129 }
1130 return mIcon;
1131 }
1132
1133 /**
Griff Hazen959591e2014-05-15 22:26:18 -07001134 * Get additional metadata carried around with this Action.
1135 */
1136 public Bundle getExtras() {
1137 return mExtras;
1138 }
1139
1140 /**
Alex Hills42b0c4d2016-04-26 13:35:36 -04001141 * Return whether the platform should automatically generate possible replies for this
1142 * {@link Action}
1143 */
1144 public boolean getAllowGeneratedReplies() {
1145 return mAllowGeneratedReplies;
1146 }
1147
1148 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001149 * Get the list of inputs to be collected from the user when this action is sent.
Shane Brennan472a3b32016-12-12 15:28:10 -08001150 * May return null if no remote inputs were added. Only returns inputs which accept
1151 * a text input. For inputs which only accept data use {@link #getDataOnlyRemoteInputs}.
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001152 */
1153 public RemoteInput[] getRemoteInputs() {
1154 return mRemoteInputs;
1155 }
1156
1157 /**
Shane Brennan472a3b32016-12-12 15:28:10 -08001158 * Get the list of inputs to be collected from the user that ONLY accept data when this
1159 * action is sent. These remote inputs are guaranteed to return true on a call to
1160 * {@link RemoteInput#isDataOnly}.
1161 *
1162 * May return null if no data-only remote inputs were added.
1163 *
1164 * This method exists so that legacy RemoteInput collectors that pre-date the addition
1165 * of non-textual RemoteInputs do not access these remote inputs.
1166 */
1167 public RemoteInput[] getDataOnlyRemoteInputs() {
1168 return (RemoteInput[]) mExtras.getParcelableArray(EXTRA_DATA_ONLY_INPUTS);
1169 }
1170
1171 /**
Griff Hazen959591e2014-05-15 22:26:18 -07001172 * Builder class for {@link Action} objects.
1173 */
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001174 public static final class Builder {
Dan Sandler86647982015-05-13 23:41:13 -04001175 private final Icon mIcon;
Griff Hazen959591e2014-05-15 22:26:18 -07001176 private final CharSequence mTitle;
1177 private final PendingIntent mIntent;
Alex Hills42b0c4d2016-04-26 13:35:36 -04001178 private boolean mAllowGeneratedReplies;
Griff Hazen959591e2014-05-15 22:26:18 -07001179 private final Bundle mExtras;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001180 private ArrayList<RemoteInput> mRemoteInputs;
Griff Hazen959591e2014-05-15 22:26:18 -07001181
1182 /**
1183 * Construct a new builder for {@link Action} object.
1184 * @param icon icon to show for this action
1185 * @param title the title of the action
1186 * @param intent the {@link PendingIntent} to fire when users trigger this action
1187 */
Dan Sandler86647982015-05-13 23:41:13 -04001188 @Deprecated
Griff Hazen959591e2014-05-15 22:26:18 -07001189 public Builder(int icon, CharSequence title, PendingIntent intent) {
Adrian Roos7af53622016-10-12 13:44:05 -07001190 this(Icon.createWithResource("", icon), title, intent);
Dan Sandler86647982015-05-13 23:41:13 -04001191 }
1192
1193 /**
1194 * Construct a new builder for {@link Action} object.
1195 * @param icon icon to show for this action
1196 * @param title the title of the action
1197 * @param intent the {@link PendingIntent} to fire when users trigger this action
1198 */
1199 public Builder(Icon icon, CharSequence title, PendingIntent intent) {
Adrian Roos7af53622016-10-12 13:44:05 -07001200 this(icon, title, intent, new Bundle(), null, false);
Griff Hazen959591e2014-05-15 22:26:18 -07001201 }
1202
1203 /**
1204 * Construct a new builder for {@link Action} object using the fields from an
1205 * {@link Action}.
1206 * @param action the action to read fields from.
1207 */
1208 public Builder(Action action) {
Adrian Roos7af53622016-10-12 13:44:05 -07001209 this(action.getIcon(), action.title, action.actionIntent,
1210 new Bundle(action.mExtras), action.getRemoteInputs(),
1211 action.getAllowGeneratedReplies());
Griff Hazen959591e2014-05-15 22:26:18 -07001212 }
1213
Dan Sandler86647982015-05-13 23:41:13 -04001214 private Builder(Icon icon, CharSequence title, PendingIntent intent, Bundle extras,
Adrian Roos7af53622016-10-12 13:44:05 -07001215 RemoteInput[] remoteInputs, boolean allowGeneratedReplies) {
Griff Hazen959591e2014-05-15 22:26:18 -07001216 mIcon = icon;
1217 mTitle = title;
1218 mIntent = intent;
1219 mExtras = extras;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001220 if (remoteInputs != null) {
1221 mRemoteInputs = new ArrayList<RemoteInput>(remoteInputs.length);
1222 Collections.addAll(mRemoteInputs, remoteInputs);
1223 }
Adrian Roos7af53622016-10-12 13:44:05 -07001224 mAllowGeneratedReplies = allowGeneratedReplies;
Griff Hazen959591e2014-05-15 22:26:18 -07001225 }
1226
1227 /**
1228 * Merge additional metadata into this builder.
1229 *
1230 * <p>Values within the Bundle will replace existing extras values in this Builder.
1231 *
1232 * @see Notification.Action#extras
1233 */
1234 public Builder addExtras(Bundle extras) {
1235 if (extras != null) {
1236 mExtras.putAll(extras);
1237 }
1238 return this;
1239 }
1240
1241 /**
1242 * Get the metadata Bundle used by this Builder.
1243 *
1244 * <p>The returned Bundle is shared with this Builder.
1245 */
1246 public Bundle getExtras() {
1247 return mExtras;
1248 }
1249
1250 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001251 * Add an input to be collected from the user when this action is sent.
1252 * Response values can be retrieved from the fired intent by using the
1253 * {@link RemoteInput#getResultsFromIntent} function.
1254 * @param remoteInput a {@link RemoteInput} to add to the action
1255 * @return this object for method chaining
1256 */
1257 public Builder addRemoteInput(RemoteInput remoteInput) {
1258 if (mRemoteInputs == null) {
1259 mRemoteInputs = new ArrayList<RemoteInput>();
1260 }
1261 mRemoteInputs.add(remoteInput);
1262 return this;
1263 }
1264
1265 /**
Alex Hills42b0c4d2016-04-26 13:35:36 -04001266 * Set whether the platform should automatically generate possible replies to add to
1267 * {@link RemoteInput#getChoices()}. If the {@link Action} doesn't have a
1268 * {@link RemoteInput}, this has no effect.
1269 * @param allowGeneratedReplies {@code true} to allow generated replies, {@code false}
1270 * otherwise
1271 * @return this object for method chaining
1272 * The default value is {@code false}
1273 */
1274 public Builder setAllowGeneratedReplies(boolean allowGeneratedReplies) {
1275 mAllowGeneratedReplies = allowGeneratedReplies;
1276 return this;
1277 }
1278
1279 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001280 * Apply an extender to this action builder. Extenders may be used to add
1281 * metadata or change options on this builder.
1282 */
Griff Hazen61a9e862014-05-22 16:05:19 -07001283 public Builder extend(Extender extender) {
1284 extender.extend(this);
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001285 return this;
1286 }
1287
1288 /**
Griff Hazen959591e2014-05-15 22:26:18 -07001289 * Combine all of the options that have been set and return a new {@link Action}
1290 * object.
1291 * @return the built action
1292 */
1293 public Action build() {
Shane Brennan472a3b32016-12-12 15:28:10 -08001294 ArrayList<RemoteInput> dataOnlyInputs = new ArrayList<>();
1295 RemoteInput[] previousDataInputs =
1296 (RemoteInput[]) mExtras.getParcelableArray(EXTRA_DATA_ONLY_INPUTS);
Felipe Lemedfd11962017-01-18 18:38:46 -08001297 if (previousDataInputs != null) {
Shane Brennan472a3b32016-12-12 15:28:10 -08001298 for (RemoteInput input : previousDataInputs) {
1299 dataOnlyInputs.add(input);
1300 }
1301 }
1302 List<RemoteInput> textInputs = new ArrayList<>();
1303 if (mRemoteInputs != null) {
1304 for (RemoteInput input : mRemoteInputs) {
1305 if (input.isDataOnly()) {
1306 dataOnlyInputs.add(input);
1307 } else {
1308 textInputs.add(input);
1309 }
1310 }
1311 }
1312 if (!dataOnlyInputs.isEmpty()) {
1313 RemoteInput[] dataInputsArr =
1314 dataOnlyInputs.toArray(new RemoteInput[dataOnlyInputs.size()]);
1315 mExtras.putParcelableArray(EXTRA_DATA_ONLY_INPUTS, dataInputsArr);
1316 }
1317 RemoteInput[] textInputsArr = textInputs.isEmpty()
1318 ? null : textInputs.toArray(new RemoteInput[textInputs.size()]);
1319 return new Action(mIcon, mTitle, mIntent, mExtras, textInputsArr,
Alex Hills42b0c4d2016-04-26 13:35:36 -04001320 mAllowGeneratedReplies);
Griff Hazen959591e2014-05-15 22:26:18 -07001321 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001322 }
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001323
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001324 @Override
1325 public Action clone() {
1326 return new Action(
Dan Sandler86647982015-05-13 23:41:13 -04001327 getIcon(),
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001328 title,
1329 actionIntent, // safe to alias
Julia Reynolds53c934c2016-09-16 14:03:43 -04001330 mExtras == null ? new Bundle() : new Bundle(mExtras),
Alex Hills42b0c4d2016-04-26 13:35:36 -04001331 getRemoteInputs(),
1332 getAllowGeneratedReplies());
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001333 }
1334 @Override
1335 public int describeContents() {
1336 return 0;
1337 }
1338 @Override
1339 public void writeToParcel(Parcel out, int flags) {
Dan Sandler86647982015-05-13 23:41:13 -04001340 final Icon ic = getIcon();
1341 if (ic != null) {
1342 out.writeInt(1);
1343 ic.writeToParcel(out, 0);
1344 } else {
1345 out.writeInt(0);
1346 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001347 TextUtils.writeToParcel(title, out, flags);
1348 if (actionIntent != null) {
1349 out.writeInt(1);
1350 actionIntent.writeToParcel(out, flags);
1351 } else {
1352 out.writeInt(0);
1353 }
Griff Hazen959591e2014-05-15 22:26:18 -07001354 out.writeBundle(mExtras);
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001355 out.writeTypedArray(mRemoteInputs, flags);
Alex Hills42b0c4d2016-04-26 13:35:36 -04001356 out.writeInt(mAllowGeneratedReplies ? 1 : 0);
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001357 }
Griff Hazen959591e2014-05-15 22:26:18 -07001358 public static final Parcelable.Creator<Action> CREATOR =
1359 new Parcelable.Creator<Action>() {
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001360 public Action createFromParcel(Parcel in) {
1361 return new Action(in);
1362 }
1363 public Action[] newArray(int size) {
1364 return new Action[size];
1365 }
1366 };
Griff Hazen61a9e862014-05-22 16:05:19 -07001367
1368 /**
1369 * Extender interface for use with {@link Builder#extend}. Extenders may be used to add
1370 * metadata or change options on an action builder.
1371 */
1372 public interface Extender {
1373 /**
1374 * Apply this extender to a notification action builder.
1375 * @param builder the builder to be modified.
1376 * @return the build object for chaining.
1377 */
1378 public Builder extend(Builder builder);
1379 }
1380
1381 /**
1382 * Wearable extender for notification actions. To add extensions to an action,
1383 * create a new {@link android.app.Notification.Action.WearableExtender} object using
1384 * the {@code WearableExtender()} constructor and apply it to a
1385 * {@link android.app.Notification.Action.Builder} using
1386 * {@link android.app.Notification.Action.Builder#extend}.
1387 *
1388 * <pre class="prettyprint">
1389 * Notification.Action action = new Notification.Action.Builder(
1390 * R.drawable.archive_all, "Archive all", actionIntent)
Griff Hazen14f57992014-05-26 09:07:14 -07001391 * .extend(new Notification.Action.WearableExtender()
Griff Hazen61a9e862014-05-22 16:05:19 -07001392 * .setAvailableOffline(false))
Griff Hazen14f57992014-05-26 09:07:14 -07001393 * .build();</pre>
Griff Hazen61a9e862014-05-22 16:05:19 -07001394 */
1395 public static final class WearableExtender implements Extender {
1396 /** Notification action extra which contains wearable extensions */
1397 private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS";
1398
Pete Gastaf6781d2014-10-07 15:17:05 -04001399 // Keys within EXTRA_WEARABLE_EXTENSIONS for wearable options.
Griff Hazen61a9e862014-05-22 16:05:19 -07001400 private static final String KEY_FLAGS = "flags";
Pete Gastaf6781d2014-10-07 15:17:05 -04001401 private static final String KEY_IN_PROGRESS_LABEL = "inProgressLabel";
1402 private static final String KEY_CONFIRM_LABEL = "confirmLabel";
1403 private static final String KEY_CANCEL_LABEL = "cancelLabel";
Griff Hazen61a9e862014-05-22 16:05:19 -07001404
1405 // Flags bitwise-ored to mFlags
1406 private static final int FLAG_AVAILABLE_OFFLINE = 0x1;
Alex Hills9ab3a232016-04-05 14:54:56 -04001407 private static final int FLAG_HINT_LAUNCHES_ACTIVITY = 1 << 1;
Alex Hills9f087612016-06-07 09:08:59 -04001408 private static final int FLAG_HINT_DISPLAY_INLINE = 1 << 2;
Griff Hazen61a9e862014-05-22 16:05:19 -07001409
1410 // Default value for flags integer
1411 private static final int DEFAULT_FLAGS = FLAG_AVAILABLE_OFFLINE;
1412
1413 private int mFlags = DEFAULT_FLAGS;
1414
Pete Gastaf6781d2014-10-07 15:17:05 -04001415 private CharSequence mInProgressLabel;
1416 private CharSequence mConfirmLabel;
1417 private CharSequence mCancelLabel;
1418
Griff Hazen61a9e862014-05-22 16:05:19 -07001419 /**
1420 * Create a {@link android.app.Notification.Action.WearableExtender} with default
1421 * options.
1422 */
1423 public WearableExtender() {
1424 }
1425
1426 /**
1427 * Create a {@link android.app.Notification.Action.WearableExtender} by reading
1428 * wearable options present in an existing notification action.
1429 * @param action the notification action to inspect.
1430 */
1431 public WearableExtender(Action action) {
1432 Bundle wearableBundle = action.getExtras().getBundle(EXTRA_WEARABLE_EXTENSIONS);
1433 if (wearableBundle != null) {
1434 mFlags = wearableBundle.getInt(KEY_FLAGS, DEFAULT_FLAGS);
Pete Gastaf6781d2014-10-07 15:17:05 -04001435 mInProgressLabel = wearableBundle.getCharSequence(KEY_IN_PROGRESS_LABEL);
1436 mConfirmLabel = wearableBundle.getCharSequence(KEY_CONFIRM_LABEL);
1437 mCancelLabel = wearableBundle.getCharSequence(KEY_CANCEL_LABEL);
Griff Hazen61a9e862014-05-22 16:05:19 -07001438 }
1439 }
1440
1441 /**
1442 * Apply wearable extensions to a notification action that is being built. This is
1443 * typically called by the {@link android.app.Notification.Action.Builder#extend}
1444 * method of {@link android.app.Notification.Action.Builder}.
1445 */
1446 @Override
1447 public Action.Builder extend(Action.Builder builder) {
1448 Bundle wearableBundle = new Bundle();
1449
1450 if (mFlags != DEFAULT_FLAGS) {
1451 wearableBundle.putInt(KEY_FLAGS, mFlags);
1452 }
Pete Gastaf6781d2014-10-07 15:17:05 -04001453 if (mInProgressLabel != null) {
1454 wearableBundle.putCharSequence(KEY_IN_PROGRESS_LABEL, mInProgressLabel);
1455 }
1456 if (mConfirmLabel != null) {
1457 wearableBundle.putCharSequence(KEY_CONFIRM_LABEL, mConfirmLabel);
1458 }
1459 if (mCancelLabel != null) {
1460 wearableBundle.putCharSequence(KEY_CANCEL_LABEL, mCancelLabel);
1461 }
Griff Hazen61a9e862014-05-22 16:05:19 -07001462
1463 builder.getExtras().putBundle(EXTRA_WEARABLE_EXTENSIONS, wearableBundle);
1464 return builder;
1465 }
1466
1467 @Override
1468 public WearableExtender clone() {
1469 WearableExtender that = new WearableExtender();
1470 that.mFlags = this.mFlags;
Pete Gastaf6781d2014-10-07 15:17:05 -04001471 that.mInProgressLabel = this.mInProgressLabel;
1472 that.mConfirmLabel = this.mConfirmLabel;
1473 that.mCancelLabel = this.mCancelLabel;
Griff Hazen61a9e862014-05-22 16:05:19 -07001474 return that;
1475 }
1476
1477 /**
1478 * Set whether this action is available when the wearable device is not connected to
1479 * a companion device. The user can still trigger this action when the wearable device is
1480 * offline, but a visual hint will indicate that the action may not be available.
1481 * Defaults to true.
1482 */
1483 public WearableExtender setAvailableOffline(boolean availableOffline) {
1484 setFlag(FLAG_AVAILABLE_OFFLINE, availableOffline);
1485 return this;
1486 }
1487
1488 /**
1489 * Get whether this action is available when the wearable device is not connected to
1490 * a companion device. The user can still trigger this action when the wearable device is
1491 * offline, but a visual hint will indicate that the action may not be available.
1492 * Defaults to true.
1493 */
1494 public boolean isAvailableOffline() {
1495 return (mFlags & FLAG_AVAILABLE_OFFLINE) != 0;
1496 }
1497
1498 private void setFlag(int mask, boolean value) {
1499 if (value) {
1500 mFlags |= mask;
1501 } else {
1502 mFlags &= ~mask;
1503 }
1504 }
Pete Gastaf6781d2014-10-07 15:17:05 -04001505
1506 /**
1507 * Set a label to display while the wearable is preparing to automatically execute the
1508 * action. This is usually a 'ing' verb ending in ellipsis like "Sending..."
1509 *
1510 * @param label the label to display while the action is being prepared to execute
1511 * @return this object for method chaining
1512 */
1513 public WearableExtender setInProgressLabel(CharSequence label) {
1514 mInProgressLabel = label;
1515 return this;
1516 }
1517
1518 /**
1519 * Get the label to display while the wearable is preparing to automatically execute
1520 * the action. This is usually a 'ing' verb ending in ellipsis like "Sending..."
1521 *
1522 * @return the label to display while the action is being prepared to execute
1523 */
1524 public CharSequence getInProgressLabel() {
1525 return mInProgressLabel;
1526 }
1527
1528 /**
1529 * Set a label to display to confirm that the action should be executed.
1530 * This is usually an imperative verb like "Send".
1531 *
1532 * @param label the label to confirm the action should be executed
1533 * @return this object for method chaining
1534 */
1535 public WearableExtender setConfirmLabel(CharSequence label) {
1536 mConfirmLabel = label;
1537 return this;
1538 }
1539
1540 /**
1541 * Get the label to display to confirm that the action should be executed.
1542 * This is usually an imperative verb like "Send".
1543 *
1544 * @return the label to confirm the action should be executed
1545 */
1546 public CharSequence getConfirmLabel() {
1547 return mConfirmLabel;
1548 }
1549
1550 /**
1551 * Set a label to display to cancel the action.
1552 * This is usually an imperative verb, like "Cancel".
1553 *
1554 * @param label the label to display to cancel the action
1555 * @return this object for method chaining
1556 */
1557 public WearableExtender setCancelLabel(CharSequence label) {
1558 mCancelLabel = label;
1559 return this;
1560 }
1561
1562 /**
1563 * Get the label to display to cancel the action.
1564 * This is usually an imperative verb like "Cancel".
1565 *
1566 * @return the label to display to cancel the action
1567 */
1568 public CharSequence getCancelLabel() {
1569 return mCancelLabel;
1570 }
Alex Hills9ab3a232016-04-05 14:54:56 -04001571
1572 /**
1573 * Set a hint that this Action will launch an {@link Activity} directly, telling the
1574 * platform that it can generate the appropriate transitions.
1575 * @param hintLaunchesActivity {@code true} if the content intent will launch
1576 * an activity and transitions should be generated, false otherwise.
1577 * @return this object for method chaining
1578 */
Alex Hills4ec3ff42016-04-12 11:36:18 -04001579 public WearableExtender setHintLaunchesActivity(
Alex Hills9ab3a232016-04-05 14:54:56 -04001580 boolean hintLaunchesActivity) {
1581 setFlag(FLAG_HINT_LAUNCHES_ACTIVITY, hintLaunchesActivity);
1582 return this;
1583 }
1584
1585 /**
1586 * Get a hint that this Action will launch an {@link Activity} directly, telling the
1587 * platform that it can generate the appropriate transitions
1588 * @return {@code true} if the content intent will launch an activity and transitions
1589 * should be generated, false otherwise. The default value is {@code false} if this was
1590 * never set.
1591 */
Alex Hills4ec3ff42016-04-12 11:36:18 -04001592 public boolean getHintLaunchesActivity() {
Alex Hills9ab3a232016-04-05 14:54:56 -04001593 return (mFlags & FLAG_HINT_LAUNCHES_ACTIVITY) != 0;
1594 }
Alex Hills9f087612016-06-07 09:08:59 -04001595
1596 /**
1597 * Set a hint that this Action should be displayed inline.
1598 *
1599 * @param hintDisplayInline {@code true} if action should be displayed inline, false
1600 * otherwise
1601 * @return this object for method chaining
1602 */
1603 public WearableExtender setHintDisplayActionInline(
1604 boolean hintDisplayInline) {
1605 setFlag(FLAG_HINT_DISPLAY_INLINE, hintDisplayInline);
1606 return this;
1607 }
1608
1609 /**
1610 * Get a hint that this Action should be displayed inline.
1611 *
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08001612 * @return {@code true} if the Action should be displayed inline, {@code false}
Alex Hills9f087612016-06-07 09:08:59 -04001613 * otherwise. The default value is {@code false} if this was never set.
1614 */
1615 public boolean getHintDisplayActionInline() {
1616 return (mFlags & FLAG_HINT_DISPLAY_INLINE) != 0;
1617 }
Griff Hazen61a9e862014-05-22 16:05:19 -07001618 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001619 }
1620
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001621 /**
1622 * Array of all {@link Action} structures attached to this notification by
1623 * {@link Builder#addAction(int, CharSequence, PendingIntent)}. Mostly useful for instances of
1624 * {@link android.service.notification.NotificationListenerService} that provide an alternative
1625 * interface for invoking actions.
1626 */
Daniel Sandlerea2a3172013-02-20 22:24:20 -05001627 public Action[] actions;
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001628
1629 /**
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001630 * Replacement version of this notification whose content will be shown
1631 * in an insecure context such as atop a secure keyguard. See {@link #visibility}
1632 * and {@link #VISIBILITY_PUBLIC}.
1633 */
1634 public Notification publicVersion;
1635
1636 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001637 * Constructs a Notification object with default values.
Joe Onorato46439ce2010-11-19 13:56:21 -08001638 * You might want to consider using {@link Builder} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001639 */
1640 public Notification()
1641 {
1642 this.when = System.currentTimeMillis();
Selim Cinekb85f36fd2016-04-20 18:46:36 -07001643 this.creationTime = System.currentTimeMillis();
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001644 this.priority = PRIORITY_DEFAULT;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001645 }
1646
1647 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001648 * @hide
1649 */
1650 public Notification(Context context, int icon, CharSequence tickerText, long when,
1651 CharSequence contentTitle, CharSequence contentText, Intent contentIntent)
1652 {
Chris Wren1ce4b6d2015-06-11 10:19:43 -04001653 new Builder(context)
1654 .setWhen(when)
1655 .setSmallIcon(icon)
1656 .setTicker(tickerText)
1657 .setContentTitle(contentTitle)
1658 .setContentText(contentText)
1659 .setContentIntent(PendingIntent.getActivity(context, 0, contentIntent, 0))
1660 .buildInto(this);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001661 }
1662
1663 /**
1664 * Constructs a Notification object with the information needed to
1665 * have a status bar icon without the standard expanded view.
1666 *
1667 * @param icon The resource id of the icon to put in the status bar.
1668 * @param tickerText The text that flows by in the status bar when the notification first
1669 * activates.
1670 * @param when The time to show in the time field. In the System.currentTimeMillis
1671 * timebase.
Joe Onorato46439ce2010-11-19 13:56:21 -08001672 *
1673 * @deprecated Use {@link Builder} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001674 */
Joe Onorato46439ce2010-11-19 13:56:21 -08001675 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001676 public Notification(int icon, CharSequence tickerText, long when)
1677 {
1678 this.icon = icon;
1679 this.tickerText = tickerText;
1680 this.when = when;
Selim Cinekb85f36fd2016-04-20 18:46:36 -07001681 this.creationTime = System.currentTimeMillis();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001682 }
1683
1684 /**
1685 * Unflatten the notification from a parcel.
1686 */
Svet Ganovddb94882016-06-23 19:55:24 -07001687 @SuppressWarnings("unchecked")
1688 public Notification(Parcel parcel) {
1689 // IMPORTANT: Add unmarshaling code in readFromParcel as the pending
1690 // intents in extras are always written as the last entry.
1691 readFromParcelImpl(parcel);
1692 // Must be read last!
Felipe Lemedd85da62016-06-28 11:29:54 -07001693 allPendingIntents = (ArraySet<PendingIntent>) parcel.readArraySet(null);
Svet Ganovddb94882016-06-23 19:55:24 -07001694 }
1695
1696 private void readFromParcelImpl(Parcel parcel)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001697 {
1698 int version = parcel.readInt();
1699
1700 when = parcel.readLong();
Selim Cinekb85f36fd2016-04-20 18:46:36 -07001701 creationTime = parcel.readLong();
Dan Sandler3936e7a2015-05-19 20:59:12 -04001702 if (parcel.readInt() != 0) {
1703 mSmallIcon = Icon.CREATOR.createFromParcel(parcel);
Dan Sandler4e787062015-06-17 15:09:48 -04001704 if (mSmallIcon.getType() == Icon.TYPE_RESOURCE) {
1705 icon = mSmallIcon.getResId();
1706 }
Dan Sandler3936e7a2015-05-19 20:59:12 -04001707 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001708 number = parcel.readInt();
1709 if (parcel.readInt() != 0) {
1710 contentIntent = PendingIntent.CREATOR.createFromParcel(parcel);
1711 }
1712 if (parcel.readInt() != 0) {
1713 deleteIntent = PendingIntent.CREATOR.createFromParcel(parcel);
1714 }
1715 if (parcel.readInt() != 0) {
1716 tickerText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
1717 }
1718 if (parcel.readInt() != 0) {
Joe Onorato46439ce2010-11-19 13:56:21 -08001719 tickerView = RemoteViews.CREATOR.createFromParcel(parcel);
Joe Onoratoef1e7762010-09-17 18:38:38 -04001720 }
1721 if (parcel.readInt() != 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001722 contentView = RemoteViews.CREATOR.createFromParcel(parcel);
1723 }
Joe Onorato561d3852010-11-20 18:09:34 -08001724 if (parcel.readInt() != 0) {
Dan Sandlerd63f9322015-05-06 15:18:49 -04001725 mLargeIcon = Icon.CREATOR.createFromParcel(parcel);
Joe Onorato561d3852010-11-20 18:09:34 -08001726 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001727 defaults = parcel.readInt();
1728 flags = parcel.readInt();
1729 if (parcel.readInt() != 0) {
1730 sound = Uri.CREATOR.createFromParcel(parcel);
1731 }
1732
1733 audioStreamType = parcel.readInt();
John Spurlockc0650f022014-07-19 13:22:39 -04001734 if (parcel.readInt() != 0) {
1735 audioAttributes = AudioAttributes.CREATOR.createFromParcel(parcel);
1736 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001737 vibrate = parcel.createLongArray();
1738 ledARGB = parcel.readInt();
1739 ledOnMS = parcel.readInt();
1740 ledOffMS = parcel.readInt();
1741 iconLevel = parcel.readInt();
Daniel Sandlere46cbd32010-06-17 10:35:26 -04001742
1743 if (parcel.readInt() != 0) {
1744 fullScreenIntent = PendingIntent.CREATOR.createFromParcel(parcel);
1745 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001746
1747 priority = parcel.readInt();
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001748
John Spurlockfd7f1e02014-03-18 16:41:57 -04001749 category = parcel.readString();
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001750
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001751 mGroupKey = parcel.readString();
1752
1753 mSortKey = parcel.readString();
1754
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001755 extras = Bundle.setDefusable(parcel.readBundle(), true); // may be null
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001756
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001757 actions = parcel.createTypedArray(Action.CREATOR); // may be null
1758
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001759 if (parcel.readInt() != 0) {
Daniel Sandlerf3b73432012-03-27 15:01:25 -04001760 bigContentView = RemoteViews.CREATOR.createFromParcel(parcel);
1761 }
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001762
Chris Wren8fd39ec2014-02-27 17:43:26 -05001763 if (parcel.readInt() != 0) {
1764 headsUpContentView = RemoteViews.CREATOR.createFromParcel(parcel);
1765 }
1766
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001767 visibility = parcel.readInt();
1768
1769 if (parcel.readInt() != 0) {
1770 publicVersion = Notification.CREATOR.createFromParcel(parcel);
1771 }
Dan Sandler26e81cf2014-05-06 10:01:27 -04001772
1773 color = parcel.readInt();
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04001774
1775 if (parcel.readInt() != 0) {
1776 mChannelId = parcel.readString();
1777 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001778 }
1779
Andy Stadler110988c2010-12-03 14:29:16 -08001780 @Override
Joe Onorato18e69df2010-05-17 22:26:12 -07001781 public Notification clone() {
1782 Notification that = new Notification();
Daniel Sandler1a497d32013-04-18 14:52:45 -04001783 cloneInto(that, true);
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001784 return that;
1785 }
Joe Onorato18e69df2010-05-17 22:26:12 -07001786
Daniel Sandler1a497d32013-04-18 14:52:45 -04001787 /**
1788 * Copy all (or if heavy is false, all except Bitmaps and RemoteViews) members
1789 * of this into that.
1790 * @hide
1791 */
1792 public void cloneInto(Notification that, boolean heavy) {
Joe Onorato18e69df2010-05-17 22:26:12 -07001793 that.when = this.when;
Selim Cinekb85f36fd2016-04-20 18:46:36 -07001794 that.creationTime = this.creationTime;
Dan Sandlerd63f9322015-05-06 15:18:49 -04001795 that.mSmallIcon = this.mSmallIcon;
Joe Onorato18e69df2010-05-17 22:26:12 -07001796 that.number = this.number;
1797
1798 // PendingIntents are global, so there's no reason (or way) to clone them.
1799 that.contentIntent = this.contentIntent;
1800 that.deleteIntent = this.deleteIntent;
Daniel Sandlere46cbd32010-06-17 10:35:26 -04001801 that.fullScreenIntent = this.fullScreenIntent;
Joe Onorato18e69df2010-05-17 22:26:12 -07001802
1803 if (this.tickerText != null) {
1804 that.tickerText = this.tickerText.toString();
1805 }
Daniel Sandler1a497d32013-04-18 14:52:45 -04001806 if (heavy && this.tickerView != null) {
Joe Onorato46439ce2010-11-19 13:56:21 -08001807 that.tickerView = this.tickerView.clone();
Joe Onoratoef1e7762010-09-17 18:38:38 -04001808 }
Daniel Sandler1a497d32013-04-18 14:52:45 -04001809 if (heavy && this.contentView != null) {
Joe Onorato18e69df2010-05-17 22:26:12 -07001810 that.contentView = this.contentView.clone();
1811 }
Dan Sandlerd63f9322015-05-06 15:18:49 -04001812 if (heavy && this.mLargeIcon != null) {
1813 that.mLargeIcon = this.mLargeIcon;
Joe Onorato561d3852010-11-20 18:09:34 -08001814 }
Jozef BABJAKa8b91832011-02-22 08:05:08 +01001815 that.iconLevel = this.iconLevel;
Joe Onorato18e69df2010-05-17 22:26:12 -07001816 that.sound = this.sound; // android.net.Uri is immutable
1817 that.audioStreamType = this.audioStreamType;
John Spurlockc0650f022014-07-19 13:22:39 -04001818 if (this.audioAttributes != null) {
1819 that.audioAttributes = new AudioAttributes.Builder(this.audioAttributes).build();
1820 }
Joe Onorato18e69df2010-05-17 22:26:12 -07001821
1822 final long[] vibrate = this.vibrate;
1823 if (vibrate != null) {
1824 final int N = vibrate.length;
1825 final long[] vib = that.vibrate = new long[N];
1826 System.arraycopy(vibrate, 0, vib, 0, N);
1827 }
1828
1829 that.ledARGB = this.ledARGB;
1830 that.ledOnMS = this.ledOnMS;
1831 that.ledOffMS = this.ledOffMS;
1832 that.defaults = this.defaults;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001833
Joe Onorato18e69df2010-05-17 22:26:12 -07001834 that.flags = this.flags;
1835
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001836 that.priority = this.priority;
Joe Malin8d40d042012-11-05 11:36:40 -08001837
John Spurlockfd7f1e02014-03-18 16:41:57 -04001838 that.category = this.category;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001839
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001840 that.mGroupKey = this.mGroupKey;
1841
1842 that.mSortKey = this.mSortKey;
1843
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001844 if (this.extras != null) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04001845 try {
1846 that.extras = new Bundle(this.extras);
1847 // will unparcel
1848 that.extras.size();
1849 } catch (BadParcelableException e) {
1850 Log.e(TAG, "could not unparcel extras from notification: " + this, e);
1851 that.extras = null;
1852 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001853 }
1854
Felipe Lemedd85da62016-06-28 11:29:54 -07001855 if (!ArrayUtils.isEmpty(allPendingIntents)) {
1856 that.allPendingIntents = new ArraySet<>(allPendingIntents);
Svet Ganovddb94882016-06-23 19:55:24 -07001857 }
1858
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001859 if (this.actions != null) {
1860 that.actions = new Action[this.actions.length];
1861 for(int i=0; i<this.actions.length; i++) {
1862 that.actions[i] = this.actions[i].clone();
1863 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001864 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001865
Daniel Sandler1a497d32013-04-18 14:52:45 -04001866 if (heavy && this.bigContentView != null) {
Daniel Sandlerf3b73432012-03-27 15:01:25 -04001867 that.bigContentView = this.bigContentView.clone();
1868 }
Daniel Sandler1a497d32013-04-18 14:52:45 -04001869
Chris Wren8fd39ec2014-02-27 17:43:26 -05001870 if (heavy && this.headsUpContentView != null) {
1871 that.headsUpContentView = this.headsUpContentView.clone();
1872 }
1873
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001874 that.visibility = this.visibility;
1875
1876 if (this.publicVersion != null) {
1877 that.publicVersion = new Notification();
1878 this.publicVersion.cloneInto(that.publicVersion, heavy);
1879 }
1880
Dan Sandler26e81cf2014-05-06 10:01:27 -04001881 that.color = this.color;
1882
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04001883 that.mChannelId = this.mChannelId;
1884
Daniel Sandler1a497d32013-04-18 14:52:45 -04001885 if (!heavy) {
1886 that.lightenPayload(); // will clean out extras
1887 }
1888 }
1889
1890 /**
1891 * Removes heavyweight parts of the Notification object for archival or for sending to
1892 * listeners when the full contents are not necessary.
1893 * @hide
1894 */
1895 public final void lightenPayload() {
1896 tickerView = null;
1897 contentView = null;
1898 bigContentView = null;
Chris Wren8fd39ec2014-02-27 17:43:26 -05001899 headsUpContentView = null;
Dan Sandlerd63f9322015-05-06 15:18:49 -04001900 mLargeIcon = null;
Dan Sandler50128532015-12-08 15:42:41 -05001901 if (extras != null && !extras.isEmpty()) {
1902 final Set<String> keyset = extras.keySet();
1903 final int N = keyset.size();
1904 final String[] keys = keyset.toArray(new String[N]);
1905 for (int i=0; i<N; i++) {
1906 final String key = keys[i];
1907 final Object obj = extras.get(key);
1908 if (obj != null &&
1909 ( obj instanceof Parcelable
1910 || obj instanceof Parcelable[]
1911 || obj instanceof SparseArray
1912 || obj instanceof ArrayList)) {
1913 extras.remove(key);
1914 }
1915 }
Daniel Sandler1a497d32013-04-18 14:52:45 -04001916 }
Joe Onorato18e69df2010-05-17 22:26:12 -07001917 }
1918
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04001919 /**
1920 * Make sure this CharSequence is safe to put into a bundle, which basically
1921 * means it had better not be some custom Parcelable implementation.
1922 * @hide
1923 */
1924 public static CharSequence safeCharSequence(CharSequence cs) {
Christoph Studer535ec612014-09-03 15:47:47 +02001925 if (cs == null) return cs;
1926 if (cs.length() > MAX_CHARSEQUENCE_LENGTH) {
1927 cs = cs.subSequence(0, MAX_CHARSEQUENCE_LENGTH);
1928 }
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04001929 if (cs instanceof Parcelable) {
1930 Log.e(TAG, "warning: " + cs.getClass().getCanonicalName()
1931 + " instance is a custom Parcelable and not allowed in Notification");
1932 return cs.toString();
1933 }
Selim Cinek60a54252016-02-26 17:03:25 -08001934 return removeTextSizeSpans(cs);
1935 }
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04001936
Selim Cinek60a54252016-02-26 17:03:25 -08001937 private static CharSequence removeTextSizeSpans(CharSequence charSequence) {
1938 if (charSequence instanceof Spanned) {
1939 Spanned ss = (Spanned) charSequence;
1940 Object[] spans = ss.getSpans(0, ss.length(), Object.class);
1941 SpannableStringBuilder builder = new SpannableStringBuilder(ss.toString());
1942 for (Object span : spans) {
1943 Object resultSpan = span;
Selim Cinek89991a22016-03-07 19:51:54 -08001944 if (resultSpan instanceof CharacterStyle) {
1945 resultSpan = ((CharacterStyle) span).getUnderlying();
1946 }
1947 if (resultSpan instanceof TextAppearanceSpan) {
1948 TextAppearanceSpan originalSpan = (TextAppearanceSpan) resultSpan;
Selim Cinek60a54252016-02-26 17:03:25 -08001949 resultSpan = new TextAppearanceSpan(
1950 originalSpan.getFamily(),
1951 originalSpan.getTextStyle(),
1952 -1,
1953 originalSpan.getTextColor(),
1954 originalSpan.getLinkTextColor());
Selim Cinek89991a22016-03-07 19:51:54 -08001955 } else if (resultSpan instanceof RelativeSizeSpan
1956 || resultSpan instanceof AbsoluteSizeSpan) {
Selim Cinek60a54252016-02-26 17:03:25 -08001957 continue;
Selim Cinek89991a22016-03-07 19:51:54 -08001958 } else {
1959 resultSpan = span;
Selim Cinek60a54252016-02-26 17:03:25 -08001960 }
1961 builder.setSpan(resultSpan, ss.getSpanStart(span), ss.getSpanEnd(span),
1962 ss.getSpanFlags(span));
1963 }
1964 return builder;
1965 }
1966 return charSequence;
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04001967 }
1968
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001969 public int describeContents() {
1970 return 0;
1971 }
1972
1973 /**
Dan Sandler4e787062015-06-17 15:09:48 -04001974 * Flatten this notification into a parcel.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001975 */
Svet Ganovddb94882016-06-23 19:55:24 -07001976 public void writeToParcel(Parcel parcel, int flags) {
1977 // We need to mark all pending intents getting into the notification
1978 // system as being put there to later allow the notification ranker
1979 // to launch them and by doing so add the app to the battery saver white
1980 // list for a short period of time. The problem is that the system
1981 // cannot look into the extras as there may be parcelables there that
1982 // the platform does not know how to handle. To go around that we have
1983 // an explicit list of the pending intents in the extras bundle.
Felipe Lemedd85da62016-06-28 11:29:54 -07001984 final boolean collectPendingIntents = (allPendingIntents == null);
Svet Ganovddb94882016-06-23 19:55:24 -07001985 if (collectPendingIntents) {
1986 PendingIntent.setOnMarshaledListener(
1987 (PendingIntent intent, Parcel out, int outFlags) -> {
1988 if (parcel == out) {
Felipe Lemedd85da62016-06-28 11:29:54 -07001989 if (allPendingIntents == null) {
1990 allPendingIntents = new ArraySet<>();
Svet Ganovddb94882016-06-23 19:55:24 -07001991 }
Felipe Lemedd85da62016-06-28 11:29:54 -07001992 allPendingIntents.add(intent);
Svet Ganovddb94882016-06-23 19:55:24 -07001993 }
1994 });
1995 }
1996 try {
1997 // IMPORTANT: Add marshaling code in writeToParcelImpl as we
1998 // want to intercept all pending events written to the pacel.
1999 writeToParcelImpl(parcel, flags);
2000 // Must be written last!
Felipe Lemedd85da62016-06-28 11:29:54 -07002001 parcel.writeArraySet(allPendingIntents);
Svet Ganovddb94882016-06-23 19:55:24 -07002002 } finally {
2003 if (collectPendingIntents) {
2004 PendingIntent.setOnMarshaledListener(null);
2005 }
2006 }
2007 }
2008
2009 private void writeToParcelImpl(Parcel parcel, int flags) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002010 parcel.writeInt(1);
2011
2012 parcel.writeLong(when);
Selim Cinekb85f36fd2016-04-20 18:46:36 -07002013 parcel.writeLong(creationTime);
Dan Sandler4e787062015-06-17 15:09:48 -04002014 if (mSmallIcon == null && icon != 0) {
2015 // you snuck an icon in here without using the builder; let's try to keep it
2016 mSmallIcon = Icon.createWithResource("", icon);
2017 }
Dan Sandler3936e7a2015-05-19 20:59:12 -04002018 if (mSmallIcon != null) {
2019 parcel.writeInt(1);
2020 mSmallIcon.writeToParcel(parcel, 0);
2021 } else {
2022 parcel.writeInt(0);
2023 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002024 parcel.writeInt(number);
2025 if (contentIntent != null) {
2026 parcel.writeInt(1);
2027 contentIntent.writeToParcel(parcel, 0);
2028 } else {
2029 parcel.writeInt(0);
2030 }
2031 if (deleteIntent != null) {
2032 parcel.writeInt(1);
2033 deleteIntent.writeToParcel(parcel, 0);
2034 } else {
2035 parcel.writeInt(0);
2036 }
2037 if (tickerText != null) {
2038 parcel.writeInt(1);
2039 TextUtils.writeToParcel(tickerText, parcel, flags);
2040 } else {
2041 parcel.writeInt(0);
2042 }
Joe Onorato46439ce2010-11-19 13:56:21 -08002043 if (tickerView != null) {
Joe Onoratoef1e7762010-09-17 18:38:38 -04002044 parcel.writeInt(1);
Joe Onorato46439ce2010-11-19 13:56:21 -08002045 tickerView.writeToParcel(parcel, 0);
Joe Onoratoef1e7762010-09-17 18:38:38 -04002046 } else {
2047 parcel.writeInt(0);
2048 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002049 if (contentView != null) {
2050 parcel.writeInt(1);
2051 contentView.writeToParcel(parcel, 0);
2052 } else {
2053 parcel.writeInt(0);
2054 }
Selim Cinek279fa862016-06-14 10:57:25 -07002055 if (mLargeIcon == null && largeIcon != null) {
2056 // you snuck an icon in here without using the builder; let's try to keep it
2057 mLargeIcon = Icon.createWithBitmap(largeIcon);
2058 }
Dan Sandlerd63f9322015-05-06 15:18:49 -04002059 if (mLargeIcon != null) {
Joe Onorato561d3852010-11-20 18:09:34 -08002060 parcel.writeInt(1);
Dan Sandlerd63f9322015-05-06 15:18:49 -04002061 mLargeIcon.writeToParcel(parcel, 0);
Joe Onorato561d3852010-11-20 18:09:34 -08002062 } else {
2063 parcel.writeInt(0);
2064 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002065
2066 parcel.writeInt(defaults);
2067 parcel.writeInt(this.flags);
2068
2069 if (sound != null) {
2070 parcel.writeInt(1);
2071 sound.writeToParcel(parcel, 0);
2072 } else {
2073 parcel.writeInt(0);
2074 }
2075 parcel.writeInt(audioStreamType);
John Spurlockc0650f022014-07-19 13:22:39 -04002076
2077 if (audioAttributes != null) {
2078 parcel.writeInt(1);
2079 audioAttributes.writeToParcel(parcel, 0);
2080 } else {
2081 parcel.writeInt(0);
2082 }
2083
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002084 parcel.writeLongArray(vibrate);
2085 parcel.writeInt(ledARGB);
2086 parcel.writeInt(ledOnMS);
2087 parcel.writeInt(ledOffMS);
2088 parcel.writeInt(iconLevel);
Daniel Sandlere46cbd32010-06-17 10:35:26 -04002089
2090 if (fullScreenIntent != null) {
2091 parcel.writeInt(1);
2092 fullScreenIntent.writeToParcel(parcel, 0);
2093 } else {
2094 parcel.writeInt(0);
2095 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002096
2097 parcel.writeInt(priority);
Joe Malin8d40d042012-11-05 11:36:40 -08002098
John Spurlockfd7f1e02014-03-18 16:41:57 -04002099 parcel.writeString(category);
Joe Malin8d40d042012-11-05 11:36:40 -08002100
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002101 parcel.writeString(mGroupKey);
2102
2103 parcel.writeString(mSortKey);
2104
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002105 parcel.writeBundle(extras); // null ok
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002106
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002107 parcel.writeTypedArray(actions, 0); // null ok
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002108
Daniel Sandlerf3b73432012-03-27 15:01:25 -04002109 if (bigContentView != null) {
2110 parcel.writeInt(1);
2111 bigContentView.writeToParcel(parcel, 0);
2112 } else {
2113 parcel.writeInt(0);
2114 }
Dan Sandler0bf2ed82013-12-21 23:33:41 -06002115
Chris Wren8fd39ec2014-02-27 17:43:26 -05002116 if (headsUpContentView != null) {
2117 parcel.writeInt(1);
2118 headsUpContentView.writeToParcel(parcel, 0);
2119 } else {
2120 parcel.writeInt(0);
2121 }
2122
Dan Sandler0bf2ed82013-12-21 23:33:41 -06002123 parcel.writeInt(visibility);
2124
2125 if (publicVersion != null) {
2126 parcel.writeInt(1);
2127 publicVersion.writeToParcel(parcel, 0);
2128 } else {
2129 parcel.writeInt(0);
2130 }
Dan Sandler26e81cf2014-05-06 10:01:27 -04002131
2132 parcel.writeInt(color);
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04002133
2134 if (mChannelId != null) {
2135 parcel.writeInt(1);
2136 parcel.writeString(mChannelId);
2137 } else {
2138 parcel.writeInt(0);
2139 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002140 }
2141
2142 /**
2143 * Parcelable.Creator that instantiates Notification objects
2144 */
2145 public static final Parcelable.Creator<Notification> CREATOR
2146 = new Parcelable.Creator<Notification>()
2147 {
2148 public Notification createFromParcel(Parcel parcel)
2149 {
2150 return new Notification(parcel);
2151 }
2152
2153 public Notification[] newArray(int size)
2154 {
2155 return new Notification[size];
2156 }
2157 };
2158
2159 /**
2160 * Sets the {@link #contentView} field to be a view with the standard "Latest Event"
2161 * layout.
2162 *
2163 * <p>Uses the {@link #icon} and {@link #when} fields to set the icon and time fields
2164 * in the view.</p>
2165 * @param context The context for your application / activity.
2166 * @param contentTitle The title that goes in the expanded entry.
2167 * @param contentText The text that goes in the expanded entry.
2168 * @param contentIntent The intent to launch when the user clicks the expanded notification.
2169 * If this is an activity, it must include the
2170 * {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} flag, which requires
Scott Main7aee61f2011-02-08 11:25:01 -08002171 * that you take care of task management as described in the
2172 * <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back
2173 * Stack</a> document.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002174 *
Joe Onorato46439ce2010-11-19 13:56:21 -08002175 * @deprecated Use {@link Builder} instead.
Chris Wrena05db382015-06-24 15:18:34 -04002176 * @removed
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002177 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002178 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002179 public void setLatestEventInfo(Context context,
2180 CharSequence contentTitle, CharSequence contentText, PendingIntent contentIntent) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002181 if (context.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1){
2182 Log.e(TAG, "setLatestEventInfo() is deprecated and you should feel deprecated.",
2183 new Throwable());
2184 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002185
Selim Cinek4ac6f602016-06-13 15:47:03 -07002186 if (context.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N) {
2187 extras.putBoolean(EXTRA_SHOW_WHEN, true);
2188 }
2189
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002190 // ensure that any information already set directly is preserved
2191 final Notification.Builder builder = new Notification.Builder(context, this);
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002192
2193 // now apply the latestEventInfo fields
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002194 if (contentTitle != null) {
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002195 builder.setContentTitle(contentTitle);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002196 }
2197 if (contentText != null) {
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002198 builder.setContentText(contentText);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002199 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002200 builder.setContentIntent(contentIntent);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002201
2202 builder.build(); // callers expect this notification to be ready to use
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002203 }
2204
Julia Reynoldsda303542015-11-23 14:00:20 -05002205 /**
2206 * @hide
2207 */
2208 public static void addFieldsFromContext(Context context, Notification notification) {
Jeff Sharkey012bc7b2016-04-11 16:30:27 -06002209 addFieldsFromContext(context.getApplicationInfo(), context.getUserId(), notification);
2210 }
2211
2212 /**
2213 * @hide
2214 */
2215 public static void addFieldsFromContext(ApplicationInfo ai, int userId,
2216 Notification notification) {
2217 notification.extras.putParcelable(EXTRA_BUILDER_APPLICATION_INFO, ai);
2218 notification.extras.putInt(EXTRA_ORIGINATING_USERID, userId);
Julia Reynoldsda303542015-11-23 14:00:20 -05002219 }
2220
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002221 @Override
2222 public String toString() {
2223 StringBuilder sb = new StringBuilder();
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002224 sb.append("Notification(pri=");
2225 sb.append(priority);
2226 sb.append(" contentView=");
Joe Onoratoc9596d62011-01-12 17:03:11 -08002227 if (contentView != null) {
2228 sb.append(contentView.getPackage());
2229 sb.append("/0x");
2230 sb.append(Integer.toHexString(contentView.getLayoutId()));
2231 } else {
2232 sb.append("null");
2233 }
2234 sb.append(" vibrate=");
Daniel Sandler6738eee2012-11-16 12:03:32 -05002235 if ((this.defaults & DEFAULT_VIBRATE) != 0) {
2236 sb.append("default");
2237 } else if (this.vibrate != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002238 int N = this.vibrate.length-1;
2239 sb.append("[");
2240 for (int i=0; i<N; i++) {
2241 sb.append(this.vibrate[i]);
2242 sb.append(',');
2243 }
Simon Schoar8cf97d92009-06-10 22:08:37 +02002244 if (N != -1) {
2245 sb.append(this.vibrate[N]);
2246 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002247 sb.append("]");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002248 } else {
2249 sb.append("null");
2250 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002251 sb.append(" sound=");
Daniel Sandler6738eee2012-11-16 12:03:32 -05002252 if ((this.defaults & DEFAULT_SOUND) != 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002253 sb.append("default");
Daniel Sandler6738eee2012-11-16 12:03:32 -05002254 } else if (this.sound != null) {
2255 sb.append(this.sound.toString());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002256 } else {
2257 sb.append("null");
2258 }
Chris Wren365b6d32015-07-16 10:39:26 -04002259 if (this.tickerText != null) {
2260 sb.append(" tick");
2261 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002262 sb.append(" defaults=0x");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002263 sb.append(Integer.toHexString(this.defaults));
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002264 sb.append(" flags=0x");
Daniel Sandlere46cbd32010-06-17 10:35:26 -04002265 sb.append(Integer.toHexString(this.flags));
Dan Sandler26e81cf2014-05-06 10:01:27 -04002266 sb.append(String.format(" color=0x%08x", this.color));
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002267 if (this.category != null) {
2268 sb.append(" category=");
2269 sb.append(this.category);
2270 }
2271 if (this.mGroupKey != null) {
2272 sb.append(" groupKey=");
2273 sb.append(this.mGroupKey);
2274 }
2275 if (this.mSortKey != null) {
2276 sb.append(" sortKey=");
2277 sb.append(this.mSortKey);
2278 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002279 if (actions != null) {
Dan Sandler1b718782014-07-18 12:43:45 -04002280 sb.append(" actions=");
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002281 sb.append(actions.length);
Dan Sandler1b718782014-07-18 12:43:45 -04002282 }
2283 sb.append(" vis=");
2284 sb.append(visibilityToString(this.visibility));
2285 if (this.publicVersion != null) {
2286 sb.append(" publicVersion=");
2287 sb.append(publicVersion.toString());
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002288 }
2289 sb.append(")");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002290 return sb.toString();
2291 }
Joe Onorato46439ce2010-11-19 13:56:21 -08002292
Dan Sandler1b718782014-07-18 12:43:45 -04002293 /**
2294 * {@hide}
2295 */
2296 public static String visibilityToString(int vis) {
2297 switch (vis) {
2298 case VISIBILITY_PRIVATE:
2299 return "PRIVATE";
2300 case VISIBILITY_PUBLIC:
2301 return "PUBLIC";
2302 case VISIBILITY_SECRET:
2303 return "SECRET";
2304 default:
2305 return "UNKNOWN(" + String.valueOf(vis) + ")";
2306 }
2307 }
2308
Joe Onoratocb109a02011-01-18 17:57:41 -08002309 /**
John Spurlock1d881a12015-03-18 19:21:54 -04002310 * {@hide}
2311 */
2312 public static String priorityToString(@Priority int pri) {
2313 switch (pri) {
2314 case PRIORITY_MIN:
2315 return "MIN";
2316 case PRIORITY_LOW:
2317 return "LOW";
2318 case PRIORITY_DEFAULT:
2319 return "DEFAULT";
2320 case PRIORITY_HIGH:
2321 return "HIGH";
2322 case PRIORITY_MAX:
2323 return "MAX";
2324 default:
2325 return "UNKNOWN(" + String.valueOf(pri) + ")";
2326 }
2327 }
2328
2329 /**
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04002330 * Returns the id of the channel this notification posts to.
2331 */
Julia Reynolds37856052016-11-11 09:20:07 -05002332 public String getChannel() {
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04002333 return mChannelId;
2334 }
2335
2336 /**
Dan Sandlerd63f9322015-05-06 15:18:49 -04002337 * The small icon representing this notification in the status bar and content view.
2338 *
2339 * @return the small icon representing this notification.
2340 *
2341 * @see Builder#getSmallIcon()
2342 * @see Builder#setSmallIcon(Icon)
2343 */
2344 public Icon getSmallIcon() {
2345 return mSmallIcon;
2346 }
2347
2348 /**
2349 * Used when notifying to clean up legacy small icons.
2350 * @hide
2351 */
2352 public void setSmallIcon(Icon icon) {
2353 mSmallIcon = icon;
2354 }
2355
2356 /**
2357 * The large icon shown in this notification's content view.
2358 * @see Builder#getLargeIcon()
2359 * @see Builder#setLargeIcon(Icon)
2360 */
2361 public Icon getLargeIcon() {
2362 return mLargeIcon;
2363 }
2364
2365 /**
Christoph Studer4600f9b2014-07-22 22:44:43 +02002366 * @hide
2367 */
Christoph Studerc8db24b2014-07-25 17:50:30 +02002368 public boolean isGroupSummary() {
2369 return mGroupKey != null && (flags & FLAG_GROUP_SUMMARY) != 0;
2370 }
2371
2372 /**
2373 * @hide
2374 */
2375 public boolean isGroupChild() {
2376 return mGroupKey != null && (flags & FLAG_GROUP_SUMMARY) == 0;
2377 }
2378
2379 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002380 * Builder class for {@link Notification} objects.
Joe Malin8d40d042012-11-05 11:36:40 -08002381 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002382 * Provides a convenient way to set the various fields of a {@link Notification} and generate
Scott Main183bf112012-08-13 19:12:13 -07002383 * content views using the platform's notification layout template. If your app supports
2384 * versions of Android as old as API level 4, you can instead use
2385 * {@link android.support.v4.app.NotificationCompat.Builder NotificationCompat.Builder},
2386 * available in the <a href="{@docRoot}tools/extras/support-library.html">Android Support
2387 * library</a>.
Joe Malin8d40d042012-11-05 11:36:40 -08002388 *
Scott Main183bf112012-08-13 19:12:13 -07002389 * <p>Example:
Joe Malin8d40d042012-11-05 11:36:40 -08002390 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002391 * <pre class="prettyprint">
Scott Main183bf112012-08-13 19:12:13 -07002392 * Notification noti = new Notification.Builder(mContext)
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002393 * .setContentTitle(&quot;New mail from &quot; + sender.toString())
2394 * .setContentText(subject)
2395 * .setSmallIcon(R.drawable.new_mail)
2396 * .setLargeIcon(aBitmap)
Chris Wrenfbd96ba2012-05-01 12:03:58 -04002397 * .build();
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002398 * </pre>
Joe Onoratocb109a02011-01-18 17:57:41 -08002399 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002400 public static class Builder {
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05002401 /**
2402 * @hide
2403 */
2404 public static final String EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT =
2405 "android.rebuild.contentViewActionCount";
2406 /**
2407 * @hide
2408 */
2409 public static final String EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT
2410 = "android.rebuild.bigViewActionCount";
2411 /**
2412 * @hide
2413 */
2414 public static final String EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT
2415 = "android.rebuild.hudViewActionCount";
2416
Daniel Sandler602ad1c2012-06-12 16:06:27 -04002417 private static final int MAX_ACTION_BUTTONS = 3;
Daniel Sandler8680bf82012-05-15 16:52:52 -04002418
Joe Onorato46439ce2010-11-19 13:56:21 -08002419 private Context mContext;
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002420 private Notification mN;
2421 private Bundle mUserExtras = new Bundle();
Chris Wrenfbd96ba2012-05-01 12:03:58 -04002422 private Style mStyle;
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002423 private ArrayList<Action> mActions = new ArrayList<Action>(MAX_ACTION_BUTTONS);
2424 private ArrayList<String> mPersonList = new ArrayList<String>();
2425 private NotificationColorUtil mColorUtil;
2426 private boolean mColorUtilInited = false;
Christoph Studer7ac80e62014-08-04 16:01:57 +02002427
2428 /**
Adrian Roos4ff3b122016-02-01 12:26:13 -08002429 * Caches a contrast-enhanced version of {@link #mCachedContrastColorIsFor}.
2430 */
2431 private int mCachedContrastColor = COLOR_INVALID;
2432 private int mCachedContrastColorIsFor = COLOR_INVALID;
Adrian Roos487374f2017-01-11 15:48:14 -08002433 /**
2434 * Caches a ambient version of {@link #mCachedContrastColorIsFor}.
2435 */
2436 private int mCachedAmbientColor = COLOR_INVALID;
2437 private int mCachedAmbientColorIsFor = COLOR_INVALID;
Adrian Roos4ff3b122016-02-01 12:26:13 -08002438
2439 /**
Adrian Roos70d7aa32017-01-11 15:39:06 -08002440 * Caches an instance of StandardTemplateParams. Note that this may have been used before,
2441 * so make sure to call {@link StandardTemplateParams#reset()} before using it.
2442 */
2443 StandardTemplateParams mParams = new StandardTemplateParams();
2444
2445 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002446 * Constructs a new Builder with the defaults:
Joe Onoratocb109a02011-01-18 17:57:41 -08002447 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002448
2449 * <table>
2450 * <tr><th align=right>priority</th>
2451 * <td>{@link #PRIORITY_DEFAULT}</td></tr>
2452 * <tr><th align=right>when</th>
2453 * <td>now ({@link System#currentTimeMillis()})</td></tr>
2454 * <tr><th align=right>audio stream</th>
2455 * <td>{@link #STREAM_DEFAULT}</td></tr>
2456 * </table>
Joe Onoratocb109a02011-01-18 17:57:41 -08002457 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002458
2459 * @param context
2460 * A {@link Context} that will be used by the Builder to construct the
2461 * RemoteViews. The Context will not be held past the lifetime of this Builder
2462 * object.
Joe Onoratocb109a02011-01-18 17:57:41 -08002463 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002464 public Builder(Context context) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002465 this(context, null);
Joe Onorato46439ce2010-11-19 13:56:21 -08002466 }
2467
Joe Onoratocb109a02011-01-18 17:57:41 -08002468 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002469 * @hide
Christoph Studer4600f9b2014-07-22 22:44:43 +02002470 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002471 public Builder(Context context, Notification toAdopt) {
2472 mContext = context;
Christoph Studer4600f9b2014-07-22 22:44:43 +02002473
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002474 if (toAdopt == null) {
2475 mN = new Notification();
Selim Cinek0ff1ce602016-04-05 18:27:16 -07002476 if (context.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N) {
2477 mN.extras.putBoolean(EXTRA_SHOW_WHEN, true);
2478 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002479 mN.priority = PRIORITY_DEFAULT;
2480 mN.visibility = VISIBILITY_PRIVATE;
2481 } else {
2482 mN = toAdopt;
2483 if (mN.actions != null) {
2484 Collections.addAll(mActions, mN.actions);
Christoph Studer4600f9b2014-07-22 22:44:43 +02002485 }
2486
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002487 if (mN.extras.containsKey(EXTRA_PEOPLE)) {
2488 Collections.addAll(mPersonList, mN.extras.getStringArray(EXTRA_PEOPLE));
2489 }
2490
Selim Cinek4ac6f602016-06-13 15:47:03 -07002491 if (mN.getSmallIcon() == null && mN.icon != 0) {
2492 setSmallIcon(mN.icon);
2493 }
2494
2495 if (mN.getLargeIcon() == null && mN.largeIcon != null) {
2496 setLargeIcon(mN.largeIcon);
2497 }
2498
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002499 String templateClass = mN.extras.getString(EXTRA_TEMPLATE);
2500 if (!TextUtils.isEmpty(templateClass)) {
2501 final Class<? extends Style> styleClass
2502 = getNotificationStyleClass(templateClass);
2503 if (styleClass == null) {
2504 Log.d(TAG, "Unknown style class: " + templateClass);
2505 } else {
2506 try {
Adrian Roosc1a80b02016-04-05 14:54:55 -07002507 final Constructor<? extends Style> ctor =
2508 styleClass.getDeclaredConstructor();
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002509 ctor.setAccessible(true);
2510 final Style style = ctor.newInstance();
2511 style.restoreFromExtras(mN.extras);
2512
2513 if (style != null) {
2514 setStyle(style);
2515 }
2516 } catch (Throwable t) {
2517 Log.e(TAG, "Could not create Style", t);
2518 }
2519 }
2520 }
2521
2522 }
2523 }
2524
2525 private NotificationColorUtil getColorUtil() {
2526 if (!mColorUtilInited) {
2527 mColorUtilInited = true;
2528 if (mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.LOLLIPOP) {
2529 mColorUtil = NotificationColorUtil.getInstance(mContext);
Christoph Studer4600f9b2014-07-22 22:44:43 +02002530 }
2531 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002532 return mColorUtil;
Christoph Studer4600f9b2014-07-22 22:44:43 +02002533 }
2534
2535 /**
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04002536 * Specifies the channel the notification should be delivered on.
2537 */
2538 public Builder setChannel(String channelId) {
2539 mN.mChannelId = channelId;
2540 return this;
2541 }
2542
2543 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002544 * Add a timestamp pertaining to the notification (usually the time the event occurred).
Selim Cinek0ff1ce602016-04-05 18:27:16 -07002545 *
2546 * For apps targeting {@link android.os.Build.VERSION_CODES#N} and above, this time is not
2547 * shown anymore by default and must be opted into by using
2548 * {@link android.app.Notification.Builder#setShowWhen(boolean)}
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002549 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002550 * @see Notification#when
Joe Onoratocb109a02011-01-18 17:57:41 -08002551 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002552 public Builder setWhen(long when) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002553 mN.when = when;
Joe Onorato46439ce2010-11-19 13:56:21 -08002554 return this;
2555 }
2556
Joe Onoratocb109a02011-01-18 17:57:41 -08002557 /**
Griff Hazen50c11652014-05-16 09:46:31 -07002558 * Control whether the timestamp set with {@link #setWhen(long) setWhen} is shown
Daniel Sandler0c890492012-09-12 17:23:10 -07002559 * in the content view.
Selim Cinek0ff1ce602016-04-05 18:27:16 -07002560 * For apps targeting {@link android.os.Build.VERSION_CODES#N} and above, this defaults to
2561 * {@code false}. For earlier apps, the default is {@code true}.
Daniel Sandler0c890492012-09-12 17:23:10 -07002562 */
2563 public Builder setShowWhen(boolean show) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002564 mN.extras.putBoolean(EXTRA_SHOW_WHEN, show);
Daniel Sandler0c890492012-09-12 17:23:10 -07002565 return this;
2566 }
2567
2568 /**
Daniel Sandlerd33b8032012-05-10 11:41:48 -04002569 * Show the {@link Notification#when} field as a stopwatch.
Joe Malin8d40d042012-11-05 11:36:40 -08002570 *
2571 * Instead of presenting <code>when</code> as a timestamp, the notification will show an
Daniel Sandlerd33b8032012-05-10 11:41:48 -04002572 * automatically updating display of the minutes and seconds since <code>when</code>.
Daniel Sandlera2985ed2012-04-03 16:42:00 -04002573 *
Daniel Sandlerd33b8032012-05-10 11:41:48 -04002574 * Useful when showing an elapsed time (like an ongoing phone call).
2575 *
Selim Cinek81c23aa2016-02-25 16:23:13 -08002576 * The counter can also be set to count down to <code>when</code> when using
Adrian Roos96b7e202016-05-17 13:50:38 -07002577 * {@link #setChronometerCountDown(boolean)}.
Selim Cinek81c23aa2016-02-25 16:23:13 -08002578 *
Daniel Sandlerd33b8032012-05-10 11:41:48 -04002579 * @see android.widget.Chronometer
Daniel Sandlera2985ed2012-04-03 16:42:00 -04002580 * @see Notification#when
Adrian Roos96b7e202016-05-17 13:50:38 -07002581 * @see #setChronometerCountDown(boolean)
Daniel Sandlera2985ed2012-04-03 16:42:00 -04002582 */
2583 public Builder setUsesChronometer(boolean b) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002584 mN.extras.putBoolean(EXTRA_SHOW_CHRONOMETER, b);
Daniel Sandlera2985ed2012-04-03 16:42:00 -04002585 return this;
2586 }
2587
2588 /**
Selim Cinek81c23aa2016-02-25 16:23:13 -08002589 * Sets the Chronometer to count down instead of counting up.
2590 *
2591 * <p>This is only relevant if {@link #setUsesChronometer(boolean)} has been set to true.
2592 * If it isn't set the chronometer will count up.
2593 *
2594 * @see #setUsesChronometer(boolean)
2595 */
Adrian Roos96b7e202016-05-17 13:50:38 -07002596 public Builder setChronometerCountDown(boolean countDown) {
2597 mN.extras.putBoolean(EXTRA_CHRONOMETER_COUNT_DOWN, countDown);
Selim Cinek81c23aa2016-02-25 16:23:13 -08002598 return this;
2599 }
2600
2601 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002602 * Set the small icon resource, which will be used to represent the notification in the
2603 * status bar.
Joe Onoratocb109a02011-01-18 17:57:41 -08002604 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002605
2606 * The platform template for the expanded view will draw this icon in the left, unless a
2607 * {@link #setLargeIcon(Bitmap) large icon} has also been specified, in which case the small
2608 * icon will be moved to the right-hand side.
2609 *
2610
2611 * @param icon
2612 * A resource ID in the application's package of the drawable to use.
2613 * @see Notification#icon
Joe Onoratocb109a02011-01-18 17:57:41 -08002614 */
Tor Norbye7b9c9122013-05-30 16:48:33 -07002615 public Builder setSmallIcon(@DrawableRes int icon) {
Dan Sandlerd63f9322015-05-06 15:18:49 -04002616 return setSmallIcon(icon != 0
2617 ? Icon.createWithResource(mContext, icon)
2618 : null);
Joe Onorato46439ce2010-11-19 13:56:21 -08002619 }
2620
Joe Onoratocb109a02011-01-18 17:57:41 -08002621 /**
2622 * A variant of {@link #setSmallIcon(int) setSmallIcon(int)} that takes an additional
2623 * level parameter for when the icon is a {@link android.graphics.drawable.LevelListDrawable
2624 * LevelListDrawable}.
2625 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002626 * @param icon A resource ID in the application's package of the drawable to use.
Joe Onoratocb109a02011-01-18 17:57:41 -08002627 * @param level The level to use for the icon.
2628 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002629 * @see Notification#icon
2630 * @see Notification#iconLevel
Joe Onoratocb109a02011-01-18 17:57:41 -08002631 */
Tor Norbye7b9c9122013-05-30 16:48:33 -07002632 public Builder setSmallIcon(@DrawableRes int icon, int level) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002633 mN.iconLevel = level;
Dan Sandlerd63f9322015-05-06 15:18:49 -04002634 return setSmallIcon(icon);
2635 }
2636
2637 /**
2638 * Set the small icon, which will be used to represent the notification in the
2639 * status bar and content view (unless overriden there by a
2640 * {@link #setLargeIcon(Bitmap) large icon}).
2641 *
2642 * @param icon An Icon object to use.
2643 * @see Notification#icon
2644 */
2645 public Builder setSmallIcon(Icon icon) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002646 mN.setSmallIcon(icon);
2647 if (icon != null && icon.getType() == Icon.TYPE_RESOURCE) {
2648 mN.icon = icon.getResId();
2649 }
Joe Onorato46439ce2010-11-19 13:56:21 -08002650 return this;
2651 }
2652
Joe Onoratocb109a02011-01-18 17:57:41 -08002653 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002654 * Set the first line of text in the platform notification template.
Joe Onoratocb109a02011-01-18 17:57:41 -08002655 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002656 public Builder setContentTitle(CharSequence title) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002657 mN.extras.putCharSequence(EXTRA_TITLE, safeCharSequence(title));
Joe Onorato46439ce2010-11-19 13:56:21 -08002658 return this;
2659 }
2660
Joe Onoratocb109a02011-01-18 17:57:41 -08002661 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002662 * Set the second line of text in the platform notification template.
Joe Onoratocb109a02011-01-18 17:57:41 -08002663 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002664 public Builder setContentText(CharSequence text) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002665 mN.extras.putCharSequence(EXTRA_TEXT, safeCharSequence(text));
Joe Onorato46439ce2010-11-19 13:56:21 -08002666 return this;
2667 }
2668
Joe Onoratocb109a02011-01-18 17:57:41 -08002669 /**
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07002670 * This provides some additional information that is displayed in the notification. No
2671 * guarantees are given where exactly it is displayed.
2672 *
2673 * <p>This information should only be provided if it provides an essential
2674 * benefit to the understanding of the notification. The more text you provide the
2675 * less readable it becomes. For example, an email client should only provide the account
2676 * name here if more than one email account has been added.</p>
2677 *
2678 * <p>As of {@link android.os.Build.VERSION_CODES#N} this information is displayed in the
2679 * notification header area.
2680 *
2681 * On Android versions before {@link android.os.Build.VERSION_CODES#N}
2682 * this will be shown in the third line of text in the platform notification template.
2683 * You should not be using {@link #setProgress(int, int, boolean)} at the
2684 * same time on those versions; they occupy the same place.
2685 * </p>
Daniel Sandlerf3b73432012-03-27 15:01:25 -04002686 */
2687 public Builder setSubText(CharSequence text) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002688 mN.extras.putCharSequence(EXTRA_SUB_TEXT, safeCharSequence(text));
Daniel Sandlerf3b73432012-03-27 15:01:25 -04002689 return this;
2690 }
2691
2692 /**
Adrian Roose458aa82015-12-08 16:17:19 -08002693 * Set the remote input history.
2694 *
2695 * This should be set to the most recent inputs that have been sent
2696 * through a {@link RemoteInput} of this Notification and cleared once the it is no
2697 * longer relevant (e.g. for chat notifications once the other party has responded).
2698 *
2699 * The most recent input must be stored at the 0 index, the second most recent at the
2700 * 1 index, etc. Note that the system will limit both how far back the inputs will be shown
2701 * and how much of each individual input is shown.
2702 *
2703 * <p>Note: The reply text will only be shown on notifications that have least one action
2704 * with a {@code RemoteInput}.</p>
2705 */
2706 public Builder setRemoteInputHistory(CharSequence[] text) {
2707 if (text == null) {
2708 mN.extras.putCharSequenceArray(EXTRA_REMOTE_INPUT_HISTORY, null);
2709 } else {
2710 final int N = Math.min(MAX_REPLY_HISTORY, text.length);
2711 CharSequence[] safe = new CharSequence[N];
2712 for (int i = 0; i < N; i++) {
2713 safe[i] = safeCharSequence(text[i]);
2714 }
2715 mN.extras.putCharSequenceArray(EXTRA_REMOTE_INPUT_HISTORY, safe);
2716 }
2717 return this;
2718 }
2719
2720 /**
Joe Onoratocb109a02011-01-18 17:57:41 -08002721 * Set the large number at the right-hand side of the notification. This is
2722 * equivalent to setContentInfo, although it might show the number in a different
2723 * font size for readability.
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07002724 *
2725 * @deprecated this number is not shown anywhere anymore
Joe Onoratocb109a02011-01-18 17:57:41 -08002726 */
Aurimas Liutikas514c5ef2016-05-24 15:22:55 -07002727 @Deprecated
Joe Onorato8595a3d2010-11-19 18:12:07 -08002728 public Builder setNumber(int number) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002729 mN.number = number;
Joe Onorato8595a3d2010-11-19 18:12:07 -08002730 return this;
2731 }
2732
Joe Onoratocb109a02011-01-18 17:57:41 -08002733 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002734 * A small piece of additional information pertaining to this notification.
2735 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002736 * The platform template will draw this on the last line of the notification, at the far
2737 * right (to the right of a smallIcon if it has been placed there).
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07002738 *
2739 * @deprecated use {@link #setSubText(CharSequence)} instead to set a text in the header.
2740 * For legacy apps targeting a version below {@link android.os.Build.VERSION_CODES#N} this
2741 * field will still show up, but the subtext will take precedence.
Joe Onoratocb109a02011-01-18 17:57:41 -08002742 */
Aurimas Liutikas514c5ef2016-05-24 15:22:55 -07002743 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08002744 public Builder setContentInfo(CharSequence info) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002745 mN.extras.putCharSequence(EXTRA_INFO_TEXT, safeCharSequence(info));
Joe Onorato46439ce2010-11-19 13:56:21 -08002746 return this;
2747 }
2748
Joe Onoratocb109a02011-01-18 17:57:41 -08002749 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002750 * Set the progress this notification represents.
2751 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002752 * The platform template will represent this using a {@link ProgressBar}.
Jeff Sharkey1c400132011-08-05 14:50:13 -07002753 */
2754 public Builder setProgress(int max, int progress, boolean indeterminate) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002755 mN.extras.putInt(EXTRA_PROGRESS, progress);
2756 mN.extras.putInt(EXTRA_PROGRESS_MAX, max);
2757 mN.extras.putBoolean(EXTRA_PROGRESS_INDETERMINATE, indeterminate);
Jeff Sharkey1c400132011-08-05 14:50:13 -07002758 return this;
2759 }
2760
2761 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002762 * Supply a custom RemoteViews to use instead of the platform template.
2763 *
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002764 * Use {@link #setCustomContentView(RemoteViews)} instead.
Joe Onoratocb109a02011-01-18 17:57:41 -08002765 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002766 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08002767 public Builder setContent(RemoteViews views) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002768 return setCustomContentView(views);
2769 }
2770
2771 /**
2772 * Supply custom RemoteViews to use instead of the platform template.
2773 *
2774 * This will override the layout that would otherwise be constructed by this Builder
2775 * object.
2776 */
2777 public Builder setCustomContentView(RemoteViews contentView) {
2778 mN.contentView = contentView;
2779 return this;
2780 }
2781
2782 /**
2783 * Supply custom RemoteViews to use instead of the platform template in the expanded form.
2784 *
2785 * This will override the expanded layout that would otherwise be constructed by this
2786 * Builder object.
2787 */
2788 public Builder setCustomBigContentView(RemoteViews contentView) {
2789 mN.bigContentView = contentView;
2790 return this;
2791 }
2792
2793 /**
2794 * Supply custom RemoteViews to use instead of the platform template in the heads up dialog.
2795 *
2796 * This will override the heads-up layout that would otherwise be constructed by this
2797 * Builder object.
2798 */
2799 public Builder setCustomHeadsUpContentView(RemoteViews contentView) {
2800 mN.headsUpContentView = contentView;
Joe Onorato46439ce2010-11-19 13:56:21 -08002801 return this;
2802 }
2803
Joe Onoratocb109a02011-01-18 17:57:41 -08002804 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002805 * Supply a {@link PendingIntent} to be sent when the notification is clicked.
2806 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002807 * As of {@link android.os.Build.VERSION_CODES#HONEYCOMB}, if this field is unset and you
2808 * have specified a custom RemoteViews with {@link #setContent(RemoteViews)}, you can use
2809 * {@link RemoteViews#setOnClickPendingIntent RemoteViews.setOnClickPendingIntent(int,PendingIntent)}
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002810 * to assign PendingIntents to individual views in that custom layout (i.e., to create
Daniel Sandlerf3b73432012-03-27 15:01:25 -04002811 * clickable buttons inside the notification view).
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002812 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002813 * @see Notification#contentIntent Notification.contentIntent
Joe Onoratocb109a02011-01-18 17:57:41 -08002814 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002815 public Builder setContentIntent(PendingIntent intent) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002816 mN.contentIntent = intent;
Joe Onorato46439ce2010-11-19 13:56:21 -08002817 return this;
2818 }
2819
Joe Onoratocb109a02011-01-18 17:57:41 -08002820 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002821 * Supply a {@link PendingIntent} to send when the notification is cleared explicitly by the user.
2822 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002823 * @see Notification#deleteIntent
Joe Onoratocb109a02011-01-18 17:57:41 -08002824 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002825 public Builder setDeleteIntent(PendingIntent intent) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002826 mN.deleteIntent = intent;
Joe Onorato46439ce2010-11-19 13:56:21 -08002827 return this;
2828 }
2829
Joe Onoratocb109a02011-01-18 17:57:41 -08002830 /**
2831 * An intent to launch instead of posting the notification to the status bar.
2832 * Only for use with extremely high-priority notifications demanding the user's
2833 * <strong>immediate</strong> attention, such as an incoming phone call or
2834 * alarm clock that the user has explicitly set to a particular time.
2835 * If this facility is used for something else, please give the user an option
2836 * to turn it off and use a normal notification, as this can be extremely
2837 * disruptive.
2838 *
Chris Wren47c20a12014-06-18 17:27:29 -04002839 * <p>
2840 * The system UI may choose to display a heads-up notification, instead of
2841 * launching this intent, while the user is using the device.
2842 * </p>
2843 *
Joe Onoratocb109a02011-01-18 17:57:41 -08002844 * @param intent The pending intent to launch.
2845 * @param highPriority Passing true will cause this notification to be sent
2846 * even if other notifications are suppressed.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002847 *
2848 * @see Notification#fullScreenIntent
Joe Onoratocb109a02011-01-18 17:57:41 -08002849 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002850 public Builder setFullScreenIntent(PendingIntent intent, boolean highPriority) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002851 mN.fullScreenIntent = intent;
Joe Onorato46439ce2010-11-19 13:56:21 -08002852 setFlag(FLAG_HIGH_PRIORITY, highPriority);
2853 return this;
2854 }
2855
Joe Onoratocb109a02011-01-18 17:57:41 -08002856 /**
Dan Sandler5fcdf6e2014-07-18 11:31:15 -04002857 * Set the "ticker" text which is sent to accessibility services.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002858 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002859 * @see Notification#tickerText
Joe Onoratocb109a02011-01-18 17:57:41 -08002860 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002861 public Builder setTicker(CharSequence tickerText) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002862 mN.tickerText = safeCharSequence(tickerText);
Joe Onorato46439ce2010-11-19 13:56:21 -08002863 return this;
2864 }
2865
Joe Onoratocb109a02011-01-18 17:57:41 -08002866 /**
Dan Sandler5fcdf6e2014-07-18 11:31:15 -04002867 * Obsolete version of {@link #setTicker(CharSequence)}.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002868 *
Joe Onoratocb109a02011-01-18 17:57:41 -08002869 */
Dan Sandler5fcdf6e2014-07-18 11:31:15 -04002870 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08002871 public Builder setTicker(CharSequence tickerText, RemoteViews views) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002872 setTicker(tickerText);
2873 // views is ignored
Joe Onorato46439ce2010-11-19 13:56:21 -08002874 return this;
2875 }
2876
Joe Onoratocb109a02011-01-18 17:57:41 -08002877 /**
Dan Sandlerd63f9322015-05-06 15:18:49 -04002878 * Add a large icon to the notification content view.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002879 *
2880 * In the platform template, this image will be shown on the left of the notification view
Dan Sandlerd63f9322015-05-06 15:18:49 -04002881 * in place of the {@link #setSmallIcon(Icon) small icon} (which will be placed in a small
2882 * badge atop the large icon).
Dan Sandler08a04c12015-05-06 15:18:49 -04002883 */
Dan Sandlerd63f9322015-05-06 15:18:49 -04002884 public Builder setLargeIcon(Bitmap b) {
2885 return setLargeIcon(b != null ? Icon.createWithBitmap(b) : null);
2886 }
2887
2888 /**
2889 * Add a large icon to the notification content view.
2890 *
2891 * In the platform template, this image will be shown on the left of the notification view
2892 * in place of the {@link #setSmallIcon(Icon) small icon} (which will be placed in a small
2893 * badge atop the large icon).
2894 */
2895 public Builder setLargeIcon(Icon icon) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002896 mN.mLargeIcon = icon;
2897 mN.extras.putParcelable(EXTRA_LARGE_ICON, icon);
Joe Onorato46439ce2010-11-19 13:56:21 -08002898 return this;
2899 }
2900
Joe Onoratocb109a02011-01-18 17:57:41 -08002901 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002902 * Set the sound to play.
2903 *
John Spurlockc0650f022014-07-19 13:22:39 -04002904 * It will be played using the {@link #AUDIO_ATTRIBUTES_DEFAULT default audio attributes}
2905 * for notifications.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002906 *
2907 * @see Notification#sound
Joe Onoratocb109a02011-01-18 17:57:41 -08002908 */
Joe Onorato52f80cd2010-11-21 15:34:48 -08002909 public Builder setSound(Uri sound) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002910 mN.sound = sound;
2911 mN.audioAttributes = AUDIO_ATTRIBUTES_DEFAULT;
Joe Onorato52f80cd2010-11-21 15:34:48 -08002912 return this;
2913 }
2914
Joe Onoratocb109a02011-01-18 17:57:41 -08002915 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002916 * Set the sound to play, along with a specific stream on which to play it.
Joe Onoratocb109a02011-01-18 17:57:41 -08002917 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002918 * See {@link android.media.AudioManager} for the <code>STREAM_</code> constants.
2919 *
John Spurlockc0650f022014-07-19 13:22:39 -04002920 * @deprecated use {@link #setSound(Uri, AudioAttributes)} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002921 * @see Notification#sound
Joe Onoratocb109a02011-01-18 17:57:41 -08002922 */
Jean-Michel Trivi81f871e2014-08-06 16:32:38 -07002923 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08002924 public Builder setSound(Uri sound, int streamType) {
Jean-Michel Trivi2f7511f2016-11-28 15:40:27 -08002925 PlayerBase.deprecateStreamTypeForPlayback(streamType, "Notification", "setSound()");
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002926 mN.sound = sound;
2927 mN.audioStreamType = streamType;
Joe Onorato46439ce2010-11-19 13:56:21 -08002928 return this;
2929 }
2930
Joe Onoratocb109a02011-01-18 17:57:41 -08002931 /**
John Spurlockc0650f022014-07-19 13:22:39 -04002932 * Set the sound to play, along with specific {@link AudioAttributes audio attributes} to
2933 * use during playback.
2934 *
John Spurlockc0650f022014-07-19 13:22:39 -04002935 * @see Notification#sound
2936 */
2937 public Builder setSound(Uri sound, AudioAttributes audioAttributes) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002938 mN.sound = sound;
2939 mN.audioAttributes = audioAttributes;
John Spurlockc0650f022014-07-19 13:22:39 -04002940 return this;
2941 }
2942
2943 /**
Joe Onoratocb109a02011-01-18 17:57:41 -08002944 * Set the vibration pattern to use.
2945 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002946 * See {@link android.os.Vibrator#vibrate(long[], int)} for a discussion of the
2947 * <code>pattern</code> parameter.
2948 *
Chris Wren47c20a12014-06-18 17:27:29 -04002949 * <p>
2950 * A notification that vibrates is more likely to be presented as a heads-up notification.
2951 * </p>
2952 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002953 * @see Notification#vibrate
Joe Onoratocb109a02011-01-18 17:57:41 -08002954 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002955 public Builder setVibrate(long[] pattern) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002956 mN.vibrate = pattern;
Joe Onorato46439ce2010-11-19 13:56:21 -08002957 return this;
2958 }
2959
Joe Onoratocb109a02011-01-18 17:57:41 -08002960 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002961 * Set the desired color for the indicator LED on the device, as well as the
2962 * blink duty cycle (specified in milliseconds).
2963 *
2964
2965 * Not all devices will honor all (or even any) of these values.
2966 *
2967
2968 * @see Notification#ledARGB
2969 * @see Notification#ledOnMS
2970 * @see Notification#ledOffMS
Joe Onoratocb109a02011-01-18 17:57:41 -08002971 */
Tor Norbye80756e32015-03-02 09:39:27 -08002972 public Builder setLights(@ColorInt int argb, int onMs, int offMs) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002973 mN.ledARGB = argb;
2974 mN.ledOnMS = onMs;
2975 mN.ledOffMS = offMs;
Julia Reynolds10ee1fc2015-11-09 11:04:55 -05002976 if (onMs != 0 || offMs != 0) {
2977 mN.flags |= FLAG_SHOW_LIGHTS;
2978 }
Joe Onorato46439ce2010-11-19 13:56:21 -08002979 return this;
2980 }
2981
Joe Onoratocb109a02011-01-18 17:57:41 -08002982 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002983 * Set whether this is an "ongoing" notification.
Joe Onoratocb109a02011-01-18 17:57:41 -08002984 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002985
2986 * Ongoing notifications cannot be dismissed by the user, so your application or service
2987 * must take care of canceling them.
2988 *
2989
2990 * They are typically used to indicate a background task that the user is actively engaged
2991 * with (e.g., playing music) or is pending in some way and therefore occupying the device
2992 * (e.g., a file download, sync operation, active network connection).
2993 *
2994
2995 * @see Notification#FLAG_ONGOING_EVENT
2996 * @see Service#setForeground(boolean)
Joe Onoratocb109a02011-01-18 17:57:41 -08002997 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002998 public Builder setOngoing(boolean ongoing) {
2999 setFlag(FLAG_ONGOING_EVENT, ongoing);
3000 return this;
3001 }
3002
Joe Onoratocb109a02011-01-18 17:57:41 -08003003 /**
3004 * Set this flag if you would only like the sound, vibrate
3005 * and ticker to be played if the notification is not already showing.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003006 *
3007 * @see Notification#FLAG_ONLY_ALERT_ONCE
Joe Onoratocb109a02011-01-18 17:57:41 -08003008 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003009 public Builder setOnlyAlertOnce(boolean onlyAlertOnce) {
3010 setFlag(FLAG_ONLY_ALERT_ONCE, onlyAlertOnce);
3011 return this;
3012 }
3013
Joe Onoratocb109a02011-01-18 17:57:41 -08003014 /**
Julia Reynolds04499532016-09-13 14:04:53 -04003015 * Make this notification automatically dismissed when the user touches it.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003016 *
3017 * @see Notification#FLAG_AUTO_CANCEL
Joe Onoratocb109a02011-01-18 17:57:41 -08003018 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003019 public Builder setAutoCancel(boolean autoCancel) {
Joe Onorato281d83f2011-01-04 17:13:10 -08003020 setFlag(FLAG_AUTO_CANCEL, autoCancel);
Joe Onorato46439ce2010-11-19 13:56:21 -08003021 return this;
3022 }
3023
Joe Onoratocb109a02011-01-18 17:57:41 -08003024 /**
Griff Hazendfcb0802014-02-11 12:00:00 -08003025 * Set whether or not this notification should not bridge to other devices.
3026 *
3027 * <p>Some notifications can be bridged to other devices for remote display.
3028 * This hint can be set to recommend this notification not be bridged.
3029 */
3030 public Builder setLocalOnly(boolean localOnly) {
3031 setFlag(FLAG_LOCAL_ONLY, localOnly);
3032 return this;
3033 }
3034
3035 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003036 * Set which notification properties will be inherited from system defaults.
Joe Onoratocb109a02011-01-18 17:57:41 -08003037 * <p>
3038 * The value should be one or more of the following fields combined with
3039 * bitwise-or:
3040 * {@link #DEFAULT_SOUND}, {@link #DEFAULT_VIBRATE}, {@link #DEFAULT_LIGHTS}.
3041 * <p>
3042 * For all default values, use {@link #DEFAULT_ALL}.
3043 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003044 public Builder setDefaults(int defaults) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003045 mN.defaults = defaults;
Joe Onorato46439ce2010-11-19 13:56:21 -08003046 return this;
3047 }
3048
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003049 /**
3050 * Set the priority of this notification.
3051 *
3052 * @see Notification#priority
3053 */
Tor Norbyed9273d62013-05-30 15:59:53 -07003054 public Builder setPriority(@Priority int pri) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003055 mN.priority = pri;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003056 return this;
3057 }
Joe Malin8d40d042012-11-05 11:36:40 -08003058
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003059 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -04003060 * Set the notification category.
Joe Malin8d40d042012-11-05 11:36:40 -08003061 *
John Spurlockfd7f1e02014-03-18 16:41:57 -04003062 * @see Notification#category
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003063 */
John Spurlockfd7f1e02014-03-18 16:41:57 -04003064 public Builder setCategory(String category) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003065 mN.category = category;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003066 return this;
3067 }
3068
3069 /**
Chris Wrendde75302014-03-26 17:24:15 -04003070 * Add a person that is relevant to this notification.
3071 *
Chris Wrene6c48932014-09-29 17:19:27 -04003072 * <P>
3073 * Depending on user preferences, this annotation may allow the notification to pass
3074 * through interruption filters, and to appear more prominently in the user interface.
3075 * </P>
3076 *
3077 * <P>
3078 * The person should be specified by the {@code String} representation of a
3079 * {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI}.
3080 * </P>
3081 *
3082 * <P>The system will also attempt to resolve {@code mailto:} and {@code tel:} schema
3083 * URIs. The path part of these URIs must exist in the contacts database, in the
3084 * appropriate column, or the reference will be discarded as invalid. Telephone schema
3085 * URIs will be resolved by {@link android.provider.ContactsContract.PhoneLookup}.
3086 * </P>
3087 *
3088 * @param uri A URI for the person.
Chris Wrendde75302014-03-26 17:24:15 -04003089 * @see Notification#EXTRA_PEOPLE
3090 */
Chris Wrene6c48932014-09-29 17:19:27 -04003091 public Builder addPerson(String uri) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003092 mPersonList.add(uri);
Chris Wrendde75302014-03-26 17:24:15 -04003093 return this;
3094 }
3095
3096 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003097 * Set this notification to be part of a group of notifications sharing the same key.
3098 * Grouped notifications may display in a cluster or stack on devices which
3099 * support such rendering.
3100 *
3101 * <p>To make this notification the summary for its group, also call
3102 * {@link #setGroupSummary}. A sort order can be specified for group members by using
3103 * {@link #setSortKey}.
3104 * @param groupKey The group key of the group.
3105 * @return this object for method chaining
3106 */
3107 public Builder setGroup(String groupKey) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003108 mN.mGroupKey = groupKey;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003109 return this;
3110 }
3111
3112 /**
3113 * Set this notification to be the group summary for a group of notifications.
3114 * Grouped notifications may display in a cluster or stack on devices which
Julia Reynolds04499532016-09-13 14:04:53 -04003115 * support such rendering. If thereRequires a group key also be set using {@link #setGroup}.
3116 * The group summary may be suppressed if too few notifications are included in the group.
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003117 * @param isGroupSummary Whether this notification should be a group summary.
3118 * @return this object for method chaining
3119 */
3120 public Builder setGroupSummary(boolean isGroupSummary) {
3121 setFlag(FLAG_GROUP_SUMMARY, isGroupSummary);
3122 return this;
3123 }
3124
3125 /**
3126 * Set a sort key that orders this notification among other notifications from the
3127 * same package. This can be useful if an external sort was already applied and an app
3128 * would like to preserve this. Notifications will be sorted lexicographically using this
3129 * value, although providing different priorities in addition to providing sort key may
3130 * cause this value to be ignored.
3131 *
3132 * <p>This sort key can also be used to order members of a notification group. See
Griff Hazen9e1379f2014-05-20 12:50:51 -07003133 * {@link #setGroup}.
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003134 *
3135 * @see String#compareTo(String)
3136 */
3137 public Builder setSortKey(String sortKey) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003138 mN.mSortKey = sortKey;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003139 return this;
3140 }
3141
3142 /**
Griff Hazen720042b2014-02-24 15:46:56 -08003143 * Merge additional metadata into this notification.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003144 *
Griff Hazen720042b2014-02-24 15:46:56 -08003145 * <p>Values within the Bundle will replace existing extras values in this Builder.
3146 *
3147 * @see Notification#extras
3148 */
Griff Hazen959591e2014-05-15 22:26:18 -07003149 public Builder addExtras(Bundle extras) {
3150 if (extras != null) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003151 mUserExtras.putAll(extras);
Griff Hazen720042b2014-02-24 15:46:56 -08003152 }
3153 return this;
3154 }
3155
3156 /**
3157 * Set metadata for this notification.
3158 *
3159 * <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 -04003160 * current contents are copied into the Notification each time {@link #build()} is
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003161 * called.
3162 *
Griff Hazen720042b2014-02-24 15:46:56 -08003163 * <p>Replaces any existing extras values with those from the provided Bundle.
3164 * Use {@link #addExtras} to merge in metadata instead.
3165 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003166 * @see Notification#extras
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003167 */
Griff Hazen959591e2014-05-15 22:26:18 -07003168 public Builder setExtras(Bundle extras) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003169 if (extras != null) {
3170 mUserExtras = extras;
3171 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003172 return this;
3173 }
3174
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003175 /**
Griff Hazen720042b2014-02-24 15:46:56 -08003176 * Get the current metadata Bundle used by this notification Builder.
3177 *
3178 * <p>The returned Bundle is shared with this Builder.
3179 *
3180 * <p>The current contents of this Bundle are copied into the Notification each time
3181 * {@link #build()} is called.
3182 *
3183 * @see Notification#extras
3184 */
3185 public Bundle getExtras() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003186 return mUserExtras;
3187 }
3188
3189 private Bundle getAllExtras() {
3190 final Bundle saveExtras = (Bundle) mUserExtras.clone();
3191 saveExtras.putAll(mN.extras);
3192 return saveExtras;
Griff Hazen720042b2014-02-24 15:46:56 -08003193 }
3194
3195 /**
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003196 * Add an action to this notification. Actions are typically displayed by
3197 * the system as a button adjacent to the notification content.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04003198 * <p>
3199 * Every action must have an icon (32dp square and matching the
3200 * <a href="{@docRoot}design/style/iconography.html#action-bar">Holo
3201 * Dark action bar</a> visual style), a textual label, and a {@link PendingIntent}.
3202 * <p>
3203 * A notification in its expanded form can display up to 3 actions, from left to right in
3204 * the order they were added. Actions will not be displayed when the notification is
3205 * collapsed, however, so be sure that any essential functions may be accessed by the user
3206 * in some other way (for example, in the Activity pointed to by {@link #contentIntent}).
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003207 *
3208 * @param icon Resource ID of a drawable that represents the action.
3209 * @param title Text describing the action.
3210 * @param intent PendingIntent to be fired when the action is invoked.
Dan Sandler86647982015-05-13 23:41:13 -04003211 *
3212 * @deprecated Use {@link #addAction(Action)} instead.
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003213 */
Dan Sandler86647982015-05-13 23:41:13 -04003214 @Deprecated
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003215 public Builder addAction(int icon, CharSequence title, PendingIntent intent) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04003216 mActions.add(new Action(icon, safeCharSequence(title), intent));
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003217 return this;
3218 }
3219
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003220 /**
Griff Hazen959591e2014-05-15 22:26:18 -07003221 * Add an action to this notification. Actions are typically displayed by
3222 * the system as a button adjacent to the notification content.
3223 * <p>
3224 * Every action must have an icon (32dp square and matching the
3225 * <a href="{@docRoot}design/style/iconography.html#action-bar">Holo
3226 * Dark action bar</a> visual style), a textual label, and a {@link PendingIntent}.
3227 * <p>
3228 * A notification in its expanded form can display up to 3 actions, from left to right in
3229 * the order they were added. Actions will not be displayed when the notification is
3230 * collapsed, however, so be sure that any essential functions may be accessed by the user
3231 * in some other way (for example, in the Activity pointed to by {@link #contentIntent}).
3232 *
3233 * @param action The action to add.
3234 */
3235 public Builder addAction(Action action) {
3236 mActions.add(action);
3237 return this;
3238 }
3239
3240 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003241 * Alter the complete list of actions attached to this notification.
3242 * @see #addAction(Action).
3243 *
3244 * @param actions
3245 * @return
3246 */
3247 public Builder setActions(Action... actions) {
3248 mActions.clear();
3249 for (int i = 0; i < actions.length; i++) {
3250 mActions.add(actions[i]);
3251 }
3252 return this;
3253 }
3254
3255 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003256 * Add a rich notification style to be applied at build time.
3257 *
3258 * @param style Object responsible for modifying the notification style.
3259 */
3260 public Builder setStyle(Style style) {
3261 if (mStyle != style) {
3262 mStyle = style;
Daniel Sandlerc08dea22012-06-28 08:35:24 -07003263 if (mStyle != null) {
3264 mStyle.setBuilder(this);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003265 mN.extras.putString(EXTRA_TEMPLATE, style.getClass().getName());
3266 } else {
3267 mN.extras.remove(EXTRA_TEMPLATE);
Daniel Sandlerc08dea22012-06-28 08:35:24 -07003268 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003269 }
3270 return this;
3271 }
3272
Dan Sandler0bf2ed82013-12-21 23:33:41 -06003273 /**
3274 * Specify the value of {@link #visibility}.
Griff Hazenb720abe2014-05-20 13:15:30 -07003275 *
Dan Sandler0bf2ed82013-12-21 23:33:41 -06003276 * @param visibility One of {@link #VISIBILITY_PRIVATE} (the default),
3277 * {@link #VISIBILITY_SECRET}, or {@link #VISIBILITY_PUBLIC}.
3278 *
3279 * @return The same Builder.
3280 */
3281 public Builder setVisibility(int visibility) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003282 mN.visibility = visibility;
Dan Sandler0bf2ed82013-12-21 23:33:41 -06003283 return this;
3284 }
3285
3286 /**
3287 * Supply a replacement Notification whose contents should be shown in insecure contexts
3288 * (i.e. atop the secure lockscreen). See {@link #visibility} and {@link #VISIBILITY_PUBLIC}.
3289 * @param n A replacement notification, presumably with some or all info redacted.
3290 * @return The same Builder.
3291 */
3292 public Builder setPublicVersion(Notification n) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003293 if (n != null) {
3294 mN.publicVersion = new Notification();
3295 n.cloneInto(mN.publicVersion, /*heavy=*/ true);
3296 } else {
3297 mN.publicVersion = null;
3298 }
Dan Sandler0bf2ed82013-12-21 23:33:41 -06003299 return this;
3300 }
3301
Griff Hazenb720abe2014-05-20 13:15:30 -07003302 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003303 * Apply an extender to this notification builder. Extenders may be used to add
3304 * metadata or change options on this builder.
3305 */
Griff Hazen61a9e862014-05-22 16:05:19 -07003306 public Builder extend(Extender extender) {
3307 extender.extend(this);
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003308 return this;
3309 }
3310
Dan Sandler4e787062015-06-17 15:09:48 -04003311 /**
3312 * @hide
3313 */
Julia Reynoldse46bb372016-03-17 11:05:58 -04003314 public Builder setFlag(int mask, boolean value) {
Joe Onorato46439ce2010-11-19 13:56:21 -08003315 if (value) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003316 mN.flags |= mask;
Joe Onorato46439ce2010-11-19 13:56:21 -08003317 } else {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003318 mN.flags &= ~mask;
Joe Onorato46439ce2010-11-19 13:56:21 -08003319 }
Julia Reynoldse46bb372016-03-17 11:05:58 -04003320 return this;
Joe Onorato46439ce2010-11-19 13:56:21 -08003321 }
3322
Dan Sandler26e81cf2014-05-06 10:01:27 -04003323 /**
3324 * Sets {@link Notification#color}.
3325 *
3326 * @param argb The accent color to use
3327 *
3328 * @return The same Builder.
3329 */
Tor Norbye80756e32015-03-02 09:39:27 -08003330 public Builder setColor(@ColorInt int argb) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003331 mN.color = argb;
Julia Reynolds10ee1fc2015-11-09 11:04:55 -05003332 sanitizeColor();
Dan Sandler26e81cf2014-05-06 10:01:27 -04003333 return this;
3334 }
3335
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003336 private Drawable getProfileBadgeDrawable() {
Chris Wren66619a22016-05-12 16:42:37 -04003337 if (mContext.getUserId() == UserHandle.USER_SYSTEM) {
3338 // This user can never be a badged profile,
3339 // and also includes USER_ALL system notifications.
3340 return null;
3341 }
Christoph Studer7ac80e62014-08-04 16:01:57 +02003342 // Note: This assumes that the current user can read the profile badge of the
3343 // originating user.
Selim Cineke6ff9462016-01-15 15:07:06 -08003344 return mContext.getPackageManager().getUserBadgeForDensityNoBackground(
Julia Reynoldsda303542015-11-23 14:00:20 -05003345 new UserHandle(mContext.getUserId()), 0);
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003346 }
3347
3348 private Bitmap getProfileBadge() {
3349 Drawable badge = getProfileBadgeDrawable();
Kenny Guy8a0101b2014-05-08 23:34:12 +01003350 if (badge == null) {
3351 return null;
3352 }
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003353 final int size = mContext.getResources().getDimensionPixelSize(
3354 R.dimen.notification_badge_size);
3355 Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
Kenny Guy8a0101b2014-05-08 23:34:12 +01003356 Canvas canvas = new Canvas(bitmap);
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003357 badge.setBounds(0, 0, size, size);
Kenny Guy8a0101b2014-05-08 23:34:12 +01003358 badge.draw(canvas);
3359 return bitmap;
3360 }
3361
Selim Cinekc848c3a2016-01-13 15:27:30 -08003362 private void bindProfileBadge(RemoteViews contentView) {
Kenny Guy98193ea2014-07-24 19:54:37 +01003363 Bitmap profileBadge = getProfileBadge();
3364
Kenny Guy98193ea2014-07-24 19:54:37 +01003365 if (profileBadge != null) {
Selim Cinekc848c3a2016-01-13 15:27:30 -08003366 contentView.setImageViewBitmap(R.id.profile_badge, profileBadge);
3367 contentView.setViewVisibility(R.id.profile_badge, View.VISIBLE);
Kenny Guy98193ea2014-07-24 19:54:37 +01003368 }
Kenny Guy98193ea2014-07-24 19:54:37 +01003369 }
3370
Christoph Studerfe718432014-09-01 18:21:18 +02003371 private void resetStandardTemplate(RemoteViews contentView) {
Selim Cinekeaa29ca2015-11-23 13:51:13 -08003372 resetNotificationHeader(contentView);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003373 resetContentMargins(contentView);
Christoph Studerfe718432014-09-01 18:21:18 +02003374 contentView.setViewVisibility(R.id.right_icon, View.GONE);
Selim Cinek860b6da2015-12-16 19:02:19 -08003375 contentView.setViewVisibility(R.id.title, View.GONE);
Christoph Studerfe718432014-09-01 18:21:18 +02003376 contentView.setTextViewText(R.id.title, null);
Selim Cinek41598732016-01-11 16:58:37 -08003377 contentView.setViewVisibility(R.id.text, View.GONE);
Christoph Studerfe718432014-09-01 18:21:18 +02003378 contentView.setTextViewText(R.id.text, null);
Selim Cinek29603462015-11-17 19:04:39 -08003379 contentView.setViewVisibility(R.id.text_line_1, View.GONE);
Selim Cinek41598732016-01-11 16:58:37 -08003380 contentView.setTextViewText(R.id.text_line_1, null);
Christoph Studerfe718432014-09-01 18:21:18 +02003381 contentView.setViewVisibility(R.id.progress, View.GONE);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003382 }
3383
Selim Cinekeaa29ca2015-11-23 13:51:13 -08003384 /**
3385 * Resets the notification header to its original state
3386 */
3387 private void resetNotificationHeader(RemoteViews contentView) {
Adrian Roosc4337a32016-08-02 18:30:34 -07003388 // Small icon doesn't need to be reset, as it's always set. Resetting would prevent
3389 // re-using the drawable when the notification is updated.
Selim Cinek7b836392015-12-04 20:02:59 -08003390 contentView.setBoolean(R.id.notification_header, "setExpanded", false);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003391 contentView.setTextViewText(R.id.app_name_text, null);
Christoph Studerca1db712014-09-10 17:31:33 +02003392 contentView.setViewVisibility(R.id.chronometer, View.GONE);
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07003393 contentView.setViewVisibility(R.id.header_text, View.GONE);
Adrian Roos9dfb78f2016-06-30 15:43:44 -07003394 contentView.setTextViewText(R.id.header_text, null);
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07003395 contentView.setViewVisibility(R.id.header_text_divider, View.GONE);
Selim Cinek29603462015-11-17 19:04:39 -08003396 contentView.setViewVisibility(R.id.time_divider, View.GONE);
Selim Cinekb85f36fd2016-04-20 18:46:36 -07003397 contentView.setViewVisibility(R.id.time, View.GONE);
Selim Cinekc848c3a2016-01-13 15:27:30 -08003398 contentView.setImageViewIcon(R.id.profile_badge, null);
3399 contentView.setViewVisibility(R.id.profile_badge, View.GONE);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003400 }
3401
3402 private void resetContentMargins(RemoteViews contentView) {
Adrian Roos2d5dbba2016-06-08 17:11:53 -07003403 contentView.setViewLayoutMarginEndDimen(R.id.line1, 0);
3404 contentView.setViewLayoutMarginEndDimen(R.id.text, 0);
Christoph Studerfe718432014-09-01 18:21:18 +02003405 }
3406
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003407 private RemoteViews applyStandardTemplate(int resId) {
Adrian Roos70d7aa32017-01-11 15:39:06 -08003408 return applyStandardTemplate(resId, mParams.reset().fillTextsFrom(this));
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02003409 }
3410
3411 /**
3412 * @param hasProgress whether the progress bar should be shown and set
3413 */
3414 private RemoteViews applyStandardTemplate(int resId, boolean hasProgress) {
Adrian Roos70d7aa32017-01-11 15:39:06 -08003415 return applyStandardTemplate(resId, mParams.reset().hasProgress(hasProgress)
3416 .fillTextsFrom(this));
Adrian Roosc1a80b02016-04-05 14:54:55 -07003417 }
3418
Adrian Roos70d7aa32017-01-11 15:39:06 -08003419 private RemoteViews applyStandardTemplate(int resId, StandardTemplateParams p) {
Kenny Guy77320062014-08-27 21:37:15 +01003420 RemoteViews contentView = new BuilderRemoteViews(mContext.getApplicationInfo(), resId);
Dan Sandler539aad42014-08-04 00:43:39 -04003421
Christoph Studerfe718432014-09-01 18:21:18 +02003422 resetStandardTemplate(contentView);
3423
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003424 final Bundle ex = mN.extras;
Dan Sandler190d58d2014-05-15 09:33:39 -04003425
Adrian Roos487374f2017-01-11 15:48:14 -08003426 bindNotificationHeader(contentView, p.ambient);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003427 bindLargeIcon(contentView);
Adrian Roos70d7aa32017-01-11 15:39:06 -08003428 boolean showProgress = handleProgressBar(p.hasProgress, contentView, ex);
3429 if (p.title != null) {
Selim Cinek860b6da2015-12-16 19:02:19 -08003430 contentView.setViewVisibility(R.id.title, View.VISIBLE);
Adrian Roos70d7aa32017-01-11 15:39:06 -08003431 contentView.setTextViewText(R.id.title, p.title);
Selim Cinek954cc232016-05-20 13:29:23 -07003432 contentView.setViewLayoutWidth(R.id.title, showProgress
3433 ? ViewGroup.LayoutParams.WRAP_CONTENT
3434 : ViewGroup.LayoutParams.MATCH_PARENT);
Joe Onorato561d3852010-11-20 18:09:34 -08003435 }
Adrian Roos70d7aa32017-01-11 15:39:06 -08003436 if (p.text != null) {
Selim Cinek41598732016-01-11 16:58:37 -08003437 int textId = showProgress ? com.android.internal.R.id.text_line_1
3438 : com.android.internal.R.id.text;
Adrian Roos70d7aa32017-01-11 15:39:06 -08003439 contentView.setTextViewText(textId, p.text);
Selim Cinek41598732016-01-11 16:58:37 -08003440 contentView.setViewVisibility(textId, View.VISIBLE);
Joe Onorato561d3852010-11-20 18:09:34 -08003441 }
Selim Cinekc848c3a2016-01-13 15:27:30 -08003442
Selim Cinek279fa862016-06-14 10:57:25 -07003443 setContentMinHeight(contentView, showProgress || mN.hasLargeIcon());
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003444
Selim Cinek29603462015-11-17 19:04:39 -08003445 return contentView;
3446 }
3447
Selim Cinek860b6da2015-12-16 19:02:19 -08003448 /**
3449 * @param remoteView the remote view to update the minheight in
3450 * @param hasMinHeight does it have a mimHeight
3451 * @hide
3452 */
3453 void setContentMinHeight(RemoteViews remoteView, boolean hasMinHeight) {
3454 int minHeight = 0;
3455 if (hasMinHeight) {
3456 // we need to set the minHeight of the notification
3457 minHeight = mContext.getResources().getDimensionPixelSize(
3458 com.android.internal.R.dimen.notification_min_content_height);
3459 }
3460 remoteView.setInt(R.id.notification_main_column, "setMinimumHeight", minHeight);
3461 }
3462
Selim Cinek29603462015-11-17 19:04:39 -08003463 private boolean handleProgressBar(boolean hasProgress, RemoteViews contentView, Bundle ex) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003464 final int max = ex.getInt(EXTRA_PROGRESS_MAX, 0);
3465 final int progress = ex.getInt(EXTRA_PROGRESS, 0);
3466 final boolean ind = ex.getBoolean(EXTRA_PROGRESS_INDETERMINATE);
3467 if (hasProgress && (max != 0 || ind)) {
Selim Cinek29603462015-11-17 19:04:39 -08003468 contentView.setViewVisibility(com.android.internal.R.id.progress, View.VISIBLE);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003469 contentView.setProgressBar(
Selim Cinek29603462015-11-17 19:04:39 -08003470 R.id.progress, max, progress, ind);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003471 contentView.setProgressBackgroundTintList(
3472 R.id.progress, ColorStateList.valueOf(mContext.getColor(
3473 R.color.notification_progress_background_color)));
Selim Cinek29603462015-11-17 19:04:39 -08003474 if (mN.color != COLOR_DEFAULT) {
Adrian Roos4ff3b122016-02-01 12:26:13 -08003475 ColorStateList colorStateList = ColorStateList.valueOf(resolveContrastColor());
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003476 contentView.setProgressTintList(R.id.progress, colorStateList);
3477 contentView.setProgressIndeterminateTintList(R.id.progress, colorStateList);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003478 }
Selim Cinek29603462015-11-17 19:04:39 -08003479 return true;
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003480 } else {
3481 contentView.setViewVisibility(R.id.progress, View.GONE);
Selim Cinek29603462015-11-17 19:04:39 -08003482 return false;
Jeff Sharkey1c400132011-08-05 14:50:13 -07003483 }
Joe Onorato561d3852010-11-20 18:09:34 -08003484 }
3485
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003486 private void bindLargeIcon(RemoteViews contentView) {
Selim Cinek279fa862016-06-14 10:57:25 -07003487 if (mN.mLargeIcon == null && mN.largeIcon != null) {
3488 mN.mLargeIcon = Icon.createWithBitmap(mN.largeIcon);
3489 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003490 if (mN.mLargeIcon != null) {
3491 contentView.setViewVisibility(R.id.right_icon, View.VISIBLE);
3492 contentView.setImageViewIcon(R.id.right_icon, mN.mLargeIcon);
3493 processLargeLegacyIcon(mN.mLargeIcon, contentView);
Adrian Roos2d5dbba2016-06-08 17:11:53 -07003494 int endMargin = R.dimen.notification_content_picture_margin;
3495 contentView.setViewLayoutMarginEndDimen(R.id.line1, endMargin);
3496 contentView.setViewLayoutMarginEndDimen(R.id.text, endMargin);
3497 contentView.setViewLayoutMarginEndDimen(R.id.progress, endMargin);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003498 }
3499 }
3500
Adrian Roos487374f2017-01-11 15:48:14 -08003501 private void bindNotificationHeader(RemoteViews contentView, boolean ambient) {
3502 bindSmallIcon(contentView, ambient);
3503 bindHeaderAppName(contentView, ambient);
3504 if (!ambient) {
3505 // Ambient view does not have these
3506 bindHeaderText(contentView);
3507 bindHeaderChronometerAndTime(contentView);
3508 bindExpandButton(contentView);
3509 bindProfileBadge(contentView);
3510 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003511 }
3512
3513 private void bindExpandButton(RemoteViews contentView) {
Adrian Roos4ff3b122016-02-01 12:26:13 -08003514 contentView.setDrawableParameters(R.id.expand_button, false, -1, resolveContrastColor(),
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003515 PorterDuff.Mode.SRC_ATOP, -1);
Selim Cinekea4bef72015-12-02 15:51:10 -08003516 contentView.setInt(R.id.notification_header, "setOriginalNotificationColor",
Adrian Roos4ff3b122016-02-01 12:26:13 -08003517 resolveContrastColor());
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003518 }
3519
3520 private void bindHeaderChronometerAndTime(RemoteViews contentView) {
3521 if (showsTimeOrChronometer()) {
Selim Cinek29603462015-11-17 19:04:39 -08003522 contentView.setViewVisibility(R.id.time_divider, View.VISIBLE);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003523 if (mN.extras.getBoolean(EXTRA_SHOW_CHRONOMETER)) {
3524 contentView.setViewVisibility(R.id.chronometer, View.VISIBLE);
3525 contentView.setLong(R.id.chronometer, "setBase",
3526 mN.when + (SystemClock.elapsedRealtime() - System.currentTimeMillis()));
3527 contentView.setBoolean(R.id.chronometer, "setStarted", true);
Adrian Roos96b7e202016-05-17 13:50:38 -07003528 boolean countsDown = mN.extras.getBoolean(EXTRA_CHRONOMETER_COUNT_DOWN);
Selim Cinekc3b752e2016-04-20 16:13:59 -07003529 contentView.setChronometerCountDown(R.id.chronometer, countsDown);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003530 } else {
3531 contentView.setViewVisibility(R.id.time, View.VISIBLE);
3532 contentView.setLong(R.id.time, "setTime", mN.when);
3533 }
Selim Cinekb85f36fd2016-04-20 18:46:36 -07003534 } else {
3535 // We still want a time to be set but gone, such that we can show and hide it
3536 // on demand in case it's a child notification without anything in the header
3537 contentView.setLong(R.id.time, "setTime", mN.when != 0 ? mN.when : mN.creationTime);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003538 }
3539 }
3540
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07003541 private void bindHeaderText(RemoteViews contentView) {
3542 CharSequence headerText = mN.extras.getCharSequence(EXTRA_SUB_TEXT);
3543 if (headerText == null && mStyle != null && mStyle.mSummaryTextSet
Selim Cinek03d0d652015-11-13 13:18:09 -05003544 && mStyle.hasSummaryInHeader()) {
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07003545 headerText = mStyle.mSummaryText;
Selim Cinek03d0d652015-11-13 13:18:09 -05003546 }
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07003547 if (headerText == null
3548 && mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N
3549 && mN.extras.getCharSequence(EXTRA_INFO_TEXT) != null) {
3550 headerText = mN.extras.getCharSequence(EXTRA_INFO_TEXT);
3551 }
3552 if (headerText != null) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003553 // TODO: Remove the span entirely to only have the string with propper formating.
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07003554 contentView.setTextViewText(R.id.header_text, processLegacyText(headerText));
3555 contentView.setViewVisibility(R.id.header_text, View.VISIBLE);
3556 contentView.setViewVisibility(R.id.header_text_divider, View.VISIBLE);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003557 }
3558 }
3559
Adrian Rooseba05822016-04-22 17:09:27 -07003560 /**
3561 * @hide
3562 */
3563 public String loadHeaderAppName() {
Dan Sandler732bd6c2016-04-12 14:20:32 -04003564 CharSequence name = null;
3565 final PackageManager pm = mContext.getPackageManager();
3566 if (mN.extras.containsKey(EXTRA_SUBSTITUTE_APP_NAME)) {
3567 // only system packages which lump together a bunch of unrelated stuff
3568 // may substitute a different name to make the purpose of the
3569 // notification more clear. the correct package label should always
3570 // be accessible via SystemUI.
3571 final String pkg = mContext.getPackageName();
3572 final String subName = mN.extras.getString(EXTRA_SUBSTITUTE_APP_NAME);
3573 if (PackageManager.PERMISSION_GRANTED == pm.checkPermission(
3574 android.Manifest.permission.SUBSTITUTE_NOTIFICATION_APP_NAME, pkg)) {
3575 name = subName;
3576 } else {
3577 Log.w(TAG, "warning: pkg "
3578 + pkg + " attempting to substitute app name '" + subName
3579 + "' without holding perm "
3580 + android.Manifest.permission.SUBSTITUTE_NOTIFICATION_APP_NAME);
3581 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003582 }
Dan Sandler732bd6c2016-04-12 14:20:32 -04003583 if (TextUtils.isEmpty(name)) {
3584 name = pm.getApplicationLabel(mContext.getApplicationInfo());
3585 }
3586 if (TextUtils.isEmpty(name)) {
3587 // still nothing?
3588 return null;
3589 }
3590
3591 return String.valueOf(name);
3592 }
Adrian Roos487374f2017-01-11 15:48:14 -08003593 private void bindHeaderAppName(RemoteViews contentView, boolean ambient) {
Dan Sandler732bd6c2016-04-12 14:20:32 -04003594 contentView.setTextViewText(R.id.app_name_text, loadHeaderAppName());
Adrian Roos487374f2017-01-11 15:48:14 -08003595 contentView.setTextColor(R.id.app_name_text,
3596 ambient ? resolveAmbientColor() : resolveContrastColor());
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003597 }
3598
Adrian Roos487374f2017-01-11 15:48:14 -08003599 private void bindSmallIcon(RemoteViews contentView, boolean ambient) {
Selim Cinek279fa862016-06-14 10:57:25 -07003600 if (mN.mSmallIcon == null && mN.icon != 0) {
3601 mN.mSmallIcon = Icon.createWithResource(mContext, mN.icon);
3602 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003603 contentView.setImageViewIcon(R.id.icon, mN.mSmallIcon);
Adrian Roos9b45a152016-06-28 13:32:29 -07003604 contentView.setDrawableParameters(R.id.icon, false /* targetBackground */,
3605 -1 /* alpha */, -1 /* colorFilter */, null /* mode */, mN.iconLevel);
Adrian Roos487374f2017-01-11 15:48:14 -08003606 processSmallIconColor(mN.mSmallIcon, contentView, ambient);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003607 }
3608
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003609 /**
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02003610 * @return true if the built notification will show the time or the chronometer; false
3611 * otherwise
3612 */
3613 private boolean showsTimeOrChronometer() {
Selim Cinekc2c0b042016-05-18 17:13:46 -07003614 return mN.showsTime() || mN.showsChronometer();
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02003615 }
3616
Christoph Studerfe718432014-09-01 18:21:18 +02003617 private void resetStandardTemplateWithActions(RemoteViews big) {
Adrian Roos4c1fcc82016-03-31 14:39:39 -07003618 // actions_container is only reset when there are no actions to avoid focus issues with
3619 // remote inputs.
Christoph Studerfe718432014-09-01 18:21:18 +02003620 big.setViewVisibility(R.id.actions, View.GONE);
Christoph Studerfe718432014-09-01 18:21:18 +02003621 big.removeAllViews(R.id.actions);
Adrian Roose458aa82015-12-08 16:17:19 -08003622
3623 big.setViewVisibility(R.id.notification_material_reply_container, View.GONE);
3624 big.setTextViewText(R.id.notification_material_reply_text_1, null);
3625
3626 big.setViewVisibility(R.id.notification_material_reply_text_2, View.GONE);
3627 big.setTextViewText(R.id.notification_material_reply_text_2, null);
3628 big.setViewVisibility(R.id.notification_material_reply_text_3, View.GONE);
3629 big.setTextViewText(R.id.notification_material_reply_text_3, null);
Adrian Roosf852a422016-06-03 13:33:43 -07003630
3631 big.setViewLayoutMarginBottomDimen(R.id.notification_action_list_margin_target, 0);
Christoph Studerfe718432014-09-01 18:21:18 +02003632 }
3633
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003634 private RemoteViews applyStandardTemplateWithActions(int layoutId) {
Adrian Roos70d7aa32017-01-11 15:39:06 -08003635 return applyStandardTemplateWithActions(layoutId, mParams.reset().fillTextsFrom(this));
Adrian Roos48d746a2016-04-12 14:57:28 -07003636 }
3637
Adrian Roos70d7aa32017-01-11 15:39:06 -08003638 private RemoteViews applyStandardTemplateWithActions(int layoutId,
3639 StandardTemplateParams p) {
3640 RemoteViews big = applyStandardTemplate(layoutId, p);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003641
Christoph Studerfe718432014-09-01 18:21:18 +02003642 resetStandardTemplateWithActions(big);
3643
Adrian Roose458aa82015-12-08 16:17:19 -08003644 boolean validRemoteInput = false;
3645
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003646 int N = mActions.size();
Adrian Roos487374f2017-01-11 15:48:14 -08003647 boolean emphazisedMode = mN.fullScreenIntent != null && !p.ambient;
Selim Cinek06e9e1f2016-07-08 17:14:16 -07003648 big.setBoolean(R.id.actions, "setEmphasizedMode", emphazisedMode);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003649 if (N > 0) {
Adrian Roos7052de52016-03-03 15:53:34 -08003650 big.setViewVisibility(R.id.actions_container, View.VISIBLE);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003651 big.setViewVisibility(R.id.actions, View.VISIBLE);
Adrian Roos487374f2017-01-11 15:48:14 -08003652 if (p.ambient) {
3653 big.setInt(R.id.actions, "setBackgroundColor", Color.TRANSPARENT);
3654 }
Adrian Roosf852a422016-06-03 13:33:43 -07003655 big.setViewLayoutMarginBottomDimen(R.id.notification_action_list_margin_target,
3656 R.dimen.notification_action_list_height);
Daniel Sandler8680bf82012-05-15 16:52:52 -04003657 if (N>MAX_ACTION_BUTTONS) N=MAX_ACTION_BUTTONS;
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003658 for (int i=0; i<N; i++) {
Adrian Roose458aa82015-12-08 16:17:19 -08003659 Action action = mActions.get(i);
3660 validRemoteInput |= hasValidRemoteInput(action);
3661
Selim Cinek06e9e1f2016-07-08 17:14:16 -07003662 final RemoteViews button = generateActionButton(action, emphazisedMode,
Adrian Roos487374f2017-01-11 15:48:14 -08003663 i % 2 != 0, p.ambient);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003664 big.addView(R.id.actions, button);
3665 }
Adrian Roos4c1fcc82016-03-31 14:39:39 -07003666 } else {
3667 big.setViewVisibility(R.id.actions_container, View.GONE);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003668 }
Adrian Roose458aa82015-12-08 16:17:19 -08003669
3670 CharSequence[] replyText = mN.extras.getCharSequenceArray(EXTRA_REMOTE_INPUT_HISTORY);
Adrian Roos487374f2017-01-11 15:48:14 -08003671 if (!p.ambient && validRemoteInput && replyText != null
Adrian Roose458aa82015-12-08 16:17:19 -08003672 && replyText.length > 0 && !TextUtils.isEmpty(replyText[0])) {
3673 big.setViewVisibility(R.id.notification_material_reply_container, View.VISIBLE);
3674 big.setTextViewText(R.id.notification_material_reply_text_1, replyText[0]);
3675
3676 if (replyText.length > 1 && !TextUtils.isEmpty(replyText[1])) {
3677 big.setViewVisibility(R.id.notification_material_reply_text_2, View.VISIBLE);
3678 big.setTextViewText(R.id.notification_material_reply_text_2, replyText[1]);
3679
3680 if (replyText.length > 2 && !TextUtils.isEmpty(replyText[2])) {
3681 big.setViewVisibility(
3682 R.id.notification_material_reply_text_3, View.VISIBLE);
3683 big.setTextViewText(R.id.notification_material_reply_text_3, replyText[2]);
3684 }
3685 }
3686 }
3687
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003688 return big;
3689 }
3690
Adrian Roose458aa82015-12-08 16:17:19 -08003691 private boolean hasValidRemoteInput(Action action) {
3692 if (TextUtils.isEmpty(action.title) || action.actionIntent == null) {
3693 // Weird actions
3694 return false;
3695 }
3696
3697 RemoteInput[] remoteInputs = action.getRemoteInputs();
3698 if (remoteInputs == null) {
3699 return false;
3700 }
3701
3702 for (RemoteInput r : remoteInputs) {
3703 CharSequence[] choices = r.getChoices();
3704 if (r.getAllowFreeFormInput() || (choices != null && choices.length != 0)) {
3705 return true;
3706 }
3707 }
3708 return false;
3709 }
3710
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003711 /**
3712 * Construct a RemoteViews for the final 1U notification layout. In order:
3713 * 1. Custom contentView from the caller
3714 * 2. Style's proposed content view
3715 * 3. Standard template view
3716 */
Julia Reynolds3b848122016-02-26 10:45:32 -05003717 public RemoteViews createContentView() {
Selim Cinek593610c2016-02-16 18:42:57 -08003718 if (mN.contentView != null && (mStyle == null || !mStyle.displayCustomViewInline())) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003719 return mN.contentView;
3720 } else if (mStyle != null) {
3721 final RemoteViews styleView = mStyle.makeContentView();
3722 if (styleView != null) {
3723 return styleView;
3724 }
Joe Onorato46439ce2010-11-19 13:56:21 -08003725 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003726 return applyStandardTemplate(getBaseLayoutResource());
Joe Onorato46439ce2010-11-19 13:56:21 -08003727 }
3728
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003729 /**
3730 * Construct a RemoteViews for the final big notification layout.
3731 */
Julia Reynolds3b848122016-02-26 10:45:32 -05003732 public RemoteViews createBigContentView() {
Selim Cinek850a8542015-11-11 11:48:36 -05003733 RemoteViews result = null;
Selim Cinek593610c2016-02-16 18:42:57 -08003734 if (mN.bigContentView != null
3735 && (mStyle == null || !mStyle.displayCustomViewInline())) {
Julia Reynolds089e3e42015-11-18 09:59:57 -05003736 return mN.bigContentView;
3737 } else if (mStyle != null) {
Selim Cinek850a8542015-11-11 11:48:36 -05003738 result = mStyle.makeBigContentView();
Selim Cinek90dcf6d2015-11-18 20:24:13 -08003739 hideLine1Text(result);
Selim Cinekcc10bfb2016-02-10 16:24:21 -08003740 } else if (mActions.size() != 0) {
3741 result = applyStandardTemplateWithActions(getBigBaseLayoutResource());
Selim Cinek850a8542015-11-11 11:48:36 -05003742 }
3743 adaptNotificationHeaderForBigContentView(result);
3744 return result;
3745 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003746
Selim Cinekeaa29ca2015-11-23 13:51:13 -08003747 /**
3748 * Construct a RemoteViews for the final notification header only
3749 *
3750 * @hide
3751 */
3752 public RemoteViews makeNotificationHeader() {
3753 RemoteViews header = new BuilderRemoteViews(mContext.getApplicationInfo(),
3754 R.layout.notification_template_header);
3755 resetNotificationHeader(header);
Adrian Roos487374f2017-01-11 15:48:14 -08003756 bindNotificationHeader(header, false /* ambient */);
Selim Cinekeaa29ca2015-11-23 13:51:13 -08003757 return header;
3758 }
3759
Adrian Roos487374f2017-01-11 15:48:14 -08003760 /**
3761 * Construct a RemoteViews for the ambient version of the notification.
3762 *
3763 * @hide
3764 */
3765 public RemoteViews makeAmbientNotification() {
3766 RemoteViews ambient = applyStandardTemplateWithActions(
3767 R.layout.notification_template_material_ambient,
3768 mParams.reset().fillTextsFrom(this).hasProgress(false).ambient(true));
3769 return ambient;
3770 }
3771
Selim Cinek29603462015-11-17 19:04:39 -08003772 private void hideLine1Text(RemoteViews result) {
Selim Cinekcc10bfb2016-02-10 16:24:21 -08003773 if (result != null) {
3774 result.setViewVisibility(R.id.text_line_1, View.GONE);
3775 }
Selim Cinek29603462015-11-17 19:04:39 -08003776 }
3777
Selim Cinek850a8542015-11-11 11:48:36 -05003778 private void adaptNotificationHeaderForBigContentView(RemoteViews result) {
Selim Cinekcc10bfb2016-02-10 16:24:21 -08003779 if (result != null) {
3780 result.setBoolean(R.id.notification_header, "setExpanded", true);
3781 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003782 }
3783
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003784 /**
3785 * Construct a RemoteViews for the final heads-up notification layout.
3786 */
Julia Reynolds3b848122016-02-26 10:45:32 -05003787 public RemoteViews createHeadsUpContentView() {
Selim Cinek593610c2016-02-16 18:42:57 -08003788 if (mN.headsUpContentView != null
3789 && (mStyle == null || !mStyle.displayCustomViewInline())) {
Julia Reynolds089e3e42015-11-18 09:59:57 -05003790 return mN.headsUpContentView;
3791 } else if (mStyle != null) {
3792 final RemoteViews styleView = mStyle.makeHeadsUpContentView();
3793 if (styleView != null) {
3794 return styleView;
3795 }
3796 } else if (mActions.size() == 0) {
3797 return null;
3798 }
3799
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003800 return applyStandardTemplateWithActions(getBigBaseLayoutResource());
Chris Wren8fd39ec2014-02-27 17:43:26 -05003801 }
3802
Selim Cinek624c02db2015-12-14 21:00:02 -08003803 /**
3804 * Construct a RemoteViews for the display in public contexts like on the lockscreen.
3805 *
3806 * @hide
3807 */
3808 public RemoteViews makePublicContentView() {
3809 if (mN.publicVersion != null) {
3810 final Builder builder = recoverBuilder(mContext, mN.publicVersion);
Julia Reynolds3b848122016-02-26 10:45:32 -05003811 return builder.createContentView();
Selim Cinek624c02db2015-12-14 21:00:02 -08003812 }
3813 Bundle savedBundle = mN.extras;
3814 Style style = mStyle;
3815 mStyle = null;
3816 Icon largeIcon = mN.mLargeIcon;
3817 mN.mLargeIcon = null;
Selim Cinek279fa862016-06-14 10:57:25 -07003818 Bitmap largeIconLegacy = mN.largeIcon;
3819 mN.largeIcon = null;
Selim Cinek624c02db2015-12-14 21:00:02 -08003820 Bundle publicExtras = new Bundle();
3821 publicExtras.putBoolean(EXTRA_SHOW_WHEN,
3822 savedBundle.getBoolean(EXTRA_SHOW_WHEN));
3823 publicExtras.putBoolean(EXTRA_SHOW_CHRONOMETER,
3824 savedBundle.getBoolean(EXTRA_SHOW_CHRONOMETER));
Adrian Roos96b7e202016-05-17 13:50:38 -07003825 publicExtras.putBoolean(EXTRA_CHRONOMETER_COUNT_DOWN,
3826 savedBundle.getBoolean(EXTRA_CHRONOMETER_COUNT_DOWN));
Selim Cinek624c02db2015-12-14 21:00:02 -08003827 publicExtras.putCharSequence(EXTRA_TITLE,
3828 mContext.getString(R.string.notification_hidden_text));
3829 mN.extras = publicExtras;
3830 final RemoteViews publicView = applyStandardTemplate(getBaseLayoutResource());
3831 mN.extras = savedBundle;
3832 mN.mLargeIcon = largeIcon;
Selim Cinek279fa862016-06-14 10:57:25 -07003833 mN.largeIcon = largeIconLegacy;
Selim Cinek624c02db2015-12-14 21:00:02 -08003834 mStyle = style;
3835 return publicView;
3836 }
3837
3838
Chris Wren8fd39ec2014-02-27 17:43:26 -05003839
Selim Cinek06e9e1f2016-07-08 17:14:16 -07003840 private RemoteViews generateActionButton(Action action, boolean emphazisedMode,
Adrian Roos487374f2017-01-11 15:48:14 -08003841 boolean oddAction, boolean ambient) {
Daniel Sandler8680bf82012-05-15 16:52:52 -04003842 final boolean tombstone = (action.actionIntent == null);
Selim Cinekf33b1112015-07-15 17:45:11 -07003843 RemoteViews button = new BuilderRemoteViews(mContext.getApplicationInfo(),
Selim Cinek06e9e1f2016-07-08 17:14:16 -07003844 emphazisedMode ? getEmphasizedActionLayoutResource()
3845 : tombstone ? getActionTombstoneLayoutResource()
3846 : getActionLayoutResource());
Daniel Sandler8680bf82012-05-15 16:52:52 -04003847 if (!tombstone) {
Daniel Sandlere5518842012-05-10 16:20:40 -04003848 button.setOnClickPendingIntent(R.id.action0, action.actionIntent);
Daniel Sandlere5518842012-05-10 16:20:40 -04003849 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003850 button.setContentDescription(R.id.action0, action.title);
Adrian Roosfe84e1f2015-11-04 15:55:39 -08003851 if (action.mRemoteInputs != null) {
3852 button.setRemoteInputs(R.id.action0, action.mRemoteInputs);
3853 }
Selim Cinek06e9e1f2016-07-08 17:14:16 -07003854 if (emphazisedMode) {
Selim Cinek981962e2016-07-20 20:41:58 -07003855 // change the background bgColor
3856 int bgColor = mContext.getColor(oddAction ? R.color.notification_action_list
3857 : R.color.notification_action_list_dark);
3858 button.setDrawableParameters(R.id.button_holder, true, -1, bgColor,
Selim Cinek06e9e1f2016-07-08 17:14:16 -07003859 PorterDuff.Mode.SRC_ATOP, -1);
Selim Cinek981962e2016-07-20 20:41:58 -07003860 CharSequence title = action.title;
3861 ColorStateList[] outResultColor = null;
3862 if (isLegacy()) {
3863 title = clearColorSpans(title);
3864 } else {
3865 outResultColor = new ColorStateList[1];
3866 title = ensureColorSpanContrast(title, bgColor, outResultColor);
3867 }
3868 button.setTextViewText(R.id.action0, title);
3869 if (outResultColor != null && outResultColor[0] != null) {
3870 // We need to set the text color as well since changing a text to uppercase
3871 // clears its spans.
3872 button.setTextColor(R.id.action0, outResultColor[0]);
3873 } else if (mN.color != COLOR_DEFAULT) {
3874 button.setTextColor(R.id.action0,resolveContrastColor());
3875 }
Selim Cinek06e9e1f2016-07-08 17:14:16 -07003876 } else {
Selim Cinek981962e2016-07-20 20:41:58 -07003877 button.setTextViewText(R.id.action0, processLegacyText(action.title));
Selim Cinek06e9e1f2016-07-08 17:14:16 -07003878 if (mN.color != COLOR_DEFAULT) {
Adrian Roos487374f2017-01-11 15:48:14 -08003879 button.setTextColor(R.id.action0,
3880 ambient ? resolveAmbientColor() : resolveContrastColor());
Selim Cinek06e9e1f2016-07-08 17:14:16 -07003881 }
Adrian Roosfe84e1f2015-11-04 15:55:39 -08003882 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003883 return button;
3884 }
3885
Joe Onoratocb109a02011-01-18 17:57:41 -08003886 /**
Selim Cinek981962e2016-07-20 20:41:58 -07003887 * Clears all color spans of a text
3888 * @param charSequence the input text
3889 * @return the same text but without color spans
3890 */
3891 private CharSequence clearColorSpans(CharSequence charSequence) {
3892 if (charSequence instanceof Spanned) {
3893 Spanned ss = (Spanned) charSequence;
3894 Object[] spans = ss.getSpans(0, ss.length(), Object.class);
3895 SpannableStringBuilder builder = new SpannableStringBuilder(ss.toString());
3896 for (Object span : spans) {
3897 Object resultSpan = span;
3898 if (resultSpan instanceof CharacterStyle) {
3899 resultSpan = ((CharacterStyle) span).getUnderlying();
3900 }
3901 if (resultSpan instanceof TextAppearanceSpan) {
3902 TextAppearanceSpan originalSpan = (TextAppearanceSpan) resultSpan;
3903 if (originalSpan.getTextColor() != null) {
3904 resultSpan = new TextAppearanceSpan(
3905 originalSpan.getFamily(),
3906 originalSpan.getTextStyle(),
3907 originalSpan.getTextSize(),
3908 null,
3909 originalSpan.getLinkTextColor());
3910 }
3911 } else if (resultSpan instanceof ForegroundColorSpan
3912 || (resultSpan instanceof BackgroundColorSpan)) {
3913 continue;
3914 } else {
3915 resultSpan = span;
3916 }
3917 builder.setSpan(resultSpan, ss.getSpanStart(span), ss.getSpanEnd(span),
3918 ss.getSpanFlags(span));
3919 }
3920 return builder;
3921 }
3922 return charSequence;
3923 }
3924
3925 /**
3926 * Ensures contrast on color spans against a background color. also returns the color of the
3927 * text if a span was found that spans over the whole text.
3928 *
3929 * @param charSequence the charSequence on which the spans are
3930 * @param background the background color to ensure the contrast against
3931 * @param outResultColor an array in which a color will be returned as the first element if
3932 * there exists a full length color span.
3933 * @return the contrasted charSequence
3934 */
3935 private CharSequence ensureColorSpanContrast(CharSequence charSequence, int background,
3936 ColorStateList[] outResultColor) {
3937 if (charSequence instanceof Spanned) {
3938 Spanned ss = (Spanned) charSequence;
3939 Object[] spans = ss.getSpans(0, ss.length(), Object.class);
3940 SpannableStringBuilder builder = new SpannableStringBuilder(ss.toString());
3941 for (Object span : spans) {
3942 Object resultSpan = span;
3943 int spanStart = ss.getSpanStart(span);
3944 int spanEnd = ss.getSpanEnd(span);
3945 boolean fullLength = (spanEnd - spanStart) == charSequence.length();
3946 if (resultSpan instanceof CharacterStyle) {
3947 resultSpan = ((CharacterStyle) span).getUnderlying();
3948 }
3949 if (resultSpan instanceof TextAppearanceSpan) {
3950 TextAppearanceSpan originalSpan = (TextAppearanceSpan) resultSpan;
3951 ColorStateList textColor = originalSpan.getTextColor();
3952 if (textColor != null) {
3953 int[] colors = textColor.getColors();
3954 int[] newColors = new int[colors.length];
3955 for (int i = 0; i < newColors.length; i++) {
3956 newColors[i] = NotificationColorUtil.ensureLargeTextContrast(
3957 colors[i], background);
3958 }
3959 textColor = new ColorStateList(textColor.getStates().clone(),
3960 newColors);
3961 resultSpan = new TextAppearanceSpan(
3962 originalSpan.getFamily(),
3963 originalSpan.getTextStyle(),
3964 originalSpan.getTextSize(),
3965 textColor,
3966 originalSpan.getLinkTextColor());
3967 if (fullLength) {
3968 outResultColor[0] = new ColorStateList(
3969 textColor.getStates().clone(), newColors);
3970 }
3971 }
3972 } else if (resultSpan instanceof ForegroundColorSpan) {
3973 ForegroundColorSpan originalSpan = (ForegroundColorSpan) resultSpan;
3974 int foregroundColor = originalSpan.getForegroundColor();
3975 foregroundColor = NotificationColorUtil.ensureLargeTextContrast(
3976 foregroundColor, background);
3977 resultSpan = new ForegroundColorSpan(foregroundColor);
3978 if (fullLength) {
3979 outResultColor[0] = ColorStateList.valueOf(foregroundColor);
3980 }
3981 } else {
3982 resultSpan = span;
3983 }
3984
3985 builder.setSpan(resultSpan, spanStart, spanEnd, ss.getSpanFlags(span));
3986 }
3987 return builder;
3988 }
3989 return charSequence;
3990 }
3991
3992 /**
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003993 * @return Whether we are currently building a notification from a legacy (an app that
Alan Viverette3cb07a462014-06-06 14:19:53 -07003994 * doesn't create material notifications by itself) app.
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003995 */
3996 private boolean isLegacy() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003997 return getColorUtil() != null;
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003998 }
3999
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004000 private CharSequence processLegacyText(CharSequence charSequence) {
4001 if (isLegacy()) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004002 return getColorUtil().invertCharSequenceColors(charSequence);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004003 } else {
4004 return charSequence;
4005 }
4006 }
4007
Dan Sandler26e81cf2014-05-06 10:01:27 -04004008 /**
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004009 * Apply any necessariy colors to the small icon
Dan Sandler26e81cf2014-05-06 10:01:27 -04004010 */
Adrian Roos487374f2017-01-11 15:48:14 -08004011 private void processSmallIconColor(Icon smallIcon, RemoteViews contentView,
4012 boolean ambient) {
Selim Cinekea4bef72015-12-02 15:51:10 -08004013 boolean colorable = !isLegacy() || getColorUtil().isGrayscaleIcon(mContext, smallIcon);
Adrian Roos487374f2017-01-11 15:48:14 -08004014 int color = ambient ? resolveAmbientColor() : resolveContrastColor();
Selim Cinekea4bef72015-12-02 15:51:10 -08004015 if (colorable) {
Adrian Roos487374f2017-01-11 15:48:14 -08004016 contentView.setDrawableParameters(R.id.icon, false, -1, color,
Jorim Jaggi92df1f22014-12-16 19:44:41 +01004017 PorterDuff.Mode.SRC_ATOP, -1);
Selim Cinekea4bef72015-12-02 15:51:10 -08004018
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004019 }
Selim Cinekea4bef72015-12-02 15:51:10 -08004020 contentView.setInt(R.id.notification_header, "setOriginalIconColor",
Adrian Roos487374f2017-01-11 15:48:14 -08004021 colorable ? color : NotificationHeaderView.NO_COLOR);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004022 }
4023
Dan Sandler26e81cf2014-05-06 10:01:27 -04004024 /**
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004025 * Make the largeIcon dark if it's a fake smallIcon (that is,
Dan Sandler26e81cf2014-05-06 10:01:27 -04004026 * if it's grayscale).
4027 */
4028 // TODO: also check bounds, transparency, that sort of thing.
Dan Sandlerd63f9322015-05-06 15:18:49 -04004029 private void processLargeLegacyIcon(Icon largeIcon, RemoteViews contentView) {
4030 if (largeIcon != null && isLegacy()
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004031 && getColorUtil().isGrayscaleIcon(mContext, largeIcon)) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004032 // resolve color will fall back to the default when legacy
Adrian Roos4ff3b122016-02-01 12:26:13 -08004033 contentView.setDrawableParameters(R.id.icon, false, -1, resolveContrastColor(),
Dan Sandler190d58d2014-05-15 09:33:39 -04004034 PorterDuff.Mode.SRC_ATOP, -1);
Jorim Jaggi92df1f22014-12-16 19:44:41 +01004035 }
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004036 }
4037
Julia Reynolds10ee1fc2015-11-09 11:04:55 -05004038 private void sanitizeColor() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004039 if (mN.color != COLOR_DEFAULT) {
4040 mN.color |= 0xFF000000; // no alpha for custom colors
Jorim Jaggi74419312014-06-10 20:57:21 +02004041 }
Jorim Jaggi74419312014-06-10 20:57:21 +02004042 }
4043
Adrian Roos4ff3b122016-02-01 12:26:13 -08004044 int resolveContrastColor() {
4045 if (mCachedContrastColorIsFor == mN.color && mCachedContrastColor != COLOR_INVALID) {
4046 return mCachedContrastColor;
Dan Sandler26e81cf2014-05-06 10:01:27 -04004047 }
Adrian Roos4ff3b122016-02-01 12:26:13 -08004048 final int contrasted = NotificationColorUtil.resolveContrastColor(mContext, mN.color);
4049
4050 mCachedContrastColorIsFor = mN.color;
4051 return mCachedContrastColor = contrasted;
Dan Sandler26e81cf2014-05-06 10:01:27 -04004052 }
4053
Adrian Roos487374f2017-01-11 15:48:14 -08004054 int resolveAmbientColor() {
4055 if (mCachedAmbientColorIsFor == mN.color && mCachedAmbientColorIsFor != COLOR_INVALID) {
4056 return mCachedAmbientColor;
4057 }
4058 final int contrasted = NotificationColorUtil.resolveAmbientColor(mContext, mN.color);
4059
4060 mCachedAmbientColorIsFor = mN.color;
4061 return mCachedAmbientColor = contrasted;
4062 }
4063
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004064 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004065 * Apply the unstyled operations and return a new {@link Notification} object.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04004066 * @hide
Joe Onoratocb109a02011-01-18 17:57:41 -08004067 */
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04004068 public Notification buildUnstyled() {
Daniel Sandlera0a938c2012-03-15 08:42:37 -04004069 if (mActions.size() > 0) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004070 mN.actions = new Action[mActions.size()];
4071 mActions.toArray(mN.actions);
Daniel Sandlera0a938c2012-03-15 08:42:37 -04004072 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004073 if (!mPersonList.isEmpty()) {
4074 mN.extras.putStringArray(EXTRA_PEOPLE,
4075 mPersonList.toArray(new String[mPersonList.size()]));
Dan Sandler0bf2ed82013-12-21 23:33:41 -06004076 }
Selim Cinek247fa012016-02-18 09:50:48 -08004077 if (mN.bigContentView != null || mN.contentView != null
4078 || mN.headsUpContentView != null) {
4079 mN.extras.putBoolean(EXTRA_CONTAINS_CUSTOM_VIEW, true);
4080 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004081 return mN;
Joe Onorato46439ce2010-11-19 13:56:21 -08004082 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004083
Julia Reynolds3b848122016-02-26 10:45:32 -05004084 /**
4085 * Creates a Builder from an existing notification so further changes can be made.
4086 * @param context The context for your application / activity.
4087 * @param n The notification to create a Builder from.
4088 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004089 public static Notification.Builder recoverBuilder(Context context, Notification n) {
Christoph Studer4600f9b2014-07-22 22:44:43 +02004090 // Re-create notification context so we can access app resources.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004091 ApplicationInfo applicationInfo = n.extras.getParcelable(
4092 EXTRA_BUILDER_APPLICATION_INFO);
Christoph Studer4600f9b2014-07-22 22:44:43 +02004093 Context builderContext;
Julia Reynoldsda303542015-11-23 14:00:20 -05004094 if (applicationInfo != null) {
4095 try {
4096 builderContext = context.createApplicationContext(applicationInfo,
4097 Context.CONTEXT_RESTRICTED);
4098 } catch (NameNotFoundException e) {
4099 Log.e(TAG, "ApplicationInfo " + applicationInfo + " not found");
4100 builderContext = context; // try with our context
4101 }
4102 } else {
4103 builderContext = context; // try with given context
Christoph Studer4600f9b2014-07-22 22:44:43 +02004104 }
4105
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004106 return new Builder(builderContext, n);
Christoph Studer4600f9b2014-07-22 22:44:43 +02004107 }
4108
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004109 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004110 * @deprecated Use {@link #build()} instead.
4111 */
4112 @Deprecated
4113 public Notification getNotification() {
4114 return build();
4115 }
4116
4117 /**
4118 * Combine all of the options that have been set and return a new {@link Notification}
4119 * object.
4120 */
4121 public Notification build() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004122 // first, add any extras from the calling code
4123 if (mUserExtras != null) {
4124 mN.extras = getAllExtras();
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07004125 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004126
Selim Cinekb85f36fd2016-04-20 18:46:36 -07004127 mN.creationTime = System.currentTimeMillis();
4128
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004129 // lazy stuff from mContext; see comment in Builder(Context, Notification)
Julia Reynoldsda303542015-11-23 14:00:20 -05004130 Notification.addFieldsFromContext(mContext, mN);
Christoph Studer943aa672014-08-03 20:31:16 +02004131
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004132 buildUnstyled();
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004133
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004134 if (mStyle != null) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004135 mStyle.buildStyled(mN);
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004136 }
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004137
Adrian Roos5081c0d2016-02-26 16:04:19 -08004138 if (mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N
4139 && (mStyle == null || !mStyle.displayCustomViewInline())) {
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004140 if (mN.contentView == null) {
Julia Reynolds3b848122016-02-26 10:45:32 -05004141 mN.contentView = createContentView();
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004142 mN.extras.putInt(EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT,
4143 mN.contentView.getSequenceNumber());
4144 }
4145 if (mN.bigContentView == null) {
Julia Reynolds3b848122016-02-26 10:45:32 -05004146 mN.bigContentView = createBigContentView();
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004147 if (mN.bigContentView != null) {
4148 mN.extras.putInt(EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT,
4149 mN.bigContentView.getSequenceNumber());
4150 }
4151 }
4152 if (mN.headsUpContentView == null) {
Julia Reynolds3b848122016-02-26 10:45:32 -05004153 mN.headsUpContentView = createHeadsUpContentView();
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004154 if (mN.headsUpContentView != null) {
4155 mN.extras.putInt(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT,
4156 mN.headsUpContentView.getSequenceNumber());
4157 }
4158 }
4159 }
4160
Julia Reynolds4c0c2022016-02-02 15:11:59 -05004161 if ((mN.defaults & DEFAULT_LIGHTS) != 0) {
4162 mN.flags |= FLAG_SHOW_LIGHTS;
4163 }
4164
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004165 return mN;
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004166 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05004167
4168 /**
4169 * Apply this Builder to an existing {@link Notification} object.
4170 *
4171 * @hide
4172 */
4173 public Notification buildInto(Notification n) {
Daniel Sandler1a497d32013-04-18 14:52:45 -04004174 build().cloneInto(n, true);
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05004175 return n;
4176 }
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004177
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004178 /**
Adrian Roos184bfe022016-03-03 13:41:44 -08004179 * Removes RemoteViews that were created for compatibility from {@param n}, if they did not
4180 * change.
4181 *
4182 * @return {@param n}, if no stripping is needed, otherwise a stripped clone of {@param n}.
4183 *
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004184 * @hide
4185 */
Adrian Roos184bfe022016-03-03 13:41:44 -08004186 public static Notification maybeCloneStrippedForDelivery(Notification n) {
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004187 String templateClass = n.extras.getString(EXTRA_TEMPLATE);
Adrian Roos184bfe022016-03-03 13:41:44 -08004188
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004189 // Only strip views for known Styles because we won't know how to
4190 // re-create them otherwise.
Adrian Roos184bfe022016-03-03 13:41:44 -08004191 if (!TextUtils.isEmpty(templateClass)
4192 && getNotificationStyleClass(templateClass) == null) {
4193 return n;
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004194 }
Adrian Roos184bfe022016-03-03 13:41:44 -08004195
4196 // Only strip unmodified BuilderRemoteViews.
4197 boolean stripContentView = n.contentView instanceof BuilderRemoteViews &&
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004198 n.extras.getInt(EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT, -1) ==
Adrian Roos184bfe022016-03-03 13:41:44 -08004199 n.contentView.getSequenceNumber();
4200 boolean stripBigContentView = n.bigContentView instanceof BuilderRemoteViews &&
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004201 n.extras.getInt(EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT, -1) ==
Adrian Roos184bfe022016-03-03 13:41:44 -08004202 n.bigContentView.getSequenceNumber();
4203 boolean stripHeadsUpContentView = n.headsUpContentView instanceof BuilderRemoteViews &&
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004204 n.extras.getInt(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT, -1) ==
Adrian Roos184bfe022016-03-03 13:41:44 -08004205 n.headsUpContentView.getSequenceNumber();
4206
4207 // Nothing to do here, no need to clone.
4208 if (!stripContentView && !stripBigContentView && !stripHeadsUpContentView) {
4209 return n;
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004210 }
Adrian Roos184bfe022016-03-03 13:41:44 -08004211
4212 Notification clone = n.clone();
4213 if (stripContentView) {
4214 clone.contentView = null;
4215 clone.extras.remove(EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT);
4216 }
4217 if (stripBigContentView) {
4218 clone.bigContentView = null;
4219 clone.extras.remove(EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT);
4220 }
4221 if (stripHeadsUpContentView) {
4222 clone.headsUpContentView = null;
4223 clone.extras.remove(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT);
4224 }
4225 return clone;
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004226 }
4227
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004228 private int getBaseLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07004229 return R.layout.notification_template_material_base;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004230 }
4231
4232 private int getBigBaseLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07004233 return R.layout.notification_template_material_big_base;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004234 }
4235
4236 private int getBigPictureLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07004237 return R.layout.notification_template_material_big_picture;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004238 }
4239
4240 private int getBigTextLayoutResource() {
Jorim Jaggi445d3c02014-08-19 22:33:42 +02004241 return R.layout.notification_template_material_big_text;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004242 }
4243
4244 private int getInboxLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07004245 return R.layout.notification_template_material_inbox;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004246 }
4247
Adrian Roosc1a80b02016-04-05 14:54:55 -07004248 private int getMessagingLayoutResource() {
4249 return R.layout.notification_template_material_messaging;
4250 }
4251
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004252 private int getActionLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07004253 return R.layout.notification_material_action;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004254 }
4255
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004256 private int getEmphasizedActionLayoutResource() {
4257 return R.layout.notification_material_action_emphasized;
4258 }
4259
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004260 private int getActionTombstoneLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07004261 return R.layout.notification_material_action_tombstone;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004262 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004263 }
4264
Selim Cinek279fa862016-06-14 10:57:25 -07004265 private boolean hasLargeIcon() {
4266 return mLargeIcon != null || largeIcon != null;
4267 }
4268
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004269 /**
Selim Cinekc2c0b042016-05-18 17:13:46 -07004270 * @return true if the notification will show the time; false otherwise
Selim Cinekb85f36fd2016-04-20 18:46:36 -07004271 * @hide
4272 */
Selim Cinekc2c0b042016-05-18 17:13:46 -07004273 public boolean showsTime() {
Selim Cinekb85f36fd2016-04-20 18:46:36 -07004274 return when != 0 && extras.getBoolean(EXTRA_SHOW_WHEN);
4275 }
4276
4277 /**
Selim Cinekc2c0b042016-05-18 17:13:46 -07004278 * @return true if the notification will show a chronometer; false otherwise
4279 * @hide
4280 */
4281 public boolean showsChronometer() {
4282 return when != 0 && extras.getBoolean(EXTRA_SHOW_CHRONOMETER);
4283 }
4284
4285 /**
Julia Reynolds4a02afb2016-12-13 13:39:52 -05004286 * @hide
4287 */
4288 @SystemApi
4289 public static Class<? extends Style> getNotificationStyleClass(String templateClass) {
4290 Class<? extends Style>[] classes = new Class[] {
4291 BigTextStyle.class, BigPictureStyle.class, InboxStyle.class, MediaStyle.class,
4292 DecoratedCustomViewStyle.class, DecoratedMediaCustomViewStyle.class,
4293 MessagingStyle.class };
4294 for (Class<? extends Style> innerClass : classes) {
4295 if (templateClass.equals(innerClass.getName())) {
4296 return innerClass;
4297 }
4298 }
4299 return null;
4300 }
4301
4302 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004303 * An object that can apply a rich notification style to a {@link Notification.Builder}
4304 * object.
4305 */
Griff Hazendfcb0802014-02-11 12:00:00 -08004306 public static abstract class Style {
Chris Wrend6297db2012-05-03 16:20:13 -04004307 private CharSequence mBigContentTitle;
Jorim Jaggi457a10d2014-09-08 16:18:23 +02004308
4309 /**
4310 * @hide
4311 */
4312 protected CharSequence mSummaryText = null;
4313
4314 /**
4315 * @hide
4316 */
4317 protected boolean mSummaryTextSet = false;
Chris Wrend6297db2012-05-03 16:20:13 -04004318
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004319 protected Builder mBuilder;
4320
Chris Wrend6297db2012-05-03 16:20:13 -04004321 /**
4322 * Overrides ContentTitle in the big form of the template.
4323 * This defaults to the value passed to setContentTitle().
4324 */
4325 protected void internalSetBigContentTitle(CharSequence title) {
4326 mBigContentTitle = title;
4327 }
4328
4329 /**
4330 * Set the first line of text after the detail section in the big form of the template.
4331 */
4332 protected void internalSetSummaryText(CharSequence cs) {
4333 mSummaryText = cs;
Daniel Sandler619738c2012-06-07 16:33:08 -04004334 mSummaryTextSet = true;
Chris Wrend6297db2012-05-03 16:20:13 -04004335 }
4336
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004337 public void setBuilder(Builder builder) {
4338 if (mBuilder != builder) {
4339 mBuilder = builder;
Daniel Sandlerc08dea22012-06-28 08:35:24 -07004340 if (mBuilder != null) {
4341 mBuilder.setStyle(this);
4342 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004343 }
4344 }
4345
Chris Wrend6297db2012-05-03 16:20:13 -04004346 protected void checkBuilder() {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004347 if (mBuilder == null) {
4348 throw new IllegalArgumentException("Style requires a valid Builder object");
4349 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004350 }
Chris Wrend6297db2012-05-03 16:20:13 -04004351
4352 protected RemoteViews getStandardView(int layoutId) {
4353 checkBuilder();
4354
Christoph Studer4600f9b2014-07-22 22:44:43 +02004355 // Nasty.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004356 CharSequence oldBuilderContentTitle =
4357 mBuilder.getAllExtras().getCharSequence(EXTRA_TITLE);
Chris Wrend6297db2012-05-03 16:20:13 -04004358 if (mBigContentTitle != null) {
4359 mBuilder.setContentTitle(mBigContentTitle);
4360 }
4361
Chris Wrend6297db2012-05-03 16:20:13 -04004362 RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(layoutId);
4363
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004364 mBuilder.getAllExtras().putCharSequence(EXTRA_TITLE, oldBuilderContentTitle);
Christoph Studer4600f9b2014-07-22 22:44:43 +02004365
Chris Wrend6297db2012-05-03 16:20:13 -04004366 if (mBigContentTitle != null && mBigContentTitle.equals("")) {
4367 contentView.setViewVisibility(R.id.line1, View.GONE);
Chris Wren67dc9a02012-05-16 01:03:20 -04004368 } else {
4369 contentView.setViewVisibility(R.id.line1, View.VISIBLE);
Chris Wrend6297db2012-05-03 16:20:13 -04004370 }
4371
Chris Wrend6297db2012-05-03 16:20:13 -04004372 return contentView;
4373 }
4374
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004375 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004376 * Construct a Style-specific RemoteViews for the final 1U notification layout.
4377 * The default implementation has nothing additional to add.
4378 * @hide
4379 */
4380 public RemoteViews makeContentView() {
4381 return null;
4382 }
4383
4384 /**
4385 * Construct a Style-specific RemoteViews for the final big notification layout.
4386 * @hide
4387 */
4388 public RemoteViews makeBigContentView() {
4389 return null;
4390 }
4391
4392 /**
4393 * Construct a Style-specific RemoteViews for the final HUN layout.
4394 * @hide
4395 */
4396 public RemoteViews makeHeadsUpContentView() {
4397 return null;
4398 }
4399
4400 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004401 * Apply any style-specific extras to this notification before shipping it out.
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004402 * @hide
4403 */
4404 public void addExtras(Bundle extras) {
4405 if (mSummaryTextSet) {
4406 extras.putCharSequence(EXTRA_SUMMARY_TEXT, mSummaryText);
4407 }
4408 if (mBigContentTitle != null) {
4409 extras.putCharSequence(EXTRA_TITLE_BIG, mBigContentTitle);
4410 }
Chris Wren91ad5632013-06-05 15:05:57 -04004411 extras.putString(EXTRA_TEMPLATE, this.getClass().getName());
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004412 }
4413
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04004414 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004415 * Reconstruct the internal state of this Style object from extras.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04004416 * @hide
4417 */
Christoph Studer4600f9b2014-07-22 22:44:43 +02004418 protected void restoreFromExtras(Bundle extras) {
4419 if (extras.containsKey(EXTRA_SUMMARY_TEXT)) {
4420 mSummaryText = extras.getCharSequence(EXTRA_SUMMARY_TEXT);
4421 mSummaryTextSet = true;
4422 }
4423 if (extras.containsKey(EXTRA_TITLE_BIG)) {
4424 mBigContentTitle = extras.getCharSequence(EXTRA_TITLE_BIG);
4425 }
4426 }
4427
4428
4429 /**
4430 * @hide
4431 */
4432 public Notification buildStyled(Notification wip) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004433 addExtras(wip.extras);
Christoph Studer4600f9b2014-07-22 22:44:43 +02004434 return wip;
4435 }
4436
Daniel Sandler0ec46202015-06-24 01:27:05 -04004437 /**
4438 * @hide
4439 */
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07004440 public void purgeResources() {}
4441
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04004442 /**
4443 * Calls {@link android.app.Notification.Builder#build()} on the Builder this Style is
4444 * attached to.
4445 *
4446 * @return the fully constructed Notification.
4447 */
4448 public Notification build() {
4449 checkBuilder();
4450 return mBuilder.build();
4451 }
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004452
4453 /**
4454 * @hide
4455 * @return true if the style positions the progress bar on the second line; false if the
4456 * style hides the progress bar
4457 */
4458 protected boolean hasProgress() {
4459 return true;
4460 }
Selim Cinek03d0d652015-11-13 13:18:09 -05004461
4462 /**
4463 * @hide
4464 * @return Whether we should put the summary be put into the notification header
4465 */
4466 public boolean hasSummaryInHeader() {
4467 return true;
4468 }
Selim Cinek593610c2016-02-16 18:42:57 -08004469
4470 /**
4471 * @hide
4472 * @return Whether custom content views are displayed inline in the style
4473 */
4474 public boolean displayCustomViewInline() {
4475 return false;
4476 }
Joe Onorato46439ce2010-11-19 13:56:21 -08004477 }
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004478
4479 /**
Daniel Sandler4dfbe832012-04-11 14:51:46 -04004480 * Helper class for generating large-format notifications that include a large image attachment.
Joe Malin8d40d042012-11-05 11:36:40 -08004481 *
Robert Ly91c5ce32014-06-08 15:37:00 -07004482 * Here's how you'd set the <code>BigPictureStyle</code> on a notification:
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004483 * <pre class="prettyprint">
Robert Ly91c5ce32014-06-08 15:37:00 -07004484 * Notification notif = new Notification.Builder(mContext)
4485 * .setContentTitle(&quot;New photo from &quot; + sender.toString())
4486 * .setContentText(subject)
4487 * .setSmallIcon(R.drawable.new_post)
4488 * .setLargeIcon(aBitmap)
4489 * .setStyle(new Notification.BigPictureStyle()
4490 * .bigPicture(aBigBitmap))
4491 * .build();
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004492 * </pre>
Joe Malin8d40d042012-11-05 11:36:40 -08004493 *
Daniel Sandler4dfbe832012-04-11 14:51:46 -04004494 * @see Notification#bigContentView
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004495 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004496 public static class BigPictureStyle extends Style {
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004497 private Bitmap mPicture;
Dan Sandlerd63f9322015-05-06 15:18:49 -04004498 private Icon mBigLargeIcon;
Chris Wren3745a3d2012-05-22 15:11:52 -04004499 private boolean mBigLargeIconSet = false;
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004500
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004501 public BigPictureStyle() {
4502 }
4503
Adrian Roosf5faf9d2016-05-23 13:56:15 -07004504 /**
4505 * @deprecated use {@code BigPictureStyle()}.
4506 */
4507 @Deprecated
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004508 public BigPictureStyle(Builder builder) {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004509 setBuilder(builder);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004510 }
4511
Chris Wrend6297db2012-05-03 16:20:13 -04004512 /**
4513 * Overrides ContentTitle in the big form of the template.
4514 * This defaults to the value passed to setContentTitle().
4515 */
4516 public BigPictureStyle setBigContentTitle(CharSequence title) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04004517 internalSetBigContentTitle(safeCharSequence(title));
Chris Wrend6297db2012-05-03 16:20:13 -04004518 return this;
4519 }
4520
4521 /**
4522 * Set the first line of text after the detail section in the big form of the template.
4523 */
4524 public BigPictureStyle setSummaryText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04004525 internalSetSummaryText(safeCharSequence(cs));
Chris Wrend6297db2012-05-03 16:20:13 -04004526 return this;
4527 }
4528
Chris Wren0bd664d2012-08-01 13:56:56 -04004529 /**
4530 * Provide the bitmap to be used as the payload for the BigPicture notification.
4531 */
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004532 public BigPictureStyle bigPicture(Bitmap b) {
4533 mPicture = b;
4534 return this;
4535 }
4536
Chris Wren3745a3d2012-05-22 15:11:52 -04004537 /**
Chris Wren3745a3d2012-05-22 15:11:52 -04004538 * Override the large icon when the big notification is shown.
4539 */
4540 public BigPictureStyle bigLargeIcon(Bitmap b) {
Dan Sandlerd63f9322015-05-06 15:18:49 -04004541 return bigLargeIcon(b != null ? Icon.createWithBitmap(b) : null);
4542 }
4543
4544 /**
4545 * Override the large icon when the big notification is shown.
4546 */
4547 public BigPictureStyle bigLargeIcon(Icon icon) {
Chris Wren3745a3d2012-05-22 15:11:52 -04004548 mBigLargeIconSet = true;
Dan Sandlerd63f9322015-05-06 15:18:49 -04004549 mBigLargeIcon = icon;
Chris Wren3745a3d2012-05-22 15:11:52 -04004550 return this;
4551 }
4552
Riley Andrews0394a0c2015-11-03 23:36:52 -08004553 /** @hide */
4554 public static final int MIN_ASHMEM_BITMAP_SIZE = 128 * (1 << 10);
4555
Daniel Sandler0ec46202015-06-24 01:27:05 -04004556 /**
4557 * @hide
4558 */
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07004559 @Override
4560 public void purgeResources() {
4561 super.purgeResources();
Riley Andrews8cee7c12015-11-01 23:36:04 -08004562 if (mPicture != null &&
4563 mPicture.isMutable() &&
Riley Andrews0394a0c2015-11-03 23:36:52 -08004564 mPicture.getAllocationByteCount() >= MIN_ASHMEM_BITMAP_SIZE) {
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07004565 mPicture = mPicture.createAshmemBitmap();
4566 }
4567 if (mBigLargeIcon != null) {
4568 mBigLargeIcon.convertToAshmem();
4569 }
4570 }
Christoph Studer5c510ee2014-12-15 16:32:27 +01004571
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004572 /**
4573 * @hide
4574 */
4575 public RemoteViews makeBigContentView() {
4576 // Replace mN.mLargeIcon with mBigLargeIcon if mBigLargeIconSet
Christoph Studer5c510ee2014-12-15 16:32:27 +01004577 // This covers the following cases:
4578 // 1. mBigLargeIconSet -> mBigLargeIcon (null or non-null) applies, overrides
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004579 // mN.mLargeIcon
4580 // 2. !mBigLargeIconSet -> mN.mLargeIcon applies
Dan Sandlerd63f9322015-05-06 15:18:49 -04004581 Icon oldLargeIcon = null;
Selim Cineke99acb22016-08-04 12:55:48 -07004582 Bitmap largeIconLegacy = null;
Christoph Studer5c510ee2014-12-15 16:32:27 +01004583 if (mBigLargeIconSet) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004584 oldLargeIcon = mBuilder.mN.mLargeIcon;
4585 mBuilder.mN.mLargeIcon = mBigLargeIcon;
Selim Cineke99acb22016-08-04 12:55:48 -07004586 // The legacy largeIcon might not allow us to clear the image, as it's taken in
4587 // replacement if the other one is null. Because we're restoring these legacy icons
4588 // for old listeners, this is in general non-null.
4589 largeIconLegacy = mBuilder.mN.largeIcon;
4590 mBuilder.mN.largeIcon = null;
Christoph Studer5c510ee2014-12-15 16:32:27 +01004591 }
4592
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004593 RemoteViews contentView = getStandardView(mBuilder.getBigPictureLayoutResource());
Selim Cinek03d0d652015-11-13 13:18:09 -05004594 if (mSummaryTextSet) {
4595 contentView.setTextViewText(R.id.text, mBuilder.processLegacyText(mSummaryText));
Selim Cinekc848c3a2016-01-13 15:27:30 -08004596 contentView.setViewVisibility(R.id.text, View.VISIBLE);
Selim Cinek03d0d652015-11-13 13:18:09 -05004597 }
Selim Cinek279fa862016-06-14 10:57:25 -07004598 mBuilder.setContentMinHeight(contentView, mBuilder.mN.hasLargeIcon());
Selim Cinek53e64a42015-11-16 10:40:56 -08004599
Christoph Studer5c510ee2014-12-15 16:32:27 +01004600 if (mBigLargeIconSet) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004601 mBuilder.mN.mLargeIcon = oldLargeIcon;
Selim Cineke99acb22016-08-04 12:55:48 -07004602 mBuilder.mN.largeIcon = largeIconLegacy;
Christoph Studer5c510ee2014-12-15 16:32:27 +01004603 }
4604
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004605 contentView.setImageViewBitmap(R.id.big_picture, mPicture);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004606 return contentView;
4607 }
4608
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004609 /**
4610 * @hide
4611 */
4612 public void addExtras(Bundle extras) {
4613 super.addExtras(extras);
4614
4615 if (mBigLargeIconSet) {
4616 extras.putParcelable(EXTRA_LARGE_ICON_BIG, mBigLargeIcon);
4617 }
4618 extras.putParcelable(EXTRA_PICTURE, mPicture);
4619 }
4620
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04004621 /**
4622 * @hide
4623 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004624 @Override
Christoph Studer4600f9b2014-07-22 22:44:43 +02004625 protected void restoreFromExtras(Bundle extras) {
4626 super.restoreFromExtras(extras);
4627
4628 if (extras.containsKey(EXTRA_LARGE_ICON_BIG)) {
Christoph Studer5c510ee2014-12-15 16:32:27 +01004629 mBigLargeIconSet = true;
Christoph Studer4600f9b2014-07-22 22:44:43 +02004630 mBigLargeIcon = extras.getParcelable(EXTRA_LARGE_ICON_BIG);
Chris Wren3745a3d2012-05-22 15:11:52 -04004631 }
Christoph Studer4600f9b2014-07-22 22:44:43 +02004632 mPicture = extras.getParcelable(EXTRA_PICTURE);
4633 }
Selim Cinek03d0d652015-11-13 13:18:09 -05004634
4635 /**
4636 * @hide
4637 */
4638 @Override
4639 public boolean hasSummaryInHeader() {
4640 return false;
4641 }
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004642 }
4643
4644 /**
Daniel Sandler4dfbe832012-04-11 14:51:46 -04004645 * Helper class for generating large-format notifications that include a lot of text.
Joe Malin8d40d042012-11-05 11:36:40 -08004646 *
Robert Ly91c5ce32014-06-08 15:37:00 -07004647 * Here's how you'd set the <code>BigTextStyle</code> on a notification:
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004648 * <pre class="prettyprint">
Robert Ly91c5ce32014-06-08 15:37:00 -07004649 * Notification notif = new Notification.Builder(mContext)
4650 * .setContentTitle(&quot;New mail from &quot; + sender.toString())
4651 * .setContentText(subject)
4652 * .setSmallIcon(R.drawable.new_mail)
4653 * .setLargeIcon(aBitmap)
4654 * .setStyle(new Notification.BigTextStyle()
4655 * .bigText(aVeryLongString))
4656 * .build();
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004657 * </pre>
Joe Malin8d40d042012-11-05 11:36:40 -08004658 *
Daniel Sandler4dfbe832012-04-11 14:51:46 -04004659 * @see Notification#bigContentView
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004660 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004661 public static class BigTextStyle extends Style {
Jorim Jaggi457a10d2014-09-08 16:18:23 +02004662
4663 private static final int MAX_LINES = 13;
Selim Cinek3a2c4b92015-12-17 17:01:17 -08004664 private static final int LINES_CONSUMED_BY_ACTIONS = 4;
Jorim Jaggi457a10d2014-09-08 16:18:23 +02004665
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004666 private CharSequence mBigText;
4667
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004668 public BigTextStyle() {
4669 }
4670
Adrian Roosf5faf9d2016-05-23 13:56:15 -07004671 /**
4672 * @deprecated use {@code BigTextStyle()}.
4673 */
4674 @Deprecated
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004675 public BigTextStyle(Builder builder) {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004676 setBuilder(builder);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004677 }
4678
Chris Wrend6297db2012-05-03 16:20:13 -04004679 /**
4680 * Overrides ContentTitle in the big form of the template.
4681 * This defaults to the value passed to setContentTitle().
4682 */
4683 public BigTextStyle setBigContentTitle(CharSequence title) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04004684 internalSetBigContentTitle(safeCharSequence(title));
Chris Wrend6297db2012-05-03 16:20:13 -04004685 return this;
4686 }
4687
4688 /**
4689 * Set the first line of text after the detail section in the big form of the template.
4690 */
4691 public BigTextStyle setSummaryText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04004692 internalSetSummaryText(safeCharSequence(cs));
Chris Wrend6297db2012-05-03 16:20:13 -04004693 return this;
4694 }
4695
Chris Wren0bd664d2012-08-01 13:56:56 -04004696 /**
4697 * Provide the longer text to be displayed in the big form of the
4698 * template in place of the content text.
4699 */
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004700 public BigTextStyle bigText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04004701 mBigText = safeCharSequence(cs);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004702 return this;
4703 }
4704
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004705 /**
4706 * @hide
4707 */
4708 public void addExtras(Bundle extras) {
4709 super.addExtras(extras);
4710
Christoph Studer4600f9b2014-07-22 22:44:43 +02004711 extras.putCharSequence(EXTRA_BIG_TEXT, mBigText);
4712 }
4713
4714 /**
4715 * @hide
4716 */
4717 @Override
4718 protected void restoreFromExtras(Bundle extras) {
4719 super.restoreFromExtras(extras);
4720
4721 mBigText = extras.getCharSequence(EXTRA_BIG_TEXT);
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004722 }
4723
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004724 /**
4725 * @hide
4726 */
4727 public RemoteViews makeBigContentView() {
Christoph Studer4600f9b2014-07-22 22:44:43 +02004728
4729 // Nasty
Selim Cinek75998782016-04-26 10:39:17 -07004730 CharSequence text = mBuilder.getAllExtras().getCharSequence(EXTRA_TEXT);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004731 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, null);
Daniel Sandler916ad912012-06-13 12:17:07 -04004732
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004733 RemoteViews contentView = getStandardView(mBuilder.getBigTextLayoutResource());
Joe Malin8d40d042012-11-05 11:36:40 -08004734
Selim Cinek75998782016-04-26 10:39:17 -07004735 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, text);
Christoph Studer4600f9b2014-07-22 22:44:43 +02004736
Selim Cinek3a2c4b92015-12-17 17:01:17 -08004737 CharSequence bigTextText = mBuilder.processLegacyText(mBigText);
Selim Cinek75998782016-04-26 10:39:17 -07004738 if (TextUtils.isEmpty(bigTextText)) {
4739 // In case the bigtext is null / empty fall back to the normal text to avoid a weird
4740 // experience
4741 bigTextText = mBuilder.processLegacyText(text);
4742 }
Adrian Roosb1f427c2016-05-26 12:27:15 -07004743 applyBigTextContentView(mBuilder, contentView, bigTextText);
Selim Cinek4fb12d32015-11-19 18:10:48 -08004744
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004745 return contentView;
4746 }
4747
Adrian Roosb1f427c2016-05-26 12:27:15 -07004748 static void applyBigTextContentView(Builder builder,
4749 RemoteViews contentView, CharSequence bigTextText) {
4750 contentView.setTextViewText(R.id.big_text, bigTextText);
4751 contentView.setViewVisibility(R.id.big_text,
4752 TextUtils.isEmpty(bigTextText) ? View.GONE : View.VISIBLE);
4753 contentView.setInt(R.id.big_text, "setMaxLines", calculateMaxLines(builder));
Selim Cinek279fa862016-06-14 10:57:25 -07004754 contentView.setBoolean(R.id.big_text, "setHasImage", builder.mN.hasLargeIcon());
Adrian Roosb1f427c2016-05-26 12:27:15 -07004755 }
4756
4757 private static int calculateMaxLines(Builder builder) {
Jorim Jaggi457a10d2014-09-08 16:18:23 +02004758 int lineCount = MAX_LINES;
Adrian Roosb1f427c2016-05-26 12:27:15 -07004759 boolean hasActions = builder.mActions.size() > 0;
Jorim Jaggi457a10d2014-09-08 16:18:23 +02004760 if (hasActions) {
4761 lineCount -= LINES_CONSUMED_BY_ACTIONS;
4762 }
Jorim Jaggi457a10d2014-09-08 16:18:23 +02004763 return lineCount;
4764 }
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004765 }
Daniel Sandler879c5e02012-04-17 16:46:51 -04004766
4767 /**
Alex Hillsfc737de2016-03-23 17:33:02 -04004768 * Helper class for generating large-format notifications that include multiple back-and-forth
4769 * messages of varying types between any number of people.
4770 *
4771 * <br>
4772 * If the platform does not provide large-format notifications, this method has no effect. The
4773 * user will always see the normal notification view.
4774 * <br>
4775 * This class is a "rebuilder": It attaches to a Builder object and modifies its behavior, like
4776 * so:
4777 * <pre class="prettyprint">
4778 *
4779 * Notification noti = new Notification.Builder()
4780 * .setContentTitle(&quot;2 new messages wtih &quot; + sender.toString())
4781 * .setContentText(subject)
4782 * .setSmallIcon(R.drawable.new_message)
4783 * .setLargeIcon(aBitmap)
4784 * .setStyle(new Notification.MessagingStyle(resources.getString(R.string.reply_name))
4785 * .addMessage(messages[0].getText(), messages[0].getTime(), messages[0].getSender())
4786 * .addMessage(messages[1].getText(), messages[1].getTime(), messages[1].getSender()))
4787 * .build();
4788 * </pre>
4789 */
4790 public static class MessagingStyle extends Style {
4791
4792 /**
4793 * The maximum number of messages that will be retained in the Notification itself (the
4794 * number displayed is up to the platform).
4795 */
4796 public static final int MAXIMUM_RETAINED_MESSAGES = 25;
4797
4798 CharSequence mUserDisplayName;
4799 CharSequence mConversationTitle;
Alex Hillsd9b04d92016-04-11 16:38:16 -04004800 List<Message> mMessages = new ArrayList<>();
Adrian Roos437cd562017-01-18 15:47:03 -08004801 List<Message> mHistoricMessages = new ArrayList<>();
Alex Hillsfc737de2016-03-23 17:33:02 -04004802
4803 MessagingStyle() {
4804 }
4805
4806 /**
Alex Hillsfd590442016-10-07 09:52:44 -04004807 * @param userDisplayName Required - the name to be displayed for any replies sent by the
4808 * user before the posting app reposts the notification with those messages after they've
4809 * been actually sent and in previous messages sent by the user added in
Alex Hillsfc737de2016-03-23 17:33:02 -04004810 * {@link #addMessage(Notification.MessagingStyle.Message)}
4811 */
Alex Hillsfd590442016-10-07 09:52:44 -04004812 public MessagingStyle(@NonNull CharSequence userDisplayName) {
Alex Hillsfc737de2016-03-23 17:33:02 -04004813 mUserDisplayName = userDisplayName;
4814 }
4815
4816 /**
4817 * Returns the name to be displayed for any replies sent by the user
4818 */
4819 public CharSequence getUserDisplayName() {
4820 return mUserDisplayName;
4821 }
4822
4823 /**
Alex Hillsfc737de2016-03-23 17:33:02 -04004824 * Sets the title to be displayed on this conversation. This should only be used for
4825 * group messaging and left unset for one-on-one conversations.
4826 * @param conversationTitle
4827 * @return this object for method chaining.
4828 */
4829 public MessagingStyle setConversationTitle(CharSequence conversationTitle) {
4830 mConversationTitle = conversationTitle;
4831 return this;
4832 }
4833
4834 /**
4835 * Return the title to be displayed on this conversation. Can be <code>null</code> and
4836 * should be for one-on-one conversations
4837 */
4838 public CharSequence getConversationTitle() {
4839 return mConversationTitle;
4840 }
4841
4842 /**
4843 * Adds a message for display by this notification. Convenience call for a simple
4844 * {@link Message} in {@link #addMessage(Notification.MessagingStyle.Message)}.
4845 * @param text A {@link CharSequence} to be displayed as the message content
4846 * @param timestamp Time at which the message arrived
4847 * @param sender A {@link CharSequence} to be used for displaying the name of the
4848 * sender. Should be <code>null</code> for messages by the current user, in which case
4849 * the platform will insert {@link #getUserDisplayName()}.
4850 * Should be unique amongst all individuals in the conversation, and should be
4851 * consistent during re-posts of the notification.
4852 *
4853 * @see Message#Message(CharSequence, long, CharSequence)
4854 *
4855 * @return this object for method chaining
4856 */
4857 public MessagingStyle addMessage(CharSequence text, long timestamp, CharSequence sender) {
Adrian Roos437cd562017-01-18 15:47:03 -08004858 return addMessage(new Message(text, timestamp, sender));
Alex Hillsfc737de2016-03-23 17:33:02 -04004859 }
4860
4861 /**
4862 * Adds a {@link Message} for display in this notification.
Adrian Roos437cd562017-01-18 15:47:03 -08004863 *
4864 * <p>The messages should be added in chronologic order, i.e. the oldest first,
4865 * the newest last.
4866 *
Alex Hillsfc737de2016-03-23 17:33:02 -04004867 * @param message The {@link Message} to be displayed
4868 * @return this object for method chaining
4869 */
4870 public MessagingStyle addMessage(Message message) {
4871 mMessages.add(message);
4872 if (mMessages.size() > MAXIMUM_RETAINED_MESSAGES) {
4873 mMessages.remove(0);
4874 }
4875 return this;
4876 }
4877
4878 /**
Adrian Roos437cd562017-01-18 15:47:03 -08004879 * Adds a {@link Message} for historic context in this notification.
4880 *
4881 * <p>Messages should be added as historic if they are not the main subject of the
4882 * notification but may give context to a conversation. The system may choose to present
4883 * them only when relevant, e.g. when replying to a message through a {@link RemoteInput}.
4884 *
4885 * <p>The messages should be added in chronologic order, i.e. the oldest first,
4886 * the newest last.
4887 *
4888 * @param message The historic {@link Message} to be added
4889 * @return this object for method chaining
4890 */
4891 public MessagingStyle addHistoricMessage(Message message) {
4892 mHistoricMessages.add(message);
4893 if (mHistoricMessages.size() > MAXIMUM_RETAINED_MESSAGES) {
4894 mHistoricMessages.remove(0);
4895 }
4896 return this;
4897 }
4898
4899 /**
Alex Hillsfc737de2016-03-23 17:33:02 -04004900 * Gets the list of {@code Message} objects that represent the notification
4901 */
4902 public List<Message> getMessages() {
4903 return mMessages;
4904 }
4905
4906 /**
Adrian Roos437cd562017-01-18 15:47:03 -08004907 * Gets the list of historic {@code Message}s in the notification.
4908 */
4909 public List<Message> getHistoricMessages() {
4910 return mHistoricMessages;
4911 }
4912
4913 /**
Alex Hillsfc737de2016-03-23 17:33:02 -04004914 * @hide
4915 */
4916 @Override
4917 public void addExtras(Bundle extras) {
4918 super.addExtras(extras);
4919 if (mUserDisplayName != null) {
4920 extras.putCharSequence(EXTRA_SELF_DISPLAY_NAME, mUserDisplayName);
4921 }
4922 if (mConversationTitle != null) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04004923 extras.putCharSequence(EXTRA_CONVERSATION_TITLE, mConversationTitle);
Alex Hillsfc737de2016-03-23 17:33:02 -04004924 }
Alex Hillsd9b04d92016-04-11 16:38:16 -04004925 if (!mMessages.isEmpty()) { extras.putParcelableArray(EXTRA_MESSAGES,
4926 Message.getBundleArrayForMessages(mMessages));
Alex Hillsfc737de2016-03-23 17:33:02 -04004927 }
Adrian Roos437cd562017-01-18 15:47:03 -08004928 if (!mHistoricMessages.isEmpty()) { extras.putParcelableArray(EXTRA_HISTORIC_MESSAGES,
4929 Message.getBundleArrayForMessages(mHistoricMessages));
4930 }
Adrian Roos33fbd2c2016-05-27 15:35:28 -07004931
4932 fixTitleAndTextExtras(extras);
4933 }
4934
4935 private void fixTitleAndTextExtras(Bundle extras) {
4936 Message m = findLatestIncomingMessage();
4937 CharSequence text = (m == null) ? null : m.mText;
4938 CharSequence sender = m == null ? null
4939 : TextUtils.isEmpty(m.mSender) ? mUserDisplayName : m.mSender;
4940 CharSequence title;
4941 if (!TextUtils.isEmpty(mConversationTitle)) {
4942 if (!TextUtils.isEmpty(sender)) {
4943 BidiFormatter bidi = BidiFormatter.getInstance();
4944 title = mBuilder.mContext.getString(
4945 com.android.internal.R.string.notification_messaging_title_template,
4946 bidi.unicodeWrap(mConversationTitle), bidi.unicodeWrap(m.mSender));
4947 } else {
4948 title = mConversationTitle;
4949 }
4950 } else {
4951 title = sender;
4952 }
4953
4954 if (title != null) {
4955 extras.putCharSequence(EXTRA_TITLE, title);
4956 }
4957 if (text != null) {
4958 extras.putCharSequence(EXTRA_TEXT, text);
4959 }
Alex Hillsfc737de2016-03-23 17:33:02 -04004960 }
4961
4962 /**
4963 * @hide
4964 */
4965 @Override
4966 protected void restoreFromExtras(Bundle extras) {
4967 super.restoreFromExtras(extras);
4968
4969 mMessages.clear();
Adrian Roos437cd562017-01-18 15:47:03 -08004970 mHistoricMessages.clear();
Adrian Roos96b7e202016-05-17 13:50:38 -07004971 mUserDisplayName = extras.getCharSequence(EXTRA_SELF_DISPLAY_NAME);
4972 mConversationTitle = extras.getCharSequence(EXTRA_CONVERSATION_TITLE);
Adrian Roos437cd562017-01-18 15:47:03 -08004973 Parcelable[] messages = extras.getParcelableArray(EXTRA_MESSAGES);
4974 if (messages != null && messages instanceof Parcelable[]) {
4975 mMessages = Message.getMessagesFromBundleArray(messages);
4976 }
4977 Parcelable[] histMessages = extras.getParcelableArray(EXTRA_HISTORIC_MESSAGES);
4978 if (histMessages != null && histMessages instanceof Parcelable[]) {
4979 mHistoricMessages = Message.getMessagesFromBundleArray(histMessages);
Alex Hillsfc737de2016-03-23 17:33:02 -04004980 }
4981 }
4982
4983 /**
4984 * @hide
4985 */
Adrian Roosc1a80b02016-04-05 14:54:55 -07004986 @Override
4987 public RemoteViews makeContentView() {
4988 Message m = findLatestIncomingMessage();
4989 CharSequence title = mConversationTitle != null
4990 ? mConversationTitle
4991 : (m == null) ? null : m.mSender;
4992 CharSequence text = (m == null)
4993 ? null
4994 : mConversationTitle != null ? makeMessageLine(m) : m.mText;
Alex Hillsfc737de2016-03-23 17:33:02 -04004995
Adrian Roos70d7aa32017-01-11 15:39:06 -08004996 return mBuilder.applyStandardTemplateWithActions(mBuilder.getBaseLayoutResource(),
4997 mBuilder.mParams.reset().hasProgress(false).title(title).text(text));
Adrian Roosc1a80b02016-04-05 14:54:55 -07004998 }
4999
5000 private Message findLatestIncomingMessage() {
5001 for (int i = mMessages.size() - 1; i >= 0; i--) {
5002 Message m = mMessages.get(i);
5003 // Incoming messages have a non-empty sender.
5004 if (!TextUtils.isEmpty(m.mSender)) {
5005 return m;
5006 }
5007 }
Adrian Roos33fbd2c2016-05-27 15:35:28 -07005008 if (!mMessages.isEmpty()) {
5009 // No incoming messages, fall back to outgoing message
5010 return mMessages.get(mMessages.size() - 1);
5011 }
Adrian Roosc1a80b02016-04-05 14:54:55 -07005012 return null;
5013 }
5014
5015 /**
5016 * @hide
5017 */
5018 @Override
5019 public RemoteViews makeBigContentView() {
5020 CharSequence title = !TextUtils.isEmpty(super.mBigContentTitle)
5021 ? super.mBigContentTitle
5022 : mConversationTitle;
5023 boolean hasTitle = !TextUtils.isEmpty(title);
5024
Adrian Roosfeafa052016-06-01 17:09:45 -07005025 if (mMessages.size() == 1) {
5026 // Special case for a single message: Use the big text style
5027 // so the collapsed and expanded versions match nicely.
5028 CharSequence bigTitle;
5029 CharSequence text;
5030 if (hasTitle) {
5031 bigTitle = title;
5032 text = makeMessageLine(mMessages.get(0));
5033 } else {
5034 bigTitle = mMessages.get(0).mSender;
5035 text = mMessages.get(0).mText;
5036 }
Adrian Roosb1f427c2016-05-26 12:27:15 -07005037 RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(
5038 mBuilder.getBigTextLayoutResource(),
Adrian Roos70d7aa32017-01-11 15:39:06 -08005039 mBuilder.mParams.reset().hasProgress(false).title(bigTitle).text(null));
Adrian Roosb1f427c2016-05-26 12:27:15 -07005040 BigTextStyle.applyBigTextContentView(mBuilder, contentView, text);
5041 return contentView;
5042 }
5043
Adrian Roos48d746a2016-04-12 14:57:28 -07005044 RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(
Adrian Roosc1a80b02016-04-05 14:54:55 -07005045 mBuilder.getMessagingLayoutResource(),
Adrian Roos70d7aa32017-01-11 15:39:06 -08005046 mBuilder.mParams.reset().hasProgress(false).title(title).text(null));
Adrian Roosc1a80b02016-04-05 14:54:55 -07005047
5048 int[] rowIds = {R.id.inbox_text0, R.id.inbox_text1, R.id.inbox_text2, R.id.inbox_text3,
5049 R.id.inbox_text4, R.id.inbox_text5, R.id.inbox_text6};
5050
5051 // Make sure all rows are gone in case we reuse a view.
5052 for (int rowId : rowIds) {
5053 contentView.setViewVisibility(rowId, View.GONE);
5054 }
5055
5056 int i=0;
Adrian Roos2d5dbba2016-06-08 17:11:53 -07005057 contentView.setViewLayoutMarginBottomDimen(R.id.line1,
5058 hasTitle ? R.dimen.notification_messaging_spacing : 0);
Adrian Roosc1a80b02016-04-05 14:54:55 -07005059 contentView.setInt(R.id.notification_messaging, "setNumIndentLines",
Selim Cinek279fa862016-06-14 10:57:25 -07005060 !mBuilder.mN.hasLargeIcon() ? 0 : (hasTitle ? 1 : 2));
Adrian Roosc1a80b02016-04-05 14:54:55 -07005061
Adrian Roosfeafa052016-06-01 17:09:45 -07005062 int contractedChildId = View.NO_ID;
5063 Message contractedMessage = findLatestIncomingMessage();
Adrian Roos437cd562017-01-18 15:47:03 -08005064 int firstHistoricMessage = Math.max(0, mHistoricMessages.size()
5065 - (rowIds.length - mMessages.size()));
5066 while (firstHistoricMessage + i < mHistoricMessages.size() && i < rowIds.length) {
5067 Message m = mHistoricMessages.get(firstHistoricMessage + i);
5068 int rowId = rowIds[i];
5069
5070 contentView.setTextViewText(rowId, makeMessageLine(m));
5071
5072 if (contractedMessage == m) {
5073 contractedChildId = rowId;
5074 }
5075
5076 i++;
5077 }
5078
Adrian Roosc1a80b02016-04-05 14:54:55 -07005079 int firstMessage = Math.max(0, mMessages.size() - rowIds.length);
5080 while (firstMessage + i < mMessages.size() && i < rowIds.length) {
5081 Message m = mMessages.get(firstMessage + i);
5082 int rowId = rowIds[i];
5083
5084 contentView.setViewVisibility(rowId, View.VISIBLE);
5085 contentView.setTextViewText(rowId, makeMessageLine(m));
5086
Adrian Roosfeafa052016-06-01 17:09:45 -07005087 if (contractedMessage == m) {
5088 contractedChildId = rowId;
5089 }
5090
Adrian Roosc1a80b02016-04-05 14:54:55 -07005091 i++;
5092 }
Adrian Roos437cd562017-01-18 15:47:03 -08005093 // Clear the remaining views for reapply. Ensures that historic message views can
5094 // reliably be identified as being GONE and having non-null text.
5095 while (i < rowIds.length) {
5096 int rowId = rowIds[i];
5097 contentView.setTextViewText(rowId, null);
5098 i++;
5099 }
5100
Adrian Roosfeafa052016-06-01 17:09:45 -07005101 // Record this here to allow transformation between the contracted and expanded views.
5102 contentView.setInt(R.id.notification_messaging, "setContractedChildId",
5103 contractedChildId);
Alex Hillsfc737de2016-03-23 17:33:02 -04005104 return contentView;
5105 }
5106
Adrian Roosc1a80b02016-04-05 14:54:55 -07005107 private CharSequence makeMessageLine(Message m) {
5108 BidiFormatter bidi = BidiFormatter.getInstance();
5109 SpannableStringBuilder sb = new SpannableStringBuilder();
5110 if (TextUtils.isEmpty(m.mSender)) {
5111 CharSequence replyName = mUserDisplayName == null ? "" : mUserDisplayName;
5112 sb.append(bidi.unicodeWrap(replyName),
5113 makeFontColorSpan(mBuilder.resolveContrastColor()),
5114 0 /* flags */);
5115 } else {
5116 sb.append(bidi.unicodeWrap(m.mSender),
5117 makeFontColorSpan(Color.BLACK),
5118 0 /* flags */);
5119 }
5120 CharSequence text = m.mText == null ? "" : m.mText;
5121 sb.append(" ").append(bidi.unicodeWrap(text));
5122 return sb;
5123 }
5124
Adrian Roosdedd1df2016-04-26 16:38:47 -07005125 /**
5126 * @hide
5127 */
5128 @Override
5129 public RemoteViews makeHeadsUpContentView() {
5130 Message m = findLatestIncomingMessage();
5131 CharSequence title = mConversationTitle != null
5132 ? mConversationTitle
5133 : (m == null) ? null : m.mSender;
5134 CharSequence text = (m == null)
5135 ? null
5136 : mConversationTitle != null ? makeMessageLine(m) : m.mText;
5137
5138 return mBuilder.applyStandardTemplateWithActions(mBuilder.getBigBaseLayoutResource(),
Adrian Roos70d7aa32017-01-11 15:39:06 -08005139 mBuilder.mParams.reset().hasProgress(false).title(title).text(text));
Adrian Roosdedd1df2016-04-26 16:38:47 -07005140 }
5141
Adrian Roosc1a80b02016-04-05 14:54:55 -07005142 private static TextAppearanceSpan makeFontColorSpan(int color) {
5143 return new TextAppearanceSpan(null, 0, 0,
5144 ColorStateList.valueOf(color), null);
5145 }
5146
Alex Hillsd9b04d92016-04-11 16:38:16 -04005147 public static final class Message {
5148
5149 static final String KEY_TEXT = "text";
5150 static final String KEY_TIMESTAMP = "time";
5151 static final String KEY_SENDER = "sender";
5152 static final String KEY_DATA_MIME_TYPE = "type";
5153 static final String KEY_DATA_URI= "uri";
Alex Hillsfc737de2016-03-23 17:33:02 -04005154
5155 private final CharSequence mText;
5156 private final long mTimestamp;
5157 private final CharSequence mSender;
5158
5159 private String mDataMimeType;
5160 private Uri mDataUri;
5161
5162 /**
5163 * Constructor
5164 * @param text A {@link CharSequence} to be displayed as the message content
5165 * @param timestamp Time at which the message arrived
5166 * @param sender A {@link CharSequence} to be used for displaying the name of the
5167 * sender. Should be <code>null</code> for messages by the current user, in which case
5168 * the platform will insert {@link MessagingStyle#getUserDisplayName()}.
5169 * Should be unique amongst all individuals in the conversation, and should be
5170 * consistent during re-posts of the notification.
5171 */
5172 public Message(CharSequence text, long timestamp, CharSequence sender){
5173 mText = text;
5174 mTimestamp = timestamp;
5175 mSender = sender;
5176 }
5177
5178 /**
5179 * Sets a binary blob of data and an associated MIME type for a message. In the case
5180 * where the platform doesn't support the MIME type, the original text provided in the
5181 * constructor will be used.
5182 * @param dataMimeType The MIME type of the content. See
5183 * <a href="{@docRoot}notifications/messaging.html"> for the list of supported MIME
5184 * types on Android and Android Wear.
5185 * @param dataUri The uri containing the content whose type is given by the MIME type.
5186 * <p class="note">
5187 * <ol>
5188 * <li>Notification Listeners including the System UI need permission to access the
5189 * data the Uri points to. The recommended ways to do this are:</li>
5190 * <li>Store the data in your own ContentProvider, making sure that other apps have
5191 * the correct permission to access your provider. The preferred mechanism for
5192 * providing access is to use per-URI permissions which are temporary and only
5193 * grant access to the receiving application. An easy way to create a
5194 * ContentProvider like this is to use the FileProvider helper class.</li>
5195 * <li>Use the system MediaStore. The MediaStore is primarily aimed at video, audio
5196 * and image MIME types, however beginning with Android 3.0 (API level 11) it can
5197 * also store non-media types (see MediaStore.Files for more info). Files can be
5198 * inserted into the MediaStore using scanFile() after which a content:// style
5199 * Uri suitable for sharing is passed to the provided onScanCompleted() callback.
5200 * Note that once added to the system MediaStore the content is accessible to any
5201 * app on the device.</li>
5202 * </ol>
5203 * @return this object for method chaining
5204 */
5205 public Message setData(String dataMimeType, Uri dataUri) {
5206 mDataMimeType = dataMimeType;
5207 mDataUri = dataUri;
5208 return this;
5209 }
5210
Alex Hillsfc737de2016-03-23 17:33:02 -04005211 /**
5212 * Get the text to be used for this message, or the fallback text if a type and content
5213 * Uri have been set
5214 */
5215 public CharSequence getText() {
5216 return mText;
5217 }
5218
5219 /**
5220 * Get the time at which this message arrived
5221 */
5222 public long getTimestamp() {
5223 return mTimestamp;
5224 }
5225
5226 /**
5227 * Get the text used to display the contact's name in the messaging experience
5228 */
5229 public CharSequence getSender() {
5230 return mSender;
5231 }
5232
5233 /**
5234 * Get the MIME type of the data pointed to by the Uri
5235 */
5236 public String getDataMimeType() {
5237 return mDataMimeType;
5238 }
5239
5240 /**
5241 * Get the the Uri pointing to the content of the message. Can be null, in which case
5242 * {@see #getText()} is used.
5243 */
5244 public Uri getDataUri() {
5245 return mDataUri;
5246 }
5247
Alex Hillsd9b04d92016-04-11 16:38:16 -04005248 private Bundle toBundle() {
5249 Bundle bundle = new Bundle();
Alex Hillsfc737de2016-03-23 17:33:02 -04005250 if (mText != null) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04005251 bundle.putCharSequence(KEY_TEXT, mText);
Alex Hillsfc737de2016-03-23 17:33:02 -04005252 }
Alex Hillsd9b04d92016-04-11 16:38:16 -04005253 bundle.putLong(KEY_TIMESTAMP, mTimestamp);
Alex Hillsfc737de2016-03-23 17:33:02 -04005254 if (mSender != null) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04005255 bundle.putCharSequence(KEY_SENDER, mSender);
Alex Hillsfc737de2016-03-23 17:33:02 -04005256 }
5257 if (mDataMimeType != null) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04005258 bundle.putString(KEY_DATA_MIME_TYPE, mDataMimeType);
Alex Hillsfc737de2016-03-23 17:33:02 -04005259 }
5260 if (mDataUri != null) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04005261 bundle.putParcelable(KEY_DATA_URI, mDataUri);
Alex Hillsfc737de2016-03-23 17:33:02 -04005262 }
Alex Hillsd9b04d92016-04-11 16:38:16 -04005263 return bundle;
Alex Hillsfc737de2016-03-23 17:33:02 -04005264 }
5265
Alex Hillsd9b04d92016-04-11 16:38:16 -04005266 static Bundle[] getBundleArrayForMessages(List<Message> messages) {
5267 Bundle[] bundles = new Bundle[messages.size()];
5268 final int N = messages.size();
5269 for (int i = 0; i < N; i++) {
5270 bundles[i] = messages.get(i).toBundle();
5271 }
5272 return bundles;
5273 }
5274
Adrian Roosdedd1df2016-04-26 16:38:47 -07005275 static List<Message> getMessagesFromBundleArray(Parcelable[] bundles) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04005276 List<Message> messages = new ArrayList<>(bundles.length);
5277 for (int i = 0; i < bundles.length; i++) {
Adrian Roosdedd1df2016-04-26 16:38:47 -07005278 if (bundles[i] instanceof Bundle) {
5279 Message message = getMessageFromBundle((Bundle)bundles[i]);
5280 if (message != null) {
5281 messages.add(message);
5282 }
Alex Hillsd9b04d92016-04-11 16:38:16 -04005283 }
5284 }
5285 return messages;
5286 }
5287
5288 static Message getMessageFromBundle(Bundle bundle) {
5289 try {
Adrian Roosfbddd2c2016-05-13 12:57:20 -07005290 if (!bundle.containsKey(KEY_TEXT) || !bundle.containsKey(KEY_TIMESTAMP)) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04005291 return null;
5292 } else {
5293 Message message = new Message(bundle.getCharSequence(KEY_TEXT),
5294 bundle.getLong(KEY_TIMESTAMP), bundle.getCharSequence(KEY_SENDER));
5295 if (bundle.containsKey(KEY_DATA_MIME_TYPE) &&
5296 bundle.containsKey(KEY_DATA_URI)) {
5297
5298 message.setData(bundle.getString(KEY_DATA_MIME_TYPE),
5299 (Uri) bundle.getParcelable(KEY_DATA_URI));
Alex Hillsfc737de2016-03-23 17:33:02 -04005300 }
Alex Hillsd9b04d92016-04-11 16:38:16 -04005301 return message;
5302 }
5303 } catch (ClassCastException e) {
5304 return null;
5305 }
5306 }
Alex Hillsfc737de2016-03-23 17:33:02 -04005307 }
5308 }
5309
5310 /**
Daniel Sandler879c5e02012-04-17 16:46:51 -04005311 * Helper class for generating large-format notifications that include a list of (up to 5) strings.
Joe Malin8d40d042012-11-05 11:36:40 -08005312 *
Robert Ly91c5ce32014-06-08 15:37:00 -07005313 * Here's how you'd set the <code>InboxStyle</code> on a notification:
Daniel Sandler879c5e02012-04-17 16:46:51 -04005314 * <pre class="prettyprint">
Robert Ly91c5ce32014-06-08 15:37:00 -07005315 * Notification notif = new Notification.Builder(mContext)
5316 * .setContentTitle(&quot;5 New mails from &quot; + sender.toString())
5317 * .setContentText(subject)
5318 * .setSmallIcon(R.drawable.new_mail)
5319 * .setLargeIcon(aBitmap)
5320 * .setStyle(new Notification.InboxStyle()
5321 * .addLine(str1)
5322 * .addLine(str2)
5323 * .setContentTitle(&quot;&quot;)
5324 * .setSummaryText(&quot;+3 more&quot;))
5325 * .build();
Daniel Sandler879c5e02012-04-17 16:46:51 -04005326 * </pre>
Joe Malin8d40d042012-11-05 11:36:40 -08005327 *
Daniel Sandler879c5e02012-04-17 16:46:51 -04005328 * @see Notification#bigContentView
5329 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005330 public static class InboxStyle extends Style {
Daniel Sandler879c5e02012-04-17 16:46:51 -04005331 private ArrayList<CharSequence> mTexts = new ArrayList<CharSequence>(5);
5332
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005333 public InboxStyle() {
5334 }
5335
Adrian Roosf5faf9d2016-05-23 13:56:15 -07005336 /**
5337 * @deprecated use {@code InboxStyle()}.
5338 */
5339 @Deprecated
Daniel Sandler879c5e02012-04-17 16:46:51 -04005340 public InboxStyle(Builder builder) {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005341 setBuilder(builder);
Daniel Sandler879c5e02012-04-17 16:46:51 -04005342 }
5343
Chris Wrend6297db2012-05-03 16:20:13 -04005344 /**
5345 * Overrides ContentTitle in the big form of the template.
5346 * This defaults to the value passed to setContentTitle().
5347 */
5348 public InboxStyle setBigContentTitle(CharSequence title) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04005349 internalSetBigContentTitle(safeCharSequence(title));
Chris Wrend6297db2012-05-03 16:20:13 -04005350 return this;
5351 }
5352
5353 /**
5354 * Set the first line of text after the detail section in the big form of the template.
5355 */
5356 public InboxStyle setSummaryText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04005357 internalSetSummaryText(safeCharSequence(cs));
Chris Wrend6297db2012-05-03 16:20:13 -04005358 return this;
5359 }
5360
Chris Wren0bd664d2012-08-01 13:56:56 -04005361 /**
5362 * Append a line to the digest section of the Inbox notification.
5363 */
Daniel Sandler879c5e02012-04-17 16:46:51 -04005364 public InboxStyle addLine(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04005365 mTexts.add(safeCharSequence(cs));
Daniel Sandler879c5e02012-04-17 16:46:51 -04005366 return this;
5367 }
5368
Daniel Sandlerf45564e2013-04-15 15:05:08 -04005369 /**
5370 * @hide
5371 */
5372 public void addExtras(Bundle extras) {
5373 super.addExtras(extras);
Christoph Studer4600f9b2014-07-22 22:44:43 +02005374
Daniel Sandlerf45564e2013-04-15 15:05:08 -04005375 CharSequence[] a = new CharSequence[mTexts.size()];
5376 extras.putCharSequenceArray(EXTRA_TEXT_LINES, mTexts.toArray(a));
5377 }
5378
Christoph Studer4600f9b2014-07-22 22:44:43 +02005379 /**
5380 * @hide
5381 */
5382 @Override
5383 protected void restoreFromExtras(Bundle extras) {
5384 super.restoreFromExtras(extras);
5385
5386 mTexts.clear();
5387 if (extras.containsKey(EXTRA_TEXT_LINES)) {
5388 Collections.addAll(mTexts, extras.getCharSequenceArray(EXTRA_TEXT_LINES));
5389 }
5390 }
5391
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005392 /**
5393 * @hide
5394 */
5395 public RemoteViews makeBigContentView() {
Selim Cinekc848c3a2016-01-13 15:27:30 -08005396 // Remove the content text so it disappears unless you have a summary
Christoph Studer4600f9b2014-07-22 22:44:43 +02005397 // Nasty
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005398 CharSequence oldBuilderContentText = mBuilder.mN.extras.getCharSequence(EXTRA_TEXT);
5399 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, null);
Christoph Studer4600f9b2014-07-22 22:44:43 +02005400
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01005401 RemoteViews contentView = getStandardView(mBuilder.getInboxLayoutResource());
Daniel Sandler619738c2012-06-07 16:33:08 -04005402
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005403 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, oldBuilderContentText);
Christoph Studer4600f9b2014-07-22 22:44:43 +02005404
Chris Wrend6297db2012-05-03 16:20:13 -04005405 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 -04005406 R.id.inbox_text4, R.id.inbox_text5, R.id.inbox_text6};
Chris Wrend6297db2012-05-03 16:20:13 -04005407
Chris Wren4ed80d52012-05-17 09:30:03 -04005408 // Make sure all rows are gone in case we reuse a view.
5409 for (int rowId : rowIds) {
5410 contentView.setViewVisibility(rowId, View.GONE);
5411 }
5412
Daniel Sandler879c5e02012-04-17 16:46:51 -04005413 int i=0;
Selim Cinek07c80172016-04-21 16:40:47 -07005414 int topPadding = mBuilder.mContext.getResources().getDimensionPixelSize(
5415 R.dimen.notification_inbox_item_top_padding);
Selim Cinek247fa012016-02-18 09:50:48 -08005416 boolean first = true;
Selim Cinek07c80172016-04-21 16:40:47 -07005417 int onlyViewId = 0;
5418 int maxRows = rowIds.length;
5419 if (mBuilder.mActions.size() > 0) {
5420 maxRows--;
5421 }
5422 while (i < mTexts.size() && i < maxRows) {
Daniel Sandler879c5e02012-04-17 16:46:51 -04005423 CharSequence str = mTexts.get(i);
Selim Cinek07c80172016-04-21 16:40:47 -07005424 if (!TextUtils.isEmpty(str)) {
Daniel Sandler879c5e02012-04-17 16:46:51 -04005425 contentView.setViewVisibility(rowIds[i], View.VISIBLE);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01005426 contentView.setTextViewText(rowIds[i], mBuilder.processLegacyText(str));
Selim Cinek07c80172016-04-21 16:40:47 -07005427 contentView.setViewPadding(rowIds[i], 0, topPadding, 0, 0);
Selim Cinek247fa012016-02-18 09:50:48 -08005428 handleInboxImageMargin(contentView, rowIds[i], first);
Selim Cinek07c80172016-04-21 16:40:47 -07005429 if (first) {
5430 onlyViewId = rowIds[i];
5431 } else {
5432 onlyViewId = 0;
5433 }
Selim Cinek247fa012016-02-18 09:50:48 -08005434 first = false;
Daniel Sandler879c5e02012-04-17 16:46:51 -04005435 }
5436 i++;
5437 }
Selim Cinek07c80172016-04-21 16:40:47 -07005438 if (onlyViewId != 0) {
5439 // We only have 1 entry, lets make it look like the normal Text of a Bigtext
5440 topPadding = mBuilder.mContext.getResources().getDimensionPixelSize(
5441 R.dimen.notification_text_margin_top);
5442 contentView.setViewPadding(onlyViewId, 0, topPadding, 0, 0);
5443 }
Selim Cinek1e0bf612015-11-20 15:57:26 -08005444
Daniel Sandler879c5e02012-04-17 16:46:51 -04005445 return contentView;
5446 }
Selim Cinek1e0bf612015-11-20 15:57:26 -08005447
Selim Cinek247fa012016-02-18 09:50:48 -08005448 private void handleInboxImageMargin(RemoteViews contentView, int id, boolean first) {
Selim Cinek1e0bf612015-11-20 15:57:26 -08005449 int endMargin = 0;
Selim Cinek247fa012016-02-18 09:50:48 -08005450 if (first) {
5451 final int max = mBuilder.mN.extras.getInt(EXTRA_PROGRESS_MAX, 0);
5452 final boolean ind = mBuilder.mN.extras.getBoolean(EXTRA_PROGRESS_INDETERMINATE);
5453 boolean hasProgress = max != 0 || ind;
Selim Cinek279fa862016-06-14 10:57:25 -07005454 if (mBuilder.mN.hasLargeIcon() && !hasProgress) {
Adrian Roos2d5dbba2016-06-08 17:11:53 -07005455 endMargin = R.dimen.notification_content_picture_margin;
Selim Cinek247fa012016-02-18 09:50:48 -08005456 }
Selim Cinek1e0bf612015-11-20 15:57:26 -08005457 }
Adrian Roos2d5dbba2016-06-08 17:11:53 -07005458 contentView.setViewLayoutMarginEndDimen(id, endMargin);
Selim Cinek1e0bf612015-11-20 15:57:26 -08005459 }
Daniel Sandler879c5e02012-04-17 16:46:51 -04005460 }
Dan Sandler842dd772014-05-15 09:36:47 -04005461
5462 /**
5463 * Notification style for media playback notifications.
5464 *
5465 * In the expanded form, {@link Notification#bigContentView}, up to 5
5466 * {@link Notification.Action}s specified with
Dan Sandler86647982015-05-13 23:41:13 -04005467 * {@link Notification.Builder#addAction(Action) addAction} will be
Dan Sandler842dd772014-05-15 09:36:47 -04005468 * shown as icon-only pushbuttons, suitable for transport controls. The Bitmap given to
5469 * {@link Notification.Builder#setLargeIcon(android.graphics.Bitmap) setLargeIcon()} will be
5470 * treated as album artwork.
5471 *
5472 * Unlike the other styles provided here, MediaStyle can also modify the standard-size
5473 * {@link Notification#contentView}; by providing action indices to
Christoph Studerfde6f4d2014-12-12 13:23:26 +01005474 * {@link #setShowActionsInCompactView(int...)} you can promote up to 3 actions to be displayed
Dan Sandler842dd772014-05-15 09:36:47 -04005475 * in the standard view alongside the usual content.
5476 *
Bryan Mawhinney6be8de32014-07-18 10:35:12 +01005477 * Notifications created with MediaStyle will have their category set to
5478 * {@link Notification#CATEGORY_TRANSPORT CATEGORY_TRANSPORT} unless you set a different
5479 * category using {@link Notification.Builder#setCategory(String) setCategory()}.
5480 *
Jeff Browndba34ba2014-06-24 20:46:03 -07005481 * Finally, if you attach a {@link android.media.session.MediaSession.Token} using
5482 * {@link android.app.Notification.MediaStyle#setMediaSession(MediaSession.Token)},
Dan Sandler842dd772014-05-15 09:36:47 -04005483 * the System UI can identify this as a notification representing an active media session
5484 * and respond accordingly (by showing album artwork in the lockscreen, for example).
5485 *
5486 * To use this style with your Notification, feed it to
5487 * {@link Notification.Builder#setStyle(android.app.Notification.Style)} like so:
5488 * <pre class="prettyprint">
5489 * Notification noti = new Notification.Builder()
5490 * .setSmallIcon(R.drawable.ic_stat_player)
Christoph Studere935fe92014-11-24 14:18:06 +01005491 * .setContentTitle(&quot;Track title&quot;)
5492 * .setContentText(&quot;Artist - Album&quot;)
5493 * .setLargeIcon(albumArtBitmap))
Bryan Mawhinney6be8de32014-07-18 10:35:12 +01005494 * .setStyle(<b>new Notification.MediaStyle()</b>
5495 * .setMediaSession(mySession))
Dan Sandler842dd772014-05-15 09:36:47 -04005496 * .build();
5497 * </pre>
5498 *
5499 * @see Notification#bigContentView
5500 */
5501 public static class MediaStyle extends Style {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02005502 static final int MAX_MEDIA_BUTTONS_IN_COMPACT = 3;
Dan Sandler842dd772014-05-15 09:36:47 -04005503 static final int MAX_MEDIA_BUTTONS = 5;
5504
5505 private int[] mActionsToShowInCompact = null;
Jeff Browndba34ba2014-06-24 20:46:03 -07005506 private MediaSession.Token mToken;
Dan Sandler842dd772014-05-15 09:36:47 -04005507
5508 public MediaStyle() {
5509 }
5510
Adrian Roosf5faf9d2016-05-23 13:56:15 -07005511 /**
5512 * @deprecated use {@code MediaStyle()}.
5513 */
5514 @Deprecated
Dan Sandler842dd772014-05-15 09:36:47 -04005515 public MediaStyle(Builder builder) {
5516 setBuilder(builder);
5517 }
5518
5519 /**
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02005520 * 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 -04005521 * notification view.
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02005522 *
5523 * @param actions the indices of the actions to show in the compact notification view
Dan Sandler842dd772014-05-15 09:36:47 -04005524 */
5525 public MediaStyle setShowActionsInCompactView(int...actions) {
5526 mActionsToShowInCompact = actions;
5527 return this;
5528 }
5529
5530 /**
Jeff Browndba34ba2014-06-24 20:46:03 -07005531 * Attach a {@link android.media.session.MediaSession.Token} to this Notification
5532 * to provide additional playback information and control to the SystemUI.
Dan Sandler842dd772014-05-15 09:36:47 -04005533 */
Jeff Browndba34ba2014-06-24 20:46:03 -07005534 public MediaStyle setMediaSession(MediaSession.Token token) {
Dan Sandler842dd772014-05-15 09:36:47 -04005535 mToken = token;
5536 return this;
5537 }
5538
Christoph Studer4600f9b2014-07-22 22:44:43 +02005539 /**
5540 * @hide
5541 */
Dan Sandler842dd772014-05-15 09:36:47 -04005542 @Override
5543 public Notification buildStyled(Notification wip) {
Christoph Studer4600f9b2014-07-22 22:44:43 +02005544 super.buildStyled(wip);
Bryan Mawhinney6be8de32014-07-18 10:35:12 +01005545 if (wip.category == null) {
5546 wip.category = Notification.CATEGORY_TRANSPORT;
5547 }
Dan Sandler842dd772014-05-15 09:36:47 -04005548 return wip;
5549 }
5550
Christoph Studer4600f9b2014-07-22 22:44:43 +02005551 /**
5552 * @hide
5553 */
5554 @Override
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005555 public RemoteViews makeContentView() {
5556 return makeMediaContentView();
Christoph Studer4600f9b2014-07-22 22:44:43 +02005557 }
5558
5559 /**
5560 * @hide
5561 */
5562 @Override
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005563 public RemoteViews makeBigContentView() {
5564 return makeMediaBigContentView();
Christoph Studer4600f9b2014-07-22 22:44:43 +02005565 }
5566
Selim Cinekcc10bfb2016-02-10 16:24:21 -08005567 /**
5568 * @hide
5569 */
5570 @Override
5571 public RemoteViews makeHeadsUpContentView() {
5572 RemoteViews expanded = makeMediaBigContentView();
5573 return expanded != null ? expanded : makeMediaContentView();
5574 }
5575
Dan Sandler842dd772014-05-15 09:36:47 -04005576 /** @hide */
5577 @Override
5578 public void addExtras(Bundle extras) {
5579 super.addExtras(extras);
5580
5581 if (mToken != null) {
5582 extras.putParcelable(EXTRA_MEDIA_SESSION, mToken);
5583 }
Bryan Mawhinneye191f902014-07-22 12:50:09 +01005584 if (mActionsToShowInCompact != null) {
5585 extras.putIntArray(EXTRA_COMPACT_ACTIONS, mActionsToShowInCompact);
5586 }
Dan Sandler842dd772014-05-15 09:36:47 -04005587 }
5588
Christoph Studer4600f9b2014-07-22 22:44:43 +02005589 /**
5590 * @hide
5591 */
5592 @Override
5593 protected void restoreFromExtras(Bundle extras) {
5594 super.restoreFromExtras(extras);
5595
5596 if (extras.containsKey(EXTRA_MEDIA_SESSION)) {
5597 mToken = extras.getParcelable(EXTRA_MEDIA_SESSION);
5598 }
5599 if (extras.containsKey(EXTRA_COMPACT_ACTIONS)) {
5600 mActionsToShowInCompact = extras.getIntArray(EXTRA_COMPACT_ACTIONS);
5601 }
5602 }
5603
Selim Cinek5bf069a2015-11-10 19:14:27 -05005604 private RemoteViews generateMediaActionButton(Action action, int color) {
Dan Sandler842dd772014-05-15 09:36:47 -04005605 final boolean tombstone = (action.actionIntent == null);
Selim Cinekf33b1112015-07-15 17:45:11 -07005606 RemoteViews button = new BuilderRemoteViews(mBuilder.mContext.getApplicationInfo(),
Alan Viverette3cb07a462014-06-06 14:19:53 -07005607 R.layout.notification_material_media_action);
Dan Sandler68079d52015-07-22 10:45:30 -04005608 button.setImageViewIcon(R.id.action0, action.getIcon());
Selim Cinek5bf069a2015-11-10 19:14:27 -05005609 button.setDrawableParameters(R.id.action0, false, -1, color, PorterDuff.Mode.SRC_ATOP,
5610 -1);
Dan Sandler842dd772014-05-15 09:36:47 -04005611 if (!tombstone) {
5612 button.setOnClickPendingIntent(R.id.action0, action.actionIntent);
5613 }
5614 button.setContentDescription(R.id.action0, action.title);
5615 return button;
5616 }
5617
5618 private RemoteViews makeMediaContentView() {
5619 RemoteViews view = mBuilder.applyStandardTemplate(
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02005620 R.layout.notification_template_material_media, false /* hasProgress */);
Dan Sandler842dd772014-05-15 09:36:47 -04005621
5622 final int numActions = mBuilder.mActions.size();
5623 final int N = mActionsToShowInCompact == null
5624 ? 0
5625 : Math.min(mActionsToShowInCompact.length, MAX_MEDIA_BUTTONS_IN_COMPACT);
5626 if (N > 0) {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02005627 view.removeAllViews(com.android.internal.R.id.media_actions);
Dan Sandler842dd772014-05-15 09:36:47 -04005628 for (int i = 0; i < N; i++) {
5629 if (i >= numActions) {
5630 throw new IllegalArgumentException(String.format(
5631 "setShowActionsInCompactView: action %d out of bounds (max %d)",
5632 i, numActions - 1));
5633 }
5634
5635 final Action action = mBuilder.mActions.get(mActionsToShowInCompact[i]);
Selim Cinek5bf069a2015-11-10 19:14:27 -05005636 final RemoteViews button = generateMediaActionButton(action,
Adrian Roos4ff3b122016-02-01 12:26:13 -08005637 mBuilder.resolveContrastColor());
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02005638 view.addView(com.android.internal.R.id.media_actions, button);
Dan Sandler842dd772014-05-15 09:36:47 -04005639 }
5640 }
Selim Cinekfdc738f2016-01-27 20:04:27 -08005641 handleImage(view);
5642 // handle the content margin
Adrian Roos2d5dbba2016-06-08 17:11:53 -07005643 int endMargin = R.dimen.notification_content_margin_end;
Selim Cinek279fa862016-06-14 10:57:25 -07005644 if (mBuilder.mN.hasLargeIcon()) {
Adrian Roos2d5dbba2016-06-08 17:11:53 -07005645 endMargin = R.dimen.notification_content_plus_picture_margin_end;
Selim Cinekfdc738f2016-01-27 20:04:27 -08005646 }
Adrian Roos2d5dbba2016-06-08 17:11:53 -07005647 view.setViewLayoutMarginEndDimen(R.id.notification_main_column, endMargin);
Dan Sandler842dd772014-05-15 09:36:47 -04005648 return view;
5649 }
5650
5651 private RemoteViews makeMediaBigContentView() {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02005652 final int actionCount = Math.min(mBuilder.mActions.size(), MAX_MEDIA_BUTTONS);
Selim Cinekcc10bfb2016-02-10 16:24:21 -08005653 // Dont add an expanded view if there is no more content to be revealed
5654 int actionsInCompact = mActionsToShowInCompact == null
5655 ? 0
5656 : Math.min(mActionsToShowInCompact.length, MAX_MEDIA_BUTTONS_IN_COMPACT);
Selim Cinek279fa862016-06-14 10:57:25 -07005657 if (!mBuilder.mN.hasLargeIcon() && actionCount <= actionsInCompact) {
Selim Cinekcc10bfb2016-02-10 16:24:21 -08005658 return null;
5659 }
Selim Cinek5bf069a2015-11-10 19:14:27 -05005660 RemoteViews big = mBuilder.applyStandardTemplate(
5661 R.layout.notification_template_material_big_media,
5662 false);
Dan Sandler842dd772014-05-15 09:36:47 -04005663
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02005664 if (actionCount > 0) {
5665 big.removeAllViews(com.android.internal.R.id.media_actions);
5666 for (int i = 0; i < actionCount; i++) {
Selim Cinek5bf069a2015-11-10 19:14:27 -05005667 final RemoteViews button = generateMediaActionButton(mBuilder.mActions.get(i),
Adrian Roos4ff3b122016-02-01 12:26:13 -08005668 mBuilder.resolveContrastColor());
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02005669 big.addView(com.android.internal.R.id.media_actions, button);
Dan Sandler842dd772014-05-15 09:36:47 -04005670 }
5671 }
Selim Cinek5bf069a2015-11-10 19:14:27 -05005672 handleImage(big);
Dan Sandler842dd772014-05-15 09:36:47 -04005673 return big;
5674 }
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02005675
Selim Cinek5bf069a2015-11-10 19:14:27 -05005676 private void handleImage(RemoteViews contentView) {
Selim Cinek279fa862016-06-14 10:57:25 -07005677 if (mBuilder.mN.hasLargeIcon()) {
Adrian Roos2d5dbba2016-06-08 17:11:53 -07005678 contentView.setViewLayoutMarginEndDimen(R.id.line1, 0);
5679 contentView.setViewLayoutMarginEndDimen(R.id.text, 0);
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02005680 }
5681 }
5682
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02005683 /**
5684 * @hide
5685 */
5686 @Override
5687 protected boolean hasProgress() {
5688 return false;
5689 }
Dan Sandler842dd772014-05-15 09:36:47 -04005690 }
Griff Hazen61a9e862014-05-22 16:05:19 -07005691
Selim Cinek593610c2016-02-16 18:42:57 -08005692 /**
5693 * Notification style for custom views that are decorated by the system
5694 *
5695 * <p>Instead of providing a notification that is completely custom, a developer can set this
5696 * style and still obtain system decorations like the notification header with the expand
5697 * affordance and actions.
5698 *
5699 * <p>Use {@link android.app.Notification.Builder#setCustomContentView(RemoteViews)},
5700 * {@link android.app.Notification.Builder#setCustomBigContentView(RemoteViews)} and
5701 * {@link android.app.Notification.Builder#setCustomHeadsUpContentView(RemoteViews)} to set the
5702 * corresponding custom views to display.
5703 *
5704 * To use this style with your Notification, feed it to
5705 * {@link Notification.Builder#setStyle(android.app.Notification.Style)} like so:
5706 * <pre class="prettyprint">
5707 * Notification noti = new Notification.Builder()
5708 * .setSmallIcon(R.drawable.ic_stat_player)
5709 * .setLargeIcon(albumArtBitmap))
5710 * .setCustomContentView(contentView);
5711 * .setStyle(<b>new Notification.DecoratedCustomViewStyle()</b>)
5712 * .build();
5713 * </pre>
5714 */
5715 public static class DecoratedCustomViewStyle extends Style {
5716
5717 public DecoratedCustomViewStyle() {
5718 }
5719
Selim Cinek593610c2016-02-16 18:42:57 -08005720 /**
5721 * @hide
5722 */
5723 public boolean displayCustomViewInline() {
5724 return true;
5725 }
5726
5727 /**
5728 * @hide
5729 */
5730 @Override
5731 public RemoteViews makeContentView() {
5732 return makeStandardTemplateWithCustomContent(mBuilder.mN.contentView);
5733 }
5734
5735 /**
5736 * @hide
5737 */
5738 @Override
5739 public RemoteViews makeBigContentView() {
5740 return makeDecoratedBigContentView();
5741 }
5742
5743 /**
5744 * @hide
5745 */
5746 @Override
5747 public RemoteViews makeHeadsUpContentView() {
5748 return makeDecoratedHeadsUpContentView();
5749 }
5750
Selim Cinek593610c2016-02-16 18:42:57 -08005751 private RemoteViews makeDecoratedHeadsUpContentView() {
5752 RemoteViews headsUpContentView = mBuilder.mN.headsUpContentView == null
5753 ? mBuilder.mN.contentView
5754 : mBuilder.mN.headsUpContentView;
5755 if (mBuilder.mActions.size() == 0) {
5756 return makeStandardTemplateWithCustomContent(headsUpContentView);
5757 }
5758 RemoteViews remoteViews = mBuilder.applyStandardTemplateWithActions(
5759 mBuilder.getBigBaseLayoutResource());
Selim Cinek247fa012016-02-18 09:50:48 -08005760 buildIntoRemoteViewContent(remoteViews, headsUpContentView);
Selim Cinek593610c2016-02-16 18:42:57 -08005761 return remoteViews;
5762 }
5763
Selim Cinek593610c2016-02-16 18:42:57 -08005764 private RemoteViews makeStandardTemplateWithCustomContent(RemoteViews customContent) {
5765 RemoteViews remoteViews = mBuilder.applyStandardTemplate(
5766 mBuilder.getBaseLayoutResource());
Selim Cinek247fa012016-02-18 09:50:48 -08005767 buildIntoRemoteViewContent(remoteViews, customContent);
Selim Cinek593610c2016-02-16 18:42:57 -08005768 return remoteViews;
5769 }
5770
Selim Cinek593610c2016-02-16 18:42:57 -08005771 private RemoteViews makeDecoratedBigContentView() {
5772 RemoteViews bigContentView = mBuilder.mN.bigContentView == null
5773 ? mBuilder.mN.contentView
5774 : mBuilder.mN.bigContentView;
5775 if (mBuilder.mActions.size() == 0) {
5776 return makeStandardTemplateWithCustomContent(bigContentView);
5777 }
5778 RemoteViews remoteViews = mBuilder.applyStandardTemplateWithActions(
5779 mBuilder.getBigBaseLayoutResource());
Selim Cinek247fa012016-02-18 09:50:48 -08005780 buildIntoRemoteViewContent(remoteViews, bigContentView);
Selim Cinek593610c2016-02-16 18:42:57 -08005781 return remoteViews;
5782 }
Selim Cinek247fa012016-02-18 09:50:48 -08005783
5784 private void buildIntoRemoteViewContent(RemoteViews remoteViews,
5785 RemoteViews customContent) {
Adrian Roos5081c0d2016-02-26 16:04:19 -08005786 if (customContent != null) {
Selim Cinekf91017e2016-03-14 12:25:09 -07005787 // Need to clone customContent before adding, because otherwise it can no longer be
5788 // parceled independently of remoteViews.
Adrian Roos5081c0d2016-02-26 16:04:19 -08005789 customContent = customContent.clone();
Selim Cinekf91017e2016-03-14 12:25:09 -07005790 remoteViews.removeAllViews(R.id.notification_main_column);
5791 remoteViews.addView(R.id.notification_main_column, customContent);
Adrian Roos5081c0d2016-02-26 16:04:19 -08005792 }
Selim Cinek247fa012016-02-18 09:50:48 -08005793 // also update the end margin if there is an image
Adrian Roos2d5dbba2016-06-08 17:11:53 -07005794 int endMargin = R.dimen.notification_content_margin_end;
Selim Cinek279fa862016-06-14 10:57:25 -07005795 if (mBuilder.mN.hasLargeIcon()) {
Adrian Roos2d5dbba2016-06-08 17:11:53 -07005796 endMargin = R.dimen.notification_content_plus_picture_margin_end;
Selim Cinek247fa012016-02-18 09:50:48 -08005797 }
Adrian Roos2d5dbba2016-06-08 17:11:53 -07005798 remoteViews.setViewLayoutMarginEndDimen(R.id.notification_main_column, endMargin);
Selim Cinek247fa012016-02-18 09:50:48 -08005799 }
Selim Cinek593610c2016-02-16 18:42:57 -08005800 }
5801
Selim Cinek03eb3b72016-02-18 10:39:45 -08005802 /**
5803 * Notification style for media custom views that are decorated by the system
5804 *
5805 * <p>Instead of providing a media notification that is completely custom, a developer can set
5806 * this style and still obtain system decorations like the notification header with the expand
5807 * affordance and actions.
5808 *
5809 * <p>Use {@link android.app.Notification.Builder#setCustomContentView(RemoteViews)},
5810 * {@link android.app.Notification.Builder#setCustomBigContentView(RemoteViews)} and
5811 * {@link android.app.Notification.Builder#setCustomHeadsUpContentView(RemoteViews)} to set the
5812 * corresponding custom views to display.
5813 *
5814 * To use this style with your Notification, feed it to
5815 * {@link Notification.Builder#setStyle(android.app.Notification.Style)} like so:
5816 * <pre class="prettyprint">
5817 * Notification noti = new Notification.Builder()
5818 * .setSmallIcon(R.drawable.ic_stat_player)
5819 * .setLargeIcon(albumArtBitmap))
5820 * .setCustomContentView(contentView);
5821 * .setStyle(<b>new Notification.DecoratedMediaCustomViewStyle()</b>
5822 * .setMediaSession(mySession))
5823 * .build();
5824 * </pre>
5825 *
5826 * @see android.app.Notification.DecoratedCustomViewStyle
5827 * @see android.app.Notification.MediaStyle
5828 */
5829 public static class DecoratedMediaCustomViewStyle extends MediaStyle {
5830
5831 public DecoratedMediaCustomViewStyle() {
5832 }
5833
Selim Cinek03eb3b72016-02-18 10:39:45 -08005834 /**
5835 * @hide
5836 */
5837 public boolean displayCustomViewInline() {
5838 return true;
5839 }
5840
5841 /**
5842 * @hide
5843 */
5844 @Override
5845 public RemoteViews makeContentView() {
5846 RemoteViews remoteViews = super.makeContentView();
5847 return buildIntoRemoteView(remoteViews, R.id.notification_content_container,
5848 mBuilder.mN.contentView);
5849 }
5850
5851 /**
5852 * @hide
5853 */
5854 @Override
5855 public RemoteViews makeBigContentView() {
5856 RemoteViews customRemoteView = mBuilder.mN.bigContentView != null
5857 ? mBuilder.mN.bigContentView
5858 : mBuilder.mN.contentView;
5859 return makeBigContentViewWithCustomContent(customRemoteView);
5860 }
5861
5862 private RemoteViews makeBigContentViewWithCustomContent(RemoteViews customRemoteView) {
5863 RemoteViews remoteViews = super.makeBigContentView();
5864 if (remoteViews != null) {
5865 return buildIntoRemoteView(remoteViews, R.id.notification_main_column,
5866 customRemoteView);
5867 } else if (customRemoteView != mBuilder.mN.contentView){
5868 remoteViews = super.makeContentView();
5869 return buildIntoRemoteView(remoteViews, R.id.notification_content_container,
5870 customRemoteView);
5871 } else {
5872 return null;
5873 }
5874 }
5875
5876 /**
5877 * @hide
5878 */
5879 @Override
5880 public RemoteViews makeHeadsUpContentView() {
5881 RemoteViews customRemoteView = mBuilder.mN.headsUpContentView != null
5882 ? mBuilder.mN.headsUpContentView
5883 : mBuilder.mN.contentView;
5884 return makeBigContentViewWithCustomContent(customRemoteView);
5885 }
5886
5887 private RemoteViews buildIntoRemoteView(RemoteViews remoteViews, int id,
5888 RemoteViews customContent) {
Adrian Roos5081c0d2016-02-26 16:04:19 -08005889 if (customContent != null) {
Selim Cinekf91017e2016-03-14 12:25:09 -07005890 // Need to clone customContent before adding, because otherwise it can no longer be
5891 // parceled independently of remoteViews.
Adrian Roos5081c0d2016-02-26 16:04:19 -08005892 customContent = customContent.clone();
Selim Cinekf91017e2016-03-14 12:25:09 -07005893 remoteViews.removeAllViews(id);
5894 remoteViews.addView(id, customContent);
Adrian Roos5081c0d2016-02-26 16:04:19 -08005895 }
Selim Cinek03eb3b72016-02-18 10:39:45 -08005896 return remoteViews;
5897 }
5898 }
5899
Christoph Studer4600f9b2014-07-22 22:44:43 +02005900 // When adding a new Style subclass here, don't forget to update
5901 // Builder.getNotificationStyleClass.
5902
Griff Hazen61a9e862014-05-22 16:05:19 -07005903 /**
5904 * Extender interface for use with {@link Builder#extend}. Extenders may be used to add
5905 * metadata or change options on a notification builder.
5906 */
5907 public interface Extender {
5908 /**
5909 * Apply this extender to a notification builder.
5910 * @param builder the builder to be modified.
5911 * @return the build object for chaining.
5912 */
5913 public Builder extend(Builder builder);
5914 }
5915
5916 /**
5917 * Helper class to add wearable extensions to notifications.
5918 * <p class="note"> See
5919 * <a href="{@docRoot}wear/notifications/creating.html">Creating Notifications
5920 * for Android Wear</a> for more information on how to use this class.
5921 * <p>
5922 * To create a notification with wearable extensions:
5923 * <ol>
5924 * <li>Create a {@link android.app.Notification.Builder}, setting any desired
5925 * properties.
5926 * <li>Create a {@link android.app.Notification.WearableExtender}.
5927 * <li>Set wearable-specific properties using the
5928 * {@code add} and {@code set} methods of {@link android.app.Notification.WearableExtender}.
5929 * <li>Call {@link android.app.Notification.Builder#extend} to apply the extensions to a
5930 * notification.
5931 * <li>Post the notification to the notification system with the
5932 * {@code NotificationManager.notify(...)} methods.
5933 * </ol>
5934 *
5935 * <pre class="prettyprint">
5936 * Notification notif = new Notification.Builder(mContext)
5937 * .setContentTitle(&quot;New mail from &quot; + sender.toString())
5938 * .setContentText(subject)
5939 * .setSmallIcon(R.drawable.new_mail)
5940 * .extend(new Notification.WearableExtender()
5941 * .setContentIcon(R.drawable.new_mail))
5942 * .build();
5943 * NotificationManager notificationManger =
5944 * (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
5945 * notificationManger.notify(0, notif);</pre>
5946 *
5947 * <p>Wearable extensions can be accessed on an existing notification by using the
5948 * {@code WearableExtender(Notification)} constructor,
5949 * and then using the {@code get} methods to access values.
5950 *
5951 * <pre class="prettyprint">
5952 * Notification.WearableExtender wearableExtender = new Notification.WearableExtender(
5953 * notification);
Griff Hazen14f57992014-05-26 09:07:14 -07005954 * List&lt;Notification&gt; pages = wearableExtender.getPages();</pre>
Griff Hazen61a9e862014-05-22 16:05:19 -07005955 */
5956 public static final class WearableExtender implements Extender {
5957 /**
5958 * Sentinel value for an action index that is unset.
5959 */
5960 public static final int UNSET_ACTION_INDEX = -1;
5961
5962 /**
5963 * Size value for use with {@link #setCustomSizePreset} to show this notification with
5964 * default sizing.
5965 * <p>For custom display notifications created using {@link #setDisplayIntent},
Paul Soulosaa4f4bf2015-08-04 11:59:45 -07005966 * the default is {@link #SIZE_MEDIUM}. All other notifications size automatically based
Griff Hazen61a9e862014-05-22 16:05:19 -07005967 * on their content.
5968 */
5969 public static final int SIZE_DEFAULT = 0;
5970
5971 /**
5972 * Size value for use with {@link #setCustomSizePreset} to show this notification
5973 * with an extra small size.
5974 * <p>This value is only applicable for custom display notifications created using
5975 * {@link #setDisplayIntent}.
5976 */
5977 public static final int SIZE_XSMALL = 1;
5978
5979 /**
5980 * Size value for use with {@link #setCustomSizePreset} to show this notification
5981 * with a small size.
5982 * <p>This value is only applicable for custom display notifications created using
5983 * {@link #setDisplayIntent}.
5984 */
5985 public static final int SIZE_SMALL = 2;
5986
5987 /**
5988 * Size value for use with {@link #setCustomSizePreset} to show this notification
5989 * with a medium size.
5990 * <p>This value is only applicable for custom display notifications created using
5991 * {@link #setDisplayIntent}.
5992 */
5993 public static final int SIZE_MEDIUM = 3;
5994
5995 /**
5996 * Size value for use with {@link #setCustomSizePreset} to show this notification
5997 * with a large size.
5998 * <p>This value is only applicable for custom display notifications created using
5999 * {@link #setDisplayIntent}.
6000 */
6001 public static final int SIZE_LARGE = 4;
6002
Griff Hazend5f11f92014-05-27 15:40:09 -07006003 /**
6004 * Size value for use with {@link #setCustomSizePreset} to show this notification
6005 * full screen.
6006 * <p>This value is only applicable for custom display notifications created using
6007 * {@link #setDisplayIntent}.
6008 */
6009 public static final int SIZE_FULL_SCREEN = 5;
6010
Griff Hazen5f2edfc2014-09-29 16:28:44 -07006011 /**
6012 * Sentinel value for use with {@link #setHintScreenTimeout} to keep the screen on for a
6013 * short amount of time when this notification is displayed on the screen. This
6014 * is the default value.
6015 */
6016 public static final int SCREEN_TIMEOUT_SHORT = 0;
6017
6018 /**
6019 * Sentinel value for use with {@link #setHintScreenTimeout} to keep the screen on
6020 * for a longer amount of time when this notification is displayed on the screen.
6021 */
6022 public static final int SCREEN_TIMEOUT_LONG = -1;
6023
Griff Hazen61a9e862014-05-22 16:05:19 -07006024 /** Notification extra which contains wearable extensions */
6025 private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS";
6026
Pete Gastaf6781d2014-10-07 15:17:05 -04006027 // Keys within EXTRA_WEARABLE_EXTENSIONS for wearable options.
Griff Hazen61a9e862014-05-22 16:05:19 -07006028 private static final String KEY_ACTIONS = "actions";
6029 private static final String KEY_FLAGS = "flags";
6030 private static final String KEY_DISPLAY_INTENT = "displayIntent";
6031 private static final String KEY_PAGES = "pages";
6032 private static final String KEY_BACKGROUND = "background";
6033 private static final String KEY_CONTENT_ICON = "contentIcon";
6034 private static final String KEY_CONTENT_ICON_GRAVITY = "contentIconGravity";
6035 private static final String KEY_CONTENT_ACTION_INDEX = "contentActionIndex";
6036 private static final String KEY_CUSTOM_SIZE_PRESET = "customSizePreset";
6037 private static final String KEY_CUSTOM_CONTENT_HEIGHT = "customContentHeight";
6038 private static final String KEY_GRAVITY = "gravity";
Griff Hazen5f2edfc2014-09-29 16:28:44 -07006039 private static final String KEY_HINT_SCREEN_TIMEOUT = "hintScreenTimeout";
Nadia Benbernou948627e2016-04-14 14:41:08 -04006040 private static final String KEY_DISMISSAL_ID = "dismissalId";
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04006041 private static final String KEY_BRIDGE_TAG = "bridgeTag";
Griff Hazen61a9e862014-05-22 16:05:19 -07006042
6043 // Flags bitwise-ored to mFlags
6044 private static final int FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE = 0x1;
6045 private static final int FLAG_HINT_HIDE_ICON = 1 << 1;
6046 private static final int FLAG_HINT_SHOW_BACKGROUND_ONLY = 1 << 2;
6047 private static final int FLAG_START_SCROLL_BOTTOM = 1 << 3;
Griff Hazen5f2edfc2014-09-29 16:28:44 -07006048 private static final int FLAG_HINT_AVOID_BACKGROUND_CLIPPING = 1 << 4;
Alex Hills4bcb06b2016-04-05 14:26:25 -04006049 private static final int FLAG_BIG_PICTURE_AMBIENT = 1 << 5;
Alex Hills9ab3a232016-04-05 14:54:56 -04006050 private static final int FLAG_HINT_CONTENT_INTENT_LAUNCHES_ACTIVITY = 1 << 6;
Griff Hazen61a9e862014-05-22 16:05:19 -07006051
6052 // Default value for flags integer
6053 private static final int DEFAULT_FLAGS = FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE;
6054
6055 private static final int DEFAULT_CONTENT_ICON_GRAVITY = Gravity.END;
6056 private static final int DEFAULT_GRAVITY = Gravity.BOTTOM;
6057
6058 private ArrayList<Action> mActions = new ArrayList<Action>();
6059 private int mFlags = DEFAULT_FLAGS;
6060 private PendingIntent mDisplayIntent;
6061 private ArrayList<Notification> mPages = new ArrayList<Notification>();
6062 private Bitmap mBackground;
6063 private int mContentIcon;
6064 private int mContentIconGravity = DEFAULT_CONTENT_ICON_GRAVITY;
6065 private int mContentActionIndex = UNSET_ACTION_INDEX;
6066 private int mCustomSizePreset = SIZE_DEFAULT;
6067 private int mCustomContentHeight;
6068 private int mGravity = DEFAULT_GRAVITY;
Griff Hazen5f2edfc2014-09-29 16:28:44 -07006069 private int mHintScreenTimeout;
Nadia Benbernou948627e2016-04-14 14:41:08 -04006070 private String mDismissalId;
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04006071 private String mBridgeTag;
Griff Hazen61a9e862014-05-22 16:05:19 -07006072
6073 /**
6074 * Create a {@link android.app.Notification.WearableExtender} with default
6075 * options.
6076 */
6077 public WearableExtender() {
6078 }
6079
6080 public WearableExtender(Notification notif) {
6081 Bundle wearableBundle = notif.extras.getBundle(EXTRA_WEARABLE_EXTENSIONS);
6082 if (wearableBundle != null) {
6083 List<Action> actions = wearableBundle.getParcelableArrayList(KEY_ACTIONS);
6084 if (actions != null) {
6085 mActions.addAll(actions);
6086 }
6087
6088 mFlags = wearableBundle.getInt(KEY_FLAGS, DEFAULT_FLAGS);
6089 mDisplayIntent = wearableBundle.getParcelable(KEY_DISPLAY_INTENT);
6090
6091 Notification[] pages = getNotificationArrayFromBundle(
6092 wearableBundle, KEY_PAGES);
6093 if (pages != null) {
6094 Collections.addAll(mPages, pages);
6095 }
6096
6097 mBackground = wearableBundle.getParcelable(KEY_BACKGROUND);
6098 mContentIcon = wearableBundle.getInt(KEY_CONTENT_ICON);
6099 mContentIconGravity = wearableBundle.getInt(KEY_CONTENT_ICON_GRAVITY,
6100 DEFAULT_CONTENT_ICON_GRAVITY);
6101 mContentActionIndex = wearableBundle.getInt(KEY_CONTENT_ACTION_INDEX,
6102 UNSET_ACTION_INDEX);
6103 mCustomSizePreset = wearableBundle.getInt(KEY_CUSTOM_SIZE_PRESET,
6104 SIZE_DEFAULT);
6105 mCustomContentHeight = wearableBundle.getInt(KEY_CUSTOM_CONTENT_HEIGHT);
6106 mGravity = wearableBundle.getInt(KEY_GRAVITY, DEFAULT_GRAVITY);
Griff Hazen5f2edfc2014-09-29 16:28:44 -07006107 mHintScreenTimeout = wearableBundle.getInt(KEY_HINT_SCREEN_TIMEOUT);
Nadia Benbernou948627e2016-04-14 14:41:08 -04006108 mDismissalId = wearableBundle.getString(KEY_DISMISSAL_ID);
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04006109 mBridgeTag = wearableBundle.getString(KEY_BRIDGE_TAG);
Griff Hazen61a9e862014-05-22 16:05:19 -07006110 }
6111 }
6112
6113 /**
6114 * Apply wearable extensions to a notification that is being built. This is typically
6115 * called by the {@link android.app.Notification.Builder#extend} method of
6116 * {@link android.app.Notification.Builder}.
6117 */
6118 @Override
6119 public Notification.Builder extend(Notification.Builder builder) {
6120 Bundle wearableBundle = new Bundle();
6121
6122 if (!mActions.isEmpty()) {
6123 wearableBundle.putParcelableArrayList(KEY_ACTIONS, mActions);
6124 }
6125 if (mFlags != DEFAULT_FLAGS) {
6126 wearableBundle.putInt(KEY_FLAGS, mFlags);
6127 }
6128 if (mDisplayIntent != null) {
6129 wearableBundle.putParcelable(KEY_DISPLAY_INTENT, mDisplayIntent);
6130 }
6131 if (!mPages.isEmpty()) {
6132 wearableBundle.putParcelableArray(KEY_PAGES, mPages.toArray(
6133 new Notification[mPages.size()]));
6134 }
6135 if (mBackground != null) {
6136 wearableBundle.putParcelable(KEY_BACKGROUND, mBackground);
6137 }
6138 if (mContentIcon != 0) {
6139 wearableBundle.putInt(KEY_CONTENT_ICON, mContentIcon);
6140 }
6141 if (mContentIconGravity != DEFAULT_CONTENT_ICON_GRAVITY) {
6142 wearableBundle.putInt(KEY_CONTENT_ICON_GRAVITY, mContentIconGravity);
6143 }
6144 if (mContentActionIndex != UNSET_ACTION_INDEX) {
6145 wearableBundle.putInt(KEY_CONTENT_ACTION_INDEX,
6146 mContentActionIndex);
6147 }
6148 if (mCustomSizePreset != SIZE_DEFAULT) {
6149 wearableBundle.putInt(KEY_CUSTOM_SIZE_PRESET, mCustomSizePreset);
6150 }
6151 if (mCustomContentHeight != 0) {
6152 wearableBundle.putInt(KEY_CUSTOM_CONTENT_HEIGHT, mCustomContentHeight);
6153 }
6154 if (mGravity != DEFAULT_GRAVITY) {
6155 wearableBundle.putInt(KEY_GRAVITY, mGravity);
6156 }
Griff Hazen5f2edfc2014-09-29 16:28:44 -07006157 if (mHintScreenTimeout != 0) {
6158 wearableBundle.putInt(KEY_HINT_SCREEN_TIMEOUT, mHintScreenTimeout);
6159 }
Nadia Benbernou948627e2016-04-14 14:41:08 -04006160 if (mDismissalId != null) {
6161 wearableBundle.putString(KEY_DISMISSAL_ID, mDismissalId);
6162 }
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04006163 if (mBridgeTag != null) {
6164 wearableBundle.putString(KEY_BRIDGE_TAG, mBridgeTag);
6165 }
Griff Hazen61a9e862014-05-22 16:05:19 -07006166
6167 builder.getExtras().putBundle(EXTRA_WEARABLE_EXTENSIONS, wearableBundle);
6168 return builder;
6169 }
6170
6171 @Override
6172 public WearableExtender clone() {
6173 WearableExtender that = new WearableExtender();
6174 that.mActions = new ArrayList<Action>(this.mActions);
6175 that.mFlags = this.mFlags;
6176 that.mDisplayIntent = this.mDisplayIntent;
6177 that.mPages = new ArrayList<Notification>(this.mPages);
6178 that.mBackground = this.mBackground;
6179 that.mContentIcon = this.mContentIcon;
6180 that.mContentIconGravity = this.mContentIconGravity;
6181 that.mContentActionIndex = this.mContentActionIndex;
6182 that.mCustomSizePreset = this.mCustomSizePreset;
6183 that.mCustomContentHeight = this.mCustomContentHeight;
6184 that.mGravity = this.mGravity;
Griff Hazen5f2edfc2014-09-29 16:28:44 -07006185 that.mHintScreenTimeout = this.mHintScreenTimeout;
Nadia Benbernou948627e2016-04-14 14:41:08 -04006186 that.mDismissalId = this.mDismissalId;
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04006187 that.mBridgeTag = this.mBridgeTag;
Griff Hazen61a9e862014-05-22 16:05:19 -07006188 return that;
6189 }
6190
6191 /**
6192 * Add a wearable action to this notification.
6193 *
6194 * <p>When wearable actions are added using this method, the set of actions that
6195 * show on a wearable device splits from devices that only show actions added
6196 * using {@link android.app.Notification.Builder#addAction}. This allows for customization
6197 * of which actions display on different devices.
6198 *
6199 * @param action the action to add to this notification
6200 * @return this object for method chaining
6201 * @see android.app.Notification.Action
6202 */
6203 public WearableExtender addAction(Action action) {
6204 mActions.add(action);
6205 return this;
6206 }
6207
6208 /**
6209 * Adds wearable actions to this notification.
6210 *
6211 * <p>When wearable actions are added using this method, the set of actions that
6212 * show on a wearable device splits from devices that only show actions added
6213 * using {@link android.app.Notification.Builder#addAction}. This allows for customization
6214 * of which actions display on different devices.
6215 *
6216 * @param actions the actions to add to this notification
6217 * @return this object for method chaining
6218 * @see android.app.Notification.Action
6219 */
6220 public WearableExtender addActions(List<Action> actions) {
6221 mActions.addAll(actions);
6222 return this;
6223 }
6224
6225 /**
6226 * Clear all wearable actions present on this builder.
6227 * @return this object for method chaining.
6228 * @see #addAction
6229 */
6230 public WearableExtender clearActions() {
6231 mActions.clear();
6232 return this;
6233 }
6234
6235 /**
6236 * Get the wearable actions present on this notification.
6237 */
6238 public List<Action> getActions() {
6239 return mActions;
6240 }
6241
6242 /**
6243 * Set an intent to launch inside of an activity view when displaying
Griff Hazen14f57992014-05-26 09:07:14 -07006244 * this notification. The {@link PendingIntent} provided should be for an activity.
6245 *
6246 * <pre class="prettyprint">
6247 * Intent displayIntent = new Intent(context, MyDisplayActivity.class);
6248 * PendingIntent displayPendingIntent = PendingIntent.getActivity(context,
6249 * 0, displayIntent, PendingIntent.FLAG_UPDATE_CURRENT);
6250 * Notification notif = new Notification.Builder(context)
6251 * .extend(new Notification.WearableExtender()
6252 * .setDisplayIntent(displayPendingIntent)
6253 * .setCustomSizePreset(Notification.WearableExtender.SIZE_MEDIUM))
6254 * .build();</pre>
6255 *
6256 * <p>The activity to launch needs to allow embedding, must be exported, and
Griff Hazen831ca9d2014-06-17 00:38:38 -07006257 * should have an empty task affinity. It is also recommended to use the device
6258 * default light theme.
Griff Hazen14f57992014-05-26 09:07:14 -07006259 *
6260 * <p>Example AndroidManifest.xml entry:
6261 * <pre class="prettyprint">
6262 * &lt;activity android:name=&quot;com.example.MyDisplayActivity&quot;
6263 * android:exported=&quot;true&quot;
6264 * android:allowEmbedded=&quot;true&quot;
Griff Hazen831ca9d2014-06-17 00:38:38 -07006265 * android:taskAffinity=&quot;&quot;
6266 * android:theme=&quot;@android:style/Theme.DeviceDefault.Light&quot; /&gt;</pre>
Griff Hazen61a9e862014-05-22 16:05:19 -07006267 *
6268 * @param intent the {@link PendingIntent} for an activity
6269 * @return this object for method chaining
6270 * @see android.app.Notification.WearableExtender#getDisplayIntent
6271 */
6272 public WearableExtender setDisplayIntent(PendingIntent intent) {
6273 mDisplayIntent = intent;
6274 return this;
6275 }
6276
6277 /**
6278 * Get the intent to launch inside of an activity view when displaying this
6279 * notification. This {@code PendingIntent} should be for an activity.
6280 */
6281 public PendingIntent getDisplayIntent() {
6282 return mDisplayIntent;
6283 }
6284
6285 /**
6286 * Add an additional page of content to display with this notification. The current
6287 * notification forms the first page, and pages added using this function form
6288 * subsequent pages. This field can be used to separate a notification into multiple
6289 * sections.
6290 *
6291 * @param page the notification to add as another page
6292 * @return this object for method chaining
6293 * @see android.app.Notification.WearableExtender#getPages
6294 */
6295 public WearableExtender addPage(Notification page) {
6296 mPages.add(page);
6297 return this;
6298 }
6299
6300 /**
6301 * Add additional pages of content to display with this notification. The current
6302 * notification forms the first page, and pages added using this function form
6303 * subsequent pages. This field can be used to separate a notification into multiple
6304 * sections.
6305 *
6306 * @param pages a list of notifications
6307 * @return this object for method chaining
6308 * @see android.app.Notification.WearableExtender#getPages
6309 */
6310 public WearableExtender addPages(List<Notification> pages) {
6311 mPages.addAll(pages);
6312 return this;
6313 }
6314
6315 /**
6316 * Clear all additional pages present on this builder.
6317 * @return this object for method chaining.
6318 * @see #addPage
6319 */
6320 public WearableExtender clearPages() {
6321 mPages.clear();
6322 return this;
6323 }
6324
6325 /**
6326 * Get the array of additional pages of content for displaying this notification. The
6327 * current notification forms the first page, and elements within this array form
6328 * subsequent pages. This field can be used to separate a notification into multiple
6329 * sections.
6330 * @return the pages for this notification
6331 */
6332 public List<Notification> getPages() {
6333 return mPages;
6334 }
6335
6336 /**
6337 * Set a background image to be displayed behind the notification content.
6338 * Contrary to the {@link android.app.Notification.BigPictureStyle}, this background
6339 * will work with any notification style.
6340 *
6341 * @param background the background bitmap
6342 * @return this object for method chaining
6343 * @see android.app.Notification.WearableExtender#getBackground
6344 */
6345 public WearableExtender setBackground(Bitmap background) {
6346 mBackground = background;
6347 return this;
6348 }
6349
6350 /**
6351 * Get a background image to be displayed behind the notification content.
6352 * Contrary to the {@link android.app.Notification.BigPictureStyle}, this background
6353 * will work with any notification style.
6354 *
6355 * @return the background image
6356 * @see android.app.Notification.WearableExtender#setBackground
6357 */
6358 public Bitmap getBackground() {
6359 return mBackground;
6360 }
6361
6362 /**
6363 * Set an icon that goes with the content of this notification.
6364 */
6365 public WearableExtender setContentIcon(int icon) {
6366 mContentIcon = icon;
6367 return this;
6368 }
6369
6370 /**
6371 * Get an icon that goes with the content of this notification.
6372 */
6373 public int getContentIcon() {
6374 return mContentIcon;
6375 }
6376
6377 /**
6378 * Set the gravity that the content icon should have within the notification display.
6379 * Supported values include {@link android.view.Gravity#START} and
6380 * {@link android.view.Gravity#END}. The default value is {@link android.view.Gravity#END}.
6381 * @see #setContentIcon
6382 */
6383 public WearableExtender setContentIconGravity(int contentIconGravity) {
6384 mContentIconGravity = contentIconGravity;
6385 return this;
6386 }
6387
6388 /**
6389 * Get the gravity that the content icon should have within the notification display.
6390 * Supported values include {@link android.view.Gravity#START} and
6391 * {@link android.view.Gravity#END}. The default value is {@link android.view.Gravity#END}.
6392 * @see #getContentIcon
6393 */
6394 public int getContentIconGravity() {
6395 return mContentIconGravity;
6396 }
6397
6398 /**
6399 * Set an action from this notification's actions to be clickable with the content of
Griff Hazen14f57992014-05-26 09:07:14 -07006400 * this notification. This action will no longer display separately from the
6401 * notification's content.
6402 *
Griff Hazenca48d352014-05-28 22:37:13 -07006403 * <p>For notifications with multiple pages, child pages can also have content actions
Griff Hazen14f57992014-05-26 09:07:14 -07006404 * set, although the list of available actions comes from the main notification and not
6405 * from the child page's notification.
6406 *
6407 * @param actionIndex The index of the action to hoist onto the current notification page.
6408 * If wearable actions were added to the main notification, this index
6409 * will apply to that list, otherwise it will apply to the regular
6410 * actions list.
Griff Hazen61a9e862014-05-22 16:05:19 -07006411 */
6412 public WearableExtender setContentAction(int actionIndex) {
6413 mContentActionIndex = actionIndex;
6414 return this;
6415 }
6416
6417 /**
Griff Hazenca48d352014-05-28 22:37:13 -07006418 * Get the index of the notification action, if any, that was specified as being clickable
6419 * with the content of this notification. This action will no longer display separately
Griff Hazen14f57992014-05-26 09:07:14 -07006420 * from the notification's content.
Griff Hazen61a9e862014-05-22 16:05:19 -07006421 *
Griff Hazenca48d352014-05-28 22:37:13 -07006422 * <p>For notifications with multiple pages, child pages can also have content actions
Griff Hazen14f57992014-05-26 09:07:14 -07006423 * set, although the list of available actions comes from the main notification and not
6424 * from the child page's notification.
6425 *
6426 * <p>If wearable specific actions were added to the main notification, this index will
6427 * apply to that list, otherwise it will apply to the regular actions list.
Griff Hazenca48d352014-05-28 22:37:13 -07006428 *
6429 * @return the action index or {@link #UNSET_ACTION_INDEX} if no action was selected.
Griff Hazen61a9e862014-05-22 16:05:19 -07006430 */
6431 public int getContentAction() {
6432 return mContentActionIndex;
6433 }
6434
6435 /**
6436 * Set the gravity that this notification should have within the available viewport space.
6437 * Supported values include {@link android.view.Gravity#TOP},
6438 * {@link android.view.Gravity#CENTER_VERTICAL} and {@link android.view.Gravity#BOTTOM}.
6439 * The default value is {@link android.view.Gravity#BOTTOM}.
6440 */
6441 public WearableExtender setGravity(int gravity) {
6442 mGravity = gravity;
6443 return this;
6444 }
6445
6446 /**
6447 * Get the gravity that this notification should have within the available viewport space.
6448 * Supported values include {@link android.view.Gravity#TOP},
6449 * {@link android.view.Gravity#CENTER_VERTICAL} and {@link android.view.Gravity#BOTTOM}.
6450 * The default value is {@link android.view.Gravity#BOTTOM}.
6451 */
6452 public int getGravity() {
6453 return mGravity;
6454 }
6455
6456 /**
6457 * Set the custom size preset for the display of this notification out of the available
6458 * presets found in {@link android.app.Notification.WearableExtender}, e.g.
6459 * {@link #SIZE_LARGE}.
6460 * <p>Some custom size presets are only applicable for custom display notifications created
6461 * using {@link android.app.Notification.WearableExtender#setDisplayIntent}. Check the
6462 * documentation for the preset in question. See also
6463 * {@link #setCustomContentHeight} and {@link #getCustomSizePreset}.
6464 */
6465 public WearableExtender setCustomSizePreset(int sizePreset) {
6466 mCustomSizePreset = sizePreset;
6467 return this;
6468 }
6469
6470 /**
6471 * Get the custom size preset for the display of this notification out of the available
6472 * presets found in {@link android.app.Notification.WearableExtender}, e.g.
6473 * {@link #SIZE_LARGE}.
6474 * <p>Some custom size presets are only applicable for custom display notifications created
6475 * using {@link #setDisplayIntent}. Check the documentation for the preset in question.
6476 * See also {@link #setCustomContentHeight} and {@link #setCustomSizePreset}.
6477 */
6478 public int getCustomSizePreset() {
6479 return mCustomSizePreset;
6480 }
6481
6482 /**
6483 * Set the custom height in pixels for the display of this notification's content.
6484 * <p>This option is only available for custom display notifications created
6485 * using {@link android.app.Notification.WearableExtender#setDisplayIntent}. See also
6486 * {@link android.app.Notification.WearableExtender#setCustomSizePreset} and
6487 * {@link #getCustomContentHeight}.
6488 */
6489 public WearableExtender setCustomContentHeight(int height) {
6490 mCustomContentHeight = height;
6491 return this;
6492 }
6493
6494 /**
6495 * Get the custom height in pixels for the display of this notification's content.
6496 * <p>This option is only available for custom display notifications created
6497 * using {@link #setDisplayIntent}. See also {@link #setCustomSizePreset} and
6498 * {@link #setCustomContentHeight}.
6499 */
6500 public int getCustomContentHeight() {
6501 return mCustomContentHeight;
6502 }
6503
6504 /**
6505 * Set whether the scrolling position for the contents of this notification should start
6506 * at the bottom of the contents instead of the top when the contents are too long to
6507 * display within the screen. Default is false (start scroll at the top).
6508 */
6509 public WearableExtender setStartScrollBottom(boolean startScrollBottom) {
6510 setFlag(FLAG_START_SCROLL_BOTTOM, startScrollBottom);
6511 return this;
6512 }
6513
6514 /**
6515 * Get whether the scrolling position for the contents of this notification should start
6516 * at the bottom of the contents instead of the top when the contents are too long to
6517 * display within the screen. Default is false (start scroll at the top).
6518 */
6519 public boolean getStartScrollBottom() {
6520 return (mFlags & FLAG_START_SCROLL_BOTTOM) != 0;
6521 }
6522
6523 /**
6524 * Set whether the content intent is available when the wearable device is not connected
6525 * to a companion device. The user can still trigger this intent when the wearable device
6526 * is offline, but a visual hint will indicate that the content intent may not be available.
6527 * Defaults to true.
6528 */
6529 public WearableExtender setContentIntentAvailableOffline(
6530 boolean contentIntentAvailableOffline) {
6531 setFlag(FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE, contentIntentAvailableOffline);
6532 return this;
6533 }
6534
6535 /**
6536 * Get whether the content intent is available when the wearable device is not connected
6537 * to a companion device. The user can still trigger this intent when the wearable device
6538 * is offline, but a visual hint will indicate that the content intent may not be available.
6539 * Defaults to true.
6540 */
6541 public boolean getContentIntentAvailableOffline() {
6542 return (mFlags & FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE) != 0;
6543 }
6544
6545 /**
6546 * Set a hint that this notification's icon should not be displayed.
6547 * @param hintHideIcon {@code true} to hide the icon, {@code false} otherwise.
6548 * @return this object for method chaining
6549 */
6550 public WearableExtender setHintHideIcon(boolean hintHideIcon) {
6551 setFlag(FLAG_HINT_HIDE_ICON, hintHideIcon);
6552 return this;
6553 }
6554
6555 /**
6556 * Get a hint that this notification's icon should not be displayed.
6557 * @return {@code true} if this icon should not be displayed, false otherwise.
6558 * The default value is {@code false} if this was never set.
6559 */
6560 public boolean getHintHideIcon() {
6561 return (mFlags & FLAG_HINT_HIDE_ICON) != 0;
6562 }
6563
6564 /**
6565 * Set a visual hint that only the background image of this notification should be
6566 * displayed, and other semantic content should be hidden. This hint is only applicable
6567 * to sub-pages added using {@link #addPage}.
6568 */
6569 public WearableExtender setHintShowBackgroundOnly(boolean hintShowBackgroundOnly) {
6570 setFlag(FLAG_HINT_SHOW_BACKGROUND_ONLY, hintShowBackgroundOnly);
6571 return this;
6572 }
6573
6574 /**
6575 * Get a visual hint that only the background image of this notification should be
6576 * displayed, and other semantic content should be hidden. This hint is only applicable
6577 * to sub-pages added using {@link android.app.Notification.WearableExtender#addPage}.
6578 */
6579 public boolean getHintShowBackgroundOnly() {
6580 return (mFlags & FLAG_HINT_SHOW_BACKGROUND_ONLY) != 0;
6581 }
6582
Griff Hazen5f2edfc2014-09-29 16:28:44 -07006583 /**
Griff Hazen9c5be4e2014-11-17 17:47:50 -08006584 * Set a hint that this notification's background should not be clipped if possible,
6585 * and should instead be resized to fully display on the screen, retaining the aspect
6586 * ratio of the image. This can be useful for images like barcodes or qr codes.
Griff Hazen5f2edfc2014-09-29 16:28:44 -07006587 * @param hintAvoidBackgroundClipping {@code true} to avoid clipping if possible.
6588 * @return this object for method chaining
6589 */
6590 public WearableExtender setHintAvoidBackgroundClipping(
6591 boolean hintAvoidBackgroundClipping) {
6592 setFlag(FLAG_HINT_AVOID_BACKGROUND_CLIPPING, hintAvoidBackgroundClipping);
6593 return this;
6594 }
6595
6596 /**
Griff Hazen9c5be4e2014-11-17 17:47:50 -08006597 * Get a hint that this notification's background should not be clipped if possible,
6598 * and should instead be resized to fully display on the screen, retaining the aspect
6599 * ratio of the image. This can be useful for images like barcodes or qr codes.
Griff Hazen5f2edfc2014-09-29 16:28:44 -07006600 * @return {@code true} if it's ok if the background is clipped on the screen, false
6601 * otherwise. The default value is {@code false} if this was never set.
6602 */
6603 public boolean getHintAvoidBackgroundClipping() {
6604 return (mFlags & FLAG_HINT_AVOID_BACKGROUND_CLIPPING) != 0;
6605 }
6606
6607 /**
6608 * Set a hint that the screen should remain on for at least this duration when
6609 * this notification is displayed on the screen.
6610 * @param timeout The requested screen timeout in milliseconds. Can also be either
6611 * {@link #SCREEN_TIMEOUT_SHORT} or {@link #SCREEN_TIMEOUT_LONG}.
6612 * @return this object for method chaining
6613 */
6614 public WearableExtender setHintScreenTimeout(int timeout) {
6615 mHintScreenTimeout = timeout;
6616 return this;
6617 }
6618
6619 /**
6620 * Get the duration, in milliseconds, that the screen should remain on for
6621 * when this notification is displayed.
6622 * @return the duration in milliseconds if > 0, or either one of the sentinel values
6623 * {@link #SCREEN_TIMEOUT_SHORT} or {@link #SCREEN_TIMEOUT_LONG}.
6624 */
6625 public int getHintScreenTimeout() {
6626 return mHintScreenTimeout;
6627 }
6628
Alex Hills9ab3a232016-04-05 14:54:56 -04006629 /**
Alex Hills4bcb06b2016-04-05 14:26:25 -04006630 * Set a hint that this notification's {@link BigPictureStyle} (if present) should be
6631 * converted to low-bit and displayed in ambient mode, especially useful for barcodes and
6632 * qr codes, as well as other simple black-and-white tickets.
6633 * @param hintAmbientBigPicture {@code true} to enable converstion and ambient.
6634 * @return this object for method chaining
6635 */
6636 public WearableExtender setHintAmbientBigPicture(boolean hintAmbientBigPicture) {
6637 setFlag(FLAG_BIG_PICTURE_AMBIENT, hintAmbientBigPicture);
6638 return this;
6639 }
6640
6641 /**
6642 * Get a hint that this notification's {@link BigPictureStyle} (if present) should be
6643 * converted to low-bit and displayed in ambient mode, especially useful for barcodes and
6644 * qr codes, as well as other simple black-and-white tickets.
6645 * @return {@code true} if it should be displayed in ambient, false otherwise
6646 * otherwise. The default value is {@code false} if this was never set.
6647 */
6648 public boolean getHintAmbientBigPicture() {
6649 return (mFlags & FLAG_BIG_PICTURE_AMBIENT) != 0;
6650 }
6651
6652 /**
Alex Hills9ab3a232016-04-05 14:54:56 -04006653 * Set a hint that this notification's content intent will launch an {@link Activity}
6654 * directly, telling the platform that it can generate the appropriate transitions.
6655 * @param hintContentIntentLaunchesActivity {@code true} if the content intent will launch
6656 * an activity and transitions should be generated, false otherwise.
6657 * @return this object for method chaining
6658 */
6659 public WearableExtender setHintContentIntentLaunchesActivity(
6660 boolean hintContentIntentLaunchesActivity) {
6661 setFlag(FLAG_HINT_CONTENT_INTENT_LAUNCHES_ACTIVITY, hintContentIntentLaunchesActivity);
6662 return this;
6663 }
6664
6665 /**
6666 * Get a hint that this notification's content intent will launch an {@link Activity}
6667 * directly, telling the platform that it can generate the appropriate transitions
6668 * @return {@code true} if the content intent will launch an activity and transitions should
6669 * be generated, false otherwise. The default value is {@code false} if this was never set.
6670 */
6671 public boolean getHintContentIntentLaunchesActivity() {
6672 return (mFlags & FLAG_HINT_CONTENT_INTENT_LAUNCHES_ACTIVITY) != 0;
6673 }
6674
Nadia Benbernou948627e2016-04-14 14:41:08 -04006675 /**
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04006676 * Sets the dismissal id for this notification. If a notification is posted with a
6677 * dismissal id, then when that notification is canceled, notifications on other wearables
6678 * and the paired Android phone having that same dismissal id will also be canceled. See
Nadia Benbernou948627e2016-04-14 14:41:08 -04006679 * <a href="{@docRoot}wear/notifications/index.html">Adding Wearable Features to
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04006680 * Notifications</a> for more information.
Nadia Benbernou948627e2016-04-14 14:41:08 -04006681 * @param dismissalId the dismissal id of the notification.
6682 * @return this object for method chaining
6683 */
6684 public WearableExtender setDismissalId(String dismissalId) {
6685 mDismissalId = dismissalId;
6686 return this;
6687 }
6688
6689 /**
6690 * Returns the dismissal id of the notification.
6691 * @return the dismissal id of the notification or null if it has not been set.
6692 */
6693 public String getDismissalId() {
6694 return mDismissalId;
6695 }
6696
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04006697 /**
6698 * Sets a bridge tag for this notification. A bridge tag can be set for notifications
6699 * posted from a phone to provide finer-grained control on what notifications are bridged
6700 * to wearables. See <a href="{@docRoot}wear/notifications/index.html">Adding Wearable
6701 * Features to Notifications</a> for more information.
6702 * @param bridgeTag the bridge tag of the notification.
6703 * @return this object for method chaining
6704 */
6705 public WearableExtender setBridgeTag(String bridgeTag) {
6706 mBridgeTag = bridgeTag;
6707 return this;
6708 }
6709
6710 /**
6711 * Returns the bridge tag of the notification.
6712 * @return the bridge tag or null if not present.
6713 */
6714 public String getBridgeTag() {
6715 return mBridgeTag;
6716 }
6717
Griff Hazen61a9e862014-05-22 16:05:19 -07006718 private void setFlag(int mask, boolean value) {
6719 if (value) {
6720 mFlags |= mask;
6721 } else {
6722 mFlags &= ~mask;
6723 }
6724 }
6725 }
6726
6727 /**
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08006728 * <p>Helper class to add Android Auto extensions to notifications. To create a notification
6729 * with car extensions:
6730 *
6731 * <ol>
6732 * <li>Create an {@link Notification.Builder}, setting any desired
6733 * properties.
6734 * <li>Create a {@link CarExtender}.
6735 * <li>Set car-specific properties using the {@code add} and {@code set} methods of
6736 * {@link CarExtender}.
6737 * <li>Call {@link Notification.Builder#extend(Notification.Extender)}
6738 * to apply the extensions to a notification.
6739 * </ol>
6740 *
6741 * <pre class="prettyprint">
6742 * Notification notification = new Notification.Builder(context)
6743 * ...
6744 * .extend(new CarExtender()
6745 * .set*(...))
6746 * .build();
6747 * </pre>
6748 *
6749 * <p>Car extensions can be accessed on an existing notification by using the
6750 * {@code CarExtender(Notification)} constructor, and then using the {@code get} methods
6751 * to access values.
6752 */
6753 public static final class CarExtender implements Extender {
6754 private static final String TAG = "CarExtender";
6755
6756 private static final String EXTRA_CAR_EXTENDER = "android.car.EXTENSIONS";
6757 private static final String EXTRA_LARGE_ICON = "large_icon";
6758 private static final String EXTRA_CONVERSATION = "car_conversation";
6759 private static final String EXTRA_COLOR = "app_color";
6760
6761 private Bitmap mLargeIcon;
6762 private UnreadConversation mUnreadConversation;
6763 private int mColor = Notification.COLOR_DEFAULT;
6764
6765 /**
6766 * Create a {@link CarExtender} with default options.
6767 */
6768 public CarExtender() {
6769 }
6770
6771 /**
6772 * Create a {@link CarExtender} from the CarExtender options of an existing Notification.
6773 *
6774 * @param notif The notification from which to copy options.
6775 */
6776 public CarExtender(Notification notif) {
6777 Bundle carBundle = notif.extras == null ?
6778 null : notif.extras.getBundle(EXTRA_CAR_EXTENDER);
6779 if (carBundle != null) {
6780 mLargeIcon = carBundle.getParcelable(EXTRA_LARGE_ICON);
6781 mColor = carBundle.getInt(EXTRA_COLOR, Notification.COLOR_DEFAULT);
6782
6783 Bundle b = carBundle.getBundle(EXTRA_CONVERSATION);
6784 mUnreadConversation = UnreadConversation.getUnreadConversationFromBundle(b);
6785 }
6786 }
6787
6788 /**
6789 * Apply car extensions to a notification that is being built. This is typically called by
6790 * the {@link Notification.Builder#extend(Notification.Extender)}
6791 * method of {@link Notification.Builder}.
6792 */
6793 @Override
6794 public Notification.Builder extend(Notification.Builder builder) {
6795 Bundle carExtensions = new Bundle();
6796
6797 if (mLargeIcon != null) {
6798 carExtensions.putParcelable(EXTRA_LARGE_ICON, mLargeIcon);
6799 }
6800 if (mColor != Notification.COLOR_DEFAULT) {
6801 carExtensions.putInt(EXTRA_COLOR, mColor);
6802 }
6803
6804 if (mUnreadConversation != null) {
6805 Bundle b = mUnreadConversation.getBundleForUnreadConversation();
6806 carExtensions.putBundle(EXTRA_CONVERSATION, b);
6807 }
6808
6809 builder.getExtras().putBundle(EXTRA_CAR_EXTENDER, carExtensions);
6810 return builder;
6811 }
6812
6813 /**
6814 * Sets the accent color to use when Android Auto presents the notification.
6815 *
6816 * Android Auto uses the color set with {@link Notification.Builder#setColor(int)}
6817 * to accent the displayed notification. However, not all colors are acceptable in an
6818 * automotive setting. This method can be used to override the color provided in the
6819 * notification in such a situation.
6820 */
Tor Norbye80756e32015-03-02 09:39:27 -08006821 public CarExtender setColor(@ColorInt int color) {
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08006822 mColor = color;
6823 return this;
6824 }
6825
6826 /**
6827 * Gets the accent color.
6828 *
Julia Reynoldsd9228f12015-10-20 10:37:27 -04006829 * @see #setColor
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08006830 */
Tor Norbye80756e32015-03-02 09:39:27 -08006831 @ColorInt
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08006832 public int getColor() {
6833 return mColor;
6834 }
6835
6836 /**
6837 * Sets the large icon of the car notification.
6838 *
6839 * If no large icon is set in the extender, Android Auto will display the icon
6840 * specified by {@link Notification.Builder#setLargeIcon(android.graphics.Bitmap)}
6841 *
6842 * @param largeIcon The large icon to use in the car notification.
6843 * @return This object for method chaining.
6844 */
6845 public CarExtender setLargeIcon(Bitmap largeIcon) {
6846 mLargeIcon = largeIcon;
6847 return this;
6848 }
6849
6850 /**
6851 * Gets the large icon used in this car notification, or null if no icon has been set.
6852 *
6853 * @return The large icon for the car notification.
6854 * @see CarExtender#setLargeIcon
6855 */
6856 public Bitmap getLargeIcon() {
6857 return mLargeIcon;
6858 }
6859
6860 /**
6861 * Sets the unread conversation in a message notification.
6862 *
6863 * @param unreadConversation The unread part of the conversation this notification conveys.
6864 * @return This object for method chaining.
6865 */
6866 public CarExtender setUnreadConversation(UnreadConversation unreadConversation) {
6867 mUnreadConversation = unreadConversation;
6868 return this;
6869 }
6870
6871 /**
6872 * Returns the unread conversation conveyed by this notification.
6873 * @see #setUnreadConversation(UnreadConversation)
6874 */
6875 public UnreadConversation getUnreadConversation() {
6876 return mUnreadConversation;
6877 }
6878
6879 /**
6880 * A class which holds the unread messages from a conversation.
6881 */
6882 public static class UnreadConversation {
6883 private static final String KEY_AUTHOR = "author";
6884 private static final String KEY_TEXT = "text";
6885 private static final String KEY_MESSAGES = "messages";
6886 private static final String KEY_REMOTE_INPUT = "remote_input";
6887 private static final String KEY_ON_REPLY = "on_reply";
6888 private static final String KEY_ON_READ = "on_read";
6889 private static final String KEY_PARTICIPANTS = "participants";
6890 private static final String KEY_TIMESTAMP = "timestamp";
6891
6892 private final String[] mMessages;
6893 private final RemoteInput mRemoteInput;
6894 private final PendingIntent mReplyPendingIntent;
6895 private final PendingIntent mReadPendingIntent;
6896 private final String[] mParticipants;
6897 private final long mLatestTimestamp;
6898
6899 UnreadConversation(String[] messages, RemoteInput remoteInput,
6900 PendingIntent replyPendingIntent, PendingIntent readPendingIntent,
6901 String[] participants, long latestTimestamp) {
6902 mMessages = messages;
6903 mRemoteInput = remoteInput;
6904 mReadPendingIntent = readPendingIntent;
6905 mReplyPendingIntent = replyPendingIntent;
6906 mParticipants = participants;
6907 mLatestTimestamp = latestTimestamp;
6908 }
6909
6910 /**
6911 * Gets the list of messages conveyed by this notification.
6912 */
6913 public String[] getMessages() {
6914 return mMessages;
6915 }
6916
6917 /**
6918 * Gets the remote input that will be used to convey the response to a message list, or
6919 * null if no such remote input exists.
6920 */
6921 public RemoteInput getRemoteInput() {
6922 return mRemoteInput;
6923 }
6924
6925 /**
6926 * Gets the pending intent that will be triggered when the user replies to this
6927 * notification.
6928 */
6929 public PendingIntent getReplyPendingIntent() {
6930 return mReplyPendingIntent;
6931 }
6932
6933 /**
6934 * Gets the pending intent that Android Auto will send after it reads aloud all messages
6935 * in this object's message list.
6936 */
6937 public PendingIntent getReadPendingIntent() {
6938 return mReadPendingIntent;
6939 }
6940
6941 /**
6942 * Gets the participants in the conversation.
6943 */
6944 public String[] getParticipants() {
6945 return mParticipants;
6946 }
6947
6948 /**
6949 * Gets the firs participant in the conversation.
6950 */
6951 public String getParticipant() {
6952 return mParticipants.length > 0 ? mParticipants[0] : null;
6953 }
6954
6955 /**
6956 * Gets the timestamp of the conversation.
6957 */
6958 public long getLatestTimestamp() {
6959 return mLatestTimestamp;
6960 }
6961
6962 Bundle getBundleForUnreadConversation() {
6963 Bundle b = new Bundle();
6964 String author = null;
6965 if (mParticipants != null && mParticipants.length > 1) {
6966 author = mParticipants[0];
6967 }
6968 Parcelable[] messages = new Parcelable[mMessages.length];
6969 for (int i = 0; i < messages.length; i++) {
6970 Bundle m = new Bundle();
6971 m.putString(KEY_TEXT, mMessages[i]);
6972 m.putString(KEY_AUTHOR, author);
6973 messages[i] = m;
6974 }
6975 b.putParcelableArray(KEY_MESSAGES, messages);
6976 if (mRemoteInput != null) {
6977 b.putParcelable(KEY_REMOTE_INPUT, mRemoteInput);
6978 }
6979 b.putParcelable(KEY_ON_REPLY, mReplyPendingIntent);
6980 b.putParcelable(KEY_ON_READ, mReadPendingIntent);
6981 b.putStringArray(KEY_PARTICIPANTS, mParticipants);
6982 b.putLong(KEY_TIMESTAMP, mLatestTimestamp);
6983 return b;
6984 }
6985
6986 static UnreadConversation getUnreadConversationFromBundle(Bundle b) {
6987 if (b == null) {
6988 return null;
6989 }
6990 Parcelable[] parcelableMessages = b.getParcelableArray(KEY_MESSAGES);
6991 String[] messages = null;
6992 if (parcelableMessages != null) {
6993 String[] tmp = new String[parcelableMessages.length];
6994 boolean success = true;
6995 for (int i = 0; i < tmp.length; i++) {
6996 if (!(parcelableMessages[i] instanceof Bundle)) {
6997 success = false;
6998 break;
6999 }
7000 tmp[i] = ((Bundle) parcelableMessages[i]).getString(KEY_TEXT);
7001 if (tmp[i] == null) {
7002 success = false;
7003 break;
7004 }
7005 }
7006 if (success) {
7007 messages = tmp;
7008 } else {
7009 return null;
7010 }
7011 }
7012
7013 PendingIntent onRead = b.getParcelable(KEY_ON_READ);
7014 PendingIntent onReply = b.getParcelable(KEY_ON_REPLY);
7015
7016 RemoteInput remoteInput = b.getParcelable(KEY_REMOTE_INPUT);
7017
7018 String[] participants = b.getStringArray(KEY_PARTICIPANTS);
7019 if (participants == null || participants.length != 1) {
7020 return null;
7021 }
7022
7023 return new UnreadConversation(messages,
7024 remoteInput,
7025 onReply,
7026 onRead,
7027 participants, b.getLong(KEY_TIMESTAMP));
7028 }
7029 };
7030
7031 /**
7032 * Builder class for {@link CarExtender.UnreadConversation} objects.
7033 */
7034 public static class Builder {
7035 private final List<String> mMessages = new ArrayList<String>();
7036 private final String mParticipant;
7037 private RemoteInput mRemoteInput;
7038 private PendingIntent mReadPendingIntent;
7039 private PendingIntent mReplyPendingIntent;
7040 private long mLatestTimestamp;
7041
7042 /**
7043 * Constructs a new builder for {@link CarExtender.UnreadConversation}.
7044 *
7045 * @param name The name of the other participant in the conversation.
7046 */
7047 public Builder(String name) {
7048 mParticipant = name;
7049 }
7050
7051 /**
7052 * Appends a new unread message to the list of messages for this conversation.
7053 *
7054 * The messages should be added from oldest to newest.
7055 *
7056 * @param message The text of the new unread message.
7057 * @return This object for method chaining.
7058 */
7059 public Builder addMessage(String message) {
7060 mMessages.add(message);
7061 return this;
7062 }
7063
7064 /**
7065 * Sets the pending intent and remote input which will convey the reply to this
7066 * notification.
7067 *
7068 * @param pendingIntent The pending intent which will be triggered on a reply.
7069 * @param remoteInput The remote input parcelable which will carry the reply.
7070 * @return This object for method chaining.
7071 *
7072 * @see CarExtender.UnreadConversation#getRemoteInput
7073 * @see CarExtender.UnreadConversation#getReplyPendingIntent
7074 */
7075 public Builder setReplyAction(
7076 PendingIntent pendingIntent, RemoteInput remoteInput) {
7077 mRemoteInput = remoteInput;
7078 mReplyPendingIntent = pendingIntent;
7079
7080 return this;
7081 }
7082
7083 /**
7084 * Sets the pending intent that will be sent once the messages in this notification
7085 * are read.
7086 *
7087 * @param pendingIntent The pending intent to use.
7088 * @return This object for method chaining.
7089 */
7090 public Builder setReadPendingIntent(PendingIntent pendingIntent) {
7091 mReadPendingIntent = pendingIntent;
7092 return this;
7093 }
7094
7095 /**
7096 * Sets the timestamp of the most recent message in an unread conversation.
7097 *
7098 * If a messaging notification has been posted by your application and has not
7099 * yet been cancelled, posting a later notification with the same id and tag
7100 * but without a newer timestamp may result in Android Auto not displaying a
7101 * heads up notification for the later notification.
7102 *
7103 * @param timestamp The timestamp of the most recent message in the conversation.
7104 * @return This object for method chaining.
7105 */
7106 public Builder setLatestTimestamp(long timestamp) {
7107 mLatestTimestamp = timestamp;
7108 return this;
7109 }
7110
7111 /**
7112 * Builds a new unread conversation object.
7113 *
7114 * @return The new unread conversation object.
7115 */
7116 public UnreadConversation build() {
7117 String[] messages = mMessages.toArray(new String[mMessages.size()]);
7118 String[] participants = { mParticipant };
7119 return new UnreadConversation(messages, mRemoteInput, mReplyPendingIntent,
7120 mReadPendingIntent, participants, mLatestTimestamp);
7121 }
7122 }
7123 }
7124
7125 /**
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08007126 * <p>Helper class to add Android TV extensions to notifications. To create a notification
7127 * with a TV extension:
7128 *
7129 * <ol>
7130 * <li>Create an {@link Notification.Builder}, setting any desired properties.
7131 * <li>Create a {@link TvExtender}.
7132 * <li>Set TV-specific properties using the {@code set} methods of
7133 * {@link TvExtender}.
7134 * <li>Call {@link Notification.Builder#extend(Notification.Extender)}
7135 * to apply the extension to a notification.
7136 * </ol>
7137 *
7138 * <pre class="prettyprint">
7139 * Notification notification = new Notification.Builder(context)
7140 * ...
7141 * .extend(new TvExtender()
7142 * .set*(...))
7143 * .build();
7144 * </pre>
7145 *
7146 * <p>TV extensions can be accessed on an existing notification by using the
7147 * {@code TvExtender(Notification)} constructor, and then using the {@code get} methods
7148 * to access values.
7149 *
7150 * @hide
7151 */
7152 @SystemApi
7153 public static final class TvExtender implements Extender {
7154 private static final String TAG = "TvExtender";
7155
7156 private static final String EXTRA_TV_EXTENDER = "android.tv.EXTENSIONS";
7157 private static final String EXTRA_FLAGS = "flags";
7158 private static final String EXTRA_CONTENT_INTENT = "content_intent";
7159 private static final String EXTRA_DELETE_INTENT = "delete_intent";
7160
7161 // Flags bitwise-ored to mFlags
7162 private static final int FLAG_AVAILABLE_ON_TV = 0x1;
7163
7164 private int mFlags;
7165 private PendingIntent mContentIntent;
7166 private PendingIntent mDeleteIntent;
7167
7168 /**
7169 * Create a {@link TvExtender} with default options.
7170 */
7171 public TvExtender() {
7172 mFlags = FLAG_AVAILABLE_ON_TV;
7173 }
7174
7175 /**
7176 * Create a {@link TvExtender} from the TvExtender options of an existing Notification.
7177 *
7178 * @param notif The notification from which to copy options.
7179 */
7180 public TvExtender(Notification notif) {
7181 Bundle bundle = notif.extras == null ?
7182 null : notif.extras.getBundle(EXTRA_TV_EXTENDER);
7183 if (bundle != null) {
7184 mFlags = bundle.getInt(EXTRA_FLAGS);
7185 mContentIntent = bundle.getParcelable(EXTRA_CONTENT_INTENT);
7186 mDeleteIntent = bundle.getParcelable(EXTRA_DELETE_INTENT);
7187 }
7188 }
7189
7190 /**
7191 * Apply a TV extension to a notification that is being built. This is typically called by
7192 * the {@link Notification.Builder#extend(Notification.Extender)}
7193 * method of {@link Notification.Builder}.
7194 */
7195 @Override
7196 public Notification.Builder extend(Notification.Builder builder) {
7197 Bundle bundle = new Bundle();
7198
7199 bundle.putInt(EXTRA_FLAGS, mFlags);
7200 if (mContentIntent != null) {
7201 bundle.putParcelable(EXTRA_CONTENT_INTENT, mContentIntent);
7202 }
7203
7204 if (mDeleteIntent != null) {
7205 bundle.putParcelable(EXTRA_DELETE_INTENT, mDeleteIntent);
7206 }
7207
7208 builder.getExtras().putBundle(EXTRA_TV_EXTENDER, bundle);
7209 return builder;
7210 }
7211
7212 /**
7213 * Returns true if this notification should be shown on TV. This method return true
7214 * if the notification was extended with a TvExtender.
7215 */
7216 public boolean isAvailableOnTv() {
7217 return (mFlags & FLAG_AVAILABLE_ON_TV) != 0;
7218 }
7219
7220 /**
7221 * Supplies a {@link PendingIntent} to be sent when the notification is selected on TV.
7222 * If provided, it is used instead of the content intent specified
7223 * at the level of Notification.
7224 */
7225 public TvExtender setContentIntent(PendingIntent intent) {
7226 mContentIntent = intent;
7227 return this;
7228 }
7229
7230 /**
7231 * Returns the TV-specific content intent. If this method returns null, the
7232 * main content intent on the notification should be used.
7233 *
7234 * @see {@link Notification#contentIntent}
7235 */
7236 public PendingIntent getContentIntent() {
7237 return mContentIntent;
7238 }
7239
7240 /**
7241 * Supplies a {@link PendingIntent} to send when the notification is cleared explicitly
7242 * by the user on TV. If provided, it is used instead of the delete intent specified
7243 * at the level of Notification.
7244 */
7245 public TvExtender setDeleteIntent(PendingIntent intent) {
7246 mDeleteIntent = intent;
7247 return this;
7248 }
7249
7250 /**
7251 * Returns the TV-specific delete intent. If this method returns null, the
7252 * main delete intent on the notification should be used.
7253 *
7254 * @see {@link Notification#deleteIntent}
7255 */
7256 public PendingIntent getDeleteIntent() {
7257 return mDeleteIntent;
7258 }
7259 }
7260
7261 /**
Griff Hazen61a9e862014-05-22 16:05:19 -07007262 * Get an array of Notification objects from a parcelable array bundle field.
7263 * Update the bundle to have a typed array so fetches in the future don't need
7264 * to do an array copy.
7265 */
7266 private static Notification[] getNotificationArrayFromBundle(Bundle bundle, String key) {
7267 Parcelable[] array = bundle.getParcelableArray(key);
7268 if (array instanceof Notification[] || array == null) {
7269 return (Notification[]) array;
7270 }
7271 Notification[] typedArray = Arrays.copyOf(array, array.length,
7272 Notification[].class);
7273 bundle.putParcelableArray(key, typedArray);
7274 return typedArray;
7275 }
Christoph Studer4600f9b2014-07-22 22:44:43 +02007276
7277 private static class BuilderRemoteViews extends RemoteViews {
7278 public BuilderRemoteViews(Parcel parcel) {
7279 super(parcel);
7280 }
7281
Kenny Guy77320062014-08-27 21:37:15 +01007282 public BuilderRemoteViews(ApplicationInfo appInfo, int layoutId) {
7283 super(appInfo, layoutId);
Christoph Studer4600f9b2014-07-22 22:44:43 +02007284 }
7285
7286 @Override
7287 public BuilderRemoteViews clone() {
7288 Parcel p = Parcel.obtain();
7289 writeToParcel(p, 0);
7290 p.setDataPosition(0);
7291 BuilderRemoteViews brv = new BuilderRemoteViews(p);
7292 p.recycle();
7293 return brv;
7294 }
7295 }
Adrian Roos70d7aa32017-01-11 15:39:06 -08007296
7297 private static class StandardTemplateParams {
7298 boolean hasProgress = true;
7299 boolean ambient = false;
7300 CharSequence title;
7301 CharSequence text;
7302
7303 final StandardTemplateParams reset() {
7304 hasProgress = true;
7305 ambient = false;
7306 title = null;
7307 text = null;
7308 return this;
7309 }
7310
7311 final StandardTemplateParams hasProgress(boolean hasProgress) {
7312 this.hasProgress = hasProgress;
7313 return this;
7314 }
7315
7316 final StandardTemplateParams title(CharSequence title) {
7317 this.title = title;
7318 return this;
7319 }
7320
7321 final StandardTemplateParams text(CharSequence text) {
7322 this.text = text;
7323 return this;
7324 }
7325
7326 final StandardTemplateParams ambient(boolean ambient) {
7327 this.ambient = ambient;
7328 return this;
7329 }
7330
7331 final StandardTemplateParams fillTextsFrom(Builder b) {
7332 Bundle extras = b.mN.extras;
7333 title = b.processLegacyText(extras.getCharSequence(EXTRA_TITLE));
7334 text = b.processLegacyText(extras.getCharSequence(EXTRA_TEXT));
7335 return this;
7336 }
7337 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007338}