blob: 72ad159bf7f166ac6bba50c4bc4b61cbae2ae0d6 [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;
Julia Reynolds13d898c2017-02-02 12:22:05 -050031import android.content.pm.ShortcutInfo;
Jorim Jaggief72a192014-08-26 21:57:46 +020032import android.content.res.ColorStateList;
Joe Onoratoef1e7762010-09-17 18:38:38 -040033import android.graphics.Bitmap;
Kenny Guy8a0101b2014-05-08 23:34:12 +010034import android.graphics.Canvas;
Adrian Roosc1a80b02016-04-05 14:54:55 -070035import android.graphics.Color;
Jorim Jaggi5c2d8462014-03-21 17:37:00 +010036import android.graphics.PorterDuff;
Kenny Guy8a0101b2014-05-08 23:34:12 +010037import android.graphics.drawable.Drawable;
Dan Sandlerd63f9322015-05-06 15:18:49 -040038import android.graphics.drawable.Icon;
John Spurlockc0650f022014-07-19 13:22:39 -040039import android.media.AudioAttributes;
Jeff Sharkey098d5802012-04-26 17:30:34 -070040import android.media.AudioManager;
Jean-Michel Trivi2f7511f2016-11-28 15:40:27 -080041import android.media.PlayerBase;
Jeff Browndba34ba2014-06-24 20:46:03 -070042import android.media.session.MediaSession;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080043import android.net.Uri;
Daniel Sandlerdcbaf662013-04-26 16:23:09 -040044import android.os.BadParcelableException;
Christoph Studer239f8352014-08-25 15:13:18 +020045import android.os.Build;
Daniel Sandler2561b0b2012-02-13 21:04:12 -050046import android.os.Bundle;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080047import android.os.Parcel;
48import android.os.Parcelable;
Daniel Sandlera2985ed2012-04-03 16:42:00 -040049import android.os.SystemClock;
Selim Cinek6743c0b2017-01-18 18:24:01 -080050import android.os.SystemProperties;
Jeff Sharkey6d515712012-09-20 16:06:08 -070051import android.os.UserHandle;
Adrian Roosc1a80b02016-04-05 14:54:55 -070052import android.text.BidiFormatter;
Selim Cinek60a54252016-02-26 17:03:25 -080053import android.text.SpannableStringBuilder;
54import android.text.Spanned;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080055import android.text.TextUtils;
Selim Cinek60a54252016-02-26 17:03:25 -080056import android.text.style.AbsoluteSizeSpan;
Selim Cinek981962e2016-07-20 20:41:58 -070057import android.text.style.BackgroundColorSpan;
Selim Cinek89991a22016-03-07 19:51:54 -080058import android.text.style.CharacterStyle;
Selim Cinek981962e2016-07-20 20:41:58 -070059import android.text.style.ForegroundColorSpan;
Selim Cinek60a54252016-02-26 17:03:25 -080060import android.text.style.RelativeSizeSpan;
61import android.text.style.TextAppearanceSpan;
Svet Ganovddb94882016-06-23 19:55:24 -070062import android.util.ArraySet;
Daniel Sandlerdcbaf662013-04-26 16:23:09 -040063import android.util.Log;
Dan Sandler50128532015-12-08 15:42:41 -050064import android.util.SparseArray;
Griff Hazen61a9e862014-05-22 16:05:19 -070065import android.view.Gravity;
Selim Cinekea4bef72015-12-02 15:51:10 -080066import android.view.NotificationHeaderView;
Joe Onorato8595a3d2010-11-19 18:12:07 -080067import android.view.View;
Selim Cinek954cc232016-05-20 13:29:23 -070068import android.view.ViewGroup;
Jeff Sharkey1c400132011-08-05 14:50:13 -070069import android.widget.ProgressBar;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080070import android.widget.RemoteViews;
71
Griff Hazen959591e2014-05-15 22:26:18 -070072import com.android.internal.R;
Svet Ganovddb94882016-06-23 19:55:24 -070073import com.android.internal.util.ArrayUtils;
Griff Hazenc091ba82014-05-16 10:13:26 -070074import com.android.internal.util.NotificationColorUtil;
Adrian Roos0bc3f6a2017-03-06 11:54:05 -080075import com.android.internal.util.Preconditions;
Griff Hazen959591e2014-05-15 22:26:18 -070076
Tor Norbyed9273d62013-05-30 15:59:53 -070077import java.lang.annotation.Retention;
78import java.lang.annotation.RetentionPolicy;
Christoph Studer4600f9b2014-07-22 22:44:43 +020079import java.lang.reflect.Constructor;
Daniel Sandler2561b0b2012-02-13 21:04:12 -050080import java.util.ArrayList;
Griff Hazen61a9e862014-05-22 16:05:19 -070081import java.util.Arrays;
Griff Hazen5cadc3b2014-05-20 09:55:39 -070082import java.util.Collections;
Griff Hazen61a9e862014-05-22 16:05:19 -070083import java.util.List;
Dan Sandler50128532015-12-08 15:42:41 -050084import java.util.Set;
Joe Onorato561d3852010-11-20 18:09:34 -080085
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080086/**
87 * A class that represents how a persistent notification is to be presented to
88 * the user using the {@link android.app.NotificationManager}.
89 *
Joe Onoratocb109a02011-01-18 17:57:41 -080090 * <p>The {@link Notification.Builder Notification.Builder} has been added to make it
91 * easier to construct Notifications.</p>
92 *
Joe Fernandez558459f2011-10-13 16:47:36 -070093 * <div class="special reference">
94 * <h3>Developer Guides</h3>
95 * <p>For a guide to creating notifications, read the
96 * <a href="{@docRoot}guide/topics/ui/notifiers/notifications.html">Status Bar Notifications</a>
97 * developer guide.</p>
98 * </div>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080099 */
100public class Notification implements Parcelable
101{
Daniel Sandlerdcbaf662013-04-26 16:23:09 -0400102 private static final String TAG = "Notification";
103
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800104 /**
Daniel Sandler01df1c62014-06-09 10:54:01 -0400105 * An activity that provides a user interface for adjusting notification preferences for its
106 * containing application. Optional but recommended for apps that post
107 * {@link android.app.Notification Notifications}.
108 */
109 @SdkConstant(SdkConstantType.INTENT_CATEGORY)
110 public static final String INTENT_CATEGORY_NOTIFICATION_PREFERENCES
111 = "android.intent.category.NOTIFICATION_PREFERENCES";
112
113 /**
Julia Reynolds2619b5e2017-02-09 09:58:15 -0500114 * Optional extra for {@link #INTENT_CATEGORY_NOTIFICATION_PREFERENCES}. If provided, will
115 * contain a {@link NotificationChannel#getId() channel id} that can be used to narrow down
116 * what in app notifications settings should be shown.
117 */
118 public static final String EXTRA_CHANNEL_ID = "android.intent.extra.CHANNEL_ID";
119
120 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800121 * Use all default values (where applicable).
122 */
123 public static final int DEFAULT_ALL = ~0;
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500124
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800125 /**
126 * Use the default notification sound. This will ignore any given
127 * {@link #sound}.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500128 *
Chris Wren47c20a12014-06-18 17:27:29 -0400129 * <p>
130 * A notification that is noisy is more likely to be presented as a heads-up notification.
131 * </p>
132 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800133 * @see #defaults
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500134 */
135
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800136 public static final int DEFAULT_SOUND = 1;
137
138 /**
139 * Use the default notification vibrate. This will ignore any given
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500140 * {@link #vibrate}. Using phone vibration requires the
Scott Mainb8b36452009-04-26 15:50:49 -0700141 * {@link android.Manifest.permission#VIBRATE VIBRATE} permission.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500142 *
Chris Wren47c20a12014-06-18 17:27:29 -0400143 * <p>
144 * A notification that vibrates is more likely to be presented as a heads-up notification.
145 * </p>
146 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800147 * @see #defaults
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500148 */
149
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800150 public static final int DEFAULT_VIBRATE = 2;
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500151
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800152 /**
153 * Use the default notification lights. This will ignore the
154 * {@link #FLAG_SHOW_LIGHTS} bit, and {@link #ledARGB}, {@link #ledOffMS}, or
155 * {@link #ledOnMS}.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500156 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800157 * @see #defaults
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500158 */
159
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800160 public static final int DEFAULT_LIGHTS = 4;
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500161
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800162 /**
Christoph Studer535ec612014-09-03 15:47:47 +0200163 * Maximum length of CharSequences accepted by Builder and friends.
164 *
165 * <p>
166 * Avoids spamming the system with overly large strings such as full e-mails.
167 */
168 private static final int MAX_CHARSEQUENCE_LENGTH = 5 * 1024;
169
170 /**
Adrian Roose458aa82015-12-08 16:17:19 -0800171 * Maximum entries of reply text that are accepted by Builder and friends.
172 */
173 private static final int MAX_REPLY_HISTORY = 5;
174
175 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500176 * A timestamp related to this notification, in milliseconds since the epoch.
Joe Malin8d40d042012-11-05 11:36:40 -0800177 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500178 * Default value: {@link System#currentTimeMillis() Now}.
179 *
180 * Choose a timestamp that will be most relevant to the user. For most finite events, this
181 * corresponds to the time the event happened (or will happen, in the case of events that have
182 * yet to occur but about which the user is being informed). Indefinite events should be
Joe Malin8d40d042012-11-05 11:36:40 -0800183 * timestamped according to when the activity began.
184 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500185 * Some examples:
Joe Malin8d40d042012-11-05 11:36:40 -0800186 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500187 * <ul>
188 * <li>Notification of a new chat message should be stamped when the message was received.</li>
189 * <li>Notification of an ongoing file download (with a progress bar, for example) should be stamped when the download started.</li>
190 * <li>Notification of a completed file download should be stamped when the download finished.</li>
191 * <li>Notification of an upcoming meeting should be stamped with the time the meeting will begin (that is, in the future).</li>
192 * <li>Notification of an ongoing stopwatch (increasing timer) should be stamped with the watch's start time.
193 * <li>Notification of an ongoing countdown timer should be stamped with the timer's end time.
Joe Malin8d40d042012-11-05 11:36:40 -0800194 * </ul>
195 *
Selim Cinek0ff1ce602016-04-05 18:27:16 -0700196 * For apps targeting {@link android.os.Build.VERSION_CODES#N} and above, this time is not shown
197 * anymore by default and must be opted into by using
198 * {@link android.app.Notification.Builder#setShowWhen(boolean)}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800199 */
200 public long when;
201
202 /**
Selim Cinekb85f36fd2016-04-20 18:46:36 -0700203 * The creation time of the notification
204 */
205 private long creationTime;
206
207 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800208 * The resource id of a drawable to use as the icon in the status bar.
Dan Sandler86647982015-05-13 23:41:13 -0400209 *
210 * @deprecated Use {@link Builder#setSmallIcon(Icon)} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800211 */
Dan Sandler86647982015-05-13 23:41:13 -0400212 @Deprecated
Tor Norbye7b9c9122013-05-30 16:48:33 -0700213 @DrawableRes
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800214 public int icon;
215
216 /**
Joe Onorato46439ce2010-11-19 13:56:21 -0800217 * If the icon in the status bar is to have more than one level, you can set this. Otherwise,
218 * leave it at its default value of 0.
219 *
220 * @see android.widget.ImageView#setImageLevel
Griff Hazen959591e2014-05-15 22:26:18 -0700221 * @see android.graphics.drawable.Drawable#setLevel
Joe Onorato46439ce2010-11-19 13:56:21 -0800222 */
223 public int iconLevel;
224
225 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500226 * The number of events that this notification represents. For example, in a new mail
227 * notification, this could be the number of unread messages.
Joe Malin8d40d042012-11-05 11:36:40 -0800228 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500229 * The system may or may not use this field to modify the appearance of the notification. For
230 * example, before {@link android.os.Build.VERSION_CODES#HONEYCOMB}, this number was
231 * superimposed over the icon in the status bar. Starting with
232 * {@link android.os.Build.VERSION_CODES#HONEYCOMB}, the template used by
233 * {@link Notification.Builder} has displayed the number in the expanded notification view.
Julia Reynolds13d898c2017-02-02 12:22:05 -0500234 * Starting with {@link android.os.Build.VERSION_CODES#O}, the number may be displayed as a
235 * badge icon in Launchers that support badging.
Joe Malin8d40d042012-11-05 11:36:40 -0800236 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800237 */
Julia Reynolds13d898c2017-02-02 12:22:05 -0500238 public int number = 1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800239
240 /**
241 * The intent to execute when the expanded status entry is clicked. If
242 * this is an activity, it must include the
243 * {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} flag, which requires
Scott Main7aee61f2011-02-08 11:25:01 -0800244 * that you take care of task management as described in the
245 * <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back
Dianne Hackborn6ceca582012-01-10 15:24:26 -0800246 * Stack</a> document. In particular, make sure to read the notification section
247 * <a href="{@docRoot}guide/topics/ui/notifiers/notifications.html#HandlingNotifications">Handling
248 * Notifications</a> for the correct ways to launch an application from a
249 * notification.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800250 */
251 public PendingIntent contentIntent;
252
253 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500254 * The intent to execute when the notification is explicitly dismissed by the user, either with
255 * the "Clear All" button or by swiping it away individually.
256 *
257 * This probably shouldn't be launching an activity since several of those will be sent
258 * at the same time.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800259 */
260 public PendingIntent deleteIntent;
261
262 /**
Dianne Hackborn170bae72010-09-03 15:14:28 -0700263 * An intent to launch instead of posting the notification to the status bar.
Joe Onoratocb109a02011-01-18 17:57:41 -0800264 *
Chris Wren47c20a12014-06-18 17:27:29 -0400265 * <p>
266 * The system UI may choose to display a heads-up notification, instead of
267 * launching this intent, while the user is using the device.
268 * </p>
269 *
Joe Onoratocb109a02011-01-18 17:57:41 -0800270 * @see Notification.Builder#setFullScreenIntent
Daniel Sandlere46cbd32010-06-17 10:35:26 -0400271 */
272 public PendingIntent fullScreenIntent;
273
274 /**
Dan Sandler5fcdf6e2014-07-18 11:31:15 -0400275 * Text that summarizes this notification for accessibility services.
276 *
277 * As of the L release, this text is no longer shown on screen, but it is still useful to
278 * accessibility services (where it serves as an audible announcement of the notification's
279 * appearance).
Joe Onoratoef1e7762010-09-17 18:38:38 -0400280 *
Joe Onorato46439ce2010-11-19 13:56:21 -0800281 * @see #tickerView
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800282 */
283 public CharSequence tickerText;
284
285 /**
Dan Sandler5fcdf6e2014-07-18 11:31:15 -0400286 * Formerly, a view showing the {@link #tickerText}.
287 *
288 * No longer displayed in the status bar as of API 21.
Joe Onoratoef1e7762010-09-17 18:38:38 -0400289 */
Dan Sandler5fcdf6e2014-07-18 11:31:15 -0400290 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -0800291 public RemoteViews tickerView;
Joe Onoratoef1e7762010-09-17 18:38:38 -0400292
293 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400294 * The view that will represent this notification in the notification list (which is pulled
295 * down from the status bar).
296 *
Julia Reynoldsd4ea7412016-02-17 14:00:56 -0500297 * As of N, this field may be null. The notification view is determined by the inputs
298 * to {@link Notification.Builder}; a custom RemoteViews can optionally be
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400299 * supplied with {@link Notification.Builder#setCustomContentView(RemoteViews)}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800300 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400301 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800302 public RemoteViews contentView;
303
Daniel Sandlera0a938c2012-03-15 08:42:37 -0400304 /**
Daniel Sandler4dfbe832012-04-11 14:51:46 -0400305 * A large-format version of {@link #contentView}, giving the Notification an
Daniel Sandlerf3b73432012-03-27 15:01:25 -0400306 * opportunity to show more detail. The system UI may choose to show this
307 * instead of the normal content view at its discretion.
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400308 *
Julia Reynoldsd4ea7412016-02-17 14:00:56 -0500309 * As of N, this field may be null. The expanded notification view is determined by the
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400310 * inputs to {@link Notification.Builder}; a custom RemoteViews can optionally be
311 * supplied with {@link Notification.Builder#setCustomBigContentView(RemoteViews)}.
Daniel Sandlerf3b73432012-03-27 15:01:25 -0400312 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400313 @Deprecated
Daniel Sandlerf3b73432012-03-27 15:01:25 -0400314 public RemoteViews bigContentView;
315
Chris Wren8fd39ec2014-02-27 17:43:26 -0500316
317 /**
Chris Wren47c20a12014-06-18 17:27:29 -0400318 * A medium-format version of {@link #contentView}, providing the Notification an
319 * opportunity to add action buttons to contentView. At its discretion, the system UI may
320 * choose to show this as a heads-up notification, which will pop up so the user can see
321 * it without leaving their current activity.
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400322 *
Julia Reynoldsd4ea7412016-02-17 14:00:56 -0500323 * As of N, this field may be null. The heads-up notification view is determined by the
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400324 * inputs to {@link Notification.Builder}; a custom RemoteViews can optionally be
325 * supplied with {@link Notification.Builder#setCustomHeadsUpContentView(RemoteViews)}.
Chris Wren8fd39ec2014-02-27 17:43:26 -0500326 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400327 @Deprecated
Chris Wren8fd39ec2014-02-27 17:43:26 -0500328 public RemoteViews headsUpContentView;
329
Daniel Sandlerf3b73432012-03-27 15:01:25 -0400330 /**
Dan Sandler86647982015-05-13 23:41:13 -0400331 * A large bitmap to be shown in the notification content area.
332 *
333 * @deprecated Use {@link Builder#setLargeIcon(Icon)} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800334 */
Dan Sandler86647982015-05-13 23:41:13 -0400335 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -0800336 public Bitmap largeIcon;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800337
338 /**
339 * The sound to play.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500340 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800341 * <p>
Chris Wren47c20a12014-06-18 17:27:29 -0400342 * A notification that is noisy is more likely to be presented as a heads-up notification.
343 * </p>
344 *
345 * <p>
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500346 * To play the default notification sound, see {@link #defaults}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800347 * </p>
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500348 * @deprecated use {@link NotificationChannel#getSound()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800349 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500350 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800351 public Uri sound;
352
353 /**
354 * Use this constant as the value for audioStreamType to request that
355 * the default stream type for notifications be used. Currently the
Jeff Sharkey098d5802012-04-26 17:30:34 -0700356 * default stream type is {@link AudioManager#STREAM_NOTIFICATION}.
John Spurlockc0650f022014-07-19 13:22:39 -0400357 *
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500358 * @deprecated Use {@link NotificationChannel#getAudioAttributes()} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800359 */
Jean-Michel Trivi81f871e2014-08-06 16:32:38 -0700360 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800361 public static final int STREAM_DEFAULT = -1;
362
363 /**
364 * The audio stream type to use when playing the sound.
365 * Should be one of the STREAM_ constants from
366 * {@link android.media.AudioManager}.
John Spurlockc0650f022014-07-19 13:22:39 -0400367 *
368 * @deprecated Use {@link #audioAttributes} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800369 */
Jean-Michel Trivi81f871e2014-08-06 16:32:38 -0700370 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800371 public int audioStreamType = STREAM_DEFAULT;
372
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800373 /**
John Spurlockc0650f022014-07-19 13:22:39 -0400374 * The default value of {@link #audioAttributes}.
375 */
376 public static final AudioAttributes AUDIO_ATTRIBUTES_DEFAULT = new AudioAttributes.Builder()
377 .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
378 .setUsage(AudioAttributes.USAGE_NOTIFICATION)
379 .build();
380
381 /**
382 * The {@link AudioAttributes audio attributes} to use when playing the sound.
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500383 *
384 * @deprecated use {@link NotificationChannel#getAudioAttributes()} instead.
John Spurlockc0650f022014-07-19 13:22:39 -0400385 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500386 @Deprecated
John Spurlockc0650f022014-07-19 13:22:39 -0400387 public AudioAttributes audioAttributes = AUDIO_ATTRIBUTES_DEFAULT;
388
389 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500390 * The pattern with which to vibrate.
391 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800392 * <p>
393 * To vibrate the default pattern, see {@link #defaults}.
394 * </p>
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500395 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800396 * @see android.os.Vibrator#vibrate(long[],int)
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500397 * @deprecated use {@link NotificationChannel#getVibrationPattern()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800398 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500399 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800400 public long[] vibrate;
401
402 /**
403 * The color of the led. The hardware will do its best approximation.
404 *
405 * @see #FLAG_SHOW_LIGHTS
406 * @see #flags
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500407 * @deprecated use {@link NotificationChannel#shouldShowLights()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800408 */
Tor Norbye80756e32015-03-02 09:39:27 -0800409 @ColorInt
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500410 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800411 public int ledARGB;
412
413 /**
414 * The number of milliseconds for the LED to be on while it's flashing.
415 * The hardware will do its best approximation.
416 *
417 * @see #FLAG_SHOW_LIGHTS
418 * @see #flags
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500419 * @deprecated use {@link NotificationChannel#shouldShowLights()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800420 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500421 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800422 public int ledOnMS;
423
424 /**
425 * The number of milliseconds for the LED to be off while it's flashing.
426 * The hardware will do its best approximation.
427 *
428 * @see #FLAG_SHOW_LIGHTS
429 * @see #flags
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500430 *
431 * @deprecated use {@link NotificationChannel#shouldShowLights()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800432 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500433 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800434 public int ledOffMS;
435
436 /**
437 * Specifies which values should be taken from the defaults.
438 * <p>
439 * To set, OR the desired from {@link #DEFAULT_SOUND},
440 * {@link #DEFAULT_VIBRATE}, {@link #DEFAULT_LIGHTS}. For all default
441 * values, use {@link #DEFAULT_ALL}.
442 * </p>
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500443 *
444 * @deprecated use {@link NotificationChannel#getSound()} and
445 * {@link NotificationChannel#shouldShowLights()} and
446 * {@link NotificationChannel#shouldVibrate()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800447 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500448 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800449 public int defaults;
450
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800451 /**
452 * Bit to be bitwise-ored into the {@link #flags} field that should be
453 * set if you want the LED on for this notification.
454 * <ul>
455 * <li>To turn the LED off, pass 0 in the alpha channel for colorARGB
456 * or 0 for both ledOnMS and ledOffMS.</li>
457 * <li>To turn the LED on, pass 1 for ledOnMS and 0 for ledOffMS.</li>
458 * <li>To flash the LED, pass the number of milliseconds that it should
459 * be on and off to ledOnMS and ledOffMS.</li>
460 * </ul>
461 * <p>
462 * Since hardware varies, you are not guaranteed that any of the values
463 * you pass are honored exactly. Use the system defaults (TODO) if possible
464 * because they will be set to values that work on any given hardware.
465 * <p>
466 * The alpha channel must be set for forward compatibility.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500467 *
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500468 * @deprecated use {@link NotificationChannel#shouldShowLights()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800469 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500470 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800471 public static final int FLAG_SHOW_LIGHTS = 0x00000001;
472
473 /**
474 * Bit to be bitwise-ored into the {@link #flags} field that should be
475 * set if this notification is in reference to something that is ongoing,
476 * like a phone call. It should not be set if this notification is in
477 * reference to something that happened at a particular point in time,
478 * like a missed phone call.
479 */
480 public static final int FLAG_ONGOING_EVENT = 0x00000002;
481
482 /**
483 * Bit to be bitwise-ored into the {@link #flags} field that if set,
Scott Mainb8b36452009-04-26 15:50:49 -0700484 * the audio will be repeated until the notification is
485 * cancelled or the notification window is opened.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800486 */
487 public static final int FLAG_INSISTENT = 0x00000004;
488
489 /**
490 * Bit to be bitwise-ored into the {@link #flags} field that should be
Griff Hazen293977b2014-04-28 08:37:20 -0700491 * set if you would only like the sound, vibrate and ticker to be played
492 * if the notification was not already showing.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800493 */
494 public static final int FLAG_ONLY_ALERT_ONCE = 0x00000008;
495
496 /**
497 * Bit to be bitwise-ored into the {@link #flags} field that should be
498 * set if the notification should be canceled when it is clicked by the
Daniel Sandler8aa9ae62012-12-04 23:31:47 -0500499 * user.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800500 */
501 public static final int FLAG_AUTO_CANCEL = 0x00000010;
502
503 /**
504 * Bit to be bitwise-ored into the {@link #flags} field that should be
505 * set if the notification should not be canceled when the user clicks
506 * the Clear all button.
507 */
508 public static final int FLAG_NO_CLEAR = 0x00000020;
509
Dianne Hackbornd8a43f62009-08-17 23:33:56 -0700510 /**
511 * Bit to be bitwise-ored into the {@link #flags} field that should be
512 * set if this notification represents a currently running service. This
513 * will normally be set for you by {@link Service#startForeground}.
514 */
515 public static final int FLAG_FOREGROUND_SERVICE = 0x00000040;
516
Daniel Sandlere46cbd32010-06-17 10:35:26 -0400517 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500518 * Obsolete flag indicating high-priority notifications; use the priority field instead.
Joe Malin8d40d042012-11-05 11:36:40 -0800519 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500520 * @deprecated Use {@link #priority} with a positive value.
Daniel Sandlere46cbd32010-06-17 10:35:26 -0400521 */
Aurimas Liutikas514c5ef2016-05-24 15:22:55 -0700522 @Deprecated
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500523 public static final int FLAG_HIGH_PRIORITY = 0x00000080;
Daniel Sandlere46cbd32010-06-17 10:35:26 -0400524
Griff Hazendfcb0802014-02-11 12:00:00 -0800525 /**
526 * Bit to be bitswise-ored into the {@link #flags} field that should be
527 * set if this notification is relevant to the current device only
528 * and it is not recommended that it bridge to other devices.
529 */
530 public static final int FLAG_LOCAL_ONLY = 0x00000100;
531
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700532 /**
533 * Bit to be bitswise-ored into the {@link #flags} field that should be
534 * set if this notification is the group summary for a group of notifications.
535 * Grouped notifications may display in a cluster or stack on devices which
536 * support such rendering. Requires a group key also be set using {@link Builder#setGroup}.
537 */
538 public static final int FLAG_GROUP_SUMMARY = 0x00000200;
539
Julia Reynoldse46bb372016-03-17 11:05:58 -0400540 /**
541 * Bit to be bitswise-ored into the {@link #flags} field that should be
542 * set if this notification is the group summary for an auto-group of notifications.
543 *
544 * @hide
545 */
546 @SystemApi
547 public static final int FLAG_AUTOGROUP_SUMMARY = 0x00000400;
548
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800549 public int flags;
550
Tor Norbyed9273d62013-05-30 15:59:53 -0700551 /** @hide */
552 @IntDef({PRIORITY_DEFAULT,PRIORITY_LOW,PRIORITY_MIN,PRIORITY_HIGH,PRIORITY_MAX})
553 @Retention(RetentionPolicy.SOURCE)
554 public @interface Priority {}
555
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800556 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500557 * Default notification {@link #priority}. If your application does not prioritize its own
558 * notifications, use this value for all notifications.
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500559 *
560 * @deprecated use {@link NotificationManager#IMPORTANCE_DEFAULT} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500561 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500562 @Deprecated
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500563 public static final int PRIORITY_DEFAULT = 0;
564
565 /**
566 * Lower {@link #priority}, for items that are less important. The UI may choose to show these
567 * items smaller, or at a different position in the list, compared with your app's
568 * {@link #PRIORITY_DEFAULT} items.
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500569 *
570 * @deprecated use {@link NotificationManager#IMPORTANCE_LOW} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500571 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500572 @Deprecated
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500573 public static final int PRIORITY_LOW = -1;
574
575 /**
576 * Lowest {@link #priority}; these items might not be shown to the user except under special
577 * circumstances, such as detailed notification logs.
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500578 *
579 * @deprecated use {@link NotificationManager#IMPORTANCE_MIN} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500580 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500581 @Deprecated
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500582 public static final int PRIORITY_MIN = -2;
583
584 /**
585 * Higher {@link #priority}, for more important notifications or alerts. The UI may choose to
586 * show these items larger, or at a different position in notification lists, compared with
587 * your app's {@link #PRIORITY_DEFAULT} items.
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500588 *
589 * @deprecated use {@link NotificationManager#IMPORTANCE_HIGH} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500590 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500591 @Deprecated
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500592 public static final int PRIORITY_HIGH = 1;
593
594 /**
595 * Highest {@link #priority}, for your application's most important items that require the
596 * user's prompt attention or input.
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500597 *
598 * @deprecated use {@link NotificationManager#IMPORTANCE_HIGH} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500599 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500600 @Deprecated
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500601 public static final int PRIORITY_MAX = 2;
602
603 /**
604 * Relative priority for this notification.
Joe Malin8d40d042012-11-05 11:36:40 -0800605 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500606 * Priority is an indication of how much of the user's valuable attention should be consumed by
607 * this notification. Low-priority notifications may be hidden from the user in certain
608 * situations, while the user might be interrupted for a higher-priority notification. The
Daniel Sandler6738eee2012-11-16 12:03:32 -0500609 * system will make a determination about how to interpret this priority when presenting
610 * the notification.
Chris Wren47c20a12014-06-18 17:27:29 -0400611 *
612 * <p>
613 * A notification that is at least {@link #PRIORITY_HIGH} is more likely to be presented
614 * as a heads-up notification.
615 * </p>
616 *
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500617 * @deprecated use {@link NotificationChannel#getImportance()} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500618 */
Tor Norbyed9273d62013-05-30 15:59:53 -0700619 @Priority
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500620 @Deprecated
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500621 public int priority;
Joe Malin8d40d042012-11-05 11:36:40 -0800622
Dan Sandler26e81cf2014-05-06 10:01:27 -0400623 /**
624 * Accent color (an ARGB integer like the constants in {@link android.graphics.Color})
625 * to be applied by the standard Style templates when presenting this notification.
626 *
627 * The current template design constructs a colorful header image by overlaying the
628 * {@link #icon} image (stenciled in white) atop a field of this color. Alpha components are
629 * ignored.
630 */
Tor Norbye80756e32015-03-02 09:39:27 -0800631 @ColorInt
Dan Sandler26e81cf2014-05-06 10:01:27 -0400632 public int color = COLOR_DEFAULT;
633
634 /**
635 * Special value of {@link #color} telling the system not to decorate this notification with
636 * any special color but instead use default colors when presenting this notification.
637 */
Tor Norbye80756e32015-03-02 09:39:27 -0800638 @ColorInt
Dan Sandler26e81cf2014-05-06 10:01:27 -0400639 public static final int COLOR_DEFAULT = 0; // AKA Color.TRANSPARENT
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600640
641 /**
Adrian Roos4ff3b122016-02-01 12:26:13 -0800642 * Special value of {@link #color} used as a place holder for an invalid color.
Selim Cinek99104832017-01-25 14:47:33 -0800643 * @hide
Adrian Roos4ff3b122016-02-01 12:26:13 -0800644 */
645 @ColorInt
Selim Cinek99104832017-01-25 14:47:33 -0800646 public static final int COLOR_INVALID = 1;
Adrian Roos4ff3b122016-02-01 12:26:13 -0800647
648 /**
Adrian Roosc1a80b02016-04-05 14:54:55 -0700649 * Sphere of visibility of this notification, which affects how and when the SystemUI reveals
650 * the notification's presence and contents in untrusted situations (namely, on the secure
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600651 * lockscreen).
652 *
653 * The default level, {@link #VISIBILITY_PRIVATE}, behaves exactly as notifications have always
654 * done on Android: The notification's {@link #icon} and {@link #tickerText} (if available) are
655 * shown in all situations, but the contents are only available if the device is unlocked for
656 * the appropriate user.
657 *
658 * A more permissive policy can be expressed by {@link #VISIBILITY_PUBLIC}; such a notification
659 * can be read even in an "insecure" context (that is, above a secure lockscreen).
660 * To modify the public version of this notification—for example, to redact some portions—see
661 * {@link Builder#setPublicVersion(Notification)}.
662 *
663 * Finally, a notification can be made {@link #VISIBILITY_SECRET}, which will suppress its icon
664 * and ticker until the user has bypassed the lockscreen.
665 */
666 public int visibility;
667
Griff Hazenfc3922d2014-08-20 11:56:44 -0700668 /**
669 * Notification visibility: Show this notification in its entirety on all lockscreens.
670 *
671 * {@see #visibility}
672 */
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600673 public static final int VISIBILITY_PUBLIC = 1;
Griff Hazenfc3922d2014-08-20 11:56:44 -0700674
675 /**
676 * Notification visibility: Show this notification on all lockscreens, but conceal sensitive or
677 * private information on secure lockscreens.
678 *
679 * {@see #visibility}
680 */
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600681 public static final int VISIBILITY_PRIVATE = 0;
Griff Hazenfc3922d2014-08-20 11:56:44 -0700682
683 /**
684 * Notification visibility: Do not reveal any part of this notification on a secure lockscreen.
685 *
686 * {@see #visibility}
687 */
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600688 public static final int VISIBILITY_SECRET = -1;
689
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500690 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400691 * Notification category: incoming call (voice or video) or similar synchronous communication request.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500692 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400693 public static final String CATEGORY_CALL = "call";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500694
695 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400696 * Notification category: incoming direct message (SMS, instant message, etc.).
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500697 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400698 public static final String CATEGORY_MESSAGE = "msg";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500699
700 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400701 * Notification category: asynchronous bulk message (email).
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500702 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400703 public static final String CATEGORY_EMAIL = "email";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500704
705 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400706 * Notification category: calendar event.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500707 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400708 public static final String CATEGORY_EVENT = "event";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500709
710 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400711 * Notification category: promotion or advertisement.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500712 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400713 public static final String CATEGORY_PROMO = "promo";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500714
715 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400716 * Notification category: alarm or timer.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500717 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400718 public static final String CATEGORY_ALARM = "alarm";
719
720 /**
721 * Notification category: progress of a long-running background operation.
722 */
723 public static final String CATEGORY_PROGRESS = "progress";
724
725 /**
726 * Notification category: social network or sharing update.
727 */
728 public static final String CATEGORY_SOCIAL = "social";
729
730 /**
731 * Notification category: error in background operation or authentication status.
732 */
733 public static final String CATEGORY_ERROR = "err";
734
735 /**
736 * Notification category: media transport control for playback.
737 */
738 public static final String CATEGORY_TRANSPORT = "transport";
739
740 /**
741 * Notification category: system or device status update. Reserved for system use.
742 */
743 public static final String CATEGORY_SYSTEM = "sys";
744
745 /**
746 * Notification category: indication of running background service.
747 */
748 public static final String CATEGORY_SERVICE = "service";
749
750 /**
John Spurlock0a69c8c2014-03-21 13:30:57 -0400751 * Notification category: a specific, timely recommendation for a single thing.
752 * For example, a news app might want to recommend a news story it believes the user will
753 * want to read next.
754 */
755 public static final String CATEGORY_RECOMMENDATION = "recommendation";
756
757 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400758 * Notification category: ongoing information about device or contextual status.
759 */
760 public static final String CATEGORY_STATUS = "status";
761
762 /**
John Spurlock24d3dad2015-04-02 12:24:02 -0400763 * Notification category: user-scheduled reminder.
764 */
765 public static final String CATEGORY_REMINDER = "reminder";
766
767 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400768 * One of the predefined notification categories (see the <code>CATEGORY_*</code> constants)
769 * that best describes this Notification. May be used by the system for ranking and filtering.
770 */
771 public String category;
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500772
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700773 private String mGroupKey;
774
775 /**
776 * Get the key used to group this notification into a cluster or stack
777 * with other notifications on devices which support such rendering.
778 */
779 public String getGroup() {
780 return mGroupKey;
781 }
782
783 private String mSortKey;
784
785 /**
786 * Get a sort key that orders this notification among other notifications from the
787 * same package. This can be useful if an external sort was already applied and an app
788 * would like to preserve this. Notifications will be sorted lexicographically using this
789 * value, although providing different priorities in addition to providing sort key may
790 * cause this value to be ignored.
791 *
792 * <p>This sort key can also be used to order members of a notification group. See
793 * {@link Builder#setGroup}.
794 *
795 * @see String#compareTo(String)
796 */
797 public String getSortKey() {
798 return mSortKey;
799 }
800
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500801 /**
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400802 * Additional semantic data to be carried around with this Notification.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400803 * <p>
804 * The extras keys defined here are intended to capture the original inputs to {@link Builder}
805 * APIs, and are intended to be used by
806 * {@link android.service.notification.NotificationListenerService} implementations to extract
807 * detailed information from notification objects.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500808 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400809 public Bundle extras = new Bundle();
810
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400811 /**
Felipe Lemedd85da62016-06-28 11:29:54 -0700812 * All pending intents in the notification as the system needs to be able to access them but
813 * touching the extras bundle in the system process is not safe because the bundle may contain
Svet Ganovddb94882016-06-23 19:55:24 -0700814 * custom parcelable objects.
815 *
816 * @hide
817 */
Felipe Lemedd85da62016-06-28 11:29:54 -0700818 public ArraySet<PendingIntent> allPendingIntents;
Svet Ganovddb94882016-06-23 19:55:24 -0700819
820 /**
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400821 * {@link #extras} key: this is the title of the notification,
822 * as supplied to {@link Builder#setContentTitle(CharSequence)}.
823 */
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -0500824 public static final String EXTRA_TITLE = "android.title";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400825
826 /**
827 * {@link #extras} key: this is the title of the notification when shown in expanded form,
828 * e.g. as supplied to {@link BigTextStyle#setBigContentTitle(CharSequence)}.
829 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400830 public static final String EXTRA_TITLE_BIG = EXTRA_TITLE + ".big";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400831
832 /**
833 * {@link #extras} key: this is the main text payload, as supplied to
834 * {@link Builder#setContentText(CharSequence)}.
835 */
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -0500836 public static final String EXTRA_TEXT = "android.text";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400837
838 /**
839 * {@link #extras} key: this is a third line of text, as supplied to
840 * {@link Builder#setSubText(CharSequence)}.
841 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400842 public static final String EXTRA_SUB_TEXT = "android.subText";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400843
844 /**
Adrian Roose458aa82015-12-08 16:17:19 -0800845 * {@link #extras} key: this is the remote input history, as supplied to
846 * {@link Builder#setRemoteInputHistory(CharSequence[])}.
Adrian Roos005e7742016-04-13 15:02:20 -0700847 *
848 * Apps can fill this through {@link Builder#setRemoteInputHistory(CharSequence[])}
849 * with the most recent inputs that have been sent through a {@link RemoteInput} of this
850 * Notification and are expected to clear it once the it is no longer relevant (e.g. for chat
851 * notifications once the other party has responded).
852 *
853 * The extra with this key is of type CharSequence[] and contains the most recent entry at
854 * the 0 index, the second most recent at the 1 index, etc.
855 *
856 * @see Builder#setRemoteInputHistory(CharSequence[])
Adrian Roose458aa82015-12-08 16:17:19 -0800857 */
858 public static final String EXTRA_REMOTE_INPUT_HISTORY = "android.remoteInputHistory";
859
860 /**
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400861 * {@link #extras} key: this is a small piece of additional text as supplied to
862 * {@link Builder#setContentInfo(CharSequence)}.
863 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400864 public static final String EXTRA_INFO_TEXT = "android.infoText";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400865
866 /**
867 * {@link #extras} key: this is a line of summary information intended to be shown
868 * alongside expanded notifications, as supplied to (e.g.)
869 * {@link BigTextStyle#setSummaryText(CharSequence)}.
870 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400871 public static final String EXTRA_SUMMARY_TEXT = "android.summaryText";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400872
873 /**
Christoph Studer4600f9b2014-07-22 22:44:43 +0200874 * {@link #extras} key: this is the longer text shown in the big form of a
875 * {@link BigTextStyle} notification, as supplied to
876 * {@link BigTextStyle#bigText(CharSequence)}.
877 */
878 public static final String EXTRA_BIG_TEXT = "android.bigText";
879
880 /**
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400881 * {@link #extras} key: this is the resource ID of the notification's main small icon, as
882 * supplied to {@link Builder#setSmallIcon(int)}.
Julia Reynoldse071abd2017-03-22 10:52:11 -0400883 *
884 * @deprecated Use {@link #getSmallIcon()}, which supports a wider variety of icon sources.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400885 */
Julia Reynoldse071abd2017-03-22 10:52:11 -0400886 @Deprecated
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -0500887 public static final String EXTRA_SMALL_ICON = "android.icon";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400888
889 /**
890 * {@link #extras} key: this is a bitmap to be used instead of the small icon when showing the
891 * notification payload, as
892 * supplied to {@link Builder#setLargeIcon(android.graphics.Bitmap)}.
Julia Reynoldse071abd2017-03-22 10:52:11 -0400893 *
894 * @deprecated Use {@link #getLargeIcon()}, which supports a wider variety of icon sources.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400895 */
Julia Reynoldse071abd2017-03-22 10:52:11 -0400896 @Deprecated
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400897 public static final String EXTRA_LARGE_ICON = "android.largeIcon";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400898
899 /**
900 * {@link #extras} key: this is a bitmap to be used instead of the one from
901 * {@link Builder#setLargeIcon(android.graphics.Bitmap)} when the notification is
902 * shown in its expanded form, as supplied to
903 * {@link BigPictureStyle#bigLargeIcon(android.graphics.Bitmap)}.
904 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400905 public static final String EXTRA_LARGE_ICON_BIG = EXTRA_LARGE_ICON + ".big";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400906
907 /**
908 * {@link #extras} key: this is the progress value supplied to
909 * {@link Builder#setProgress(int, int, boolean)}.
910 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400911 public static final String EXTRA_PROGRESS = "android.progress";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400912
913 /**
914 * {@link #extras} key: this is the maximum value supplied to
915 * {@link Builder#setProgress(int, int, boolean)}.
916 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400917 public static final String EXTRA_PROGRESS_MAX = "android.progressMax";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400918
919 /**
920 * {@link #extras} key: whether the progress bar is indeterminate, supplied to
921 * {@link Builder#setProgress(int, int, boolean)}.
922 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400923 public static final String EXTRA_PROGRESS_INDETERMINATE = "android.progressIndeterminate";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400924
925 /**
926 * {@link #extras} key: whether {@link #when} should be shown as a count-up timer (specifically
927 * a {@link android.widget.Chronometer}) instead of a timestamp, as supplied to
928 * {@link Builder#setUsesChronometer(boolean)}.
929 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400930 public static final String EXTRA_SHOW_CHRONOMETER = "android.showChronometer";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400931
932 /**
Selim Cinek81c23aa2016-02-25 16:23:13 -0800933 * {@link #extras} key: whether the chronometer set on the notification should count down
934 * instead of counting up. Is only relevant if key {@link #EXTRA_SHOW_CHRONOMETER} is present.
Adrian Roos96b7e202016-05-17 13:50:38 -0700935 * This extra is a boolean. The default is false.
Selim Cinek81c23aa2016-02-25 16:23:13 -0800936 */
Adrian Roos96b7e202016-05-17 13:50:38 -0700937 public static final String EXTRA_CHRONOMETER_COUNT_DOWN = "android.chronometerCountDown";
Selim Cinek81c23aa2016-02-25 16:23:13 -0800938
939 /**
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400940 * {@link #extras} key: whether {@link #when} should be shown,
941 * as supplied to {@link Builder#setShowWhen(boolean)}.
942 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400943 public static final String EXTRA_SHOW_WHEN = "android.showWhen";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400944
945 /**
946 * {@link #extras} key: this is a bitmap to be shown in {@link BigPictureStyle} expanded
947 * notifications, supplied to {@link BigPictureStyle#bigPicture(android.graphics.Bitmap)}.
948 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400949 public static final String EXTRA_PICTURE = "android.picture";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400950
951 /**
952 * {@link #extras} key: An array of CharSequences to show in {@link InboxStyle} expanded
953 * notifications, each of which was supplied to {@link InboxStyle#addLine(CharSequence)}.
954 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400955 public static final String EXTRA_TEXT_LINES = "android.textLines";
Dan Sandler842dd772014-05-15 09:36:47 -0400956
957 /**
958 * {@link #extras} key: A string representing the name of the specific
959 * {@link android.app.Notification.Style} used to create this notification.
960 */
Chris Wren91ad5632013-06-05 15:05:57 -0400961 public static final String EXTRA_TEMPLATE = "android.template";
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400962
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400963 /**
Chris Wrene6c48932014-09-29 17:19:27 -0400964 * {@link #extras} key: A String array containing the people that this notification relates to,
965 * each of which was supplied to {@link Builder#addPerson(String)}.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400966 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400967 public static final String EXTRA_PEOPLE = "android.people";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500968
969 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400970 * Allow certain system-generated notifications to appear before the device is provisioned.
971 * Only available to notifications coming from the android package.
972 * @hide
973 */
974 public static final String EXTRA_ALLOW_DURING_SETUP = "android.allowDuringSetup";
975
976 /**
Jose Limae9e3b3b2014-05-18 23:44:50 -0700977 * {@link #extras} key: A
978 * {@link android.content.ContentUris content URI} pointing to an image that can be displayed
979 * in the background when the notification is selected. The URI must point to an image stream
980 * suitable for passing into
981 * {@link android.graphics.BitmapFactory#decodeStream(java.io.InputStream)
982 * BitmapFactory.decodeStream}; all other content types will be ignored. The content provider
983 * URI used for this purpose must require no permissions to read the image data.
984 */
985 public static final String EXTRA_BACKGROUND_IMAGE_URI = "android.backgroundImageUri";
986
987 /**
Dan Sandler842dd772014-05-15 09:36:47 -0400988 * {@link #extras} key: A
Jeff Browndba34ba2014-06-24 20:46:03 -0700989 * {@link android.media.session.MediaSession.Token} associated with a
Dan Sandler842dd772014-05-15 09:36:47 -0400990 * {@link android.app.Notification.MediaStyle} notification.
991 */
992 public static final String EXTRA_MEDIA_SESSION = "android.mediaSession";
993
994 /**
Bryan Mawhinneye191f902014-07-22 12:50:09 +0100995 * {@link #extras} key: the indices of actions to be shown in the compact view,
996 * as supplied to (e.g.) {@link MediaStyle#setShowActionsInCompactView(int...)}.
997 */
998 public static final String EXTRA_COMPACT_ACTIONS = "android.compactActions";
999
Christoph Studer943aa672014-08-03 20:31:16 +02001000 /**
Alex Hillsfc737de2016-03-23 17:33:02 -04001001 * {@link #extras} key: the username to be displayed for all messages sent by the user including
1002 * direct replies
Adrian Roos96b7e202016-05-17 13:50:38 -07001003 * {@link android.app.Notification.MessagingStyle} notification. This extra is a
1004 * {@link CharSequence}
Alex Hillsfc737de2016-03-23 17:33:02 -04001005 */
1006 public static final String EXTRA_SELF_DISPLAY_NAME = "android.selfDisplayName";
1007
1008 /**
Adrian Roos96b7e202016-05-17 13:50:38 -07001009 * {@link #extras} key: a {@link CharSequence} to be displayed as the title to a conversation
Alex Hillsd9b04d92016-04-11 16:38:16 -04001010 * represented by a {@link android.app.Notification.MessagingStyle}
Alex Hillsfc737de2016-03-23 17:33:02 -04001011 */
Alex Hillsd9b04d92016-04-11 16:38:16 -04001012 public static final String EXTRA_CONVERSATION_TITLE = "android.conversationTitle";
Alex Hillsfc737de2016-03-23 17:33:02 -04001013
1014 /**
1015 * {@link #extras} key: an array of {@link android.app.Notification.MessagingStyle.Message}
1016 * bundles provided by a
Adrian Roos96b7e202016-05-17 13:50:38 -07001017 * {@link android.app.Notification.MessagingStyle} notification. This extra is a parcelable
1018 * array of bundles.
Alex Hillsfc737de2016-03-23 17:33:02 -04001019 */
1020 public static final String EXTRA_MESSAGES = "android.messages";
1021
1022 /**
Adrian Roos437cd562017-01-18 15:47:03 -08001023 * {@link #extras} key: an array of
1024 * {@link android.app.Notification.MessagingStyle#addHistoricMessage historic}
1025 * {@link android.app.Notification.MessagingStyle.Message} bundles provided by a
1026 * {@link android.app.Notification.MessagingStyle} notification. This extra is a parcelable
1027 * array of bundles.
1028 */
1029 public static final String EXTRA_HISTORIC_MESSAGES = "android.messages.historic";
1030
1031 /**
Selim Cinek7b9605b2017-01-19 17:36:00 -08001032 * {@link #extras} key: whether the notification should be colorized as
1033 * supplied to {@link Builder#setColorized(boolean)}}.
1034 */
1035 public static final String EXTRA_COLORIZED = "android.colorized";
1036
1037 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04001038 * @hide
1039 */
1040 public static final String EXTRA_BUILDER_APPLICATION_INFO = "android.appInfo";
1041
Selim Cinek247fa012016-02-18 09:50:48 -08001042 /**
1043 * @hide
1044 */
1045 public static final String EXTRA_CONTAINS_CUSTOM_VIEW = "android.contains.customView";
1046
Shane Brennan472a3b32016-12-12 15:28:10 -08001047 /**
1048 * {@link #extras} key: the audio contents of this notification.
1049 *
1050 * This is for use when rendering the notification on an audio-focused interface;
1051 * the audio contents are a complete sound sample that contains the contents/body of the
1052 * notification. This may be used in substitute of a Text-to-Speech reading of the
1053 * notification. For example if the notification represents a voice message this should point
1054 * to the audio of that message.
1055 *
1056 * The data stored under this key should be a String representation of a Uri that contains the
1057 * audio contents in one of the following formats: WAV, PCM 16-bit, AMR-WB.
1058 *
1059 * This extra is unnecessary if you are using {@code MessagingStyle} since each {@code Message}
1060 * has a field for holding data URI. That field can be used for audio.
1061 * See {@code Message#setData}.
1062 *
1063 * Example usage:
1064 * <pre>
1065 * {@code
1066 * Notification.Builder myBuilder = (build your Notification as normal);
1067 * myBuilder.getExtras().putString(EXTRA_AUDIO_CONTENTS_URI, myAudioUri.toString());
1068 * }
1069 * </pre>
1070 */
1071 public static final String EXTRA_AUDIO_CONTENTS_URI = "android.audioContents";
1072
Dan Sandler80eaa592016-04-14 23:34:54 -04001073 /** @hide */
1074 @SystemApi
Dan Sandler732bd6c2016-04-12 14:20:32 -04001075 public static final String EXTRA_SUBSTITUTE_APP_NAME = "android.substName";
1076
Dan Sandlerd63f9322015-05-06 15:18:49 -04001077 private Icon mSmallIcon;
1078 private Icon mLargeIcon;
1079
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04001080 private String mChannelId;
Julia Reynolds2a128742016-11-28 14:29:25 -05001081 private long mTimeout;
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04001082
Julia Reynolds13d898c2017-02-02 12:22:05 -05001083 private String mShortcutId;
1084
1085 /**
1086 * If this notification is being shown as a badge, always show as a number.
1087 */
1088 public static final int BADGE_ICON_NONE = 0;
1089
1090 /**
1091 * If this notification is being shown as a badge, use the {@link #getSmallIcon()} to
1092 * represent this notification.
1093 */
1094 public static final int BADGE_ICON_SMALL = 1;
1095
1096 /**
1097 * If this notification is being shown as a badge, use the {@link #getLargeIcon()} to
1098 * represent this notification.
1099 */
1100 public static final int BADGE_ICON_LARGE = 2;
Julia Reynolds7d5b4da2017-03-20 10:18:49 -04001101 private int mBadgeIcon = BADGE_ICON_NONE;
Julia Reynolds13d898c2017-02-02 12:22:05 -05001102
Chris Wren51c75102013-07-16 20:49:17 -04001103 /**
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001104 * Structure to encapsulate a named action that can be shown as part of this notification.
1105 * It must include an icon, a label, and a {@link PendingIntent} to be fired when the action is
1106 * selected by the user.
1107 * <p>
Griff Hazen959591e2014-05-15 22:26:18 -07001108 * Apps should use {@link Notification.Builder#addAction(int, CharSequence, PendingIntent)}
1109 * or {@link Notification.Builder#addAction(Notification.Action)}
1110 * to attach actions.
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001111 */
Daniel Sandlerea2a3172013-02-20 22:24:20 -05001112 public static class Action implements Parcelable {
Shane Brennan472a3b32016-12-12 15:28:10 -08001113 /**
1114 * {@link #extras} key: Keys to a {@link Parcelable} {@link ArrayList} of
1115 * {@link RemoteInput}s.
1116 *
1117 * This is intended for {@link RemoteInput}s that only accept data, meaning
1118 * {@link RemoteInput#getAllowFreeFormInput} is false, {@link RemoteInput#getChoices}
1119 * is null or empty, and {@link RemoteInput#getAllowedDataTypes is non-null and not
1120 * empty. These {@link RemoteInput}s will be ignored by devices that do not
1121 * support non-text-based {@link RemoteInput}s. See {@link Builder#build}.
1122 *
1123 * You can test if a RemoteInput matches these constraints using
1124 * {@link RemoteInput#isDataOnly}.
1125 */
1126 private static final String EXTRA_DATA_ONLY_INPUTS = "android.extra.DATA_ONLY_INPUTS";
1127
Griff Hazen959591e2014-05-15 22:26:18 -07001128 private final Bundle mExtras;
Dan Sandler86647982015-05-13 23:41:13 -04001129 private Icon mIcon;
Griff Hazen61a9e862014-05-22 16:05:19 -07001130 private final RemoteInput[] mRemoteInputs;
Alex Hills1f27f882017-01-12 11:24:46 -05001131 private boolean mAllowGeneratedReplies = true;
Griff Hazen959591e2014-05-15 22:26:18 -07001132
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001133 /**
1134 * Small icon representing the action.
Dan Sandler86647982015-05-13 23:41:13 -04001135 *
1136 * @deprecated Use {@link Action#getIcon()} instead.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001137 */
Dan Sandler86647982015-05-13 23:41:13 -04001138 @Deprecated
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001139 public int icon;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001140
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001141 /**
1142 * Title of the action.
1143 */
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001144 public CharSequence title;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001145
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001146 /**
1147 * Intent to send when the user invokes this action. May be null, in which case the action
1148 * may be rendered in a disabled presentation by the system UI.
1149 */
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001150 public PendingIntent actionIntent;
Griff Hazen959591e2014-05-15 22:26:18 -07001151
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001152 private Action(Parcel in) {
Dan Sandler86647982015-05-13 23:41:13 -04001153 if (in.readInt() != 0) {
1154 mIcon = Icon.CREATOR.createFromParcel(in);
Dan Sandler68079d52015-07-22 10:45:30 -04001155 if (mIcon.getType() == Icon.TYPE_RESOURCE) {
1156 icon = mIcon.getResId();
1157 }
Dan Sandler86647982015-05-13 23:41:13 -04001158 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001159 title = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
1160 if (in.readInt() == 1) {
1161 actionIntent = PendingIntent.CREATOR.createFromParcel(in);
1162 }
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001163 mExtras = Bundle.setDefusable(in.readBundle(), true);
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001164 mRemoteInputs = in.createTypedArray(RemoteInput.CREATOR);
Alex Hills42b0c4d2016-04-26 13:35:36 -04001165 mAllowGeneratedReplies = in.readInt() == 1;
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001166 }
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001167
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001168 /**
Dan Sandler86647982015-05-13 23:41:13 -04001169 * @deprecated Use {@link android.app.Notification.Action.Builder}.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001170 */
Dan Sandler86647982015-05-13 23:41:13 -04001171 @Deprecated
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001172 public Action(int icon, CharSequence title, PendingIntent intent) {
Alex Hills1f27f882017-01-12 11:24:46 -05001173 this(Icon.createWithResource("", icon), title, intent, new Bundle(), null, true);
Griff Hazen959591e2014-05-15 22:26:18 -07001174 }
1175
Adrian Roos7af53622016-10-12 13:44:05 -07001176 /** Keep in sync with {@link Notification.Action.Builder#Builder(Action)}! */
Dan Sandler86647982015-05-13 23:41:13 -04001177 private Action(Icon icon, CharSequence title, PendingIntent intent, Bundle extras,
Alex Hills42b0c4d2016-04-26 13:35:36 -04001178 RemoteInput[] remoteInputs, boolean allowGeneratedReplies) {
Dan Sandler86647982015-05-13 23:41:13 -04001179 this.mIcon = icon;
Gus Prevasf5bff46f2015-08-24 10:34:28 -04001180 if (icon != null && icon.getType() == Icon.TYPE_RESOURCE) {
1181 this.icon = icon.getResId();
1182 }
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001183 this.title = title;
1184 this.actionIntent = intent;
Griff Hazen959591e2014-05-15 22:26:18 -07001185 this.mExtras = extras != null ? extras : new Bundle();
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001186 this.mRemoteInputs = remoteInputs;
Alex Hills42b0c4d2016-04-26 13:35:36 -04001187 this.mAllowGeneratedReplies = allowGeneratedReplies;
Griff Hazen959591e2014-05-15 22:26:18 -07001188 }
1189
1190 /**
Dan Sandler86647982015-05-13 23:41:13 -04001191 * Return an icon representing the action.
1192 */
1193 public Icon getIcon() {
1194 if (mIcon == null && icon != 0) {
1195 // you snuck an icon in here without using the builder; let's try to keep it
1196 mIcon = Icon.createWithResource("", icon);
1197 }
1198 return mIcon;
1199 }
1200
1201 /**
Griff Hazen959591e2014-05-15 22:26:18 -07001202 * Get additional metadata carried around with this Action.
1203 */
1204 public Bundle getExtras() {
1205 return mExtras;
1206 }
1207
1208 /**
Alex Hills42b0c4d2016-04-26 13:35:36 -04001209 * Return whether the platform should automatically generate possible replies for this
1210 * {@link Action}
1211 */
1212 public boolean getAllowGeneratedReplies() {
1213 return mAllowGeneratedReplies;
1214 }
1215
1216 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001217 * Get the list of inputs to be collected from the user when this action is sent.
Shane Brennan472a3b32016-12-12 15:28:10 -08001218 * May return null if no remote inputs were added. Only returns inputs which accept
1219 * a text input. For inputs which only accept data use {@link #getDataOnlyRemoteInputs}.
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001220 */
1221 public RemoteInput[] getRemoteInputs() {
1222 return mRemoteInputs;
1223 }
1224
1225 /**
Shane Brennan472a3b32016-12-12 15:28:10 -08001226 * Get the list of inputs to be collected from the user that ONLY accept data when this
1227 * action is sent. These remote inputs are guaranteed to return true on a call to
1228 * {@link RemoteInput#isDataOnly}.
1229 *
1230 * May return null if no data-only remote inputs were added.
1231 *
1232 * This method exists so that legacy RemoteInput collectors that pre-date the addition
1233 * of non-textual RemoteInputs do not access these remote inputs.
1234 */
1235 public RemoteInput[] getDataOnlyRemoteInputs() {
1236 return (RemoteInput[]) mExtras.getParcelableArray(EXTRA_DATA_ONLY_INPUTS);
1237 }
1238
1239 /**
Griff Hazen959591e2014-05-15 22:26:18 -07001240 * Builder class for {@link Action} objects.
1241 */
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001242 public static final class Builder {
Dan Sandler86647982015-05-13 23:41:13 -04001243 private final Icon mIcon;
Griff Hazen959591e2014-05-15 22:26:18 -07001244 private final CharSequence mTitle;
1245 private final PendingIntent mIntent;
Alex Hills1f27f882017-01-12 11:24:46 -05001246 private boolean mAllowGeneratedReplies = true;
Griff Hazen959591e2014-05-15 22:26:18 -07001247 private final Bundle mExtras;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001248 private ArrayList<RemoteInput> mRemoteInputs;
Griff Hazen959591e2014-05-15 22:26:18 -07001249
1250 /**
1251 * Construct a new builder for {@link Action} object.
1252 * @param icon icon to show for this action
1253 * @param title the title of the action
1254 * @param intent the {@link PendingIntent} to fire when users trigger this action
1255 */
Dan Sandler86647982015-05-13 23:41:13 -04001256 @Deprecated
Griff Hazen959591e2014-05-15 22:26:18 -07001257 public Builder(int icon, CharSequence title, PendingIntent intent) {
Adrian Roos7af53622016-10-12 13:44:05 -07001258 this(Icon.createWithResource("", icon), title, intent);
Dan Sandler86647982015-05-13 23:41:13 -04001259 }
1260
1261 /**
1262 * Construct a new builder for {@link Action} object.
1263 * @param icon icon to show for this action
1264 * @param title the title of the action
1265 * @param intent the {@link PendingIntent} to fire when users trigger this action
1266 */
1267 public Builder(Icon icon, CharSequence title, PendingIntent intent) {
Alex Hills1f27f882017-01-12 11:24:46 -05001268 this(icon, title, intent, new Bundle(), null, true);
Griff Hazen959591e2014-05-15 22:26:18 -07001269 }
1270
1271 /**
1272 * Construct a new builder for {@link Action} object using the fields from an
1273 * {@link Action}.
1274 * @param action the action to read fields from.
1275 */
1276 public Builder(Action action) {
Adrian Roos7af53622016-10-12 13:44:05 -07001277 this(action.getIcon(), action.title, action.actionIntent,
1278 new Bundle(action.mExtras), action.getRemoteInputs(),
1279 action.getAllowGeneratedReplies());
Griff Hazen959591e2014-05-15 22:26:18 -07001280 }
1281
Dan Sandler86647982015-05-13 23:41:13 -04001282 private Builder(Icon icon, CharSequence title, PendingIntent intent, Bundle extras,
Adrian Roos7af53622016-10-12 13:44:05 -07001283 RemoteInput[] remoteInputs, boolean allowGeneratedReplies) {
Griff Hazen959591e2014-05-15 22:26:18 -07001284 mIcon = icon;
1285 mTitle = title;
1286 mIntent = intent;
1287 mExtras = extras;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001288 if (remoteInputs != null) {
1289 mRemoteInputs = new ArrayList<RemoteInput>(remoteInputs.length);
1290 Collections.addAll(mRemoteInputs, remoteInputs);
1291 }
Adrian Roos7af53622016-10-12 13:44:05 -07001292 mAllowGeneratedReplies = allowGeneratedReplies;
Griff Hazen959591e2014-05-15 22:26:18 -07001293 }
1294
1295 /**
1296 * Merge additional metadata into this builder.
1297 *
1298 * <p>Values within the Bundle will replace existing extras values in this Builder.
1299 *
1300 * @see Notification.Action#extras
1301 */
1302 public Builder addExtras(Bundle extras) {
1303 if (extras != null) {
1304 mExtras.putAll(extras);
1305 }
1306 return this;
1307 }
1308
1309 /**
1310 * Get the metadata Bundle used by this Builder.
1311 *
1312 * <p>The returned Bundle is shared with this Builder.
1313 */
1314 public Bundle getExtras() {
1315 return mExtras;
1316 }
1317
1318 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001319 * Add an input to be collected from the user when this action is sent.
1320 * Response values can be retrieved from the fired intent by using the
1321 * {@link RemoteInput#getResultsFromIntent} function.
1322 * @param remoteInput a {@link RemoteInput} to add to the action
1323 * @return this object for method chaining
1324 */
1325 public Builder addRemoteInput(RemoteInput remoteInput) {
1326 if (mRemoteInputs == null) {
1327 mRemoteInputs = new ArrayList<RemoteInput>();
1328 }
1329 mRemoteInputs.add(remoteInput);
1330 return this;
1331 }
1332
1333 /**
Alex Hills42b0c4d2016-04-26 13:35:36 -04001334 * Set whether the platform should automatically generate possible replies to add to
1335 * {@link RemoteInput#getChoices()}. If the {@link Action} doesn't have a
1336 * {@link RemoteInput}, this has no effect.
1337 * @param allowGeneratedReplies {@code true} to allow generated replies, {@code false}
1338 * otherwise
1339 * @return this object for method chaining
Alex Hills1f27f882017-01-12 11:24:46 -05001340 * The default value is {@code true}
Alex Hills42b0c4d2016-04-26 13:35:36 -04001341 */
1342 public Builder setAllowGeneratedReplies(boolean allowGeneratedReplies) {
1343 mAllowGeneratedReplies = allowGeneratedReplies;
1344 return this;
1345 }
1346
1347 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001348 * Apply an extender to this action builder. Extenders may be used to add
1349 * metadata or change options on this builder.
1350 */
Griff Hazen61a9e862014-05-22 16:05:19 -07001351 public Builder extend(Extender extender) {
1352 extender.extend(this);
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001353 return this;
1354 }
1355
1356 /**
Griff Hazen959591e2014-05-15 22:26:18 -07001357 * Combine all of the options that have been set and return a new {@link Action}
1358 * object.
1359 * @return the built action
1360 */
1361 public Action build() {
Shane Brennan472a3b32016-12-12 15:28:10 -08001362 ArrayList<RemoteInput> dataOnlyInputs = new ArrayList<>();
1363 RemoteInput[] previousDataInputs =
1364 (RemoteInput[]) mExtras.getParcelableArray(EXTRA_DATA_ONLY_INPUTS);
Felipe Lemedfd11962017-01-18 18:38:46 -08001365 if (previousDataInputs != null) {
Shane Brennan472a3b32016-12-12 15:28:10 -08001366 for (RemoteInput input : previousDataInputs) {
1367 dataOnlyInputs.add(input);
1368 }
1369 }
1370 List<RemoteInput> textInputs = new ArrayList<>();
1371 if (mRemoteInputs != null) {
1372 for (RemoteInput input : mRemoteInputs) {
1373 if (input.isDataOnly()) {
1374 dataOnlyInputs.add(input);
1375 } else {
1376 textInputs.add(input);
1377 }
1378 }
1379 }
1380 if (!dataOnlyInputs.isEmpty()) {
1381 RemoteInput[] dataInputsArr =
1382 dataOnlyInputs.toArray(new RemoteInput[dataOnlyInputs.size()]);
1383 mExtras.putParcelableArray(EXTRA_DATA_ONLY_INPUTS, dataInputsArr);
1384 }
1385 RemoteInput[] textInputsArr = textInputs.isEmpty()
1386 ? null : textInputs.toArray(new RemoteInput[textInputs.size()]);
1387 return new Action(mIcon, mTitle, mIntent, mExtras, textInputsArr,
Alex Hills42b0c4d2016-04-26 13:35:36 -04001388 mAllowGeneratedReplies);
Griff Hazen959591e2014-05-15 22:26:18 -07001389 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001390 }
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001391
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001392 @Override
1393 public Action clone() {
1394 return new Action(
Dan Sandler86647982015-05-13 23:41:13 -04001395 getIcon(),
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001396 title,
1397 actionIntent, // safe to alias
Julia Reynolds53c934c2016-09-16 14:03:43 -04001398 mExtras == null ? new Bundle() : new Bundle(mExtras),
Alex Hills42b0c4d2016-04-26 13:35:36 -04001399 getRemoteInputs(),
1400 getAllowGeneratedReplies());
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001401 }
1402 @Override
1403 public int describeContents() {
1404 return 0;
1405 }
1406 @Override
1407 public void writeToParcel(Parcel out, int flags) {
Dan Sandler86647982015-05-13 23:41:13 -04001408 final Icon ic = getIcon();
1409 if (ic != null) {
1410 out.writeInt(1);
1411 ic.writeToParcel(out, 0);
1412 } else {
1413 out.writeInt(0);
1414 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001415 TextUtils.writeToParcel(title, out, flags);
1416 if (actionIntent != null) {
1417 out.writeInt(1);
1418 actionIntent.writeToParcel(out, flags);
1419 } else {
1420 out.writeInt(0);
1421 }
Griff Hazen959591e2014-05-15 22:26:18 -07001422 out.writeBundle(mExtras);
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001423 out.writeTypedArray(mRemoteInputs, flags);
Alex Hills42b0c4d2016-04-26 13:35:36 -04001424 out.writeInt(mAllowGeneratedReplies ? 1 : 0);
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001425 }
Griff Hazen959591e2014-05-15 22:26:18 -07001426 public static final Parcelable.Creator<Action> CREATOR =
1427 new Parcelable.Creator<Action>() {
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001428 public Action createFromParcel(Parcel in) {
1429 return new Action(in);
1430 }
1431 public Action[] newArray(int size) {
1432 return new Action[size];
1433 }
1434 };
Griff Hazen61a9e862014-05-22 16:05:19 -07001435
1436 /**
1437 * Extender interface for use with {@link Builder#extend}. Extenders may be used to add
1438 * metadata or change options on an action builder.
1439 */
1440 public interface Extender {
1441 /**
1442 * Apply this extender to a notification action builder.
1443 * @param builder the builder to be modified.
1444 * @return the build object for chaining.
1445 */
1446 public Builder extend(Builder builder);
1447 }
1448
1449 /**
1450 * Wearable extender for notification actions. To add extensions to an action,
1451 * create a new {@link android.app.Notification.Action.WearableExtender} object using
1452 * the {@code WearableExtender()} constructor and apply it to a
1453 * {@link android.app.Notification.Action.Builder} using
1454 * {@link android.app.Notification.Action.Builder#extend}.
1455 *
1456 * <pre class="prettyprint">
1457 * Notification.Action action = new Notification.Action.Builder(
1458 * R.drawable.archive_all, "Archive all", actionIntent)
Griff Hazen14f57992014-05-26 09:07:14 -07001459 * .extend(new Notification.Action.WearableExtender()
Griff Hazen61a9e862014-05-22 16:05:19 -07001460 * .setAvailableOffline(false))
Griff Hazen14f57992014-05-26 09:07:14 -07001461 * .build();</pre>
Griff Hazen61a9e862014-05-22 16:05:19 -07001462 */
1463 public static final class WearableExtender implements Extender {
1464 /** Notification action extra which contains wearable extensions */
1465 private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS";
1466
Pete Gastaf6781d2014-10-07 15:17:05 -04001467 // Keys within EXTRA_WEARABLE_EXTENSIONS for wearable options.
Griff Hazen61a9e862014-05-22 16:05:19 -07001468 private static final String KEY_FLAGS = "flags";
Pete Gastaf6781d2014-10-07 15:17:05 -04001469 private static final String KEY_IN_PROGRESS_LABEL = "inProgressLabel";
1470 private static final String KEY_CONFIRM_LABEL = "confirmLabel";
1471 private static final String KEY_CANCEL_LABEL = "cancelLabel";
Griff Hazen61a9e862014-05-22 16:05:19 -07001472
1473 // Flags bitwise-ored to mFlags
1474 private static final int FLAG_AVAILABLE_OFFLINE = 0x1;
Alex Hills9ab3a232016-04-05 14:54:56 -04001475 private static final int FLAG_HINT_LAUNCHES_ACTIVITY = 1 << 1;
Alex Hills9f087612016-06-07 09:08:59 -04001476 private static final int FLAG_HINT_DISPLAY_INLINE = 1 << 2;
Griff Hazen61a9e862014-05-22 16:05:19 -07001477
1478 // Default value for flags integer
1479 private static final int DEFAULT_FLAGS = FLAG_AVAILABLE_OFFLINE;
1480
1481 private int mFlags = DEFAULT_FLAGS;
1482
Pete Gastaf6781d2014-10-07 15:17:05 -04001483 private CharSequence mInProgressLabel;
1484 private CharSequence mConfirmLabel;
1485 private CharSequence mCancelLabel;
1486
Griff Hazen61a9e862014-05-22 16:05:19 -07001487 /**
1488 * Create a {@link android.app.Notification.Action.WearableExtender} with default
1489 * options.
1490 */
1491 public WearableExtender() {
1492 }
1493
1494 /**
1495 * Create a {@link android.app.Notification.Action.WearableExtender} by reading
1496 * wearable options present in an existing notification action.
1497 * @param action the notification action to inspect.
1498 */
1499 public WearableExtender(Action action) {
1500 Bundle wearableBundle = action.getExtras().getBundle(EXTRA_WEARABLE_EXTENSIONS);
1501 if (wearableBundle != null) {
1502 mFlags = wearableBundle.getInt(KEY_FLAGS, DEFAULT_FLAGS);
Pete Gastaf6781d2014-10-07 15:17:05 -04001503 mInProgressLabel = wearableBundle.getCharSequence(KEY_IN_PROGRESS_LABEL);
1504 mConfirmLabel = wearableBundle.getCharSequence(KEY_CONFIRM_LABEL);
1505 mCancelLabel = wearableBundle.getCharSequence(KEY_CANCEL_LABEL);
Griff Hazen61a9e862014-05-22 16:05:19 -07001506 }
1507 }
1508
1509 /**
1510 * Apply wearable extensions to a notification action that is being built. This is
1511 * typically called by the {@link android.app.Notification.Action.Builder#extend}
1512 * method of {@link android.app.Notification.Action.Builder}.
1513 */
1514 @Override
1515 public Action.Builder extend(Action.Builder builder) {
1516 Bundle wearableBundle = new Bundle();
1517
1518 if (mFlags != DEFAULT_FLAGS) {
1519 wearableBundle.putInt(KEY_FLAGS, mFlags);
1520 }
Pete Gastaf6781d2014-10-07 15:17:05 -04001521 if (mInProgressLabel != null) {
1522 wearableBundle.putCharSequence(KEY_IN_PROGRESS_LABEL, mInProgressLabel);
1523 }
1524 if (mConfirmLabel != null) {
1525 wearableBundle.putCharSequence(KEY_CONFIRM_LABEL, mConfirmLabel);
1526 }
1527 if (mCancelLabel != null) {
1528 wearableBundle.putCharSequence(KEY_CANCEL_LABEL, mCancelLabel);
1529 }
Griff Hazen61a9e862014-05-22 16:05:19 -07001530
1531 builder.getExtras().putBundle(EXTRA_WEARABLE_EXTENSIONS, wearableBundle);
1532 return builder;
1533 }
1534
1535 @Override
1536 public WearableExtender clone() {
1537 WearableExtender that = new WearableExtender();
1538 that.mFlags = this.mFlags;
Pete Gastaf6781d2014-10-07 15:17:05 -04001539 that.mInProgressLabel = this.mInProgressLabel;
1540 that.mConfirmLabel = this.mConfirmLabel;
1541 that.mCancelLabel = this.mCancelLabel;
Griff Hazen61a9e862014-05-22 16:05:19 -07001542 return that;
1543 }
1544
1545 /**
1546 * Set whether this action is available when the wearable device is not connected to
1547 * a companion device. The user can still trigger this action when the wearable device is
1548 * offline, but a visual hint will indicate that the action may not be available.
1549 * Defaults to true.
1550 */
1551 public WearableExtender setAvailableOffline(boolean availableOffline) {
1552 setFlag(FLAG_AVAILABLE_OFFLINE, availableOffline);
1553 return this;
1554 }
1555
1556 /**
1557 * Get whether this action is available when the wearable device is not connected to
1558 * a companion device. The user can still trigger this action when the wearable device is
1559 * offline, but a visual hint will indicate that the action may not be available.
1560 * Defaults to true.
1561 */
1562 public boolean isAvailableOffline() {
1563 return (mFlags & FLAG_AVAILABLE_OFFLINE) != 0;
1564 }
1565
1566 private void setFlag(int mask, boolean value) {
1567 if (value) {
1568 mFlags |= mask;
1569 } else {
1570 mFlags &= ~mask;
1571 }
1572 }
Pete Gastaf6781d2014-10-07 15:17:05 -04001573
1574 /**
1575 * Set a label to display while the wearable is preparing to automatically execute the
1576 * action. This is usually a 'ing' verb ending in ellipsis like "Sending..."
1577 *
1578 * @param label the label to display while the action is being prepared to execute
1579 * @return this object for method chaining
1580 */
1581 public WearableExtender setInProgressLabel(CharSequence label) {
1582 mInProgressLabel = label;
1583 return this;
1584 }
1585
1586 /**
1587 * Get the label to display while the wearable is preparing to automatically execute
1588 * the action. This is usually a 'ing' verb ending in ellipsis like "Sending..."
1589 *
1590 * @return the label to display while the action is being prepared to execute
1591 */
1592 public CharSequence getInProgressLabel() {
1593 return mInProgressLabel;
1594 }
1595
1596 /**
1597 * Set a label to display to confirm that the action should be executed.
1598 * This is usually an imperative verb like "Send".
1599 *
1600 * @param label the label to confirm the action should be executed
1601 * @return this object for method chaining
1602 */
1603 public WearableExtender setConfirmLabel(CharSequence label) {
1604 mConfirmLabel = label;
1605 return this;
1606 }
1607
1608 /**
1609 * Get the label to display to confirm that the action should be executed.
1610 * This is usually an imperative verb like "Send".
1611 *
1612 * @return the label to confirm the action should be executed
1613 */
1614 public CharSequence getConfirmLabel() {
1615 return mConfirmLabel;
1616 }
1617
1618 /**
1619 * Set a label to display to cancel the action.
1620 * This is usually an imperative verb, like "Cancel".
1621 *
1622 * @param label the label to display to cancel the action
1623 * @return this object for method chaining
1624 */
1625 public WearableExtender setCancelLabel(CharSequence label) {
1626 mCancelLabel = label;
1627 return this;
1628 }
1629
1630 /**
1631 * Get the label to display to cancel the action.
1632 * This is usually an imperative verb like "Cancel".
1633 *
1634 * @return the label to display to cancel the action
1635 */
1636 public CharSequence getCancelLabel() {
1637 return mCancelLabel;
1638 }
Alex Hills9ab3a232016-04-05 14:54:56 -04001639
1640 /**
1641 * Set a hint that this Action will launch an {@link Activity} directly, telling the
1642 * platform that it can generate the appropriate transitions.
1643 * @param hintLaunchesActivity {@code true} if the content intent will launch
1644 * an activity and transitions should be generated, false otherwise.
1645 * @return this object for method chaining
1646 */
Alex Hills4ec3ff42016-04-12 11:36:18 -04001647 public WearableExtender setHintLaunchesActivity(
Alex Hills9ab3a232016-04-05 14:54:56 -04001648 boolean hintLaunchesActivity) {
1649 setFlag(FLAG_HINT_LAUNCHES_ACTIVITY, hintLaunchesActivity);
1650 return this;
1651 }
1652
1653 /**
1654 * Get a hint that this Action will launch an {@link Activity} directly, telling the
1655 * platform that it can generate the appropriate transitions
1656 * @return {@code true} if the content intent will launch an activity and transitions
1657 * should be generated, false otherwise. The default value is {@code false} if this was
1658 * never set.
1659 */
Alex Hills4ec3ff42016-04-12 11:36:18 -04001660 public boolean getHintLaunchesActivity() {
Alex Hills9ab3a232016-04-05 14:54:56 -04001661 return (mFlags & FLAG_HINT_LAUNCHES_ACTIVITY) != 0;
1662 }
Alex Hills9f087612016-06-07 09:08:59 -04001663
1664 /**
1665 * Set a hint that this Action should be displayed inline.
1666 *
1667 * @param hintDisplayInline {@code true} if action should be displayed inline, false
1668 * otherwise
1669 * @return this object for method chaining
1670 */
1671 public WearableExtender setHintDisplayActionInline(
1672 boolean hintDisplayInline) {
1673 setFlag(FLAG_HINT_DISPLAY_INLINE, hintDisplayInline);
1674 return this;
1675 }
1676
1677 /**
1678 * Get a hint that this Action should be displayed inline.
1679 *
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08001680 * @return {@code true} if the Action should be displayed inline, {@code false}
Alex Hills9f087612016-06-07 09:08:59 -04001681 * otherwise. The default value is {@code false} if this was never set.
1682 */
1683 public boolean getHintDisplayActionInline() {
1684 return (mFlags & FLAG_HINT_DISPLAY_INLINE) != 0;
1685 }
Griff Hazen61a9e862014-05-22 16:05:19 -07001686 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001687 }
1688
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001689 /**
1690 * Array of all {@link Action} structures attached to this notification by
1691 * {@link Builder#addAction(int, CharSequence, PendingIntent)}. Mostly useful for instances of
1692 * {@link android.service.notification.NotificationListenerService} that provide an alternative
1693 * interface for invoking actions.
1694 */
Daniel Sandlerea2a3172013-02-20 22:24:20 -05001695 public Action[] actions;
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001696
1697 /**
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001698 * Replacement version of this notification whose content will be shown
1699 * in an insecure context such as atop a secure keyguard. See {@link #visibility}
1700 * and {@link #VISIBILITY_PUBLIC}.
1701 */
1702 public Notification publicVersion;
1703
1704 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001705 * Constructs a Notification object with default values.
Joe Onorato46439ce2010-11-19 13:56:21 -08001706 * You might want to consider using {@link Builder} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001707 */
1708 public Notification()
1709 {
1710 this.when = System.currentTimeMillis();
Selim Cinekb85f36fd2016-04-20 18:46:36 -07001711 this.creationTime = System.currentTimeMillis();
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001712 this.priority = PRIORITY_DEFAULT;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001713 }
1714
1715 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001716 * @hide
1717 */
1718 public Notification(Context context, int icon, CharSequence tickerText, long when,
1719 CharSequence contentTitle, CharSequence contentText, Intent contentIntent)
1720 {
Chris Wren1ce4b6d2015-06-11 10:19:43 -04001721 new Builder(context)
1722 .setWhen(when)
1723 .setSmallIcon(icon)
1724 .setTicker(tickerText)
1725 .setContentTitle(contentTitle)
1726 .setContentText(contentText)
1727 .setContentIntent(PendingIntent.getActivity(context, 0, contentIntent, 0))
1728 .buildInto(this);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001729 }
1730
1731 /**
1732 * Constructs a Notification object with the information needed to
1733 * have a status bar icon without the standard expanded view.
1734 *
1735 * @param icon The resource id of the icon to put in the status bar.
1736 * @param tickerText The text that flows by in the status bar when the notification first
1737 * activates.
1738 * @param when The time to show in the time field. In the System.currentTimeMillis
1739 * timebase.
Joe Onorato46439ce2010-11-19 13:56:21 -08001740 *
1741 * @deprecated Use {@link Builder} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001742 */
Joe Onorato46439ce2010-11-19 13:56:21 -08001743 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001744 public Notification(int icon, CharSequence tickerText, long when)
1745 {
1746 this.icon = icon;
1747 this.tickerText = tickerText;
1748 this.when = when;
Selim Cinekb85f36fd2016-04-20 18:46:36 -07001749 this.creationTime = System.currentTimeMillis();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001750 }
1751
1752 /**
1753 * Unflatten the notification from a parcel.
1754 */
Svet Ganovddb94882016-06-23 19:55:24 -07001755 @SuppressWarnings("unchecked")
1756 public Notification(Parcel parcel) {
1757 // IMPORTANT: Add unmarshaling code in readFromParcel as the pending
1758 // intents in extras are always written as the last entry.
1759 readFromParcelImpl(parcel);
1760 // Must be read last!
Felipe Lemedd85da62016-06-28 11:29:54 -07001761 allPendingIntents = (ArraySet<PendingIntent>) parcel.readArraySet(null);
Svet Ganovddb94882016-06-23 19:55:24 -07001762 }
1763
1764 private void readFromParcelImpl(Parcel parcel)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001765 {
1766 int version = parcel.readInt();
1767
1768 when = parcel.readLong();
Selim Cinekb85f36fd2016-04-20 18:46:36 -07001769 creationTime = parcel.readLong();
Dan Sandler3936e7a2015-05-19 20:59:12 -04001770 if (parcel.readInt() != 0) {
1771 mSmallIcon = Icon.CREATOR.createFromParcel(parcel);
Dan Sandler4e787062015-06-17 15:09:48 -04001772 if (mSmallIcon.getType() == Icon.TYPE_RESOURCE) {
1773 icon = mSmallIcon.getResId();
1774 }
Dan Sandler3936e7a2015-05-19 20:59:12 -04001775 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001776 number = parcel.readInt();
1777 if (parcel.readInt() != 0) {
1778 contentIntent = PendingIntent.CREATOR.createFromParcel(parcel);
1779 }
1780 if (parcel.readInt() != 0) {
1781 deleteIntent = PendingIntent.CREATOR.createFromParcel(parcel);
1782 }
1783 if (parcel.readInt() != 0) {
1784 tickerText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
1785 }
1786 if (parcel.readInt() != 0) {
Joe Onorato46439ce2010-11-19 13:56:21 -08001787 tickerView = RemoteViews.CREATOR.createFromParcel(parcel);
Joe Onoratoef1e7762010-09-17 18:38:38 -04001788 }
1789 if (parcel.readInt() != 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001790 contentView = RemoteViews.CREATOR.createFromParcel(parcel);
1791 }
Joe Onorato561d3852010-11-20 18:09:34 -08001792 if (parcel.readInt() != 0) {
Dan Sandlerd63f9322015-05-06 15:18:49 -04001793 mLargeIcon = Icon.CREATOR.createFromParcel(parcel);
Joe Onorato561d3852010-11-20 18:09:34 -08001794 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001795 defaults = parcel.readInt();
1796 flags = parcel.readInt();
1797 if (parcel.readInt() != 0) {
1798 sound = Uri.CREATOR.createFromParcel(parcel);
1799 }
1800
1801 audioStreamType = parcel.readInt();
John Spurlockc0650f022014-07-19 13:22:39 -04001802 if (parcel.readInt() != 0) {
1803 audioAttributes = AudioAttributes.CREATOR.createFromParcel(parcel);
1804 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001805 vibrate = parcel.createLongArray();
1806 ledARGB = parcel.readInt();
1807 ledOnMS = parcel.readInt();
1808 ledOffMS = parcel.readInt();
1809 iconLevel = parcel.readInt();
Daniel Sandlere46cbd32010-06-17 10:35:26 -04001810
1811 if (parcel.readInt() != 0) {
1812 fullScreenIntent = PendingIntent.CREATOR.createFromParcel(parcel);
1813 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001814
1815 priority = parcel.readInt();
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001816
John Spurlockfd7f1e02014-03-18 16:41:57 -04001817 category = parcel.readString();
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001818
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001819 mGroupKey = parcel.readString();
1820
1821 mSortKey = parcel.readString();
1822
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001823 extras = Bundle.setDefusable(parcel.readBundle(), true); // may be null
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001824
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001825 actions = parcel.createTypedArray(Action.CREATOR); // may be null
1826
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001827 if (parcel.readInt() != 0) {
Daniel Sandlerf3b73432012-03-27 15:01:25 -04001828 bigContentView = RemoteViews.CREATOR.createFromParcel(parcel);
1829 }
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001830
Chris Wren8fd39ec2014-02-27 17:43:26 -05001831 if (parcel.readInt() != 0) {
1832 headsUpContentView = RemoteViews.CREATOR.createFromParcel(parcel);
1833 }
1834
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001835 visibility = parcel.readInt();
1836
1837 if (parcel.readInt() != 0) {
1838 publicVersion = Notification.CREATOR.createFromParcel(parcel);
1839 }
Dan Sandler26e81cf2014-05-06 10:01:27 -04001840
1841 color = parcel.readInt();
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04001842
1843 if (parcel.readInt() != 0) {
1844 mChannelId = parcel.readString();
1845 }
Julia Reynolds2a128742016-11-28 14:29:25 -05001846 mTimeout = parcel.readLong();
Julia Reynolds13d898c2017-02-02 12:22:05 -05001847
1848 if (parcel.readInt() != 0) {
1849 mShortcutId = parcel.readString();
1850 }
1851
1852 mBadgeIcon = parcel.readInt();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001853 }
1854
Andy Stadler110988c2010-12-03 14:29:16 -08001855 @Override
Joe Onorato18e69df2010-05-17 22:26:12 -07001856 public Notification clone() {
1857 Notification that = new Notification();
Daniel Sandler1a497d32013-04-18 14:52:45 -04001858 cloneInto(that, true);
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001859 return that;
1860 }
Joe Onorato18e69df2010-05-17 22:26:12 -07001861
Daniel Sandler1a497d32013-04-18 14:52:45 -04001862 /**
1863 * Copy all (or if heavy is false, all except Bitmaps and RemoteViews) members
1864 * of this into that.
1865 * @hide
1866 */
1867 public void cloneInto(Notification that, boolean heavy) {
Joe Onorato18e69df2010-05-17 22:26:12 -07001868 that.when = this.when;
Selim Cinekb85f36fd2016-04-20 18:46:36 -07001869 that.creationTime = this.creationTime;
Dan Sandlerd63f9322015-05-06 15:18:49 -04001870 that.mSmallIcon = this.mSmallIcon;
Joe Onorato18e69df2010-05-17 22:26:12 -07001871 that.number = this.number;
1872
1873 // PendingIntents are global, so there's no reason (or way) to clone them.
1874 that.contentIntent = this.contentIntent;
1875 that.deleteIntent = this.deleteIntent;
Daniel Sandlere46cbd32010-06-17 10:35:26 -04001876 that.fullScreenIntent = this.fullScreenIntent;
Joe Onorato18e69df2010-05-17 22:26:12 -07001877
1878 if (this.tickerText != null) {
1879 that.tickerText = this.tickerText.toString();
1880 }
Daniel Sandler1a497d32013-04-18 14:52:45 -04001881 if (heavy && this.tickerView != null) {
Joe Onorato46439ce2010-11-19 13:56:21 -08001882 that.tickerView = this.tickerView.clone();
Joe Onoratoef1e7762010-09-17 18:38:38 -04001883 }
Daniel Sandler1a497d32013-04-18 14:52:45 -04001884 if (heavy && this.contentView != null) {
Joe Onorato18e69df2010-05-17 22:26:12 -07001885 that.contentView = this.contentView.clone();
1886 }
Dan Sandlerd63f9322015-05-06 15:18:49 -04001887 if (heavy && this.mLargeIcon != null) {
1888 that.mLargeIcon = this.mLargeIcon;
Joe Onorato561d3852010-11-20 18:09:34 -08001889 }
Jozef BABJAKa8b91832011-02-22 08:05:08 +01001890 that.iconLevel = this.iconLevel;
Joe Onorato18e69df2010-05-17 22:26:12 -07001891 that.sound = this.sound; // android.net.Uri is immutable
1892 that.audioStreamType = this.audioStreamType;
John Spurlockc0650f022014-07-19 13:22:39 -04001893 if (this.audioAttributes != null) {
1894 that.audioAttributes = new AudioAttributes.Builder(this.audioAttributes).build();
1895 }
Joe Onorato18e69df2010-05-17 22:26:12 -07001896
1897 final long[] vibrate = this.vibrate;
1898 if (vibrate != null) {
1899 final int N = vibrate.length;
1900 final long[] vib = that.vibrate = new long[N];
1901 System.arraycopy(vibrate, 0, vib, 0, N);
1902 }
1903
1904 that.ledARGB = this.ledARGB;
1905 that.ledOnMS = this.ledOnMS;
1906 that.ledOffMS = this.ledOffMS;
1907 that.defaults = this.defaults;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001908
Joe Onorato18e69df2010-05-17 22:26:12 -07001909 that.flags = this.flags;
1910
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001911 that.priority = this.priority;
Joe Malin8d40d042012-11-05 11:36:40 -08001912
John Spurlockfd7f1e02014-03-18 16:41:57 -04001913 that.category = this.category;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001914
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001915 that.mGroupKey = this.mGroupKey;
1916
1917 that.mSortKey = this.mSortKey;
1918
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001919 if (this.extras != null) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04001920 try {
1921 that.extras = new Bundle(this.extras);
1922 // will unparcel
1923 that.extras.size();
1924 } catch (BadParcelableException e) {
1925 Log.e(TAG, "could not unparcel extras from notification: " + this, e);
1926 that.extras = null;
1927 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001928 }
1929
Felipe Lemedd85da62016-06-28 11:29:54 -07001930 if (!ArrayUtils.isEmpty(allPendingIntents)) {
1931 that.allPendingIntents = new ArraySet<>(allPendingIntents);
Svet Ganovddb94882016-06-23 19:55:24 -07001932 }
1933
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001934 if (this.actions != null) {
1935 that.actions = new Action[this.actions.length];
1936 for(int i=0; i<this.actions.length; i++) {
liangweikang63b03b52017-03-16 19:22:15 +08001937 if ( this.actions[i] != null) {
1938 that.actions[i] = this.actions[i].clone();
1939 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001940 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001941 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001942
Daniel Sandler1a497d32013-04-18 14:52:45 -04001943 if (heavy && this.bigContentView != null) {
Daniel Sandlerf3b73432012-03-27 15:01:25 -04001944 that.bigContentView = this.bigContentView.clone();
1945 }
Daniel Sandler1a497d32013-04-18 14:52:45 -04001946
Chris Wren8fd39ec2014-02-27 17:43:26 -05001947 if (heavy && this.headsUpContentView != null) {
1948 that.headsUpContentView = this.headsUpContentView.clone();
1949 }
1950
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001951 that.visibility = this.visibility;
1952
1953 if (this.publicVersion != null) {
1954 that.publicVersion = new Notification();
1955 this.publicVersion.cloneInto(that.publicVersion, heavy);
1956 }
1957
Dan Sandler26e81cf2014-05-06 10:01:27 -04001958 that.color = this.color;
1959
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04001960 that.mChannelId = this.mChannelId;
Julia Reynolds2a128742016-11-28 14:29:25 -05001961 that.mTimeout = this.mTimeout;
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04001962
Daniel Sandler1a497d32013-04-18 14:52:45 -04001963 if (!heavy) {
1964 that.lightenPayload(); // will clean out extras
1965 }
1966 }
1967
1968 /**
1969 * Removes heavyweight parts of the Notification object for archival or for sending to
1970 * listeners when the full contents are not necessary.
1971 * @hide
1972 */
1973 public final void lightenPayload() {
1974 tickerView = null;
1975 contentView = null;
1976 bigContentView = null;
Chris Wren8fd39ec2014-02-27 17:43:26 -05001977 headsUpContentView = null;
Dan Sandlerd63f9322015-05-06 15:18:49 -04001978 mLargeIcon = null;
Dan Sandler50128532015-12-08 15:42:41 -05001979 if (extras != null && !extras.isEmpty()) {
1980 final Set<String> keyset = extras.keySet();
1981 final int N = keyset.size();
1982 final String[] keys = keyset.toArray(new String[N]);
1983 for (int i=0; i<N; i++) {
1984 final String key = keys[i];
Dmitri Plotnikov22281362017-01-30 11:16:21 -08001985 if (TvExtender.EXTRA_TV_EXTENDER.equals(key)) {
1986 continue;
1987 }
Dan Sandler50128532015-12-08 15:42:41 -05001988 final Object obj = extras.get(key);
1989 if (obj != null &&
1990 ( obj instanceof Parcelable
1991 || obj instanceof Parcelable[]
1992 || obj instanceof SparseArray
1993 || obj instanceof ArrayList)) {
1994 extras.remove(key);
1995 }
1996 }
Daniel Sandler1a497d32013-04-18 14:52:45 -04001997 }
Joe Onorato18e69df2010-05-17 22:26:12 -07001998 }
1999
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04002000 /**
2001 * Make sure this CharSequence is safe to put into a bundle, which basically
2002 * means it had better not be some custom Parcelable implementation.
2003 * @hide
2004 */
2005 public static CharSequence safeCharSequence(CharSequence cs) {
Christoph Studer535ec612014-09-03 15:47:47 +02002006 if (cs == null) return cs;
2007 if (cs.length() > MAX_CHARSEQUENCE_LENGTH) {
2008 cs = cs.subSequence(0, MAX_CHARSEQUENCE_LENGTH);
2009 }
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04002010 if (cs instanceof Parcelable) {
2011 Log.e(TAG, "warning: " + cs.getClass().getCanonicalName()
2012 + " instance is a custom Parcelable and not allowed in Notification");
2013 return cs.toString();
2014 }
Selim Cinek60a54252016-02-26 17:03:25 -08002015 return removeTextSizeSpans(cs);
2016 }
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04002017
Selim Cinek60a54252016-02-26 17:03:25 -08002018 private static CharSequence removeTextSizeSpans(CharSequence charSequence) {
2019 if (charSequence instanceof Spanned) {
2020 Spanned ss = (Spanned) charSequence;
2021 Object[] spans = ss.getSpans(0, ss.length(), Object.class);
2022 SpannableStringBuilder builder = new SpannableStringBuilder(ss.toString());
2023 for (Object span : spans) {
2024 Object resultSpan = span;
Selim Cinek89991a22016-03-07 19:51:54 -08002025 if (resultSpan instanceof CharacterStyle) {
2026 resultSpan = ((CharacterStyle) span).getUnderlying();
2027 }
2028 if (resultSpan instanceof TextAppearanceSpan) {
2029 TextAppearanceSpan originalSpan = (TextAppearanceSpan) resultSpan;
Selim Cinek60a54252016-02-26 17:03:25 -08002030 resultSpan = new TextAppearanceSpan(
2031 originalSpan.getFamily(),
2032 originalSpan.getTextStyle(),
2033 -1,
2034 originalSpan.getTextColor(),
2035 originalSpan.getLinkTextColor());
Selim Cinek89991a22016-03-07 19:51:54 -08002036 } else if (resultSpan instanceof RelativeSizeSpan
2037 || resultSpan instanceof AbsoluteSizeSpan) {
Selim Cinek60a54252016-02-26 17:03:25 -08002038 continue;
Selim Cinek89991a22016-03-07 19:51:54 -08002039 } else {
2040 resultSpan = span;
Selim Cinek60a54252016-02-26 17:03:25 -08002041 }
2042 builder.setSpan(resultSpan, ss.getSpanStart(span), ss.getSpanEnd(span),
2043 ss.getSpanFlags(span));
2044 }
2045 return builder;
2046 }
2047 return charSequence;
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04002048 }
2049
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002050 public int describeContents() {
2051 return 0;
2052 }
2053
2054 /**
Dan Sandler4e787062015-06-17 15:09:48 -04002055 * Flatten this notification into a parcel.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002056 */
Svet Ganovddb94882016-06-23 19:55:24 -07002057 public void writeToParcel(Parcel parcel, int flags) {
2058 // We need to mark all pending intents getting into the notification
2059 // system as being put there to later allow the notification ranker
2060 // to launch them and by doing so add the app to the battery saver white
2061 // list for a short period of time. The problem is that the system
2062 // cannot look into the extras as there may be parcelables there that
2063 // the platform does not know how to handle. To go around that we have
2064 // an explicit list of the pending intents in the extras bundle.
Felipe Lemedd85da62016-06-28 11:29:54 -07002065 final boolean collectPendingIntents = (allPendingIntents == null);
Svet Ganovddb94882016-06-23 19:55:24 -07002066 if (collectPendingIntents) {
2067 PendingIntent.setOnMarshaledListener(
2068 (PendingIntent intent, Parcel out, int outFlags) -> {
2069 if (parcel == out) {
Felipe Lemedd85da62016-06-28 11:29:54 -07002070 if (allPendingIntents == null) {
2071 allPendingIntents = new ArraySet<>();
Svet Ganovddb94882016-06-23 19:55:24 -07002072 }
Felipe Lemedd85da62016-06-28 11:29:54 -07002073 allPendingIntents.add(intent);
Svet Ganovddb94882016-06-23 19:55:24 -07002074 }
2075 });
2076 }
2077 try {
2078 // IMPORTANT: Add marshaling code in writeToParcelImpl as we
Julia Reynolds13d898c2017-02-02 12:22:05 -05002079 // want to intercept all pending events written to the parcel.
Svet Ganovddb94882016-06-23 19:55:24 -07002080 writeToParcelImpl(parcel, flags);
2081 // Must be written last!
Felipe Lemedd85da62016-06-28 11:29:54 -07002082 parcel.writeArraySet(allPendingIntents);
Svet Ganovddb94882016-06-23 19:55:24 -07002083 } finally {
2084 if (collectPendingIntents) {
2085 PendingIntent.setOnMarshaledListener(null);
2086 }
2087 }
2088 }
2089
2090 private void writeToParcelImpl(Parcel parcel, int flags) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002091 parcel.writeInt(1);
2092
2093 parcel.writeLong(when);
Selim Cinekb85f36fd2016-04-20 18:46:36 -07002094 parcel.writeLong(creationTime);
Dan Sandler4e787062015-06-17 15:09:48 -04002095 if (mSmallIcon == null && icon != 0) {
2096 // you snuck an icon in here without using the builder; let's try to keep it
2097 mSmallIcon = Icon.createWithResource("", icon);
2098 }
Dan Sandler3936e7a2015-05-19 20:59:12 -04002099 if (mSmallIcon != null) {
2100 parcel.writeInt(1);
2101 mSmallIcon.writeToParcel(parcel, 0);
2102 } else {
2103 parcel.writeInt(0);
2104 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002105 parcel.writeInt(number);
2106 if (contentIntent != null) {
2107 parcel.writeInt(1);
2108 contentIntent.writeToParcel(parcel, 0);
2109 } else {
2110 parcel.writeInt(0);
2111 }
2112 if (deleteIntent != null) {
2113 parcel.writeInt(1);
2114 deleteIntent.writeToParcel(parcel, 0);
2115 } else {
2116 parcel.writeInt(0);
2117 }
2118 if (tickerText != null) {
2119 parcel.writeInt(1);
2120 TextUtils.writeToParcel(tickerText, parcel, flags);
2121 } else {
2122 parcel.writeInt(0);
2123 }
Joe Onorato46439ce2010-11-19 13:56:21 -08002124 if (tickerView != null) {
Joe Onoratoef1e7762010-09-17 18:38:38 -04002125 parcel.writeInt(1);
Joe Onorato46439ce2010-11-19 13:56:21 -08002126 tickerView.writeToParcel(parcel, 0);
Joe Onoratoef1e7762010-09-17 18:38:38 -04002127 } else {
2128 parcel.writeInt(0);
2129 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002130 if (contentView != null) {
2131 parcel.writeInt(1);
2132 contentView.writeToParcel(parcel, 0);
2133 } else {
2134 parcel.writeInt(0);
2135 }
Selim Cinek279fa862016-06-14 10:57:25 -07002136 if (mLargeIcon == null && largeIcon != null) {
2137 // you snuck an icon in here without using the builder; let's try to keep it
2138 mLargeIcon = Icon.createWithBitmap(largeIcon);
2139 }
Dan Sandlerd63f9322015-05-06 15:18:49 -04002140 if (mLargeIcon != null) {
Joe Onorato561d3852010-11-20 18:09:34 -08002141 parcel.writeInt(1);
Dan Sandlerd63f9322015-05-06 15:18:49 -04002142 mLargeIcon.writeToParcel(parcel, 0);
Joe Onorato561d3852010-11-20 18:09:34 -08002143 } else {
2144 parcel.writeInt(0);
2145 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002146
2147 parcel.writeInt(defaults);
2148 parcel.writeInt(this.flags);
2149
2150 if (sound != null) {
2151 parcel.writeInt(1);
2152 sound.writeToParcel(parcel, 0);
2153 } else {
2154 parcel.writeInt(0);
2155 }
2156 parcel.writeInt(audioStreamType);
John Spurlockc0650f022014-07-19 13:22:39 -04002157
2158 if (audioAttributes != null) {
2159 parcel.writeInt(1);
2160 audioAttributes.writeToParcel(parcel, 0);
2161 } else {
2162 parcel.writeInt(0);
2163 }
2164
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002165 parcel.writeLongArray(vibrate);
2166 parcel.writeInt(ledARGB);
2167 parcel.writeInt(ledOnMS);
2168 parcel.writeInt(ledOffMS);
2169 parcel.writeInt(iconLevel);
Daniel Sandlere46cbd32010-06-17 10:35:26 -04002170
2171 if (fullScreenIntent != null) {
2172 parcel.writeInt(1);
2173 fullScreenIntent.writeToParcel(parcel, 0);
2174 } else {
2175 parcel.writeInt(0);
2176 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002177
2178 parcel.writeInt(priority);
Joe Malin8d40d042012-11-05 11:36:40 -08002179
John Spurlockfd7f1e02014-03-18 16:41:57 -04002180 parcel.writeString(category);
Joe Malin8d40d042012-11-05 11:36:40 -08002181
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002182 parcel.writeString(mGroupKey);
2183
2184 parcel.writeString(mSortKey);
2185
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002186 parcel.writeBundle(extras); // null ok
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002187
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002188 parcel.writeTypedArray(actions, 0); // null ok
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002189
Daniel Sandlerf3b73432012-03-27 15:01:25 -04002190 if (bigContentView != null) {
2191 parcel.writeInt(1);
2192 bigContentView.writeToParcel(parcel, 0);
2193 } else {
2194 parcel.writeInt(0);
2195 }
Dan Sandler0bf2ed82013-12-21 23:33:41 -06002196
Chris Wren8fd39ec2014-02-27 17:43:26 -05002197 if (headsUpContentView != null) {
2198 parcel.writeInt(1);
2199 headsUpContentView.writeToParcel(parcel, 0);
2200 } else {
2201 parcel.writeInt(0);
2202 }
2203
Dan Sandler0bf2ed82013-12-21 23:33:41 -06002204 parcel.writeInt(visibility);
2205
2206 if (publicVersion != null) {
2207 parcel.writeInt(1);
2208 publicVersion.writeToParcel(parcel, 0);
2209 } else {
2210 parcel.writeInt(0);
2211 }
Dan Sandler26e81cf2014-05-06 10:01:27 -04002212
2213 parcel.writeInt(color);
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04002214
2215 if (mChannelId != null) {
2216 parcel.writeInt(1);
2217 parcel.writeString(mChannelId);
2218 } else {
2219 parcel.writeInt(0);
2220 }
Julia Reynolds2a128742016-11-28 14:29:25 -05002221 parcel.writeLong(mTimeout);
Julia Reynolds13d898c2017-02-02 12:22:05 -05002222
2223 if (mShortcutId != null) {
2224 parcel.writeInt(1);
2225 parcel.writeString(mShortcutId);
2226 } else {
2227 parcel.writeInt(0);
2228 }
2229
2230 parcel.writeInt(mBadgeIcon);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002231 }
2232
2233 /**
2234 * Parcelable.Creator that instantiates Notification objects
2235 */
2236 public static final Parcelable.Creator<Notification> CREATOR
2237 = new Parcelable.Creator<Notification>()
2238 {
2239 public Notification createFromParcel(Parcel parcel)
2240 {
2241 return new Notification(parcel);
2242 }
2243
2244 public Notification[] newArray(int size)
2245 {
2246 return new Notification[size];
2247 }
2248 };
2249
2250 /**
2251 * Sets the {@link #contentView} field to be a view with the standard "Latest Event"
2252 * layout.
2253 *
2254 * <p>Uses the {@link #icon} and {@link #when} fields to set the icon and time fields
2255 * in the view.</p>
2256 * @param context The context for your application / activity.
2257 * @param contentTitle The title that goes in the expanded entry.
2258 * @param contentText The text that goes in the expanded entry.
2259 * @param contentIntent The intent to launch when the user clicks the expanded notification.
2260 * If this is an activity, it must include the
2261 * {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} flag, which requires
Scott Main7aee61f2011-02-08 11:25:01 -08002262 * that you take care of task management as described in the
2263 * <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back
2264 * Stack</a> document.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002265 *
Joe Onorato46439ce2010-11-19 13:56:21 -08002266 * @deprecated Use {@link Builder} instead.
Chris Wrena05db382015-06-24 15:18:34 -04002267 * @removed
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002268 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002269 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002270 public void setLatestEventInfo(Context context,
2271 CharSequence contentTitle, CharSequence contentText, PendingIntent contentIntent) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002272 if (context.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1){
2273 Log.e(TAG, "setLatestEventInfo() is deprecated and you should feel deprecated.",
2274 new Throwable());
2275 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002276
Selim Cinek4ac6f602016-06-13 15:47:03 -07002277 if (context.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N) {
2278 extras.putBoolean(EXTRA_SHOW_WHEN, true);
2279 }
2280
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002281 // ensure that any information already set directly is preserved
2282 final Notification.Builder builder = new Notification.Builder(context, this);
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002283
2284 // now apply the latestEventInfo fields
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002285 if (contentTitle != null) {
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002286 builder.setContentTitle(contentTitle);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002287 }
2288 if (contentText != null) {
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002289 builder.setContentText(contentText);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002290 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002291 builder.setContentIntent(contentIntent);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002292
2293 builder.build(); // callers expect this notification to be ready to use
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002294 }
2295
Julia Reynoldsda303542015-11-23 14:00:20 -05002296 /**
2297 * @hide
2298 */
2299 public static void addFieldsFromContext(Context context, Notification notification) {
Julia Reynoldse071abd2017-03-22 10:52:11 -04002300 addFieldsFromContext(context.getApplicationInfo(), notification);
Jeff Sharkey012bc7b2016-04-11 16:30:27 -06002301 }
2302
2303 /**
2304 * @hide
2305 */
Julia Reynoldse071abd2017-03-22 10:52:11 -04002306 public static void addFieldsFromContext(ApplicationInfo ai, Notification notification) {
Jeff Sharkey012bc7b2016-04-11 16:30:27 -06002307 notification.extras.putParcelable(EXTRA_BUILDER_APPLICATION_INFO, ai);
Julia Reynoldsda303542015-11-23 14:00:20 -05002308 }
2309
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002310 @Override
2311 public String toString() {
2312 StringBuilder sb = new StringBuilder();
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002313 sb.append("Notification(pri=");
2314 sb.append(priority);
2315 sb.append(" contentView=");
Joe Onoratoc9596d62011-01-12 17:03:11 -08002316 if (contentView != null) {
2317 sb.append(contentView.getPackage());
2318 sb.append("/0x");
2319 sb.append(Integer.toHexString(contentView.getLayoutId()));
2320 } else {
2321 sb.append("null");
2322 }
2323 sb.append(" vibrate=");
Daniel Sandler6738eee2012-11-16 12:03:32 -05002324 if ((this.defaults & DEFAULT_VIBRATE) != 0) {
2325 sb.append("default");
2326 } else if (this.vibrate != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002327 int N = this.vibrate.length-1;
2328 sb.append("[");
2329 for (int i=0; i<N; i++) {
2330 sb.append(this.vibrate[i]);
2331 sb.append(',');
2332 }
Simon Schoar8cf97d92009-06-10 22:08:37 +02002333 if (N != -1) {
2334 sb.append(this.vibrate[N]);
2335 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002336 sb.append("]");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002337 } else {
2338 sb.append("null");
2339 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002340 sb.append(" sound=");
Daniel Sandler6738eee2012-11-16 12:03:32 -05002341 if ((this.defaults & DEFAULT_SOUND) != 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002342 sb.append("default");
Daniel Sandler6738eee2012-11-16 12:03:32 -05002343 } else if (this.sound != null) {
2344 sb.append(this.sound.toString());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002345 } else {
2346 sb.append("null");
2347 }
Chris Wren365b6d32015-07-16 10:39:26 -04002348 if (this.tickerText != null) {
2349 sb.append(" tick");
2350 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002351 sb.append(" defaults=0x");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002352 sb.append(Integer.toHexString(this.defaults));
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002353 sb.append(" flags=0x");
Daniel Sandlere46cbd32010-06-17 10:35:26 -04002354 sb.append(Integer.toHexString(this.flags));
Dan Sandler26e81cf2014-05-06 10:01:27 -04002355 sb.append(String.format(" color=0x%08x", this.color));
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002356 if (this.category != null) {
2357 sb.append(" category=");
2358 sb.append(this.category);
2359 }
2360 if (this.mGroupKey != null) {
2361 sb.append(" groupKey=");
2362 sb.append(this.mGroupKey);
2363 }
2364 if (this.mSortKey != null) {
2365 sb.append(" sortKey=");
2366 sb.append(this.mSortKey);
2367 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002368 if (actions != null) {
Dan Sandler1b718782014-07-18 12:43:45 -04002369 sb.append(" actions=");
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002370 sb.append(actions.length);
Dan Sandler1b718782014-07-18 12:43:45 -04002371 }
2372 sb.append(" vis=");
2373 sb.append(visibilityToString(this.visibility));
2374 if (this.publicVersion != null) {
2375 sb.append(" publicVersion=");
2376 sb.append(publicVersion.toString());
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002377 }
2378 sb.append(")");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002379 return sb.toString();
2380 }
Joe Onorato46439ce2010-11-19 13:56:21 -08002381
Dan Sandler1b718782014-07-18 12:43:45 -04002382 /**
2383 * {@hide}
2384 */
2385 public static String visibilityToString(int vis) {
2386 switch (vis) {
2387 case VISIBILITY_PRIVATE:
2388 return "PRIVATE";
2389 case VISIBILITY_PUBLIC:
2390 return "PUBLIC";
2391 case VISIBILITY_SECRET:
2392 return "SECRET";
2393 default:
2394 return "UNKNOWN(" + String.valueOf(vis) + ")";
2395 }
2396 }
2397
Joe Onoratocb109a02011-01-18 17:57:41 -08002398 /**
John Spurlock1d881a12015-03-18 19:21:54 -04002399 * {@hide}
2400 */
2401 public static String priorityToString(@Priority int pri) {
2402 switch (pri) {
2403 case PRIORITY_MIN:
2404 return "MIN";
2405 case PRIORITY_LOW:
2406 return "LOW";
2407 case PRIORITY_DEFAULT:
2408 return "DEFAULT";
2409 case PRIORITY_HIGH:
2410 return "HIGH";
2411 case PRIORITY_MAX:
2412 return "MAX";
2413 default:
2414 return "UNKNOWN(" + String.valueOf(pri) + ")";
2415 }
2416 }
2417
2418 /**
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04002419 * Returns the id of the channel this notification posts to.
2420 */
Julia Reynolds37856052016-11-11 09:20:07 -05002421 public String getChannel() {
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04002422 return mChannelId;
2423 }
2424
2425 /**
Julia Reynolds13d898c2017-02-02 12:22:05 -05002426 * Returns the time at which this notification should be canceled by the system, if it's not
2427 * canceled already.
Julia Reynolds2a128742016-11-28 14:29:25 -05002428 */
2429 public long getTimeout() {
2430 return mTimeout;
2431 }
2432
2433 /**
Julia Reynoldse071abd2017-03-22 10:52:11 -04002434 * @removed
Julia Reynolds13d898c2017-02-02 12:22:05 -05002435 * Returns what icon should be shown for this notification if it is being displayed in a
2436 * Launcher that supports badging. Will be one of {@link #BADGE_ICON_NONE},
2437 * {@link #BADGE_ICON_SMALL}, or {@link #BADGE_ICON_LARGE}.
2438 */
Julia Reynoldse071abd2017-03-22 10:52:11 -04002439 @Deprecated
Julia Reynolds13d898c2017-02-02 12:22:05 -05002440 public int getBadgeIcon() {
2441 return mBadgeIcon;
2442 }
2443
2444 /**
Julia Reynoldse071abd2017-03-22 10:52:11 -04002445 * Returns what icon should be shown for this notification if it is being displayed in a
2446 * Launcher that supports badging. Will be one of {@link #BADGE_ICON_NONE},
2447 * {@link #BADGE_ICON_SMALL}, or {@link #BADGE_ICON_LARGE}.
2448 */
2449 public int getBadgeIconType() {
2450 return mBadgeIcon;
2451 }
2452
2453 /**
Julia Reynolds13d898c2017-02-02 12:22:05 -05002454 * Returns the {@link ShortcutInfo#getId() id} that this notification supersedes, if any.
2455 */
2456 public String getShortcutId() {
2457 return mShortcutId;
2458 }
2459
2460 /**
Dan Sandlerd63f9322015-05-06 15:18:49 -04002461 * The small icon representing this notification in the status bar and content view.
2462 *
2463 * @return the small icon representing this notification.
2464 *
2465 * @see Builder#getSmallIcon()
2466 * @see Builder#setSmallIcon(Icon)
2467 */
2468 public Icon getSmallIcon() {
2469 return mSmallIcon;
2470 }
2471
2472 /**
2473 * Used when notifying to clean up legacy small icons.
2474 * @hide
2475 */
2476 public void setSmallIcon(Icon icon) {
2477 mSmallIcon = icon;
2478 }
2479
2480 /**
2481 * The large icon shown in this notification's content view.
2482 * @see Builder#getLargeIcon()
2483 * @see Builder#setLargeIcon(Icon)
2484 */
2485 public Icon getLargeIcon() {
2486 return mLargeIcon;
2487 }
2488
2489 /**
Christoph Studer4600f9b2014-07-22 22:44:43 +02002490 * @hide
2491 */
Christoph Studerc8db24b2014-07-25 17:50:30 +02002492 public boolean isGroupSummary() {
2493 return mGroupKey != null && (flags & FLAG_GROUP_SUMMARY) != 0;
2494 }
2495
2496 /**
2497 * @hide
2498 */
2499 public boolean isGroupChild() {
2500 return mGroupKey != null && (flags & FLAG_GROUP_SUMMARY) == 0;
2501 }
2502
2503 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002504 * Builder class for {@link Notification} objects.
Joe Malin8d40d042012-11-05 11:36:40 -08002505 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002506 * Provides a convenient way to set the various fields of a {@link Notification} and generate
Scott Main183bf112012-08-13 19:12:13 -07002507 * content views using the platform's notification layout template. If your app supports
2508 * versions of Android as old as API level 4, you can instead use
2509 * {@link android.support.v4.app.NotificationCompat.Builder NotificationCompat.Builder},
2510 * available in the <a href="{@docRoot}tools/extras/support-library.html">Android Support
2511 * library</a>.
Joe Malin8d40d042012-11-05 11:36:40 -08002512 *
Scott Main183bf112012-08-13 19:12:13 -07002513 * <p>Example:
Joe Malin8d40d042012-11-05 11:36:40 -08002514 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002515 * <pre class="prettyprint">
Scott Main183bf112012-08-13 19:12:13 -07002516 * Notification noti = new Notification.Builder(mContext)
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002517 * .setContentTitle(&quot;New mail from &quot; + sender.toString())
2518 * .setContentText(subject)
2519 * .setSmallIcon(R.drawable.new_mail)
2520 * .setLargeIcon(aBitmap)
Chris Wrenfbd96ba2012-05-01 12:03:58 -04002521 * .build();
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002522 * </pre>
Joe Onoratocb109a02011-01-18 17:57:41 -08002523 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002524 public static class Builder {
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05002525 /**
2526 * @hide
2527 */
2528 public static final String EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT =
2529 "android.rebuild.contentViewActionCount";
2530 /**
2531 * @hide
2532 */
2533 public static final String EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT
2534 = "android.rebuild.bigViewActionCount";
2535 /**
2536 * @hide
2537 */
2538 public static final String EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT
2539 = "android.rebuild.hudViewActionCount";
2540
Daniel Sandler602ad1c2012-06-12 16:06:27 -04002541 private static final int MAX_ACTION_BUTTONS = 3;
Daniel Sandler8680bf82012-05-15 16:52:52 -04002542
Selim Cinek6743c0b2017-01-18 18:24:01 -08002543 private static final boolean USE_ONLY_TITLE_IN_LOW_PRIORITY_SUMMARY =
2544 SystemProperties.getBoolean("notifications.only_title", true);
2545
Joe Onorato46439ce2010-11-19 13:56:21 -08002546 private Context mContext;
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002547 private Notification mN;
2548 private Bundle mUserExtras = new Bundle();
Chris Wrenfbd96ba2012-05-01 12:03:58 -04002549 private Style mStyle;
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002550 private ArrayList<Action> mActions = new ArrayList<Action>(MAX_ACTION_BUTTONS);
2551 private ArrayList<String> mPersonList = new ArrayList<String>();
2552 private NotificationColorUtil mColorUtil;
Selim Cinek7b9605b2017-01-19 17:36:00 -08002553 private boolean mIsLegacy;
2554 private boolean mIsLegacyInitialized;
Christoph Studer7ac80e62014-08-04 16:01:57 +02002555
2556 /**
Adrian Roos4ff3b122016-02-01 12:26:13 -08002557 * Caches a contrast-enhanced version of {@link #mCachedContrastColorIsFor}.
2558 */
2559 private int mCachedContrastColor = COLOR_INVALID;
2560 private int mCachedContrastColorIsFor = COLOR_INVALID;
Adrian Roos487374f2017-01-11 15:48:14 -08002561 /**
2562 * Caches a ambient version of {@link #mCachedContrastColorIsFor}.
2563 */
2564 private int mCachedAmbientColor = COLOR_INVALID;
2565 private int mCachedAmbientColorIsFor = COLOR_INVALID;
Adrian Roos4ff3b122016-02-01 12:26:13 -08002566
2567 /**
Adrian Roos70d7aa32017-01-11 15:39:06 -08002568 * Caches an instance of StandardTemplateParams. Note that this may have been used before,
2569 * so make sure to call {@link StandardTemplateParams#reset()} before using it.
2570 */
2571 StandardTemplateParams mParams = new StandardTemplateParams();
Selim Cinek7b9605b2017-01-19 17:36:00 -08002572 private int mTextColorsAreForBackground = COLOR_INVALID;
2573 private int mPrimaryTextColor = COLOR_INVALID;
2574 private int mSecondaryTextColor = COLOR_INVALID;
2575 private int mActionBarColor = COLOR_INVALID;
Adrian Roos70d7aa32017-01-11 15:39:06 -08002576
2577 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002578 * Constructs a new Builder with the defaults:
Joe Onoratocb109a02011-01-18 17:57:41 -08002579 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002580
2581 * <table>
2582 * <tr><th align=right>priority</th>
2583 * <td>{@link #PRIORITY_DEFAULT}</td></tr>
2584 * <tr><th align=right>when</th>
2585 * <td>now ({@link System#currentTimeMillis()})</td></tr>
2586 * <tr><th align=right>audio stream</th>
2587 * <td>{@link #STREAM_DEFAULT}</td></tr>
2588 * </table>
Joe Onoratocb109a02011-01-18 17:57:41 -08002589 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002590
2591 * @param context
2592 * A {@link Context} that will be used by the Builder to construct the
2593 * RemoteViews. The Context will not be held past the lifetime of this Builder
2594 * object.
Geoffrey Pitsch5caa2762017-01-12 09:35:54 -05002595 * @param channelId
2596 * The constructed Notification will be posted on this
2597 * {@link NotificationChannel}. To use a NotificationChannel, it must first be
2598 * created using {@link NotificationManager#createNotificationChannel}.
Joe Onoratocb109a02011-01-18 17:57:41 -08002599 */
Geoffrey Pitsch5caa2762017-01-12 09:35:54 -05002600 public Builder(Context context, String channelId) {
2601 this(context, (Notification) null);
2602 mN.mChannelId = channelId;
2603 }
2604
2605 /**
2606 * @deprecated use {@link Notification.Builder#Notification.Builder(Context, String)}
2607 * instead. All posted Notifications must specify a NotificationChannel Id.
2608 */
2609 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08002610 public Builder(Context context) {
Geoffrey Pitsch5caa2762017-01-12 09:35:54 -05002611 this(context, (Notification) null);
Joe Onorato46439ce2010-11-19 13:56:21 -08002612 }
2613
Joe Onoratocb109a02011-01-18 17:57:41 -08002614 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002615 * @hide
Christoph Studer4600f9b2014-07-22 22:44:43 +02002616 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002617 public Builder(Context context, Notification toAdopt) {
2618 mContext = context;
Christoph Studer4600f9b2014-07-22 22:44:43 +02002619
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002620 if (toAdopt == null) {
2621 mN = new Notification();
Selim Cinek0ff1ce602016-04-05 18:27:16 -07002622 if (context.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N) {
2623 mN.extras.putBoolean(EXTRA_SHOW_WHEN, true);
2624 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002625 mN.priority = PRIORITY_DEFAULT;
2626 mN.visibility = VISIBILITY_PRIVATE;
2627 } else {
2628 mN = toAdopt;
2629 if (mN.actions != null) {
2630 Collections.addAll(mActions, mN.actions);
Christoph Studer4600f9b2014-07-22 22:44:43 +02002631 }
2632
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002633 if (mN.extras.containsKey(EXTRA_PEOPLE)) {
2634 Collections.addAll(mPersonList, mN.extras.getStringArray(EXTRA_PEOPLE));
2635 }
2636
Selim Cinek4ac6f602016-06-13 15:47:03 -07002637 if (mN.getSmallIcon() == null && mN.icon != 0) {
2638 setSmallIcon(mN.icon);
2639 }
2640
2641 if (mN.getLargeIcon() == null && mN.largeIcon != null) {
2642 setLargeIcon(mN.largeIcon);
2643 }
2644
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002645 String templateClass = mN.extras.getString(EXTRA_TEMPLATE);
2646 if (!TextUtils.isEmpty(templateClass)) {
2647 final Class<? extends Style> styleClass
2648 = getNotificationStyleClass(templateClass);
2649 if (styleClass == null) {
2650 Log.d(TAG, "Unknown style class: " + templateClass);
2651 } else {
2652 try {
Adrian Roosc1a80b02016-04-05 14:54:55 -07002653 final Constructor<? extends Style> ctor =
2654 styleClass.getDeclaredConstructor();
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002655 ctor.setAccessible(true);
2656 final Style style = ctor.newInstance();
2657 style.restoreFromExtras(mN.extras);
2658
2659 if (style != null) {
2660 setStyle(style);
2661 }
2662 } catch (Throwable t) {
2663 Log.e(TAG, "Could not create Style", t);
2664 }
2665 }
2666 }
2667
2668 }
2669 }
2670
2671 private NotificationColorUtil getColorUtil() {
Selim Cinek99104832017-01-25 14:47:33 -08002672 if (mColorUtil == null) {
2673 mColorUtil = NotificationColorUtil.getInstance(mContext);
Christoph Studer4600f9b2014-07-22 22:44:43 +02002674 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002675 return mColorUtil;
Christoph Studer4600f9b2014-07-22 22:44:43 +02002676 }
2677
2678 /**
Julia Reynolds13d898c2017-02-02 12:22:05 -05002679 * If this notification is duplicative of a Launcher shortcut, sets the
2680 * {@link ShortcutInfo#getId() id} of the shortcut, in case the Launcher wants to hide
2681 * the shortcut.
2682 *
2683 * This field will be ignored by Launchers that don't support badging or
2684 * {@link android.content.pm.ShortcutManager shortcuts}.
2685 *
2686 * @param shortcutId the {@link ShortcutInfo#getId() id} of the shortcut this notification
2687 * supersedes
2688 */
2689 public Builder setShortcutId(String shortcutId) {
2690 mN.mShortcutId = shortcutId;
2691 return this;
2692 }
2693
2694 /**
Julia Reynoldse071abd2017-03-22 10:52:11 -04002695 * @removed
Julia Reynolds13d898c2017-02-02 12:22:05 -05002696 * Sets which icon to display as a badge for this notification.
2697 *
2698 * Must be one of {@link #BADGE_ICON_NONE}, {@link #BADGE_ICON_SMALL},
2699 * {@link #BADGE_ICON_LARGE}.
2700 *
2701 * Note: This value might be ignored, for launchers that don't support badge icons.
2702 */
Julia Reynoldse071abd2017-03-22 10:52:11 -04002703 @Deprecated
Julia Reynolds13d898c2017-02-02 12:22:05 -05002704 public Builder chooseBadgeIcon(int icon) {
2705 mN.mBadgeIcon = icon;
2706 return this;
2707 }
2708
2709 /**
Julia Reynoldse071abd2017-03-22 10:52:11 -04002710 * Sets which icon to display as a badge for this notification.
2711 *
2712 * Must be one of {@link #BADGE_ICON_NONE}, {@link #BADGE_ICON_SMALL},
2713 * {@link #BADGE_ICON_LARGE}.
2714 *
2715 * Note: This value might be ignored, for launchers that don't support badge icons.
2716 */
Julia Reynolds612beb22017-03-30 10:48:30 -04002717 public Builder setBadgeIconType(int icon) {
Julia Reynoldse071abd2017-03-22 10:52:11 -04002718 mN.mBadgeIcon = icon;
2719 return this;
2720 }
2721
2722 /**
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04002723 * Specifies the channel the notification should be delivered on.
2724 */
2725 public Builder setChannel(String channelId) {
2726 mN.mChannelId = channelId;
2727 return this;
2728 }
2729
2730 /**
Julia Reynolds50989772017-02-23 14:32:16 -05002731 * Specifies a duration in milliseconds after which this notification should be canceled,
2732 * if it is not already canceled.
Julia Reynolds2a128742016-11-28 14:29:25 -05002733 */
Julia Reynolds50989772017-02-23 14:32:16 -05002734 public Builder setTimeout(long durationMs) {
2735 mN.mTimeout = durationMs;
Julia Reynolds2a128742016-11-28 14:29:25 -05002736 return this;
2737 }
2738
2739 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002740 * Add a timestamp pertaining to the notification (usually the time the event occurred).
Selim Cinek0ff1ce602016-04-05 18:27:16 -07002741 *
2742 * For apps targeting {@link android.os.Build.VERSION_CODES#N} and above, this time is not
2743 * shown anymore by default and must be opted into by using
2744 * {@link android.app.Notification.Builder#setShowWhen(boolean)}
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002745 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002746 * @see Notification#when
Joe Onoratocb109a02011-01-18 17:57:41 -08002747 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002748 public Builder setWhen(long when) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002749 mN.when = when;
Joe Onorato46439ce2010-11-19 13:56:21 -08002750 return this;
2751 }
2752
Joe Onoratocb109a02011-01-18 17:57:41 -08002753 /**
Griff Hazen50c11652014-05-16 09:46:31 -07002754 * Control whether the timestamp set with {@link #setWhen(long) setWhen} is shown
Daniel Sandler0c890492012-09-12 17:23:10 -07002755 * in the content view.
Selim Cinek0ff1ce602016-04-05 18:27:16 -07002756 * For apps targeting {@link android.os.Build.VERSION_CODES#N} and above, this defaults to
2757 * {@code false}. For earlier apps, the default is {@code true}.
Daniel Sandler0c890492012-09-12 17:23:10 -07002758 */
2759 public Builder setShowWhen(boolean show) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002760 mN.extras.putBoolean(EXTRA_SHOW_WHEN, show);
Daniel Sandler0c890492012-09-12 17:23:10 -07002761 return this;
2762 }
2763
2764 /**
Daniel Sandlerd33b8032012-05-10 11:41:48 -04002765 * Show the {@link Notification#when} field as a stopwatch.
Joe Malin8d40d042012-11-05 11:36:40 -08002766 *
2767 * Instead of presenting <code>when</code> as a timestamp, the notification will show an
Daniel Sandlerd33b8032012-05-10 11:41:48 -04002768 * automatically updating display of the minutes and seconds since <code>when</code>.
Daniel Sandlera2985ed2012-04-03 16:42:00 -04002769 *
Daniel Sandlerd33b8032012-05-10 11:41:48 -04002770 * Useful when showing an elapsed time (like an ongoing phone call).
2771 *
Selim Cinek81c23aa2016-02-25 16:23:13 -08002772 * The counter can also be set to count down to <code>when</code> when using
Adrian Roos96b7e202016-05-17 13:50:38 -07002773 * {@link #setChronometerCountDown(boolean)}.
Selim Cinek81c23aa2016-02-25 16:23:13 -08002774 *
Daniel Sandlerd33b8032012-05-10 11:41:48 -04002775 * @see android.widget.Chronometer
Daniel Sandlera2985ed2012-04-03 16:42:00 -04002776 * @see Notification#when
Adrian Roos96b7e202016-05-17 13:50:38 -07002777 * @see #setChronometerCountDown(boolean)
Daniel Sandlera2985ed2012-04-03 16:42:00 -04002778 */
2779 public Builder setUsesChronometer(boolean b) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002780 mN.extras.putBoolean(EXTRA_SHOW_CHRONOMETER, b);
Daniel Sandlera2985ed2012-04-03 16:42:00 -04002781 return this;
2782 }
2783
2784 /**
Selim Cinek81c23aa2016-02-25 16:23:13 -08002785 * Sets the Chronometer to count down instead of counting up.
2786 *
2787 * <p>This is only relevant if {@link #setUsesChronometer(boolean)} has been set to true.
2788 * If it isn't set the chronometer will count up.
2789 *
2790 * @see #setUsesChronometer(boolean)
2791 */
Adrian Roos96b7e202016-05-17 13:50:38 -07002792 public Builder setChronometerCountDown(boolean countDown) {
2793 mN.extras.putBoolean(EXTRA_CHRONOMETER_COUNT_DOWN, countDown);
Selim Cinek81c23aa2016-02-25 16:23:13 -08002794 return this;
2795 }
2796
2797 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002798 * Set the small icon resource, which will be used to represent the notification in the
2799 * status bar.
Joe Onoratocb109a02011-01-18 17:57:41 -08002800 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002801
2802 * The platform template for the expanded view will draw this icon in the left, unless a
2803 * {@link #setLargeIcon(Bitmap) large icon} has also been specified, in which case the small
2804 * icon will be moved to the right-hand side.
2805 *
2806
2807 * @param icon
2808 * A resource ID in the application's package of the drawable to use.
2809 * @see Notification#icon
Joe Onoratocb109a02011-01-18 17:57:41 -08002810 */
Tor Norbye7b9c9122013-05-30 16:48:33 -07002811 public Builder setSmallIcon(@DrawableRes int icon) {
Dan Sandlerd63f9322015-05-06 15:18:49 -04002812 return setSmallIcon(icon != 0
2813 ? Icon.createWithResource(mContext, icon)
2814 : null);
Joe Onorato46439ce2010-11-19 13:56:21 -08002815 }
2816
Joe Onoratocb109a02011-01-18 17:57:41 -08002817 /**
2818 * A variant of {@link #setSmallIcon(int) setSmallIcon(int)} that takes an additional
2819 * level parameter for when the icon is a {@link android.graphics.drawable.LevelListDrawable
2820 * LevelListDrawable}.
2821 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002822 * @param icon A resource ID in the application's package of the drawable to use.
Joe Onoratocb109a02011-01-18 17:57:41 -08002823 * @param level The level to use for the icon.
2824 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002825 * @see Notification#icon
2826 * @see Notification#iconLevel
Joe Onoratocb109a02011-01-18 17:57:41 -08002827 */
Tor Norbye7b9c9122013-05-30 16:48:33 -07002828 public Builder setSmallIcon(@DrawableRes int icon, int level) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002829 mN.iconLevel = level;
Dan Sandlerd63f9322015-05-06 15:18:49 -04002830 return setSmallIcon(icon);
2831 }
2832
2833 /**
2834 * Set the small icon, which will be used to represent the notification in the
2835 * status bar and content view (unless overriden there by a
2836 * {@link #setLargeIcon(Bitmap) large icon}).
2837 *
2838 * @param icon An Icon object to use.
2839 * @see Notification#icon
2840 */
2841 public Builder setSmallIcon(Icon icon) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002842 mN.setSmallIcon(icon);
2843 if (icon != null && icon.getType() == Icon.TYPE_RESOURCE) {
2844 mN.icon = icon.getResId();
2845 }
Joe Onorato46439ce2010-11-19 13:56:21 -08002846 return this;
2847 }
2848
Joe Onoratocb109a02011-01-18 17:57:41 -08002849 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002850 * Set the first line of text in the platform notification template.
Joe Onoratocb109a02011-01-18 17:57:41 -08002851 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002852 public Builder setContentTitle(CharSequence title) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002853 mN.extras.putCharSequence(EXTRA_TITLE, safeCharSequence(title));
Joe Onorato46439ce2010-11-19 13:56:21 -08002854 return this;
2855 }
2856
Joe Onoratocb109a02011-01-18 17:57:41 -08002857 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002858 * Set the second line of text in the platform notification template.
Joe Onoratocb109a02011-01-18 17:57:41 -08002859 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002860 public Builder setContentText(CharSequence text) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002861 mN.extras.putCharSequence(EXTRA_TEXT, safeCharSequence(text));
Joe Onorato46439ce2010-11-19 13:56:21 -08002862 return this;
2863 }
2864
Joe Onoratocb109a02011-01-18 17:57:41 -08002865 /**
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07002866 * This provides some additional information that is displayed in the notification. No
2867 * guarantees are given where exactly it is displayed.
2868 *
2869 * <p>This information should only be provided if it provides an essential
2870 * benefit to the understanding of the notification. The more text you provide the
2871 * less readable it becomes. For example, an email client should only provide the account
2872 * name here if more than one email account has been added.</p>
2873 *
2874 * <p>As of {@link android.os.Build.VERSION_CODES#N} this information is displayed in the
2875 * notification header area.
2876 *
2877 * On Android versions before {@link android.os.Build.VERSION_CODES#N}
2878 * this will be shown in the third line of text in the platform notification template.
2879 * You should not be using {@link #setProgress(int, int, boolean)} at the
2880 * same time on those versions; they occupy the same place.
2881 * </p>
Daniel Sandlerf3b73432012-03-27 15:01:25 -04002882 */
2883 public Builder setSubText(CharSequence text) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002884 mN.extras.putCharSequence(EXTRA_SUB_TEXT, safeCharSequence(text));
Daniel Sandlerf3b73432012-03-27 15:01:25 -04002885 return this;
2886 }
2887
2888 /**
Adrian Roose458aa82015-12-08 16:17:19 -08002889 * Set the remote input history.
2890 *
2891 * This should be set to the most recent inputs that have been sent
2892 * through a {@link RemoteInput} of this Notification and cleared once the it is no
2893 * longer relevant (e.g. for chat notifications once the other party has responded).
2894 *
2895 * The most recent input must be stored at the 0 index, the second most recent at the
2896 * 1 index, etc. Note that the system will limit both how far back the inputs will be shown
2897 * and how much of each individual input is shown.
2898 *
2899 * <p>Note: The reply text will only be shown on notifications that have least one action
2900 * with a {@code RemoteInput}.</p>
2901 */
2902 public Builder setRemoteInputHistory(CharSequence[] text) {
2903 if (text == null) {
2904 mN.extras.putCharSequenceArray(EXTRA_REMOTE_INPUT_HISTORY, null);
2905 } else {
2906 final int N = Math.min(MAX_REPLY_HISTORY, text.length);
2907 CharSequence[] safe = new CharSequence[N];
2908 for (int i = 0; i < N; i++) {
2909 safe[i] = safeCharSequence(text[i]);
2910 }
2911 mN.extras.putCharSequenceArray(EXTRA_REMOTE_INPUT_HISTORY, safe);
2912 }
2913 return this;
2914 }
2915
2916 /**
Julia Reynolds13d898c2017-02-02 12:22:05 -05002917 * Sets the number of items this notification represents. May be displayed as a badge count
2918 * for Launchers that support badging.
Joe Onoratocb109a02011-01-18 17:57:41 -08002919 */
Joe Onorato8595a3d2010-11-19 18:12:07 -08002920 public Builder setNumber(int number) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002921 mN.number = number;
Joe Onorato8595a3d2010-11-19 18:12:07 -08002922 return this;
2923 }
2924
Joe Onoratocb109a02011-01-18 17:57:41 -08002925 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002926 * A small piece of additional information pertaining to this notification.
2927 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002928 * The platform template will draw this on the last line of the notification, at the far
2929 * right (to the right of a smallIcon if it has been placed there).
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07002930 *
2931 * @deprecated use {@link #setSubText(CharSequence)} instead to set a text in the header.
2932 * For legacy apps targeting a version below {@link android.os.Build.VERSION_CODES#N} this
2933 * field will still show up, but the subtext will take precedence.
Joe Onoratocb109a02011-01-18 17:57:41 -08002934 */
Aurimas Liutikas514c5ef2016-05-24 15:22:55 -07002935 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08002936 public Builder setContentInfo(CharSequence info) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002937 mN.extras.putCharSequence(EXTRA_INFO_TEXT, safeCharSequence(info));
Joe Onorato46439ce2010-11-19 13:56:21 -08002938 return this;
2939 }
2940
Joe Onoratocb109a02011-01-18 17:57:41 -08002941 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002942 * Set the progress this notification represents.
2943 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002944 * The platform template will represent this using a {@link ProgressBar}.
Jeff Sharkey1c400132011-08-05 14:50:13 -07002945 */
2946 public Builder setProgress(int max, int progress, boolean indeterminate) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002947 mN.extras.putInt(EXTRA_PROGRESS, progress);
2948 mN.extras.putInt(EXTRA_PROGRESS_MAX, max);
2949 mN.extras.putBoolean(EXTRA_PROGRESS_INDETERMINATE, indeterminate);
Jeff Sharkey1c400132011-08-05 14:50:13 -07002950 return this;
2951 }
2952
2953 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002954 * Supply a custom RemoteViews to use instead of the platform template.
2955 *
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002956 * Use {@link #setCustomContentView(RemoteViews)} instead.
Joe Onoratocb109a02011-01-18 17:57:41 -08002957 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002958 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08002959 public Builder setContent(RemoteViews views) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002960 return setCustomContentView(views);
2961 }
2962
2963 /**
2964 * Supply custom RemoteViews to use instead of the platform template.
2965 *
2966 * This will override the layout that would otherwise be constructed by this Builder
2967 * object.
2968 */
2969 public Builder setCustomContentView(RemoteViews contentView) {
2970 mN.contentView = contentView;
2971 return this;
2972 }
2973
2974 /**
2975 * Supply custom RemoteViews to use instead of the platform template in the expanded form.
2976 *
2977 * This will override the expanded layout that would otherwise be constructed by this
2978 * Builder object.
2979 */
2980 public Builder setCustomBigContentView(RemoteViews contentView) {
2981 mN.bigContentView = contentView;
2982 return this;
2983 }
2984
2985 /**
2986 * Supply custom RemoteViews to use instead of the platform template in the heads up dialog.
2987 *
2988 * This will override the heads-up layout that would otherwise be constructed by this
2989 * Builder object.
2990 */
2991 public Builder setCustomHeadsUpContentView(RemoteViews contentView) {
2992 mN.headsUpContentView = contentView;
Joe Onorato46439ce2010-11-19 13:56:21 -08002993 return this;
2994 }
2995
Joe Onoratocb109a02011-01-18 17:57:41 -08002996 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002997 * Supply a {@link PendingIntent} to be sent when the notification is clicked.
2998 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002999 * As of {@link android.os.Build.VERSION_CODES#HONEYCOMB}, if this field is unset and you
3000 * have specified a custom RemoteViews with {@link #setContent(RemoteViews)}, you can use
3001 * {@link RemoteViews#setOnClickPendingIntent RemoteViews.setOnClickPendingIntent(int,PendingIntent)}
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003002 * to assign PendingIntents to individual views in that custom layout (i.e., to create
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003003 * clickable buttons inside the notification view).
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003004 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003005 * @see Notification#contentIntent Notification.contentIntent
Joe Onoratocb109a02011-01-18 17:57:41 -08003006 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003007 public Builder setContentIntent(PendingIntent intent) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003008 mN.contentIntent = intent;
Joe Onorato46439ce2010-11-19 13:56:21 -08003009 return this;
3010 }
3011
Joe Onoratocb109a02011-01-18 17:57:41 -08003012 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003013 * Supply a {@link PendingIntent} to send when the notification is cleared explicitly by the user.
3014 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003015 * @see Notification#deleteIntent
Joe Onoratocb109a02011-01-18 17:57:41 -08003016 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003017 public Builder setDeleteIntent(PendingIntent intent) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003018 mN.deleteIntent = intent;
Joe Onorato46439ce2010-11-19 13:56:21 -08003019 return this;
3020 }
3021
Joe Onoratocb109a02011-01-18 17:57:41 -08003022 /**
3023 * An intent to launch instead of posting the notification to the status bar.
3024 * Only for use with extremely high-priority notifications demanding the user's
3025 * <strong>immediate</strong> attention, such as an incoming phone call or
3026 * alarm clock that the user has explicitly set to a particular time.
3027 * If this facility is used for something else, please give the user an option
3028 * to turn it off and use a normal notification, as this can be extremely
3029 * disruptive.
3030 *
Chris Wren47c20a12014-06-18 17:27:29 -04003031 * <p>
3032 * The system UI may choose to display a heads-up notification, instead of
3033 * launching this intent, while the user is using the device.
3034 * </p>
3035 *
Joe Onoratocb109a02011-01-18 17:57:41 -08003036 * @param intent The pending intent to launch.
3037 * @param highPriority Passing true will cause this notification to be sent
3038 * even if other notifications are suppressed.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003039 *
3040 * @see Notification#fullScreenIntent
Joe Onoratocb109a02011-01-18 17:57:41 -08003041 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003042 public Builder setFullScreenIntent(PendingIntent intent, boolean highPriority) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003043 mN.fullScreenIntent = intent;
Joe Onorato46439ce2010-11-19 13:56:21 -08003044 setFlag(FLAG_HIGH_PRIORITY, highPriority);
3045 return this;
3046 }
3047
Joe Onoratocb109a02011-01-18 17:57:41 -08003048 /**
Dan Sandler5fcdf6e2014-07-18 11:31:15 -04003049 * Set the "ticker" text which is sent to accessibility services.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003050 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003051 * @see Notification#tickerText
Joe Onoratocb109a02011-01-18 17:57:41 -08003052 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003053 public Builder setTicker(CharSequence tickerText) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003054 mN.tickerText = safeCharSequence(tickerText);
Joe Onorato46439ce2010-11-19 13:56:21 -08003055 return this;
3056 }
3057
Joe Onoratocb109a02011-01-18 17:57:41 -08003058 /**
Dan Sandler5fcdf6e2014-07-18 11:31:15 -04003059 * Obsolete version of {@link #setTicker(CharSequence)}.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003060 *
Joe Onoratocb109a02011-01-18 17:57:41 -08003061 */
Dan Sandler5fcdf6e2014-07-18 11:31:15 -04003062 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08003063 public Builder setTicker(CharSequence tickerText, RemoteViews views) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003064 setTicker(tickerText);
3065 // views is ignored
Joe Onorato46439ce2010-11-19 13:56:21 -08003066 return this;
3067 }
3068
Joe Onoratocb109a02011-01-18 17:57:41 -08003069 /**
Dan Sandlerd63f9322015-05-06 15:18:49 -04003070 * Add a large icon to the notification content view.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003071 *
3072 * In the platform template, this image will be shown on the left of the notification view
Dan Sandlerd63f9322015-05-06 15:18:49 -04003073 * in place of the {@link #setSmallIcon(Icon) small icon} (which will be placed in a small
3074 * badge atop the large icon).
Dan Sandler08a04c12015-05-06 15:18:49 -04003075 */
Dan Sandlerd63f9322015-05-06 15:18:49 -04003076 public Builder setLargeIcon(Bitmap b) {
3077 return setLargeIcon(b != null ? Icon.createWithBitmap(b) : null);
3078 }
3079
3080 /**
3081 * Add a large icon to the notification content view.
3082 *
3083 * In the platform template, this image will be shown on the left of the notification view
3084 * in place of the {@link #setSmallIcon(Icon) small icon} (which will be placed in a small
3085 * badge atop the large icon).
3086 */
3087 public Builder setLargeIcon(Icon icon) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003088 mN.mLargeIcon = icon;
3089 mN.extras.putParcelable(EXTRA_LARGE_ICON, icon);
Joe Onorato46439ce2010-11-19 13:56:21 -08003090 return this;
3091 }
3092
Joe Onoratocb109a02011-01-18 17:57:41 -08003093 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003094 * Set the sound to play.
3095 *
John Spurlockc0650f022014-07-19 13:22:39 -04003096 * It will be played using the {@link #AUDIO_ATTRIBUTES_DEFAULT default audio attributes}
3097 * for notifications.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003098 *
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003099 * @deprecated use {@link NotificationChannel#setSound(Uri, AudioAttributes)} instead.
Joe Onoratocb109a02011-01-18 17:57:41 -08003100 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003101 @Deprecated
Joe Onorato52f80cd2010-11-21 15:34:48 -08003102 public Builder setSound(Uri sound) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003103 mN.sound = sound;
3104 mN.audioAttributes = AUDIO_ATTRIBUTES_DEFAULT;
Joe Onorato52f80cd2010-11-21 15:34:48 -08003105 return this;
3106 }
3107
Joe Onoratocb109a02011-01-18 17:57:41 -08003108 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003109 * Set the sound to play, along with a specific stream on which to play it.
Joe Onoratocb109a02011-01-18 17:57:41 -08003110 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003111 * See {@link android.media.AudioManager} for the <code>STREAM_</code> constants.
3112 *
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003113 * @deprecated use {@link NotificationChannel#setSound(Uri, AudioAttributes)}.
Joe Onoratocb109a02011-01-18 17:57:41 -08003114 */
Jean-Michel Trivi81f871e2014-08-06 16:32:38 -07003115 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08003116 public Builder setSound(Uri sound, int streamType) {
Jean-Michel Trivi2f7511f2016-11-28 15:40:27 -08003117 PlayerBase.deprecateStreamTypeForPlayback(streamType, "Notification", "setSound()");
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003118 mN.sound = sound;
3119 mN.audioStreamType = streamType;
Joe Onorato46439ce2010-11-19 13:56:21 -08003120 return this;
3121 }
3122
Joe Onoratocb109a02011-01-18 17:57:41 -08003123 /**
John Spurlockc0650f022014-07-19 13:22:39 -04003124 * Set the sound to play, along with specific {@link AudioAttributes audio attributes} to
3125 * use during playback.
3126 *
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003127 * @deprecated use {@link NotificationChannel#setSound(Uri, AudioAttributes)} instead.
John Spurlockc0650f022014-07-19 13:22:39 -04003128 * @see Notification#sound
3129 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003130 @Deprecated
John Spurlockc0650f022014-07-19 13:22:39 -04003131 public Builder setSound(Uri sound, AudioAttributes audioAttributes) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003132 mN.sound = sound;
3133 mN.audioAttributes = audioAttributes;
John Spurlockc0650f022014-07-19 13:22:39 -04003134 return this;
3135 }
3136
3137 /**
Joe Onoratocb109a02011-01-18 17:57:41 -08003138 * Set the vibration pattern to use.
3139 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003140 * See {@link android.os.Vibrator#vibrate(long[], int)} for a discussion of the
3141 * <code>pattern</code> parameter.
3142 *
Chris Wren47c20a12014-06-18 17:27:29 -04003143 * <p>
3144 * A notification that vibrates is more likely to be presented as a heads-up notification.
3145 * </p>
3146 *
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003147 * @deprecated use {@link NotificationChannel#setVibrationPattern(long[])} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003148 * @see Notification#vibrate
Joe Onoratocb109a02011-01-18 17:57:41 -08003149 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003150 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08003151 public Builder setVibrate(long[] pattern) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003152 mN.vibrate = pattern;
Joe Onorato46439ce2010-11-19 13:56:21 -08003153 return this;
3154 }
3155
Joe Onoratocb109a02011-01-18 17:57:41 -08003156 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003157 * Set the desired color for the indicator LED on the device, as well as the
3158 * blink duty cycle (specified in milliseconds).
3159 *
3160
3161 * Not all devices will honor all (or even any) of these values.
3162 *
Julia Reynolds529e3322017-02-06 08:33:01 -05003163 * @deprecated use {@link NotificationChannel#enableLights(boolean)} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003164 * @see Notification#ledARGB
3165 * @see Notification#ledOnMS
3166 * @see Notification#ledOffMS
Joe Onoratocb109a02011-01-18 17:57:41 -08003167 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003168 @Deprecated
Tor Norbye80756e32015-03-02 09:39:27 -08003169 public Builder setLights(@ColorInt int argb, int onMs, int offMs) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003170 mN.ledARGB = argb;
3171 mN.ledOnMS = onMs;
3172 mN.ledOffMS = offMs;
Julia Reynolds10ee1fc2015-11-09 11:04:55 -05003173 if (onMs != 0 || offMs != 0) {
3174 mN.flags |= FLAG_SHOW_LIGHTS;
3175 }
Joe Onorato46439ce2010-11-19 13:56:21 -08003176 return this;
3177 }
3178
Joe Onoratocb109a02011-01-18 17:57:41 -08003179 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003180 * Set whether this is an "ongoing" notification.
Joe Onoratocb109a02011-01-18 17:57:41 -08003181 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003182
3183 * Ongoing notifications cannot be dismissed by the user, so your application or service
3184 * must take care of canceling them.
3185 *
3186
3187 * They are typically used to indicate a background task that the user is actively engaged
3188 * with (e.g., playing music) or is pending in some way and therefore occupying the device
3189 * (e.g., a file download, sync operation, active network connection).
3190 *
3191
3192 * @see Notification#FLAG_ONGOING_EVENT
3193 * @see Service#setForeground(boolean)
Joe Onoratocb109a02011-01-18 17:57:41 -08003194 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003195 public Builder setOngoing(boolean ongoing) {
3196 setFlag(FLAG_ONGOING_EVENT, ongoing);
3197 return this;
3198 }
3199
Joe Onoratocb109a02011-01-18 17:57:41 -08003200 /**
Selim Cinek7b9605b2017-01-19 17:36:00 -08003201 * Set whether this notification should be colorized. When set, the color set with
3202 * {@link #setColor(int)} will be used as the background color of this notification.
3203 * <p>
Selim Cinek7b9605b2017-01-19 17:36:00 -08003204 * This should only be used for high priority ongoing tasks like navigation, an ongoing
3205 * call, or other similarly high-priority events for the user.
Selim Cinek99104832017-01-25 14:47:33 -08003206 * <p>
3207 * For most styles, the coloring will only be applied if the notification is ongoing.
3208 * However, for {@link MediaStyle} and {@link DecoratedMediaCustomViewStyle} notifications
3209 * that have a media session attached there is no requirement for it to be ongoing.
Selim Cinek7b9605b2017-01-19 17:36:00 -08003210 *
3211 * @see Builder#setOngoing(boolean)
3212 * @see Builder#setColor(int)
Selim Cinek99104832017-01-25 14:47:33 -08003213 * @see MediaStyle#setMediaSession(MediaSession.Token)
Selim Cinek7b9605b2017-01-19 17:36:00 -08003214 */
3215 public Builder setColorized(boolean colorize) {
3216 mN.extras.putBoolean(EXTRA_COLORIZED, colorize);
3217 return this;
3218 }
3219
3220 /**
Joe Onoratocb109a02011-01-18 17:57:41 -08003221 * Set this flag if you would only like the sound, vibrate
3222 * and ticker to be played if the notification is not already showing.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003223 *
3224 * @see Notification#FLAG_ONLY_ALERT_ONCE
Joe Onoratocb109a02011-01-18 17:57:41 -08003225 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003226 public Builder setOnlyAlertOnce(boolean onlyAlertOnce) {
3227 setFlag(FLAG_ONLY_ALERT_ONCE, onlyAlertOnce);
3228 return this;
3229 }
3230
Joe Onoratocb109a02011-01-18 17:57:41 -08003231 /**
Julia Reynolds04499532016-09-13 14:04:53 -04003232 * Make this notification automatically dismissed when the user touches it.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003233 *
3234 * @see Notification#FLAG_AUTO_CANCEL
Joe Onoratocb109a02011-01-18 17:57:41 -08003235 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003236 public Builder setAutoCancel(boolean autoCancel) {
Joe Onorato281d83f2011-01-04 17:13:10 -08003237 setFlag(FLAG_AUTO_CANCEL, autoCancel);
Joe Onorato46439ce2010-11-19 13:56:21 -08003238 return this;
3239 }
3240
Joe Onoratocb109a02011-01-18 17:57:41 -08003241 /**
Griff Hazendfcb0802014-02-11 12:00:00 -08003242 * Set whether or not this notification should not bridge to other devices.
3243 *
3244 * <p>Some notifications can be bridged to other devices for remote display.
3245 * This hint can be set to recommend this notification not be bridged.
3246 */
3247 public Builder setLocalOnly(boolean localOnly) {
3248 setFlag(FLAG_LOCAL_ONLY, localOnly);
3249 return this;
3250 }
3251
3252 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003253 * Set which notification properties will be inherited from system defaults.
Joe Onoratocb109a02011-01-18 17:57:41 -08003254 * <p>
3255 * The value should be one or more of the following fields combined with
3256 * bitwise-or:
3257 * {@link #DEFAULT_SOUND}, {@link #DEFAULT_VIBRATE}, {@link #DEFAULT_LIGHTS}.
3258 * <p>
3259 * For all default values, use {@link #DEFAULT_ALL}.
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003260 *
3261 * @deprecated use {@link NotificationChannel#enableVibration(boolean)} and
Julia Reynolds529e3322017-02-06 08:33:01 -05003262 * {@link NotificationChannel#enableLights(boolean)} and
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003263 * {@link NotificationChannel#setSound(Uri, AudioAttributes)} instead.
Joe Onoratocb109a02011-01-18 17:57:41 -08003264 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003265 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08003266 public Builder setDefaults(int defaults) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003267 mN.defaults = defaults;
Joe Onorato46439ce2010-11-19 13:56:21 -08003268 return this;
3269 }
3270
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003271 /**
3272 * Set the priority of this notification.
3273 *
3274 * @see Notification#priority
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003275 * @deprecated use {@link NotificationChannel#setImportance(int)} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003276 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003277 @Deprecated
Tor Norbyed9273d62013-05-30 15:59:53 -07003278 public Builder setPriority(@Priority int pri) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003279 mN.priority = pri;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003280 return this;
3281 }
Joe Malin8d40d042012-11-05 11:36:40 -08003282
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003283 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -04003284 * Set the notification category.
Joe Malin8d40d042012-11-05 11:36:40 -08003285 *
John Spurlockfd7f1e02014-03-18 16:41:57 -04003286 * @see Notification#category
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003287 */
John Spurlockfd7f1e02014-03-18 16:41:57 -04003288 public Builder setCategory(String category) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003289 mN.category = category;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003290 return this;
3291 }
3292
3293 /**
Chris Wrendde75302014-03-26 17:24:15 -04003294 * Add a person that is relevant to this notification.
3295 *
Chris Wrene6c48932014-09-29 17:19:27 -04003296 * <P>
3297 * Depending on user preferences, this annotation may allow the notification to pass
Julia Reynoldse071abd2017-03-22 10:52:11 -04003298 * through interruption filters, if this notification is of category {@link #CATEGORY_CALL}
3299 * or {@link #CATEGORY_MESSAGE}. The addition of people may also cause this notification to
3300 * appear more prominently in the user interface.
Chris Wrene6c48932014-09-29 17:19:27 -04003301 * </P>
3302 *
3303 * <P>
3304 * The person should be specified by the {@code String} representation of a
3305 * {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI}.
3306 * </P>
3307 *
3308 * <P>The system will also attempt to resolve {@code mailto:} and {@code tel:} schema
3309 * URIs. The path part of these URIs must exist in the contacts database, in the
3310 * appropriate column, or the reference will be discarded as invalid. Telephone schema
3311 * URIs will be resolved by {@link android.provider.ContactsContract.PhoneLookup}.
3312 * </P>
3313 *
3314 * @param uri A URI for the person.
Chris Wrendde75302014-03-26 17:24:15 -04003315 * @see Notification#EXTRA_PEOPLE
3316 */
Chris Wrene6c48932014-09-29 17:19:27 -04003317 public Builder addPerson(String uri) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003318 mPersonList.add(uri);
Chris Wrendde75302014-03-26 17:24:15 -04003319 return this;
3320 }
3321
3322 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003323 * Set this notification to be part of a group of notifications sharing the same key.
3324 * Grouped notifications may display in a cluster or stack on devices which
3325 * support such rendering.
3326 *
3327 * <p>To make this notification the summary for its group, also call
3328 * {@link #setGroupSummary}. A sort order can be specified for group members by using
3329 * {@link #setSortKey}.
3330 * @param groupKey The group key of the group.
3331 * @return this object for method chaining
3332 */
3333 public Builder setGroup(String groupKey) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003334 mN.mGroupKey = groupKey;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003335 return this;
3336 }
3337
3338 /**
3339 * Set this notification to be the group summary for a group of notifications.
3340 * Grouped notifications may display in a cluster or stack on devices which
Julia Reynolds04499532016-09-13 14:04:53 -04003341 * support such rendering. If thereRequires a group key also be set using {@link #setGroup}.
3342 * The group summary may be suppressed if too few notifications are included in the group.
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003343 * @param isGroupSummary Whether this notification should be a group summary.
3344 * @return this object for method chaining
3345 */
3346 public Builder setGroupSummary(boolean isGroupSummary) {
3347 setFlag(FLAG_GROUP_SUMMARY, isGroupSummary);
3348 return this;
3349 }
3350
3351 /**
3352 * Set a sort key that orders this notification among other notifications from the
3353 * same package. This can be useful if an external sort was already applied and an app
3354 * would like to preserve this. Notifications will be sorted lexicographically using this
3355 * value, although providing different priorities in addition to providing sort key may
3356 * cause this value to be ignored.
3357 *
3358 * <p>This sort key can also be used to order members of a notification group. See
Griff Hazen9e1379f2014-05-20 12:50:51 -07003359 * {@link #setGroup}.
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003360 *
3361 * @see String#compareTo(String)
3362 */
3363 public Builder setSortKey(String sortKey) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003364 mN.mSortKey = sortKey;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003365 return this;
3366 }
3367
3368 /**
Griff Hazen720042b2014-02-24 15:46:56 -08003369 * Merge additional metadata into this notification.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003370 *
Griff Hazen720042b2014-02-24 15:46:56 -08003371 * <p>Values within the Bundle will replace existing extras values in this Builder.
3372 *
3373 * @see Notification#extras
3374 */
Griff Hazen959591e2014-05-15 22:26:18 -07003375 public Builder addExtras(Bundle extras) {
3376 if (extras != null) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003377 mUserExtras.putAll(extras);
Griff Hazen720042b2014-02-24 15:46:56 -08003378 }
3379 return this;
3380 }
3381
3382 /**
3383 * Set metadata for this notification.
3384 *
3385 * <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 -04003386 * current contents are copied into the Notification each time {@link #build()} is
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003387 * called.
3388 *
Griff Hazen720042b2014-02-24 15:46:56 -08003389 * <p>Replaces any existing extras values with those from the provided Bundle.
3390 * Use {@link #addExtras} to merge in metadata instead.
3391 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003392 * @see Notification#extras
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003393 */
Griff Hazen959591e2014-05-15 22:26:18 -07003394 public Builder setExtras(Bundle extras) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003395 if (extras != null) {
3396 mUserExtras = extras;
3397 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003398 return this;
3399 }
3400
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003401 /**
Griff Hazen720042b2014-02-24 15:46:56 -08003402 * Get the current metadata Bundle used by this notification Builder.
3403 *
3404 * <p>The returned Bundle is shared with this Builder.
3405 *
3406 * <p>The current contents of this Bundle are copied into the Notification each time
3407 * {@link #build()} is called.
3408 *
3409 * @see Notification#extras
3410 */
3411 public Bundle getExtras() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003412 return mUserExtras;
3413 }
3414
3415 private Bundle getAllExtras() {
3416 final Bundle saveExtras = (Bundle) mUserExtras.clone();
3417 saveExtras.putAll(mN.extras);
3418 return saveExtras;
Griff Hazen720042b2014-02-24 15:46:56 -08003419 }
3420
3421 /**
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003422 * Add an action to this notification. Actions are typically displayed by
3423 * the system as a button adjacent to the notification content.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04003424 * <p>
3425 * Every action must have an icon (32dp square and matching the
3426 * <a href="{@docRoot}design/style/iconography.html#action-bar">Holo
3427 * Dark action bar</a> visual style), a textual label, and a {@link PendingIntent}.
3428 * <p>
3429 * A notification in its expanded form can display up to 3 actions, from left to right in
3430 * the order they were added. Actions will not be displayed when the notification is
3431 * collapsed, however, so be sure that any essential functions may be accessed by the user
3432 * in some other way (for example, in the Activity pointed to by {@link #contentIntent}).
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003433 *
3434 * @param icon Resource ID of a drawable that represents the action.
3435 * @param title Text describing the action.
3436 * @param intent PendingIntent to be fired when the action is invoked.
Dan Sandler86647982015-05-13 23:41:13 -04003437 *
3438 * @deprecated Use {@link #addAction(Action)} instead.
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003439 */
Dan Sandler86647982015-05-13 23:41:13 -04003440 @Deprecated
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003441 public Builder addAction(int icon, CharSequence title, PendingIntent intent) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04003442 mActions.add(new Action(icon, safeCharSequence(title), intent));
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003443 return this;
3444 }
3445
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003446 /**
Griff Hazen959591e2014-05-15 22:26:18 -07003447 * Add an action to this notification. Actions are typically displayed by
3448 * the system as a button adjacent to the notification content.
3449 * <p>
3450 * Every action must have an icon (32dp square and matching the
3451 * <a href="{@docRoot}design/style/iconography.html#action-bar">Holo
3452 * Dark action bar</a> visual style), a textual label, and a {@link PendingIntent}.
3453 * <p>
3454 * A notification in its expanded form can display up to 3 actions, from left to right in
3455 * the order they were added. Actions will not be displayed when the notification is
3456 * collapsed, however, so be sure that any essential functions may be accessed by the user
3457 * in some other way (for example, in the Activity pointed to by {@link #contentIntent}).
3458 *
3459 * @param action The action to add.
3460 */
3461 public Builder addAction(Action action) {
liangweikang63b03b52017-03-16 19:22:15 +08003462 if (action != null) {
3463 mActions.add(action);
3464 }
Griff Hazen959591e2014-05-15 22:26:18 -07003465 return this;
3466 }
3467
3468 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003469 * Alter the complete list of actions attached to this notification.
3470 * @see #addAction(Action).
3471 *
3472 * @param actions
3473 * @return
3474 */
3475 public Builder setActions(Action... actions) {
3476 mActions.clear();
3477 for (int i = 0; i < actions.length; i++) {
liangweikang63b03b52017-03-16 19:22:15 +08003478 if (actions[i] != null) {
3479 mActions.add(actions[i]);
3480 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003481 }
3482 return this;
3483 }
3484
3485 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003486 * Add a rich notification style to be applied at build time.
3487 *
3488 * @param style Object responsible for modifying the notification style.
3489 */
3490 public Builder setStyle(Style style) {
3491 if (mStyle != style) {
3492 mStyle = style;
Daniel Sandlerc08dea22012-06-28 08:35:24 -07003493 if (mStyle != null) {
3494 mStyle.setBuilder(this);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003495 mN.extras.putString(EXTRA_TEMPLATE, style.getClass().getName());
3496 } else {
3497 mN.extras.remove(EXTRA_TEMPLATE);
Daniel Sandlerc08dea22012-06-28 08:35:24 -07003498 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003499 }
3500 return this;
3501 }
3502
Dan Sandler0bf2ed82013-12-21 23:33:41 -06003503 /**
3504 * Specify the value of {@link #visibility}.
Griff Hazenb720abe2014-05-20 13:15:30 -07003505 *
Dan Sandler0bf2ed82013-12-21 23:33:41 -06003506 * @param visibility One of {@link #VISIBILITY_PRIVATE} (the default),
3507 * {@link #VISIBILITY_SECRET}, or {@link #VISIBILITY_PUBLIC}.
3508 *
3509 * @return The same Builder.
3510 */
3511 public Builder setVisibility(int visibility) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003512 mN.visibility = visibility;
Dan Sandler0bf2ed82013-12-21 23:33:41 -06003513 return this;
3514 }
3515
3516 /**
3517 * Supply a replacement Notification whose contents should be shown in insecure contexts
3518 * (i.e. atop the secure lockscreen). See {@link #visibility} and {@link #VISIBILITY_PUBLIC}.
3519 * @param n A replacement notification, presumably with some or all info redacted.
3520 * @return The same Builder.
3521 */
3522 public Builder setPublicVersion(Notification n) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003523 if (n != null) {
3524 mN.publicVersion = new Notification();
3525 n.cloneInto(mN.publicVersion, /*heavy=*/ true);
3526 } else {
3527 mN.publicVersion = null;
3528 }
Dan Sandler0bf2ed82013-12-21 23:33:41 -06003529 return this;
3530 }
3531
Griff Hazenb720abe2014-05-20 13:15:30 -07003532 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003533 * Apply an extender to this notification builder. Extenders may be used to add
3534 * metadata or change options on this builder.
3535 */
Griff Hazen61a9e862014-05-22 16:05:19 -07003536 public Builder extend(Extender extender) {
3537 extender.extend(this);
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003538 return this;
3539 }
3540
Dan Sandler4e787062015-06-17 15:09:48 -04003541 /**
3542 * @hide
3543 */
Julia Reynoldse46bb372016-03-17 11:05:58 -04003544 public Builder setFlag(int mask, boolean value) {
Joe Onorato46439ce2010-11-19 13:56:21 -08003545 if (value) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003546 mN.flags |= mask;
Joe Onorato46439ce2010-11-19 13:56:21 -08003547 } else {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003548 mN.flags &= ~mask;
Joe Onorato46439ce2010-11-19 13:56:21 -08003549 }
Julia Reynoldse46bb372016-03-17 11:05:58 -04003550 return this;
Joe Onorato46439ce2010-11-19 13:56:21 -08003551 }
3552
Dan Sandler26e81cf2014-05-06 10:01:27 -04003553 /**
3554 * Sets {@link Notification#color}.
3555 *
3556 * @param argb The accent color to use
3557 *
3558 * @return The same Builder.
3559 */
Tor Norbye80756e32015-03-02 09:39:27 -08003560 public Builder setColor(@ColorInt int argb) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003561 mN.color = argb;
Julia Reynolds10ee1fc2015-11-09 11:04:55 -05003562 sanitizeColor();
Dan Sandler26e81cf2014-05-06 10:01:27 -04003563 return this;
3564 }
3565
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003566 private Drawable getProfileBadgeDrawable() {
Chris Wren66619a22016-05-12 16:42:37 -04003567 if (mContext.getUserId() == UserHandle.USER_SYSTEM) {
3568 // This user can never be a badged profile,
3569 // and also includes USER_ALL system notifications.
3570 return null;
3571 }
Christoph Studer7ac80e62014-08-04 16:01:57 +02003572 // Note: This assumes that the current user can read the profile badge of the
3573 // originating user.
Selim Cineke6ff9462016-01-15 15:07:06 -08003574 return mContext.getPackageManager().getUserBadgeForDensityNoBackground(
Julia Reynoldsda303542015-11-23 14:00:20 -05003575 new UserHandle(mContext.getUserId()), 0);
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003576 }
3577
3578 private Bitmap getProfileBadge() {
3579 Drawable badge = getProfileBadgeDrawable();
Kenny Guy8a0101b2014-05-08 23:34:12 +01003580 if (badge == null) {
3581 return null;
3582 }
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003583 final int size = mContext.getResources().getDimensionPixelSize(
3584 R.dimen.notification_badge_size);
3585 Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
Kenny Guy8a0101b2014-05-08 23:34:12 +01003586 Canvas canvas = new Canvas(bitmap);
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003587 badge.setBounds(0, 0, size, size);
Kenny Guy8a0101b2014-05-08 23:34:12 +01003588 badge.draw(canvas);
3589 return bitmap;
3590 }
3591
Selim Cinekc848c3a2016-01-13 15:27:30 -08003592 private void bindProfileBadge(RemoteViews contentView) {
Kenny Guy98193ea2014-07-24 19:54:37 +01003593 Bitmap profileBadge = getProfileBadge();
3594
Kenny Guy98193ea2014-07-24 19:54:37 +01003595 if (profileBadge != null) {
Selim Cinekc848c3a2016-01-13 15:27:30 -08003596 contentView.setImageViewBitmap(R.id.profile_badge, profileBadge);
3597 contentView.setViewVisibility(R.id.profile_badge, View.VISIBLE);
Selim Cinek7b9605b2017-01-19 17:36:00 -08003598 if (isColorized()) {
3599 contentView.setDrawableParameters(R.id.profile_badge, false, -1,
3600 getPrimaryTextColor(), PorterDuff.Mode.SRC_ATOP, -1);
3601 }
Kenny Guy98193ea2014-07-24 19:54:37 +01003602 }
Kenny Guy98193ea2014-07-24 19:54:37 +01003603 }
3604
Christoph Studerfe718432014-09-01 18:21:18 +02003605 private void resetStandardTemplate(RemoteViews contentView) {
Selim Cinekeaa29ca2015-11-23 13:51:13 -08003606 resetNotificationHeader(contentView);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003607 resetContentMargins(contentView);
Christoph Studerfe718432014-09-01 18:21:18 +02003608 contentView.setViewVisibility(R.id.right_icon, View.GONE);
Selim Cinek860b6da2015-12-16 19:02:19 -08003609 contentView.setViewVisibility(R.id.title, View.GONE);
Christoph Studerfe718432014-09-01 18:21:18 +02003610 contentView.setTextViewText(R.id.title, null);
Selim Cinek41598732016-01-11 16:58:37 -08003611 contentView.setViewVisibility(R.id.text, View.GONE);
Christoph Studerfe718432014-09-01 18:21:18 +02003612 contentView.setTextViewText(R.id.text, null);
Selim Cinek29603462015-11-17 19:04:39 -08003613 contentView.setViewVisibility(R.id.text_line_1, View.GONE);
Selim Cinek41598732016-01-11 16:58:37 -08003614 contentView.setTextViewText(R.id.text_line_1, null);
Christoph Studerfe718432014-09-01 18:21:18 +02003615 contentView.setViewVisibility(R.id.progress, View.GONE);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003616 }
3617
Selim Cinekeaa29ca2015-11-23 13:51:13 -08003618 /**
3619 * Resets the notification header to its original state
3620 */
3621 private void resetNotificationHeader(RemoteViews contentView) {
Adrian Roosc4337a32016-08-02 18:30:34 -07003622 // Small icon doesn't need to be reset, as it's always set. Resetting would prevent
3623 // re-using the drawable when the notification is updated.
Selim Cinek7b836392015-12-04 20:02:59 -08003624 contentView.setBoolean(R.id.notification_header, "setExpanded", false);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003625 contentView.setTextViewText(R.id.app_name_text, null);
Christoph Studerca1db712014-09-10 17:31:33 +02003626 contentView.setViewVisibility(R.id.chronometer, View.GONE);
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07003627 contentView.setViewVisibility(R.id.header_text, View.GONE);
Adrian Roos9dfb78f2016-06-30 15:43:44 -07003628 contentView.setTextViewText(R.id.header_text, null);
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07003629 contentView.setViewVisibility(R.id.header_text_divider, View.GONE);
Selim Cinek29603462015-11-17 19:04:39 -08003630 contentView.setViewVisibility(R.id.time_divider, View.GONE);
Selim Cinekb85f36fd2016-04-20 18:46:36 -07003631 contentView.setViewVisibility(R.id.time, View.GONE);
Selim Cinekc848c3a2016-01-13 15:27:30 -08003632 contentView.setImageViewIcon(R.id.profile_badge, null);
3633 contentView.setViewVisibility(R.id.profile_badge, View.GONE);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003634 }
3635
3636 private void resetContentMargins(RemoteViews contentView) {
Adrian Roos2d5dbba2016-06-08 17:11:53 -07003637 contentView.setViewLayoutMarginEndDimen(R.id.line1, 0);
3638 contentView.setViewLayoutMarginEndDimen(R.id.text, 0);
Christoph Studerfe718432014-09-01 18:21:18 +02003639 }
3640
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003641 private RemoteViews applyStandardTemplate(int resId) {
Adrian Roos70d7aa32017-01-11 15:39:06 -08003642 return applyStandardTemplate(resId, mParams.reset().fillTextsFrom(this));
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02003643 }
3644
3645 /**
3646 * @param hasProgress whether the progress bar should be shown and set
3647 */
3648 private RemoteViews applyStandardTemplate(int resId, boolean hasProgress) {
Adrian Roos70d7aa32017-01-11 15:39:06 -08003649 return applyStandardTemplate(resId, mParams.reset().hasProgress(hasProgress)
3650 .fillTextsFrom(this));
Adrian Roosc1a80b02016-04-05 14:54:55 -07003651 }
3652
Adrian Roos70d7aa32017-01-11 15:39:06 -08003653 private RemoteViews applyStandardTemplate(int resId, StandardTemplateParams p) {
Kenny Guy77320062014-08-27 21:37:15 +01003654 RemoteViews contentView = new BuilderRemoteViews(mContext.getApplicationInfo(), resId);
Dan Sandler539aad42014-08-04 00:43:39 -04003655
Christoph Studerfe718432014-09-01 18:21:18 +02003656 resetStandardTemplate(contentView);
3657
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003658 final Bundle ex = mN.extras;
Selim Cinek7b9605b2017-01-19 17:36:00 -08003659 updateBackgroundColor(contentView);
Adrian Roos487374f2017-01-11 15:48:14 -08003660 bindNotificationHeader(contentView, p.ambient);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003661 bindLargeIcon(contentView);
Adrian Roos70d7aa32017-01-11 15:39:06 -08003662 boolean showProgress = handleProgressBar(p.hasProgress, contentView, ex);
3663 if (p.title != null) {
Selim Cinek860b6da2015-12-16 19:02:19 -08003664 contentView.setViewVisibility(R.id.title, View.VISIBLE);
Adrian Roos70d7aa32017-01-11 15:39:06 -08003665 contentView.setTextViewText(R.id.title, p.title);
Adrian Roos72171622017-01-27 10:32:06 -08003666 if (!p.ambient) {
3667 setTextViewColorPrimary(contentView, R.id.title);
3668 }
Selim Cinek954cc232016-05-20 13:29:23 -07003669 contentView.setViewLayoutWidth(R.id.title, showProgress
3670 ? ViewGroup.LayoutParams.WRAP_CONTENT
3671 : ViewGroup.LayoutParams.MATCH_PARENT);
Joe Onorato561d3852010-11-20 18:09:34 -08003672 }
Adrian Roos70d7aa32017-01-11 15:39:06 -08003673 if (p.text != null) {
Selim Cinek41598732016-01-11 16:58:37 -08003674 int textId = showProgress ? com.android.internal.R.id.text_line_1
3675 : com.android.internal.R.id.text;
Adrian Roos70d7aa32017-01-11 15:39:06 -08003676 contentView.setTextViewText(textId, p.text);
Adrian Roos72171622017-01-27 10:32:06 -08003677 if (!p.ambient) {
3678 setTextViewColorSecondary(contentView, textId);
3679 }
Selim Cinek41598732016-01-11 16:58:37 -08003680 contentView.setViewVisibility(textId, View.VISIBLE);
Joe Onorato561d3852010-11-20 18:09:34 -08003681 }
Selim Cinekc848c3a2016-01-13 15:27:30 -08003682
Selim Cinek279fa862016-06-14 10:57:25 -07003683 setContentMinHeight(contentView, showProgress || mN.hasLargeIcon());
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003684
Selim Cinek29603462015-11-17 19:04:39 -08003685 return contentView;
3686 }
3687
Selim Cinek7b9605b2017-01-19 17:36:00 -08003688 private void setTextViewColorPrimary(RemoteViews contentView, int id) {
3689 ensureColors();
3690 contentView.setTextColor(id, mPrimaryTextColor);
3691 }
3692
3693 private int getPrimaryTextColor() {
3694 ensureColors();
3695 return mPrimaryTextColor;
3696 }
3697
3698 private int getActionBarColor() {
3699 ensureColors();
3700 return mActionBarColor;
3701 }
3702
3703 private void setTextViewColorSecondary(RemoteViews contentView, int id) {
3704 ensureColors();
3705 contentView.setTextColor(id, mSecondaryTextColor);
3706 }
3707
3708 private void ensureColors() {
3709 int backgroundColor = getBackgroundColor();
3710 if (mPrimaryTextColor == COLOR_INVALID
3711 || mSecondaryTextColor == COLOR_INVALID
3712 || mActionBarColor == COLOR_INVALID
3713 || mTextColorsAreForBackground != backgroundColor) {
3714 mTextColorsAreForBackground = backgroundColor;
3715 mPrimaryTextColor = NotificationColorUtil.resolvePrimaryColor(
3716 mContext, backgroundColor);
3717 mSecondaryTextColor = NotificationColorUtil.resolveSecondaryColor(
3718 mContext, backgroundColor);
Selim Cinek875ba9b2017-02-13 16:20:17 -08003719 mActionBarColor = NotificationColorUtil.resolveActionBarColor(mContext,
3720 backgroundColor);
Selim Cinek7b9605b2017-01-19 17:36:00 -08003721 }
3722 }
3723
3724 private void updateBackgroundColor(RemoteViews contentView) {
3725 if (isColorized()) {
3726 contentView.setInt(R.id.status_bar_latest_event_content, "setBackgroundColor",
3727 getBackgroundColor());
3728 } else {
3729 // Clear it!
3730 contentView.setInt(R.id.status_bar_latest_event_content, "setBackgroundResource",
3731 0);
3732 }
3733 }
3734
Selim Cinek860b6da2015-12-16 19:02:19 -08003735 /**
3736 * @param remoteView the remote view to update the minheight in
3737 * @param hasMinHeight does it have a mimHeight
3738 * @hide
3739 */
3740 void setContentMinHeight(RemoteViews remoteView, boolean hasMinHeight) {
3741 int minHeight = 0;
3742 if (hasMinHeight) {
3743 // we need to set the minHeight of the notification
3744 minHeight = mContext.getResources().getDimensionPixelSize(
3745 com.android.internal.R.dimen.notification_min_content_height);
3746 }
3747 remoteView.setInt(R.id.notification_main_column, "setMinimumHeight", minHeight);
3748 }
3749
Selim Cinek29603462015-11-17 19:04:39 -08003750 private boolean handleProgressBar(boolean hasProgress, RemoteViews contentView, Bundle ex) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003751 final int max = ex.getInt(EXTRA_PROGRESS_MAX, 0);
3752 final int progress = ex.getInt(EXTRA_PROGRESS, 0);
3753 final boolean ind = ex.getBoolean(EXTRA_PROGRESS_INDETERMINATE);
3754 if (hasProgress && (max != 0 || ind)) {
Selim Cinek29603462015-11-17 19:04:39 -08003755 contentView.setViewVisibility(com.android.internal.R.id.progress, View.VISIBLE);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003756 contentView.setProgressBar(
Selim Cinek29603462015-11-17 19:04:39 -08003757 R.id.progress, max, progress, ind);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003758 contentView.setProgressBackgroundTintList(
3759 R.id.progress, ColorStateList.valueOf(mContext.getColor(
3760 R.color.notification_progress_background_color)));
Selim Cinek29603462015-11-17 19:04:39 -08003761 if (mN.color != COLOR_DEFAULT) {
Adrian Roos4ff3b122016-02-01 12:26:13 -08003762 ColorStateList colorStateList = ColorStateList.valueOf(resolveContrastColor());
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003763 contentView.setProgressTintList(R.id.progress, colorStateList);
3764 contentView.setProgressIndeterminateTintList(R.id.progress, colorStateList);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003765 }
Selim Cinek29603462015-11-17 19:04:39 -08003766 return true;
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003767 } else {
3768 contentView.setViewVisibility(R.id.progress, View.GONE);
Selim Cinek29603462015-11-17 19:04:39 -08003769 return false;
Jeff Sharkey1c400132011-08-05 14:50:13 -07003770 }
Joe Onorato561d3852010-11-20 18:09:34 -08003771 }
3772
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003773 private void bindLargeIcon(RemoteViews contentView) {
Selim Cinek279fa862016-06-14 10:57:25 -07003774 if (mN.mLargeIcon == null && mN.largeIcon != null) {
3775 mN.mLargeIcon = Icon.createWithBitmap(mN.largeIcon);
3776 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003777 if (mN.mLargeIcon != null) {
3778 contentView.setViewVisibility(R.id.right_icon, View.VISIBLE);
3779 contentView.setImageViewIcon(R.id.right_icon, mN.mLargeIcon);
3780 processLargeLegacyIcon(mN.mLargeIcon, contentView);
Adrian Roos2d5dbba2016-06-08 17:11:53 -07003781 int endMargin = R.dimen.notification_content_picture_margin;
3782 contentView.setViewLayoutMarginEndDimen(R.id.line1, endMargin);
3783 contentView.setViewLayoutMarginEndDimen(R.id.text, endMargin);
3784 contentView.setViewLayoutMarginEndDimen(R.id.progress, endMargin);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003785 }
3786 }
3787
Adrian Roos487374f2017-01-11 15:48:14 -08003788 private void bindNotificationHeader(RemoteViews contentView, boolean ambient) {
3789 bindSmallIcon(contentView, ambient);
3790 bindHeaderAppName(contentView, ambient);
3791 if (!ambient) {
3792 // Ambient view does not have these
3793 bindHeaderText(contentView);
3794 bindHeaderChronometerAndTime(contentView);
3795 bindExpandButton(contentView);
3796 bindProfileBadge(contentView);
3797 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003798 }
3799
3800 private void bindExpandButton(RemoteViews contentView) {
Selim Cinek99104832017-01-25 14:47:33 -08003801 int color = getPrimaryHighlightColor();
Selim Cinek7b9605b2017-01-19 17:36:00 -08003802 contentView.setDrawableParameters(R.id.expand_button, false, -1, color,
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003803 PorterDuff.Mode.SRC_ATOP, -1);
Selim Cinekea4bef72015-12-02 15:51:10 -08003804 contentView.setInt(R.id.notification_header, "setOriginalNotificationColor",
Selim Cinek7b9605b2017-01-19 17:36:00 -08003805 color);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003806 }
3807
Selim Cinek99104832017-01-25 14:47:33 -08003808 /**
3809 * @return the color that is used as the first primary highlight color. This is applied
3810 * in several places like the action buttons or the app name in the header.
3811 */
3812 private int getPrimaryHighlightColor() {
3813 return isColorized() ? getPrimaryTextColor() : resolveContrastColor();
3814 }
3815
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003816 private void bindHeaderChronometerAndTime(RemoteViews contentView) {
3817 if (showsTimeOrChronometer()) {
Selim Cinek29603462015-11-17 19:04:39 -08003818 contentView.setViewVisibility(R.id.time_divider, View.VISIBLE);
Selim Cinek7b9605b2017-01-19 17:36:00 -08003819 setTextViewColorSecondary(contentView, R.id.time_divider);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003820 if (mN.extras.getBoolean(EXTRA_SHOW_CHRONOMETER)) {
3821 contentView.setViewVisibility(R.id.chronometer, View.VISIBLE);
3822 contentView.setLong(R.id.chronometer, "setBase",
3823 mN.when + (SystemClock.elapsedRealtime() - System.currentTimeMillis()));
3824 contentView.setBoolean(R.id.chronometer, "setStarted", true);
Adrian Roos96b7e202016-05-17 13:50:38 -07003825 boolean countsDown = mN.extras.getBoolean(EXTRA_CHRONOMETER_COUNT_DOWN);
Selim Cinekc3b752e2016-04-20 16:13:59 -07003826 contentView.setChronometerCountDown(R.id.chronometer, countsDown);
Selim Cinek7b9605b2017-01-19 17:36:00 -08003827 setTextViewColorSecondary(contentView, R.id.chronometer);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003828 } else {
3829 contentView.setViewVisibility(R.id.time, View.VISIBLE);
3830 contentView.setLong(R.id.time, "setTime", mN.when);
Selim Cinek7b9605b2017-01-19 17:36:00 -08003831 setTextViewColorSecondary(contentView, R.id.time);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003832 }
Selim Cinekb85f36fd2016-04-20 18:46:36 -07003833 } else {
3834 // We still want a time to be set but gone, such that we can show and hide it
3835 // on demand in case it's a child notification without anything in the header
3836 contentView.setLong(R.id.time, "setTime", mN.when != 0 ? mN.when : mN.creationTime);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003837 }
3838 }
3839
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07003840 private void bindHeaderText(RemoteViews contentView) {
3841 CharSequence headerText = mN.extras.getCharSequence(EXTRA_SUB_TEXT);
3842 if (headerText == null && mStyle != null && mStyle.mSummaryTextSet
Selim Cinek03d0d652015-11-13 13:18:09 -05003843 && mStyle.hasSummaryInHeader()) {
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07003844 headerText = mStyle.mSummaryText;
Selim Cinek03d0d652015-11-13 13:18:09 -05003845 }
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07003846 if (headerText == null
3847 && mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N
3848 && mN.extras.getCharSequence(EXTRA_INFO_TEXT) != null) {
3849 headerText = mN.extras.getCharSequence(EXTRA_INFO_TEXT);
3850 }
3851 if (headerText != null) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003852 // TODO: Remove the span entirely to only have the string with propper formating.
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07003853 contentView.setTextViewText(R.id.header_text, processLegacyText(headerText));
Selim Cinek7b9605b2017-01-19 17:36:00 -08003854 setTextViewColorSecondary(contentView, R.id.header_text);
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07003855 contentView.setViewVisibility(R.id.header_text, View.VISIBLE);
3856 contentView.setViewVisibility(R.id.header_text_divider, View.VISIBLE);
Selim Cinek7b9605b2017-01-19 17:36:00 -08003857 setTextViewColorSecondary(contentView, R.id.header_text_divider);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003858 }
3859 }
3860
Adrian Rooseba05822016-04-22 17:09:27 -07003861 /**
3862 * @hide
3863 */
3864 public String loadHeaderAppName() {
Dan Sandler732bd6c2016-04-12 14:20:32 -04003865 CharSequence name = null;
3866 final PackageManager pm = mContext.getPackageManager();
3867 if (mN.extras.containsKey(EXTRA_SUBSTITUTE_APP_NAME)) {
3868 // only system packages which lump together a bunch of unrelated stuff
3869 // may substitute a different name to make the purpose of the
3870 // notification more clear. the correct package label should always
3871 // be accessible via SystemUI.
3872 final String pkg = mContext.getPackageName();
3873 final String subName = mN.extras.getString(EXTRA_SUBSTITUTE_APP_NAME);
3874 if (PackageManager.PERMISSION_GRANTED == pm.checkPermission(
3875 android.Manifest.permission.SUBSTITUTE_NOTIFICATION_APP_NAME, pkg)) {
3876 name = subName;
3877 } else {
3878 Log.w(TAG, "warning: pkg "
3879 + pkg + " attempting to substitute app name '" + subName
3880 + "' without holding perm "
3881 + android.Manifest.permission.SUBSTITUTE_NOTIFICATION_APP_NAME);
3882 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003883 }
Dan Sandler732bd6c2016-04-12 14:20:32 -04003884 if (TextUtils.isEmpty(name)) {
3885 name = pm.getApplicationLabel(mContext.getApplicationInfo());
3886 }
3887 if (TextUtils.isEmpty(name)) {
3888 // still nothing?
3889 return null;
3890 }
3891
3892 return String.valueOf(name);
3893 }
Adrian Roos487374f2017-01-11 15:48:14 -08003894 private void bindHeaderAppName(RemoteViews contentView, boolean ambient) {
Dan Sandler732bd6c2016-04-12 14:20:32 -04003895 contentView.setTextViewText(R.id.app_name_text, loadHeaderAppName());
Adrian Roos72171622017-01-27 10:32:06 -08003896 if (isColorized() && !ambient) {
Selim Cinek7b9605b2017-01-19 17:36:00 -08003897 setTextViewColorPrimary(contentView, R.id.app_name_text);
3898 } else {
3899 contentView.setTextColor(R.id.app_name_text,
3900 ambient ? resolveAmbientColor() : resolveContrastColor());
3901 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003902 }
3903
Adrian Roos487374f2017-01-11 15:48:14 -08003904 private void bindSmallIcon(RemoteViews contentView, boolean ambient) {
Selim Cinek279fa862016-06-14 10:57:25 -07003905 if (mN.mSmallIcon == null && mN.icon != 0) {
3906 mN.mSmallIcon = Icon.createWithResource(mContext, mN.icon);
3907 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003908 contentView.setImageViewIcon(R.id.icon, mN.mSmallIcon);
Adrian Roos9b45a152016-06-28 13:32:29 -07003909 contentView.setDrawableParameters(R.id.icon, false /* targetBackground */,
3910 -1 /* alpha */, -1 /* colorFilter */, null /* mode */, mN.iconLevel);
Adrian Roos487374f2017-01-11 15:48:14 -08003911 processSmallIconColor(mN.mSmallIcon, contentView, ambient);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003912 }
3913
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003914 /**
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02003915 * @return true if the built notification will show the time or the chronometer; false
3916 * otherwise
3917 */
3918 private boolean showsTimeOrChronometer() {
Selim Cinekc2c0b042016-05-18 17:13:46 -07003919 return mN.showsTime() || mN.showsChronometer();
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02003920 }
3921
Christoph Studerfe718432014-09-01 18:21:18 +02003922 private void resetStandardTemplateWithActions(RemoteViews big) {
Adrian Roos4c1fcc82016-03-31 14:39:39 -07003923 // actions_container is only reset when there are no actions to avoid focus issues with
3924 // remote inputs.
Christoph Studerfe718432014-09-01 18:21:18 +02003925 big.setViewVisibility(R.id.actions, View.GONE);
Christoph Studerfe718432014-09-01 18:21:18 +02003926 big.removeAllViews(R.id.actions);
Adrian Roose458aa82015-12-08 16:17:19 -08003927
3928 big.setViewVisibility(R.id.notification_material_reply_container, View.GONE);
3929 big.setTextViewText(R.id.notification_material_reply_text_1, null);
3930
3931 big.setViewVisibility(R.id.notification_material_reply_text_2, View.GONE);
3932 big.setTextViewText(R.id.notification_material_reply_text_2, null);
3933 big.setViewVisibility(R.id.notification_material_reply_text_3, View.GONE);
3934 big.setTextViewText(R.id.notification_material_reply_text_3, null);
Adrian Roosf852a422016-06-03 13:33:43 -07003935
3936 big.setViewLayoutMarginBottomDimen(R.id.notification_action_list_margin_target, 0);
Christoph Studerfe718432014-09-01 18:21:18 +02003937 }
3938
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003939 private RemoteViews applyStandardTemplateWithActions(int layoutId) {
Adrian Roos70d7aa32017-01-11 15:39:06 -08003940 return applyStandardTemplateWithActions(layoutId, mParams.reset().fillTextsFrom(this));
Adrian Roos48d746a2016-04-12 14:57:28 -07003941 }
3942
Adrian Roos70d7aa32017-01-11 15:39:06 -08003943 private RemoteViews applyStandardTemplateWithActions(int layoutId,
3944 StandardTemplateParams p) {
3945 RemoteViews big = applyStandardTemplate(layoutId, p);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003946
Christoph Studerfe718432014-09-01 18:21:18 +02003947 resetStandardTemplateWithActions(big);
3948
Adrian Roose458aa82015-12-08 16:17:19 -08003949 boolean validRemoteInput = false;
3950
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003951 int N = mActions.size();
Adrian Roos487374f2017-01-11 15:48:14 -08003952 boolean emphazisedMode = mN.fullScreenIntent != null && !p.ambient;
Selim Cinek06e9e1f2016-07-08 17:14:16 -07003953 big.setBoolean(R.id.actions, "setEmphasizedMode", emphazisedMode);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003954 if (N > 0) {
Adrian Roos7052de52016-03-03 15:53:34 -08003955 big.setViewVisibility(R.id.actions_container, View.VISIBLE);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003956 big.setViewVisibility(R.id.actions, View.VISIBLE);
Adrian Roos487374f2017-01-11 15:48:14 -08003957 if (p.ambient) {
3958 big.setInt(R.id.actions, "setBackgroundColor", Color.TRANSPARENT);
Selim Cinek7b9605b2017-01-19 17:36:00 -08003959 } else if (isColorized()) {
3960 big.setInt(R.id.actions, "setBackgroundColor", getActionBarColor());
3961 } else {
3962 big.setInt(R.id.actions, "setBackgroundColor", mContext.getColor(
3963 R.color.notification_action_list));
Adrian Roos487374f2017-01-11 15:48:14 -08003964 }
Adrian Roosf852a422016-06-03 13:33:43 -07003965 big.setViewLayoutMarginBottomDimen(R.id.notification_action_list_margin_target,
3966 R.dimen.notification_action_list_height);
Daniel Sandler8680bf82012-05-15 16:52:52 -04003967 if (N>MAX_ACTION_BUTTONS) N=MAX_ACTION_BUTTONS;
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003968 for (int i=0; i<N; i++) {
Adrian Roose458aa82015-12-08 16:17:19 -08003969 Action action = mActions.get(i);
3970 validRemoteInput |= hasValidRemoteInput(action);
3971
Selim Cinek06e9e1f2016-07-08 17:14:16 -07003972 final RemoteViews button = generateActionButton(action, emphazisedMode,
Adrian Roos487374f2017-01-11 15:48:14 -08003973 i % 2 != 0, p.ambient);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003974 big.addView(R.id.actions, button);
3975 }
Adrian Roos4c1fcc82016-03-31 14:39:39 -07003976 } else {
3977 big.setViewVisibility(R.id.actions_container, View.GONE);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003978 }
Adrian Roose458aa82015-12-08 16:17:19 -08003979
3980 CharSequence[] replyText = mN.extras.getCharSequenceArray(EXTRA_REMOTE_INPUT_HISTORY);
Adrian Roos487374f2017-01-11 15:48:14 -08003981 if (!p.ambient && validRemoteInput && replyText != null
Adrian Roose458aa82015-12-08 16:17:19 -08003982 && replyText.length > 0 && !TextUtils.isEmpty(replyText[0])) {
3983 big.setViewVisibility(R.id.notification_material_reply_container, View.VISIBLE);
3984 big.setTextViewText(R.id.notification_material_reply_text_1, replyText[0]);
Selim Cinek7b9605b2017-01-19 17:36:00 -08003985 setTextViewColorSecondary(big, R.id.notification_material_reply_text_1);
Adrian Roose458aa82015-12-08 16:17:19 -08003986
3987 if (replyText.length > 1 && !TextUtils.isEmpty(replyText[1])) {
3988 big.setViewVisibility(R.id.notification_material_reply_text_2, View.VISIBLE);
3989 big.setTextViewText(R.id.notification_material_reply_text_2, replyText[1]);
Selim Cinek7b9605b2017-01-19 17:36:00 -08003990 setTextViewColorSecondary(big, R.id.notification_material_reply_text_2);
Adrian Roose458aa82015-12-08 16:17:19 -08003991
3992 if (replyText.length > 2 && !TextUtils.isEmpty(replyText[2])) {
3993 big.setViewVisibility(
3994 R.id.notification_material_reply_text_3, View.VISIBLE);
3995 big.setTextViewText(R.id.notification_material_reply_text_3, replyText[2]);
Selim Cinek7b9605b2017-01-19 17:36:00 -08003996 setTextViewColorSecondary(big, R.id.notification_material_reply_text_3);
Adrian Roose458aa82015-12-08 16:17:19 -08003997 }
3998 }
3999 }
4000
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004001 return big;
4002 }
4003
Adrian Roose458aa82015-12-08 16:17:19 -08004004 private boolean hasValidRemoteInput(Action action) {
4005 if (TextUtils.isEmpty(action.title) || action.actionIntent == null) {
4006 // Weird actions
4007 return false;
4008 }
4009
4010 RemoteInput[] remoteInputs = action.getRemoteInputs();
4011 if (remoteInputs == null) {
4012 return false;
4013 }
4014
4015 for (RemoteInput r : remoteInputs) {
4016 CharSequence[] choices = r.getChoices();
4017 if (r.getAllowFreeFormInput() || (choices != null && choices.length != 0)) {
4018 return true;
4019 }
4020 }
4021 return false;
4022 }
4023
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004024 /**
4025 * Construct a RemoteViews for the final 1U notification layout. In order:
4026 * 1. Custom contentView from the caller
4027 * 2. Style's proposed content view
4028 * 3. Standard template view
4029 */
Julia Reynolds3b848122016-02-26 10:45:32 -05004030 public RemoteViews createContentView() {
Selim Cinek7d1009b2017-01-25 15:28:28 -08004031 return createContentView(false /* increasedheight */ );
4032 }
4033
4034 /**
4035 * Construct a RemoteViews for the smaller content view.
4036 *
4037 * @param increasedHeight true if this layout be created with an increased height. Some
4038 * styles may support showing more then just that basic 1U size
4039 * and the system may decide to render important notifications
4040 * slightly bigger even when collapsed.
4041 *
4042 * @hide
4043 */
4044 public RemoteViews createContentView(boolean increasedHeight) {
Selim Cinek593610c2016-02-16 18:42:57 -08004045 if (mN.contentView != null && (mStyle == null || !mStyle.displayCustomViewInline())) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004046 return mN.contentView;
4047 } else if (mStyle != null) {
Selim Cinek7d1009b2017-01-25 15:28:28 -08004048 final RemoteViews styleView = mStyle.makeContentView(increasedHeight);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004049 if (styleView != null) {
4050 return styleView;
4051 }
Joe Onorato46439ce2010-11-19 13:56:21 -08004052 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004053 return applyStandardTemplate(getBaseLayoutResource());
Joe Onorato46439ce2010-11-19 13:56:21 -08004054 }
4055
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004056 /**
4057 * Construct a RemoteViews for the final big notification layout.
4058 */
Julia Reynolds3b848122016-02-26 10:45:32 -05004059 public RemoteViews createBigContentView() {
Selim Cinek850a8542015-11-11 11:48:36 -05004060 RemoteViews result = null;
Selim Cinek593610c2016-02-16 18:42:57 -08004061 if (mN.bigContentView != null
4062 && (mStyle == null || !mStyle.displayCustomViewInline())) {
Julia Reynolds089e3e42015-11-18 09:59:57 -05004063 return mN.bigContentView;
4064 } else if (mStyle != null) {
Selim Cinek850a8542015-11-11 11:48:36 -05004065 result = mStyle.makeBigContentView();
Selim Cinek90dcf6d2015-11-18 20:24:13 -08004066 hideLine1Text(result);
Selim Cinekcc10bfb2016-02-10 16:24:21 -08004067 } else if (mActions.size() != 0) {
4068 result = applyStandardTemplateWithActions(getBigBaseLayoutResource());
Selim Cinek850a8542015-11-11 11:48:36 -05004069 }
Selim Cinek6743c0b2017-01-18 18:24:01 -08004070 makeHeaderExpanded(result);
Selim Cinek850a8542015-11-11 11:48:36 -05004071 return result;
4072 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004073
Selim Cinekeaa29ca2015-11-23 13:51:13 -08004074 /**
Selim Cinek414ad332017-02-24 19:06:12 -08004075 * Construct a RemoteViews for the final notification header only. This will not be
4076 * colorized.
Selim Cinekeaa29ca2015-11-23 13:51:13 -08004077 *
4078 * @hide
4079 */
4080 public RemoteViews makeNotificationHeader() {
Selim Cinek414ad332017-02-24 19:06:12 -08004081 Boolean colorized = (Boolean) mN.extras.get(EXTRA_COLORIZED);
4082 mN.extras.putBoolean(EXTRA_COLORIZED, false);
Selim Cinekeaa29ca2015-11-23 13:51:13 -08004083 RemoteViews header = new BuilderRemoteViews(mContext.getApplicationInfo(),
4084 R.layout.notification_template_header);
4085 resetNotificationHeader(header);
Adrian Roos487374f2017-01-11 15:48:14 -08004086 bindNotificationHeader(header, false /* ambient */);
Selim Cinek414ad332017-02-24 19:06:12 -08004087 if (colorized != null) {
4088 mN.extras.putBoolean(EXTRA_COLORIZED, colorized);
4089 } else {
4090 mN.extras.remove(EXTRA_COLORIZED);
4091 }
Selim Cinekeaa29ca2015-11-23 13:51:13 -08004092 return header;
4093 }
4094
Adrian Roos487374f2017-01-11 15:48:14 -08004095 /**
4096 * Construct a RemoteViews for the ambient version of the notification.
4097 *
4098 * @hide
4099 */
4100 public RemoteViews makeAmbientNotification() {
4101 RemoteViews ambient = applyStandardTemplateWithActions(
4102 R.layout.notification_template_material_ambient,
Adrian Roos0bc3f6a2017-03-06 11:54:05 -08004103 mParams.reset().ambient(true).fillTextsFrom(this).hasProgress(false));
Adrian Roos487374f2017-01-11 15:48:14 -08004104 return ambient;
4105 }
4106
Selim Cinek29603462015-11-17 19:04:39 -08004107 private void hideLine1Text(RemoteViews result) {
Selim Cinekcc10bfb2016-02-10 16:24:21 -08004108 if (result != null) {
4109 result.setViewVisibility(R.id.text_line_1, View.GONE);
4110 }
Selim Cinek29603462015-11-17 19:04:39 -08004111 }
4112
Selim Cinek6743c0b2017-01-18 18:24:01 -08004113 /**
4114 * Adapt the Notification header if this view is used as an expanded view.
4115 *
4116 * @hide
4117 */
4118 public static void makeHeaderExpanded(RemoteViews result) {
Selim Cinekcc10bfb2016-02-10 16:24:21 -08004119 if (result != null) {
4120 result.setBoolean(R.id.notification_header, "setExpanded", true);
4121 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004122 }
4123
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004124 /**
4125 * Construct a RemoteViews for the final heads-up notification layout.
Selim Cinek87ed69b2017-02-09 15:59:43 -08004126 *
4127 * @param increasedHeight true if this layout be created with an increased height. Some
4128 * styles may support showing more then just that basic 1U size
4129 * and the system may decide to render important notifications
4130 * slightly bigger even when collapsed.
4131 *
4132 * @hide
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004133 */
Selim Cinek87ed69b2017-02-09 15:59:43 -08004134 public RemoteViews createHeadsUpContentView(boolean increasedHeight) {
Selim Cinek593610c2016-02-16 18:42:57 -08004135 if (mN.headsUpContentView != null
4136 && (mStyle == null || !mStyle.displayCustomViewInline())) {
Julia Reynolds089e3e42015-11-18 09:59:57 -05004137 return mN.headsUpContentView;
4138 } else if (mStyle != null) {
Selim Cinek87ed69b2017-02-09 15:59:43 -08004139 final RemoteViews styleView = mStyle.makeHeadsUpContentView(increasedHeight);
4140 if (styleView != null) {
4141 return styleView;
4142 }
Julia Reynolds089e3e42015-11-18 09:59:57 -05004143 } else if (mActions.size() == 0) {
4144 return null;
4145 }
4146
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004147 return applyStandardTemplateWithActions(getBigBaseLayoutResource());
Chris Wren8fd39ec2014-02-27 17:43:26 -05004148 }
4149
Selim Cinek624c02db2015-12-14 21:00:02 -08004150 /**
Selim Cinek87ed69b2017-02-09 15:59:43 -08004151 * Construct a RemoteViews for the final heads-up notification layout.
4152 */
4153 public RemoteViews createHeadsUpContentView() {
4154 return createHeadsUpContentView(false /* useIncreasedHeight */);
4155 }
4156
4157 /**
Selim Cinek624c02db2015-12-14 21:00:02 -08004158 * Construct a RemoteViews for the display in public contexts like on the lockscreen.
4159 *
4160 * @hide
4161 */
4162 public RemoteViews makePublicContentView() {
4163 if (mN.publicVersion != null) {
4164 final Builder builder = recoverBuilder(mContext, mN.publicVersion);
Julia Reynolds3b848122016-02-26 10:45:32 -05004165 return builder.createContentView();
Selim Cinek624c02db2015-12-14 21:00:02 -08004166 }
4167 Bundle savedBundle = mN.extras;
4168 Style style = mStyle;
4169 mStyle = null;
4170 Icon largeIcon = mN.mLargeIcon;
4171 mN.mLargeIcon = null;
Selim Cinek279fa862016-06-14 10:57:25 -07004172 Bitmap largeIconLegacy = mN.largeIcon;
4173 mN.largeIcon = null;
Selim Cinek624c02db2015-12-14 21:00:02 -08004174 Bundle publicExtras = new Bundle();
4175 publicExtras.putBoolean(EXTRA_SHOW_WHEN,
4176 savedBundle.getBoolean(EXTRA_SHOW_WHEN));
4177 publicExtras.putBoolean(EXTRA_SHOW_CHRONOMETER,
4178 savedBundle.getBoolean(EXTRA_SHOW_CHRONOMETER));
Adrian Roos96b7e202016-05-17 13:50:38 -07004179 publicExtras.putBoolean(EXTRA_CHRONOMETER_COUNT_DOWN,
4180 savedBundle.getBoolean(EXTRA_CHRONOMETER_COUNT_DOWN));
Selim Cinek624c02db2015-12-14 21:00:02 -08004181 publicExtras.putCharSequence(EXTRA_TITLE,
4182 mContext.getString(R.string.notification_hidden_text));
4183 mN.extras = publicExtras;
4184 final RemoteViews publicView = applyStandardTemplate(getBaseLayoutResource());
4185 mN.extras = savedBundle;
4186 mN.mLargeIcon = largeIcon;
Selim Cinek279fa862016-06-14 10:57:25 -07004187 mN.largeIcon = largeIconLegacy;
Selim Cinek624c02db2015-12-14 21:00:02 -08004188 mStyle = style;
4189 return publicView;
4190 }
4191
Selim Cinek6743c0b2017-01-18 18:24:01 -08004192 /**
4193 * Construct a content view for the display when low - priority
4194 *
4195 * @param useRegularSubtext uses the normal subtext set if there is one available. Otherwise
4196 * a new subtext is created consisting of the content of the
4197 * notification.
4198 * @hide
4199 */
4200 public RemoteViews makeLowPriorityContentView(boolean useRegularSubtext) {
4201 int color = mN.color;
4202 mN.color = COLOR_DEFAULT;
4203 CharSequence summary = mN.extras.getCharSequence(EXTRA_SUB_TEXT);
4204 if (!useRegularSubtext || TextUtils.isEmpty(summary)) {
4205 CharSequence newSummary = createSummaryText();
4206 if (!TextUtils.isEmpty(newSummary)) {
4207 mN.extras.putCharSequence(EXTRA_SUB_TEXT, newSummary);
4208 }
4209 }
Selim Cinek875ba9b2017-02-13 16:20:17 -08004210
Selim Cinek6743c0b2017-01-18 18:24:01 -08004211 RemoteViews header = makeNotificationHeader();
Selim Cinek1b554392017-02-28 17:22:49 -08004212 header.setBoolean(R.id.notification_header, "setAcceptAllTouches", true);
Selim Cinek6743c0b2017-01-18 18:24:01 -08004213 if (summary != null) {
4214 mN.extras.putCharSequence(EXTRA_SUB_TEXT, summary);
4215 } else {
4216 mN.extras.remove(EXTRA_SUB_TEXT);
4217 }
4218 mN.color = color;
4219 return header;
4220 }
Selim Cinek624c02db2015-12-14 21:00:02 -08004221
Selim Cinek6743c0b2017-01-18 18:24:01 -08004222 private CharSequence createSummaryText() {
4223 CharSequence titleText = mN.extras.getCharSequence(Notification.EXTRA_TITLE);
4224 if (USE_ONLY_TITLE_IN_LOW_PRIORITY_SUMMARY) {
4225 return titleText;
4226 }
4227 SpannableStringBuilder summary = new SpannableStringBuilder();
4228 if (titleText == null) {
4229 titleText = mN.extras.getCharSequence(Notification.EXTRA_TITLE_BIG);
4230 }
4231 BidiFormatter bidi = BidiFormatter.getInstance();
4232 if (titleText != null) {
4233 summary.append(bidi.unicodeWrap(titleText));
4234 }
4235 CharSequence contentText = mN.extras.getCharSequence(Notification.EXTRA_TEXT);
4236 if (titleText != null && contentText != null) {
4237 summary.append(bidi.unicodeWrap(mContext.getText(
4238 R.string.notification_header_divider_symbol_with_spaces)));
4239 }
4240 if (contentText != null) {
4241 summary.append(bidi.unicodeWrap(contentText));
4242 }
4243 return summary;
4244 }
Chris Wren8fd39ec2014-02-27 17:43:26 -05004245
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004246 private RemoteViews generateActionButton(Action action, boolean emphazisedMode,
Adrian Roos487374f2017-01-11 15:48:14 -08004247 boolean oddAction, boolean ambient) {
Daniel Sandler8680bf82012-05-15 16:52:52 -04004248 final boolean tombstone = (action.actionIntent == null);
Selim Cinekf33b1112015-07-15 17:45:11 -07004249 RemoteViews button = new BuilderRemoteViews(mContext.getApplicationInfo(),
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004250 emphazisedMode ? getEmphasizedActionLayoutResource()
4251 : tombstone ? getActionTombstoneLayoutResource()
4252 : getActionLayoutResource());
Daniel Sandler8680bf82012-05-15 16:52:52 -04004253 if (!tombstone) {
Daniel Sandlere5518842012-05-10 16:20:40 -04004254 button.setOnClickPendingIntent(R.id.action0, action.actionIntent);
Daniel Sandlere5518842012-05-10 16:20:40 -04004255 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004256 button.setContentDescription(R.id.action0, action.title);
Adrian Roosfe84e1f2015-11-04 15:55:39 -08004257 if (action.mRemoteInputs != null) {
4258 button.setRemoteInputs(R.id.action0, action.mRemoteInputs);
4259 }
Selim Cinek7b9605b2017-01-19 17:36:00 -08004260 // TODO: handle emphasized mode / actions right
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004261 if (emphazisedMode) {
Selim Cinek981962e2016-07-20 20:41:58 -07004262 // change the background bgColor
4263 int bgColor = mContext.getColor(oddAction ? R.color.notification_action_list
4264 : R.color.notification_action_list_dark);
4265 button.setDrawableParameters(R.id.button_holder, true, -1, bgColor,
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004266 PorterDuff.Mode.SRC_ATOP, -1);
Selim Cinek981962e2016-07-20 20:41:58 -07004267 CharSequence title = action.title;
4268 ColorStateList[] outResultColor = null;
4269 if (isLegacy()) {
4270 title = clearColorSpans(title);
4271 } else {
4272 outResultColor = new ColorStateList[1];
4273 title = ensureColorSpanContrast(title, bgColor, outResultColor);
4274 }
4275 button.setTextViewText(R.id.action0, title);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004276 setTextViewColorPrimary(button, R.id.action0);
Selim Cinek981962e2016-07-20 20:41:58 -07004277 if (outResultColor != null && outResultColor[0] != null) {
4278 // We need to set the text color as well since changing a text to uppercase
4279 // clears its spans.
4280 button.setTextColor(R.id.action0, outResultColor[0]);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004281 } else if (mN.color != COLOR_DEFAULT && !isColorized()) {
Selim Cinek981962e2016-07-20 20:41:58 -07004282 button.setTextColor(R.id.action0,resolveContrastColor());
4283 }
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004284 } else {
Selim Cinek981962e2016-07-20 20:41:58 -07004285 button.setTextViewText(R.id.action0, processLegacyText(action.title));
Adrian Roos72171622017-01-27 10:32:06 -08004286 if (isColorized() && !ambient) {
Selim Cinek7b9605b2017-01-19 17:36:00 -08004287 setTextViewColorPrimary(button, R.id.action0);
4288 } else if (mN.color != COLOR_DEFAULT) {
Adrian Roos487374f2017-01-11 15:48:14 -08004289 button.setTextColor(R.id.action0,
4290 ambient ? resolveAmbientColor() : resolveContrastColor());
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004291 }
Adrian Roosfe84e1f2015-11-04 15:55:39 -08004292 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004293 return button;
4294 }
4295
Joe Onoratocb109a02011-01-18 17:57:41 -08004296 /**
Selim Cinek981962e2016-07-20 20:41:58 -07004297 * Clears all color spans of a text
4298 * @param charSequence the input text
4299 * @return the same text but without color spans
4300 */
4301 private CharSequence clearColorSpans(CharSequence charSequence) {
4302 if (charSequence instanceof Spanned) {
4303 Spanned ss = (Spanned) charSequence;
4304 Object[] spans = ss.getSpans(0, ss.length(), Object.class);
4305 SpannableStringBuilder builder = new SpannableStringBuilder(ss.toString());
4306 for (Object span : spans) {
4307 Object resultSpan = span;
4308 if (resultSpan instanceof CharacterStyle) {
4309 resultSpan = ((CharacterStyle) span).getUnderlying();
4310 }
4311 if (resultSpan instanceof TextAppearanceSpan) {
4312 TextAppearanceSpan originalSpan = (TextAppearanceSpan) resultSpan;
4313 if (originalSpan.getTextColor() != null) {
4314 resultSpan = new TextAppearanceSpan(
4315 originalSpan.getFamily(),
4316 originalSpan.getTextStyle(),
4317 originalSpan.getTextSize(),
4318 null,
4319 originalSpan.getLinkTextColor());
4320 }
4321 } else if (resultSpan instanceof ForegroundColorSpan
4322 || (resultSpan instanceof BackgroundColorSpan)) {
4323 continue;
4324 } else {
4325 resultSpan = span;
4326 }
4327 builder.setSpan(resultSpan, ss.getSpanStart(span), ss.getSpanEnd(span),
4328 ss.getSpanFlags(span));
4329 }
4330 return builder;
4331 }
4332 return charSequence;
4333 }
4334
4335 /**
4336 * Ensures contrast on color spans against a background color. also returns the color of the
4337 * text if a span was found that spans over the whole text.
4338 *
4339 * @param charSequence the charSequence on which the spans are
4340 * @param background the background color to ensure the contrast against
4341 * @param outResultColor an array in which a color will be returned as the first element if
4342 * there exists a full length color span.
4343 * @return the contrasted charSequence
4344 */
4345 private CharSequence ensureColorSpanContrast(CharSequence charSequence, int background,
4346 ColorStateList[] outResultColor) {
4347 if (charSequence instanceof Spanned) {
4348 Spanned ss = (Spanned) charSequence;
4349 Object[] spans = ss.getSpans(0, ss.length(), Object.class);
4350 SpannableStringBuilder builder = new SpannableStringBuilder(ss.toString());
4351 for (Object span : spans) {
4352 Object resultSpan = span;
4353 int spanStart = ss.getSpanStart(span);
4354 int spanEnd = ss.getSpanEnd(span);
4355 boolean fullLength = (spanEnd - spanStart) == charSequence.length();
4356 if (resultSpan instanceof CharacterStyle) {
4357 resultSpan = ((CharacterStyle) span).getUnderlying();
4358 }
4359 if (resultSpan instanceof TextAppearanceSpan) {
4360 TextAppearanceSpan originalSpan = (TextAppearanceSpan) resultSpan;
4361 ColorStateList textColor = originalSpan.getTextColor();
4362 if (textColor != null) {
4363 int[] colors = textColor.getColors();
4364 int[] newColors = new int[colors.length];
4365 for (int i = 0; i < newColors.length; i++) {
4366 newColors[i] = NotificationColorUtil.ensureLargeTextContrast(
4367 colors[i], background);
4368 }
4369 textColor = new ColorStateList(textColor.getStates().clone(),
4370 newColors);
4371 resultSpan = new TextAppearanceSpan(
4372 originalSpan.getFamily(),
4373 originalSpan.getTextStyle(),
4374 originalSpan.getTextSize(),
4375 textColor,
4376 originalSpan.getLinkTextColor());
4377 if (fullLength) {
4378 outResultColor[0] = new ColorStateList(
4379 textColor.getStates().clone(), newColors);
4380 }
4381 }
4382 } else if (resultSpan instanceof ForegroundColorSpan) {
4383 ForegroundColorSpan originalSpan = (ForegroundColorSpan) resultSpan;
4384 int foregroundColor = originalSpan.getForegroundColor();
4385 foregroundColor = NotificationColorUtil.ensureLargeTextContrast(
4386 foregroundColor, background);
4387 resultSpan = new ForegroundColorSpan(foregroundColor);
4388 if (fullLength) {
4389 outResultColor[0] = ColorStateList.valueOf(foregroundColor);
4390 }
4391 } else {
4392 resultSpan = span;
4393 }
4394
4395 builder.setSpan(resultSpan, spanStart, spanEnd, ss.getSpanFlags(span));
4396 }
4397 return builder;
4398 }
4399 return charSequence;
4400 }
4401
4402 /**
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004403 * @return Whether we are currently building a notification from a legacy (an app that
Alan Viverette3cb07a462014-06-06 14:19:53 -07004404 * doesn't create material notifications by itself) app.
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004405 */
4406 private boolean isLegacy() {
Selim Cinek7b9605b2017-01-19 17:36:00 -08004407 if (!mIsLegacyInitialized) {
4408 mIsLegacy = mContext.getApplicationInfo().targetSdkVersion
4409 < Build.VERSION_CODES.LOLLIPOP;
4410 mIsLegacyInitialized = true;
4411 }
4412 return mIsLegacy;
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004413 }
4414
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004415 private CharSequence processLegacyText(CharSequence charSequence) {
Adrian Roos0bc3f6a2017-03-06 11:54:05 -08004416 return processLegacyText(charSequence, false /* ambient */);
4417 }
4418
4419 private CharSequence processLegacyText(CharSequence charSequence, boolean ambient) {
4420 boolean isAlreadyLightText = isLegacy() || textColorsNeedInversion();
4421 boolean wantLightText = ambient;
4422 if (isAlreadyLightText != wantLightText) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004423 return getColorUtil().invertCharSequenceColors(charSequence);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004424 } else {
4425 return charSequence;
4426 }
4427 }
4428
Dan Sandler26e81cf2014-05-06 10:01:27 -04004429 /**
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004430 * Apply any necessariy colors to the small icon
Dan Sandler26e81cf2014-05-06 10:01:27 -04004431 */
Adrian Roos487374f2017-01-11 15:48:14 -08004432 private void processSmallIconColor(Icon smallIcon, RemoteViews contentView,
4433 boolean ambient) {
Selim Cinekea4bef72015-12-02 15:51:10 -08004434 boolean colorable = !isLegacy() || getColorUtil().isGrayscaleIcon(mContext, smallIcon);
Selim Cinek99104832017-01-25 14:47:33 -08004435 int color = ambient ? resolveAmbientColor() : getPrimaryHighlightColor();
Selim Cinekea4bef72015-12-02 15:51:10 -08004436 if (colorable) {
Adrian Roos487374f2017-01-11 15:48:14 -08004437 contentView.setDrawableParameters(R.id.icon, false, -1, color,
Jorim Jaggi92df1f22014-12-16 19:44:41 +01004438 PorterDuff.Mode.SRC_ATOP, -1);
Selim Cinekea4bef72015-12-02 15:51:10 -08004439
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004440 }
Selim Cinekea4bef72015-12-02 15:51:10 -08004441 contentView.setInt(R.id.notification_header, "setOriginalIconColor",
Adrian Roos487374f2017-01-11 15:48:14 -08004442 colorable ? color : NotificationHeaderView.NO_COLOR);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004443 }
4444
Dan Sandler26e81cf2014-05-06 10:01:27 -04004445 /**
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004446 * Make the largeIcon dark if it's a fake smallIcon (that is,
Dan Sandler26e81cf2014-05-06 10:01:27 -04004447 * if it's grayscale).
4448 */
4449 // TODO: also check bounds, transparency, that sort of thing.
Dan Sandlerd63f9322015-05-06 15:18:49 -04004450 private void processLargeLegacyIcon(Icon largeIcon, RemoteViews contentView) {
4451 if (largeIcon != null && isLegacy()
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004452 && getColorUtil().isGrayscaleIcon(mContext, largeIcon)) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004453 // resolve color will fall back to the default when legacy
Adrian Roos4ff3b122016-02-01 12:26:13 -08004454 contentView.setDrawableParameters(R.id.icon, false, -1, resolveContrastColor(),
Dan Sandler190d58d2014-05-15 09:33:39 -04004455 PorterDuff.Mode.SRC_ATOP, -1);
Jorim Jaggi92df1f22014-12-16 19:44:41 +01004456 }
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004457 }
4458
Julia Reynolds10ee1fc2015-11-09 11:04:55 -05004459 private void sanitizeColor() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004460 if (mN.color != COLOR_DEFAULT) {
4461 mN.color |= 0xFF000000; // no alpha for custom colors
Jorim Jaggi74419312014-06-10 20:57:21 +02004462 }
Jorim Jaggi74419312014-06-10 20:57:21 +02004463 }
4464
Adrian Roos4ff3b122016-02-01 12:26:13 -08004465 int resolveContrastColor() {
4466 if (mCachedContrastColorIsFor == mN.color && mCachedContrastColor != COLOR_INVALID) {
4467 return mCachedContrastColor;
Dan Sandler26e81cf2014-05-06 10:01:27 -04004468 }
Adrian Roos4ff3b122016-02-01 12:26:13 -08004469 final int contrasted = NotificationColorUtil.resolveContrastColor(mContext, mN.color);
4470
4471 mCachedContrastColorIsFor = mN.color;
4472 return mCachedContrastColor = contrasted;
Dan Sandler26e81cf2014-05-06 10:01:27 -04004473 }
4474
Adrian Roos487374f2017-01-11 15:48:14 -08004475 int resolveAmbientColor() {
4476 if (mCachedAmbientColorIsFor == mN.color && mCachedAmbientColorIsFor != COLOR_INVALID) {
4477 return mCachedAmbientColor;
4478 }
4479 final int contrasted = NotificationColorUtil.resolveAmbientColor(mContext, mN.color);
4480
4481 mCachedAmbientColorIsFor = mN.color;
4482 return mCachedAmbientColor = contrasted;
4483 }
4484
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004485 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004486 * Apply the unstyled operations and return a new {@link Notification} object.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04004487 * @hide
Joe Onoratocb109a02011-01-18 17:57:41 -08004488 */
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04004489 public Notification buildUnstyled() {
Daniel Sandlera0a938c2012-03-15 08:42:37 -04004490 if (mActions.size() > 0) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004491 mN.actions = new Action[mActions.size()];
4492 mActions.toArray(mN.actions);
Daniel Sandlera0a938c2012-03-15 08:42:37 -04004493 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004494 if (!mPersonList.isEmpty()) {
4495 mN.extras.putStringArray(EXTRA_PEOPLE,
4496 mPersonList.toArray(new String[mPersonList.size()]));
Dan Sandler0bf2ed82013-12-21 23:33:41 -06004497 }
Selim Cinek247fa012016-02-18 09:50:48 -08004498 if (mN.bigContentView != null || mN.contentView != null
4499 || mN.headsUpContentView != null) {
4500 mN.extras.putBoolean(EXTRA_CONTAINS_CUSTOM_VIEW, true);
4501 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004502 return mN;
Joe Onorato46439ce2010-11-19 13:56:21 -08004503 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004504
Julia Reynolds3b848122016-02-26 10:45:32 -05004505 /**
4506 * Creates a Builder from an existing notification so further changes can be made.
4507 * @param context The context for your application / activity.
4508 * @param n The notification to create a Builder from.
4509 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004510 public static Notification.Builder recoverBuilder(Context context, Notification n) {
Christoph Studer4600f9b2014-07-22 22:44:43 +02004511 // Re-create notification context so we can access app resources.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004512 ApplicationInfo applicationInfo = n.extras.getParcelable(
4513 EXTRA_BUILDER_APPLICATION_INFO);
Christoph Studer4600f9b2014-07-22 22:44:43 +02004514 Context builderContext;
Julia Reynoldsda303542015-11-23 14:00:20 -05004515 if (applicationInfo != null) {
4516 try {
4517 builderContext = context.createApplicationContext(applicationInfo,
4518 Context.CONTEXT_RESTRICTED);
4519 } catch (NameNotFoundException e) {
4520 Log.e(TAG, "ApplicationInfo " + applicationInfo + " not found");
4521 builderContext = context; // try with our context
4522 }
4523 } else {
4524 builderContext = context; // try with given context
Christoph Studer4600f9b2014-07-22 22:44:43 +02004525 }
4526
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004527 return new Builder(builderContext, n);
Christoph Studer4600f9b2014-07-22 22:44:43 +02004528 }
4529
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004530 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004531 * @deprecated Use {@link #build()} instead.
4532 */
4533 @Deprecated
4534 public Notification getNotification() {
4535 return build();
4536 }
4537
4538 /**
4539 * Combine all of the options that have been set and return a new {@link Notification}
4540 * object.
4541 */
4542 public Notification build() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004543 // first, add any extras from the calling code
4544 if (mUserExtras != null) {
4545 mN.extras = getAllExtras();
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07004546 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004547
Selim Cinekb85f36fd2016-04-20 18:46:36 -07004548 mN.creationTime = System.currentTimeMillis();
4549
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004550 // lazy stuff from mContext; see comment in Builder(Context, Notification)
Julia Reynoldsda303542015-11-23 14:00:20 -05004551 Notification.addFieldsFromContext(mContext, mN);
Christoph Studer943aa672014-08-03 20:31:16 +02004552
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004553 buildUnstyled();
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004554
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004555 if (mStyle != null) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004556 mStyle.buildStyled(mN);
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004557 }
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004558
Adrian Roos5081c0d2016-02-26 16:04:19 -08004559 if (mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N
4560 && (mStyle == null || !mStyle.displayCustomViewInline())) {
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004561 if (mN.contentView == null) {
Julia Reynolds3b848122016-02-26 10:45:32 -05004562 mN.contentView = createContentView();
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004563 mN.extras.putInt(EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT,
4564 mN.contentView.getSequenceNumber());
4565 }
4566 if (mN.bigContentView == null) {
Julia Reynolds3b848122016-02-26 10:45:32 -05004567 mN.bigContentView = createBigContentView();
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004568 if (mN.bigContentView != null) {
4569 mN.extras.putInt(EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT,
4570 mN.bigContentView.getSequenceNumber());
4571 }
4572 }
4573 if (mN.headsUpContentView == null) {
Julia Reynolds3b848122016-02-26 10:45:32 -05004574 mN.headsUpContentView = createHeadsUpContentView();
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004575 if (mN.headsUpContentView != null) {
4576 mN.extras.putInt(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT,
4577 mN.headsUpContentView.getSequenceNumber());
4578 }
4579 }
4580 }
4581
Julia Reynolds4c0c2022016-02-02 15:11:59 -05004582 if ((mN.defaults & DEFAULT_LIGHTS) != 0) {
4583 mN.flags |= FLAG_SHOW_LIGHTS;
4584 }
4585
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004586 return mN;
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004587 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05004588
4589 /**
4590 * Apply this Builder to an existing {@link Notification} object.
4591 *
4592 * @hide
4593 */
4594 public Notification buildInto(Notification n) {
Daniel Sandler1a497d32013-04-18 14:52:45 -04004595 build().cloneInto(n, true);
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05004596 return n;
4597 }
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004598
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004599 /**
Adrian Roos184bfe022016-03-03 13:41:44 -08004600 * Removes RemoteViews that were created for compatibility from {@param n}, if they did not
4601 * change.
4602 *
4603 * @return {@param n}, if no stripping is needed, otherwise a stripped clone of {@param n}.
4604 *
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004605 * @hide
4606 */
Adrian Roos184bfe022016-03-03 13:41:44 -08004607 public static Notification maybeCloneStrippedForDelivery(Notification n) {
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004608 String templateClass = n.extras.getString(EXTRA_TEMPLATE);
Adrian Roos184bfe022016-03-03 13:41:44 -08004609
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004610 // Only strip views for known Styles because we won't know how to
4611 // re-create them otherwise.
Adrian Roos184bfe022016-03-03 13:41:44 -08004612 if (!TextUtils.isEmpty(templateClass)
4613 && getNotificationStyleClass(templateClass) == null) {
4614 return n;
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004615 }
Adrian Roos184bfe022016-03-03 13:41:44 -08004616
4617 // Only strip unmodified BuilderRemoteViews.
4618 boolean stripContentView = n.contentView instanceof BuilderRemoteViews &&
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004619 n.extras.getInt(EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT, -1) ==
Adrian Roos184bfe022016-03-03 13:41:44 -08004620 n.contentView.getSequenceNumber();
4621 boolean stripBigContentView = n.bigContentView instanceof BuilderRemoteViews &&
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004622 n.extras.getInt(EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT, -1) ==
Adrian Roos184bfe022016-03-03 13:41:44 -08004623 n.bigContentView.getSequenceNumber();
4624 boolean stripHeadsUpContentView = n.headsUpContentView instanceof BuilderRemoteViews &&
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004625 n.extras.getInt(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT, -1) ==
Adrian Roos184bfe022016-03-03 13:41:44 -08004626 n.headsUpContentView.getSequenceNumber();
4627
4628 // Nothing to do here, no need to clone.
4629 if (!stripContentView && !stripBigContentView && !stripHeadsUpContentView) {
4630 return n;
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004631 }
Adrian Roos184bfe022016-03-03 13:41:44 -08004632
4633 Notification clone = n.clone();
4634 if (stripContentView) {
4635 clone.contentView = null;
4636 clone.extras.remove(EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT);
4637 }
4638 if (stripBigContentView) {
4639 clone.bigContentView = null;
4640 clone.extras.remove(EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT);
4641 }
4642 if (stripHeadsUpContentView) {
4643 clone.headsUpContentView = null;
4644 clone.extras.remove(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT);
4645 }
4646 return clone;
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004647 }
4648
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004649 private int getBaseLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07004650 return R.layout.notification_template_material_base;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004651 }
4652
4653 private int getBigBaseLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07004654 return R.layout.notification_template_material_big_base;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004655 }
4656
4657 private int getBigPictureLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07004658 return R.layout.notification_template_material_big_picture;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004659 }
4660
4661 private int getBigTextLayoutResource() {
Jorim Jaggi445d3c02014-08-19 22:33:42 +02004662 return R.layout.notification_template_material_big_text;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004663 }
4664
4665 private int getInboxLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07004666 return R.layout.notification_template_material_inbox;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004667 }
4668
Adrian Roosc1a80b02016-04-05 14:54:55 -07004669 private int getMessagingLayoutResource() {
4670 return R.layout.notification_template_material_messaging;
4671 }
4672
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004673 private int getActionLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07004674 return R.layout.notification_material_action;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004675 }
4676
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004677 private int getEmphasizedActionLayoutResource() {
4678 return R.layout.notification_material_action_emphasized;
4679 }
4680
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004681 private int getActionTombstoneLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07004682 return R.layout.notification_material_action_tombstone;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004683 }
Selim Cinek7b9605b2017-01-19 17:36:00 -08004684
4685 private int getBackgroundColor() {
4686 if (isColorized()) {
4687 return mN.color;
4688 } else {
4689 return COLOR_DEFAULT;
4690 }
4691 }
4692
4693 private boolean isColorized() {
4694 return mN.isColorized();
4695 }
Selim Cinek99104832017-01-25 14:47:33 -08004696
4697 private boolean textColorsNeedInversion() {
4698 if (mStyle == null || !MediaStyle.class.equals(mStyle.getClass())) {
4699 return false;
4700 }
4701 int targetSdkVersion = mContext.getApplicationInfo().targetSdkVersion;
4702 return targetSdkVersion > Build.VERSION_CODES.M
4703 && targetSdkVersion < Build.VERSION_CODES.O;
4704 }
Selim Cinek7b9605b2017-01-19 17:36:00 -08004705 }
4706
4707 /**
4708 * @return whether this notification is ongoing and can't be dismissed by the user.
4709 */
4710 private boolean isOngoing() {
4711 final int ongoingFlags = Notification.FLAG_FOREGROUND_SERVICE
4712 | Notification.FLAG_ONGOING_EVENT | Notification.FLAG_NO_CLEAR;
4713 return (flags & ongoingFlags) != 0;
4714 }
4715
4716 /**
Selim Cinek99104832017-01-25 14:47:33 -08004717 * @return whether this notification has a media session attached
4718 * @hide
4719 */
4720 public boolean hasMediaSession() {
4721 return extras.getParcelable(Notification.EXTRA_MEDIA_SESSION) != null;
4722 }
4723
4724 /**
Selim Cinek7d1009b2017-01-25 15:28:28 -08004725 * @return the style class of this notification
4726 * @hide
4727 */
4728 public Class<? extends Notification.Style> getNotificationStyle() {
4729 String templateClass = extras.getString(Notification.EXTRA_TEMPLATE);
4730
4731 if (!TextUtils.isEmpty(templateClass)) {
4732 return Notification.getNotificationStyleClass(templateClass);
4733 }
4734 return null;
4735 }
4736
4737 /**
Selim Cinek99104832017-01-25 14:47:33 -08004738 * @return true if this notification is colorized. This also factors in whether the
Selim Cinek7b9605b2017-01-19 17:36:00 -08004739 * notification is ongoing.
4740 *
4741 * @hide
4742 */
4743 public boolean isColorized() {
Selim Cinek99104832017-01-25 14:47:33 -08004744 Class<? extends Style> style = getNotificationStyle();
4745 if (MediaStyle.class.equals(style)) {
4746 Boolean colorized = (Boolean) extras.get(EXTRA_COLORIZED);
4747 if ((colorized == null || colorized) && hasMediaSession()) {
4748 return true;
4749 }
4750 } else if (DecoratedMediaCustomViewStyle.class.equals(style)) {
4751 if (extras.getBoolean(EXTRA_COLORIZED) && hasMediaSession()) {
4752 return true;
4753 }
4754 }
Selim Cinek7b9605b2017-01-19 17:36:00 -08004755 return extras.getBoolean(EXTRA_COLORIZED) && isOngoing();
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004756 }
4757
Selim Cinek279fa862016-06-14 10:57:25 -07004758 private boolean hasLargeIcon() {
4759 return mLargeIcon != null || largeIcon != null;
4760 }
4761
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004762 /**
Selim Cinekc2c0b042016-05-18 17:13:46 -07004763 * @return true if the notification will show the time; false otherwise
Selim Cinekb85f36fd2016-04-20 18:46:36 -07004764 * @hide
4765 */
Selim Cinekc2c0b042016-05-18 17:13:46 -07004766 public boolean showsTime() {
Selim Cinekb85f36fd2016-04-20 18:46:36 -07004767 return when != 0 && extras.getBoolean(EXTRA_SHOW_WHEN);
4768 }
4769
4770 /**
Selim Cinekc2c0b042016-05-18 17:13:46 -07004771 * @return true if the notification will show a chronometer; false otherwise
4772 * @hide
4773 */
4774 public boolean showsChronometer() {
4775 return when != 0 && extras.getBoolean(EXTRA_SHOW_CHRONOMETER);
4776 }
4777
4778 /**
Julia Reynolds4a02afb2016-12-13 13:39:52 -05004779 * @hide
4780 */
4781 @SystemApi
4782 public static Class<? extends Style> getNotificationStyleClass(String templateClass) {
4783 Class<? extends Style>[] classes = new Class[] {
4784 BigTextStyle.class, BigPictureStyle.class, InboxStyle.class, MediaStyle.class,
4785 DecoratedCustomViewStyle.class, DecoratedMediaCustomViewStyle.class,
4786 MessagingStyle.class };
4787 for (Class<? extends Style> innerClass : classes) {
4788 if (templateClass.equals(innerClass.getName())) {
4789 return innerClass;
4790 }
4791 }
4792 return null;
4793 }
4794
4795 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004796 * An object that can apply a rich notification style to a {@link Notification.Builder}
4797 * object.
4798 */
Griff Hazendfcb0802014-02-11 12:00:00 -08004799 public static abstract class Style {
Chris Wrend6297db2012-05-03 16:20:13 -04004800 private CharSequence mBigContentTitle;
Jorim Jaggi457a10d2014-09-08 16:18:23 +02004801
4802 /**
4803 * @hide
4804 */
4805 protected CharSequence mSummaryText = null;
4806
4807 /**
4808 * @hide
4809 */
4810 protected boolean mSummaryTextSet = false;
Chris Wrend6297db2012-05-03 16:20:13 -04004811
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004812 protected Builder mBuilder;
4813
Chris Wrend6297db2012-05-03 16:20:13 -04004814 /**
4815 * Overrides ContentTitle in the big form of the template.
4816 * This defaults to the value passed to setContentTitle().
4817 */
4818 protected void internalSetBigContentTitle(CharSequence title) {
4819 mBigContentTitle = title;
4820 }
4821
4822 /**
4823 * Set the first line of text after the detail section in the big form of the template.
4824 */
4825 protected void internalSetSummaryText(CharSequence cs) {
4826 mSummaryText = cs;
Daniel Sandler619738c2012-06-07 16:33:08 -04004827 mSummaryTextSet = true;
Chris Wrend6297db2012-05-03 16:20:13 -04004828 }
4829
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004830 public void setBuilder(Builder builder) {
4831 if (mBuilder != builder) {
4832 mBuilder = builder;
Daniel Sandlerc08dea22012-06-28 08:35:24 -07004833 if (mBuilder != null) {
4834 mBuilder.setStyle(this);
4835 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004836 }
4837 }
4838
Chris Wrend6297db2012-05-03 16:20:13 -04004839 protected void checkBuilder() {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004840 if (mBuilder == null) {
4841 throw new IllegalArgumentException("Style requires a valid Builder object");
4842 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004843 }
Chris Wrend6297db2012-05-03 16:20:13 -04004844
4845 protected RemoteViews getStandardView(int layoutId) {
4846 checkBuilder();
4847
Christoph Studer4600f9b2014-07-22 22:44:43 +02004848 // Nasty.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004849 CharSequence oldBuilderContentTitle =
4850 mBuilder.getAllExtras().getCharSequence(EXTRA_TITLE);
Chris Wrend6297db2012-05-03 16:20:13 -04004851 if (mBigContentTitle != null) {
4852 mBuilder.setContentTitle(mBigContentTitle);
4853 }
4854
Chris Wrend6297db2012-05-03 16:20:13 -04004855 RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(layoutId);
4856
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004857 mBuilder.getAllExtras().putCharSequence(EXTRA_TITLE, oldBuilderContentTitle);
Christoph Studer4600f9b2014-07-22 22:44:43 +02004858
Chris Wrend6297db2012-05-03 16:20:13 -04004859 if (mBigContentTitle != null && mBigContentTitle.equals("")) {
4860 contentView.setViewVisibility(R.id.line1, View.GONE);
Chris Wren67dc9a02012-05-16 01:03:20 -04004861 } else {
4862 contentView.setViewVisibility(R.id.line1, View.VISIBLE);
Chris Wrend6297db2012-05-03 16:20:13 -04004863 }
4864
Chris Wrend6297db2012-05-03 16:20:13 -04004865 return contentView;
4866 }
4867
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004868 /**
Selim Cinek7d1009b2017-01-25 15:28:28 -08004869 * Construct a Style-specific RemoteViews for the collapsed notification layout.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004870 * The default implementation has nothing additional to add.
Selim Cinek7d1009b2017-01-25 15:28:28 -08004871 *
4872 * @param increasedHeight true if this layout be created with an increased height.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004873 * @hide
4874 */
Selim Cinek7d1009b2017-01-25 15:28:28 -08004875 public RemoteViews makeContentView(boolean increasedHeight) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004876 return null;
4877 }
4878
4879 /**
4880 * Construct a Style-specific RemoteViews for the final big notification layout.
4881 * @hide
4882 */
4883 public RemoteViews makeBigContentView() {
4884 return null;
4885 }
4886
4887 /**
4888 * Construct a Style-specific RemoteViews for the final HUN layout.
Selim Cinek87ed69b2017-02-09 15:59:43 -08004889 *
4890 * @param increasedHeight true if this layout be created with an increased height.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004891 * @hide
4892 */
Selim Cinek87ed69b2017-02-09 15:59:43 -08004893 public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004894 return null;
4895 }
4896
4897 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004898 * Apply any style-specific extras to this notification before shipping it out.
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004899 * @hide
4900 */
4901 public void addExtras(Bundle extras) {
4902 if (mSummaryTextSet) {
4903 extras.putCharSequence(EXTRA_SUMMARY_TEXT, mSummaryText);
4904 }
4905 if (mBigContentTitle != null) {
4906 extras.putCharSequence(EXTRA_TITLE_BIG, mBigContentTitle);
4907 }
Chris Wren91ad5632013-06-05 15:05:57 -04004908 extras.putString(EXTRA_TEMPLATE, this.getClass().getName());
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004909 }
4910
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04004911 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004912 * Reconstruct the internal state of this Style object from extras.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04004913 * @hide
4914 */
Christoph Studer4600f9b2014-07-22 22:44:43 +02004915 protected void restoreFromExtras(Bundle extras) {
4916 if (extras.containsKey(EXTRA_SUMMARY_TEXT)) {
4917 mSummaryText = extras.getCharSequence(EXTRA_SUMMARY_TEXT);
4918 mSummaryTextSet = true;
4919 }
4920 if (extras.containsKey(EXTRA_TITLE_BIG)) {
4921 mBigContentTitle = extras.getCharSequence(EXTRA_TITLE_BIG);
4922 }
4923 }
4924
4925
4926 /**
4927 * @hide
4928 */
4929 public Notification buildStyled(Notification wip) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004930 addExtras(wip.extras);
Christoph Studer4600f9b2014-07-22 22:44:43 +02004931 return wip;
4932 }
4933
Daniel Sandler0ec46202015-06-24 01:27:05 -04004934 /**
4935 * @hide
4936 */
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07004937 public void purgeResources() {}
4938
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04004939 /**
4940 * Calls {@link android.app.Notification.Builder#build()} on the Builder this Style is
4941 * attached to.
4942 *
4943 * @return the fully constructed Notification.
4944 */
4945 public Notification build() {
4946 checkBuilder();
4947 return mBuilder.build();
4948 }
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004949
4950 /**
4951 * @hide
4952 * @return true if the style positions the progress bar on the second line; false if the
4953 * style hides the progress bar
4954 */
4955 protected boolean hasProgress() {
4956 return true;
4957 }
Selim Cinek03d0d652015-11-13 13:18:09 -05004958
4959 /**
4960 * @hide
4961 * @return Whether we should put the summary be put into the notification header
4962 */
4963 public boolean hasSummaryInHeader() {
4964 return true;
4965 }
Selim Cinek593610c2016-02-16 18:42:57 -08004966
4967 /**
4968 * @hide
4969 * @return Whether custom content views are displayed inline in the style
4970 */
4971 public boolean displayCustomViewInline() {
4972 return false;
4973 }
Joe Onorato46439ce2010-11-19 13:56:21 -08004974 }
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004975
4976 /**
Daniel Sandler4dfbe832012-04-11 14:51:46 -04004977 * Helper class for generating large-format notifications that include a large image attachment.
Joe Malin8d40d042012-11-05 11:36:40 -08004978 *
Robert Ly91c5ce32014-06-08 15:37:00 -07004979 * Here's how you'd set the <code>BigPictureStyle</code> on a notification:
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004980 * <pre class="prettyprint">
Robert Ly91c5ce32014-06-08 15:37:00 -07004981 * Notification notif = new Notification.Builder(mContext)
4982 * .setContentTitle(&quot;New photo from &quot; + sender.toString())
4983 * .setContentText(subject)
4984 * .setSmallIcon(R.drawable.new_post)
4985 * .setLargeIcon(aBitmap)
4986 * .setStyle(new Notification.BigPictureStyle()
4987 * .bigPicture(aBigBitmap))
4988 * .build();
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004989 * </pre>
Joe Malin8d40d042012-11-05 11:36:40 -08004990 *
Daniel Sandler4dfbe832012-04-11 14:51:46 -04004991 * @see Notification#bigContentView
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004992 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004993 public static class BigPictureStyle extends Style {
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004994 private Bitmap mPicture;
Dan Sandlerd63f9322015-05-06 15:18:49 -04004995 private Icon mBigLargeIcon;
Chris Wren3745a3d2012-05-22 15:11:52 -04004996 private boolean mBigLargeIconSet = false;
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004997
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004998 public BigPictureStyle() {
4999 }
5000
Adrian Roosf5faf9d2016-05-23 13:56:15 -07005001 /**
5002 * @deprecated use {@code BigPictureStyle()}.
5003 */
5004 @Deprecated
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005005 public BigPictureStyle(Builder builder) {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005006 setBuilder(builder);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005007 }
5008
Chris Wrend6297db2012-05-03 16:20:13 -04005009 /**
5010 * Overrides ContentTitle in the big form of the template.
5011 * This defaults to the value passed to setContentTitle().
5012 */
5013 public BigPictureStyle setBigContentTitle(CharSequence title) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04005014 internalSetBigContentTitle(safeCharSequence(title));
Chris Wrend6297db2012-05-03 16:20:13 -04005015 return this;
5016 }
5017
5018 /**
5019 * Set the first line of text after the detail section in the big form of the template.
5020 */
5021 public BigPictureStyle setSummaryText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04005022 internalSetSummaryText(safeCharSequence(cs));
Chris Wrend6297db2012-05-03 16:20:13 -04005023 return this;
5024 }
5025
Chris Wren0bd664d2012-08-01 13:56:56 -04005026 /**
5027 * Provide the bitmap to be used as the payload for the BigPicture notification.
5028 */
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005029 public BigPictureStyle bigPicture(Bitmap b) {
5030 mPicture = b;
5031 return this;
5032 }
5033
Chris Wren3745a3d2012-05-22 15:11:52 -04005034 /**
Chris Wren3745a3d2012-05-22 15:11:52 -04005035 * Override the large icon when the big notification is shown.
5036 */
5037 public BigPictureStyle bigLargeIcon(Bitmap b) {
Dan Sandlerd63f9322015-05-06 15:18:49 -04005038 return bigLargeIcon(b != null ? Icon.createWithBitmap(b) : null);
5039 }
5040
5041 /**
5042 * Override the large icon when the big notification is shown.
5043 */
5044 public BigPictureStyle bigLargeIcon(Icon icon) {
Chris Wren3745a3d2012-05-22 15:11:52 -04005045 mBigLargeIconSet = true;
Dan Sandlerd63f9322015-05-06 15:18:49 -04005046 mBigLargeIcon = icon;
Chris Wren3745a3d2012-05-22 15:11:52 -04005047 return this;
5048 }
5049
Riley Andrews0394a0c2015-11-03 23:36:52 -08005050 /** @hide */
5051 public static final int MIN_ASHMEM_BITMAP_SIZE = 128 * (1 << 10);
5052
Daniel Sandler0ec46202015-06-24 01:27:05 -04005053 /**
5054 * @hide
5055 */
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07005056 @Override
5057 public void purgeResources() {
5058 super.purgeResources();
Riley Andrews8cee7c12015-11-01 23:36:04 -08005059 if (mPicture != null &&
5060 mPicture.isMutable() &&
Riley Andrews0394a0c2015-11-03 23:36:52 -08005061 mPicture.getAllocationByteCount() >= MIN_ASHMEM_BITMAP_SIZE) {
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07005062 mPicture = mPicture.createAshmemBitmap();
5063 }
5064 if (mBigLargeIcon != null) {
5065 mBigLargeIcon.convertToAshmem();
5066 }
5067 }
Christoph Studer5c510ee2014-12-15 16:32:27 +01005068
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005069 /**
5070 * @hide
5071 */
5072 public RemoteViews makeBigContentView() {
5073 // Replace mN.mLargeIcon with mBigLargeIcon if mBigLargeIconSet
Christoph Studer5c510ee2014-12-15 16:32:27 +01005074 // This covers the following cases:
5075 // 1. mBigLargeIconSet -> mBigLargeIcon (null or non-null) applies, overrides
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005076 // mN.mLargeIcon
5077 // 2. !mBigLargeIconSet -> mN.mLargeIcon applies
Dan Sandlerd63f9322015-05-06 15:18:49 -04005078 Icon oldLargeIcon = null;
Selim Cineke99acb22016-08-04 12:55:48 -07005079 Bitmap largeIconLegacy = null;
Christoph Studer5c510ee2014-12-15 16:32:27 +01005080 if (mBigLargeIconSet) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005081 oldLargeIcon = mBuilder.mN.mLargeIcon;
5082 mBuilder.mN.mLargeIcon = mBigLargeIcon;
Selim Cineke99acb22016-08-04 12:55:48 -07005083 // The legacy largeIcon might not allow us to clear the image, as it's taken in
5084 // replacement if the other one is null. Because we're restoring these legacy icons
5085 // for old listeners, this is in general non-null.
5086 largeIconLegacy = mBuilder.mN.largeIcon;
5087 mBuilder.mN.largeIcon = null;
Christoph Studer5c510ee2014-12-15 16:32:27 +01005088 }
5089
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01005090 RemoteViews contentView = getStandardView(mBuilder.getBigPictureLayoutResource());
Selim Cinek03d0d652015-11-13 13:18:09 -05005091 if (mSummaryTextSet) {
5092 contentView.setTextViewText(R.id.text, mBuilder.processLegacyText(mSummaryText));
Selim Cinek7b9605b2017-01-19 17:36:00 -08005093 mBuilder.setTextViewColorSecondary(contentView, R.id.text);
Selim Cinekc848c3a2016-01-13 15:27:30 -08005094 contentView.setViewVisibility(R.id.text, View.VISIBLE);
Selim Cinek03d0d652015-11-13 13:18:09 -05005095 }
Selim Cinek279fa862016-06-14 10:57:25 -07005096 mBuilder.setContentMinHeight(contentView, mBuilder.mN.hasLargeIcon());
Selim Cinek53e64a42015-11-16 10:40:56 -08005097
Christoph Studer5c510ee2014-12-15 16:32:27 +01005098 if (mBigLargeIconSet) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005099 mBuilder.mN.mLargeIcon = oldLargeIcon;
Selim Cineke99acb22016-08-04 12:55:48 -07005100 mBuilder.mN.largeIcon = largeIconLegacy;
Christoph Studer5c510ee2014-12-15 16:32:27 +01005101 }
5102
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005103 contentView.setImageViewBitmap(R.id.big_picture, mPicture);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005104 return contentView;
5105 }
5106
Daniel Sandlerf45564e2013-04-15 15:05:08 -04005107 /**
5108 * @hide
5109 */
5110 public void addExtras(Bundle extras) {
5111 super.addExtras(extras);
5112
5113 if (mBigLargeIconSet) {
5114 extras.putParcelable(EXTRA_LARGE_ICON_BIG, mBigLargeIcon);
5115 }
5116 extras.putParcelable(EXTRA_PICTURE, mPicture);
5117 }
5118
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04005119 /**
5120 * @hide
5121 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005122 @Override
Christoph Studer4600f9b2014-07-22 22:44:43 +02005123 protected void restoreFromExtras(Bundle extras) {
5124 super.restoreFromExtras(extras);
5125
5126 if (extras.containsKey(EXTRA_LARGE_ICON_BIG)) {
Christoph Studer5c510ee2014-12-15 16:32:27 +01005127 mBigLargeIconSet = true;
Christoph Studer4600f9b2014-07-22 22:44:43 +02005128 mBigLargeIcon = extras.getParcelable(EXTRA_LARGE_ICON_BIG);
Chris Wren3745a3d2012-05-22 15:11:52 -04005129 }
Christoph Studer4600f9b2014-07-22 22:44:43 +02005130 mPicture = extras.getParcelable(EXTRA_PICTURE);
5131 }
Selim Cinek03d0d652015-11-13 13:18:09 -05005132
5133 /**
5134 * @hide
5135 */
5136 @Override
5137 public boolean hasSummaryInHeader() {
5138 return false;
5139 }
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005140 }
5141
5142 /**
Daniel Sandler4dfbe832012-04-11 14:51:46 -04005143 * Helper class for generating large-format notifications that include a lot of text.
Joe Malin8d40d042012-11-05 11:36:40 -08005144 *
Robert Ly91c5ce32014-06-08 15:37:00 -07005145 * Here's how you'd set the <code>BigTextStyle</code> on a notification:
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005146 * <pre class="prettyprint">
Robert Ly91c5ce32014-06-08 15:37:00 -07005147 * Notification notif = new Notification.Builder(mContext)
5148 * .setContentTitle(&quot;New mail from &quot; + sender.toString())
5149 * .setContentText(subject)
5150 * .setSmallIcon(R.drawable.new_mail)
5151 * .setLargeIcon(aBitmap)
5152 * .setStyle(new Notification.BigTextStyle()
5153 * .bigText(aVeryLongString))
5154 * .build();
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005155 * </pre>
Joe Malin8d40d042012-11-05 11:36:40 -08005156 *
Daniel Sandler4dfbe832012-04-11 14:51:46 -04005157 * @see Notification#bigContentView
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005158 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005159 public static class BigTextStyle extends Style {
Jorim Jaggi457a10d2014-09-08 16:18:23 +02005160
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005161 private CharSequence mBigText;
5162
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005163 public BigTextStyle() {
5164 }
5165
Adrian Roosf5faf9d2016-05-23 13:56:15 -07005166 /**
5167 * @deprecated use {@code BigTextStyle()}.
5168 */
5169 @Deprecated
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005170 public BigTextStyle(Builder builder) {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005171 setBuilder(builder);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005172 }
5173
Chris Wrend6297db2012-05-03 16:20:13 -04005174 /**
5175 * Overrides ContentTitle in the big form of the template.
5176 * This defaults to the value passed to setContentTitle().
5177 */
5178 public BigTextStyle setBigContentTitle(CharSequence title) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04005179 internalSetBigContentTitle(safeCharSequence(title));
Chris Wrend6297db2012-05-03 16:20:13 -04005180 return this;
5181 }
5182
5183 /**
5184 * Set the first line of text after the detail section in the big form of the template.
5185 */
5186 public BigTextStyle setSummaryText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04005187 internalSetSummaryText(safeCharSequence(cs));
Chris Wrend6297db2012-05-03 16:20:13 -04005188 return this;
5189 }
5190
Chris Wren0bd664d2012-08-01 13:56:56 -04005191 /**
5192 * Provide the longer text to be displayed in the big form of the
5193 * template in place of the content text.
5194 */
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005195 public BigTextStyle bigText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04005196 mBigText = safeCharSequence(cs);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005197 return this;
5198 }
5199
Daniel Sandlerf45564e2013-04-15 15:05:08 -04005200 /**
5201 * @hide
5202 */
5203 public void addExtras(Bundle extras) {
5204 super.addExtras(extras);
5205
Christoph Studer4600f9b2014-07-22 22:44:43 +02005206 extras.putCharSequence(EXTRA_BIG_TEXT, mBigText);
5207 }
5208
5209 /**
5210 * @hide
5211 */
5212 @Override
5213 protected void restoreFromExtras(Bundle extras) {
5214 super.restoreFromExtras(extras);
5215
5216 mBigText = extras.getCharSequence(EXTRA_BIG_TEXT);
Daniel Sandlerf45564e2013-04-15 15:05:08 -04005217 }
5218
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005219 /**
Selim Cinek7d1009b2017-01-25 15:28:28 -08005220 * @param increasedHeight true if this layout be created with an increased height.
5221 *
5222 * @hide
5223 */
5224 @Override
5225 public RemoteViews makeContentView(boolean increasedHeight) {
5226 if (increasedHeight) {
5227 ArrayList<Action> actions = mBuilder.mActions;
5228 mBuilder.mActions = new ArrayList<>();
5229 RemoteViews remoteViews = makeBigContentView();
5230 mBuilder.mActions = actions;
5231 return remoteViews;
5232 }
5233 return super.makeContentView(increasedHeight);
5234 }
5235
5236 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005237 * @hide
5238 */
Selim Cinek87ed69b2017-02-09 15:59:43 -08005239 @Override
5240 public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
5241 if (increasedHeight && mBuilder.mActions.size() > 0) {
5242 return makeBigContentView();
5243 }
5244 return super.makeHeadsUpContentView(increasedHeight);
5245 }
5246
5247 /**
5248 * @hide
5249 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005250 public RemoteViews makeBigContentView() {
Christoph Studer4600f9b2014-07-22 22:44:43 +02005251
5252 // Nasty
Selim Cinek75998782016-04-26 10:39:17 -07005253 CharSequence text = mBuilder.getAllExtras().getCharSequence(EXTRA_TEXT);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005254 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, null);
Daniel Sandler916ad912012-06-13 12:17:07 -04005255
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01005256 RemoteViews contentView = getStandardView(mBuilder.getBigTextLayoutResource());
Joe Malin8d40d042012-11-05 11:36:40 -08005257
Selim Cinek75998782016-04-26 10:39:17 -07005258 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, text);
Christoph Studer4600f9b2014-07-22 22:44:43 +02005259
Selim Cinek3a2c4b92015-12-17 17:01:17 -08005260 CharSequence bigTextText = mBuilder.processLegacyText(mBigText);
Selim Cinek75998782016-04-26 10:39:17 -07005261 if (TextUtils.isEmpty(bigTextText)) {
5262 // In case the bigtext is null / empty fall back to the normal text to avoid a weird
5263 // experience
5264 bigTextText = mBuilder.processLegacyText(text);
5265 }
Adrian Roosb1f427c2016-05-26 12:27:15 -07005266 applyBigTextContentView(mBuilder, contentView, bigTextText);
Selim Cinek4fb12d32015-11-19 18:10:48 -08005267
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005268 return contentView;
5269 }
5270
Adrian Roosb1f427c2016-05-26 12:27:15 -07005271 static void applyBigTextContentView(Builder builder,
5272 RemoteViews contentView, CharSequence bigTextText) {
5273 contentView.setTextViewText(R.id.big_text, bigTextText);
Selim Cinek7b9605b2017-01-19 17:36:00 -08005274 builder.setTextViewColorSecondary(contentView, R.id.big_text);
Adrian Roosb1f427c2016-05-26 12:27:15 -07005275 contentView.setViewVisibility(R.id.big_text,
5276 TextUtils.isEmpty(bigTextText) ? View.GONE : View.VISIBLE);
Selim Cinek279fa862016-06-14 10:57:25 -07005277 contentView.setBoolean(R.id.big_text, "setHasImage", builder.mN.hasLargeIcon());
Adrian Roosb1f427c2016-05-26 12:27:15 -07005278 }
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005279 }
Daniel Sandler879c5e02012-04-17 16:46:51 -04005280
5281 /**
Alex Hillsfc737de2016-03-23 17:33:02 -04005282 * Helper class for generating large-format notifications that include multiple back-and-forth
5283 * messages of varying types between any number of people.
5284 *
5285 * <br>
5286 * If the platform does not provide large-format notifications, this method has no effect. The
5287 * user will always see the normal notification view.
5288 * <br>
5289 * This class is a "rebuilder": It attaches to a Builder object and modifies its behavior, like
5290 * so:
5291 * <pre class="prettyprint">
5292 *
5293 * Notification noti = new Notification.Builder()
5294 * .setContentTitle(&quot;2 new messages wtih &quot; + sender.toString())
5295 * .setContentText(subject)
5296 * .setSmallIcon(R.drawable.new_message)
5297 * .setLargeIcon(aBitmap)
5298 * .setStyle(new Notification.MessagingStyle(resources.getString(R.string.reply_name))
5299 * .addMessage(messages[0].getText(), messages[0].getTime(), messages[0].getSender())
5300 * .addMessage(messages[1].getText(), messages[1].getTime(), messages[1].getSender()))
5301 * .build();
5302 * </pre>
5303 */
5304 public static class MessagingStyle extends Style {
5305
5306 /**
5307 * The maximum number of messages that will be retained in the Notification itself (the
5308 * number displayed is up to the platform).
5309 */
5310 public static final int MAXIMUM_RETAINED_MESSAGES = 25;
5311
5312 CharSequence mUserDisplayName;
5313 CharSequence mConversationTitle;
Alex Hillsd9b04d92016-04-11 16:38:16 -04005314 List<Message> mMessages = new ArrayList<>();
Adrian Roos437cd562017-01-18 15:47:03 -08005315 List<Message> mHistoricMessages = new ArrayList<>();
Alex Hillsfc737de2016-03-23 17:33:02 -04005316
5317 MessagingStyle() {
5318 }
5319
5320 /**
Alex Hillsfd590442016-10-07 09:52:44 -04005321 * @param userDisplayName Required - the name to be displayed for any replies sent by the
5322 * user before the posting app reposts the notification with those messages after they've
5323 * been actually sent and in previous messages sent by the user added in
Alex Hillsfc737de2016-03-23 17:33:02 -04005324 * {@link #addMessage(Notification.MessagingStyle.Message)}
5325 */
Alex Hillsfd590442016-10-07 09:52:44 -04005326 public MessagingStyle(@NonNull CharSequence userDisplayName) {
Alex Hillsfc737de2016-03-23 17:33:02 -04005327 mUserDisplayName = userDisplayName;
5328 }
5329
5330 /**
5331 * Returns the name to be displayed for any replies sent by the user
5332 */
5333 public CharSequence getUserDisplayName() {
5334 return mUserDisplayName;
5335 }
5336
5337 /**
Alex Hillsfc737de2016-03-23 17:33:02 -04005338 * Sets the title to be displayed on this conversation. This should only be used for
5339 * group messaging and left unset for one-on-one conversations.
5340 * @param conversationTitle
5341 * @return this object for method chaining.
5342 */
5343 public MessagingStyle setConversationTitle(CharSequence conversationTitle) {
5344 mConversationTitle = conversationTitle;
5345 return this;
5346 }
5347
5348 /**
5349 * Return the title to be displayed on this conversation. Can be <code>null</code> and
5350 * should be for one-on-one conversations
5351 */
5352 public CharSequence getConversationTitle() {
5353 return mConversationTitle;
5354 }
5355
5356 /**
5357 * Adds a message for display by this notification. Convenience call for a simple
5358 * {@link Message} in {@link #addMessage(Notification.MessagingStyle.Message)}.
5359 * @param text A {@link CharSequence} to be displayed as the message content
5360 * @param timestamp Time at which the message arrived
5361 * @param sender A {@link CharSequence} to be used for displaying the name of the
5362 * sender. Should be <code>null</code> for messages by the current user, in which case
5363 * the platform will insert {@link #getUserDisplayName()}.
5364 * Should be unique amongst all individuals in the conversation, and should be
5365 * consistent during re-posts of the notification.
5366 *
5367 * @see Message#Message(CharSequence, long, CharSequence)
5368 *
5369 * @return this object for method chaining
5370 */
5371 public MessagingStyle addMessage(CharSequence text, long timestamp, CharSequence sender) {
Adrian Roos437cd562017-01-18 15:47:03 -08005372 return addMessage(new Message(text, timestamp, sender));
Alex Hillsfc737de2016-03-23 17:33:02 -04005373 }
5374
5375 /**
5376 * Adds a {@link Message} for display in this notification.
Adrian Roos437cd562017-01-18 15:47:03 -08005377 *
5378 * <p>The messages should be added in chronologic order, i.e. the oldest first,
5379 * the newest last.
5380 *
Alex Hillsfc737de2016-03-23 17:33:02 -04005381 * @param message The {@link Message} to be displayed
5382 * @return this object for method chaining
5383 */
5384 public MessagingStyle addMessage(Message message) {
5385 mMessages.add(message);
5386 if (mMessages.size() > MAXIMUM_RETAINED_MESSAGES) {
5387 mMessages.remove(0);
5388 }
5389 return this;
5390 }
5391
5392 /**
Adrian Roos437cd562017-01-18 15:47:03 -08005393 * Adds a {@link Message} for historic context in this notification.
5394 *
5395 * <p>Messages should be added as historic if they are not the main subject of the
5396 * notification but may give context to a conversation. The system may choose to present
5397 * them only when relevant, e.g. when replying to a message through a {@link RemoteInput}.
5398 *
5399 * <p>The messages should be added in chronologic order, i.e. the oldest first,
5400 * the newest last.
5401 *
5402 * @param message The historic {@link Message} to be added
5403 * @return this object for method chaining
5404 */
5405 public MessagingStyle addHistoricMessage(Message message) {
5406 mHistoricMessages.add(message);
5407 if (mHistoricMessages.size() > MAXIMUM_RETAINED_MESSAGES) {
5408 mHistoricMessages.remove(0);
5409 }
5410 return this;
5411 }
5412
5413 /**
Alex Hillsfc737de2016-03-23 17:33:02 -04005414 * Gets the list of {@code Message} objects that represent the notification
5415 */
5416 public List<Message> getMessages() {
5417 return mMessages;
5418 }
5419
5420 /**
Adrian Roos437cd562017-01-18 15:47:03 -08005421 * Gets the list of historic {@code Message}s in the notification.
5422 */
5423 public List<Message> getHistoricMessages() {
5424 return mHistoricMessages;
5425 }
5426
5427 /**
Alex Hillsfc737de2016-03-23 17:33:02 -04005428 * @hide
5429 */
5430 @Override
5431 public void addExtras(Bundle extras) {
5432 super.addExtras(extras);
5433 if (mUserDisplayName != null) {
5434 extras.putCharSequence(EXTRA_SELF_DISPLAY_NAME, mUserDisplayName);
5435 }
5436 if (mConversationTitle != null) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04005437 extras.putCharSequence(EXTRA_CONVERSATION_TITLE, mConversationTitle);
Alex Hillsfc737de2016-03-23 17:33:02 -04005438 }
Alex Hillsd9b04d92016-04-11 16:38:16 -04005439 if (!mMessages.isEmpty()) { extras.putParcelableArray(EXTRA_MESSAGES,
5440 Message.getBundleArrayForMessages(mMessages));
Alex Hillsfc737de2016-03-23 17:33:02 -04005441 }
Adrian Roos437cd562017-01-18 15:47:03 -08005442 if (!mHistoricMessages.isEmpty()) { extras.putParcelableArray(EXTRA_HISTORIC_MESSAGES,
5443 Message.getBundleArrayForMessages(mHistoricMessages));
5444 }
Adrian Roos33fbd2c2016-05-27 15:35:28 -07005445
5446 fixTitleAndTextExtras(extras);
5447 }
5448
5449 private void fixTitleAndTextExtras(Bundle extras) {
5450 Message m = findLatestIncomingMessage();
5451 CharSequence text = (m == null) ? null : m.mText;
5452 CharSequence sender = m == null ? null
5453 : TextUtils.isEmpty(m.mSender) ? mUserDisplayName : m.mSender;
5454 CharSequence title;
5455 if (!TextUtils.isEmpty(mConversationTitle)) {
5456 if (!TextUtils.isEmpty(sender)) {
5457 BidiFormatter bidi = BidiFormatter.getInstance();
5458 title = mBuilder.mContext.getString(
5459 com.android.internal.R.string.notification_messaging_title_template,
5460 bidi.unicodeWrap(mConversationTitle), bidi.unicodeWrap(m.mSender));
5461 } else {
5462 title = mConversationTitle;
5463 }
5464 } else {
5465 title = sender;
5466 }
5467
5468 if (title != null) {
5469 extras.putCharSequence(EXTRA_TITLE, title);
5470 }
5471 if (text != null) {
5472 extras.putCharSequence(EXTRA_TEXT, text);
5473 }
Alex Hillsfc737de2016-03-23 17:33:02 -04005474 }
5475
5476 /**
5477 * @hide
5478 */
5479 @Override
5480 protected void restoreFromExtras(Bundle extras) {
5481 super.restoreFromExtras(extras);
5482
5483 mMessages.clear();
Adrian Roos437cd562017-01-18 15:47:03 -08005484 mHistoricMessages.clear();
Adrian Roos96b7e202016-05-17 13:50:38 -07005485 mUserDisplayName = extras.getCharSequence(EXTRA_SELF_DISPLAY_NAME);
5486 mConversationTitle = extras.getCharSequence(EXTRA_CONVERSATION_TITLE);
Adrian Roos437cd562017-01-18 15:47:03 -08005487 Parcelable[] messages = extras.getParcelableArray(EXTRA_MESSAGES);
5488 if (messages != null && messages instanceof Parcelable[]) {
5489 mMessages = Message.getMessagesFromBundleArray(messages);
5490 }
5491 Parcelable[] histMessages = extras.getParcelableArray(EXTRA_HISTORIC_MESSAGES);
5492 if (histMessages != null && histMessages instanceof Parcelable[]) {
5493 mHistoricMessages = Message.getMessagesFromBundleArray(histMessages);
Alex Hillsfc737de2016-03-23 17:33:02 -04005494 }
5495 }
5496
5497 /**
5498 * @hide
5499 */
Adrian Roosc1a80b02016-04-05 14:54:55 -07005500 @Override
Selim Cinek7d1009b2017-01-25 15:28:28 -08005501 public RemoteViews makeContentView(boolean increasedHeight) {
5502 if (!increasedHeight) {
5503 Message m = findLatestIncomingMessage();
5504 CharSequence title = mConversationTitle != null
5505 ? mConversationTitle
5506 : (m == null) ? null : m.mSender;
5507 CharSequence text = (m == null)
5508 ? null
5509 : mConversationTitle != null ? makeMessageLine(m, mBuilder) : m.mText;
Alex Hillsfc737de2016-03-23 17:33:02 -04005510
Selim Cinek7d1009b2017-01-25 15:28:28 -08005511 return mBuilder.applyStandardTemplate(mBuilder.getBaseLayoutResource(),
5512 mBuilder.mParams.reset().hasProgress(false).title(title).text(text));
5513 } else {
5514 ArrayList<Action> actions = mBuilder.mActions;
5515 mBuilder.mActions = new ArrayList<>();
5516 RemoteViews remoteViews = makeBigContentView();
5517 mBuilder.mActions = actions;
5518 return remoteViews;
5519 }
Adrian Roosc1a80b02016-04-05 14:54:55 -07005520 }
5521
5522 private Message findLatestIncomingMessage() {
5523 for (int i = mMessages.size() - 1; i >= 0; i--) {
5524 Message m = mMessages.get(i);
5525 // Incoming messages have a non-empty sender.
5526 if (!TextUtils.isEmpty(m.mSender)) {
5527 return m;
5528 }
5529 }
Adrian Roos33fbd2c2016-05-27 15:35:28 -07005530 if (!mMessages.isEmpty()) {
5531 // No incoming messages, fall back to outgoing message
5532 return mMessages.get(mMessages.size() - 1);
5533 }
Adrian Roosc1a80b02016-04-05 14:54:55 -07005534 return null;
5535 }
5536
5537 /**
5538 * @hide
5539 */
5540 @Override
5541 public RemoteViews makeBigContentView() {
5542 CharSequence title = !TextUtils.isEmpty(super.mBigContentTitle)
5543 ? super.mBigContentTitle
5544 : mConversationTitle;
5545 boolean hasTitle = !TextUtils.isEmpty(title);
5546
Adrian Roosfeafa052016-06-01 17:09:45 -07005547 if (mMessages.size() == 1) {
5548 // Special case for a single message: Use the big text style
5549 // so the collapsed and expanded versions match nicely.
5550 CharSequence bigTitle;
5551 CharSequence text;
5552 if (hasTitle) {
5553 bigTitle = title;
Selim Cinek7b9605b2017-01-19 17:36:00 -08005554 text = makeMessageLine(mMessages.get(0), mBuilder);
Adrian Roosfeafa052016-06-01 17:09:45 -07005555 } else {
5556 bigTitle = mMessages.get(0).mSender;
5557 text = mMessages.get(0).mText;
5558 }
Adrian Roosb1f427c2016-05-26 12:27:15 -07005559 RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(
5560 mBuilder.getBigTextLayoutResource(),
Adrian Roos70d7aa32017-01-11 15:39:06 -08005561 mBuilder.mParams.reset().hasProgress(false).title(bigTitle).text(null));
Adrian Roosb1f427c2016-05-26 12:27:15 -07005562 BigTextStyle.applyBigTextContentView(mBuilder, contentView, text);
5563 return contentView;
5564 }
5565
Adrian Roos48d746a2016-04-12 14:57:28 -07005566 RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(
Adrian Roosc1a80b02016-04-05 14:54:55 -07005567 mBuilder.getMessagingLayoutResource(),
Adrian Roos70d7aa32017-01-11 15:39:06 -08005568 mBuilder.mParams.reset().hasProgress(false).title(title).text(null));
Adrian Roosc1a80b02016-04-05 14:54:55 -07005569
5570 int[] rowIds = {R.id.inbox_text0, R.id.inbox_text1, R.id.inbox_text2, R.id.inbox_text3,
5571 R.id.inbox_text4, R.id.inbox_text5, R.id.inbox_text6};
5572
5573 // Make sure all rows are gone in case we reuse a view.
5574 for (int rowId : rowIds) {
5575 contentView.setViewVisibility(rowId, View.GONE);
5576 }
5577
5578 int i=0;
Adrian Roos2d5dbba2016-06-08 17:11:53 -07005579 contentView.setViewLayoutMarginBottomDimen(R.id.line1,
5580 hasTitle ? R.dimen.notification_messaging_spacing : 0);
Adrian Roosc1a80b02016-04-05 14:54:55 -07005581 contentView.setInt(R.id.notification_messaging, "setNumIndentLines",
Selim Cinek279fa862016-06-14 10:57:25 -07005582 !mBuilder.mN.hasLargeIcon() ? 0 : (hasTitle ? 1 : 2));
Adrian Roosc1a80b02016-04-05 14:54:55 -07005583
Adrian Roosfeafa052016-06-01 17:09:45 -07005584 int contractedChildId = View.NO_ID;
5585 Message contractedMessage = findLatestIncomingMessage();
Adrian Roos437cd562017-01-18 15:47:03 -08005586 int firstHistoricMessage = Math.max(0, mHistoricMessages.size()
5587 - (rowIds.length - mMessages.size()));
5588 while (firstHistoricMessage + i < mHistoricMessages.size() && i < rowIds.length) {
5589 Message m = mHistoricMessages.get(firstHistoricMessage + i);
5590 int rowId = rowIds[i];
5591
Selim Cinek7b9605b2017-01-19 17:36:00 -08005592 contentView.setTextViewText(rowId, makeMessageLine(m, mBuilder));
Adrian Roos437cd562017-01-18 15:47:03 -08005593
5594 if (contractedMessage == m) {
5595 contractedChildId = rowId;
5596 }
5597
5598 i++;
5599 }
5600
Adrian Roosc1a80b02016-04-05 14:54:55 -07005601 int firstMessage = Math.max(0, mMessages.size() - rowIds.length);
5602 while (firstMessage + i < mMessages.size() && i < rowIds.length) {
5603 Message m = mMessages.get(firstMessage + i);
5604 int rowId = rowIds[i];
5605
5606 contentView.setViewVisibility(rowId, View.VISIBLE);
Selim Cinek7b9605b2017-01-19 17:36:00 -08005607 contentView.setTextViewText(rowId, makeMessageLine(m, mBuilder));
5608 mBuilder.setTextViewColorSecondary(contentView, rowId);
Adrian Roosc1a80b02016-04-05 14:54:55 -07005609
Adrian Roosfeafa052016-06-01 17:09:45 -07005610 if (contractedMessage == m) {
5611 contractedChildId = rowId;
5612 }
5613
Adrian Roosc1a80b02016-04-05 14:54:55 -07005614 i++;
5615 }
Adrian Roos437cd562017-01-18 15:47:03 -08005616 // Clear the remaining views for reapply. Ensures that historic message views can
5617 // reliably be identified as being GONE and having non-null text.
5618 while (i < rowIds.length) {
5619 int rowId = rowIds[i];
5620 contentView.setTextViewText(rowId, null);
5621 i++;
5622 }
5623
Adrian Roosfeafa052016-06-01 17:09:45 -07005624 // Record this here to allow transformation between the contracted and expanded views.
5625 contentView.setInt(R.id.notification_messaging, "setContractedChildId",
5626 contractedChildId);
Alex Hillsfc737de2016-03-23 17:33:02 -04005627 return contentView;
5628 }
5629
Selim Cinek7b9605b2017-01-19 17:36:00 -08005630 private CharSequence makeMessageLine(Message m, Builder builder) {
Adrian Roosc1a80b02016-04-05 14:54:55 -07005631 BidiFormatter bidi = BidiFormatter.getInstance();
5632 SpannableStringBuilder sb = new SpannableStringBuilder();
Selim Cinek7b9605b2017-01-19 17:36:00 -08005633 boolean colorize = builder.isColorized();
Adrian Roosc1a80b02016-04-05 14:54:55 -07005634 if (TextUtils.isEmpty(m.mSender)) {
5635 CharSequence replyName = mUserDisplayName == null ? "" : mUserDisplayName;
5636 sb.append(bidi.unicodeWrap(replyName),
Selim Cinek7b9605b2017-01-19 17:36:00 -08005637 makeFontColorSpan(colorize
5638 ? builder.getPrimaryTextColor()
5639 : mBuilder.resolveContrastColor()),
Adrian Roosc1a80b02016-04-05 14:54:55 -07005640 0 /* flags */);
5641 } else {
5642 sb.append(bidi.unicodeWrap(m.mSender),
Selim Cinek7b9605b2017-01-19 17:36:00 -08005643 makeFontColorSpan(colorize
5644 ? builder.getPrimaryTextColor()
5645 : Color.BLACK),
Adrian Roosc1a80b02016-04-05 14:54:55 -07005646 0 /* flags */);
5647 }
5648 CharSequence text = m.mText == null ? "" : m.mText;
5649 sb.append(" ").append(bidi.unicodeWrap(text));
5650 return sb;
5651 }
5652
Adrian Roosdedd1df2016-04-26 16:38:47 -07005653 /**
5654 * @hide
5655 */
5656 @Override
Selim Cinek87ed69b2017-02-09 15:59:43 -08005657 public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
5658 if (increasedHeight) {
5659 return makeBigContentView();
5660 }
Adrian Roosdedd1df2016-04-26 16:38:47 -07005661 Message m = findLatestIncomingMessage();
5662 CharSequence title = mConversationTitle != null
5663 ? mConversationTitle
5664 : (m == null) ? null : m.mSender;
5665 CharSequence text = (m == null)
5666 ? null
Selim Cinek7b9605b2017-01-19 17:36:00 -08005667 : mConversationTitle != null ? makeMessageLine(m, mBuilder) : m.mText;
Adrian Roosdedd1df2016-04-26 16:38:47 -07005668
5669 return mBuilder.applyStandardTemplateWithActions(mBuilder.getBigBaseLayoutResource(),
Adrian Roos70d7aa32017-01-11 15:39:06 -08005670 mBuilder.mParams.reset().hasProgress(false).title(title).text(text));
Adrian Roosdedd1df2016-04-26 16:38:47 -07005671 }
5672
Adrian Roosc1a80b02016-04-05 14:54:55 -07005673 private static TextAppearanceSpan makeFontColorSpan(int color) {
5674 return new TextAppearanceSpan(null, 0, 0,
5675 ColorStateList.valueOf(color), null);
5676 }
5677
Alex Hillsd9b04d92016-04-11 16:38:16 -04005678 public static final class Message {
5679
5680 static final String KEY_TEXT = "text";
5681 static final String KEY_TIMESTAMP = "time";
5682 static final String KEY_SENDER = "sender";
5683 static final String KEY_DATA_MIME_TYPE = "type";
5684 static final String KEY_DATA_URI= "uri";
Shane Brennan5a871862017-03-11 13:14:17 -08005685 static final String KEY_EXTRAS_BUNDLE = "extras";
Alex Hillsfc737de2016-03-23 17:33:02 -04005686
5687 private final CharSequence mText;
5688 private final long mTimestamp;
5689 private final CharSequence mSender;
5690
Shane Brennan5a871862017-03-11 13:14:17 -08005691 private Bundle mExtras = new Bundle();
Alex Hillsfc737de2016-03-23 17:33:02 -04005692 private String mDataMimeType;
5693 private Uri mDataUri;
5694
5695 /**
5696 * Constructor
5697 * @param text A {@link CharSequence} to be displayed as the message content
5698 * @param timestamp Time at which the message arrived
5699 * @param sender A {@link CharSequence} to be used for displaying the name of the
5700 * sender. Should be <code>null</code> for messages by the current user, in which case
5701 * the platform will insert {@link MessagingStyle#getUserDisplayName()}.
5702 * Should be unique amongst all individuals in the conversation, and should be
5703 * consistent during re-posts of the notification.
5704 */
5705 public Message(CharSequence text, long timestamp, CharSequence sender){
5706 mText = text;
5707 mTimestamp = timestamp;
5708 mSender = sender;
5709 }
5710
5711 /**
5712 * Sets a binary blob of data and an associated MIME type for a message. In the case
5713 * where the platform doesn't support the MIME type, the original text provided in the
5714 * constructor will be used.
5715 * @param dataMimeType The MIME type of the content. See
5716 * <a href="{@docRoot}notifications/messaging.html"> for the list of supported MIME
5717 * types on Android and Android Wear.
5718 * @param dataUri The uri containing the content whose type is given by the MIME type.
5719 * <p class="note">
5720 * <ol>
5721 * <li>Notification Listeners including the System UI need permission to access the
5722 * data the Uri points to. The recommended ways to do this are:</li>
5723 * <li>Store the data in your own ContentProvider, making sure that other apps have
5724 * the correct permission to access your provider. The preferred mechanism for
5725 * providing access is to use per-URI permissions which are temporary and only
5726 * grant access to the receiving application. An easy way to create a
5727 * ContentProvider like this is to use the FileProvider helper class.</li>
5728 * <li>Use the system MediaStore. The MediaStore is primarily aimed at video, audio
5729 * and image MIME types, however beginning with Android 3.0 (API level 11) it can
5730 * also store non-media types (see MediaStore.Files for more info). Files can be
5731 * inserted into the MediaStore using scanFile() after which a content:// style
5732 * Uri suitable for sharing is passed to the provided onScanCompleted() callback.
5733 * Note that once added to the system MediaStore the content is accessible to any
5734 * app on the device.</li>
5735 * </ol>
5736 * @return this object for method chaining
5737 */
5738 public Message setData(String dataMimeType, Uri dataUri) {
5739 mDataMimeType = dataMimeType;
5740 mDataUri = dataUri;
5741 return this;
5742 }
5743
Alex Hillsfc737de2016-03-23 17:33:02 -04005744 /**
5745 * Get the text to be used for this message, or the fallback text if a type and content
5746 * Uri have been set
5747 */
5748 public CharSequence getText() {
5749 return mText;
5750 }
5751
5752 /**
5753 * Get the time at which this message arrived
5754 */
5755 public long getTimestamp() {
5756 return mTimestamp;
5757 }
5758
5759 /**
Shane Brennan5a871862017-03-11 13:14:17 -08005760 * Get the extras Bundle for this message.
5761 */
5762 public Bundle getExtras() {
5763 return mExtras;
5764 }
5765
5766 /**
Alex Hillsfc737de2016-03-23 17:33:02 -04005767 * Get the text used to display the contact's name in the messaging experience
5768 */
5769 public CharSequence getSender() {
5770 return mSender;
5771 }
5772
5773 /**
5774 * Get the MIME type of the data pointed to by the Uri
5775 */
5776 public String getDataMimeType() {
5777 return mDataMimeType;
5778 }
5779
5780 /**
5781 * Get the the Uri pointing to the content of the message. Can be null, in which case
5782 * {@see #getText()} is used.
5783 */
5784 public Uri getDataUri() {
5785 return mDataUri;
5786 }
5787
Alex Hillsd9b04d92016-04-11 16:38:16 -04005788 private Bundle toBundle() {
5789 Bundle bundle = new Bundle();
Alex Hillsfc737de2016-03-23 17:33:02 -04005790 if (mText != null) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04005791 bundle.putCharSequence(KEY_TEXT, mText);
Alex Hillsfc737de2016-03-23 17:33:02 -04005792 }
Alex Hillsd9b04d92016-04-11 16:38:16 -04005793 bundle.putLong(KEY_TIMESTAMP, mTimestamp);
Alex Hillsfc737de2016-03-23 17:33:02 -04005794 if (mSender != null) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04005795 bundle.putCharSequence(KEY_SENDER, mSender);
Alex Hillsfc737de2016-03-23 17:33:02 -04005796 }
5797 if (mDataMimeType != null) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04005798 bundle.putString(KEY_DATA_MIME_TYPE, mDataMimeType);
Alex Hillsfc737de2016-03-23 17:33:02 -04005799 }
5800 if (mDataUri != null) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04005801 bundle.putParcelable(KEY_DATA_URI, mDataUri);
Alex Hillsfc737de2016-03-23 17:33:02 -04005802 }
Shane Brennan5a871862017-03-11 13:14:17 -08005803 if (mExtras != null) {
5804 bundle.putBundle(KEY_EXTRAS_BUNDLE, mExtras);
5805 }
Alex Hillsd9b04d92016-04-11 16:38:16 -04005806 return bundle;
Alex Hillsfc737de2016-03-23 17:33:02 -04005807 }
5808
Alex Hillsd9b04d92016-04-11 16:38:16 -04005809 static Bundle[] getBundleArrayForMessages(List<Message> messages) {
5810 Bundle[] bundles = new Bundle[messages.size()];
5811 final int N = messages.size();
5812 for (int i = 0; i < N; i++) {
5813 bundles[i] = messages.get(i).toBundle();
5814 }
5815 return bundles;
5816 }
5817
Adrian Roosdedd1df2016-04-26 16:38:47 -07005818 static List<Message> getMessagesFromBundleArray(Parcelable[] bundles) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04005819 List<Message> messages = new ArrayList<>(bundles.length);
5820 for (int i = 0; i < bundles.length; i++) {
Adrian Roosdedd1df2016-04-26 16:38:47 -07005821 if (bundles[i] instanceof Bundle) {
5822 Message message = getMessageFromBundle((Bundle)bundles[i]);
5823 if (message != null) {
5824 messages.add(message);
5825 }
Alex Hillsd9b04d92016-04-11 16:38:16 -04005826 }
5827 }
5828 return messages;
5829 }
5830
5831 static Message getMessageFromBundle(Bundle bundle) {
5832 try {
Adrian Roosfbddd2c2016-05-13 12:57:20 -07005833 if (!bundle.containsKey(KEY_TEXT) || !bundle.containsKey(KEY_TIMESTAMP)) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04005834 return null;
5835 } else {
5836 Message message = new Message(bundle.getCharSequence(KEY_TEXT),
5837 bundle.getLong(KEY_TIMESTAMP), bundle.getCharSequence(KEY_SENDER));
5838 if (bundle.containsKey(KEY_DATA_MIME_TYPE) &&
5839 bundle.containsKey(KEY_DATA_URI)) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04005840 message.setData(bundle.getString(KEY_DATA_MIME_TYPE),
5841 (Uri) bundle.getParcelable(KEY_DATA_URI));
Alex Hillsfc737de2016-03-23 17:33:02 -04005842 }
Shane Brennan5a871862017-03-11 13:14:17 -08005843 if (bundle.containsKey(KEY_EXTRAS_BUNDLE)) {
5844 message.getExtras().putAll(bundle.getBundle(KEY_EXTRAS_BUNDLE));
5845 }
Alex Hillsd9b04d92016-04-11 16:38:16 -04005846 return message;
5847 }
5848 } catch (ClassCastException e) {
5849 return null;
5850 }
5851 }
Alex Hillsfc737de2016-03-23 17:33:02 -04005852 }
5853 }
5854
5855 /**
Daniel Sandler879c5e02012-04-17 16:46:51 -04005856 * Helper class for generating large-format notifications that include a list of (up to 5) strings.
Joe Malin8d40d042012-11-05 11:36:40 -08005857 *
Robert Ly91c5ce32014-06-08 15:37:00 -07005858 * Here's how you'd set the <code>InboxStyle</code> on a notification:
Daniel Sandler879c5e02012-04-17 16:46:51 -04005859 * <pre class="prettyprint">
Robert Ly91c5ce32014-06-08 15:37:00 -07005860 * Notification notif = new Notification.Builder(mContext)
5861 * .setContentTitle(&quot;5 New mails from &quot; + sender.toString())
5862 * .setContentText(subject)
5863 * .setSmallIcon(R.drawable.new_mail)
5864 * .setLargeIcon(aBitmap)
5865 * .setStyle(new Notification.InboxStyle()
5866 * .addLine(str1)
5867 * .addLine(str2)
5868 * .setContentTitle(&quot;&quot;)
5869 * .setSummaryText(&quot;+3 more&quot;))
5870 * .build();
Daniel Sandler879c5e02012-04-17 16:46:51 -04005871 * </pre>
Joe Malin8d40d042012-11-05 11:36:40 -08005872 *
Daniel Sandler879c5e02012-04-17 16:46:51 -04005873 * @see Notification#bigContentView
5874 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005875 public static class InboxStyle extends Style {
Daniel Sandler879c5e02012-04-17 16:46:51 -04005876 private ArrayList<CharSequence> mTexts = new ArrayList<CharSequence>(5);
5877
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005878 public InboxStyle() {
5879 }
5880
Adrian Roosf5faf9d2016-05-23 13:56:15 -07005881 /**
5882 * @deprecated use {@code InboxStyle()}.
5883 */
5884 @Deprecated
Daniel Sandler879c5e02012-04-17 16:46:51 -04005885 public InboxStyle(Builder builder) {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005886 setBuilder(builder);
Daniel Sandler879c5e02012-04-17 16:46:51 -04005887 }
5888
Chris Wrend6297db2012-05-03 16:20:13 -04005889 /**
5890 * Overrides ContentTitle in the big form of the template.
5891 * This defaults to the value passed to setContentTitle().
5892 */
5893 public InboxStyle setBigContentTitle(CharSequence title) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04005894 internalSetBigContentTitle(safeCharSequence(title));
Chris Wrend6297db2012-05-03 16:20:13 -04005895 return this;
5896 }
5897
5898 /**
5899 * Set the first line of text after the detail section in the big form of the template.
5900 */
5901 public InboxStyle setSummaryText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04005902 internalSetSummaryText(safeCharSequence(cs));
Chris Wrend6297db2012-05-03 16:20:13 -04005903 return this;
5904 }
5905
Chris Wren0bd664d2012-08-01 13:56:56 -04005906 /**
5907 * Append a line to the digest section of the Inbox notification.
5908 */
Daniel Sandler879c5e02012-04-17 16:46:51 -04005909 public InboxStyle addLine(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04005910 mTexts.add(safeCharSequence(cs));
Daniel Sandler879c5e02012-04-17 16:46:51 -04005911 return this;
5912 }
5913
Daniel Sandlerf45564e2013-04-15 15:05:08 -04005914 /**
5915 * @hide
5916 */
5917 public void addExtras(Bundle extras) {
5918 super.addExtras(extras);
Christoph Studer4600f9b2014-07-22 22:44:43 +02005919
Daniel Sandlerf45564e2013-04-15 15:05:08 -04005920 CharSequence[] a = new CharSequence[mTexts.size()];
5921 extras.putCharSequenceArray(EXTRA_TEXT_LINES, mTexts.toArray(a));
5922 }
5923
Christoph Studer4600f9b2014-07-22 22:44:43 +02005924 /**
5925 * @hide
5926 */
5927 @Override
5928 protected void restoreFromExtras(Bundle extras) {
5929 super.restoreFromExtras(extras);
5930
5931 mTexts.clear();
5932 if (extras.containsKey(EXTRA_TEXT_LINES)) {
5933 Collections.addAll(mTexts, extras.getCharSequenceArray(EXTRA_TEXT_LINES));
5934 }
5935 }
5936
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005937 /**
5938 * @hide
5939 */
5940 public RemoteViews makeBigContentView() {
Selim Cinekc848c3a2016-01-13 15:27:30 -08005941 // Remove the content text so it disappears unless you have a summary
Christoph Studer4600f9b2014-07-22 22:44:43 +02005942 // Nasty
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005943 CharSequence oldBuilderContentText = mBuilder.mN.extras.getCharSequence(EXTRA_TEXT);
5944 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, null);
Christoph Studer4600f9b2014-07-22 22:44:43 +02005945
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01005946 RemoteViews contentView = getStandardView(mBuilder.getInboxLayoutResource());
Daniel Sandler619738c2012-06-07 16:33:08 -04005947
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005948 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, oldBuilderContentText);
Christoph Studer4600f9b2014-07-22 22:44:43 +02005949
Chris Wrend6297db2012-05-03 16:20:13 -04005950 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 -04005951 R.id.inbox_text4, R.id.inbox_text5, R.id.inbox_text6};
Chris Wrend6297db2012-05-03 16:20:13 -04005952
Chris Wren4ed80d52012-05-17 09:30:03 -04005953 // Make sure all rows are gone in case we reuse a view.
5954 for (int rowId : rowIds) {
5955 contentView.setViewVisibility(rowId, View.GONE);
5956 }
5957
Daniel Sandler879c5e02012-04-17 16:46:51 -04005958 int i=0;
Selim Cinek07c80172016-04-21 16:40:47 -07005959 int topPadding = mBuilder.mContext.getResources().getDimensionPixelSize(
5960 R.dimen.notification_inbox_item_top_padding);
Selim Cinek247fa012016-02-18 09:50:48 -08005961 boolean first = true;
Selim Cinek07c80172016-04-21 16:40:47 -07005962 int onlyViewId = 0;
5963 int maxRows = rowIds.length;
5964 if (mBuilder.mActions.size() > 0) {
5965 maxRows--;
5966 }
5967 while (i < mTexts.size() && i < maxRows) {
Daniel Sandler879c5e02012-04-17 16:46:51 -04005968 CharSequence str = mTexts.get(i);
Selim Cinek07c80172016-04-21 16:40:47 -07005969 if (!TextUtils.isEmpty(str)) {
Daniel Sandler879c5e02012-04-17 16:46:51 -04005970 contentView.setViewVisibility(rowIds[i], View.VISIBLE);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01005971 contentView.setTextViewText(rowIds[i], mBuilder.processLegacyText(str));
Selim Cinek7b9605b2017-01-19 17:36:00 -08005972 mBuilder.setTextViewColorSecondary(contentView, rowIds[i]);
Selim Cinek07c80172016-04-21 16:40:47 -07005973 contentView.setViewPadding(rowIds[i], 0, topPadding, 0, 0);
Selim Cinek247fa012016-02-18 09:50:48 -08005974 handleInboxImageMargin(contentView, rowIds[i], first);
Selim Cinek07c80172016-04-21 16:40:47 -07005975 if (first) {
5976 onlyViewId = rowIds[i];
5977 } else {
5978 onlyViewId = 0;
5979 }
Selim Cinek247fa012016-02-18 09:50:48 -08005980 first = false;
Daniel Sandler879c5e02012-04-17 16:46:51 -04005981 }
5982 i++;
5983 }
Selim Cinek07c80172016-04-21 16:40:47 -07005984 if (onlyViewId != 0) {
5985 // We only have 1 entry, lets make it look like the normal Text of a Bigtext
5986 topPadding = mBuilder.mContext.getResources().getDimensionPixelSize(
5987 R.dimen.notification_text_margin_top);
5988 contentView.setViewPadding(onlyViewId, 0, topPadding, 0, 0);
5989 }
Selim Cinek1e0bf612015-11-20 15:57:26 -08005990
Daniel Sandler879c5e02012-04-17 16:46:51 -04005991 return contentView;
5992 }
Selim Cinek1e0bf612015-11-20 15:57:26 -08005993
Selim Cinek247fa012016-02-18 09:50:48 -08005994 private void handleInboxImageMargin(RemoteViews contentView, int id, boolean first) {
Selim Cinek1e0bf612015-11-20 15:57:26 -08005995 int endMargin = 0;
Selim Cinek247fa012016-02-18 09:50:48 -08005996 if (first) {
5997 final int max = mBuilder.mN.extras.getInt(EXTRA_PROGRESS_MAX, 0);
5998 final boolean ind = mBuilder.mN.extras.getBoolean(EXTRA_PROGRESS_INDETERMINATE);
5999 boolean hasProgress = max != 0 || ind;
Selim Cinek279fa862016-06-14 10:57:25 -07006000 if (mBuilder.mN.hasLargeIcon() && !hasProgress) {
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006001 endMargin = R.dimen.notification_content_picture_margin;
Selim Cinek247fa012016-02-18 09:50:48 -08006002 }
Selim Cinek1e0bf612015-11-20 15:57:26 -08006003 }
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006004 contentView.setViewLayoutMarginEndDimen(id, endMargin);
Selim Cinek1e0bf612015-11-20 15:57:26 -08006005 }
Daniel Sandler879c5e02012-04-17 16:46:51 -04006006 }
Dan Sandler842dd772014-05-15 09:36:47 -04006007
6008 /**
6009 * Notification style for media playback notifications.
6010 *
6011 * In the expanded form, {@link Notification#bigContentView}, up to 5
6012 * {@link Notification.Action}s specified with
Dan Sandler86647982015-05-13 23:41:13 -04006013 * {@link Notification.Builder#addAction(Action) addAction} will be
Dan Sandler842dd772014-05-15 09:36:47 -04006014 * shown as icon-only pushbuttons, suitable for transport controls. The Bitmap given to
6015 * {@link Notification.Builder#setLargeIcon(android.graphics.Bitmap) setLargeIcon()} will be
6016 * treated as album artwork.
Selim Cinek99104832017-01-25 14:47:33 -08006017 * <p>
Dan Sandler842dd772014-05-15 09:36:47 -04006018 * Unlike the other styles provided here, MediaStyle can also modify the standard-size
6019 * {@link Notification#contentView}; by providing action indices to
Christoph Studerfde6f4d2014-12-12 13:23:26 +01006020 * {@link #setShowActionsInCompactView(int...)} you can promote up to 3 actions to be displayed
Dan Sandler842dd772014-05-15 09:36:47 -04006021 * in the standard view alongside the usual content.
Selim Cinek99104832017-01-25 14:47:33 -08006022 * <p>
Bryan Mawhinney6be8de32014-07-18 10:35:12 +01006023 * Notifications created with MediaStyle will have their category set to
6024 * {@link Notification#CATEGORY_TRANSPORT CATEGORY_TRANSPORT} unless you set a different
6025 * category using {@link Notification.Builder#setCategory(String) setCategory()}.
Selim Cinek99104832017-01-25 14:47:33 -08006026 * <p>
Jeff Browndba34ba2014-06-24 20:46:03 -07006027 * Finally, if you attach a {@link android.media.session.MediaSession.Token} using
6028 * {@link android.app.Notification.MediaStyle#setMediaSession(MediaSession.Token)},
Dan Sandler842dd772014-05-15 09:36:47 -04006029 * the System UI can identify this as a notification representing an active media session
6030 * and respond accordingly (by showing album artwork in the lockscreen, for example).
6031 *
Selim Cinek99104832017-01-25 14:47:33 -08006032 * <p>
6033 * Starting at {@link android.os.Build.VERSION_CODES#O Android O} any notification that has a
6034 * media session attached with {@link #setMediaSession(MediaSession.Token)} will be colorized.
6035 * You can opt-out of this behavior by using {@link Notification.Builder#setColorized(boolean)}.
6036 * <p>
6037 *
Dan Sandler842dd772014-05-15 09:36:47 -04006038 * To use this style with your Notification, feed it to
6039 * {@link Notification.Builder#setStyle(android.app.Notification.Style)} like so:
6040 * <pre class="prettyprint">
6041 * Notification noti = new Notification.Builder()
6042 * .setSmallIcon(R.drawable.ic_stat_player)
Christoph Studere935fe92014-11-24 14:18:06 +01006043 * .setContentTitle(&quot;Track title&quot;)
6044 * .setContentText(&quot;Artist - Album&quot;)
6045 * .setLargeIcon(albumArtBitmap))
Bryan Mawhinney6be8de32014-07-18 10:35:12 +01006046 * .setStyle(<b>new Notification.MediaStyle()</b>
6047 * .setMediaSession(mySession))
Dan Sandler842dd772014-05-15 09:36:47 -04006048 * .build();
6049 * </pre>
6050 *
6051 * @see Notification#bigContentView
Selim Cinek99104832017-01-25 14:47:33 -08006052 * @see Notification.Builder#setColorized(boolean)
Dan Sandler842dd772014-05-15 09:36:47 -04006053 */
6054 public static class MediaStyle extends Style {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006055 static final int MAX_MEDIA_BUTTONS_IN_COMPACT = 3;
Dan Sandler842dd772014-05-15 09:36:47 -04006056 static final int MAX_MEDIA_BUTTONS = 5;
6057
6058 private int[] mActionsToShowInCompact = null;
Jeff Browndba34ba2014-06-24 20:46:03 -07006059 private MediaSession.Token mToken;
Dan Sandler842dd772014-05-15 09:36:47 -04006060
6061 public MediaStyle() {
6062 }
6063
Adrian Roosf5faf9d2016-05-23 13:56:15 -07006064 /**
6065 * @deprecated use {@code MediaStyle()}.
6066 */
6067 @Deprecated
Dan Sandler842dd772014-05-15 09:36:47 -04006068 public MediaStyle(Builder builder) {
6069 setBuilder(builder);
6070 }
6071
6072 /**
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006073 * 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 -04006074 * notification view.
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006075 *
6076 * @param actions the indices of the actions to show in the compact notification view
Dan Sandler842dd772014-05-15 09:36:47 -04006077 */
6078 public MediaStyle setShowActionsInCompactView(int...actions) {
6079 mActionsToShowInCompact = actions;
6080 return this;
6081 }
6082
6083 /**
Jeff Browndba34ba2014-06-24 20:46:03 -07006084 * Attach a {@link android.media.session.MediaSession.Token} to this Notification
6085 * to provide additional playback information and control to the SystemUI.
Dan Sandler842dd772014-05-15 09:36:47 -04006086 */
Jeff Browndba34ba2014-06-24 20:46:03 -07006087 public MediaStyle setMediaSession(MediaSession.Token token) {
Dan Sandler842dd772014-05-15 09:36:47 -04006088 mToken = token;
6089 return this;
6090 }
6091
Christoph Studer4600f9b2014-07-22 22:44:43 +02006092 /**
6093 * @hide
6094 */
Dan Sandler842dd772014-05-15 09:36:47 -04006095 @Override
6096 public Notification buildStyled(Notification wip) {
Christoph Studer4600f9b2014-07-22 22:44:43 +02006097 super.buildStyled(wip);
Bryan Mawhinney6be8de32014-07-18 10:35:12 +01006098 if (wip.category == null) {
6099 wip.category = Notification.CATEGORY_TRANSPORT;
6100 }
Dan Sandler842dd772014-05-15 09:36:47 -04006101 return wip;
6102 }
6103
Christoph Studer4600f9b2014-07-22 22:44:43 +02006104 /**
6105 * @hide
6106 */
6107 @Override
Selim Cinek7d1009b2017-01-25 15:28:28 -08006108 public RemoteViews makeContentView(boolean increasedHeight) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04006109 return makeMediaContentView();
Christoph Studer4600f9b2014-07-22 22:44:43 +02006110 }
6111
6112 /**
6113 * @hide
6114 */
6115 @Override
Julia Reynoldsd9228f12015-10-20 10:37:27 -04006116 public RemoteViews makeBigContentView() {
6117 return makeMediaBigContentView();
Christoph Studer4600f9b2014-07-22 22:44:43 +02006118 }
6119
Selim Cinekcc10bfb2016-02-10 16:24:21 -08006120 /**
6121 * @hide
6122 */
6123 @Override
Selim Cinek87ed69b2017-02-09 15:59:43 -08006124 public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
Selim Cinekcc10bfb2016-02-10 16:24:21 -08006125 RemoteViews expanded = makeMediaBigContentView();
6126 return expanded != null ? expanded : makeMediaContentView();
6127 }
6128
Dan Sandler842dd772014-05-15 09:36:47 -04006129 /** @hide */
6130 @Override
6131 public void addExtras(Bundle extras) {
6132 super.addExtras(extras);
6133
6134 if (mToken != null) {
6135 extras.putParcelable(EXTRA_MEDIA_SESSION, mToken);
6136 }
Bryan Mawhinneye191f902014-07-22 12:50:09 +01006137 if (mActionsToShowInCompact != null) {
6138 extras.putIntArray(EXTRA_COMPACT_ACTIONS, mActionsToShowInCompact);
6139 }
Dan Sandler842dd772014-05-15 09:36:47 -04006140 }
6141
Christoph Studer4600f9b2014-07-22 22:44:43 +02006142 /**
6143 * @hide
6144 */
6145 @Override
6146 protected void restoreFromExtras(Bundle extras) {
6147 super.restoreFromExtras(extras);
6148
6149 if (extras.containsKey(EXTRA_MEDIA_SESSION)) {
6150 mToken = extras.getParcelable(EXTRA_MEDIA_SESSION);
6151 }
6152 if (extras.containsKey(EXTRA_COMPACT_ACTIONS)) {
6153 mActionsToShowInCompact = extras.getIntArray(EXTRA_COMPACT_ACTIONS);
6154 }
6155 }
6156
Selim Cinek5bf069a2015-11-10 19:14:27 -05006157 private RemoteViews generateMediaActionButton(Action action, int color) {
Dan Sandler842dd772014-05-15 09:36:47 -04006158 final boolean tombstone = (action.actionIntent == null);
Selim Cinekf33b1112015-07-15 17:45:11 -07006159 RemoteViews button = new BuilderRemoteViews(mBuilder.mContext.getApplicationInfo(),
Alan Viverette3cb07a462014-06-06 14:19:53 -07006160 R.layout.notification_material_media_action);
Dan Sandler68079d52015-07-22 10:45:30 -04006161 button.setImageViewIcon(R.id.action0, action.getIcon());
Selim Cinek5bf069a2015-11-10 19:14:27 -05006162 button.setDrawableParameters(R.id.action0, false, -1, color, PorterDuff.Mode.SRC_ATOP,
6163 -1);
Dan Sandler842dd772014-05-15 09:36:47 -04006164 if (!tombstone) {
6165 button.setOnClickPendingIntent(R.id.action0, action.actionIntent);
6166 }
6167 button.setContentDescription(R.id.action0, action.title);
6168 return button;
6169 }
6170
6171 private RemoteViews makeMediaContentView() {
6172 RemoteViews view = mBuilder.applyStandardTemplate(
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006173 R.layout.notification_template_material_media, false /* hasProgress */);
Dan Sandler842dd772014-05-15 09:36:47 -04006174
6175 final int numActions = mBuilder.mActions.size();
6176 final int N = mActionsToShowInCompact == null
6177 ? 0
6178 : Math.min(mActionsToShowInCompact.length, MAX_MEDIA_BUTTONS_IN_COMPACT);
6179 if (N > 0) {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006180 view.removeAllViews(com.android.internal.R.id.media_actions);
Dan Sandler842dd772014-05-15 09:36:47 -04006181 for (int i = 0; i < N; i++) {
6182 if (i >= numActions) {
6183 throw new IllegalArgumentException(String.format(
6184 "setShowActionsInCompactView: action %d out of bounds (max %d)",
6185 i, numActions - 1));
6186 }
6187
6188 final Action action = mBuilder.mActions.get(mActionsToShowInCompact[i]);
Selim Cinek5bf069a2015-11-10 19:14:27 -05006189 final RemoteViews button = generateMediaActionButton(action,
Selim Cinek99104832017-01-25 14:47:33 -08006190 getPrimaryHighlightColor());
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006191 view.addView(com.android.internal.R.id.media_actions, button);
Dan Sandler842dd772014-05-15 09:36:47 -04006192 }
6193 }
Selim Cinekfdc738f2016-01-27 20:04:27 -08006194 handleImage(view);
6195 // handle the content margin
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006196 int endMargin = R.dimen.notification_content_margin_end;
Selim Cinek279fa862016-06-14 10:57:25 -07006197 if (mBuilder.mN.hasLargeIcon()) {
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006198 endMargin = R.dimen.notification_content_plus_picture_margin_end;
Selim Cinekfdc738f2016-01-27 20:04:27 -08006199 }
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006200 view.setViewLayoutMarginEndDimen(R.id.notification_main_column, endMargin);
Dan Sandler842dd772014-05-15 09:36:47 -04006201 return view;
6202 }
6203
Selim Cinek99104832017-01-25 14:47:33 -08006204 private int getPrimaryHighlightColor() {
6205 return mBuilder.getPrimaryHighlightColor();
6206 }
6207
Dan Sandler842dd772014-05-15 09:36:47 -04006208 private RemoteViews makeMediaBigContentView() {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006209 final int actionCount = Math.min(mBuilder.mActions.size(), MAX_MEDIA_BUTTONS);
Selim Cinekcc10bfb2016-02-10 16:24:21 -08006210 // Dont add an expanded view if there is no more content to be revealed
6211 int actionsInCompact = mActionsToShowInCompact == null
6212 ? 0
6213 : Math.min(mActionsToShowInCompact.length, MAX_MEDIA_BUTTONS_IN_COMPACT);
Selim Cinek279fa862016-06-14 10:57:25 -07006214 if (!mBuilder.mN.hasLargeIcon() && actionCount <= actionsInCompact) {
Selim Cinekcc10bfb2016-02-10 16:24:21 -08006215 return null;
6216 }
Selim Cinek5bf069a2015-11-10 19:14:27 -05006217 RemoteViews big = mBuilder.applyStandardTemplate(
6218 R.layout.notification_template_material_big_media,
6219 false);
Dan Sandler842dd772014-05-15 09:36:47 -04006220
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006221 if (actionCount > 0) {
6222 big.removeAllViews(com.android.internal.R.id.media_actions);
6223 for (int i = 0; i < actionCount; i++) {
Selim Cinek5bf069a2015-11-10 19:14:27 -05006224 final RemoteViews button = generateMediaActionButton(mBuilder.mActions.get(i),
Selim Cinek99104832017-01-25 14:47:33 -08006225 getPrimaryHighlightColor());
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006226 big.addView(com.android.internal.R.id.media_actions, button);
Dan Sandler842dd772014-05-15 09:36:47 -04006227 }
6228 }
Selim Cinek5bf069a2015-11-10 19:14:27 -05006229 handleImage(big);
Dan Sandler842dd772014-05-15 09:36:47 -04006230 return big;
6231 }
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006232
Selim Cinek5bf069a2015-11-10 19:14:27 -05006233 private void handleImage(RemoteViews contentView) {
Selim Cinek279fa862016-06-14 10:57:25 -07006234 if (mBuilder.mN.hasLargeIcon()) {
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006235 contentView.setViewLayoutMarginEndDimen(R.id.line1, 0);
6236 contentView.setViewLayoutMarginEndDimen(R.id.text, 0);
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006237 }
6238 }
6239
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006240 /**
6241 * @hide
6242 */
6243 @Override
6244 protected boolean hasProgress() {
6245 return false;
6246 }
Dan Sandler842dd772014-05-15 09:36:47 -04006247 }
Griff Hazen61a9e862014-05-22 16:05:19 -07006248
Selim Cinek593610c2016-02-16 18:42:57 -08006249 /**
6250 * Notification style for custom views that are decorated by the system
6251 *
6252 * <p>Instead of providing a notification that is completely custom, a developer can set this
6253 * style and still obtain system decorations like the notification header with the expand
6254 * affordance and actions.
6255 *
6256 * <p>Use {@link android.app.Notification.Builder#setCustomContentView(RemoteViews)},
6257 * {@link android.app.Notification.Builder#setCustomBigContentView(RemoteViews)} and
6258 * {@link android.app.Notification.Builder#setCustomHeadsUpContentView(RemoteViews)} to set the
6259 * corresponding custom views to display.
6260 *
6261 * To use this style with your Notification, feed it to
6262 * {@link Notification.Builder#setStyle(android.app.Notification.Style)} like so:
6263 * <pre class="prettyprint">
6264 * Notification noti = new Notification.Builder()
6265 * .setSmallIcon(R.drawable.ic_stat_player)
6266 * .setLargeIcon(albumArtBitmap))
6267 * .setCustomContentView(contentView);
6268 * .setStyle(<b>new Notification.DecoratedCustomViewStyle()</b>)
6269 * .build();
6270 * </pre>
6271 */
6272 public static class DecoratedCustomViewStyle extends Style {
6273
6274 public DecoratedCustomViewStyle() {
6275 }
6276
Selim Cinek593610c2016-02-16 18:42:57 -08006277 /**
6278 * @hide
6279 */
6280 public boolean displayCustomViewInline() {
6281 return true;
6282 }
6283
6284 /**
6285 * @hide
6286 */
6287 @Override
Selim Cinek7d1009b2017-01-25 15:28:28 -08006288 public RemoteViews makeContentView(boolean increasedHeight) {
Selim Cinek593610c2016-02-16 18:42:57 -08006289 return makeStandardTemplateWithCustomContent(mBuilder.mN.contentView);
6290 }
6291
6292 /**
6293 * @hide
6294 */
6295 @Override
6296 public RemoteViews makeBigContentView() {
6297 return makeDecoratedBigContentView();
6298 }
6299
6300 /**
6301 * @hide
6302 */
6303 @Override
Selim Cinek87ed69b2017-02-09 15:59:43 -08006304 public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
Selim Cinek593610c2016-02-16 18:42:57 -08006305 return makeDecoratedHeadsUpContentView();
6306 }
6307
Selim Cinek593610c2016-02-16 18:42:57 -08006308 private RemoteViews makeDecoratedHeadsUpContentView() {
6309 RemoteViews headsUpContentView = mBuilder.mN.headsUpContentView == null
6310 ? mBuilder.mN.contentView
6311 : mBuilder.mN.headsUpContentView;
6312 if (mBuilder.mActions.size() == 0) {
6313 return makeStandardTemplateWithCustomContent(headsUpContentView);
6314 }
6315 RemoteViews remoteViews = mBuilder.applyStandardTemplateWithActions(
6316 mBuilder.getBigBaseLayoutResource());
Selim Cinek247fa012016-02-18 09:50:48 -08006317 buildIntoRemoteViewContent(remoteViews, headsUpContentView);
Selim Cinek593610c2016-02-16 18:42:57 -08006318 return remoteViews;
6319 }
6320
Selim Cinek593610c2016-02-16 18:42:57 -08006321 private RemoteViews makeStandardTemplateWithCustomContent(RemoteViews customContent) {
6322 RemoteViews remoteViews = mBuilder.applyStandardTemplate(
6323 mBuilder.getBaseLayoutResource());
Selim Cinek247fa012016-02-18 09:50:48 -08006324 buildIntoRemoteViewContent(remoteViews, customContent);
Selim Cinek593610c2016-02-16 18:42:57 -08006325 return remoteViews;
6326 }
6327
Selim Cinek593610c2016-02-16 18:42:57 -08006328 private RemoteViews makeDecoratedBigContentView() {
6329 RemoteViews bigContentView = mBuilder.mN.bigContentView == null
6330 ? mBuilder.mN.contentView
6331 : mBuilder.mN.bigContentView;
6332 if (mBuilder.mActions.size() == 0) {
6333 return makeStandardTemplateWithCustomContent(bigContentView);
6334 }
6335 RemoteViews remoteViews = mBuilder.applyStandardTemplateWithActions(
6336 mBuilder.getBigBaseLayoutResource());
Selim Cinek247fa012016-02-18 09:50:48 -08006337 buildIntoRemoteViewContent(remoteViews, bigContentView);
Selim Cinek593610c2016-02-16 18:42:57 -08006338 return remoteViews;
6339 }
Selim Cinek247fa012016-02-18 09:50:48 -08006340
6341 private void buildIntoRemoteViewContent(RemoteViews remoteViews,
6342 RemoteViews customContent) {
Adrian Roos5081c0d2016-02-26 16:04:19 -08006343 if (customContent != null) {
Selim Cinekf91017e2016-03-14 12:25:09 -07006344 // Need to clone customContent before adding, because otherwise it can no longer be
6345 // parceled independently of remoteViews.
Adrian Roos5081c0d2016-02-26 16:04:19 -08006346 customContent = customContent.clone();
Selim Cinekf91017e2016-03-14 12:25:09 -07006347 remoteViews.removeAllViews(R.id.notification_main_column);
6348 remoteViews.addView(R.id.notification_main_column, customContent);
Adrian Roos5081c0d2016-02-26 16:04:19 -08006349 }
Selim Cinek247fa012016-02-18 09:50:48 -08006350 // also update the end margin if there is an image
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006351 int endMargin = R.dimen.notification_content_margin_end;
Selim Cinek279fa862016-06-14 10:57:25 -07006352 if (mBuilder.mN.hasLargeIcon()) {
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006353 endMargin = R.dimen.notification_content_plus_picture_margin_end;
Selim Cinek247fa012016-02-18 09:50:48 -08006354 }
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006355 remoteViews.setViewLayoutMarginEndDimen(R.id.notification_main_column, endMargin);
Selim Cinek247fa012016-02-18 09:50:48 -08006356 }
Selim Cinek593610c2016-02-16 18:42:57 -08006357 }
6358
Selim Cinek03eb3b72016-02-18 10:39:45 -08006359 /**
6360 * Notification style for media custom views that are decorated by the system
6361 *
6362 * <p>Instead of providing a media notification that is completely custom, a developer can set
6363 * this style and still obtain system decorations like the notification header with the expand
6364 * affordance and actions.
6365 *
6366 * <p>Use {@link android.app.Notification.Builder#setCustomContentView(RemoteViews)},
6367 * {@link android.app.Notification.Builder#setCustomBigContentView(RemoteViews)} and
6368 * {@link android.app.Notification.Builder#setCustomHeadsUpContentView(RemoteViews)} to set the
6369 * corresponding custom views to display.
Selim Cinek99104832017-01-25 14:47:33 -08006370 * <p>
6371 * Contrary to {@link MediaStyle} a developer has to opt-in to the colorizing of the
6372 * notification by using {@link Notification.Builder#setColorized(boolean)}.
6373 * <p>
Selim Cinek03eb3b72016-02-18 10:39:45 -08006374 * To use this style with your Notification, feed it to
6375 * {@link Notification.Builder#setStyle(android.app.Notification.Style)} like so:
6376 * <pre class="prettyprint">
6377 * Notification noti = new Notification.Builder()
6378 * .setSmallIcon(R.drawable.ic_stat_player)
6379 * .setLargeIcon(albumArtBitmap))
6380 * .setCustomContentView(contentView);
6381 * .setStyle(<b>new Notification.DecoratedMediaCustomViewStyle()</b>
6382 * .setMediaSession(mySession))
6383 * .build();
6384 * </pre>
6385 *
6386 * @see android.app.Notification.DecoratedCustomViewStyle
6387 * @see android.app.Notification.MediaStyle
6388 */
6389 public static class DecoratedMediaCustomViewStyle extends MediaStyle {
6390
6391 public DecoratedMediaCustomViewStyle() {
6392 }
6393
Selim Cinek03eb3b72016-02-18 10:39:45 -08006394 /**
6395 * @hide
6396 */
6397 public boolean displayCustomViewInline() {
6398 return true;
6399 }
6400
6401 /**
6402 * @hide
6403 */
6404 @Override
Selim Cinek7d1009b2017-01-25 15:28:28 -08006405 public RemoteViews makeContentView(boolean increasedHeight) {
6406 RemoteViews remoteViews = super.makeContentView(false /* increasedHeight */);
Selim Cinek03eb3b72016-02-18 10:39:45 -08006407 return buildIntoRemoteView(remoteViews, R.id.notification_content_container,
6408 mBuilder.mN.contentView);
6409 }
6410
6411 /**
6412 * @hide
6413 */
6414 @Override
6415 public RemoteViews makeBigContentView() {
6416 RemoteViews customRemoteView = mBuilder.mN.bigContentView != null
6417 ? mBuilder.mN.bigContentView
6418 : mBuilder.mN.contentView;
6419 return makeBigContentViewWithCustomContent(customRemoteView);
6420 }
6421
6422 private RemoteViews makeBigContentViewWithCustomContent(RemoteViews customRemoteView) {
6423 RemoteViews remoteViews = super.makeBigContentView();
6424 if (remoteViews != null) {
6425 return buildIntoRemoteView(remoteViews, R.id.notification_main_column,
6426 customRemoteView);
6427 } else if (customRemoteView != mBuilder.mN.contentView){
Selim Cinek7d1009b2017-01-25 15:28:28 -08006428 remoteViews = super.makeContentView(false /* increasedHeight */);
Selim Cinek03eb3b72016-02-18 10:39:45 -08006429 return buildIntoRemoteView(remoteViews, R.id.notification_content_container,
6430 customRemoteView);
6431 } else {
6432 return null;
6433 }
6434 }
6435
6436 /**
6437 * @hide
6438 */
6439 @Override
Selim Cinek87ed69b2017-02-09 15:59:43 -08006440 public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
Selim Cinek03eb3b72016-02-18 10:39:45 -08006441 RemoteViews customRemoteView = mBuilder.mN.headsUpContentView != null
6442 ? mBuilder.mN.headsUpContentView
6443 : mBuilder.mN.contentView;
6444 return makeBigContentViewWithCustomContent(customRemoteView);
6445 }
6446
6447 private RemoteViews buildIntoRemoteView(RemoteViews remoteViews, int id,
6448 RemoteViews customContent) {
Adrian Roos5081c0d2016-02-26 16:04:19 -08006449 if (customContent != null) {
Selim Cinekf91017e2016-03-14 12:25:09 -07006450 // Need to clone customContent before adding, because otherwise it can no longer be
6451 // parceled independently of remoteViews.
Adrian Roos5081c0d2016-02-26 16:04:19 -08006452 customContent = customContent.clone();
Selim Cinekf91017e2016-03-14 12:25:09 -07006453 remoteViews.removeAllViews(id);
6454 remoteViews.addView(id, customContent);
Adrian Roos5081c0d2016-02-26 16:04:19 -08006455 }
Selim Cinek03eb3b72016-02-18 10:39:45 -08006456 return remoteViews;
6457 }
6458 }
6459
Christoph Studer4600f9b2014-07-22 22:44:43 +02006460 // When adding a new Style subclass here, don't forget to update
6461 // Builder.getNotificationStyleClass.
6462
Griff Hazen61a9e862014-05-22 16:05:19 -07006463 /**
6464 * Extender interface for use with {@link Builder#extend}. Extenders may be used to add
6465 * metadata or change options on a notification builder.
6466 */
6467 public interface Extender {
6468 /**
6469 * Apply this extender to a notification builder.
6470 * @param builder the builder to be modified.
6471 * @return the build object for chaining.
6472 */
6473 public Builder extend(Builder builder);
6474 }
6475
6476 /**
6477 * Helper class to add wearable extensions to notifications.
6478 * <p class="note"> See
6479 * <a href="{@docRoot}wear/notifications/creating.html">Creating Notifications
6480 * for Android Wear</a> for more information on how to use this class.
6481 * <p>
6482 * To create a notification with wearable extensions:
6483 * <ol>
6484 * <li>Create a {@link android.app.Notification.Builder}, setting any desired
6485 * properties.
6486 * <li>Create a {@link android.app.Notification.WearableExtender}.
6487 * <li>Set wearable-specific properties using the
6488 * {@code add} and {@code set} methods of {@link android.app.Notification.WearableExtender}.
6489 * <li>Call {@link android.app.Notification.Builder#extend} to apply the extensions to a
6490 * notification.
6491 * <li>Post the notification to the notification system with the
6492 * {@code NotificationManager.notify(...)} methods.
6493 * </ol>
6494 *
6495 * <pre class="prettyprint">
6496 * Notification notif = new Notification.Builder(mContext)
6497 * .setContentTitle(&quot;New mail from &quot; + sender.toString())
6498 * .setContentText(subject)
6499 * .setSmallIcon(R.drawable.new_mail)
6500 * .extend(new Notification.WearableExtender()
6501 * .setContentIcon(R.drawable.new_mail))
6502 * .build();
6503 * NotificationManager notificationManger =
6504 * (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
6505 * notificationManger.notify(0, notif);</pre>
6506 *
6507 * <p>Wearable extensions can be accessed on an existing notification by using the
6508 * {@code WearableExtender(Notification)} constructor,
6509 * and then using the {@code get} methods to access values.
6510 *
6511 * <pre class="prettyprint">
6512 * Notification.WearableExtender wearableExtender = new Notification.WearableExtender(
6513 * notification);
Griff Hazen14f57992014-05-26 09:07:14 -07006514 * List&lt;Notification&gt; pages = wearableExtender.getPages();</pre>
Griff Hazen61a9e862014-05-22 16:05:19 -07006515 */
6516 public static final class WearableExtender implements Extender {
6517 /**
6518 * Sentinel value for an action index that is unset.
6519 */
6520 public static final int UNSET_ACTION_INDEX = -1;
6521
6522 /**
6523 * Size value for use with {@link #setCustomSizePreset} to show this notification with
6524 * default sizing.
6525 * <p>For custom display notifications created using {@link #setDisplayIntent},
Paul Soulosaa4f4bf2015-08-04 11:59:45 -07006526 * the default is {@link #SIZE_MEDIUM}. All other notifications size automatically based
Griff Hazen61a9e862014-05-22 16:05:19 -07006527 * on their content.
6528 */
6529 public static final int SIZE_DEFAULT = 0;
6530
6531 /**
6532 * Size value for use with {@link #setCustomSizePreset} to show this notification
6533 * with an extra small size.
6534 * <p>This value is only applicable for custom display notifications created using
6535 * {@link #setDisplayIntent}.
6536 */
6537 public static final int SIZE_XSMALL = 1;
6538
6539 /**
6540 * Size value for use with {@link #setCustomSizePreset} to show this notification
6541 * with a small size.
6542 * <p>This value is only applicable for custom display notifications created using
6543 * {@link #setDisplayIntent}.
6544 */
6545 public static final int SIZE_SMALL = 2;
6546
6547 /**
6548 * Size value for use with {@link #setCustomSizePreset} to show this notification
6549 * with a medium size.
6550 * <p>This value is only applicable for custom display notifications created using
6551 * {@link #setDisplayIntent}.
6552 */
6553 public static final int SIZE_MEDIUM = 3;
6554
6555 /**
6556 * Size value for use with {@link #setCustomSizePreset} to show this notification
6557 * with a large size.
6558 * <p>This value is only applicable for custom display notifications created using
6559 * {@link #setDisplayIntent}.
6560 */
6561 public static final int SIZE_LARGE = 4;
6562
Griff Hazend5f11f92014-05-27 15:40:09 -07006563 /**
6564 * Size value for use with {@link #setCustomSizePreset} to show this notification
6565 * full screen.
6566 * <p>This value is only applicable for custom display notifications created using
6567 * {@link #setDisplayIntent}.
6568 */
6569 public static final int SIZE_FULL_SCREEN = 5;
6570
Griff Hazen5f2edfc2014-09-29 16:28:44 -07006571 /**
6572 * Sentinel value for use with {@link #setHintScreenTimeout} to keep the screen on for a
6573 * short amount of time when this notification is displayed on the screen. This
6574 * is the default value.
6575 */
6576 public static final int SCREEN_TIMEOUT_SHORT = 0;
6577
6578 /**
6579 * Sentinel value for use with {@link #setHintScreenTimeout} to keep the screen on
6580 * for a longer amount of time when this notification is displayed on the screen.
6581 */
6582 public static final int SCREEN_TIMEOUT_LONG = -1;
6583
Griff Hazen61a9e862014-05-22 16:05:19 -07006584 /** Notification extra which contains wearable extensions */
6585 private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS";
6586
Pete Gastaf6781d2014-10-07 15:17:05 -04006587 // Keys within EXTRA_WEARABLE_EXTENSIONS for wearable options.
Griff Hazen61a9e862014-05-22 16:05:19 -07006588 private static final String KEY_ACTIONS = "actions";
6589 private static final String KEY_FLAGS = "flags";
6590 private static final String KEY_DISPLAY_INTENT = "displayIntent";
6591 private static final String KEY_PAGES = "pages";
6592 private static final String KEY_BACKGROUND = "background";
6593 private static final String KEY_CONTENT_ICON = "contentIcon";
6594 private static final String KEY_CONTENT_ICON_GRAVITY = "contentIconGravity";
6595 private static final String KEY_CONTENT_ACTION_INDEX = "contentActionIndex";
6596 private static final String KEY_CUSTOM_SIZE_PRESET = "customSizePreset";
6597 private static final String KEY_CUSTOM_CONTENT_HEIGHT = "customContentHeight";
6598 private static final String KEY_GRAVITY = "gravity";
Griff Hazen5f2edfc2014-09-29 16:28:44 -07006599 private static final String KEY_HINT_SCREEN_TIMEOUT = "hintScreenTimeout";
Nadia Benbernou948627e2016-04-14 14:41:08 -04006600 private static final String KEY_DISMISSAL_ID = "dismissalId";
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04006601 private static final String KEY_BRIDGE_TAG = "bridgeTag";
Griff Hazen61a9e862014-05-22 16:05:19 -07006602
6603 // Flags bitwise-ored to mFlags
6604 private static final int FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE = 0x1;
6605 private static final int FLAG_HINT_HIDE_ICON = 1 << 1;
6606 private static final int FLAG_HINT_SHOW_BACKGROUND_ONLY = 1 << 2;
6607 private static final int FLAG_START_SCROLL_BOTTOM = 1 << 3;
Griff Hazen5f2edfc2014-09-29 16:28:44 -07006608 private static final int FLAG_HINT_AVOID_BACKGROUND_CLIPPING = 1 << 4;
Alex Hills4bcb06b2016-04-05 14:26:25 -04006609 private static final int FLAG_BIG_PICTURE_AMBIENT = 1 << 5;
Alex Hills9ab3a232016-04-05 14:54:56 -04006610 private static final int FLAG_HINT_CONTENT_INTENT_LAUNCHES_ACTIVITY = 1 << 6;
Griff Hazen61a9e862014-05-22 16:05:19 -07006611
6612 // Default value for flags integer
6613 private static final int DEFAULT_FLAGS = FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE;
6614
6615 private static final int DEFAULT_CONTENT_ICON_GRAVITY = Gravity.END;
6616 private static final int DEFAULT_GRAVITY = Gravity.BOTTOM;
6617
6618 private ArrayList<Action> mActions = new ArrayList<Action>();
6619 private int mFlags = DEFAULT_FLAGS;
6620 private PendingIntent mDisplayIntent;
6621 private ArrayList<Notification> mPages = new ArrayList<Notification>();
6622 private Bitmap mBackground;
6623 private int mContentIcon;
6624 private int mContentIconGravity = DEFAULT_CONTENT_ICON_GRAVITY;
6625 private int mContentActionIndex = UNSET_ACTION_INDEX;
6626 private int mCustomSizePreset = SIZE_DEFAULT;
6627 private int mCustomContentHeight;
6628 private int mGravity = DEFAULT_GRAVITY;
Griff Hazen5f2edfc2014-09-29 16:28:44 -07006629 private int mHintScreenTimeout;
Nadia Benbernou948627e2016-04-14 14:41:08 -04006630 private String mDismissalId;
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04006631 private String mBridgeTag;
Griff Hazen61a9e862014-05-22 16:05:19 -07006632
6633 /**
6634 * Create a {@link android.app.Notification.WearableExtender} with default
6635 * options.
6636 */
6637 public WearableExtender() {
6638 }
6639
6640 public WearableExtender(Notification notif) {
6641 Bundle wearableBundle = notif.extras.getBundle(EXTRA_WEARABLE_EXTENSIONS);
6642 if (wearableBundle != null) {
6643 List<Action> actions = wearableBundle.getParcelableArrayList(KEY_ACTIONS);
6644 if (actions != null) {
6645 mActions.addAll(actions);
6646 }
6647
6648 mFlags = wearableBundle.getInt(KEY_FLAGS, DEFAULT_FLAGS);
6649 mDisplayIntent = wearableBundle.getParcelable(KEY_DISPLAY_INTENT);
6650
6651 Notification[] pages = getNotificationArrayFromBundle(
6652 wearableBundle, KEY_PAGES);
6653 if (pages != null) {
6654 Collections.addAll(mPages, pages);
6655 }
6656
6657 mBackground = wearableBundle.getParcelable(KEY_BACKGROUND);
6658 mContentIcon = wearableBundle.getInt(KEY_CONTENT_ICON);
6659 mContentIconGravity = wearableBundle.getInt(KEY_CONTENT_ICON_GRAVITY,
6660 DEFAULT_CONTENT_ICON_GRAVITY);
6661 mContentActionIndex = wearableBundle.getInt(KEY_CONTENT_ACTION_INDEX,
6662 UNSET_ACTION_INDEX);
6663 mCustomSizePreset = wearableBundle.getInt(KEY_CUSTOM_SIZE_PRESET,
6664 SIZE_DEFAULT);
6665 mCustomContentHeight = wearableBundle.getInt(KEY_CUSTOM_CONTENT_HEIGHT);
6666 mGravity = wearableBundle.getInt(KEY_GRAVITY, DEFAULT_GRAVITY);
Griff Hazen5f2edfc2014-09-29 16:28:44 -07006667 mHintScreenTimeout = wearableBundle.getInt(KEY_HINT_SCREEN_TIMEOUT);
Nadia Benbernou948627e2016-04-14 14:41:08 -04006668 mDismissalId = wearableBundle.getString(KEY_DISMISSAL_ID);
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04006669 mBridgeTag = wearableBundle.getString(KEY_BRIDGE_TAG);
Griff Hazen61a9e862014-05-22 16:05:19 -07006670 }
6671 }
6672
6673 /**
6674 * Apply wearable extensions to a notification that is being built. This is typically
6675 * called by the {@link android.app.Notification.Builder#extend} method of
6676 * {@link android.app.Notification.Builder}.
6677 */
6678 @Override
6679 public Notification.Builder extend(Notification.Builder builder) {
6680 Bundle wearableBundle = new Bundle();
6681
6682 if (!mActions.isEmpty()) {
6683 wearableBundle.putParcelableArrayList(KEY_ACTIONS, mActions);
6684 }
6685 if (mFlags != DEFAULT_FLAGS) {
6686 wearableBundle.putInt(KEY_FLAGS, mFlags);
6687 }
6688 if (mDisplayIntent != null) {
6689 wearableBundle.putParcelable(KEY_DISPLAY_INTENT, mDisplayIntent);
6690 }
6691 if (!mPages.isEmpty()) {
6692 wearableBundle.putParcelableArray(KEY_PAGES, mPages.toArray(
6693 new Notification[mPages.size()]));
6694 }
6695 if (mBackground != null) {
6696 wearableBundle.putParcelable(KEY_BACKGROUND, mBackground);
6697 }
6698 if (mContentIcon != 0) {
6699 wearableBundle.putInt(KEY_CONTENT_ICON, mContentIcon);
6700 }
6701 if (mContentIconGravity != DEFAULT_CONTENT_ICON_GRAVITY) {
6702 wearableBundle.putInt(KEY_CONTENT_ICON_GRAVITY, mContentIconGravity);
6703 }
6704 if (mContentActionIndex != UNSET_ACTION_INDEX) {
6705 wearableBundle.putInt(KEY_CONTENT_ACTION_INDEX,
6706 mContentActionIndex);
6707 }
6708 if (mCustomSizePreset != SIZE_DEFAULT) {
6709 wearableBundle.putInt(KEY_CUSTOM_SIZE_PRESET, mCustomSizePreset);
6710 }
6711 if (mCustomContentHeight != 0) {
6712 wearableBundle.putInt(KEY_CUSTOM_CONTENT_HEIGHT, mCustomContentHeight);
6713 }
6714 if (mGravity != DEFAULT_GRAVITY) {
6715 wearableBundle.putInt(KEY_GRAVITY, mGravity);
6716 }
Griff Hazen5f2edfc2014-09-29 16:28:44 -07006717 if (mHintScreenTimeout != 0) {
6718 wearableBundle.putInt(KEY_HINT_SCREEN_TIMEOUT, mHintScreenTimeout);
6719 }
Nadia Benbernou948627e2016-04-14 14:41:08 -04006720 if (mDismissalId != null) {
6721 wearableBundle.putString(KEY_DISMISSAL_ID, mDismissalId);
6722 }
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04006723 if (mBridgeTag != null) {
6724 wearableBundle.putString(KEY_BRIDGE_TAG, mBridgeTag);
6725 }
Griff Hazen61a9e862014-05-22 16:05:19 -07006726
6727 builder.getExtras().putBundle(EXTRA_WEARABLE_EXTENSIONS, wearableBundle);
6728 return builder;
6729 }
6730
6731 @Override
6732 public WearableExtender clone() {
6733 WearableExtender that = new WearableExtender();
6734 that.mActions = new ArrayList<Action>(this.mActions);
6735 that.mFlags = this.mFlags;
6736 that.mDisplayIntent = this.mDisplayIntent;
6737 that.mPages = new ArrayList<Notification>(this.mPages);
6738 that.mBackground = this.mBackground;
6739 that.mContentIcon = this.mContentIcon;
6740 that.mContentIconGravity = this.mContentIconGravity;
6741 that.mContentActionIndex = this.mContentActionIndex;
6742 that.mCustomSizePreset = this.mCustomSizePreset;
6743 that.mCustomContentHeight = this.mCustomContentHeight;
6744 that.mGravity = this.mGravity;
Griff Hazen5f2edfc2014-09-29 16:28:44 -07006745 that.mHintScreenTimeout = this.mHintScreenTimeout;
Nadia Benbernou948627e2016-04-14 14:41:08 -04006746 that.mDismissalId = this.mDismissalId;
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04006747 that.mBridgeTag = this.mBridgeTag;
Griff Hazen61a9e862014-05-22 16:05:19 -07006748 return that;
6749 }
6750
6751 /**
6752 * Add a wearable action to this notification.
6753 *
6754 * <p>When wearable actions are added using this method, the set of actions that
6755 * show on a wearable device splits from devices that only show actions added
6756 * using {@link android.app.Notification.Builder#addAction}. This allows for customization
6757 * of which actions display on different devices.
6758 *
6759 * @param action the action to add to this notification
6760 * @return this object for method chaining
6761 * @see android.app.Notification.Action
6762 */
6763 public WearableExtender addAction(Action action) {
6764 mActions.add(action);
6765 return this;
6766 }
6767
6768 /**
6769 * Adds wearable actions to this notification.
6770 *
6771 * <p>When wearable actions are added using this method, the set of actions that
6772 * show on a wearable device splits from devices that only show actions added
6773 * using {@link android.app.Notification.Builder#addAction}. This allows for customization
6774 * of which actions display on different devices.
6775 *
6776 * @param actions the actions to add to this notification
6777 * @return this object for method chaining
6778 * @see android.app.Notification.Action
6779 */
6780 public WearableExtender addActions(List<Action> actions) {
6781 mActions.addAll(actions);
6782 return this;
6783 }
6784
6785 /**
6786 * Clear all wearable actions present on this builder.
6787 * @return this object for method chaining.
6788 * @see #addAction
6789 */
6790 public WearableExtender clearActions() {
6791 mActions.clear();
6792 return this;
6793 }
6794
6795 /**
6796 * Get the wearable actions present on this notification.
6797 */
6798 public List<Action> getActions() {
6799 return mActions;
6800 }
6801
6802 /**
6803 * Set an intent to launch inside of an activity view when displaying
Griff Hazen14f57992014-05-26 09:07:14 -07006804 * this notification. The {@link PendingIntent} provided should be for an activity.
6805 *
6806 * <pre class="prettyprint">
6807 * Intent displayIntent = new Intent(context, MyDisplayActivity.class);
6808 * PendingIntent displayPendingIntent = PendingIntent.getActivity(context,
6809 * 0, displayIntent, PendingIntent.FLAG_UPDATE_CURRENT);
6810 * Notification notif = new Notification.Builder(context)
6811 * .extend(new Notification.WearableExtender()
6812 * .setDisplayIntent(displayPendingIntent)
6813 * .setCustomSizePreset(Notification.WearableExtender.SIZE_MEDIUM))
6814 * .build();</pre>
6815 *
6816 * <p>The activity to launch needs to allow embedding, must be exported, and
Griff Hazen831ca9d2014-06-17 00:38:38 -07006817 * should have an empty task affinity. It is also recommended to use the device
6818 * default light theme.
Griff Hazen14f57992014-05-26 09:07:14 -07006819 *
6820 * <p>Example AndroidManifest.xml entry:
6821 * <pre class="prettyprint">
6822 * &lt;activity android:name=&quot;com.example.MyDisplayActivity&quot;
6823 * android:exported=&quot;true&quot;
6824 * android:allowEmbedded=&quot;true&quot;
Griff Hazen831ca9d2014-06-17 00:38:38 -07006825 * android:taskAffinity=&quot;&quot;
6826 * android:theme=&quot;@android:style/Theme.DeviceDefault.Light&quot; /&gt;</pre>
Griff Hazen61a9e862014-05-22 16:05:19 -07006827 *
6828 * @param intent the {@link PendingIntent} for an activity
6829 * @return this object for method chaining
6830 * @see android.app.Notification.WearableExtender#getDisplayIntent
6831 */
6832 public WearableExtender setDisplayIntent(PendingIntent intent) {
6833 mDisplayIntent = intent;
6834 return this;
6835 }
6836
6837 /**
6838 * Get the intent to launch inside of an activity view when displaying this
6839 * notification. This {@code PendingIntent} should be for an activity.
6840 */
6841 public PendingIntent getDisplayIntent() {
6842 return mDisplayIntent;
6843 }
6844
6845 /**
6846 * Add an additional page of content to display with this notification. The current
6847 * notification forms the first page, and pages added using this function form
6848 * subsequent pages. This field can be used to separate a notification into multiple
6849 * sections.
6850 *
6851 * @param page the notification to add as another page
6852 * @return this object for method chaining
6853 * @see android.app.Notification.WearableExtender#getPages
6854 */
6855 public WearableExtender addPage(Notification page) {
6856 mPages.add(page);
6857 return this;
6858 }
6859
6860 /**
6861 * Add additional pages of content to display with this notification. The current
6862 * notification forms the first page, and pages added using this function form
6863 * subsequent pages. This field can be used to separate a notification into multiple
6864 * sections.
6865 *
6866 * @param pages a list of notifications
6867 * @return this object for method chaining
6868 * @see android.app.Notification.WearableExtender#getPages
6869 */
6870 public WearableExtender addPages(List<Notification> pages) {
6871 mPages.addAll(pages);
6872 return this;
6873 }
6874
6875 /**
6876 * Clear all additional pages present on this builder.
6877 * @return this object for method chaining.
6878 * @see #addPage
6879 */
6880 public WearableExtender clearPages() {
6881 mPages.clear();
6882 return this;
6883 }
6884
6885 /**
6886 * Get the array of additional pages of content for displaying this notification. The
6887 * current notification forms the first page, and elements within this array form
6888 * subsequent pages. This field can be used to separate a notification into multiple
6889 * sections.
6890 * @return the pages for this notification
6891 */
6892 public List<Notification> getPages() {
6893 return mPages;
6894 }
6895
6896 /**
6897 * Set a background image to be displayed behind the notification content.
6898 * Contrary to the {@link android.app.Notification.BigPictureStyle}, this background
6899 * will work with any notification style.
6900 *
6901 * @param background the background bitmap
6902 * @return this object for method chaining
6903 * @see android.app.Notification.WearableExtender#getBackground
6904 */
6905 public WearableExtender setBackground(Bitmap background) {
6906 mBackground = background;
6907 return this;
6908 }
6909
6910 /**
6911 * Get a background image to be displayed behind the notification content.
6912 * Contrary to the {@link android.app.Notification.BigPictureStyle}, this background
6913 * will work with any notification style.
6914 *
6915 * @return the background image
6916 * @see android.app.Notification.WearableExtender#setBackground
6917 */
6918 public Bitmap getBackground() {
6919 return mBackground;
6920 }
6921
6922 /**
6923 * Set an icon that goes with the content of this notification.
6924 */
6925 public WearableExtender setContentIcon(int icon) {
6926 mContentIcon = icon;
6927 return this;
6928 }
6929
6930 /**
6931 * Get an icon that goes with the content of this notification.
6932 */
6933 public int getContentIcon() {
6934 return mContentIcon;
6935 }
6936
6937 /**
6938 * Set the gravity that the content icon should have within the notification display.
6939 * Supported values include {@link android.view.Gravity#START} and
6940 * {@link android.view.Gravity#END}. The default value is {@link android.view.Gravity#END}.
6941 * @see #setContentIcon
6942 */
6943 public WearableExtender setContentIconGravity(int contentIconGravity) {
6944 mContentIconGravity = contentIconGravity;
6945 return this;
6946 }
6947
6948 /**
6949 * Get the gravity that the content icon should have within the notification display.
6950 * Supported values include {@link android.view.Gravity#START} and
6951 * {@link android.view.Gravity#END}. The default value is {@link android.view.Gravity#END}.
6952 * @see #getContentIcon
6953 */
6954 public int getContentIconGravity() {
6955 return mContentIconGravity;
6956 }
6957
6958 /**
6959 * Set an action from this notification's actions to be clickable with the content of
Griff Hazen14f57992014-05-26 09:07:14 -07006960 * this notification. This action will no longer display separately from the
6961 * notification's content.
6962 *
Griff Hazenca48d352014-05-28 22:37:13 -07006963 * <p>For notifications with multiple pages, child pages can also have content actions
Griff Hazen14f57992014-05-26 09:07:14 -07006964 * set, although the list of available actions comes from the main notification and not
6965 * from the child page's notification.
6966 *
6967 * @param actionIndex The index of the action to hoist onto the current notification page.
6968 * If wearable actions were added to the main notification, this index
6969 * will apply to that list, otherwise it will apply to the regular
6970 * actions list.
Griff Hazen61a9e862014-05-22 16:05:19 -07006971 */
6972 public WearableExtender setContentAction(int actionIndex) {
6973 mContentActionIndex = actionIndex;
6974 return this;
6975 }
6976
6977 /**
Griff Hazenca48d352014-05-28 22:37:13 -07006978 * Get the index of the notification action, if any, that was specified as being clickable
6979 * with the content of this notification. This action will no longer display separately
Griff Hazen14f57992014-05-26 09:07:14 -07006980 * from the notification's content.
Griff Hazen61a9e862014-05-22 16:05:19 -07006981 *
Griff Hazenca48d352014-05-28 22:37:13 -07006982 * <p>For notifications with multiple pages, child pages can also have content actions
Griff Hazen14f57992014-05-26 09:07:14 -07006983 * set, although the list of available actions comes from the main notification and not
6984 * from the child page's notification.
6985 *
6986 * <p>If wearable specific actions were added to the main notification, this index will
6987 * apply to that list, otherwise it will apply to the regular actions list.
Griff Hazenca48d352014-05-28 22:37:13 -07006988 *
6989 * @return the action index or {@link #UNSET_ACTION_INDEX} if no action was selected.
Griff Hazen61a9e862014-05-22 16:05:19 -07006990 */
6991 public int getContentAction() {
6992 return mContentActionIndex;
6993 }
6994
6995 /**
6996 * Set the gravity that this notification should have within the available viewport space.
6997 * Supported values include {@link android.view.Gravity#TOP},
6998 * {@link android.view.Gravity#CENTER_VERTICAL} and {@link android.view.Gravity#BOTTOM}.
6999 * The default value is {@link android.view.Gravity#BOTTOM}.
7000 */
7001 public WearableExtender setGravity(int gravity) {
7002 mGravity = gravity;
7003 return this;
7004 }
7005
7006 /**
7007 * Get the gravity that this notification should have within the available viewport space.
7008 * Supported values include {@link android.view.Gravity#TOP},
7009 * {@link android.view.Gravity#CENTER_VERTICAL} and {@link android.view.Gravity#BOTTOM}.
7010 * The default value is {@link android.view.Gravity#BOTTOM}.
7011 */
7012 public int getGravity() {
7013 return mGravity;
7014 }
7015
7016 /**
7017 * Set the custom size preset for the display of this notification out of the available
7018 * presets found in {@link android.app.Notification.WearableExtender}, e.g.
7019 * {@link #SIZE_LARGE}.
7020 * <p>Some custom size presets are only applicable for custom display notifications created
7021 * using {@link android.app.Notification.WearableExtender#setDisplayIntent}. Check the
7022 * documentation for the preset in question. See also
7023 * {@link #setCustomContentHeight} and {@link #getCustomSizePreset}.
7024 */
7025 public WearableExtender setCustomSizePreset(int sizePreset) {
7026 mCustomSizePreset = sizePreset;
7027 return this;
7028 }
7029
7030 /**
7031 * Get the custom size preset for the display of this notification out of the available
7032 * presets found in {@link android.app.Notification.WearableExtender}, e.g.
7033 * {@link #SIZE_LARGE}.
7034 * <p>Some custom size presets are only applicable for custom display notifications created
7035 * using {@link #setDisplayIntent}. Check the documentation for the preset in question.
7036 * See also {@link #setCustomContentHeight} and {@link #setCustomSizePreset}.
7037 */
7038 public int getCustomSizePreset() {
7039 return mCustomSizePreset;
7040 }
7041
7042 /**
7043 * Set the custom height in pixels for the display of this notification's content.
7044 * <p>This option is only available for custom display notifications created
7045 * using {@link android.app.Notification.WearableExtender#setDisplayIntent}. See also
7046 * {@link android.app.Notification.WearableExtender#setCustomSizePreset} and
7047 * {@link #getCustomContentHeight}.
7048 */
7049 public WearableExtender setCustomContentHeight(int height) {
7050 mCustomContentHeight = height;
7051 return this;
7052 }
7053
7054 /**
7055 * Get the custom height in pixels for the display of this notification's content.
7056 * <p>This option is only available for custom display notifications created
7057 * using {@link #setDisplayIntent}. See also {@link #setCustomSizePreset} and
7058 * {@link #setCustomContentHeight}.
7059 */
7060 public int getCustomContentHeight() {
7061 return mCustomContentHeight;
7062 }
7063
7064 /**
7065 * Set whether the scrolling position for the contents of this notification should start
7066 * at the bottom of the contents instead of the top when the contents are too long to
7067 * display within the screen. Default is false (start scroll at the top).
7068 */
7069 public WearableExtender setStartScrollBottom(boolean startScrollBottom) {
7070 setFlag(FLAG_START_SCROLL_BOTTOM, startScrollBottom);
7071 return this;
7072 }
7073
7074 /**
7075 * Get whether the scrolling position for the contents of this notification should start
7076 * at the bottom of the contents instead of the top when the contents are too long to
7077 * display within the screen. Default is false (start scroll at the top).
7078 */
7079 public boolean getStartScrollBottom() {
7080 return (mFlags & FLAG_START_SCROLL_BOTTOM) != 0;
7081 }
7082
7083 /**
7084 * Set whether the content intent is available when the wearable device is not connected
7085 * to a companion device. The user can still trigger this intent when the wearable device
7086 * is offline, but a visual hint will indicate that the content intent may not be available.
7087 * Defaults to true.
7088 */
7089 public WearableExtender setContentIntentAvailableOffline(
7090 boolean contentIntentAvailableOffline) {
7091 setFlag(FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE, contentIntentAvailableOffline);
7092 return this;
7093 }
7094
7095 /**
7096 * Get whether the content intent is available when the wearable device is not connected
7097 * to a companion device. The user can still trigger this intent when the wearable device
7098 * is offline, but a visual hint will indicate that the content intent may not be available.
7099 * Defaults to true.
7100 */
7101 public boolean getContentIntentAvailableOffline() {
7102 return (mFlags & FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE) != 0;
7103 }
7104
7105 /**
7106 * Set a hint that this notification's icon should not be displayed.
7107 * @param hintHideIcon {@code true} to hide the icon, {@code false} otherwise.
7108 * @return this object for method chaining
7109 */
7110 public WearableExtender setHintHideIcon(boolean hintHideIcon) {
7111 setFlag(FLAG_HINT_HIDE_ICON, hintHideIcon);
7112 return this;
7113 }
7114
7115 /**
7116 * Get a hint that this notification's icon should not be displayed.
7117 * @return {@code true} if this icon should not be displayed, false otherwise.
7118 * The default value is {@code false} if this was never set.
7119 */
7120 public boolean getHintHideIcon() {
7121 return (mFlags & FLAG_HINT_HIDE_ICON) != 0;
7122 }
7123
7124 /**
7125 * Set a visual hint that only the background image of this notification should be
7126 * displayed, and other semantic content should be hidden. This hint is only applicable
7127 * to sub-pages added using {@link #addPage}.
7128 */
7129 public WearableExtender setHintShowBackgroundOnly(boolean hintShowBackgroundOnly) {
7130 setFlag(FLAG_HINT_SHOW_BACKGROUND_ONLY, hintShowBackgroundOnly);
7131 return this;
7132 }
7133
7134 /**
7135 * Get a visual hint that only the background image of this notification should be
7136 * displayed, and other semantic content should be hidden. This hint is only applicable
7137 * to sub-pages added using {@link android.app.Notification.WearableExtender#addPage}.
7138 */
7139 public boolean getHintShowBackgroundOnly() {
7140 return (mFlags & FLAG_HINT_SHOW_BACKGROUND_ONLY) != 0;
7141 }
7142
Griff Hazen5f2edfc2014-09-29 16:28:44 -07007143 /**
Griff Hazen9c5be4e2014-11-17 17:47:50 -08007144 * Set a hint that this notification's background should not be clipped if possible,
7145 * and should instead be resized to fully display on the screen, retaining the aspect
7146 * ratio of the image. This can be useful for images like barcodes or qr codes.
Griff Hazen5f2edfc2014-09-29 16:28:44 -07007147 * @param hintAvoidBackgroundClipping {@code true} to avoid clipping if possible.
7148 * @return this object for method chaining
7149 */
7150 public WearableExtender setHintAvoidBackgroundClipping(
7151 boolean hintAvoidBackgroundClipping) {
7152 setFlag(FLAG_HINT_AVOID_BACKGROUND_CLIPPING, hintAvoidBackgroundClipping);
7153 return this;
7154 }
7155
7156 /**
Griff Hazen9c5be4e2014-11-17 17:47:50 -08007157 * Get a hint that this notification's background should not be clipped if possible,
7158 * and should instead be resized to fully display on the screen, retaining the aspect
7159 * ratio of the image. This can be useful for images like barcodes or qr codes.
Griff Hazen5f2edfc2014-09-29 16:28:44 -07007160 * @return {@code true} if it's ok if the background is clipped on the screen, false
7161 * otherwise. The default value is {@code false} if this was never set.
7162 */
7163 public boolean getHintAvoidBackgroundClipping() {
7164 return (mFlags & FLAG_HINT_AVOID_BACKGROUND_CLIPPING) != 0;
7165 }
7166
7167 /**
7168 * Set a hint that the screen should remain on for at least this duration when
7169 * this notification is displayed on the screen.
7170 * @param timeout The requested screen timeout in milliseconds. Can also be either
7171 * {@link #SCREEN_TIMEOUT_SHORT} or {@link #SCREEN_TIMEOUT_LONG}.
7172 * @return this object for method chaining
7173 */
7174 public WearableExtender setHintScreenTimeout(int timeout) {
7175 mHintScreenTimeout = timeout;
7176 return this;
7177 }
7178
7179 /**
7180 * Get the duration, in milliseconds, that the screen should remain on for
7181 * when this notification is displayed.
7182 * @return the duration in milliseconds if > 0, or either one of the sentinel values
7183 * {@link #SCREEN_TIMEOUT_SHORT} or {@link #SCREEN_TIMEOUT_LONG}.
7184 */
7185 public int getHintScreenTimeout() {
7186 return mHintScreenTimeout;
7187 }
7188
Alex Hills9ab3a232016-04-05 14:54:56 -04007189 /**
Alex Hills4bcb06b2016-04-05 14:26:25 -04007190 * Set a hint that this notification's {@link BigPictureStyle} (if present) should be
7191 * converted to low-bit and displayed in ambient mode, especially useful for barcodes and
7192 * qr codes, as well as other simple black-and-white tickets.
7193 * @param hintAmbientBigPicture {@code true} to enable converstion and ambient.
7194 * @return this object for method chaining
7195 */
7196 public WearableExtender setHintAmbientBigPicture(boolean hintAmbientBigPicture) {
7197 setFlag(FLAG_BIG_PICTURE_AMBIENT, hintAmbientBigPicture);
7198 return this;
7199 }
7200
7201 /**
7202 * Get a hint that this notification's {@link BigPictureStyle} (if present) should be
7203 * converted to low-bit and displayed in ambient mode, especially useful for barcodes and
7204 * qr codes, as well as other simple black-and-white tickets.
7205 * @return {@code true} if it should be displayed in ambient, false otherwise
7206 * otherwise. The default value is {@code false} if this was never set.
7207 */
7208 public boolean getHintAmbientBigPicture() {
7209 return (mFlags & FLAG_BIG_PICTURE_AMBIENT) != 0;
7210 }
7211
7212 /**
Alex Hills9ab3a232016-04-05 14:54:56 -04007213 * Set a hint that this notification's content intent will launch an {@link Activity}
7214 * directly, telling the platform that it can generate the appropriate transitions.
7215 * @param hintContentIntentLaunchesActivity {@code true} if the content intent will launch
7216 * an activity and transitions should be generated, false otherwise.
7217 * @return this object for method chaining
7218 */
7219 public WearableExtender setHintContentIntentLaunchesActivity(
7220 boolean hintContentIntentLaunchesActivity) {
7221 setFlag(FLAG_HINT_CONTENT_INTENT_LAUNCHES_ACTIVITY, hintContentIntentLaunchesActivity);
7222 return this;
7223 }
7224
7225 /**
7226 * Get a hint that this notification's content intent will launch an {@link Activity}
7227 * directly, telling the platform that it can generate the appropriate transitions
7228 * @return {@code true} if the content intent will launch an activity and transitions should
7229 * be generated, false otherwise. The default value is {@code false} if this was never set.
7230 */
7231 public boolean getHintContentIntentLaunchesActivity() {
7232 return (mFlags & FLAG_HINT_CONTENT_INTENT_LAUNCHES_ACTIVITY) != 0;
7233 }
7234
Nadia Benbernou948627e2016-04-14 14:41:08 -04007235 /**
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04007236 * Sets the dismissal id for this notification. If a notification is posted with a
7237 * dismissal id, then when that notification is canceled, notifications on other wearables
7238 * and the paired Android phone having that same dismissal id will also be canceled. See
Nadia Benbernou948627e2016-04-14 14:41:08 -04007239 * <a href="{@docRoot}wear/notifications/index.html">Adding Wearable Features to
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04007240 * Notifications</a> for more information.
Nadia Benbernou948627e2016-04-14 14:41:08 -04007241 * @param dismissalId the dismissal id of the notification.
7242 * @return this object for method chaining
7243 */
7244 public WearableExtender setDismissalId(String dismissalId) {
7245 mDismissalId = dismissalId;
7246 return this;
7247 }
7248
7249 /**
7250 * Returns the dismissal id of the notification.
7251 * @return the dismissal id of the notification or null if it has not been set.
7252 */
7253 public String getDismissalId() {
7254 return mDismissalId;
7255 }
7256
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04007257 /**
7258 * Sets a bridge tag for this notification. A bridge tag can be set for notifications
7259 * posted from a phone to provide finer-grained control on what notifications are bridged
7260 * to wearables. See <a href="{@docRoot}wear/notifications/index.html">Adding Wearable
7261 * Features to Notifications</a> for more information.
7262 * @param bridgeTag the bridge tag of the notification.
7263 * @return this object for method chaining
7264 */
7265 public WearableExtender setBridgeTag(String bridgeTag) {
7266 mBridgeTag = bridgeTag;
7267 return this;
7268 }
7269
7270 /**
7271 * Returns the bridge tag of the notification.
7272 * @return the bridge tag or null if not present.
7273 */
7274 public String getBridgeTag() {
7275 return mBridgeTag;
7276 }
7277
Griff Hazen61a9e862014-05-22 16:05:19 -07007278 private void setFlag(int mask, boolean value) {
7279 if (value) {
7280 mFlags |= mask;
7281 } else {
7282 mFlags &= ~mask;
7283 }
7284 }
7285 }
7286
7287 /**
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08007288 * <p>Helper class to add Android Auto extensions to notifications. To create a notification
7289 * with car extensions:
7290 *
7291 * <ol>
7292 * <li>Create an {@link Notification.Builder}, setting any desired
7293 * properties.
7294 * <li>Create a {@link CarExtender}.
7295 * <li>Set car-specific properties using the {@code add} and {@code set} methods of
7296 * {@link CarExtender}.
7297 * <li>Call {@link Notification.Builder#extend(Notification.Extender)}
7298 * to apply the extensions to a notification.
7299 * </ol>
7300 *
7301 * <pre class="prettyprint">
7302 * Notification notification = new Notification.Builder(context)
7303 * ...
7304 * .extend(new CarExtender()
7305 * .set*(...))
7306 * .build();
7307 * </pre>
7308 *
7309 * <p>Car extensions can be accessed on an existing notification by using the
7310 * {@code CarExtender(Notification)} constructor, and then using the {@code get} methods
7311 * to access values.
7312 */
7313 public static final class CarExtender implements Extender {
7314 private static final String TAG = "CarExtender";
7315
7316 private static final String EXTRA_CAR_EXTENDER = "android.car.EXTENSIONS";
7317 private static final String EXTRA_LARGE_ICON = "large_icon";
7318 private static final String EXTRA_CONVERSATION = "car_conversation";
7319 private static final String EXTRA_COLOR = "app_color";
7320
7321 private Bitmap mLargeIcon;
7322 private UnreadConversation mUnreadConversation;
7323 private int mColor = Notification.COLOR_DEFAULT;
7324
7325 /**
7326 * Create a {@link CarExtender} with default options.
7327 */
7328 public CarExtender() {
7329 }
7330
7331 /**
7332 * Create a {@link CarExtender} from the CarExtender options of an existing Notification.
7333 *
7334 * @param notif The notification from which to copy options.
7335 */
7336 public CarExtender(Notification notif) {
7337 Bundle carBundle = notif.extras == null ?
7338 null : notif.extras.getBundle(EXTRA_CAR_EXTENDER);
7339 if (carBundle != null) {
7340 mLargeIcon = carBundle.getParcelable(EXTRA_LARGE_ICON);
7341 mColor = carBundle.getInt(EXTRA_COLOR, Notification.COLOR_DEFAULT);
7342
7343 Bundle b = carBundle.getBundle(EXTRA_CONVERSATION);
7344 mUnreadConversation = UnreadConversation.getUnreadConversationFromBundle(b);
7345 }
7346 }
7347
7348 /**
7349 * Apply car extensions to a notification that is being built. This is typically called by
7350 * the {@link Notification.Builder#extend(Notification.Extender)}
7351 * method of {@link Notification.Builder}.
7352 */
7353 @Override
7354 public Notification.Builder extend(Notification.Builder builder) {
7355 Bundle carExtensions = new Bundle();
7356
7357 if (mLargeIcon != null) {
7358 carExtensions.putParcelable(EXTRA_LARGE_ICON, mLargeIcon);
7359 }
7360 if (mColor != Notification.COLOR_DEFAULT) {
7361 carExtensions.putInt(EXTRA_COLOR, mColor);
7362 }
7363
7364 if (mUnreadConversation != null) {
7365 Bundle b = mUnreadConversation.getBundleForUnreadConversation();
7366 carExtensions.putBundle(EXTRA_CONVERSATION, b);
7367 }
7368
7369 builder.getExtras().putBundle(EXTRA_CAR_EXTENDER, carExtensions);
7370 return builder;
7371 }
7372
7373 /**
7374 * Sets the accent color to use when Android Auto presents the notification.
7375 *
7376 * Android Auto uses the color set with {@link Notification.Builder#setColor(int)}
7377 * to accent the displayed notification. However, not all colors are acceptable in an
7378 * automotive setting. This method can be used to override the color provided in the
7379 * notification in such a situation.
7380 */
Tor Norbye80756e32015-03-02 09:39:27 -08007381 public CarExtender setColor(@ColorInt int color) {
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08007382 mColor = color;
7383 return this;
7384 }
7385
7386 /**
7387 * Gets the accent color.
7388 *
Julia Reynoldsd9228f12015-10-20 10:37:27 -04007389 * @see #setColor
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08007390 */
Tor Norbye80756e32015-03-02 09:39:27 -08007391 @ColorInt
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08007392 public int getColor() {
7393 return mColor;
7394 }
7395
7396 /**
7397 * Sets the large icon of the car notification.
7398 *
7399 * If no large icon is set in the extender, Android Auto will display the icon
7400 * specified by {@link Notification.Builder#setLargeIcon(android.graphics.Bitmap)}
7401 *
7402 * @param largeIcon The large icon to use in the car notification.
7403 * @return This object for method chaining.
7404 */
7405 public CarExtender setLargeIcon(Bitmap largeIcon) {
7406 mLargeIcon = largeIcon;
7407 return this;
7408 }
7409
7410 /**
7411 * Gets the large icon used in this car notification, or null if no icon has been set.
7412 *
7413 * @return The large icon for the car notification.
7414 * @see CarExtender#setLargeIcon
7415 */
7416 public Bitmap getLargeIcon() {
7417 return mLargeIcon;
7418 }
7419
7420 /**
7421 * Sets the unread conversation in a message notification.
7422 *
7423 * @param unreadConversation The unread part of the conversation this notification conveys.
7424 * @return This object for method chaining.
7425 */
7426 public CarExtender setUnreadConversation(UnreadConversation unreadConversation) {
7427 mUnreadConversation = unreadConversation;
7428 return this;
7429 }
7430
7431 /**
7432 * Returns the unread conversation conveyed by this notification.
7433 * @see #setUnreadConversation(UnreadConversation)
7434 */
7435 public UnreadConversation getUnreadConversation() {
7436 return mUnreadConversation;
7437 }
7438
7439 /**
7440 * A class which holds the unread messages from a conversation.
7441 */
7442 public static class UnreadConversation {
7443 private static final String KEY_AUTHOR = "author";
7444 private static final String KEY_TEXT = "text";
7445 private static final String KEY_MESSAGES = "messages";
7446 private static final String KEY_REMOTE_INPUT = "remote_input";
7447 private static final String KEY_ON_REPLY = "on_reply";
7448 private static final String KEY_ON_READ = "on_read";
7449 private static final String KEY_PARTICIPANTS = "participants";
7450 private static final String KEY_TIMESTAMP = "timestamp";
7451
7452 private final String[] mMessages;
7453 private final RemoteInput mRemoteInput;
7454 private final PendingIntent mReplyPendingIntent;
7455 private final PendingIntent mReadPendingIntent;
7456 private final String[] mParticipants;
7457 private final long mLatestTimestamp;
7458
7459 UnreadConversation(String[] messages, RemoteInput remoteInput,
7460 PendingIntent replyPendingIntent, PendingIntent readPendingIntent,
7461 String[] participants, long latestTimestamp) {
7462 mMessages = messages;
7463 mRemoteInput = remoteInput;
7464 mReadPendingIntent = readPendingIntent;
7465 mReplyPendingIntent = replyPendingIntent;
7466 mParticipants = participants;
7467 mLatestTimestamp = latestTimestamp;
7468 }
7469
7470 /**
7471 * Gets the list of messages conveyed by this notification.
7472 */
7473 public String[] getMessages() {
7474 return mMessages;
7475 }
7476
7477 /**
7478 * Gets the remote input that will be used to convey the response to a message list, or
7479 * null if no such remote input exists.
7480 */
7481 public RemoteInput getRemoteInput() {
7482 return mRemoteInput;
7483 }
7484
7485 /**
7486 * Gets the pending intent that will be triggered when the user replies to this
7487 * notification.
7488 */
7489 public PendingIntent getReplyPendingIntent() {
7490 return mReplyPendingIntent;
7491 }
7492
7493 /**
7494 * Gets the pending intent that Android Auto will send after it reads aloud all messages
7495 * in this object's message list.
7496 */
7497 public PendingIntent getReadPendingIntent() {
7498 return mReadPendingIntent;
7499 }
7500
7501 /**
7502 * Gets the participants in the conversation.
7503 */
7504 public String[] getParticipants() {
7505 return mParticipants;
7506 }
7507
7508 /**
7509 * Gets the firs participant in the conversation.
7510 */
7511 public String getParticipant() {
7512 return mParticipants.length > 0 ? mParticipants[0] : null;
7513 }
7514
7515 /**
7516 * Gets the timestamp of the conversation.
7517 */
7518 public long getLatestTimestamp() {
7519 return mLatestTimestamp;
7520 }
7521
7522 Bundle getBundleForUnreadConversation() {
7523 Bundle b = new Bundle();
7524 String author = null;
7525 if (mParticipants != null && mParticipants.length > 1) {
7526 author = mParticipants[0];
7527 }
7528 Parcelable[] messages = new Parcelable[mMessages.length];
7529 for (int i = 0; i < messages.length; i++) {
7530 Bundle m = new Bundle();
7531 m.putString(KEY_TEXT, mMessages[i]);
7532 m.putString(KEY_AUTHOR, author);
7533 messages[i] = m;
7534 }
7535 b.putParcelableArray(KEY_MESSAGES, messages);
7536 if (mRemoteInput != null) {
7537 b.putParcelable(KEY_REMOTE_INPUT, mRemoteInput);
7538 }
7539 b.putParcelable(KEY_ON_REPLY, mReplyPendingIntent);
7540 b.putParcelable(KEY_ON_READ, mReadPendingIntent);
7541 b.putStringArray(KEY_PARTICIPANTS, mParticipants);
7542 b.putLong(KEY_TIMESTAMP, mLatestTimestamp);
7543 return b;
7544 }
7545
7546 static UnreadConversation getUnreadConversationFromBundle(Bundle b) {
7547 if (b == null) {
7548 return null;
7549 }
7550 Parcelable[] parcelableMessages = b.getParcelableArray(KEY_MESSAGES);
7551 String[] messages = null;
7552 if (parcelableMessages != null) {
7553 String[] tmp = new String[parcelableMessages.length];
7554 boolean success = true;
7555 for (int i = 0; i < tmp.length; i++) {
7556 if (!(parcelableMessages[i] instanceof Bundle)) {
7557 success = false;
7558 break;
7559 }
7560 tmp[i] = ((Bundle) parcelableMessages[i]).getString(KEY_TEXT);
7561 if (tmp[i] == null) {
7562 success = false;
7563 break;
7564 }
7565 }
7566 if (success) {
7567 messages = tmp;
7568 } else {
7569 return null;
7570 }
7571 }
7572
7573 PendingIntent onRead = b.getParcelable(KEY_ON_READ);
7574 PendingIntent onReply = b.getParcelable(KEY_ON_REPLY);
7575
7576 RemoteInput remoteInput = b.getParcelable(KEY_REMOTE_INPUT);
7577
7578 String[] participants = b.getStringArray(KEY_PARTICIPANTS);
7579 if (participants == null || participants.length != 1) {
7580 return null;
7581 }
7582
7583 return new UnreadConversation(messages,
7584 remoteInput,
7585 onReply,
7586 onRead,
7587 participants, b.getLong(KEY_TIMESTAMP));
7588 }
7589 };
7590
7591 /**
7592 * Builder class for {@link CarExtender.UnreadConversation} objects.
7593 */
7594 public static class Builder {
7595 private final List<String> mMessages = new ArrayList<String>();
7596 private final String mParticipant;
7597 private RemoteInput mRemoteInput;
7598 private PendingIntent mReadPendingIntent;
7599 private PendingIntent mReplyPendingIntent;
7600 private long mLatestTimestamp;
7601
7602 /**
7603 * Constructs a new builder for {@link CarExtender.UnreadConversation}.
7604 *
7605 * @param name The name of the other participant in the conversation.
7606 */
7607 public Builder(String name) {
7608 mParticipant = name;
7609 }
7610
7611 /**
7612 * Appends a new unread message to the list of messages for this conversation.
7613 *
7614 * The messages should be added from oldest to newest.
7615 *
7616 * @param message The text of the new unread message.
7617 * @return This object for method chaining.
7618 */
7619 public Builder addMessage(String message) {
7620 mMessages.add(message);
7621 return this;
7622 }
7623
7624 /**
7625 * Sets the pending intent and remote input which will convey the reply to this
7626 * notification.
7627 *
7628 * @param pendingIntent The pending intent which will be triggered on a reply.
7629 * @param remoteInput The remote input parcelable which will carry the reply.
7630 * @return This object for method chaining.
7631 *
7632 * @see CarExtender.UnreadConversation#getRemoteInput
7633 * @see CarExtender.UnreadConversation#getReplyPendingIntent
7634 */
7635 public Builder setReplyAction(
7636 PendingIntent pendingIntent, RemoteInput remoteInput) {
7637 mRemoteInput = remoteInput;
7638 mReplyPendingIntent = pendingIntent;
7639
7640 return this;
7641 }
7642
7643 /**
7644 * Sets the pending intent that will be sent once the messages in this notification
7645 * are read.
7646 *
7647 * @param pendingIntent The pending intent to use.
7648 * @return This object for method chaining.
7649 */
7650 public Builder setReadPendingIntent(PendingIntent pendingIntent) {
7651 mReadPendingIntent = pendingIntent;
7652 return this;
7653 }
7654
7655 /**
7656 * Sets the timestamp of the most recent message in an unread conversation.
7657 *
7658 * If a messaging notification has been posted by your application and has not
7659 * yet been cancelled, posting a later notification with the same id and tag
7660 * but without a newer timestamp may result in Android Auto not displaying a
7661 * heads up notification for the later notification.
7662 *
7663 * @param timestamp The timestamp of the most recent message in the conversation.
7664 * @return This object for method chaining.
7665 */
7666 public Builder setLatestTimestamp(long timestamp) {
7667 mLatestTimestamp = timestamp;
7668 return this;
7669 }
7670
7671 /**
7672 * Builds a new unread conversation object.
7673 *
7674 * @return The new unread conversation object.
7675 */
7676 public UnreadConversation build() {
7677 String[] messages = mMessages.toArray(new String[mMessages.size()]);
7678 String[] participants = { mParticipant };
7679 return new UnreadConversation(messages, mRemoteInput, mReplyPendingIntent,
7680 mReadPendingIntent, participants, mLatestTimestamp);
7681 }
7682 }
7683 }
7684
7685 /**
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08007686 * <p>Helper class to add Android TV extensions to notifications. To create a notification
7687 * with a TV extension:
7688 *
7689 * <ol>
7690 * <li>Create an {@link Notification.Builder}, setting any desired properties.
7691 * <li>Create a {@link TvExtender}.
7692 * <li>Set TV-specific properties using the {@code set} methods of
7693 * {@link TvExtender}.
7694 * <li>Call {@link Notification.Builder#extend(Notification.Extender)}
7695 * to apply the extension to a notification.
7696 * </ol>
7697 *
7698 * <pre class="prettyprint">
7699 * Notification notification = new Notification.Builder(context)
7700 * ...
7701 * .extend(new TvExtender()
7702 * .set*(...))
7703 * .build();
7704 * </pre>
7705 *
7706 * <p>TV extensions can be accessed on an existing notification by using the
7707 * {@code TvExtender(Notification)} constructor, and then using the {@code get} methods
7708 * to access values.
7709 *
7710 * @hide
7711 */
7712 @SystemApi
7713 public static final class TvExtender implements Extender {
7714 private static final String TAG = "TvExtender";
7715
7716 private static final String EXTRA_TV_EXTENDER = "android.tv.EXTENSIONS";
7717 private static final String EXTRA_FLAGS = "flags";
7718 private static final String EXTRA_CONTENT_INTENT = "content_intent";
7719 private static final String EXTRA_DELETE_INTENT = "delete_intent";
Dmitri Plotnikovb8a04ee2017-01-20 10:39:22 -08007720 private static final String EXTRA_CHANNEL_ID = "channel_id";
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08007721
7722 // Flags bitwise-ored to mFlags
7723 private static final int FLAG_AVAILABLE_ON_TV = 0x1;
7724
7725 private int mFlags;
Dmitri Plotnikovb8a04ee2017-01-20 10:39:22 -08007726 private String mChannelId;
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08007727 private PendingIntent mContentIntent;
7728 private PendingIntent mDeleteIntent;
7729
7730 /**
7731 * Create a {@link TvExtender} with default options.
7732 */
7733 public TvExtender() {
7734 mFlags = FLAG_AVAILABLE_ON_TV;
7735 }
7736
7737 /**
7738 * Create a {@link TvExtender} from the TvExtender options of an existing Notification.
7739 *
7740 * @param notif The notification from which to copy options.
7741 */
7742 public TvExtender(Notification notif) {
7743 Bundle bundle = notif.extras == null ?
7744 null : notif.extras.getBundle(EXTRA_TV_EXTENDER);
7745 if (bundle != null) {
7746 mFlags = bundle.getInt(EXTRA_FLAGS);
Dmitri Plotnikovb8a04ee2017-01-20 10:39:22 -08007747 mChannelId = bundle.getString(EXTRA_CHANNEL_ID);
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08007748 mContentIntent = bundle.getParcelable(EXTRA_CONTENT_INTENT);
7749 mDeleteIntent = bundle.getParcelable(EXTRA_DELETE_INTENT);
7750 }
7751 }
7752
7753 /**
7754 * Apply a TV extension to a notification that is being built. This is typically called by
7755 * the {@link Notification.Builder#extend(Notification.Extender)}
7756 * method of {@link Notification.Builder}.
7757 */
7758 @Override
7759 public Notification.Builder extend(Notification.Builder builder) {
7760 Bundle bundle = new Bundle();
7761
7762 bundle.putInt(EXTRA_FLAGS, mFlags);
Dmitri Plotnikovb8a04ee2017-01-20 10:39:22 -08007763 bundle.putString(EXTRA_CHANNEL_ID, mChannelId);
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08007764 if (mContentIntent != null) {
7765 bundle.putParcelable(EXTRA_CONTENT_INTENT, mContentIntent);
7766 }
7767
7768 if (mDeleteIntent != null) {
7769 bundle.putParcelable(EXTRA_DELETE_INTENT, mDeleteIntent);
7770 }
7771
7772 builder.getExtras().putBundle(EXTRA_TV_EXTENDER, bundle);
7773 return builder;
7774 }
7775
7776 /**
7777 * Returns true if this notification should be shown on TV. This method return true
7778 * if the notification was extended with a TvExtender.
7779 */
7780 public boolean isAvailableOnTv() {
7781 return (mFlags & FLAG_AVAILABLE_ON_TV) != 0;
7782 }
7783
7784 /**
Dmitri Plotnikovb8a04ee2017-01-20 10:39:22 -08007785 * Specifies the channel the notification should be delivered on when shown on TV.
7786 * It can be different from the channel that the notification is delivered to when
7787 * posting on a non-TV device.
7788 */
7789 public TvExtender setChannel(String channelId) {
7790 mChannelId = channelId;
7791 return this;
7792 }
7793
7794 /**
7795 * Returns the id of the channel this notification posts to on TV.
7796 */
7797 public String getChannel() {
7798 return mChannelId;
7799 }
7800
7801 /**
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08007802 * Supplies a {@link PendingIntent} to be sent when the notification is selected on TV.
7803 * If provided, it is used instead of the content intent specified
7804 * at the level of Notification.
7805 */
7806 public TvExtender setContentIntent(PendingIntent intent) {
7807 mContentIntent = intent;
7808 return this;
7809 }
7810
7811 /**
7812 * Returns the TV-specific content intent. If this method returns null, the
7813 * main content intent on the notification should be used.
7814 *
7815 * @see {@link Notification#contentIntent}
7816 */
7817 public PendingIntent getContentIntent() {
7818 return mContentIntent;
7819 }
7820
7821 /**
7822 * Supplies a {@link PendingIntent} to send when the notification is cleared explicitly
7823 * by the user on TV. If provided, it is used instead of the delete intent specified
7824 * at the level of Notification.
7825 */
7826 public TvExtender setDeleteIntent(PendingIntent intent) {
7827 mDeleteIntent = intent;
7828 return this;
7829 }
7830
7831 /**
7832 * Returns the TV-specific delete intent. If this method returns null, the
7833 * main delete intent on the notification should be used.
7834 *
7835 * @see {@link Notification#deleteIntent}
7836 */
7837 public PendingIntent getDeleteIntent() {
7838 return mDeleteIntent;
7839 }
7840 }
7841
7842 /**
Griff Hazen61a9e862014-05-22 16:05:19 -07007843 * Get an array of Notification objects from a parcelable array bundle field.
7844 * Update the bundle to have a typed array so fetches in the future don't need
7845 * to do an array copy.
7846 */
7847 private static Notification[] getNotificationArrayFromBundle(Bundle bundle, String key) {
7848 Parcelable[] array = bundle.getParcelableArray(key);
7849 if (array instanceof Notification[] || array == null) {
7850 return (Notification[]) array;
7851 }
7852 Notification[] typedArray = Arrays.copyOf(array, array.length,
7853 Notification[].class);
7854 bundle.putParcelableArray(key, typedArray);
7855 return typedArray;
7856 }
Christoph Studer4600f9b2014-07-22 22:44:43 +02007857
7858 private static class BuilderRemoteViews extends RemoteViews {
7859 public BuilderRemoteViews(Parcel parcel) {
7860 super(parcel);
7861 }
7862
Kenny Guy77320062014-08-27 21:37:15 +01007863 public BuilderRemoteViews(ApplicationInfo appInfo, int layoutId) {
7864 super(appInfo, layoutId);
Christoph Studer4600f9b2014-07-22 22:44:43 +02007865 }
7866
7867 @Override
7868 public BuilderRemoteViews clone() {
7869 Parcel p = Parcel.obtain();
7870 writeToParcel(p, 0);
7871 p.setDataPosition(0);
7872 BuilderRemoteViews brv = new BuilderRemoteViews(p);
7873 p.recycle();
7874 return brv;
7875 }
7876 }
Adrian Roos70d7aa32017-01-11 15:39:06 -08007877
7878 private static class StandardTemplateParams {
7879 boolean hasProgress = true;
7880 boolean ambient = false;
7881 CharSequence title;
7882 CharSequence text;
7883
7884 final StandardTemplateParams reset() {
7885 hasProgress = true;
7886 ambient = false;
7887 title = null;
7888 text = null;
7889 return this;
7890 }
7891
7892 final StandardTemplateParams hasProgress(boolean hasProgress) {
7893 this.hasProgress = hasProgress;
7894 return this;
7895 }
7896
7897 final StandardTemplateParams title(CharSequence title) {
7898 this.title = title;
7899 return this;
7900 }
7901
7902 final StandardTemplateParams text(CharSequence text) {
7903 this.text = text;
7904 return this;
7905 }
7906
7907 final StandardTemplateParams ambient(boolean ambient) {
Adrian Roos0bc3f6a2017-03-06 11:54:05 -08007908 Preconditions.checkState(title == null && text == null, "must set ambient before text");
Adrian Roos70d7aa32017-01-11 15:39:06 -08007909 this.ambient = ambient;
7910 return this;
7911 }
7912
7913 final StandardTemplateParams fillTextsFrom(Builder b) {
7914 Bundle extras = b.mN.extras;
Adrian Roos0bc3f6a2017-03-06 11:54:05 -08007915 title = b.processLegacyText(extras.getCharSequence(EXTRA_TITLE), ambient);
7916 text = b.processLegacyText(extras.getCharSequence(EXTRA_TEXT), ambient);
Adrian Roos70d7aa32017-01-11 15:39:06 -08007917 return this;
7918 }
7919 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007920}