blob: 6213ff50a8088cbbeb6f179aed643725b8e66b24 [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;
Selim Cinek7d1009b2017-01-25 15:28:28 -080052import android.service.notification.StatusBarNotification;
Adrian Roosc1a80b02016-04-05 14:54:55 -070053import android.text.BidiFormatter;
Selim Cinek60a54252016-02-26 17:03:25 -080054import android.text.SpannableStringBuilder;
55import android.text.Spanned;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080056import android.text.TextUtils;
Selim Cinek60a54252016-02-26 17:03:25 -080057import android.text.style.AbsoluteSizeSpan;
Selim Cinek981962e2016-07-20 20:41:58 -070058import android.text.style.BackgroundColorSpan;
Selim Cinek89991a22016-03-07 19:51:54 -080059import android.text.style.CharacterStyle;
Selim Cinek981962e2016-07-20 20:41:58 -070060import android.text.style.ForegroundColorSpan;
Selim Cinek60a54252016-02-26 17:03:25 -080061import android.text.style.RelativeSizeSpan;
62import android.text.style.TextAppearanceSpan;
Svet Ganovddb94882016-06-23 19:55:24 -070063import android.util.ArraySet;
Daniel Sandlerdcbaf662013-04-26 16:23:09 -040064import android.util.Log;
Dan Sandler50128532015-12-08 15:42:41 -050065import android.util.SparseArray;
Griff Hazen61a9e862014-05-22 16:05:19 -070066import android.view.Gravity;
Selim Cinekea4bef72015-12-02 15:51:10 -080067import android.view.NotificationHeaderView;
Joe Onorato8595a3d2010-11-19 18:12:07 -080068import android.view.View;
Selim Cinek954cc232016-05-20 13:29:23 -070069import android.view.ViewGroup;
Jeff Sharkey1c400132011-08-05 14:50:13 -070070import android.widget.ProgressBar;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080071import android.widget.RemoteViews;
72
Griff Hazen959591e2014-05-15 22:26:18 -070073import com.android.internal.R;
Svet Ganovddb94882016-06-23 19:55:24 -070074import com.android.internal.util.ArrayUtils;
Griff Hazenc091ba82014-05-16 10:13:26 -070075import com.android.internal.util.NotificationColorUtil;
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 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800114 * Use all default values (where applicable).
115 */
116 public static final int DEFAULT_ALL = ~0;
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500117
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800118 /**
119 * Use the default notification sound. This will ignore any given
120 * {@link #sound}.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500121 *
Chris Wren47c20a12014-06-18 17:27:29 -0400122 * <p>
123 * A notification that is noisy is more likely to be presented as a heads-up notification.
124 * </p>
125 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800126 * @see #defaults
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500127 */
128
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800129 public static final int DEFAULT_SOUND = 1;
130
131 /**
132 * Use the default notification vibrate. This will ignore any given
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500133 * {@link #vibrate}. Using phone vibration requires the
Scott Mainb8b36452009-04-26 15:50:49 -0700134 * {@link android.Manifest.permission#VIBRATE VIBRATE} permission.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500135 *
Chris Wren47c20a12014-06-18 17:27:29 -0400136 * <p>
137 * A notification that vibrates is more likely to be presented as a heads-up notification.
138 * </p>
139 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800140 * @see #defaults
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500141 */
142
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800143 public static final int DEFAULT_VIBRATE = 2;
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500144
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800145 /**
146 * Use the default notification lights. This will ignore the
147 * {@link #FLAG_SHOW_LIGHTS} bit, and {@link #ledARGB}, {@link #ledOffMS}, or
148 * {@link #ledOnMS}.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500149 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800150 * @see #defaults
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500151 */
152
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800153 public static final int DEFAULT_LIGHTS = 4;
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500154
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800155 /**
Christoph Studer535ec612014-09-03 15:47:47 +0200156 * Maximum length of CharSequences accepted by Builder and friends.
157 *
158 * <p>
159 * Avoids spamming the system with overly large strings such as full e-mails.
160 */
161 private static final int MAX_CHARSEQUENCE_LENGTH = 5 * 1024;
162
163 /**
Adrian Roose458aa82015-12-08 16:17:19 -0800164 * Maximum entries of reply text that are accepted by Builder and friends.
165 */
166 private static final int MAX_REPLY_HISTORY = 5;
167
168 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500169 * A timestamp related to this notification, in milliseconds since the epoch.
Joe Malin8d40d042012-11-05 11:36:40 -0800170 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500171 * Default value: {@link System#currentTimeMillis() Now}.
172 *
173 * Choose a timestamp that will be most relevant to the user. For most finite events, this
174 * corresponds to the time the event happened (or will happen, in the case of events that have
175 * yet to occur but about which the user is being informed). Indefinite events should be
Joe Malin8d40d042012-11-05 11:36:40 -0800176 * timestamped according to when the activity began.
177 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500178 * Some examples:
Joe Malin8d40d042012-11-05 11:36:40 -0800179 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500180 * <ul>
181 * <li>Notification of a new chat message should be stamped when the message was received.</li>
182 * <li>Notification of an ongoing file download (with a progress bar, for example) should be stamped when the download started.</li>
183 * <li>Notification of a completed file download should be stamped when the download finished.</li>
184 * <li>Notification of an upcoming meeting should be stamped with the time the meeting will begin (that is, in the future).</li>
185 * <li>Notification of an ongoing stopwatch (increasing timer) should be stamped with the watch's start time.
186 * <li>Notification of an ongoing countdown timer should be stamped with the timer's end time.
Joe Malin8d40d042012-11-05 11:36:40 -0800187 * </ul>
188 *
Selim Cinek0ff1ce602016-04-05 18:27:16 -0700189 * For apps targeting {@link android.os.Build.VERSION_CODES#N} and above, this time is not shown
190 * anymore by default and must be opted into by using
191 * {@link android.app.Notification.Builder#setShowWhen(boolean)}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800192 */
193 public long when;
194
195 /**
Selim Cinekb85f36fd2016-04-20 18:46:36 -0700196 * The creation time of the notification
197 */
198 private long creationTime;
199
200 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800201 * The resource id of a drawable to use as the icon in the status bar.
Dan Sandler86647982015-05-13 23:41:13 -0400202 *
203 * @deprecated Use {@link Builder#setSmallIcon(Icon)} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800204 */
Dan Sandler86647982015-05-13 23:41:13 -0400205 @Deprecated
Tor Norbye7b9c9122013-05-30 16:48:33 -0700206 @DrawableRes
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800207 public int icon;
208
209 /**
Joe Onorato46439ce2010-11-19 13:56:21 -0800210 * If the icon in the status bar is to have more than one level, you can set this. Otherwise,
211 * leave it at its default value of 0.
212 *
213 * @see android.widget.ImageView#setImageLevel
Griff Hazen959591e2014-05-15 22:26:18 -0700214 * @see android.graphics.drawable.Drawable#setLevel
Joe Onorato46439ce2010-11-19 13:56:21 -0800215 */
216 public int iconLevel;
217
218 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500219 * The number of events that this notification represents. For example, in a new mail
220 * notification, this could be the number of unread messages.
Joe Malin8d40d042012-11-05 11:36:40 -0800221 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500222 * The system may or may not use this field to modify the appearance of the notification. For
223 * example, before {@link android.os.Build.VERSION_CODES#HONEYCOMB}, this number was
224 * superimposed over the icon in the status bar. Starting with
225 * {@link android.os.Build.VERSION_CODES#HONEYCOMB}, the template used by
226 * {@link Notification.Builder} has displayed the number in the expanded notification view.
Julia Reynolds13d898c2017-02-02 12:22:05 -0500227 * Starting with {@link android.os.Build.VERSION_CODES#O}, the number may be displayed as a
228 * badge icon in Launchers that support badging.
Joe Malin8d40d042012-11-05 11:36:40 -0800229 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800230 */
Julia Reynolds13d898c2017-02-02 12:22:05 -0500231 public int number = 1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800232
233 /**
234 * The intent to execute when the expanded status entry is clicked. If
235 * this is an activity, it must include the
236 * {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} flag, which requires
Scott Main7aee61f2011-02-08 11:25:01 -0800237 * that you take care of task management as described in the
238 * <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back
Dianne Hackborn6ceca582012-01-10 15:24:26 -0800239 * Stack</a> document. In particular, make sure to read the notification section
240 * <a href="{@docRoot}guide/topics/ui/notifiers/notifications.html#HandlingNotifications">Handling
241 * Notifications</a> for the correct ways to launch an application from a
242 * notification.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800243 */
244 public PendingIntent contentIntent;
245
246 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500247 * The intent to execute when the notification is explicitly dismissed by the user, either with
248 * the "Clear All" button or by swiping it away individually.
249 *
250 * This probably shouldn't be launching an activity since several of those will be sent
251 * at the same time.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800252 */
253 public PendingIntent deleteIntent;
254
255 /**
Dianne Hackborn170bae72010-09-03 15:14:28 -0700256 * An intent to launch instead of posting the notification to the status bar.
Joe Onoratocb109a02011-01-18 17:57:41 -0800257 *
Chris Wren47c20a12014-06-18 17:27:29 -0400258 * <p>
259 * The system UI may choose to display a heads-up notification, instead of
260 * launching this intent, while the user is using the device.
261 * </p>
262 *
Joe Onoratocb109a02011-01-18 17:57:41 -0800263 * @see Notification.Builder#setFullScreenIntent
Daniel Sandlere46cbd32010-06-17 10:35:26 -0400264 */
265 public PendingIntent fullScreenIntent;
266
267 /**
Dan Sandler5fcdf6e2014-07-18 11:31:15 -0400268 * Text that summarizes this notification for accessibility services.
269 *
270 * As of the L release, this text is no longer shown on screen, but it is still useful to
271 * accessibility services (where it serves as an audible announcement of the notification's
272 * appearance).
Joe Onoratoef1e7762010-09-17 18:38:38 -0400273 *
Joe Onorato46439ce2010-11-19 13:56:21 -0800274 * @see #tickerView
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800275 */
276 public CharSequence tickerText;
277
278 /**
Dan Sandler5fcdf6e2014-07-18 11:31:15 -0400279 * Formerly, a view showing the {@link #tickerText}.
280 *
281 * No longer displayed in the status bar as of API 21.
Joe Onoratoef1e7762010-09-17 18:38:38 -0400282 */
Dan Sandler5fcdf6e2014-07-18 11:31:15 -0400283 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -0800284 public RemoteViews tickerView;
Joe Onoratoef1e7762010-09-17 18:38:38 -0400285
286 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400287 * The view that will represent this notification in the notification list (which is pulled
288 * down from the status bar).
289 *
Julia Reynoldsd4ea7412016-02-17 14:00:56 -0500290 * As of N, this field may be null. The notification view is determined by the inputs
291 * to {@link Notification.Builder}; a custom RemoteViews can optionally be
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400292 * supplied with {@link Notification.Builder#setCustomContentView(RemoteViews)}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800293 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400294 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800295 public RemoteViews contentView;
296
Daniel Sandlera0a938c2012-03-15 08:42:37 -0400297 /**
Daniel Sandler4dfbe832012-04-11 14:51:46 -0400298 * A large-format version of {@link #contentView}, giving the Notification an
Daniel Sandlerf3b73432012-03-27 15:01:25 -0400299 * opportunity to show more detail. The system UI may choose to show this
300 * instead of the normal content view at its discretion.
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400301 *
Julia Reynoldsd4ea7412016-02-17 14:00:56 -0500302 * As of N, this field may be null. The expanded notification view is determined by the
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400303 * inputs to {@link Notification.Builder}; a custom RemoteViews can optionally be
304 * supplied with {@link Notification.Builder#setCustomBigContentView(RemoteViews)}.
Daniel Sandlerf3b73432012-03-27 15:01:25 -0400305 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400306 @Deprecated
Daniel Sandlerf3b73432012-03-27 15:01:25 -0400307 public RemoteViews bigContentView;
308
Chris Wren8fd39ec2014-02-27 17:43:26 -0500309
310 /**
Chris Wren47c20a12014-06-18 17:27:29 -0400311 * A medium-format version of {@link #contentView}, providing the Notification an
312 * opportunity to add action buttons to contentView. At its discretion, the system UI may
313 * choose to show this as a heads-up notification, which will pop up so the user can see
314 * it without leaving their current activity.
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400315 *
Julia Reynoldsd4ea7412016-02-17 14:00:56 -0500316 * As of N, this field may be null. The heads-up notification view is determined by the
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400317 * inputs to {@link Notification.Builder}; a custom RemoteViews can optionally be
318 * supplied with {@link Notification.Builder#setCustomHeadsUpContentView(RemoteViews)}.
Chris Wren8fd39ec2014-02-27 17:43:26 -0500319 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400320 @Deprecated
Chris Wren8fd39ec2014-02-27 17:43:26 -0500321 public RemoteViews headsUpContentView;
322
Daniel Sandlerf3b73432012-03-27 15:01:25 -0400323 /**
Dan Sandler86647982015-05-13 23:41:13 -0400324 * A large bitmap to be shown in the notification content area.
325 *
326 * @deprecated Use {@link Builder#setLargeIcon(Icon)} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800327 */
Dan Sandler86647982015-05-13 23:41:13 -0400328 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -0800329 public Bitmap largeIcon;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800330
331 /**
332 * The sound to play.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500333 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800334 * <p>
Chris Wren47c20a12014-06-18 17:27:29 -0400335 * A notification that is noisy is more likely to be presented as a heads-up notification.
336 * </p>
337 *
338 * <p>
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500339 * To play the default notification sound, see {@link #defaults}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800340 * </p>
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500341 * @deprecated use {@link NotificationChannel#getSound()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800342 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500343 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800344 public Uri sound;
345
346 /**
347 * Use this constant as the value for audioStreamType to request that
348 * the default stream type for notifications be used. Currently the
Jeff Sharkey098d5802012-04-26 17:30:34 -0700349 * default stream type is {@link AudioManager#STREAM_NOTIFICATION}.
John Spurlockc0650f022014-07-19 13:22:39 -0400350 *
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500351 * @deprecated Use {@link NotificationChannel#getAudioAttributes()} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800352 */
Jean-Michel Trivi81f871e2014-08-06 16:32:38 -0700353 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800354 public static final int STREAM_DEFAULT = -1;
355
356 /**
357 * The audio stream type to use when playing the sound.
358 * Should be one of the STREAM_ constants from
359 * {@link android.media.AudioManager}.
John Spurlockc0650f022014-07-19 13:22:39 -0400360 *
361 * @deprecated Use {@link #audioAttributes} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800362 */
Jean-Michel Trivi81f871e2014-08-06 16:32:38 -0700363 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800364 public int audioStreamType = STREAM_DEFAULT;
365
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800366 /**
John Spurlockc0650f022014-07-19 13:22:39 -0400367 * The default value of {@link #audioAttributes}.
368 */
369 public static final AudioAttributes AUDIO_ATTRIBUTES_DEFAULT = new AudioAttributes.Builder()
370 .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
371 .setUsage(AudioAttributes.USAGE_NOTIFICATION)
372 .build();
373
374 /**
375 * The {@link AudioAttributes audio attributes} to use when playing the sound.
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500376 *
377 * @deprecated use {@link NotificationChannel#getAudioAttributes()} instead.
John Spurlockc0650f022014-07-19 13:22:39 -0400378 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500379 @Deprecated
John Spurlockc0650f022014-07-19 13:22:39 -0400380 public AudioAttributes audioAttributes = AUDIO_ATTRIBUTES_DEFAULT;
381
382 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500383 * The pattern with which to vibrate.
384 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800385 * <p>
386 * To vibrate the default pattern, see {@link #defaults}.
387 * </p>
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500388 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800389 * @see android.os.Vibrator#vibrate(long[],int)
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500390 * @deprecated use {@link NotificationChannel#getVibrationPattern()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800391 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500392 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800393 public long[] vibrate;
394
395 /**
396 * The color of the led. The hardware will do its best approximation.
397 *
398 * @see #FLAG_SHOW_LIGHTS
399 * @see #flags
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500400 * @deprecated use {@link NotificationChannel#shouldShowLights()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800401 */
Tor Norbye80756e32015-03-02 09:39:27 -0800402 @ColorInt
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500403 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800404 public int ledARGB;
405
406 /**
407 * The number of milliseconds for the LED to be on while it's flashing.
408 * The hardware will do its best approximation.
409 *
410 * @see #FLAG_SHOW_LIGHTS
411 * @see #flags
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500412 * @deprecated use {@link NotificationChannel#shouldShowLights()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800413 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500414 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800415 public int ledOnMS;
416
417 /**
418 * The number of milliseconds for the LED to be off while it's flashing.
419 * The hardware will do its best approximation.
420 *
421 * @see #FLAG_SHOW_LIGHTS
422 * @see #flags
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500423 *
424 * @deprecated use {@link NotificationChannel#shouldShowLights()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800425 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500426 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800427 public int ledOffMS;
428
429 /**
430 * Specifies which values should be taken from the defaults.
431 * <p>
432 * To set, OR the desired from {@link #DEFAULT_SOUND},
433 * {@link #DEFAULT_VIBRATE}, {@link #DEFAULT_LIGHTS}. For all default
434 * values, use {@link #DEFAULT_ALL}.
435 * </p>
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500436 *
437 * @deprecated use {@link NotificationChannel#getSound()} and
438 * {@link NotificationChannel#shouldShowLights()} and
439 * {@link NotificationChannel#shouldVibrate()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800440 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500441 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800442 public int defaults;
443
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800444 /**
445 * Bit to be bitwise-ored into the {@link #flags} field that should be
446 * set if you want the LED on for this notification.
447 * <ul>
448 * <li>To turn the LED off, pass 0 in the alpha channel for colorARGB
449 * or 0 for both ledOnMS and ledOffMS.</li>
450 * <li>To turn the LED on, pass 1 for ledOnMS and 0 for ledOffMS.</li>
451 * <li>To flash the LED, pass the number of milliseconds that it should
452 * be on and off to ledOnMS and ledOffMS.</li>
453 * </ul>
454 * <p>
455 * Since hardware varies, you are not guaranteed that any of the values
456 * you pass are honored exactly. Use the system defaults (TODO) if possible
457 * because they will be set to values that work on any given hardware.
458 * <p>
459 * The alpha channel must be set for forward compatibility.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500460 *
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500461 * @deprecated use {@link NotificationChannel#shouldShowLights()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800462 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500463 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800464 public static final int FLAG_SHOW_LIGHTS = 0x00000001;
465
466 /**
467 * Bit to be bitwise-ored into the {@link #flags} field that should be
468 * set if this notification is in reference to something that is ongoing,
469 * like a phone call. It should not be set if this notification is in
470 * reference to something that happened at a particular point in time,
471 * like a missed phone call.
472 */
473 public static final int FLAG_ONGOING_EVENT = 0x00000002;
474
475 /**
476 * Bit to be bitwise-ored into the {@link #flags} field that if set,
Scott Mainb8b36452009-04-26 15:50:49 -0700477 * the audio will be repeated until the notification is
478 * cancelled or the notification window is opened.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800479 */
480 public static final int FLAG_INSISTENT = 0x00000004;
481
482 /**
483 * Bit to be bitwise-ored into the {@link #flags} field that should be
Griff Hazen293977b2014-04-28 08:37:20 -0700484 * set if you would only like the sound, vibrate and ticker to be played
485 * if the notification was not already showing.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800486 */
487 public static final int FLAG_ONLY_ALERT_ONCE = 0x00000008;
488
489 /**
490 * Bit to be bitwise-ored into the {@link #flags} field that should be
491 * set if the notification should be canceled when it is clicked by the
Daniel Sandler8aa9ae62012-12-04 23:31:47 -0500492 * user.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800493 */
494 public static final int FLAG_AUTO_CANCEL = 0x00000010;
495
496 /**
497 * Bit to be bitwise-ored into the {@link #flags} field that should be
498 * set if the notification should not be canceled when the user clicks
499 * the Clear all button.
500 */
501 public static final int FLAG_NO_CLEAR = 0x00000020;
502
Dianne Hackbornd8a43f62009-08-17 23:33:56 -0700503 /**
504 * Bit to be bitwise-ored into the {@link #flags} field that should be
505 * set if this notification represents a currently running service. This
506 * will normally be set for you by {@link Service#startForeground}.
507 */
508 public static final int FLAG_FOREGROUND_SERVICE = 0x00000040;
509
Daniel Sandlere46cbd32010-06-17 10:35:26 -0400510 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500511 * Obsolete flag indicating high-priority notifications; use the priority field instead.
Joe Malin8d40d042012-11-05 11:36:40 -0800512 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500513 * @deprecated Use {@link #priority} with a positive value.
Daniel Sandlere46cbd32010-06-17 10:35:26 -0400514 */
Aurimas Liutikas514c5ef2016-05-24 15:22:55 -0700515 @Deprecated
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500516 public static final int FLAG_HIGH_PRIORITY = 0x00000080;
Daniel Sandlere46cbd32010-06-17 10:35:26 -0400517
Griff Hazendfcb0802014-02-11 12:00:00 -0800518 /**
519 * Bit to be bitswise-ored into the {@link #flags} field that should be
520 * set if this notification is relevant to the current device only
521 * and it is not recommended that it bridge to other devices.
522 */
523 public static final int FLAG_LOCAL_ONLY = 0x00000100;
524
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700525 /**
526 * Bit to be bitswise-ored into the {@link #flags} field that should be
527 * set if this notification is the group summary for a group of notifications.
528 * Grouped notifications may display in a cluster or stack on devices which
529 * support such rendering. Requires a group key also be set using {@link Builder#setGroup}.
530 */
531 public static final int FLAG_GROUP_SUMMARY = 0x00000200;
532
Julia Reynoldse46bb372016-03-17 11:05:58 -0400533 /**
534 * Bit to be bitswise-ored into the {@link #flags} field that should be
535 * set if this notification is the group summary for an auto-group of notifications.
536 *
537 * @hide
538 */
539 @SystemApi
540 public static final int FLAG_AUTOGROUP_SUMMARY = 0x00000400;
541
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800542 public int flags;
543
Tor Norbyed9273d62013-05-30 15:59:53 -0700544 /** @hide */
545 @IntDef({PRIORITY_DEFAULT,PRIORITY_LOW,PRIORITY_MIN,PRIORITY_HIGH,PRIORITY_MAX})
546 @Retention(RetentionPolicy.SOURCE)
547 public @interface Priority {}
548
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800549 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500550 * Default notification {@link #priority}. If your application does not prioritize its own
551 * notifications, use this value for all notifications.
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500552 *
553 * @deprecated use {@link NotificationManager#IMPORTANCE_DEFAULT} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500554 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500555 @Deprecated
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500556 public static final int PRIORITY_DEFAULT = 0;
557
558 /**
559 * Lower {@link #priority}, for items that are less important. The UI may choose to show these
560 * items smaller, or at a different position in the list, compared with your app's
561 * {@link #PRIORITY_DEFAULT} items.
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500562 *
563 * @deprecated use {@link NotificationManager#IMPORTANCE_LOW} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500564 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500565 @Deprecated
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500566 public static final int PRIORITY_LOW = -1;
567
568 /**
569 * Lowest {@link #priority}; these items might not be shown to the user except under special
570 * circumstances, such as detailed notification logs.
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500571 *
572 * @deprecated use {@link NotificationManager#IMPORTANCE_MIN} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500573 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500574 @Deprecated
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500575 public static final int PRIORITY_MIN = -2;
576
577 /**
578 * Higher {@link #priority}, for more important notifications or alerts. The UI may choose to
579 * show these items larger, or at a different position in notification lists, compared with
580 * your app's {@link #PRIORITY_DEFAULT} items.
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500581 *
582 * @deprecated use {@link NotificationManager#IMPORTANCE_HIGH} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500583 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500584 @Deprecated
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500585 public static final int PRIORITY_HIGH = 1;
586
587 /**
588 * Highest {@link #priority}, for your application's most important items that require the
589 * user's prompt attention or input.
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500590 *
591 * @deprecated use {@link NotificationManager#IMPORTANCE_HIGH} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500592 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500593 @Deprecated
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500594 public static final int PRIORITY_MAX = 2;
595
596 /**
597 * Relative priority for this notification.
Joe Malin8d40d042012-11-05 11:36:40 -0800598 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500599 * Priority is an indication of how much of the user's valuable attention should be consumed by
600 * this notification. Low-priority notifications may be hidden from the user in certain
601 * situations, while the user might be interrupted for a higher-priority notification. The
Daniel Sandler6738eee2012-11-16 12:03:32 -0500602 * system will make a determination about how to interpret this priority when presenting
603 * the notification.
Chris Wren47c20a12014-06-18 17:27:29 -0400604 *
605 * <p>
606 * A notification that is at least {@link #PRIORITY_HIGH} is more likely to be presented
607 * as a heads-up notification.
608 * </p>
609 *
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500610 * @deprecated use {@link NotificationChannel#getImportance()} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500611 */
Tor Norbyed9273d62013-05-30 15:59:53 -0700612 @Priority
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500613 @Deprecated
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500614 public int priority;
Joe Malin8d40d042012-11-05 11:36:40 -0800615
Dan Sandler26e81cf2014-05-06 10:01:27 -0400616 /**
617 * Accent color (an ARGB integer like the constants in {@link android.graphics.Color})
618 * to be applied by the standard Style templates when presenting this notification.
619 *
620 * The current template design constructs a colorful header image by overlaying the
621 * {@link #icon} image (stenciled in white) atop a field of this color. Alpha components are
622 * ignored.
623 */
Tor Norbye80756e32015-03-02 09:39:27 -0800624 @ColorInt
Dan Sandler26e81cf2014-05-06 10:01:27 -0400625 public int color = COLOR_DEFAULT;
626
627 /**
628 * Special value of {@link #color} telling the system not to decorate this notification with
629 * any special color but instead use default colors when presenting this notification.
630 */
Tor Norbye80756e32015-03-02 09:39:27 -0800631 @ColorInt
Dan Sandler26e81cf2014-05-06 10:01:27 -0400632 public static final int COLOR_DEFAULT = 0; // AKA Color.TRANSPARENT
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600633
634 /**
Adrian Roos4ff3b122016-02-01 12:26:13 -0800635 * Special value of {@link #color} used as a place holder for an invalid color.
636 */
637 @ColorInt
638 private static final int COLOR_INVALID = 1;
639
640 /**
Adrian Roosc1a80b02016-04-05 14:54:55 -0700641 * Sphere of visibility of this notification, which affects how and when the SystemUI reveals
642 * the notification's presence and contents in untrusted situations (namely, on the secure
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600643 * lockscreen).
644 *
645 * The default level, {@link #VISIBILITY_PRIVATE}, behaves exactly as notifications have always
646 * done on Android: The notification's {@link #icon} and {@link #tickerText} (if available) are
647 * shown in all situations, but the contents are only available if the device is unlocked for
648 * the appropriate user.
649 *
650 * A more permissive policy can be expressed by {@link #VISIBILITY_PUBLIC}; such a notification
651 * can be read even in an "insecure" context (that is, above a secure lockscreen).
652 * To modify the public version of this notification—for example, to redact some portions—see
653 * {@link Builder#setPublicVersion(Notification)}.
654 *
655 * Finally, a notification can be made {@link #VISIBILITY_SECRET}, which will suppress its icon
656 * and ticker until the user has bypassed the lockscreen.
657 */
658 public int visibility;
659
Griff Hazenfc3922d2014-08-20 11:56:44 -0700660 /**
661 * Notification visibility: Show this notification in its entirety on all lockscreens.
662 *
663 * {@see #visibility}
664 */
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600665 public static final int VISIBILITY_PUBLIC = 1;
Griff Hazenfc3922d2014-08-20 11:56:44 -0700666
667 /**
668 * Notification visibility: Show this notification on all lockscreens, but conceal sensitive or
669 * private information on secure lockscreens.
670 *
671 * {@see #visibility}
672 */
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600673 public static final int VISIBILITY_PRIVATE = 0;
Griff Hazenfc3922d2014-08-20 11:56:44 -0700674
675 /**
676 * Notification visibility: Do not reveal any part of this notification on a secure lockscreen.
677 *
678 * {@see #visibility}
679 */
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600680 public static final int VISIBILITY_SECRET = -1;
681
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500682 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400683 * Notification category: incoming call (voice or video) or similar synchronous communication request.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500684 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400685 public static final String CATEGORY_CALL = "call";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500686
687 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400688 * Notification category: incoming direct message (SMS, instant message, etc.).
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500689 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400690 public static final String CATEGORY_MESSAGE = "msg";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500691
692 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400693 * Notification category: asynchronous bulk message (email).
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500694 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400695 public static final String CATEGORY_EMAIL = "email";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500696
697 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400698 * Notification category: calendar event.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500699 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400700 public static final String CATEGORY_EVENT = "event";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500701
702 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400703 * Notification category: promotion or advertisement.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500704 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400705 public static final String CATEGORY_PROMO = "promo";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500706
707 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400708 * Notification category: alarm or timer.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500709 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400710 public static final String CATEGORY_ALARM = "alarm";
711
712 /**
713 * Notification category: progress of a long-running background operation.
714 */
715 public static final String CATEGORY_PROGRESS = "progress";
716
717 /**
718 * Notification category: social network or sharing update.
719 */
720 public static final String CATEGORY_SOCIAL = "social";
721
722 /**
723 * Notification category: error in background operation or authentication status.
724 */
725 public static final String CATEGORY_ERROR = "err";
726
727 /**
728 * Notification category: media transport control for playback.
729 */
730 public static final String CATEGORY_TRANSPORT = "transport";
731
732 /**
733 * Notification category: system or device status update. Reserved for system use.
734 */
735 public static final String CATEGORY_SYSTEM = "sys";
736
737 /**
738 * Notification category: indication of running background service.
739 */
740 public static final String CATEGORY_SERVICE = "service";
741
742 /**
John Spurlock0a69c8c2014-03-21 13:30:57 -0400743 * Notification category: a specific, timely recommendation for a single thing.
744 * For example, a news app might want to recommend a news story it believes the user will
745 * want to read next.
746 */
747 public static final String CATEGORY_RECOMMENDATION = "recommendation";
748
749 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400750 * Notification category: ongoing information about device or contextual status.
751 */
752 public static final String CATEGORY_STATUS = "status";
753
754 /**
John Spurlock24d3dad2015-04-02 12:24:02 -0400755 * Notification category: user-scheduled reminder.
756 */
757 public static final String CATEGORY_REMINDER = "reminder";
758
759 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400760 * One of the predefined notification categories (see the <code>CATEGORY_*</code> constants)
761 * that best describes this Notification. May be used by the system for ranking and filtering.
762 */
763 public String category;
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500764
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700765 private String mGroupKey;
766
767 /**
768 * Get the key used to group this notification into a cluster or stack
769 * with other notifications on devices which support such rendering.
770 */
771 public String getGroup() {
772 return mGroupKey;
773 }
774
775 private String mSortKey;
776
777 /**
778 * Get a sort key that orders this notification among other notifications from the
779 * same package. This can be useful if an external sort was already applied and an app
780 * would like to preserve this. Notifications will be sorted lexicographically using this
781 * value, although providing different priorities in addition to providing sort key may
782 * cause this value to be ignored.
783 *
784 * <p>This sort key can also be used to order members of a notification group. See
785 * {@link Builder#setGroup}.
786 *
787 * @see String#compareTo(String)
788 */
789 public String getSortKey() {
790 return mSortKey;
791 }
792
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500793 /**
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400794 * Additional semantic data to be carried around with this Notification.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400795 * <p>
796 * The extras keys defined here are intended to capture the original inputs to {@link Builder}
797 * APIs, and are intended to be used by
798 * {@link android.service.notification.NotificationListenerService} implementations to extract
799 * detailed information from notification objects.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500800 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400801 public Bundle extras = new Bundle();
802
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400803 /**
Felipe Lemedd85da62016-06-28 11:29:54 -0700804 * All pending intents in the notification as the system needs to be able to access them but
805 * touching the extras bundle in the system process is not safe because the bundle may contain
Svet Ganovddb94882016-06-23 19:55:24 -0700806 * custom parcelable objects.
807 *
808 * @hide
809 */
Felipe Lemedd85da62016-06-28 11:29:54 -0700810 public ArraySet<PendingIntent> allPendingIntents;
Svet Ganovddb94882016-06-23 19:55:24 -0700811
812 /**
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400813 * {@link #extras} key: this is the title of the notification,
814 * as supplied to {@link Builder#setContentTitle(CharSequence)}.
815 */
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -0500816 public static final String EXTRA_TITLE = "android.title";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400817
818 /**
819 * {@link #extras} key: this is the title of the notification when shown in expanded form,
820 * e.g. as supplied to {@link BigTextStyle#setBigContentTitle(CharSequence)}.
821 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400822 public static final String EXTRA_TITLE_BIG = EXTRA_TITLE + ".big";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400823
824 /**
825 * {@link #extras} key: this is the main text payload, as supplied to
826 * {@link Builder#setContentText(CharSequence)}.
827 */
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -0500828 public static final String EXTRA_TEXT = "android.text";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400829
830 /**
831 * {@link #extras} key: this is a third line of text, as supplied to
832 * {@link Builder#setSubText(CharSequence)}.
833 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400834 public static final String EXTRA_SUB_TEXT = "android.subText";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400835
836 /**
Adrian Roose458aa82015-12-08 16:17:19 -0800837 * {@link #extras} key: this is the remote input history, as supplied to
838 * {@link Builder#setRemoteInputHistory(CharSequence[])}.
Adrian Roos005e7742016-04-13 15:02:20 -0700839 *
840 * Apps can fill this through {@link Builder#setRemoteInputHistory(CharSequence[])}
841 * with the most recent inputs that have been sent through a {@link RemoteInput} of this
842 * Notification and are expected to clear it once the it is no longer relevant (e.g. for chat
843 * notifications once the other party has responded).
844 *
845 * The extra with this key is of type CharSequence[] and contains the most recent entry at
846 * the 0 index, the second most recent at the 1 index, etc.
847 *
848 * @see Builder#setRemoteInputHistory(CharSequence[])
Adrian Roose458aa82015-12-08 16:17:19 -0800849 */
850 public static final String EXTRA_REMOTE_INPUT_HISTORY = "android.remoteInputHistory";
851
852 /**
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400853 * {@link #extras} key: this is a small piece of additional text as supplied to
854 * {@link Builder#setContentInfo(CharSequence)}.
855 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400856 public static final String EXTRA_INFO_TEXT = "android.infoText";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400857
858 /**
859 * {@link #extras} key: this is a line of summary information intended to be shown
860 * alongside expanded notifications, as supplied to (e.g.)
861 * {@link BigTextStyle#setSummaryText(CharSequence)}.
862 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400863 public static final String EXTRA_SUMMARY_TEXT = "android.summaryText";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400864
865 /**
Christoph Studer4600f9b2014-07-22 22:44:43 +0200866 * {@link #extras} key: this is the longer text shown in the big form of a
867 * {@link BigTextStyle} notification, as supplied to
868 * {@link BigTextStyle#bigText(CharSequence)}.
869 */
870 public static final String EXTRA_BIG_TEXT = "android.bigText";
871
872 /**
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400873 * {@link #extras} key: this is the resource ID of the notification's main small icon, as
874 * supplied to {@link Builder#setSmallIcon(int)}.
875 */
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -0500876 public static final String EXTRA_SMALL_ICON = "android.icon";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400877
878 /**
879 * {@link #extras} key: this is a bitmap to be used instead of the small icon when showing the
880 * notification payload, as
881 * supplied to {@link Builder#setLargeIcon(android.graphics.Bitmap)}.
882 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400883 public static final String EXTRA_LARGE_ICON = "android.largeIcon";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400884
885 /**
886 * {@link #extras} key: this is a bitmap to be used instead of the one from
887 * {@link Builder#setLargeIcon(android.graphics.Bitmap)} when the notification is
888 * shown in its expanded form, as supplied to
889 * {@link BigPictureStyle#bigLargeIcon(android.graphics.Bitmap)}.
890 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400891 public static final String EXTRA_LARGE_ICON_BIG = EXTRA_LARGE_ICON + ".big";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400892
893 /**
894 * {@link #extras} key: this is the progress value supplied to
895 * {@link Builder#setProgress(int, int, boolean)}.
896 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400897 public static final String EXTRA_PROGRESS = "android.progress";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400898
899 /**
900 * {@link #extras} key: this is the maximum value supplied to
901 * {@link Builder#setProgress(int, int, boolean)}.
902 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400903 public static final String EXTRA_PROGRESS_MAX = "android.progressMax";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400904
905 /**
906 * {@link #extras} key: whether the progress bar is indeterminate, supplied to
907 * {@link Builder#setProgress(int, int, boolean)}.
908 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400909 public static final String EXTRA_PROGRESS_INDETERMINATE = "android.progressIndeterminate";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400910
911 /**
912 * {@link #extras} key: whether {@link #when} should be shown as a count-up timer (specifically
913 * a {@link android.widget.Chronometer}) instead of a timestamp, as supplied to
914 * {@link Builder#setUsesChronometer(boolean)}.
915 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400916 public static final String EXTRA_SHOW_CHRONOMETER = "android.showChronometer";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400917
918 /**
Selim Cinek81c23aa2016-02-25 16:23:13 -0800919 * {@link #extras} key: whether the chronometer set on the notification should count down
920 * instead of counting up. Is only relevant if key {@link #EXTRA_SHOW_CHRONOMETER} is present.
Adrian Roos96b7e202016-05-17 13:50:38 -0700921 * This extra is a boolean. The default is false.
Selim Cinek81c23aa2016-02-25 16:23:13 -0800922 */
Adrian Roos96b7e202016-05-17 13:50:38 -0700923 public static final String EXTRA_CHRONOMETER_COUNT_DOWN = "android.chronometerCountDown";
Selim Cinek81c23aa2016-02-25 16:23:13 -0800924
925 /**
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400926 * {@link #extras} key: whether {@link #when} should be shown,
927 * as supplied to {@link Builder#setShowWhen(boolean)}.
928 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400929 public static final String EXTRA_SHOW_WHEN = "android.showWhen";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400930
931 /**
932 * {@link #extras} key: this is a bitmap to be shown in {@link BigPictureStyle} expanded
933 * notifications, supplied to {@link BigPictureStyle#bigPicture(android.graphics.Bitmap)}.
934 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400935 public static final String EXTRA_PICTURE = "android.picture";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400936
937 /**
938 * {@link #extras} key: An array of CharSequences to show in {@link InboxStyle} expanded
939 * notifications, each of which was supplied to {@link InboxStyle#addLine(CharSequence)}.
940 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400941 public static final String EXTRA_TEXT_LINES = "android.textLines";
Dan Sandler842dd772014-05-15 09:36:47 -0400942
943 /**
944 * {@link #extras} key: A string representing the name of the specific
945 * {@link android.app.Notification.Style} used to create this notification.
946 */
Chris Wren91ad5632013-06-05 15:05:57 -0400947 public static final String EXTRA_TEMPLATE = "android.template";
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400948
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400949 /**
Chris Wrene6c48932014-09-29 17:19:27 -0400950 * {@link #extras} key: A String array containing the people that this notification relates to,
951 * each of which was supplied to {@link Builder#addPerson(String)}.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400952 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400953 public static final String EXTRA_PEOPLE = "android.people";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500954
955 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400956 * Allow certain system-generated notifications to appear before the device is provisioned.
957 * Only available to notifications coming from the android package.
958 * @hide
959 */
960 public static final String EXTRA_ALLOW_DURING_SETUP = "android.allowDuringSetup";
961
962 /**
Jose Limae9e3b3b2014-05-18 23:44:50 -0700963 * {@link #extras} key: A
964 * {@link android.content.ContentUris content URI} pointing to an image that can be displayed
965 * in the background when the notification is selected. The URI must point to an image stream
966 * suitable for passing into
967 * {@link android.graphics.BitmapFactory#decodeStream(java.io.InputStream)
968 * BitmapFactory.decodeStream}; all other content types will be ignored. The content provider
969 * URI used for this purpose must require no permissions to read the image data.
970 */
971 public static final String EXTRA_BACKGROUND_IMAGE_URI = "android.backgroundImageUri";
972
973 /**
Dan Sandler842dd772014-05-15 09:36:47 -0400974 * {@link #extras} key: A
Jeff Browndba34ba2014-06-24 20:46:03 -0700975 * {@link android.media.session.MediaSession.Token} associated with a
Dan Sandler842dd772014-05-15 09:36:47 -0400976 * {@link android.app.Notification.MediaStyle} notification.
977 */
978 public static final String EXTRA_MEDIA_SESSION = "android.mediaSession";
979
980 /**
Bryan Mawhinneye191f902014-07-22 12:50:09 +0100981 * {@link #extras} key: the indices of actions to be shown in the compact view,
982 * as supplied to (e.g.) {@link MediaStyle#setShowActionsInCompactView(int...)}.
983 */
984 public static final String EXTRA_COMPACT_ACTIONS = "android.compactActions";
985
Christoph Studer943aa672014-08-03 20:31:16 +0200986 /**
Alex Hillsfc737de2016-03-23 17:33:02 -0400987 * {@link #extras} key: the username to be displayed for all messages sent by the user including
988 * direct replies
Adrian Roos96b7e202016-05-17 13:50:38 -0700989 * {@link android.app.Notification.MessagingStyle} notification. This extra is a
990 * {@link CharSequence}
Alex Hillsfc737de2016-03-23 17:33:02 -0400991 */
992 public static final String EXTRA_SELF_DISPLAY_NAME = "android.selfDisplayName";
993
994 /**
Adrian Roos96b7e202016-05-17 13:50:38 -0700995 * {@link #extras} key: a {@link CharSequence} to be displayed as the title to a conversation
Alex Hillsd9b04d92016-04-11 16:38:16 -0400996 * represented by a {@link android.app.Notification.MessagingStyle}
Alex Hillsfc737de2016-03-23 17:33:02 -0400997 */
Alex Hillsd9b04d92016-04-11 16:38:16 -0400998 public static final String EXTRA_CONVERSATION_TITLE = "android.conversationTitle";
Alex Hillsfc737de2016-03-23 17:33:02 -0400999
1000 /**
1001 * {@link #extras} key: an array of {@link android.app.Notification.MessagingStyle.Message}
1002 * bundles provided by a
Adrian Roos96b7e202016-05-17 13:50:38 -07001003 * {@link android.app.Notification.MessagingStyle} notification. This extra is a parcelable
1004 * array of bundles.
Alex Hillsfc737de2016-03-23 17:33:02 -04001005 */
1006 public static final String EXTRA_MESSAGES = "android.messages";
1007
1008 /**
Adrian Roos437cd562017-01-18 15:47:03 -08001009 * {@link #extras} key: an array of
1010 * {@link android.app.Notification.MessagingStyle#addHistoricMessage historic}
1011 * {@link android.app.Notification.MessagingStyle.Message} bundles provided by a
1012 * {@link android.app.Notification.MessagingStyle} notification. This extra is a parcelable
1013 * array of bundles.
1014 */
1015 public static final String EXTRA_HISTORIC_MESSAGES = "android.messages.historic";
1016
1017 /**
Selim Cinek7b9605b2017-01-19 17:36:00 -08001018 * {@link #extras} key: whether the notification should be colorized as
1019 * supplied to {@link Builder#setColorized(boolean)}}.
1020 */
1021 public static final String EXTRA_COLORIZED = "android.colorized";
1022
1023 /**
Kenny Guy8942bcd2014-09-08 21:09:47 +01001024 * {@link #extras} key: the user that built the notification.
1025 *
1026 * @hide
1027 */
1028 public static final String EXTRA_ORIGINATING_USERID = "android.originatingUserId";
1029
1030 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04001031 * @hide
1032 */
1033 public static final String EXTRA_BUILDER_APPLICATION_INFO = "android.appInfo";
1034
Selim Cinek247fa012016-02-18 09:50:48 -08001035 /**
1036 * @hide
1037 */
1038 public static final String EXTRA_CONTAINS_CUSTOM_VIEW = "android.contains.customView";
1039
Shane Brennan472a3b32016-12-12 15:28:10 -08001040 /**
1041 * {@link #extras} key: the audio contents of this notification.
1042 *
1043 * This is for use when rendering the notification on an audio-focused interface;
1044 * the audio contents are a complete sound sample that contains the contents/body of the
1045 * notification. This may be used in substitute of a Text-to-Speech reading of the
1046 * notification. For example if the notification represents a voice message this should point
1047 * to the audio of that message.
1048 *
1049 * The data stored under this key should be a String representation of a Uri that contains the
1050 * audio contents in one of the following formats: WAV, PCM 16-bit, AMR-WB.
1051 *
1052 * This extra is unnecessary if you are using {@code MessagingStyle} since each {@code Message}
1053 * has a field for holding data URI. That field can be used for audio.
1054 * See {@code Message#setData}.
1055 *
1056 * Example usage:
1057 * <pre>
1058 * {@code
1059 * Notification.Builder myBuilder = (build your Notification as normal);
1060 * myBuilder.getExtras().putString(EXTRA_AUDIO_CONTENTS_URI, myAudioUri.toString());
1061 * }
1062 * </pre>
1063 */
1064 public static final String EXTRA_AUDIO_CONTENTS_URI = "android.audioContents";
1065
Dan Sandler80eaa592016-04-14 23:34:54 -04001066 /** @hide */
1067 @SystemApi
Dan Sandler732bd6c2016-04-12 14:20:32 -04001068 public static final String EXTRA_SUBSTITUTE_APP_NAME = "android.substName";
1069
Dan Sandlerd63f9322015-05-06 15:18:49 -04001070 private Icon mSmallIcon;
1071 private Icon mLargeIcon;
1072
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04001073 private String mChannelId;
Julia Reynolds2a128742016-11-28 14:29:25 -05001074 private long mTimeout;
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04001075
Julia Reynolds13d898c2017-02-02 12:22:05 -05001076 private String mShortcutId;
1077
1078 /**
1079 * If this notification is being shown as a badge, always show as a number.
1080 */
1081 public static final int BADGE_ICON_NONE = 0;
1082
1083 /**
1084 * If this notification is being shown as a badge, use the {@link #getSmallIcon()} to
1085 * represent this notification.
1086 */
1087 public static final int BADGE_ICON_SMALL = 1;
1088
1089 /**
1090 * If this notification is being shown as a badge, use the {@link #getLargeIcon()} to
1091 * represent this notification.
1092 */
1093 public static final int BADGE_ICON_LARGE = 2;
1094 private int mBadgeIcon = BADGE_ICON_LARGE;
1095
Chris Wren51c75102013-07-16 20:49:17 -04001096 /**
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001097 * Structure to encapsulate a named action that can be shown as part of this notification.
1098 * It must include an icon, a label, and a {@link PendingIntent} to be fired when the action is
1099 * selected by the user.
1100 * <p>
Griff Hazen959591e2014-05-15 22:26:18 -07001101 * Apps should use {@link Notification.Builder#addAction(int, CharSequence, PendingIntent)}
1102 * or {@link Notification.Builder#addAction(Notification.Action)}
1103 * to attach actions.
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001104 */
Daniel Sandlerea2a3172013-02-20 22:24:20 -05001105 public static class Action implements Parcelable {
Shane Brennan472a3b32016-12-12 15:28:10 -08001106 /**
1107 * {@link #extras} key: Keys to a {@link Parcelable} {@link ArrayList} of
1108 * {@link RemoteInput}s.
1109 *
1110 * This is intended for {@link RemoteInput}s that only accept data, meaning
1111 * {@link RemoteInput#getAllowFreeFormInput} is false, {@link RemoteInput#getChoices}
1112 * is null or empty, and {@link RemoteInput#getAllowedDataTypes is non-null and not
1113 * empty. These {@link RemoteInput}s will be ignored by devices that do not
1114 * support non-text-based {@link RemoteInput}s. See {@link Builder#build}.
1115 *
1116 * You can test if a RemoteInput matches these constraints using
1117 * {@link RemoteInput#isDataOnly}.
1118 */
1119 private static final String EXTRA_DATA_ONLY_INPUTS = "android.extra.DATA_ONLY_INPUTS";
1120
Griff Hazen959591e2014-05-15 22:26:18 -07001121 private final Bundle mExtras;
Dan Sandler86647982015-05-13 23:41:13 -04001122 private Icon mIcon;
Griff Hazen61a9e862014-05-22 16:05:19 -07001123 private final RemoteInput[] mRemoteInputs;
Alex Hills1f27f882017-01-12 11:24:46 -05001124 private boolean mAllowGeneratedReplies = true;
Griff Hazen959591e2014-05-15 22:26:18 -07001125
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001126 /**
1127 * Small icon representing the action.
Dan Sandler86647982015-05-13 23:41:13 -04001128 *
1129 * @deprecated Use {@link Action#getIcon()} instead.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001130 */
Dan Sandler86647982015-05-13 23:41:13 -04001131 @Deprecated
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001132 public int icon;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001133
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001134 /**
1135 * Title of the action.
1136 */
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001137 public CharSequence title;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001138
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001139 /**
1140 * Intent to send when the user invokes this action. May be null, in which case the action
1141 * may be rendered in a disabled presentation by the system UI.
1142 */
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001143 public PendingIntent actionIntent;
Griff Hazen959591e2014-05-15 22:26:18 -07001144
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001145 private Action(Parcel in) {
Dan Sandler86647982015-05-13 23:41:13 -04001146 if (in.readInt() != 0) {
1147 mIcon = Icon.CREATOR.createFromParcel(in);
Dan Sandler68079d52015-07-22 10:45:30 -04001148 if (mIcon.getType() == Icon.TYPE_RESOURCE) {
1149 icon = mIcon.getResId();
1150 }
Dan Sandler86647982015-05-13 23:41:13 -04001151 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001152 title = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
1153 if (in.readInt() == 1) {
1154 actionIntent = PendingIntent.CREATOR.createFromParcel(in);
1155 }
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001156 mExtras = Bundle.setDefusable(in.readBundle(), true);
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001157 mRemoteInputs = in.createTypedArray(RemoteInput.CREATOR);
Alex Hills42b0c4d2016-04-26 13:35:36 -04001158 mAllowGeneratedReplies = in.readInt() == 1;
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001159 }
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001160
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001161 /**
Dan Sandler86647982015-05-13 23:41:13 -04001162 * @deprecated Use {@link android.app.Notification.Action.Builder}.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001163 */
Dan Sandler86647982015-05-13 23:41:13 -04001164 @Deprecated
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001165 public Action(int icon, CharSequence title, PendingIntent intent) {
Alex Hills1f27f882017-01-12 11:24:46 -05001166 this(Icon.createWithResource("", icon), title, intent, new Bundle(), null, true);
Griff Hazen959591e2014-05-15 22:26:18 -07001167 }
1168
Adrian Roos7af53622016-10-12 13:44:05 -07001169 /** Keep in sync with {@link Notification.Action.Builder#Builder(Action)}! */
Dan Sandler86647982015-05-13 23:41:13 -04001170 private Action(Icon icon, CharSequence title, PendingIntent intent, Bundle extras,
Alex Hills42b0c4d2016-04-26 13:35:36 -04001171 RemoteInput[] remoteInputs, boolean allowGeneratedReplies) {
Dan Sandler86647982015-05-13 23:41:13 -04001172 this.mIcon = icon;
Gus Prevasf5bff46f2015-08-24 10:34:28 -04001173 if (icon != null && icon.getType() == Icon.TYPE_RESOURCE) {
1174 this.icon = icon.getResId();
1175 }
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001176 this.title = title;
1177 this.actionIntent = intent;
Griff Hazen959591e2014-05-15 22:26:18 -07001178 this.mExtras = extras != null ? extras : new Bundle();
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001179 this.mRemoteInputs = remoteInputs;
Alex Hills42b0c4d2016-04-26 13:35:36 -04001180 this.mAllowGeneratedReplies = allowGeneratedReplies;
Griff Hazen959591e2014-05-15 22:26:18 -07001181 }
1182
1183 /**
Dan Sandler86647982015-05-13 23:41:13 -04001184 * Return an icon representing the action.
1185 */
1186 public Icon getIcon() {
1187 if (mIcon == null && icon != 0) {
1188 // you snuck an icon in here without using the builder; let's try to keep it
1189 mIcon = Icon.createWithResource("", icon);
1190 }
1191 return mIcon;
1192 }
1193
1194 /**
Griff Hazen959591e2014-05-15 22:26:18 -07001195 * Get additional metadata carried around with this Action.
1196 */
1197 public Bundle getExtras() {
1198 return mExtras;
1199 }
1200
1201 /**
Alex Hills42b0c4d2016-04-26 13:35:36 -04001202 * Return whether the platform should automatically generate possible replies for this
1203 * {@link Action}
1204 */
1205 public boolean getAllowGeneratedReplies() {
1206 return mAllowGeneratedReplies;
1207 }
1208
1209 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001210 * Get the list of inputs to be collected from the user when this action is sent.
Shane Brennan472a3b32016-12-12 15:28:10 -08001211 * May return null if no remote inputs were added. Only returns inputs which accept
1212 * a text input. For inputs which only accept data use {@link #getDataOnlyRemoteInputs}.
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001213 */
1214 public RemoteInput[] getRemoteInputs() {
1215 return mRemoteInputs;
1216 }
1217
1218 /**
Shane Brennan472a3b32016-12-12 15:28:10 -08001219 * Get the list of inputs to be collected from the user that ONLY accept data when this
1220 * action is sent. These remote inputs are guaranteed to return true on a call to
1221 * {@link RemoteInput#isDataOnly}.
1222 *
1223 * May return null if no data-only remote inputs were added.
1224 *
1225 * This method exists so that legacy RemoteInput collectors that pre-date the addition
1226 * of non-textual RemoteInputs do not access these remote inputs.
1227 */
1228 public RemoteInput[] getDataOnlyRemoteInputs() {
1229 return (RemoteInput[]) mExtras.getParcelableArray(EXTRA_DATA_ONLY_INPUTS);
1230 }
1231
1232 /**
Griff Hazen959591e2014-05-15 22:26:18 -07001233 * Builder class for {@link Action} objects.
1234 */
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001235 public static final class Builder {
Dan Sandler86647982015-05-13 23:41:13 -04001236 private final Icon mIcon;
Griff Hazen959591e2014-05-15 22:26:18 -07001237 private final CharSequence mTitle;
1238 private final PendingIntent mIntent;
Alex Hills1f27f882017-01-12 11:24:46 -05001239 private boolean mAllowGeneratedReplies = true;
Griff Hazen959591e2014-05-15 22:26:18 -07001240 private final Bundle mExtras;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001241 private ArrayList<RemoteInput> mRemoteInputs;
Griff Hazen959591e2014-05-15 22:26:18 -07001242
1243 /**
1244 * Construct a new builder for {@link Action} object.
1245 * @param icon icon to show for this action
1246 * @param title the title of the action
1247 * @param intent the {@link PendingIntent} to fire when users trigger this action
1248 */
Dan Sandler86647982015-05-13 23:41:13 -04001249 @Deprecated
Griff Hazen959591e2014-05-15 22:26:18 -07001250 public Builder(int icon, CharSequence title, PendingIntent intent) {
Adrian Roos7af53622016-10-12 13:44:05 -07001251 this(Icon.createWithResource("", icon), title, intent);
Dan Sandler86647982015-05-13 23:41:13 -04001252 }
1253
1254 /**
1255 * Construct a new builder for {@link Action} object.
1256 * @param icon icon to show for this action
1257 * @param title the title of the action
1258 * @param intent the {@link PendingIntent} to fire when users trigger this action
1259 */
1260 public Builder(Icon icon, CharSequence title, PendingIntent intent) {
Alex Hills1f27f882017-01-12 11:24:46 -05001261 this(icon, title, intent, new Bundle(), null, true);
Griff Hazen959591e2014-05-15 22:26:18 -07001262 }
1263
1264 /**
1265 * Construct a new builder for {@link Action} object using the fields from an
1266 * {@link Action}.
1267 * @param action the action to read fields from.
1268 */
1269 public Builder(Action action) {
Adrian Roos7af53622016-10-12 13:44:05 -07001270 this(action.getIcon(), action.title, action.actionIntent,
1271 new Bundle(action.mExtras), action.getRemoteInputs(),
1272 action.getAllowGeneratedReplies());
Griff Hazen959591e2014-05-15 22:26:18 -07001273 }
1274
Dan Sandler86647982015-05-13 23:41:13 -04001275 private Builder(Icon icon, CharSequence title, PendingIntent intent, Bundle extras,
Adrian Roos7af53622016-10-12 13:44:05 -07001276 RemoteInput[] remoteInputs, boolean allowGeneratedReplies) {
Griff Hazen959591e2014-05-15 22:26:18 -07001277 mIcon = icon;
1278 mTitle = title;
1279 mIntent = intent;
1280 mExtras = extras;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001281 if (remoteInputs != null) {
1282 mRemoteInputs = new ArrayList<RemoteInput>(remoteInputs.length);
1283 Collections.addAll(mRemoteInputs, remoteInputs);
1284 }
Adrian Roos7af53622016-10-12 13:44:05 -07001285 mAllowGeneratedReplies = allowGeneratedReplies;
Griff Hazen959591e2014-05-15 22:26:18 -07001286 }
1287
1288 /**
1289 * Merge additional metadata into this builder.
1290 *
1291 * <p>Values within the Bundle will replace existing extras values in this Builder.
1292 *
1293 * @see Notification.Action#extras
1294 */
1295 public Builder addExtras(Bundle extras) {
1296 if (extras != null) {
1297 mExtras.putAll(extras);
1298 }
1299 return this;
1300 }
1301
1302 /**
1303 * Get the metadata Bundle used by this Builder.
1304 *
1305 * <p>The returned Bundle is shared with this Builder.
1306 */
1307 public Bundle getExtras() {
1308 return mExtras;
1309 }
1310
1311 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001312 * Add an input to be collected from the user when this action is sent.
1313 * Response values can be retrieved from the fired intent by using the
1314 * {@link RemoteInput#getResultsFromIntent} function.
1315 * @param remoteInput a {@link RemoteInput} to add to the action
1316 * @return this object for method chaining
1317 */
1318 public Builder addRemoteInput(RemoteInput remoteInput) {
1319 if (mRemoteInputs == null) {
1320 mRemoteInputs = new ArrayList<RemoteInput>();
1321 }
1322 mRemoteInputs.add(remoteInput);
1323 return this;
1324 }
1325
1326 /**
Alex Hills42b0c4d2016-04-26 13:35:36 -04001327 * Set whether the platform should automatically generate possible replies to add to
1328 * {@link RemoteInput#getChoices()}. If the {@link Action} doesn't have a
1329 * {@link RemoteInput}, this has no effect.
1330 * @param allowGeneratedReplies {@code true} to allow generated replies, {@code false}
1331 * otherwise
1332 * @return this object for method chaining
Alex Hills1f27f882017-01-12 11:24:46 -05001333 * The default value is {@code true}
Alex Hills42b0c4d2016-04-26 13:35:36 -04001334 */
1335 public Builder setAllowGeneratedReplies(boolean allowGeneratedReplies) {
1336 mAllowGeneratedReplies = allowGeneratedReplies;
1337 return this;
1338 }
1339
1340 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001341 * Apply an extender to this action builder. Extenders may be used to add
1342 * metadata or change options on this builder.
1343 */
Griff Hazen61a9e862014-05-22 16:05:19 -07001344 public Builder extend(Extender extender) {
1345 extender.extend(this);
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001346 return this;
1347 }
1348
1349 /**
Griff Hazen959591e2014-05-15 22:26:18 -07001350 * Combine all of the options that have been set and return a new {@link Action}
1351 * object.
1352 * @return the built action
1353 */
1354 public Action build() {
Shane Brennan472a3b32016-12-12 15:28:10 -08001355 ArrayList<RemoteInput> dataOnlyInputs = new ArrayList<>();
1356 RemoteInput[] previousDataInputs =
1357 (RemoteInput[]) mExtras.getParcelableArray(EXTRA_DATA_ONLY_INPUTS);
Felipe Lemedfd11962017-01-18 18:38:46 -08001358 if (previousDataInputs != null) {
Shane Brennan472a3b32016-12-12 15:28:10 -08001359 for (RemoteInput input : previousDataInputs) {
1360 dataOnlyInputs.add(input);
1361 }
1362 }
1363 List<RemoteInput> textInputs = new ArrayList<>();
1364 if (mRemoteInputs != null) {
1365 for (RemoteInput input : mRemoteInputs) {
1366 if (input.isDataOnly()) {
1367 dataOnlyInputs.add(input);
1368 } else {
1369 textInputs.add(input);
1370 }
1371 }
1372 }
1373 if (!dataOnlyInputs.isEmpty()) {
1374 RemoteInput[] dataInputsArr =
1375 dataOnlyInputs.toArray(new RemoteInput[dataOnlyInputs.size()]);
1376 mExtras.putParcelableArray(EXTRA_DATA_ONLY_INPUTS, dataInputsArr);
1377 }
1378 RemoteInput[] textInputsArr = textInputs.isEmpty()
1379 ? null : textInputs.toArray(new RemoteInput[textInputs.size()]);
1380 return new Action(mIcon, mTitle, mIntent, mExtras, textInputsArr,
Alex Hills42b0c4d2016-04-26 13:35:36 -04001381 mAllowGeneratedReplies);
Griff Hazen959591e2014-05-15 22:26:18 -07001382 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001383 }
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001384
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001385 @Override
1386 public Action clone() {
1387 return new Action(
Dan Sandler86647982015-05-13 23:41:13 -04001388 getIcon(),
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001389 title,
1390 actionIntent, // safe to alias
Julia Reynolds53c934c2016-09-16 14:03:43 -04001391 mExtras == null ? new Bundle() : new Bundle(mExtras),
Alex Hills42b0c4d2016-04-26 13:35:36 -04001392 getRemoteInputs(),
1393 getAllowGeneratedReplies());
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001394 }
1395 @Override
1396 public int describeContents() {
1397 return 0;
1398 }
1399 @Override
1400 public void writeToParcel(Parcel out, int flags) {
Dan Sandler86647982015-05-13 23:41:13 -04001401 final Icon ic = getIcon();
1402 if (ic != null) {
1403 out.writeInt(1);
1404 ic.writeToParcel(out, 0);
1405 } else {
1406 out.writeInt(0);
1407 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001408 TextUtils.writeToParcel(title, out, flags);
1409 if (actionIntent != null) {
1410 out.writeInt(1);
1411 actionIntent.writeToParcel(out, flags);
1412 } else {
1413 out.writeInt(0);
1414 }
Griff Hazen959591e2014-05-15 22:26:18 -07001415 out.writeBundle(mExtras);
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001416 out.writeTypedArray(mRemoteInputs, flags);
Alex Hills42b0c4d2016-04-26 13:35:36 -04001417 out.writeInt(mAllowGeneratedReplies ? 1 : 0);
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001418 }
Griff Hazen959591e2014-05-15 22:26:18 -07001419 public static final Parcelable.Creator<Action> CREATOR =
1420 new Parcelable.Creator<Action>() {
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001421 public Action createFromParcel(Parcel in) {
1422 return new Action(in);
1423 }
1424 public Action[] newArray(int size) {
1425 return new Action[size];
1426 }
1427 };
Griff Hazen61a9e862014-05-22 16:05:19 -07001428
1429 /**
1430 * Extender interface for use with {@link Builder#extend}. Extenders may be used to add
1431 * metadata or change options on an action builder.
1432 */
1433 public interface Extender {
1434 /**
1435 * Apply this extender to a notification action builder.
1436 * @param builder the builder to be modified.
1437 * @return the build object for chaining.
1438 */
1439 public Builder extend(Builder builder);
1440 }
1441
1442 /**
1443 * Wearable extender for notification actions. To add extensions to an action,
1444 * create a new {@link android.app.Notification.Action.WearableExtender} object using
1445 * the {@code WearableExtender()} constructor and apply it to a
1446 * {@link android.app.Notification.Action.Builder} using
1447 * {@link android.app.Notification.Action.Builder#extend}.
1448 *
1449 * <pre class="prettyprint">
1450 * Notification.Action action = new Notification.Action.Builder(
1451 * R.drawable.archive_all, "Archive all", actionIntent)
Griff Hazen14f57992014-05-26 09:07:14 -07001452 * .extend(new Notification.Action.WearableExtender()
Griff Hazen61a9e862014-05-22 16:05:19 -07001453 * .setAvailableOffline(false))
Griff Hazen14f57992014-05-26 09:07:14 -07001454 * .build();</pre>
Griff Hazen61a9e862014-05-22 16:05:19 -07001455 */
1456 public static final class WearableExtender implements Extender {
1457 /** Notification action extra which contains wearable extensions */
1458 private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS";
1459
Pete Gastaf6781d2014-10-07 15:17:05 -04001460 // Keys within EXTRA_WEARABLE_EXTENSIONS for wearable options.
Griff Hazen61a9e862014-05-22 16:05:19 -07001461 private static final String KEY_FLAGS = "flags";
Pete Gastaf6781d2014-10-07 15:17:05 -04001462 private static final String KEY_IN_PROGRESS_LABEL = "inProgressLabel";
1463 private static final String KEY_CONFIRM_LABEL = "confirmLabel";
1464 private static final String KEY_CANCEL_LABEL = "cancelLabel";
Griff Hazen61a9e862014-05-22 16:05:19 -07001465
1466 // Flags bitwise-ored to mFlags
1467 private static final int FLAG_AVAILABLE_OFFLINE = 0x1;
Alex Hills9ab3a232016-04-05 14:54:56 -04001468 private static final int FLAG_HINT_LAUNCHES_ACTIVITY = 1 << 1;
Alex Hills9f087612016-06-07 09:08:59 -04001469 private static final int FLAG_HINT_DISPLAY_INLINE = 1 << 2;
Griff Hazen61a9e862014-05-22 16:05:19 -07001470
1471 // Default value for flags integer
1472 private static final int DEFAULT_FLAGS = FLAG_AVAILABLE_OFFLINE;
1473
1474 private int mFlags = DEFAULT_FLAGS;
1475
Pete Gastaf6781d2014-10-07 15:17:05 -04001476 private CharSequence mInProgressLabel;
1477 private CharSequence mConfirmLabel;
1478 private CharSequence mCancelLabel;
1479
Griff Hazen61a9e862014-05-22 16:05:19 -07001480 /**
1481 * Create a {@link android.app.Notification.Action.WearableExtender} with default
1482 * options.
1483 */
1484 public WearableExtender() {
1485 }
1486
1487 /**
1488 * Create a {@link android.app.Notification.Action.WearableExtender} by reading
1489 * wearable options present in an existing notification action.
1490 * @param action the notification action to inspect.
1491 */
1492 public WearableExtender(Action action) {
1493 Bundle wearableBundle = action.getExtras().getBundle(EXTRA_WEARABLE_EXTENSIONS);
1494 if (wearableBundle != null) {
1495 mFlags = wearableBundle.getInt(KEY_FLAGS, DEFAULT_FLAGS);
Pete Gastaf6781d2014-10-07 15:17:05 -04001496 mInProgressLabel = wearableBundle.getCharSequence(KEY_IN_PROGRESS_LABEL);
1497 mConfirmLabel = wearableBundle.getCharSequence(KEY_CONFIRM_LABEL);
1498 mCancelLabel = wearableBundle.getCharSequence(KEY_CANCEL_LABEL);
Griff Hazen61a9e862014-05-22 16:05:19 -07001499 }
1500 }
1501
1502 /**
1503 * Apply wearable extensions to a notification action that is being built. This is
1504 * typically called by the {@link android.app.Notification.Action.Builder#extend}
1505 * method of {@link android.app.Notification.Action.Builder}.
1506 */
1507 @Override
1508 public Action.Builder extend(Action.Builder builder) {
1509 Bundle wearableBundle = new Bundle();
1510
1511 if (mFlags != DEFAULT_FLAGS) {
1512 wearableBundle.putInt(KEY_FLAGS, mFlags);
1513 }
Pete Gastaf6781d2014-10-07 15:17:05 -04001514 if (mInProgressLabel != null) {
1515 wearableBundle.putCharSequence(KEY_IN_PROGRESS_LABEL, mInProgressLabel);
1516 }
1517 if (mConfirmLabel != null) {
1518 wearableBundle.putCharSequence(KEY_CONFIRM_LABEL, mConfirmLabel);
1519 }
1520 if (mCancelLabel != null) {
1521 wearableBundle.putCharSequence(KEY_CANCEL_LABEL, mCancelLabel);
1522 }
Griff Hazen61a9e862014-05-22 16:05:19 -07001523
1524 builder.getExtras().putBundle(EXTRA_WEARABLE_EXTENSIONS, wearableBundle);
1525 return builder;
1526 }
1527
1528 @Override
1529 public WearableExtender clone() {
1530 WearableExtender that = new WearableExtender();
1531 that.mFlags = this.mFlags;
Pete Gastaf6781d2014-10-07 15:17:05 -04001532 that.mInProgressLabel = this.mInProgressLabel;
1533 that.mConfirmLabel = this.mConfirmLabel;
1534 that.mCancelLabel = this.mCancelLabel;
Griff Hazen61a9e862014-05-22 16:05:19 -07001535 return that;
1536 }
1537
1538 /**
1539 * Set whether this action is available when the wearable device is not connected to
1540 * a companion device. The user can still trigger this action when the wearable device is
1541 * offline, but a visual hint will indicate that the action may not be available.
1542 * Defaults to true.
1543 */
1544 public WearableExtender setAvailableOffline(boolean availableOffline) {
1545 setFlag(FLAG_AVAILABLE_OFFLINE, availableOffline);
1546 return this;
1547 }
1548
1549 /**
1550 * Get whether this action is available when the wearable device is not connected to
1551 * a companion device. The user can still trigger this action when the wearable device is
1552 * offline, but a visual hint will indicate that the action may not be available.
1553 * Defaults to true.
1554 */
1555 public boolean isAvailableOffline() {
1556 return (mFlags & FLAG_AVAILABLE_OFFLINE) != 0;
1557 }
1558
1559 private void setFlag(int mask, boolean value) {
1560 if (value) {
1561 mFlags |= mask;
1562 } else {
1563 mFlags &= ~mask;
1564 }
1565 }
Pete Gastaf6781d2014-10-07 15:17:05 -04001566
1567 /**
1568 * Set a label to display while the wearable is preparing to automatically execute the
1569 * action. This is usually a 'ing' verb ending in ellipsis like "Sending..."
1570 *
1571 * @param label the label to display while the action is being prepared to execute
1572 * @return this object for method chaining
1573 */
1574 public WearableExtender setInProgressLabel(CharSequence label) {
1575 mInProgressLabel = label;
1576 return this;
1577 }
1578
1579 /**
1580 * Get the label to display while the wearable is preparing to automatically execute
1581 * the action. This is usually a 'ing' verb ending in ellipsis like "Sending..."
1582 *
1583 * @return the label to display while the action is being prepared to execute
1584 */
1585 public CharSequence getInProgressLabel() {
1586 return mInProgressLabel;
1587 }
1588
1589 /**
1590 * Set a label to display to confirm that the action should be executed.
1591 * This is usually an imperative verb like "Send".
1592 *
1593 * @param label the label to confirm the action should be executed
1594 * @return this object for method chaining
1595 */
1596 public WearableExtender setConfirmLabel(CharSequence label) {
1597 mConfirmLabel = label;
1598 return this;
1599 }
1600
1601 /**
1602 * Get the label to display to confirm that the action should be executed.
1603 * This is usually an imperative verb like "Send".
1604 *
1605 * @return the label to confirm the action should be executed
1606 */
1607 public CharSequence getConfirmLabel() {
1608 return mConfirmLabel;
1609 }
1610
1611 /**
1612 * Set a label to display to cancel the action.
1613 * This is usually an imperative verb, like "Cancel".
1614 *
1615 * @param label the label to display to cancel the action
1616 * @return this object for method chaining
1617 */
1618 public WearableExtender setCancelLabel(CharSequence label) {
1619 mCancelLabel = label;
1620 return this;
1621 }
1622
1623 /**
1624 * Get the label to display to cancel the action.
1625 * This is usually an imperative verb like "Cancel".
1626 *
1627 * @return the label to display to cancel the action
1628 */
1629 public CharSequence getCancelLabel() {
1630 return mCancelLabel;
1631 }
Alex Hills9ab3a232016-04-05 14:54:56 -04001632
1633 /**
1634 * Set a hint that this Action will launch an {@link Activity} directly, telling the
1635 * platform that it can generate the appropriate transitions.
1636 * @param hintLaunchesActivity {@code true} if the content intent will launch
1637 * an activity and transitions should be generated, false otherwise.
1638 * @return this object for method chaining
1639 */
Alex Hills4ec3ff42016-04-12 11:36:18 -04001640 public WearableExtender setHintLaunchesActivity(
Alex Hills9ab3a232016-04-05 14:54:56 -04001641 boolean hintLaunchesActivity) {
1642 setFlag(FLAG_HINT_LAUNCHES_ACTIVITY, hintLaunchesActivity);
1643 return this;
1644 }
1645
1646 /**
1647 * Get a hint that this Action will launch an {@link Activity} directly, telling the
1648 * platform that it can generate the appropriate transitions
1649 * @return {@code true} if the content intent will launch an activity and transitions
1650 * should be generated, false otherwise. The default value is {@code false} if this was
1651 * never set.
1652 */
Alex Hills4ec3ff42016-04-12 11:36:18 -04001653 public boolean getHintLaunchesActivity() {
Alex Hills9ab3a232016-04-05 14:54:56 -04001654 return (mFlags & FLAG_HINT_LAUNCHES_ACTIVITY) != 0;
1655 }
Alex Hills9f087612016-06-07 09:08:59 -04001656
1657 /**
1658 * Set a hint that this Action should be displayed inline.
1659 *
1660 * @param hintDisplayInline {@code true} if action should be displayed inline, false
1661 * otherwise
1662 * @return this object for method chaining
1663 */
1664 public WearableExtender setHintDisplayActionInline(
1665 boolean hintDisplayInline) {
1666 setFlag(FLAG_HINT_DISPLAY_INLINE, hintDisplayInline);
1667 return this;
1668 }
1669
1670 /**
1671 * Get a hint that this Action should be displayed inline.
1672 *
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08001673 * @return {@code true} if the Action should be displayed inline, {@code false}
Alex Hills9f087612016-06-07 09:08:59 -04001674 * otherwise. The default value is {@code false} if this was never set.
1675 */
1676 public boolean getHintDisplayActionInline() {
1677 return (mFlags & FLAG_HINT_DISPLAY_INLINE) != 0;
1678 }
Griff Hazen61a9e862014-05-22 16:05:19 -07001679 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001680 }
1681
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001682 /**
1683 * Array of all {@link Action} structures attached to this notification by
1684 * {@link Builder#addAction(int, CharSequence, PendingIntent)}. Mostly useful for instances of
1685 * {@link android.service.notification.NotificationListenerService} that provide an alternative
1686 * interface for invoking actions.
1687 */
Daniel Sandlerea2a3172013-02-20 22:24:20 -05001688 public Action[] actions;
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001689
1690 /**
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001691 * Replacement version of this notification whose content will be shown
1692 * in an insecure context such as atop a secure keyguard. See {@link #visibility}
1693 * and {@link #VISIBILITY_PUBLIC}.
1694 */
1695 public Notification publicVersion;
1696
1697 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001698 * Constructs a Notification object with default values.
Joe Onorato46439ce2010-11-19 13:56:21 -08001699 * You might want to consider using {@link Builder} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001700 */
1701 public Notification()
1702 {
1703 this.when = System.currentTimeMillis();
Selim Cinekb85f36fd2016-04-20 18:46:36 -07001704 this.creationTime = System.currentTimeMillis();
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001705 this.priority = PRIORITY_DEFAULT;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001706 }
1707
1708 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001709 * @hide
1710 */
1711 public Notification(Context context, int icon, CharSequence tickerText, long when,
1712 CharSequence contentTitle, CharSequence contentText, Intent contentIntent)
1713 {
Chris Wren1ce4b6d2015-06-11 10:19:43 -04001714 new Builder(context)
1715 .setWhen(when)
1716 .setSmallIcon(icon)
1717 .setTicker(tickerText)
1718 .setContentTitle(contentTitle)
1719 .setContentText(contentText)
1720 .setContentIntent(PendingIntent.getActivity(context, 0, contentIntent, 0))
1721 .buildInto(this);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001722 }
1723
1724 /**
1725 * Constructs a Notification object with the information needed to
1726 * have a status bar icon without the standard expanded view.
1727 *
1728 * @param icon The resource id of the icon to put in the status bar.
1729 * @param tickerText The text that flows by in the status bar when the notification first
1730 * activates.
1731 * @param when The time to show in the time field. In the System.currentTimeMillis
1732 * timebase.
Joe Onorato46439ce2010-11-19 13:56:21 -08001733 *
1734 * @deprecated Use {@link Builder} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001735 */
Joe Onorato46439ce2010-11-19 13:56:21 -08001736 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001737 public Notification(int icon, CharSequence tickerText, long when)
1738 {
1739 this.icon = icon;
1740 this.tickerText = tickerText;
1741 this.when = when;
Selim Cinekb85f36fd2016-04-20 18:46:36 -07001742 this.creationTime = System.currentTimeMillis();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001743 }
1744
1745 /**
1746 * Unflatten the notification from a parcel.
1747 */
Svet Ganovddb94882016-06-23 19:55:24 -07001748 @SuppressWarnings("unchecked")
1749 public Notification(Parcel parcel) {
1750 // IMPORTANT: Add unmarshaling code in readFromParcel as the pending
1751 // intents in extras are always written as the last entry.
1752 readFromParcelImpl(parcel);
1753 // Must be read last!
Felipe Lemedd85da62016-06-28 11:29:54 -07001754 allPendingIntents = (ArraySet<PendingIntent>) parcel.readArraySet(null);
Svet Ganovddb94882016-06-23 19:55:24 -07001755 }
1756
1757 private void readFromParcelImpl(Parcel parcel)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001758 {
1759 int version = parcel.readInt();
1760
1761 when = parcel.readLong();
Selim Cinekb85f36fd2016-04-20 18:46:36 -07001762 creationTime = parcel.readLong();
Dan Sandler3936e7a2015-05-19 20:59:12 -04001763 if (parcel.readInt() != 0) {
1764 mSmallIcon = Icon.CREATOR.createFromParcel(parcel);
Dan Sandler4e787062015-06-17 15:09:48 -04001765 if (mSmallIcon.getType() == Icon.TYPE_RESOURCE) {
1766 icon = mSmallIcon.getResId();
1767 }
Dan Sandler3936e7a2015-05-19 20:59:12 -04001768 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001769 number = parcel.readInt();
1770 if (parcel.readInt() != 0) {
1771 contentIntent = PendingIntent.CREATOR.createFromParcel(parcel);
1772 }
1773 if (parcel.readInt() != 0) {
1774 deleteIntent = PendingIntent.CREATOR.createFromParcel(parcel);
1775 }
1776 if (parcel.readInt() != 0) {
1777 tickerText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
1778 }
1779 if (parcel.readInt() != 0) {
Joe Onorato46439ce2010-11-19 13:56:21 -08001780 tickerView = RemoteViews.CREATOR.createFromParcel(parcel);
Joe Onoratoef1e7762010-09-17 18:38:38 -04001781 }
1782 if (parcel.readInt() != 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001783 contentView = RemoteViews.CREATOR.createFromParcel(parcel);
1784 }
Joe Onorato561d3852010-11-20 18:09:34 -08001785 if (parcel.readInt() != 0) {
Dan Sandlerd63f9322015-05-06 15:18:49 -04001786 mLargeIcon = Icon.CREATOR.createFromParcel(parcel);
Joe Onorato561d3852010-11-20 18:09:34 -08001787 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001788 defaults = parcel.readInt();
1789 flags = parcel.readInt();
1790 if (parcel.readInt() != 0) {
1791 sound = Uri.CREATOR.createFromParcel(parcel);
1792 }
1793
1794 audioStreamType = parcel.readInt();
John Spurlockc0650f022014-07-19 13:22:39 -04001795 if (parcel.readInt() != 0) {
1796 audioAttributes = AudioAttributes.CREATOR.createFromParcel(parcel);
1797 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001798 vibrate = parcel.createLongArray();
1799 ledARGB = parcel.readInt();
1800 ledOnMS = parcel.readInt();
1801 ledOffMS = parcel.readInt();
1802 iconLevel = parcel.readInt();
Daniel Sandlere46cbd32010-06-17 10:35:26 -04001803
1804 if (parcel.readInt() != 0) {
1805 fullScreenIntent = PendingIntent.CREATOR.createFromParcel(parcel);
1806 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001807
1808 priority = parcel.readInt();
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001809
John Spurlockfd7f1e02014-03-18 16:41:57 -04001810 category = parcel.readString();
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001811
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001812 mGroupKey = parcel.readString();
1813
1814 mSortKey = parcel.readString();
1815
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001816 extras = Bundle.setDefusable(parcel.readBundle(), true); // may be null
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001817
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001818 actions = parcel.createTypedArray(Action.CREATOR); // may be null
1819
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001820 if (parcel.readInt() != 0) {
Daniel Sandlerf3b73432012-03-27 15:01:25 -04001821 bigContentView = RemoteViews.CREATOR.createFromParcel(parcel);
1822 }
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001823
Chris Wren8fd39ec2014-02-27 17:43:26 -05001824 if (parcel.readInt() != 0) {
1825 headsUpContentView = RemoteViews.CREATOR.createFromParcel(parcel);
1826 }
1827
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001828 visibility = parcel.readInt();
1829
1830 if (parcel.readInt() != 0) {
1831 publicVersion = Notification.CREATOR.createFromParcel(parcel);
1832 }
Dan Sandler26e81cf2014-05-06 10:01:27 -04001833
1834 color = parcel.readInt();
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04001835
1836 if (parcel.readInt() != 0) {
1837 mChannelId = parcel.readString();
1838 }
Julia Reynolds2a128742016-11-28 14:29:25 -05001839 mTimeout = parcel.readLong();
Julia Reynolds13d898c2017-02-02 12:22:05 -05001840
1841 if (parcel.readInt() != 0) {
1842 mShortcutId = parcel.readString();
1843 }
1844
1845 mBadgeIcon = parcel.readInt();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001846 }
1847
Andy Stadler110988c2010-12-03 14:29:16 -08001848 @Override
Joe Onorato18e69df2010-05-17 22:26:12 -07001849 public Notification clone() {
1850 Notification that = new Notification();
Daniel Sandler1a497d32013-04-18 14:52:45 -04001851 cloneInto(that, true);
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001852 return that;
1853 }
Joe Onorato18e69df2010-05-17 22:26:12 -07001854
Daniel Sandler1a497d32013-04-18 14:52:45 -04001855 /**
1856 * Copy all (or if heavy is false, all except Bitmaps and RemoteViews) members
1857 * of this into that.
1858 * @hide
1859 */
1860 public void cloneInto(Notification that, boolean heavy) {
Joe Onorato18e69df2010-05-17 22:26:12 -07001861 that.when = this.when;
Selim Cinekb85f36fd2016-04-20 18:46:36 -07001862 that.creationTime = this.creationTime;
Dan Sandlerd63f9322015-05-06 15:18:49 -04001863 that.mSmallIcon = this.mSmallIcon;
Joe Onorato18e69df2010-05-17 22:26:12 -07001864 that.number = this.number;
1865
1866 // PendingIntents are global, so there's no reason (or way) to clone them.
1867 that.contentIntent = this.contentIntent;
1868 that.deleteIntent = this.deleteIntent;
Daniel Sandlere46cbd32010-06-17 10:35:26 -04001869 that.fullScreenIntent = this.fullScreenIntent;
Joe Onorato18e69df2010-05-17 22:26:12 -07001870
1871 if (this.tickerText != null) {
1872 that.tickerText = this.tickerText.toString();
1873 }
Daniel Sandler1a497d32013-04-18 14:52:45 -04001874 if (heavy && this.tickerView != null) {
Joe Onorato46439ce2010-11-19 13:56:21 -08001875 that.tickerView = this.tickerView.clone();
Joe Onoratoef1e7762010-09-17 18:38:38 -04001876 }
Daniel Sandler1a497d32013-04-18 14:52:45 -04001877 if (heavy && this.contentView != null) {
Joe Onorato18e69df2010-05-17 22:26:12 -07001878 that.contentView = this.contentView.clone();
1879 }
Dan Sandlerd63f9322015-05-06 15:18:49 -04001880 if (heavy && this.mLargeIcon != null) {
1881 that.mLargeIcon = this.mLargeIcon;
Joe Onorato561d3852010-11-20 18:09:34 -08001882 }
Jozef BABJAKa8b91832011-02-22 08:05:08 +01001883 that.iconLevel = this.iconLevel;
Joe Onorato18e69df2010-05-17 22:26:12 -07001884 that.sound = this.sound; // android.net.Uri is immutable
1885 that.audioStreamType = this.audioStreamType;
John Spurlockc0650f022014-07-19 13:22:39 -04001886 if (this.audioAttributes != null) {
1887 that.audioAttributes = new AudioAttributes.Builder(this.audioAttributes).build();
1888 }
Joe Onorato18e69df2010-05-17 22:26:12 -07001889
1890 final long[] vibrate = this.vibrate;
1891 if (vibrate != null) {
1892 final int N = vibrate.length;
1893 final long[] vib = that.vibrate = new long[N];
1894 System.arraycopy(vibrate, 0, vib, 0, N);
1895 }
1896
1897 that.ledARGB = this.ledARGB;
1898 that.ledOnMS = this.ledOnMS;
1899 that.ledOffMS = this.ledOffMS;
1900 that.defaults = this.defaults;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001901
Joe Onorato18e69df2010-05-17 22:26:12 -07001902 that.flags = this.flags;
1903
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001904 that.priority = this.priority;
Joe Malin8d40d042012-11-05 11:36:40 -08001905
John Spurlockfd7f1e02014-03-18 16:41:57 -04001906 that.category = this.category;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001907
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001908 that.mGroupKey = this.mGroupKey;
1909
1910 that.mSortKey = this.mSortKey;
1911
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001912 if (this.extras != null) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04001913 try {
1914 that.extras = new Bundle(this.extras);
1915 // will unparcel
1916 that.extras.size();
1917 } catch (BadParcelableException e) {
1918 Log.e(TAG, "could not unparcel extras from notification: " + this, e);
1919 that.extras = null;
1920 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001921 }
1922
Felipe Lemedd85da62016-06-28 11:29:54 -07001923 if (!ArrayUtils.isEmpty(allPendingIntents)) {
1924 that.allPendingIntents = new ArraySet<>(allPendingIntents);
Svet Ganovddb94882016-06-23 19:55:24 -07001925 }
1926
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001927 if (this.actions != null) {
1928 that.actions = new Action[this.actions.length];
1929 for(int i=0; i<this.actions.length; i++) {
1930 that.actions[i] = this.actions[i].clone();
1931 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001932 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001933
Daniel Sandler1a497d32013-04-18 14:52:45 -04001934 if (heavy && this.bigContentView != null) {
Daniel Sandlerf3b73432012-03-27 15:01:25 -04001935 that.bigContentView = this.bigContentView.clone();
1936 }
Daniel Sandler1a497d32013-04-18 14:52:45 -04001937
Chris Wren8fd39ec2014-02-27 17:43:26 -05001938 if (heavy && this.headsUpContentView != null) {
1939 that.headsUpContentView = this.headsUpContentView.clone();
1940 }
1941
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001942 that.visibility = this.visibility;
1943
1944 if (this.publicVersion != null) {
1945 that.publicVersion = new Notification();
1946 this.publicVersion.cloneInto(that.publicVersion, heavy);
1947 }
1948
Dan Sandler26e81cf2014-05-06 10:01:27 -04001949 that.color = this.color;
1950
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04001951 that.mChannelId = this.mChannelId;
Julia Reynolds2a128742016-11-28 14:29:25 -05001952 that.mTimeout = this.mTimeout;
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04001953
Daniel Sandler1a497d32013-04-18 14:52:45 -04001954 if (!heavy) {
1955 that.lightenPayload(); // will clean out extras
1956 }
1957 }
1958
1959 /**
1960 * Removes heavyweight parts of the Notification object for archival or for sending to
1961 * listeners when the full contents are not necessary.
1962 * @hide
1963 */
1964 public final void lightenPayload() {
1965 tickerView = null;
1966 contentView = null;
1967 bigContentView = null;
Chris Wren8fd39ec2014-02-27 17:43:26 -05001968 headsUpContentView = null;
Dan Sandlerd63f9322015-05-06 15:18:49 -04001969 mLargeIcon = null;
Dan Sandler50128532015-12-08 15:42:41 -05001970 if (extras != null && !extras.isEmpty()) {
1971 final Set<String> keyset = extras.keySet();
1972 final int N = keyset.size();
1973 final String[] keys = keyset.toArray(new String[N]);
1974 for (int i=0; i<N; i++) {
1975 final String key = keys[i];
Dmitri Plotnikov22281362017-01-30 11:16:21 -08001976 if (TvExtender.EXTRA_TV_EXTENDER.equals(key)) {
1977 continue;
1978 }
Dan Sandler50128532015-12-08 15:42:41 -05001979 final Object obj = extras.get(key);
1980 if (obj != null &&
1981 ( obj instanceof Parcelable
1982 || obj instanceof Parcelable[]
1983 || obj instanceof SparseArray
1984 || obj instanceof ArrayList)) {
1985 extras.remove(key);
1986 }
1987 }
Daniel Sandler1a497d32013-04-18 14:52:45 -04001988 }
Joe Onorato18e69df2010-05-17 22:26:12 -07001989 }
1990
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04001991 /**
1992 * Make sure this CharSequence is safe to put into a bundle, which basically
1993 * means it had better not be some custom Parcelable implementation.
1994 * @hide
1995 */
1996 public static CharSequence safeCharSequence(CharSequence cs) {
Christoph Studer535ec612014-09-03 15:47:47 +02001997 if (cs == null) return cs;
1998 if (cs.length() > MAX_CHARSEQUENCE_LENGTH) {
1999 cs = cs.subSequence(0, MAX_CHARSEQUENCE_LENGTH);
2000 }
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04002001 if (cs instanceof Parcelable) {
2002 Log.e(TAG, "warning: " + cs.getClass().getCanonicalName()
2003 + " instance is a custom Parcelable and not allowed in Notification");
2004 return cs.toString();
2005 }
Selim Cinek60a54252016-02-26 17:03:25 -08002006 return removeTextSizeSpans(cs);
2007 }
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04002008
Selim Cinek60a54252016-02-26 17:03:25 -08002009 private static CharSequence removeTextSizeSpans(CharSequence charSequence) {
2010 if (charSequence instanceof Spanned) {
2011 Spanned ss = (Spanned) charSequence;
2012 Object[] spans = ss.getSpans(0, ss.length(), Object.class);
2013 SpannableStringBuilder builder = new SpannableStringBuilder(ss.toString());
2014 for (Object span : spans) {
2015 Object resultSpan = span;
Selim Cinek89991a22016-03-07 19:51:54 -08002016 if (resultSpan instanceof CharacterStyle) {
2017 resultSpan = ((CharacterStyle) span).getUnderlying();
2018 }
2019 if (resultSpan instanceof TextAppearanceSpan) {
2020 TextAppearanceSpan originalSpan = (TextAppearanceSpan) resultSpan;
Selim Cinek60a54252016-02-26 17:03:25 -08002021 resultSpan = new TextAppearanceSpan(
2022 originalSpan.getFamily(),
2023 originalSpan.getTextStyle(),
2024 -1,
2025 originalSpan.getTextColor(),
2026 originalSpan.getLinkTextColor());
Selim Cinek89991a22016-03-07 19:51:54 -08002027 } else if (resultSpan instanceof RelativeSizeSpan
2028 || resultSpan instanceof AbsoluteSizeSpan) {
Selim Cinek60a54252016-02-26 17:03:25 -08002029 continue;
Selim Cinek89991a22016-03-07 19:51:54 -08002030 } else {
2031 resultSpan = span;
Selim Cinek60a54252016-02-26 17:03:25 -08002032 }
2033 builder.setSpan(resultSpan, ss.getSpanStart(span), ss.getSpanEnd(span),
2034 ss.getSpanFlags(span));
2035 }
2036 return builder;
2037 }
2038 return charSequence;
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04002039 }
2040
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002041 public int describeContents() {
2042 return 0;
2043 }
2044
2045 /**
Dan Sandler4e787062015-06-17 15:09:48 -04002046 * Flatten this notification into a parcel.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002047 */
Svet Ganovddb94882016-06-23 19:55:24 -07002048 public void writeToParcel(Parcel parcel, int flags) {
2049 // We need to mark all pending intents getting into the notification
2050 // system as being put there to later allow the notification ranker
2051 // to launch them and by doing so add the app to the battery saver white
2052 // list for a short period of time. The problem is that the system
2053 // cannot look into the extras as there may be parcelables there that
2054 // the platform does not know how to handle. To go around that we have
2055 // an explicit list of the pending intents in the extras bundle.
Felipe Lemedd85da62016-06-28 11:29:54 -07002056 final boolean collectPendingIntents = (allPendingIntents == null);
Svet Ganovddb94882016-06-23 19:55:24 -07002057 if (collectPendingIntents) {
2058 PendingIntent.setOnMarshaledListener(
2059 (PendingIntent intent, Parcel out, int outFlags) -> {
2060 if (parcel == out) {
Felipe Lemedd85da62016-06-28 11:29:54 -07002061 if (allPendingIntents == null) {
2062 allPendingIntents = new ArraySet<>();
Svet Ganovddb94882016-06-23 19:55:24 -07002063 }
Felipe Lemedd85da62016-06-28 11:29:54 -07002064 allPendingIntents.add(intent);
Svet Ganovddb94882016-06-23 19:55:24 -07002065 }
2066 });
2067 }
2068 try {
2069 // IMPORTANT: Add marshaling code in writeToParcelImpl as we
Julia Reynolds13d898c2017-02-02 12:22:05 -05002070 // want to intercept all pending events written to the parcel.
Svet Ganovddb94882016-06-23 19:55:24 -07002071 writeToParcelImpl(parcel, flags);
2072 // Must be written last!
Felipe Lemedd85da62016-06-28 11:29:54 -07002073 parcel.writeArraySet(allPendingIntents);
Svet Ganovddb94882016-06-23 19:55:24 -07002074 } finally {
2075 if (collectPendingIntents) {
2076 PendingIntent.setOnMarshaledListener(null);
2077 }
2078 }
2079 }
2080
2081 private void writeToParcelImpl(Parcel parcel, int flags) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002082 parcel.writeInt(1);
2083
2084 parcel.writeLong(when);
Selim Cinekb85f36fd2016-04-20 18:46:36 -07002085 parcel.writeLong(creationTime);
Dan Sandler4e787062015-06-17 15:09:48 -04002086 if (mSmallIcon == null && icon != 0) {
2087 // you snuck an icon in here without using the builder; let's try to keep it
2088 mSmallIcon = Icon.createWithResource("", icon);
2089 }
Dan Sandler3936e7a2015-05-19 20:59:12 -04002090 if (mSmallIcon != null) {
2091 parcel.writeInt(1);
2092 mSmallIcon.writeToParcel(parcel, 0);
2093 } else {
2094 parcel.writeInt(0);
2095 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002096 parcel.writeInt(number);
2097 if (contentIntent != null) {
2098 parcel.writeInt(1);
2099 contentIntent.writeToParcel(parcel, 0);
2100 } else {
2101 parcel.writeInt(0);
2102 }
2103 if (deleteIntent != null) {
2104 parcel.writeInt(1);
2105 deleteIntent.writeToParcel(parcel, 0);
2106 } else {
2107 parcel.writeInt(0);
2108 }
2109 if (tickerText != null) {
2110 parcel.writeInt(1);
2111 TextUtils.writeToParcel(tickerText, parcel, flags);
2112 } else {
2113 parcel.writeInt(0);
2114 }
Joe Onorato46439ce2010-11-19 13:56:21 -08002115 if (tickerView != null) {
Joe Onoratoef1e7762010-09-17 18:38:38 -04002116 parcel.writeInt(1);
Joe Onorato46439ce2010-11-19 13:56:21 -08002117 tickerView.writeToParcel(parcel, 0);
Joe Onoratoef1e7762010-09-17 18:38:38 -04002118 } else {
2119 parcel.writeInt(0);
2120 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002121 if (contentView != null) {
2122 parcel.writeInt(1);
2123 contentView.writeToParcel(parcel, 0);
2124 } else {
2125 parcel.writeInt(0);
2126 }
Selim Cinek279fa862016-06-14 10:57:25 -07002127 if (mLargeIcon == null && largeIcon != null) {
2128 // you snuck an icon in here without using the builder; let's try to keep it
2129 mLargeIcon = Icon.createWithBitmap(largeIcon);
2130 }
Dan Sandlerd63f9322015-05-06 15:18:49 -04002131 if (mLargeIcon != null) {
Joe Onorato561d3852010-11-20 18:09:34 -08002132 parcel.writeInt(1);
Dan Sandlerd63f9322015-05-06 15:18:49 -04002133 mLargeIcon.writeToParcel(parcel, 0);
Joe Onorato561d3852010-11-20 18:09:34 -08002134 } else {
2135 parcel.writeInt(0);
2136 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002137
2138 parcel.writeInt(defaults);
2139 parcel.writeInt(this.flags);
2140
2141 if (sound != null) {
2142 parcel.writeInt(1);
2143 sound.writeToParcel(parcel, 0);
2144 } else {
2145 parcel.writeInt(0);
2146 }
2147 parcel.writeInt(audioStreamType);
John Spurlockc0650f022014-07-19 13:22:39 -04002148
2149 if (audioAttributes != null) {
2150 parcel.writeInt(1);
2151 audioAttributes.writeToParcel(parcel, 0);
2152 } else {
2153 parcel.writeInt(0);
2154 }
2155
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002156 parcel.writeLongArray(vibrate);
2157 parcel.writeInt(ledARGB);
2158 parcel.writeInt(ledOnMS);
2159 parcel.writeInt(ledOffMS);
2160 parcel.writeInt(iconLevel);
Daniel Sandlere46cbd32010-06-17 10:35:26 -04002161
2162 if (fullScreenIntent != null) {
2163 parcel.writeInt(1);
2164 fullScreenIntent.writeToParcel(parcel, 0);
2165 } else {
2166 parcel.writeInt(0);
2167 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002168
2169 parcel.writeInt(priority);
Joe Malin8d40d042012-11-05 11:36:40 -08002170
John Spurlockfd7f1e02014-03-18 16:41:57 -04002171 parcel.writeString(category);
Joe Malin8d40d042012-11-05 11:36:40 -08002172
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002173 parcel.writeString(mGroupKey);
2174
2175 parcel.writeString(mSortKey);
2176
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002177 parcel.writeBundle(extras); // null ok
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002178
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002179 parcel.writeTypedArray(actions, 0); // null ok
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002180
Daniel Sandlerf3b73432012-03-27 15:01:25 -04002181 if (bigContentView != null) {
2182 parcel.writeInt(1);
2183 bigContentView.writeToParcel(parcel, 0);
2184 } else {
2185 parcel.writeInt(0);
2186 }
Dan Sandler0bf2ed82013-12-21 23:33:41 -06002187
Chris Wren8fd39ec2014-02-27 17:43:26 -05002188 if (headsUpContentView != null) {
2189 parcel.writeInt(1);
2190 headsUpContentView.writeToParcel(parcel, 0);
2191 } else {
2192 parcel.writeInt(0);
2193 }
2194
Dan Sandler0bf2ed82013-12-21 23:33:41 -06002195 parcel.writeInt(visibility);
2196
2197 if (publicVersion != null) {
2198 parcel.writeInt(1);
2199 publicVersion.writeToParcel(parcel, 0);
2200 } else {
2201 parcel.writeInt(0);
2202 }
Dan Sandler26e81cf2014-05-06 10:01:27 -04002203
2204 parcel.writeInt(color);
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04002205
2206 if (mChannelId != null) {
2207 parcel.writeInt(1);
2208 parcel.writeString(mChannelId);
2209 } else {
2210 parcel.writeInt(0);
2211 }
Julia Reynolds2a128742016-11-28 14:29:25 -05002212 parcel.writeLong(mTimeout);
Julia Reynolds13d898c2017-02-02 12:22:05 -05002213
2214 if (mShortcutId != null) {
2215 parcel.writeInt(1);
2216 parcel.writeString(mShortcutId);
2217 } else {
2218 parcel.writeInt(0);
2219 }
2220
2221 parcel.writeInt(mBadgeIcon);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002222 }
2223
2224 /**
2225 * Parcelable.Creator that instantiates Notification objects
2226 */
2227 public static final Parcelable.Creator<Notification> CREATOR
2228 = new Parcelable.Creator<Notification>()
2229 {
2230 public Notification createFromParcel(Parcel parcel)
2231 {
2232 return new Notification(parcel);
2233 }
2234
2235 public Notification[] newArray(int size)
2236 {
2237 return new Notification[size];
2238 }
2239 };
2240
2241 /**
2242 * Sets the {@link #contentView} field to be a view with the standard "Latest Event"
2243 * layout.
2244 *
2245 * <p>Uses the {@link #icon} and {@link #when} fields to set the icon and time fields
2246 * in the view.</p>
2247 * @param context The context for your application / activity.
2248 * @param contentTitle The title that goes in the expanded entry.
2249 * @param contentText The text that goes in the expanded entry.
2250 * @param contentIntent The intent to launch when the user clicks the expanded notification.
2251 * If this is an activity, it must include the
2252 * {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} flag, which requires
Scott Main7aee61f2011-02-08 11:25:01 -08002253 * that you take care of task management as described in the
2254 * <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back
2255 * Stack</a> document.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002256 *
Joe Onorato46439ce2010-11-19 13:56:21 -08002257 * @deprecated Use {@link Builder} instead.
Chris Wrena05db382015-06-24 15:18:34 -04002258 * @removed
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002259 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002260 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002261 public void setLatestEventInfo(Context context,
2262 CharSequence contentTitle, CharSequence contentText, PendingIntent contentIntent) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002263 if (context.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1){
2264 Log.e(TAG, "setLatestEventInfo() is deprecated and you should feel deprecated.",
2265 new Throwable());
2266 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002267
Selim Cinek4ac6f602016-06-13 15:47:03 -07002268 if (context.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N) {
2269 extras.putBoolean(EXTRA_SHOW_WHEN, true);
2270 }
2271
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002272 // ensure that any information already set directly is preserved
2273 final Notification.Builder builder = new Notification.Builder(context, this);
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002274
2275 // now apply the latestEventInfo fields
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002276 if (contentTitle != null) {
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002277 builder.setContentTitle(contentTitle);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002278 }
2279 if (contentText != null) {
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002280 builder.setContentText(contentText);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002281 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002282 builder.setContentIntent(contentIntent);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002283
2284 builder.build(); // callers expect this notification to be ready to use
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002285 }
2286
Julia Reynoldsda303542015-11-23 14:00:20 -05002287 /**
2288 * @hide
2289 */
2290 public static void addFieldsFromContext(Context context, Notification notification) {
Jeff Sharkey012bc7b2016-04-11 16:30:27 -06002291 addFieldsFromContext(context.getApplicationInfo(), context.getUserId(), notification);
2292 }
2293
2294 /**
2295 * @hide
2296 */
2297 public static void addFieldsFromContext(ApplicationInfo ai, int userId,
2298 Notification notification) {
2299 notification.extras.putParcelable(EXTRA_BUILDER_APPLICATION_INFO, ai);
2300 notification.extras.putInt(EXTRA_ORIGINATING_USERID, userId);
Julia Reynoldsda303542015-11-23 14:00:20 -05002301 }
2302
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002303 @Override
2304 public String toString() {
2305 StringBuilder sb = new StringBuilder();
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002306 sb.append("Notification(pri=");
2307 sb.append(priority);
2308 sb.append(" contentView=");
Joe Onoratoc9596d62011-01-12 17:03:11 -08002309 if (contentView != null) {
2310 sb.append(contentView.getPackage());
2311 sb.append("/0x");
2312 sb.append(Integer.toHexString(contentView.getLayoutId()));
2313 } else {
2314 sb.append("null");
2315 }
2316 sb.append(" vibrate=");
Daniel Sandler6738eee2012-11-16 12:03:32 -05002317 if ((this.defaults & DEFAULT_VIBRATE) != 0) {
2318 sb.append("default");
2319 } else if (this.vibrate != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002320 int N = this.vibrate.length-1;
2321 sb.append("[");
2322 for (int i=0; i<N; i++) {
2323 sb.append(this.vibrate[i]);
2324 sb.append(',');
2325 }
Simon Schoar8cf97d92009-06-10 22:08:37 +02002326 if (N != -1) {
2327 sb.append(this.vibrate[N]);
2328 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002329 sb.append("]");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002330 } else {
2331 sb.append("null");
2332 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002333 sb.append(" sound=");
Daniel Sandler6738eee2012-11-16 12:03:32 -05002334 if ((this.defaults & DEFAULT_SOUND) != 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002335 sb.append("default");
Daniel Sandler6738eee2012-11-16 12:03:32 -05002336 } else if (this.sound != null) {
2337 sb.append(this.sound.toString());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002338 } else {
2339 sb.append("null");
2340 }
Chris Wren365b6d32015-07-16 10:39:26 -04002341 if (this.tickerText != null) {
2342 sb.append(" tick");
2343 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002344 sb.append(" defaults=0x");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002345 sb.append(Integer.toHexString(this.defaults));
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002346 sb.append(" flags=0x");
Daniel Sandlere46cbd32010-06-17 10:35:26 -04002347 sb.append(Integer.toHexString(this.flags));
Dan Sandler26e81cf2014-05-06 10:01:27 -04002348 sb.append(String.format(" color=0x%08x", this.color));
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002349 if (this.category != null) {
2350 sb.append(" category=");
2351 sb.append(this.category);
2352 }
2353 if (this.mGroupKey != null) {
2354 sb.append(" groupKey=");
2355 sb.append(this.mGroupKey);
2356 }
2357 if (this.mSortKey != null) {
2358 sb.append(" sortKey=");
2359 sb.append(this.mSortKey);
2360 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002361 if (actions != null) {
Dan Sandler1b718782014-07-18 12:43:45 -04002362 sb.append(" actions=");
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002363 sb.append(actions.length);
Dan Sandler1b718782014-07-18 12:43:45 -04002364 }
2365 sb.append(" vis=");
2366 sb.append(visibilityToString(this.visibility));
2367 if (this.publicVersion != null) {
2368 sb.append(" publicVersion=");
2369 sb.append(publicVersion.toString());
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002370 }
2371 sb.append(")");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002372 return sb.toString();
2373 }
Joe Onorato46439ce2010-11-19 13:56:21 -08002374
Dan Sandler1b718782014-07-18 12:43:45 -04002375 /**
2376 * {@hide}
2377 */
2378 public static String visibilityToString(int vis) {
2379 switch (vis) {
2380 case VISIBILITY_PRIVATE:
2381 return "PRIVATE";
2382 case VISIBILITY_PUBLIC:
2383 return "PUBLIC";
2384 case VISIBILITY_SECRET:
2385 return "SECRET";
2386 default:
2387 return "UNKNOWN(" + String.valueOf(vis) + ")";
2388 }
2389 }
2390
Joe Onoratocb109a02011-01-18 17:57:41 -08002391 /**
John Spurlock1d881a12015-03-18 19:21:54 -04002392 * {@hide}
2393 */
2394 public static String priorityToString(@Priority int pri) {
2395 switch (pri) {
2396 case PRIORITY_MIN:
2397 return "MIN";
2398 case PRIORITY_LOW:
2399 return "LOW";
2400 case PRIORITY_DEFAULT:
2401 return "DEFAULT";
2402 case PRIORITY_HIGH:
2403 return "HIGH";
2404 case PRIORITY_MAX:
2405 return "MAX";
2406 default:
2407 return "UNKNOWN(" + String.valueOf(pri) + ")";
2408 }
2409 }
2410
2411 /**
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04002412 * Returns the id of the channel this notification posts to.
2413 */
Julia Reynolds37856052016-11-11 09:20:07 -05002414 public String getChannel() {
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04002415 return mChannelId;
2416 }
2417
2418 /**
Julia Reynolds13d898c2017-02-02 12:22:05 -05002419 * Returns the time at which this notification should be canceled by the system, if it's not
2420 * canceled already.
Julia Reynolds2a128742016-11-28 14:29:25 -05002421 */
2422 public long getTimeout() {
2423 return mTimeout;
2424 }
2425
2426 /**
Julia Reynolds13d898c2017-02-02 12:22:05 -05002427 * Returns what icon should be shown for this notification if it is being displayed in a
2428 * Launcher that supports badging. Will be one of {@link #BADGE_ICON_NONE},
2429 * {@link #BADGE_ICON_SMALL}, or {@link #BADGE_ICON_LARGE}.
2430 */
2431 public int getBadgeIcon() {
2432 return mBadgeIcon;
2433 }
2434
2435 /**
2436 * Returns the {@link ShortcutInfo#getId() id} that this notification supersedes, if any.
2437 */
2438 public String getShortcutId() {
2439 return mShortcutId;
2440 }
2441
2442 /**
Dan Sandlerd63f9322015-05-06 15:18:49 -04002443 * The small icon representing this notification in the status bar and content view.
2444 *
2445 * @return the small icon representing this notification.
2446 *
2447 * @see Builder#getSmallIcon()
2448 * @see Builder#setSmallIcon(Icon)
2449 */
2450 public Icon getSmallIcon() {
2451 return mSmallIcon;
2452 }
2453
2454 /**
2455 * Used when notifying to clean up legacy small icons.
2456 * @hide
2457 */
2458 public void setSmallIcon(Icon icon) {
2459 mSmallIcon = icon;
2460 }
2461
2462 /**
2463 * The large icon shown in this notification's content view.
2464 * @see Builder#getLargeIcon()
2465 * @see Builder#setLargeIcon(Icon)
2466 */
2467 public Icon getLargeIcon() {
2468 return mLargeIcon;
2469 }
2470
2471 /**
Christoph Studer4600f9b2014-07-22 22:44:43 +02002472 * @hide
2473 */
Christoph Studerc8db24b2014-07-25 17:50:30 +02002474 public boolean isGroupSummary() {
2475 return mGroupKey != null && (flags & FLAG_GROUP_SUMMARY) != 0;
2476 }
2477
2478 /**
2479 * @hide
2480 */
2481 public boolean isGroupChild() {
2482 return mGroupKey != null && (flags & FLAG_GROUP_SUMMARY) == 0;
2483 }
2484
2485 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002486 * Builder class for {@link Notification} objects.
Joe Malin8d40d042012-11-05 11:36:40 -08002487 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002488 * Provides a convenient way to set the various fields of a {@link Notification} and generate
Scott Main183bf112012-08-13 19:12:13 -07002489 * content views using the platform's notification layout template. If your app supports
2490 * versions of Android as old as API level 4, you can instead use
2491 * {@link android.support.v4.app.NotificationCompat.Builder NotificationCompat.Builder},
2492 * available in the <a href="{@docRoot}tools/extras/support-library.html">Android Support
2493 * library</a>.
Joe Malin8d40d042012-11-05 11:36:40 -08002494 *
Scott Main183bf112012-08-13 19:12:13 -07002495 * <p>Example:
Joe Malin8d40d042012-11-05 11:36:40 -08002496 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002497 * <pre class="prettyprint">
Scott Main183bf112012-08-13 19:12:13 -07002498 * Notification noti = new Notification.Builder(mContext)
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002499 * .setContentTitle(&quot;New mail from &quot; + sender.toString())
2500 * .setContentText(subject)
2501 * .setSmallIcon(R.drawable.new_mail)
2502 * .setLargeIcon(aBitmap)
Chris Wrenfbd96ba2012-05-01 12:03:58 -04002503 * .build();
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002504 * </pre>
Joe Onoratocb109a02011-01-18 17:57:41 -08002505 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002506 public static class Builder {
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05002507 /**
2508 * @hide
2509 */
2510 public static final String EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT =
2511 "android.rebuild.contentViewActionCount";
2512 /**
2513 * @hide
2514 */
2515 public static final String EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT
2516 = "android.rebuild.bigViewActionCount";
2517 /**
2518 * @hide
2519 */
2520 public static final String EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT
2521 = "android.rebuild.hudViewActionCount";
2522
Daniel Sandler602ad1c2012-06-12 16:06:27 -04002523 private static final int MAX_ACTION_BUTTONS = 3;
Daniel Sandler8680bf82012-05-15 16:52:52 -04002524
Selim Cinek6743c0b2017-01-18 18:24:01 -08002525 private static final boolean USE_ONLY_TITLE_IN_LOW_PRIORITY_SUMMARY =
2526 SystemProperties.getBoolean("notifications.only_title", true);
2527
Joe Onorato46439ce2010-11-19 13:56:21 -08002528 private Context mContext;
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002529 private Notification mN;
2530 private Bundle mUserExtras = new Bundle();
Chris Wrenfbd96ba2012-05-01 12:03:58 -04002531 private Style mStyle;
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002532 private ArrayList<Action> mActions = new ArrayList<Action>(MAX_ACTION_BUTTONS);
2533 private ArrayList<String> mPersonList = new ArrayList<String>();
2534 private NotificationColorUtil mColorUtil;
Selim Cinek7b9605b2017-01-19 17:36:00 -08002535 private boolean mIsLegacy;
2536 private boolean mIsLegacyInitialized;
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002537 private boolean mColorUtilInited = false;
Christoph Studer7ac80e62014-08-04 16:01:57 +02002538
2539 /**
Adrian Roos4ff3b122016-02-01 12:26:13 -08002540 * Caches a contrast-enhanced version of {@link #mCachedContrastColorIsFor}.
2541 */
2542 private int mCachedContrastColor = COLOR_INVALID;
2543 private int mCachedContrastColorIsFor = COLOR_INVALID;
Adrian Roos487374f2017-01-11 15:48:14 -08002544 /**
2545 * Caches a ambient version of {@link #mCachedContrastColorIsFor}.
2546 */
2547 private int mCachedAmbientColor = COLOR_INVALID;
2548 private int mCachedAmbientColorIsFor = COLOR_INVALID;
Adrian Roos4ff3b122016-02-01 12:26:13 -08002549
2550 /**
Adrian Roos70d7aa32017-01-11 15:39:06 -08002551 * Caches an instance of StandardTemplateParams. Note that this may have been used before,
2552 * so make sure to call {@link StandardTemplateParams#reset()} before using it.
2553 */
2554 StandardTemplateParams mParams = new StandardTemplateParams();
Selim Cinek7b9605b2017-01-19 17:36:00 -08002555 private int mTextColorsAreForBackground = COLOR_INVALID;
2556 private int mPrimaryTextColor = COLOR_INVALID;
2557 private int mSecondaryTextColor = COLOR_INVALID;
2558 private int mActionBarColor = COLOR_INVALID;
Adrian Roos70d7aa32017-01-11 15:39:06 -08002559
2560 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002561 * Constructs a new Builder with the defaults:
Joe Onoratocb109a02011-01-18 17:57:41 -08002562 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002563
2564 * <table>
2565 * <tr><th align=right>priority</th>
2566 * <td>{@link #PRIORITY_DEFAULT}</td></tr>
2567 * <tr><th align=right>when</th>
2568 * <td>now ({@link System#currentTimeMillis()})</td></tr>
2569 * <tr><th align=right>audio stream</th>
2570 * <td>{@link #STREAM_DEFAULT}</td></tr>
2571 * </table>
Joe Onoratocb109a02011-01-18 17:57:41 -08002572 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002573
2574 * @param context
2575 * A {@link Context} that will be used by the Builder to construct the
2576 * RemoteViews. The Context will not be held past the lifetime of this Builder
2577 * object.
Geoffrey Pitsch5caa2762017-01-12 09:35:54 -05002578 * @param channelId
2579 * The constructed Notification will be posted on this
2580 * {@link NotificationChannel}. To use a NotificationChannel, it must first be
2581 * created using {@link NotificationManager#createNotificationChannel}.
Joe Onoratocb109a02011-01-18 17:57:41 -08002582 */
Geoffrey Pitsch5caa2762017-01-12 09:35:54 -05002583 public Builder(Context context, String channelId) {
2584 this(context, (Notification) null);
2585 mN.mChannelId = channelId;
2586 }
2587
2588 /**
2589 * @deprecated use {@link Notification.Builder#Notification.Builder(Context, String)}
2590 * instead. All posted Notifications must specify a NotificationChannel Id.
2591 */
2592 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08002593 public Builder(Context context) {
Geoffrey Pitsch5caa2762017-01-12 09:35:54 -05002594 this(context, (Notification) null);
Joe Onorato46439ce2010-11-19 13:56:21 -08002595 }
2596
Joe Onoratocb109a02011-01-18 17:57:41 -08002597 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002598 * @hide
Christoph Studer4600f9b2014-07-22 22:44:43 +02002599 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002600 public Builder(Context context, Notification toAdopt) {
2601 mContext = context;
Christoph Studer4600f9b2014-07-22 22:44:43 +02002602
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002603 if (toAdopt == null) {
2604 mN = new Notification();
Selim Cinek0ff1ce602016-04-05 18:27:16 -07002605 if (context.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N) {
2606 mN.extras.putBoolean(EXTRA_SHOW_WHEN, true);
2607 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002608 mN.priority = PRIORITY_DEFAULT;
2609 mN.visibility = VISIBILITY_PRIVATE;
2610 } else {
2611 mN = toAdopt;
2612 if (mN.actions != null) {
2613 Collections.addAll(mActions, mN.actions);
Christoph Studer4600f9b2014-07-22 22:44:43 +02002614 }
2615
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002616 if (mN.extras.containsKey(EXTRA_PEOPLE)) {
2617 Collections.addAll(mPersonList, mN.extras.getStringArray(EXTRA_PEOPLE));
2618 }
2619
Selim Cinek4ac6f602016-06-13 15:47:03 -07002620 if (mN.getSmallIcon() == null && mN.icon != 0) {
2621 setSmallIcon(mN.icon);
2622 }
2623
2624 if (mN.getLargeIcon() == null && mN.largeIcon != null) {
2625 setLargeIcon(mN.largeIcon);
2626 }
2627
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002628 String templateClass = mN.extras.getString(EXTRA_TEMPLATE);
2629 if (!TextUtils.isEmpty(templateClass)) {
2630 final Class<? extends Style> styleClass
2631 = getNotificationStyleClass(templateClass);
2632 if (styleClass == null) {
2633 Log.d(TAG, "Unknown style class: " + templateClass);
2634 } else {
2635 try {
Adrian Roosc1a80b02016-04-05 14:54:55 -07002636 final Constructor<? extends Style> ctor =
2637 styleClass.getDeclaredConstructor();
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002638 ctor.setAccessible(true);
2639 final Style style = ctor.newInstance();
2640 style.restoreFromExtras(mN.extras);
2641
2642 if (style != null) {
2643 setStyle(style);
2644 }
2645 } catch (Throwable t) {
2646 Log.e(TAG, "Could not create Style", t);
2647 }
2648 }
2649 }
2650
2651 }
2652 }
2653
2654 private NotificationColorUtil getColorUtil() {
2655 if (!mColorUtilInited) {
2656 mColorUtilInited = true;
Selim Cinek7b9605b2017-01-19 17:36:00 -08002657 if (isLegacy() || isColorized()) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002658 mColorUtil = NotificationColorUtil.getInstance(mContext);
Christoph Studer4600f9b2014-07-22 22:44:43 +02002659 }
2660 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002661 return mColorUtil;
Christoph Studer4600f9b2014-07-22 22:44:43 +02002662 }
2663
2664 /**
Julia Reynolds13d898c2017-02-02 12:22:05 -05002665 * If this notification is duplicative of a Launcher shortcut, sets the
2666 * {@link ShortcutInfo#getId() id} of the shortcut, in case the Launcher wants to hide
2667 * the shortcut.
2668 *
2669 * This field will be ignored by Launchers that don't support badging or
2670 * {@link android.content.pm.ShortcutManager shortcuts}.
2671 *
2672 * @param shortcutId the {@link ShortcutInfo#getId() id} of the shortcut this notification
2673 * supersedes
2674 */
2675 public Builder setShortcutId(String shortcutId) {
2676 mN.mShortcutId = shortcutId;
2677 return this;
2678 }
2679
2680 /**
2681 * Sets which icon to display as a badge for this notification.
2682 *
2683 * Must be one of {@link #BADGE_ICON_NONE}, {@link #BADGE_ICON_SMALL},
2684 * {@link #BADGE_ICON_LARGE}.
2685 *
2686 * Note: This value might be ignored, for launchers that don't support badge icons.
2687 */
2688 public Builder chooseBadgeIcon(int icon) {
2689 mN.mBadgeIcon = icon;
2690 return this;
2691 }
2692
2693 /**
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04002694 * Specifies the channel the notification should be delivered on.
2695 */
2696 public Builder setChannel(String channelId) {
2697 mN.mChannelId = channelId;
2698 return this;
2699 }
2700
2701 /**
Julia Reynolds2a128742016-11-28 14:29:25 -05002702 * Specifies the time at which this notification should be canceled, if it is not already
2703 * canceled.
2704 */
2705 public Builder setTimeout(long when) {
2706 mN.mTimeout = when;
2707 return this;
2708 }
2709
2710 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002711 * Add a timestamp pertaining to the notification (usually the time the event occurred).
Selim Cinek0ff1ce602016-04-05 18:27:16 -07002712 *
2713 * For apps targeting {@link android.os.Build.VERSION_CODES#N} and above, this time is not
2714 * shown anymore by default and must be opted into by using
2715 * {@link android.app.Notification.Builder#setShowWhen(boolean)}
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002716 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002717 * @see Notification#when
Joe Onoratocb109a02011-01-18 17:57:41 -08002718 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002719 public Builder setWhen(long when) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002720 mN.when = when;
Joe Onorato46439ce2010-11-19 13:56:21 -08002721 return this;
2722 }
2723
Joe Onoratocb109a02011-01-18 17:57:41 -08002724 /**
Griff Hazen50c11652014-05-16 09:46:31 -07002725 * Control whether the timestamp set with {@link #setWhen(long) setWhen} is shown
Daniel Sandler0c890492012-09-12 17:23:10 -07002726 * in the content view.
Selim Cinek0ff1ce602016-04-05 18:27:16 -07002727 * For apps targeting {@link android.os.Build.VERSION_CODES#N} and above, this defaults to
2728 * {@code false}. For earlier apps, the default is {@code true}.
Daniel Sandler0c890492012-09-12 17:23:10 -07002729 */
2730 public Builder setShowWhen(boolean show) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002731 mN.extras.putBoolean(EXTRA_SHOW_WHEN, show);
Daniel Sandler0c890492012-09-12 17:23:10 -07002732 return this;
2733 }
2734
2735 /**
Daniel Sandlerd33b8032012-05-10 11:41:48 -04002736 * Show the {@link Notification#when} field as a stopwatch.
Joe Malin8d40d042012-11-05 11:36:40 -08002737 *
2738 * Instead of presenting <code>when</code> as a timestamp, the notification will show an
Daniel Sandlerd33b8032012-05-10 11:41:48 -04002739 * automatically updating display of the minutes and seconds since <code>when</code>.
Daniel Sandlera2985ed2012-04-03 16:42:00 -04002740 *
Daniel Sandlerd33b8032012-05-10 11:41:48 -04002741 * Useful when showing an elapsed time (like an ongoing phone call).
2742 *
Selim Cinek81c23aa2016-02-25 16:23:13 -08002743 * The counter can also be set to count down to <code>when</code> when using
Adrian Roos96b7e202016-05-17 13:50:38 -07002744 * {@link #setChronometerCountDown(boolean)}.
Selim Cinek81c23aa2016-02-25 16:23:13 -08002745 *
Daniel Sandlerd33b8032012-05-10 11:41:48 -04002746 * @see android.widget.Chronometer
Daniel Sandlera2985ed2012-04-03 16:42:00 -04002747 * @see Notification#when
Adrian Roos96b7e202016-05-17 13:50:38 -07002748 * @see #setChronometerCountDown(boolean)
Daniel Sandlera2985ed2012-04-03 16:42:00 -04002749 */
2750 public Builder setUsesChronometer(boolean b) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002751 mN.extras.putBoolean(EXTRA_SHOW_CHRONOMETER, b);
Daniel Sandlera2985ed2012-04-03 16:42:00 -04002752 return this;
2753 }
2754
2755 /**
Selim Cinek81c23aa2016-02-25 16:23:13 -08002756 * Sets the Chronometer to count down instead of counting up.
2757 *
2758 * <p>This is only relevant if {@link #setUsesChronometer(boolean)} has been set to true.
2759 * If it isn't set the chronometer will count up.
2760 *
2761 * @see #setUsesChronometer(boolean)
2762 */
Adrian Roos96b7e202016-05-17 13:50:38 -07002763 public Builder setChronometerCountDown(boolean countDown) {
2764 mN.extras.putBoolean(EXTRA_CHRONOMETER_COUNT_DOWN, countDown);
Selim Cinek81c23aa2016-02-25 16:23:13 -08002765 return this;
2766 }
2767
2768 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002769 * Set the small icon resource, which will be used to represent the notification in the
2770 * status bar.
Joe Onoratocb109a02011-01-18 17:57:41 -08002771 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002772
2773 * The platform template for the expanded view will draw this icon in the left, unless a
2774 * {@link #setLargeIcon(Bitmap) large icon} has also been specified, in which case the small
2775 * icon will be moved to the right-hand side.
2776 *
2777
2778 * @param icon
2779 * A resource ID in the application's package of the drawable to use.
2780 * @see Notification#icon
Joe Onoratocb109a02011-01-18 17:57:41 -08002781 */
Tor Norbye7b9c9122013-05-30 16:48:33 -07002782 public Builder setSmallIcon(@DrawableRes int icon) {
Dan Sandlerd63f9322015-05-06 15:18:49 -04002783 return setSmallIcon(icon != 0
2784 ? Icon.createWithResource(mContext, icon)
2785 : null);
Joe Onorato46439ce2010-11-19 13:56:21 -08002786 }
2787
Joe Onoratocb109a02011-01-18 17:57:41 -08002788 /**
2789 * A variant of {@link #setSmallIcon(int) setSmallIcon(int)} that takes an additional
2790 * level parameter for when the icon is a {@link android.graphics.drawable.LevelListDrawable
2791 * LevelListDrawable}.
2792 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002793 * @param icon A resource ID in the application's package of the drawable to use.
Joe Onoratocb109a02011-01-18 17:57:41 -08002794 * @param level The level to use for the icon.
2795 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002796 * @see Notification#icon
2797 * @see Notification#iconLevel
Joe Onoratocb109a02011-01-18 17:57:41 -08002798 */
Tor Norbye7b9c9122013-05-30 16:48:33 -07002799 public Builder setSmallIcon(@DrawableRes int icon, int level) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002800 mN.iconLevel = level;
Dan Sandlerd63f9322015-05-06 15:18:49 -04002801 return setSmallIcon(icon);
2802 }
2803
2804 /**
2805 * Set the small icon, which will be used to represent the notification in the
2806 * status bar and content view (unless overriden there by a
2807 * {@link #setLargeIcon(Bitmap) large icon}).
2808 *
2809 * @param icon An Icon object to use.
2810 * @see Notification#icon
2811 */
2812 public Builder setSmallIcon(Icon icon) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002813 mN.setSmallIcon(icon);
2814 if (icon != null && icon.getType() == Icon.TYPE_RESOURCE) {
2815 mN.icon = icon.getResId();
2816 }
Joe Onorato46439ce2010-11-19 13:56:21 -08002817 return this;
2818 }
2819
Joe Onoratocb109a02011-01-18 17:57:41 -08002820 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002821 * Set the first line of text in the platform notification template.
Joe Onoratocb109a02011-01-18 17:57:41 -08002822 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002823 public Builder setContentTitle(CharSequence title) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002824 mN.extras.putCharSequence(EXTRA_TITLE, safeCharSequence(title));
Joe Onorato46439ce2010-11-19 13:56:21 -08002825 return this;
2826 }
2827
Joe Onoratocb109a02011-01-18 17:57:41 -08002828 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002829 * Set the second line of text in the platform notification template.
Joe Onoratocb109a02011-01-18 17:57:41 -08002830 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002831 public Builder setContentText(CharSequence text) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002832 mN.extras.putCharSequence(EXTRA_TEXT, safeCharSequence(text));
Joe Onorato46439ce2010-11-19 13:56:21 -08002833 return this;
2834 }
2835
Joe Onoratocb109a02011-01-18 17:57:41 -08002836 /**
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07002837 * This provides some additional information that is displayed in the notification. No
2838 * guarantees are given where exactly it is displayed.
2839 *
2840 * <p>This information should only be provided if it provides an essential
2841 * benefit to the understanding of the notification. The more text you provide the
2842 * less readable it becomes. For example, an email client should only provide the account
2843 * name here if more than one email account has been added.</p>
2844 *
2845 * <p>As of {@link android.os.Build.VERSION_CODES#N} this information is displayed in the
2846 * notification header area.
2847 *
2848 * On Android versions before {@link android.os.Build.VERSION_CODES#N}
2849 * this will be shown in the third line of text in the platform notification template.
2850 * You should not be using {@link #setProgress(int, int, boolean)} at the
2851 * same time on those versions; they occupy the same place.
2852 * </p>
Daniel Sandlerf3b73432012-03-27 15:01:25 -04002853 */
2854 public Builder setSubText(CharSequence text) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002855 mN.extras.putCharSequence(EXTRA_SUB_TEXT, safeCharSequence(text));
Daniel Sandlerf3b73432012-03-27 15:01:25 -04002856 return this;
2857 }
2858
2859 /**
Adrian Roose458aa82015-12-08 16:17:19 -08002860 * Set the remote input history.
2861 *
2862 * This should be set to the most recent inputs that have been sent
2863 * through a {@link RemoteInput} of this Notification and cleared once the it is no
2864 * longer relevant (e.g. for chat notifications once the other party has responded).
2865 *
2866 * The most recent input must be stored at the 0 index, the second most recent at the
2867 * 1 index, etc. Note that the system will limit both how far back the inputs will be shown
2868 * and how much of each individual input is shown.
2869 *
2870 * <p>Note: The reply text will only be shown on notifications that have least one action
2871 * with a {@code RemoteInput}.</p>
2872 */
2873 public Builder setRemoteInputHistory(CharSequence[] text) {
2874 if (text == null) {
2875 mN.extras.putCharSequenceArray(EXTRA_REMOTE_INPUT_HISTORY, null);
2876 } else {
2877 final int N = Math.min(MAX_REPLY_HISTORY, text.length);
2878 CharSequence[] safe = new CharSequence[N];
2879 for (int i = 0; i < N; i++) {
2880 safe[i] = safeCharSequence(text[i]);
2881 }
2882 mN.extras.putCharSequenceArray(EXTRA_REMOTE_INPUT_HISTORY, safe);
2883 }
2884 return this;
2885 }
2886
2887 /**
Julia Reynolds13d898c2017-02-02 12:22:05 -05002888 * Sets the number of items this notification represents. May be displayed as a badge count
2889 * for Launchers that support badging.
Joe Onoratocb109a02011-01-18 17:57:41 -08002890 */
Joe Onorato8595a3d2010-11-19 18:12:07 -08002891 public Builder setNumber(int number) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002892 mN.number = number;
Joe Onorato8595a3d2010-11-19 18:12:07 -08002893 return this;
2894 }
2895
Joe Onoratocb109a02011-01-18 17:57:41 -08002896 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002897 * A small piece of additional information pertaining to this notification.
2898 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002899 * The platform template will draw this on the last line of the notification, at the far
2900 * right (to the right of a smallIcon if it has been placed there).
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07002901 *
2902 * @deprecated use {@link #setSubText(CharSequence)} instead to set a text in the header.
2903 * For legacy apps targeting a version below {@link android.os.Build.VERSION_CODES#N} this
2904 * field will still show up, but the subtext will take precedence.
Joe Onoratocb109a02011-01-18 17:57:41 -08002905 */
Aurimas Liutikas514c5ef2016-05-24 15:22:55 -07002906 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08002907 public Builder setContentInfo(CharSequence info) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002908 mN.extras.putCharSequence(EXTRA_INFO_TEXT, safeCharSequence(info));
Joe Onorato46439ce2010-11-19 13:56:21 -08002909 return this;
2910 }
2911
Joe Onoratocb109a02011-01-18 17:57:41 -08002912 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002913 * Set the progress this notification represents.
2914 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002915 * The platform template will represent this using a {@link ProgressBar}.
Jeff Sharkey1c400132011-08-05 14:50:13 -07002916 */
2917 public Builder setProgress(int max, int progress, boolean indeterminate) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002918 mN.extras.putInt(EXTRA_PROGRESS, progress);
2919 mN.extras.putInt(EXTRA_PROGRESS_MAX, max);
2920 mN.extras.putBoolean(EXTRA_PROGRESS_INDETERMINATE, indeterminate);
Jeff Sharkey1c400132011-08-05 14:50:13 -07002921 return this;
2922 }
2923
2924 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002925 * Supply a custom RemoteViews to use instead of the platform template.
2926 *
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002927 * Use {@link #setCustomContentView(RemoteViews)} instead.
Joe Onoratocb109a02011-01-18 17:57:41 -08002928 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002929 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08002930 public Builder setContent(RemoteViews views) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002931 return setCustomContentView(views);
2932 }
2933
2934 /**
2935 * Supply custom RemoteViews to use instead of the platform template.
2936 *
2937 * This will override the layout that would otherwise be constructed by this Builder
2938 * object.
2939 */
2940 public Builder setCustomContentView(RemoteViews contentView) {
2941 mN.contentView = contentView;
2942 return this;
2943 }
2944
2945 /**
2946 * Supply custom RemoteViews to use instead of the platform template in the expanded form.
2947 *
2948 * This will override the expanded layout that would otherwise be constructed by this
2949 * Builder object.
2950 */
2951 public Builder setCustomBigContentView(RemoteViews contentView) {
2952 mN.bigContentView = contentView;
2953 return this;
2954 }
2955
2956 /**
2957 * Supply custom RemoteViews to use instead of the platform template in the heads up dialog.
2958 *
2959 * This will override the heads-up layout that would otherwise be constructed by this
2960 * Builder object.
2961 */
2962 public Builder setCustomHeadsUpContentView(RemoteViews contentView) {
2963 mN.headsUpContentView = contentView;
Joe Onorato46439ce2010-11-19 13:56:21 -08002964 return this;
2965 }
2966
Joe Onoratocb109a02011-01-18 17:57:41 -08002967 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002968 * Supply a {@link PendingIntent} to be sent when the notification is clicked.
2969 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002970 * As of {@link android.os.Build.VERSION_CODES#HONEYCOMB}, if this field is unset and you
2971 * have specified a custom RemoteViews with {@link #setContent(RemoteViews)}, you can use
2972 * {@link RemoteViews#setOnClickPendingIntent RemoteViews.setOnClickPendingIntent(int,PendingIntent)}
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002973 * to assign PendingIntents to individual views in that custom layout (i.e., to create
Daniel Sandlerf3b73432012-03-27 15:01:25 -04002974 * clickable buttons inside the notification view).
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002975 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002976 * @see Notification#contentIntent Notification.contentIntent
Joe Onoratocb109a02011-01-18 17:57:41 -08002977 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002978 public Builder setContentIntent(PendingIntent intent) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002979 mN.contentIntent = intent;
Joe Onorato46439ce2010-11-19 13:56:21 -08002980 return this;
2981 }
2982
Joe Onoratocb109a02011-01-18 17:57:41 -08002983 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002984 * Supply a {@link PendingIntent} to send when the notification is cleared explicitly by the user.
2985 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002986 * @see Notification#deleteIntent
Joe Onoratocb109a02011-01-18 17:57:41 -08002987 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002988 public Builder setDeleteIntent(PendingIntent intent) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002989 mN.deleteIntent = intent;
Joe Onorato46439ce2010-11-19 13:56:21 -08002990 return this;
2991 }
2992
Joe Onoratocb109a02011-01-18 17:57:41 -08002993 /**
2994 * An intent to launch instead of posting the notification to the status bar.
2995 * Only for use with extremely high-priority notifications demanding the user's
2996 * <strong>immediate</strong> attention, such as an incoming phone call or
2997 * alarm clock that the user has explicitly set to a particular time.
2998 * If this facility is used for something else, please give the user an option
2999 * to turn it off and use a normal notification, as this can be extremely
3000 * disruptive.
3001 *
Chris Wren47c20a12014-06-18 17:27:29 -04003002 * <p>
3003 * The system UI may choose to display a heads-up notification, instead of
3004 * launching this intent, while the user is using the device.
3005 * </p>
3006 *
Joe Onoratocb109a02011-01-18 17:57:41 -08003007 * @param intent The pending intent to launch.
3008 * @param highPriority Passing true will cause this notification to be sent
3009 * even if other notifications are suppressed.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003010 *
3011 * @see Notification#fullScreenIntent
Joe Onoratocb109a02011-01-18 17:57:41 -08003012 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003013 public Builder setFullScreenIntent(PendingIntent intent, boolean highPriority) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003014 mN.fullScreenIntent = intent;
Joe Onorato46439ce2010-11-19 13:56:21 -08003015 setFlag(FLAG_HIGH_PRIORITY, highPriority);
3016 return this;
3017 }
3018
Joe Onoratocb109a02011-01-18 17:57:41 -08003019 /**
Dan Sandler5fcdf6e2014-07-18 11:31:15 -04003020 * Set the "ticker" text which is sent to accessibility services.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003021 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003022 * @see Notification#tickerText
Joe Onoratocb109a02011-01-18 17:57:41 -08003023 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003024 public Builder setTicker(CharSequence tickerText) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003025 mN.tickerText = safeCharSequence(tickerText);
Joe Onorato46439ce2010-11-19 13:56:21 -08003026 return this;
3027 }
3028
Joe Onoratocb109a02011-01-18 17:57:41 -08003029 /**
Dan Sandler5fcdf6e2014-07-18 11:31:15 -04003030 * Obsolete version of {@link #setTicker(CharSequence)}.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003031 *
Joe Onoratocb109a02011-01-18 17:57:41 -08003032 */
Dan Sandler5fcdf6e2014-07-18 11:31:15 -04003033 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08003034 public Builder setTicker(CharSequence tickerText, RemoteViews views) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003035 setTicker(tickerText);
3036 // views is ignored
Joe Onorato46439ce2010-11-19 13:56:21 -08003037 return this;
3038 }
3039
Joe Onoratocb109a02011-01-18 17:57:41 -08003040 /**
Dan Sandlerd63f9322015-05-06 15:18:49 -04003041 * Add a large icon to the notification content view.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003042 *
3043 * In the platform template, this image will be shown on the left of the notification view
Dan Sandlerd63f9322015-05-06 15:18:49 -04003044 * in place of the {@link #setSmallIcon(Icon) small icon} (which will be placed in a small
3045 * badge atop the large icon).
Dan Sandler08a04c12015-05-06 15:18:49 -04003046 */
Dan Sandlerd63f9322015-05-06 15:18:49 -04003047 public Builder setLargeIcon(Bitmap b) {
3048 return setLargeIcon(b != null ? Icon.createWithBitmap(b) : null);
3049 }
3050
3051 /**
3052 * Add a large icon to the notification content view.
3053 *
3054 * In the platform template, this image will be shown on the left of the notification view
3055 * in place of the {@link #setSmallIcon(Icon) small icon} (which will be placed in a small
3056 * badge atop the large icon).
3057 */
3058 public Builder setLargeIcon(Icon icon) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003059 mN.mLargeIcon = icon;
3060 mN.extras.putParcelable(EXTRA_LARGE_ICON, icon);
Joe Onorato46439ce2010-11-19 13:56:21 -08003061 return this;
3062 }
3063
Joe Onoratocb109a02011-01-18 17:57:41 -08003064 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003065 * Set the sound to play.
3066 *
John Spurlockc0650f022014-07-19 13:22:39 -04003067 * It will be played using the {@link #AUDIO_ATTRIBUTES_DEFAULT default audio attributes}
3068 * for notifications.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003069 *
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003070 * @deprecated use {@link NotificationChannel#setSound(Uri, AudioAttributes)} instead.
Joe Onoratocb109a02011-01-18 17:57:41 -08003071 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003072 @Deprecated
Joe Onorato52f80cd2010-11-21 15:34:48 -08003073 public Builder setSound(Uri sound) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003074 mN.sound = sound;
3075 mN.audioAttributes = AUDIO_ATTRIBUTES_DEFAULT;
Joe Onorato52f80cd2010-11-21 15:34:48 -08003076 return this;
3077 }
3078
Joe Onoratocb109a02011-01-18 17:57:41 -08003079 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003080 * Set the sound to play, along with a specific stream on which to play it.
Joe Onoratocb109a02011-01-18 17:57:41 -08003081 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003082 * See {@link android.media.AudioManager} for the <code>STREAM_</code> constants.
3083 *
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003084 * @deprecated use {@link NotificationChannel#setSound(Uri, AudioAttributes)}.
Joe Onoratocb109a02011-01-18 17:57:41 -08003085 */
Jean-Michel Trivi81f871e2014-08-06 16:32:38 -07003086 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08003087 public Builder setSound(Uri sound, int streamType) {
Jean-Michel Trivi2f7511f2016-11-28 15:40:27 -08003088 PlayerBase.deprecateStreamTypeForPlayback(streamType, "Notification", "setSound()");
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003089 mN.sound = sound;
3090 mN.audioStreamType = streamType;
Joe Onorato46439ce2010-11-19 13:56:21 -08003091 return this;
3092 }
3093
Joe Onoratocb109a02011-01-18 17:57:41 -08003094 /**
John Spurlockc0650f022014-07-19 13:22:39 -04003095 * Set the sound to play, along with specific {@link AudioAttributes audio attributes} to
3096 * use during playback.
3097 *
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003098 * @deprecated use {@link NotificationChannel#setSound(Uri, AudioAttributes)} instead.
John Spurlockc0650f022014-07-19 13:22:39 -04003099 * @see Notification#sound
3100 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003101 @Deprecated
John Spurlockc0650f022014-07-19 13:22:39 -04003102 public Builder setSound(Uri sound, AudioAttributes audioAttributes) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003103 mN.sound = sound;
3104 mN.audioAttributes = audioAttributes;
John Spurlockc0650f022014-07-19 13:22:39 -04003105 return this;
3106 }
3107
3108 /**
Joe Onoratocb109a02011-01-18 17:57:41 -08003109 * Set the vibration pattern to use.
3110 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003111 * See {@link android.os.Vibrator#vibrate(long[], int)} for a discussion of the
3112 * <code>pattern</code> parameter.
3113 *
Chris Wren47c20a12014-06-18 17:27:29 -04003114 * <p>
3115 * A notification that vibrates is more likely to be presented as a heads-up notification.
3116 * </p>
3117 *
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003118 * @deprecated use {@link NotificationChannel#setVibrationPattern(long[])} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003119 * @see Notification#vibrate
Joe Onoratocb109a02011-01-18 17:57:41 -08003120 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003121 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08003122 public Builder setVibrate(long[] pattern) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003123 mN.vibrate = pattern;
Joe Onorato46439ce2010-11-19 13:56:21 -08003124 return this;
3125 }
3126
Joe Onoratocb109a02011-01-18 17:57:41 -08003127 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003128 * Set the desired color for the indicator LED on the device, as well as the
3129 * blink duty cycle (specified in milliseconds).
3130 *
3131
3132 * Not all devices will honor all (or even any) of these values.
3133 *
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003134 * @deprecated use {@link NotificationChannel#setLights(boolean)} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003135 * @see Notification#ledARGB
3136 * @see Notification#ledOnMS
3137 * @see Notification#ledOffMS
Joe Onoratocb109a02011-01-18 17:57:41 -08003138 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003139 @Deprecated
Tor Norbye80756e32015-03-02 09:39:27 -08003140 public Builder setLights(@ColorInt int argb, int onMs, int offMs) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003141 mN.ledARGB = argb;
3142 mN.ledOnMS = onMs;
3143 mN.ledOffMS = offMs;
Julia Reynolds10ee1fc2015-11-09 11:04:55 -05003144 if (onMs != 0 || offMs != 0) {
3145 mN.flags |= FLAG_SHOW_LIGHTS;
3146 }
Joe Onorato46439ce2010-11-19 13:56:21 -08003147 return this;
3148 }
3149
Joe Onoratocb109a02011-01-18 17:57:41 -08003150 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003151 * Set whether this is an "ongoing" notification.
Joe Onoratocb109a02011-01-18 17:57:41 -08003152 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003153
3154 * Ongoing notifications cannot be dismissed by the user, so your application or service
3155 * must take care of canceling them.
3156 *
3157
3158 * They are typically used to indicate a background task that the user is actively engaged
3159 * with (e.g., playing music) or is pending in some way and therefore occupying the device
3160 * (e.g., a file download, sync operation, active network connection).
3161 *
3162
3163 * @see Notification#FLAG_ONGOING_EVENT
3164 * @see Service#setForeground(boolean)
Joe Onoratocb109a02011-01-18 17:57:41 -08003165 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003166 public Builder setOngoing(boolean ongoing) {
3167 setFlag(FLAG_ONGOING_EVENT, ongoing);
3168 return this;
3169 }
3170
Joe Onoratocb109a02011-01-18 17:57:41 -08003171 /**
Selim Cinek7b9605b2017-01-19 17:36:00 -08003172 * Set whether this notification should be colorized. When set, the color set with
3173 * {@link #setColor(int)} will be used as the background color of this notification.
3174 * <p>
3175 * The coloring will only be applied if the notification is ongoing.
3176 * This should only be used for high priority ongoing tasks like navigation, an ongoing
3177 * call, or other similarly high-priority events for the user.
3178 *
3179 * @see Builder#setOngoing(boolean)
3180 * @see Builder#setColor(int)
3181 */
3182 public Builder setColorized(boolean colorize) {
3183 mN.extras.putBoolean(EXTRA_COLORIZED, colorize);
3184 return this;
3185 }
3186
3187 /**
Joe Onoratocb109a02011-01-18 17:57:41 -08003188 * Set this flag if you would only like the sound, vibrate
3189 * and ticker to be played if the notification is not already showing.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003190 *
3191 * @see Notification#FLAG_ONLY_ALERT_ONCE
Joe Onoratocb109a02011-01-18 17:57:41 -08003192 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003193 public Builder setOnlyAlertOnce(boolean onlyAlertOnce) {
3194 setFlag(FLAG_ONLY_ALERT_ONCE, onlyAlertOnce);
3195 return this;
3196 }
3197
Joe Onoratocb109a02011-01-18 17:57:41 -08003198 /**
Julia Reynolds04499532016-09-13 14:04:53 -04003199 * Make this notification automatically dismissed when the user touches it.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003200 *
3201 * @see Notification#FLAG_AUTO_CANCEL
Joe Onoratocb109a02011-01-18 17:57:41 -08003202 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003203 public Builder setAutoCancel(boolean autoCancel) {
Joe Onorato281d83f2011-01-04 17:13:10 -08003204 setFlag(FLAG_AUTO_CANCEL, autoCancel);
Joe Onorato46439ce2010-11-19 13:56:21 -08003205 return this;
3206 }
3207
Joe Onoratocb109a02011-01-18 17:57:41 -08003208 /**
Griff Hazendfcb0802014-02-11 12:00:00 -08003209 * Set whether or not this notification should not bridge to other devices.
3210 *
3211 * <p>Some notifications can be bridged to other devices for remote display.
3212 * This hint can be set to recommend this notification not be bridged.
3213 */
3214 public Builder setLocalOnly(boolean localOnly) {
3215 setFlag(FLAG_LOCAL_ONLY, localOnly);
3216 return this;
3217 }
3218
3219 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003220 * Set which notification properties will be inherited from system defaults.
Joe Onoratocb109a02011-01-18 17:57:41 -08003221 * <p>
3222 * The value should be one or more of the following fields combined with
3223 * bitwise-or:
3224 * {@link #DEFAULT_SOUND}, {@link #DEFAULT_VIBRATE}, {@link #DEFAULT_LIGHTS}.
3225 * <p>
3226 * For all default values, use {@link #DEFAULT_ALL}.
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003227 *
3228 * @deprecated use {@link NotificationChannel#enableVibration(boolean)} and
3229 * {@link NotificationChannel#setLights(boolean)} and
3230 * {@link NotificationChannel#setSound(Uri, AudioAttributes)} instead.
Joe Onoratocb109a02011-01-18 17:57:41 -08003231 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003232 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08003233 public Builder setDefaults(int defaults) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003234 mN.defaults = defaults;
Joe Onorato46439ce2010-11-19 13:56:21 -08003235 return this;
3236 }
3237
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003238 /**
3239 * Set the priority of this notification.
3240 *
3241 * @see Notification#priority
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003242 * @deprecated use {@link NotificationChannel#setImportance(int)} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003243 */
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003244 @Deprecated
Tor Norbyed9273d62013-05-30 15:59:53 -07003245 public Builder setPriority(@Priority int pri) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003246 mN.priority = pri;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003247 return this;
3248 }
Joe Malin8d40d042012-11-05 11:36:40 -08003249
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003250 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -04003251 * Set the notification category.
Joe Malin8d40d042012-11-05 11:36:40 -08003252 *
John Spurlockfd7f1e02014-03-18 16:41:57 -04003253 * @see Notification#category
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003254 */
John Spurlockfd7f1e02014-03-18 16:41:57 -04003255 public Builder setCategory(String category) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003256 mN.category = category;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003257 return this;
3258 }
3259
3260 /**
Chris Wrendde75302014-03-26 17:24:15 -04003261 * Add a person that is relevant to this notification.
3262 *
Chris Wrene6c48932014-09-29 17:19:27 -04003263 * <P>
3264 * Depending on user preferences, this annotation may allow the notification to pass
3265 * through interruption filters, and to appear more prominently in the user interface.
3266 * </P>
3267 *
3268 * <P>
3269 * The person should be specified by the {@code String} representation of a
3270 * {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI}.
3271 * </P>
3272 *
3273 * <P>The system will also attempt to resolve {@code mailto:} and {@code tel:} schema
3274 * URIs. The path part of these URIs must exist in the contacts database, in the
3275 * appropriate column, or the reference will be discarded as invalid. Telephone schema
3276 * URIs will be resolved by {@link android.provider.ContactsContract.PhoneLookup}.
3277 * </P>
3278 *
3279 * @param uri A URI for the person.
Chris Wrendde75302014-03-26 17:24:15 -04003280 * @see Notification#EXTRA_PEOPLE
3281 */
Chris Wrene6c48932014-09-29 17:19:27 -04003282 public Builder addPerson(String uri) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003283 mPersonList.add(uri);
Chris Wrendde75302014-03-26 17:24:15 -04003284 return this;
3285 }
3286
3287 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003288 * Set this notification to be part of a group of notifications sharing the same key.
3289 * Grouped notifications may display in a cluster or stack on devices which
3290 * support such rendering.
3291 *
3292 * <p>To make this notification the summary for its group, also call
3293 * {@link #setGroupSummary}. A sort order can be specified for group members by using
3294 * {@link #setSortKey}.
3295 * @param groupKey The group key of the group.
3296 * @return this object for method chaining
3297 */
3298 public Builder setGroup(String groupKey) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003299 mN.mGroupKey = groupKey;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003300 return this;
3301 }
3302
3303 /**
3304 * Set this notification to be the group summary for a group of notifications.
3305 * Grouped notifications may display in a cluster or stack on devices which
Julia Reynolds04499532016-09-13 14:04:53 -04003306 * support such rendering. If thereRequires a group key also be set using {@link #setGroup}.
3307 * The group summary may be suppressed if too few notifications are included in the group.
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003308 * @param isGroupSummary Whether this notification should be a group summary.
3309 * @return this object for method chaining
3310 */
3311 public Builder setGroupSummary(boolean isGroupSummary) {
3312 setFlag(FLAG_GROUP_SUMMARY, isGroupSummary);
3313 return this;
3314 }
3315
3316 /**
3317 * Set a sort key that orders this notification among other notifications from the
3318 * same package. This can be useful if an external sort was already applied and an app
3319 * would like to preserve this. Notifications will be sorted lexicographically using this
3320 * value, although providing different priorities in addition to providing sort key may
3321 * cause this value to be ignored.
3322 *
3323 * <p>This sort key can also be used to order members of a notification group. See
Griff Hazen9e1379f2014-05-20 12:50:51 -07003324 * {@link #setGroup}.
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003325 *
3326 * @see String#compareTo(String)
3327 */
3328 public Builder setSortKey(String sortKey) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003329 mN.mSortKey = sortKey;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003330 return this;
3331 }
3332
3333 /**
Griff Hazen720042b2014-02-24 15:46:56 -08003334 * Merge additional metadata into this notification.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003335 *
Griff Hazen720042b2014-02-24 15:46:56 -08003336 * <p>Values within the Bundle will replace existing extras values in this Builder.
3337 *
3338 * @see Notification#extras
3339 */
Griff Hazen959591e2014-05-15 22:26:18 -07003340 public Builder addExtras(Bundle extras) {
3341 if (extras != null) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003342 mUserExtras.putAll(extras);
Griff Hazen720042b2014-02-24 15:46:56 -08003343 }
3344 return this;
3345 }
3346
3347 /**
3348 * Set metadata for this notification.
3349 *
3350 * <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 -04003351 * current contents are copied into the Notification each time {@link #build()} is
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003352 * called.
3353 *
Griff Hazen720042b2014-02-24 15:46:56 -08003354 * <p>Replaces any existing extras values with those from the provided Bundle.
3355 * Use {@link #addExtras} to merge in metadata instead.
3356 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003357 * @see Notification#extras
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003358 */
Griff Hazen959591e2014-05-15 22:26:18 -07003359 public Builder setExtras(Bundle extras) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003360 if (extras != null) {
3361 mUserExtras = extras;
3362 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003363 return this;
3364 }
3365
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003366 /**
Griff Hazen720042b2014-02-24 15:46:56 -08003367 * Get the current metadata Bundle used by this notification Builder.
3368 *
3369 * <p>The returned Bundle is shared with this Builder.
3370 *
3371 * <p>The current contents of this Bundle are copied into the Notification each time
3372 * {@link #build()} is called.
3373 *
3374 * @see Notification#extras
3375 */
3376 public Bundle getExtras() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003377 return mUserExtras;
3378 }
3379
3380 private Bundle getAllExtras() {
3381 final Bundle saveExtras = (Bundle) mUserExtras.clone();
3382 saveExtras.putAll(mN.extras);
3383 return saveExtras;
Griff Hazen720042b2014-02-24 15:46:56 -08003384 }
3385
3386 /**
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003387 * Add an action to this notification. Actions are typically displayed by
3388 * the system as a button adjacent to the notification content.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04003389 * <p>
3390 * Every action must have an icon (32dp square and matching the
3391 * <a href="{@docRoot}design/style/iconography.html#action-bar">Holo
3392 * Dark action bar</a> visual style), a textual label, and a {@link PendingIntent}.
3393 * <p>
3394 * A notification in its expanded form can display up to 3 actions, from left to right in
3395 * the order they were added. Actions will not be displayed when the notification is
3396 * collapsed, however, so be sure that any essential functions may be accessed by the user
3397 * in some other way (for example, in the Activity pointed to by {@link #contentIntent}).
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003398 *
3399 * @param icon Resource ID of a drawable that represents the action.
3400 * @param title Text describing the action.
3401 * @param intent PendingIntent to be fired when the action is invoked.
Dan Sandler86647982015-05-13 23:41:13 -04003402 *
3403 * @deprecated Use {@link #addAction(Action)} instead.
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003404 */
Dan Sandler86647982015-05-13 23:41:13 -04003405 @Deprecated
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003406 public Builder addAction(int icon, CharSequence title, PendingIntent intent) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04003407 mActions.add(new Action(icon, safeCharSequence(title), intent));
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003408 return this;
3409 }
3410
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003411 /**
Griff Hazen959591e2014-05-15 22:26:18 -07003412 * Add an action to this notification. Actions are typically displayed by
3413 * the system as a button adjacent to the notification content.
3414 * <p>
3415 * Every action must have an icon (32dp square and matching the
3416 * <a href="{@docRoot}design/style/iconography.html#action-bar">Holo
3417 * Dark action bar</a> visual style), a textual label, and a {@link PendingIntent}.
3418 * <p>
3419 * A notification in its expanded form can display up to 3 actions, from left to right in
3420 * the order they were added. Actions will not be displayed when the notification is
3421 * collapsed, however, so be sure that any essential functions may be accessed by the user
3422 * in some other way (for example, in the Activity pointed to by {@link #contentIntent}).
3423 *
3424 * @param action The action to add.
3425 */
3426 public Builder addAction(Action action) {
3427 mActions.add(action);
3428 return this;
3429 }
3430
3431 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003432 * Alter the complete list of actions attached to this notification.
3433 * @see #addAction(Action).
3434 *
3435 * @param actions
3436 * @return
3437 */
3438 public Builder setActions(Action... actions) {
3439 mActions.clear();
3440 for (int i = 0; i < actions.length; i++) {
3441 mActions.add(actions[i]);
3442 }
3443 return this;
3444 }
3445
3446 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003447 * Add a rich notification style to be applied at build time.
3448 *
3449 * @param style Object responsible for modifying the notification style.
3450 */
3451 public Builder setStyle(Style style) {
3452 if (mStyle != style) {
3453 mStyle = style;
Daniel Sandlerc08dea22012-06-28 08:35:24 -07003454 if (mStyle != null) {
3455 mStyle.setBuilder(this);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003456 mN.extras.putString(EXTRA_TEMPLATE, style.getClass().getName());
3457 } else {
3458 mN.extras.remove(EXTRA_TEMPLATE);
Daniel Sandlerc08dea22012-06-28 08:35:24 -07003459 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003460 }
3461 return this;
3462 }
3463
Dan Sandler0bf2ed82013-12-21 23:33:41 -06003464 /**
3465 * Specify the value of {@link #visibility}.
Griff Hazenb720abe2014-05-20 13:15:30 -07003466 *
Dan Sandler0bf2ed82013-12-21 23:33:41 -06003467 * @param visibility One of {@link #VISIBILITY_PRIVATE} (the default),
3468 * {@link #VISIBILITY_SECRET}, or {@link #VISIBILITY_PUBLIC}.
3469 *
3470 * @return The same Builder.
3471 */
3472 public Builder setVisibility(int visibility) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003473 mN.visibility = visibility;
Dan Sandler0bf2ed82013-12-21 23:33:41 -06003474 return this;
3475 }
3476
3477 /**
3478 * Supply a replacement Notification whose contents should be shown in insecure contexts
3479 * (i.e. atop the secure lockscreen). See {@link #visibility} and {@link #VISIBILITY_PUBLIC}.
3480 * @param n A replacement notification, presumably with some or all info redacted.
3481 * @return The same Builder.
3482 */
3483 public Builder setPublicVersion(Notification n) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003484 if (n != null) {
3485 mN.publicVersion = new Notification();
3486 n.cloneInto(mN.publicVersion, /*heavy=*/ true);
3487 } else {
3488 mN.publicVersion = null;
3489 }
Dan Sandler0bf2ed82013-12-21 23:33:41 -06003490 return this;
3491 }
3492
Griff Hazenb720abe2014-05-20 13:15:30 -07003493 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003494 * Apply an extender to this notification builder. Extenders may be used to add
3495 * metadata or change options on this builder.
3496 */
Griff Hazen61a9e862014-05-22 16:05:19 -07003497 public Builder extend(Extender extender) {
3498 extender.extend(this);
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003499 return this;
3500 }
3501
Dan Sandler4e787062015-06-17 15:09:48 -04003502 /**
3503 * @hide
3504 */
Julia Reynoldse46bb372016-03-17 11:05:58 -04003505 public Builder setFlag(int mask, boolean value) {
Joe Onorato46439ce2010-11-19 13:56:21 -08003506 if (value) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003507 mN.flags |= mask;
Joe Onorato46439ce2010-11-19 13:56:21 -08003508 } else {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003509 mN.flags &= ~mask;
Joe Onorato46439ce2010-11-19 13:56:21 -08003510 }
Julia Reynoldse46bb372016-03-17 11:05:58 -04003511 return this;
Joe Onorato46439ce2010-11-19 13:56:21 -08003512 }
3513
Dan Sandler26e81cf2014-05-06 10:01:27 -04003514 /**
3515 * Sets {@link Notification#color}.
3516 *
3517 * @param argb The accent color to use
3518 *
3519 * @return The same Builder.
3520 */
Tor Norbye80756e32015-03-02 09:39:27 -08003521 public Builder setColor(@ColorInt int argb) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003522 mN.color = argb;
Julia Reynolds10ee1fc2015-11-09 11:04:55 -05003523 sanitizeColor();
Dan Sandler26e81cf2014-05-06 10:01:27 -04003524 return this;
3525 }
3526
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003527 private Drawable getProfileBadgeDrawable() {
Chris Wren66619a22016-05-12 16:42:37 -04003528 if (mContext.getUserId() == UserHandle.USER_SYSTEM) {
3529 // This user can never be a badged profile,
3530 // and also includes USER_ALL system notifications.
3531 return null;
3532 }
Christoph Studer7ac80e62014-08-04 16:01:57 +02003533 // Note: This assumes that the current user can read the profile badge of the
3534 // originating user.
Selim Cineke6ff9462016-01-15 15:07:06 -08003535 return mContext.getPackageManager().getUserBadgeForDensityNoBackground(
Julia Reynoldsda303542015-11-23 14:00:20 -05003536 new UserHandle(mContext.getUserId()), 0);
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003537 }
3538
3539 private Bitmap getProfileBadge() {
3540 Drawable badge = getProfileBadgeDrawable();
Kenny Guy8a0101b2014-05-08 23:34:12 +01003541 if (badge == null) {
3542 return null;
3543 }
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003544 final int size = mContext.getResources().getDimensionPixelSize(
3545 R.dimen.notification_badge_size);
3546 Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
Kenny Guy8a0101b2014-05-08 23:34:12 +01003547 Canvas canvas = new Canvas(bitmap);
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003548 badge.setBounds(0, 0, size, size);
Kenny Guy8a0101b2014-05-08 23:34:12 +01003549 badge.draw(canvas);
3550 return bitmap;
3551 }
3552
Selim Cinekc848c3a2016-01-13 15:27:30 -08003553 private void bindProfileBadge(RemoteViews contentView) {
Kenny Guy98193ea2014-07-24 19:54:37 +01003554 Bitmap profileBadge = getProfileBadge();
3555
Kenny Guy98193ea2014-07-24 19:54:37 +01003556 if (profileBadge != null) {
Selim Cinekc848c3a2016-01-13 15:27:30 -08003557 contentView.setImageViewBitmap(R.id.profile_badge, profileBadge);
3558 contentView.setViewVisibility(R.id.profile_badge, View.VISIBLE);
Selim Cinek7b9605b2017-01-19 17:36:00 -08003559 if (isColorized()) {
3560 contentView.setDrawableParameters(R.id.profile_badge, false, -1,
3561 getPrimaryTextColor(), PorterDuff.Mode.SRC_ATOP, -1);
3562 }
Kenny Guy98193ea2014-07-24 19:54:37 +01003563 }
Kenny Guy98193ea2014-07-24 19:54:37 +01003564 }
3565
Christoph Studerfe718432014-09-01 18:21:18 +02003566 private void resetStandardTemplate(RemoteViews contentView) {
Selim Cinekeaa29ca2015-11-23 13:51:13 -08003567 resetNotificationHeader(contentView);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003568 resetContentMargins(contentView);
Christoph Studerfe718432014-09-01 18:21:18 +02003569 contentView.setViewVisibility(R.id.right_icon, View.GONE);
Selim Cinek860b6da2015-12-16 19:02:19 -08003570 contentView.setViewVisibility(R.id.title, View.GONE);
Christoph Studerfe718432014-09-01 18:21:18 +02003571 contentView.setTextViewText(R.id.title, null);
Selim Cinek41598732016-01-11 16:58:37 -08003572 contentView.setViewVisibility(R.id.text, View.GONE);
Christoph Studerfe718432014-09-01 18:21:18 +02003573 contentView.setTextViewText(R.id.text, null);
Selim Cinek29603462015-11-17 19:04:39 -08003574 contentView.setViewVisibility(R.id.text_line_1, View.GONE);
Selim Cinek41598732016-01-11 16:58:37 -08003575 contentView.setTextViewText(R.id.text_line_1, null);
Christoph Studerfe718432014-09-01 18:21:18 +02003576 contentView.setViewVisibility(R.id.progress, View.GONE);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003577 }
3578
Selim Cinekeaa29ca2015-11-23 13:51:13 -08003579 /**
3580 * Resets the notification header to its original state
3581 */
3582 private void resetNotificationHeader(RemoteViews contentView) {
Adrian Roosc4337a32016-08-02 18:30:34 -07003583 // Small icon doesn't need to be reset, as it's always set. Resetting would prevent
3584 // re-using the drawable when the notification is updated.
Selim Cinek7b836392015-12-04 20:02:59 -08003585 contentView.setBoolean(R.id.notification_header, "setExpanded", false);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003586 contentView.setTextViewText(R.id.app_name_text, null);
Christoph Studerca1db712014-09-10 17:31:33 +02003587 contentView.setViewVisibility(R.id.chronometer, View.GONE);
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07003588 contentView.setViewVisibility(R.id.header_text, View.GONE);
Adrian Roos9dfb78f2016-06-30 15:43:44 -07003589 contentView.setTextViewText(R.id.header_text, null);
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07003590 contentView.setViewVisibility(R.id.header_text_divider, View.GONE);
Selim Cinek29603462015-11-17 19:04:39 -08003591 contentView.setViewVisibility(R.id.time_divider, View.GONE);
Selim Cinekb85f36fd2016-04-20 18:46:36 -07003592 contentView.setViewVisibility(R.id.time, View.GONE);
Selim Cinekc848c3a2016-01-13 15:27:30 -08003593 contentView.setImageViewIcon(R.id.profile_badge, null);
3594 contentView.setViewVisibility(R.id.profile_badge, View.GONE);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003595 }
3596
3597 private void resetContentMargins(RemoteViews contentView) {
Adrian Roos2d5dbba2016-06-08 17:11:53 -07003598 contentView.setViewLayoutMarginEndDimen(R.id.line1, 0);
3599 contentView.setViewLayoutMarginEndDimen(R.id.text, 0);
Christoph Studerfe718432014-09-01 18:21:18 +02003600 }
3601
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003602 private RemoteViews applyStandardTemplate(int resId) {
Adrian Roos70d7aa32017-01-11 15:39:06 -08003603 return applyStandardTemplate(resId, mParams.reset().fillTextsFrom(this));
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02003604 }
3605
3606 /**
3607 * @param hasProgress whether the progress bar should be shown and set
3608 */
3609 private RemoteViews applyStandardTemplate(int resId, boolean hasProgress) {
Adrian Roos70d7aa32017-01-11 15:39:06 -08003610 return applyStandardTemplate(resId, mParams.reset().hasProgress(hasProgress)
3611 .fillTextsFrom(this));
Adrian Roosc1a80b02016-04-05 14:54:55 -07003612 }
3613
Adrian Roos70d7aa32017-01-11 15:39:06 -08003614 private RemoteViews applyStandardTemplate(int resId, StandardTemplateParams p) {
Kenny Guy77320062014-08-27 21:37:15 +01003615 RemoteViews contentView = new BuilderRemoteViews(mContext.getApplicationInfo(), resId);
Dan Sandler539aad42014-08-04 00:43:39 -04003616
Christoph Studerfe718432014-09-01 18:21:18 +02003617 resetStandardTemplate(contentView);
3618
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003619 final Bundle ex = mN.extras;
Selim Cinek7b9605b2017-01-19 17:36:00 -08003620 updateBackgroundColor(contentView);
Adrian Roos487374f2017-01-11 15:48:14 -08003621 bindNotificationHeader(contentView, p.ambient);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003622 bindLargeIcon(contentView);
Adrian Roos70d7aa32017-01-11 15:39:06 -08003623 boolean showProgress = handleProgressBar(p.hasProgress, contentView, ex);
3624 if (p.title != null) {
Selim Cinek860b6da2015-12-16 19:02:19 -08003625 contentView.setViewVisibility(R.id.title, View.VISIBLE);
Adrian Roos70d7aa32017-01-11 15:39:06 -08003626 contentView.setTextViewText(R.id.title, p.title);
Adrian Roos72171622017-01-27 10:32:06 -08003627 if (!p.ambient) {
3628 setTextViewColorPrimary(contentView, R.id.title);
3629 }
Selim Cinek954cc232016-05-20 13:29:23 -07003630 contentView.setViewLayoutWidth(R.id.title, showProgress
3631 ? ViewGroup.LayoutParams.WRAP_CONTENT
3632 : ViewGroup.LayoutParams.MATCH_PARENT);
Joe Onorato561d3852010-11-20 18:09:34 -08003633 }
Adrian Roos70d7aa32017-01-11 15:39:06 -08003634 if (p.text != null) {
Selim Cinek41598732016-01-11 16:58:37 -08003635 int textId = showProgress ? com.android.internal.R.id.text_line_1
3636 : com.android.internal.R.id.text;
Adrian Roos70d7aa32017-01-11 15:39:06 -08003637 contentView.setTextViewText(textId, p.text);
Adrian Roos72171622017-01-27 10:32:06 -08003638 if (!p.ambient) {
3639 setTextViewColorSecondary(contentView, textId);
3640 }
Selim Cinek41598732016-01-11 16:58:37 -08003641 contentView.setViewVisibility(textId, View.VISIBLE);
Joe Onorato561d3852010-11-20 18:09:34 -08003642 }
Selim Cinekc848c3a2016-01-13 15:27:30 -08003643
Selim Cinek279fa862016-06-14 10:57:25 -07003644 setContentMinHeight(contentView, showProgress || mN.hasLargeIcon());
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003645
Selim Cinek29603462015-11-17 19:04:39 -08003646 return contentView;
3647 }
3648
Selim Cinek7b9605b2017-01-19 17:36:00 -08003649 private void setTextViewColorPrimary(RemoteViews contentView, int id) {
3650 ensureColors();
3651 contentView.setTextColor(id, mPrimaryTextColor);
3652 }
3653
3654 private int getPrimaryTextColor() {
3655 ensureColors();
3656 return mPrimaryTextColor;
3657 }
3658
3659 private int getActionBarColor() {
3660 ensureColors();
3661 return mActionBarColor;
3662 }
3663
3664 private void setTextViewColorSecondary(RemoteViews contentView, int id) {
3665 ensureColors();
3666 contentView.setTextColor(id, mSecondaryTextColor);
3667 }
3668
3669 private void ensureColors() {
3670 int backgroundColor = getBackgroundColor();
3671 if (mPrimaryTextColor == COLOR_INVALID
3672 || mSecondaryTextColor == COLOR_INVALID
3673 || mActionBarColor == COLOR_INVALID
3674 || mTextColorsAreForBackground != backgroundColor) {
3675 mTextColorsAreForBackground = backgroundColor;
3676 mPrimaryTextColor = NotificationColorUtil.resolvePrimaryColor(
3677 mContext, backgroundColor);
3678 mSecondaryTextColor = NotificationColorUtil.resolveSecondaryColor(
3679 mContext, backgroundColor);
3680 mActionBarColor = NotificationColorUtil.resolveActionBarColor(backgroundColor);
3681 }
3682 }
3683
3684 private void updateBackgroundColor(RemoteViews contentView) {
3685 if (isColorized()) {
3686 contentView.setInt(R.id.status_bar_latest_event_content, "setBackgroundColor",
3687 getBackgroundColor());
3688 } else {
3689 // Clear it!
3690 contentView.setInt(R.id.status_bar_latest_event_content, "setBackgroundResource",
3691 0);
3692 }
3693 }
3694
Selim Cinek860b6da2015-12-16 19:02:19 -08003695 /**
3696 * @param remoteView the remote view to update the minheight in
3697 * @param hasMinHeight does it have a mimHeight
3698 * @hide
3699 */
3700 void setContentMinHeight(RemoteViews remoteView, boolean hasMinHeight) {
3701 int minHeight = 0;
3702 if (hasMinHeight) {
3703 // we need to set the minHeight of the notification
3704 minHeight = mContext.getResources().getDimensionPixelSize(
3705 com.android.internal.R.dimen.notification_min_content_height);
3706 }
3707 remoteView.setInt(R.id.notification_main_column, "setMinimumHeight", minHeight);
3708 }
3709
Selim Cinek29603462015-11-17 19:04:39 -08003710 private boolean handleProgressBar(boolean hasProgress, RemoteViews contentView, Bundle ex) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003711 final int max = ex.getInt(EXTRA_PROGRESS_MAX, 0);
3712 final int progress = ex.getInt(EXTRA_PROGRESS, 0);
3713 final boolean ind = ex.getBoolean(EXTRA_PROGRESS_INDETERMINATE);
3714 if (hasProgress && (max != 0 || ind)) {
Selim Cinek29603462015-11-17 19:04:39 -08003715 contentView.setViewVisibility(com.android.internal.R.id.progress, View.VISIBLE);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003716 contentView.setProgressBar(
Selim Cinek29603462015-11-17 19:04:39 -08003717 R.id.progress, max, progress, ind);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003718 contentView.setProgressBackgroundTintList(
3719 R.id.progress, ColorStateList.valueOf(mContext.getColor(
3720 R.color.notification_progress_background_color)));
Selim Cinek29603462015-11-17 19:04:39 -08003721 if (mN.color != COLOR_DEFAULT) {
Adrian Roos4ff3b122016-02-01 12:26:13 -08003722 ColorStateList colorStateList = ColorStateList.valueOf(resolveContrastColor());
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003723 contentView.setProgressTintList(R.id.progress, colorStateList);
3724 contentView.setProgressIndeterminateTintList(R.id.progress, colorStateList);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003725 }
Selim Cinek29603462015-11-17 19:04:39 -08003726 return true;
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003727 } else {
3728 contentView.setViewVisibility(R.id.progress, View.GONE);
Selim Cinek29603462015-11-17 19:04:39 -08003729 return false;
Jeff Sharkey1c400132011-08-05 14:50:13 -07003730 }
Joe Onorato561d3852010-11-20 18:09:34 -08003731 }
3732
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003733 private void bindLargeIcon(RemoteViews contentView) {
Selim Cinek279fa862016-06-14 10:57:25 -07003734 if (mN.mLargeIcon == null && mN.largeIcon != null) {
3735 mN.mLargeIcon = Icon.createWithBitmap(mN.largeIcon);
3736 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003737 if (mN.mLargeIcon != null) {
3738 contentView.setViewVisibility(R.id.right_icon, View.VISIBLE);
3739 contentView.setImageViewIcon(R.id.right_icon, mN.mLargeIcon);
3740 processLargeLegacyIcon(mN.mLargeIcon, contentView);
Adrian Roos2d5dbba2016-06-08 17:11:53 -07003741 int endMargin = R.dimen.notification_content_picture_margin;
3742 contentView.setViewLayoutMarginEndDimen(R.id.line1, endMargin);
3743 contentView.setViewLayoutMarginEndDimen(R.id.text, endMargin);
3744 contentView.setViewLayoutMarginEndDimen(R.id.progress, endMargin);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003745 }
3746 }
3747
Adrian Roos487374f2017-01-11 15:48:14 -08003748 private void bindNotificationHeader(RemoteViews contentView, boolean ambient) {
3749 bindSmallIcon(contentView, ambient);
3750 bindHeaderAppName(contentView, ambient);
3751 if (!ambient) {
3752 // Ambient view does not have these
3753 bindHeaderText(contentView);
3754 bindHeaderChronometerAndTime(contentView);
3755 bindExpandButton(contentView);
3756 bindProfileBadge(contentView);
3757 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003758 }
3759
3760 private void bindExpandButton(RemoteViews contentView) {
Selim Cinek7b9605b2017-01-19 17:36:00 -08003761 int color = isColorized() ? getPrimaryTextColor() : resolveContrastColor();
3762 contentView.setDrawableParameters(R.id.expand_button, false, -1, color,
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003763 PorterDuff.Mode.SRC_ATOP, -1);
Selim Cinekea4bef72015-12-02 15:51:10 -08003764 contentView.setInt(R.id.notification_header, "setOriginalNotificationColor",
Selim Cinek7b9605b2017-01-19 17:36:00 -08003765 color);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003766 }
3767
3768 private void bindHeaderChronometerAndTime(RemoteViews contentView) {
3769 if (showsTimeOrChronometer()) {
Selim Cinek29603462015-11-17 19:04:39 -08003770 contentView.setViewVisibility(R.id.time_divider, View.VISIBLE);
Selim Cinek7b9605b2017-01-19 17:36:00 -08003771 setTextViewColorSecondary(contentView, R.id.time_divider);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003772 if (mN.extras.getBoolean(EXTRA_SHOW_CHRONOMETER)) {
3773 contentView.setViewVisibility(R.id.chronometer, View.VISIBLE);
3774 contentView.setLong(R.id.chronometer, "setBase",
3775 mN.when + (SystemClock.elapsedRealtime() - System.currentTimeMillis()));
3776 contentView.setBoolean(R.id.chronometer, "setStarted", true);
Adrian Roos96b7e202016-05-17 13:50:38 -07003777 boolean countsDown = mN.extras.getBoolean(EXTRA_CHRONOMETER_COUNT_DOWN);
Selim Cinekc3b752e2016-04-20 16:13:59 -07003778 contentView.setChronometerCountDown(R.id.chronometer, countsDown);
Selim Cinek7b9605b2017-01-19 17:36:00 -08003779 setTextViewColorSecondary(contentView, R.id.chronometer);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003780 } else {
3781 contentView.setViewVisibility(R.id.time, View.VISIBLE);
3782 contentView.setLong(R.id.time, "setTime", mN.when);
Selim Cinek7b9605b2017-01-19 17:36:00 -08003783 setTextViewColorSecondary(contentView, R.id.time);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003784 }
Selim Cinekb85f36fd2016-04-20 18:46:36 -07003785 } else {
3786 // We still want a time to be set but gone, such that we can show and hide it
3787 // on demand in case it's a child notification without anything in the header
3788 contentView.setLong(R.id.time, "setTime", mN.when != 0 ? mN.when : mN.creationTime);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003789 }
3790 }
3791
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07003792 private void bindHeaderText(RemoteViews contentView) {
3793 CharSequence headerText = mN.extras.getCharSequence(EXTRA_SUB_TEXT);
3794 if (headerText == null && mStyle != null && mStyle.mSummaryTextSet
Selim Cinek03d0d652015-11-13 13:18:09 -05003795 && mStyle.hasSummaryInHeader()) {
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07003796 headerText = mStyle.mSummaryText;
Selim Cinek03d0d652015-11-13 13:18:09 -05003797 }
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07003798 if (headerText == null
3799 && mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N
3800 && mN.extras.getCharSequence(EXTRA_INFO_TEXT) != null) {
3801 headerText = mN.extras.getCharSequence(EXTRA_INFO_TEXT);
3802 }
3803 if (headerText != null) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003804 // TODO: Remove the span entirely to only have the string with propper formating.
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07003805 contentView.setTextViewText(R.id.header_text, processLegacyText(headerText));
Selim Cinek7b9605b2017-01-19 17:36:00 -08003806 setTextViewColorSecondary(contentView, R.id.header_text);
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07003807 contentView.setViewVisibility(R.id.header_text, View.VISIBLE);
3808 contentView.setViewVisibility(R.id.header_text_divider, View.VISIBLE);
Selim Cinek7b9605b2017-01-19 17:36:00 -08003809 setTextViewColorSecondary(contentView, R.id.header_text_divider);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003810 }
3811 }
3812
Adrian Rooseba05822016-04-22 17:09:27 -07003813 /**
3814 * @hide
3815 */
3816 public String loadHeaderAppName() {
Dan Sandler732bd6c2016-04-12 14:20:32 -04003817 CharSequence name = null;
3818 final PackageManager pm = mContext.getPackageManager();
3819 if (mN.extras.containsKey(EXTRA_SUBSTITUTE_APP_NAME)) {
3820 // only system packages which lump together a bunch of unrelated stuff
3821 // may substitute a different name to make the purpose of the
3822 // notification more clear. the correct package label should always
3823 // be accessible via SystemUI.
3824 final String pkg = mContext.getPackageName();
3825 final String subName = mN.extras.getString(EXTRA_SUBSTITUTE_APP_NAME);
3826 if (PackageManager.PERMISSION_GRANTED == pm.checkPermission(
3827 android.Manifest.permission.SUBSTITUTE_NOTIFICATION_APP_NAME, pkg)) {
3828 name = subName;
3829 } else {
3830 Log.w(TAG, "warning: pkg "
3831 + pkg + " attempting to substitute app name '" + subName
3832 + "' without holding perm "
3833 + android.Manifest.permission.SUBSTITUTE_NOTIFICATION_APP_NAME);
3834 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003835 }
Dan Sandler732bd6c2016-04-12 14:20:32 -04003836 if (TextUtils.isEmpty(name)) {
3837 name = pm.getApplicationLabel(mContext.getApplicationInfo());
3838 }
3839 if (TextUtils.isEmpty(name)) {
3840 // still nothing?
3841 return null;
3842 }
3843
3844 return String.valueOf(name);
3845 }
Adrian Roos487374f2017-01-11 15:48:14 -08003846 private void bindHeaderAppName(RemoteViews contentView, boolean ambient) {
Dan Sandler732bd6c2016-04-12 14:20:32 -04003847 contentView.setTextViewText(R.id.app_name_text, loadHeaderAppName());
Adrian Roos72171622017-01-27 10:32:06 -08003848 if (isColorized() && !ambient) {
Selim Cinek7b9605b2017-01-19 17:36:00 -08003849 setTextViewColorPrimary(contentView, R.id.app_name_text);
3850 } else {
3851 contentView.setTextColor(R.id.app_name_text,
3852 ambient ? resolveAmbientColor() : resolveContrastColor());
3853 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003854 }
3855
Adrian Roos487374f2017-01-11 15:48:14 -08003856 private void bindSmallIcon(RemoteViews contentView, boolean ambient) {
Selim Cinek279fa862016-06-14 10:57:25 -07003857 if (mN.mSmallIcon == null && mN.icon != 0) {
3858 mN.mSmallIcon = Icon.createWithResource(mContext, mN.icon);
3859 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003860 contentView.setImageViewIcon(R.id.icon, mN.mSmallIcon);
Adrian Roos9b45a152016-06-28 13:32:29 -07003861 contentView.setDrawableParameters(R.id.icon, false /* targetBackground */,
3862 -1 /* alpha */, -1 /* colorFilter */, null /* mode */, mN.iconLevel);
Adrian Roos487374f2017-01-11 15:48:14 -08003863 processSmallIconColor(mN.mSmallIcon, contentView, ambient);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003864 }
3865
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003866 /**
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02003867 * @return true if the built notification will show the time or the chronometer; false
3868 * otherwise
3869 */
3870 private boolean showsTimeOrChronometer() {
Selim Cinekc2c0b042016-05-18 17:13:46 -07003871 return mN.showsTime() || mN.showsChronometer();
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02003872 }
3873
Christoph Studerfe718432014-09-01 18:21:18 +02003874 private void resetStandardTemplateWithActions(RemoteViews big) {
Adrian Roos4c1fcc82016-03-31 14:39:39 -07003875 // actions_container is only reset when there are no actions to avoid focus issues with
3876 // remote inputs.
Christoph Studerfe718432014-09-01 18:21:18 +02003877 big.setViewVisibility(R.id.actions, View.GONE);
Christoph Studerfe718432014-09-01 18:21:18 +02003878 big.removeAllViews(R.id.actions);
Adrian Roose458aa82015-12-08 16:17:19 -08003879
3880 big.setViewVisibility(R.id.notification_material_reply_container, View.GONE);
3881 big.setTextViewText(R.id.notification_material_reply_text_1, null);
3882
3883 big.setViewVisibility(R.id.notification_material_reply_text_2, View.GONE);
3884 big.setTextViewText(R.id.notification_material_reply_text_2, null);
3885 big.setViewVisibility(R.id.notification_material_reply_text_3, View.GONE);
3886 big.setTextViewText(R.id.notification_material_reply_text_3, null);
Adrian Roosf852a422016-06-03 13:33:43 -07003887
3888 big.setViewLayoutMarginBottomDimen(R.id.notification_action_list_margin_target, 0);
Christoph Studerfe718432014-09-01 18:21:18 +02003889 }
3890
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003891 private RemoteViews applyStandardTemplateWithActions(int layoutId) {
Adrian Roos70d7aa32017-01-11 15:39:06 -08003892 return applyStandardTemplateWithActions(layoutId, mParams.reset().fillTextsFrom(this));
Adrian Roos48d746a2016-04-12 14:57:28 -07003893 }
3894
Adrian Roos70d7aa32017-01-11 15:39:06 -08003895 private RemoteViews applyStandardTemplateWithActions(int layoutId,
3896 StandardTemplateParams p) {
3897 RemoteViews big = applyStandardTemplate(layoutId, p);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003898
Christoph Studerfe718432014-09-01 18:21:18 +02003899 resetStandardTemplateWithActions(big);
3900
Adrian Roose458aa82015-12-08 16:17:19 -08003901 boolean validRemoteInput = false;
3902
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003903 int N = mActions.size();
Adrian Roos487374f2017-01-11 15:48:14 -08003904 boolean emphazisedMode = mN.fullScreenIntent != null && !p.ambient;
Selim Cinek06e9e1f2016-07-08 17:14:16 -07003905 big.setBoolean(R.id.actions, "setEmphasizedMode", emphazisedMode);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003906 if (N > 0) {
Adrian Roos7052de52016-03-03 15:53:34 -08003907 big.setViewVisibility(R.id.actions_container, View.VISIBLE);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003908 big.setViewVisibility(R.id.actions, View.VISIBLE);
Adrian Roos487374f2017-01-11 15:48:14 -08003909 if (p.ambient) {
3910 big.setInt(R.id.actions, "setBackgroundColor", Color.TRANSPARENT);
Selim Cinek7b9605b2017-01-19 17:36:00 -08003911 } else if (isColorized()) {
3912 big.setInt(R.id.actions, "setBackgroundColor", getActionBarColor());
3913 } else {
3914 big.setInt(R.id.actions, "setBackgroundColor", mContext.getColor(
3915 R.color.notification_action_list));
Adrian Roos487374f2017-01-11 15:48:14 -08003916 }
Adrian Roosf852a422016-06-03 13:33:43 -07003917 big.setViewLayoutMarginBottomDimen(R.id.notification_action_list_margin_target,
3918 R.dimen.notification_action_list_height);
Daniel Sandler8680bf82012-05-15 16:52:52 -04003919 if (N>MAX_ACTION_BUTTONS) N=MAX_ACTION_BUTTONS;
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003920 for (int i=0; i<N; i++) {
Adrian Roose458aa82015-12-08 16:17:19 -08003921 Action action = mActions.get(i);
3922 validRemoteInput |= hasValidRemoteInput(action);
3923
Selim Cinek06e9e1f2016-07-08 17:14:16 -07003924 final RemoteViews button = generateActionButton(action, emphazisedMode,
Adrian Roos487374f2017-01-11 15:48:14 -08003925 i % 2 != 0, p.ambient);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003926 big.addView(R.id.actions, button);
3927 }
Adrian Roos4c1fcc82016-03-31 14:39:39 -07003928 } else {
3929 big.setViewVisibility(R.id.actions_container, View.GONE);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003930 }
Adrian Roose458aa82015-12-08 16:17:19 -08003931
3932 CharSequence[] replyText = mN.extras.getCharSequenceArray(EXTRA_REMOTE_INPUT_HISTORY);
Adrian Roos487374f2017-01-11 15:48:14 -08003933 if (!p.ambient && validRemoteInput && replyText != null
Adrian Roose458aa82015-12-08 16:17:19 -08003934 && replyText.length > 0 && !TextUtils.isEmpty(replyText[0])) {
3935 big.setViewVisibility(R.id.notification_material_reply_container, View.VISIBLE);
3936 big.setTextViewText(R.id.notification_material_reply_text_1, replyText[0]);
Selim Cinek7b9605b2017-01-19 17:36:00 -08003937 setTextViewColorSecondary(big, R.id.notification_material_reply_text_1);
Adrian Roose458aa82015-12-08 16:17:19 -08003938
3939 if (replyText.length > 1 && !TextUtils.isEmpty(replyText[1])) {
3940 big.setViewVisibility(R.id.notification_material_reply_text_2, View.VISIBLE);
3941 big.setTextViewText(R.id.notification_material_reply_text_2, replyText[1]);
Selim Cinek7b9605b2017-01-19 17:36:00 -08003942 setTextViewColorSecondary(big, R.id.notification_material_reply_text_2);
Adrian Roose458aa82015-12-08 16:17:19 -08003943
3944 if (replyText.length > 2 && !TextUtils.isEmpty(replyText[2])) {
3945 big.setViewVisibility(
3946 R.id.notification_material_reply_text_3, View.VISIBLE);
3947 big.setTextViewText(R.id.notification_material_reply_text_3, replyText[2]);
Selim Cinek7b9605b2017-01-19 17:36:00 -08003948 setTextViewColorSecondary(big, R.id.notification_material_reply_text_3);
Adrian Roose458aa82015-12-08 16:17:19 -08003949 }
3950 }
3951 }
3952
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003953 return big;
3954 }
3955
Adrian Roose458aa82015-12-08 16:17:19 -08003956 private boolean hasValidRemoteInput(Action action) {
3957 if (TextUtils.isEmpty(action.title) || action.actionIntent == null) {
3958 // Weird actions
3959 return false;
3960 }
3961
3962 RemoteInput[] remoteInputs = action.getRemoteInputs();
3963 if (remoteInputs == null) {
3964 return false;
3965 }
3966
3967 for (RemoteInput r : remoteInputs) {
3968 CharSequence[] choices = r.getChoices();
3969 if (r.getAllowFreeFormInput() || (choices != null && choices.length != 0)) {
3970 return true;
3971 }
3972 }
3973 return false;
3974 }
3975
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003976 /**
3977 * Construct a RemoteViews for the final 1U notification layout. In order:
3978 * 1. Custom contentView from the caller
3979 * 2. Style's proposed content view
3980 * 3. Standard template view
3981 */
Julia Reynolds3b848122016-02-26 10:45:32 -05003982 public RemoteViews createContentView() {
Selim Cinek7d1009b2017-01-25 15:28:28 -08003983 return createContentView(false /* increasedheight */ );
3984 }
3985
3986 /**
3987 * Construct a RemoteViews for the smaller content view.
3988 *
3989 * @param increasedHeight true if this layout be created with an increased height. Some
3990 * styles may support showing more then just that basic 1U size
3991 * and the system may decide to render important notifications
3992 * slightly bigger even when collapsed.
3993 *
3994 * @hide
3995 */
3996 public RemoteViews createContentView(boolean increasedHeight) {
Selim Cinek593610c2016-02-16 18:42:57 -08003997 if (mN.contentView != null && (mStyle == null || !mStyle.displayCustomViewInline())) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003998 return mN.contentView;
3999 } else if (mStyle != null) {
Selim Cinek7d1009b2017-01-25 15:28:28 -08004000 final RemoteViews styleView = mStyle.makeContentView(increasedHeight);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004001 if (styleView != null) {
4002 return styleView;
4003 }
Joe Onorato46439ce2010-11-19 13:56:21 -08004004 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004005 return applyStandardTemplate(getBaseLayoutResource());
Joe Onorato46439ce2010-11-19 13:56:21 -08004006 }
4007
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004008 /**
4009 * Construct a RemoteViews for the final big notification layout.
4010 */
Julia Reynolds3b848122016-02-26 10:45:32 -05004011 public RemoteViews createBigContentView() {
Selim Cinek850a8542015-11-11 11:48:36 -05004012 RemoteViews result = null;
Selim Cinek593610c2016-02-16 18:42:57 -08004013 if (mN.bigContentView != null
4014 && (mStyle == null || !mStyle.displayCustomViewInline())) {
Julia Reynolds089e3e42015-11-18 09:59:57 -05004015 return mN.bigContentView;
4016 } else if (mStyle != null) {
Selim Cinek850a8542015-11-11 11:48:36 -05004017 result = mStyle.makeBigContentView();
Selim Cinek90dcf6d2015-11-18 20:24:13 -08004018 hideLine1Text(result);
Selim Cinekcc10bfb2016-02-10 16:24:21 -08004019 } else if (mActions.size() != 0) {
4020 result = applyStandardTemplateWithActions(getBigBaseLayoutResource());
Selim Cinek850a8542015-11-11 11:48:36 -05004021 }
Selim Cinek6743c0b2017-01-18 18:24:01 -08004022 makeHeaderExpanded(result);
Selim Cinek850a8542015-11-11 11:48:36 -05004023 return result;
4024 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004025
Selim Cinekeaa29ca2015-11-23 13:51:13 -08004026 /**
4027 * Construct a RemoteViews for the final notification header only
4028 *
4029 * @hide
4030 */
4031 public RemoteViews makeNotificationHeader() {
4032 RemoteViews header = new BuilderRemoteViews(mContext.getApplicationInfo(),
4033 R.layout.notification_template_header);
4034 resetNotificationHeader(header);
Adrian Roos487374f2017-01-11 15:48:14 -08004035 bindNotificationHeader(header, false /* ambient */);
Selim Cinekeaa29ca2015-11-23 13:51:13 -08004036 return header;
4037 }
4038
Adrian Roos487374f2017-01-11 15:48:14 -08004039 /**
4040 * Construct a RemoteViews for the ambient version of the notification.
4041 *
4042 * @hide
4043 */
4044 public RemoteViews makeAmbientNotification() {
4045 RemoteViews ambient = applyStandardTemplateWithActions(
4046 R.layout.notification_template_material_ambient,
4047 mParams.reset().fillTextsFrom(this).hasProgress(false).ambient(true));
4048 return ambient;
4049 }
4050
Selim Cinek29603462015-11-17 19:04:39 -08004051 private void hideLine1Text(RemoteViews result) {
Selim Cinekcc10bfb2016-02-10 16:24:21 -08004052 if (result != null) {
4053 result.setViewVisibility(R.id.text_line_1, View.GONE);
4054 }
Selim Cinek29603462015-11-17 19:04:39 -08004055 }
4056
Selim Cinek6743c0b2017-01-18 18:24:01 -08004057 /**
4058 * Adapt the Notification header if this view is used as an expanded view.
4059 *
4060 * @hide
4061 */
4062 public static void makeHeaderExpanded(RemoteViews result) {
Selim Cinekcc10bfb2016-02-10 16:24:21 -08004063 if (result != null) {
4064 result.setBoolean(R.id.notification_header, "setExpanded", true);
4065 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004066 }
4067
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004068 /**
4069 * Construct a RemoteViews for the final heads-up notification layout.
4070 */
Julia Reynolds3b848122016-02-26 10:45:32 -05004071 public RemoteViews createHeadsUpContentView() {
Selim Cinek593610c2016-02-16 18:42:57 -08004072 if (mN.headsUpContentView != null
4073 && (mStyle == null || !mStyle.displayCustomViewInline())) {
Julia Reynolds089e3e42015-11-18 09:59:57 -05004074 return mN.headsUpContentView;
4075 } else if (mStyle != null) {
4076 final RemoteViews styleView = mStyle.makeHeadsUpContentView();
4077 if (styleView != null) {
4078 return styleView;
4079 }
4080 } else if (mActions.size() == 0) {
4081 return null;
4082 }
4083
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004084 return applyStandardTemplateWithActions(getBigBaseLayoutResource());
Chris Wren8fd39ec2014-02-27 17:43:26 -05004085 }
4086
Selim Cinek624c02db2015-12-14 21:00:02 -08004087 /**
4088 * Construct a RemoteViews for the display in public contexts like on the lockscreen.
4089 *
4090 * @hide
4091 */
4092 public RemoteViews makePublicContentView() {
4093 if (mN.publicVersion != null) {
4094 final Builder builder = recoverBuilder(mContext, mN.publicVersion);
Julia Reynolds3b848122016-02-26 10:45:32 -05004095 return builder.createContentView();
Selim Cinek624c02db2015-12-14 21:00:02 -08004096 }
4097 Bundle savedBundle = mN.extras;
4098 Style style = mStyle;
4099 mStyle = null;
4100 Icon largeIcon = mN.mLargeIcon;
4101 mN.mLargeIcon = null;
Selim Cinek279fa862016-06-14 10:57:25 -07004102 Bitmap largeIconLegacy = mN.largeIcon;
4103 mN.largeIcon = null;
Selim Cinek624c02db2015-12-14 21:00:02 -08004104 Bundle publicExtras = new Bundle();
4105 publicExtras.putBoolean(EXTRA_SHOW_WHEN,
4106 savedBundle.getBoolean(EXTRA_SHOW_WHEN));
4107 publicExtras.putBoolean(EXTRA_SHOW_CHRONOMETER,
4108 savedBundle.getBoolean(EXTRA_SHOW_CHRONOMETER));
Adrian Roos96b7e202016-05-17 13:50:38 -07004109 publicExtras.putBoolean(EXTRA_CHRONOMETER_COUNT_DOWN,
4110 savedBundle.getBoolean(EXTRA_CHRONOMETER_COUNT_DOWN));
Selim Cinek624c02db2015-12-14 21:00:02 -08004111 publicExtras.putCharSequence(EXTRA_TITLE,
4112 mContext.getString(R.string.notification_hidden_text));
4113 mN.extras = publicExtras;
4114 final RemoteViews publicView = applyStandardTemplate(getBaseLayoutResource());
4115 mN.extras = savedBundle;
4116 mN.mLargeIcon = largeIcon;
Selim Cinek279fa862016-06-14 10:57:25 -07004117 mN.largeIcon = largeIconLegacy;
Selim Cinek624c02db2015-12-14 21:00:02 -08004118 mStyle = style;
4119 return publicView;
4120 }
4121
Selim Cinek6743c0b2017-01-18 18:24:01 -08004122 /**
4123 * Construct a content view for the display when low - priority
4124 *
4125 * @param useRegularSubtext uses the normal subtext set if there is one available. Otherwise
4126 * a new subtext is created consisting of the content of the
4127 * notification.
4128 * @hide
4129 */
4130 public RemoteViews makeLowPriorityContentView(boolean useRegularSubtext) {
4131 int color = mN.color;
4132 mN.color = COLOR_DEFAULT;
4133 CharSequence summary = mN.extras.getCharSequence(EXTRA_SUB_TEXT);
4134 if (!useRegularSubtext || TextUtils.isEmpty(summary)) {
4135 CharSequence newSummary = createSummaryText();
4136 if (!TextUtils.isEmpty(newSummary)) {
4137 mN.extras.putCharSequence(EXTRA_SUB_TEXT, newSummary);
4138 }
4139 }
4140 RemoteViews header = makeNotificationHeader();
4141 if (summary != null) {
4142 mN.extras.putCharSequence(EXTRA_SUB_TEXT, summary);
4143 } else {
4144 mN.extras.remove(EXTRA_SUB_TEXT);
4145 }
4146 mN.color = color;
4147 return header;
4148 }
Selim Cinek624c02db2015-12-14 21:00:02 -08004149
Selim Cinek6743c0b2017-01-18 18:24:01 -08004150 private CharSequence createSummaryText() {
4151 CharSequence titleText = mN.extras.getCharSequence(Notification.EXTRA_TITLE);
4152 if (USE_ONLY_TITLE_IN_LOW_PRIORITY_SUMMARY) {
4153 return titleText;
4154 }
4155 SpannableStringBuilder summary = new SpannableStringBuilder();
4156 if (titleText == null) {
4157 titleText = mN.extras.getCharSequence(Notification.EXTRA_TITLE_BIG);
4158 }
4159 BidiFormatter bidi = BidiFormatter.getInstance();
4160 if (titleText != null) {
4161 summary.append(bidi.unicodeWrap(titleText));
4162 }
4163 CharSequence contentText = mN.extras.getCharSequence(Notification.EXTRA_TEXT);
4164 if (titleText != null && contentText != null) {
4165 summary.append(bidi.unicodeWrap(mContext.getText(
4166 R.string.notification_header_divider_symbol_with_spaces)));
4167 }
4168 if (contentText != null) {
4169 summary.append(bidi.unicodeWrap(contentText));
4170 }
4171 return summary;
4172 }
Chris Wren8fd39ec2014-02-27 17:43:26 -05004173
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004174 private RemoteViews generateActionButton(Action action, boolean emphazisedMode,
Adrian Roos487374f2017-01-11 15:48:14 -08004175 boolean oddAction, boolean ambient) {
Daniel Sandler8680bf82012-05-15 16:52:52 -04004176 final boolean tombstone = (action.actionIntent == null);
Selim Cinekf33b1112015-07-15 17:45:11 -07004177 RemoteViews button = new BuilderRemoteViews(mContext.getApplicationInfo(),
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004178 emphazisedMode ? getEmphasizedActionLayoutResource()
4179 : tombstone ? getActionTombstoneLayoutResource()
4180 : getActionLayoutResource());
Daniel Sandler8680bf82012-05-15 16:52:52 -04004181 if (!tombstone) {
Daniel Sandlere5518842012-05-10 16:20:40 -04004182 button.setOnClickPendingIntent(R.id.action0, action.actionIntent);
Daniel Sandlere5518842012-05-10 16:20:40 -04004183 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004184 button.setContentDescription(R.id.action0, action.title);
Adrian Roosfe84e1f2015-11-04 15:55:39 -08004185 if (action.mRemoteInputs != null) {
4186 button.setRemoteInputs(R.id.action0, action.mRemoteInputs);
4187 }
Selim Cinek7b9605b2017-01-19 17:36:00 -08004188 // TODO: handle emphasized mode / actions right
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004189 if (emphazisedMode) {
Selim Cinek981962e2016-07-20 20:41:58 -07004190 // change the background bgColor
4191 int bgColor = mContext.getColor(oddAction ? R.color.notification_action_list
4192 : R.color.notification_action_list_dark);
4193 button.setDrawableParameters(R.id.button_holder, true, -1, bgColor,
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004194 PorterDuff.Mode.SRC_ATOP, -1);
Selim Cinek981962e2016-07-20 20:41:58 -07004195 CharSequence title = action.title;
4196 ColorStateList[] outResultColor = null;
4197 if (isLegacy()) {
4198 title = clearColorSpans(title);
4199 } else {
4200 outResultColor = new ColorStateList[1];
4201 title = ensureColorSpanContrast(title, bgColor, outResultColor);
4202 }
4203 button.setTextViewText(R.id.action0, title);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004204 setTextViewColorPrimary(button, R.id.action0);
Selim Cinek981962e2016-07-20 20:41:58 -07004205 if (outResultColor != null && outResultColor[0] != null) {
4206 // We need to set the text color as well since changing a text to uppercase
4207 // clears its spans.
4208 button.setTextColor(R.id.action0, outResultColor[0]);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004209 } else if (mN.color != COLOR_DEFAULT && !isColorized()) {
Selim Cinek981962e2016-07-20 20:41:58 -07004210 button.setTextColor(R.id.action0,resolveContrastColor());
4211 }
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004212 } else {
Selim Cinek981962e2016-07-20 20:41:58 -07004213 button.setTextViewText(R.id.action0, processLegacyText(action.title));
Adrian Roos72171622017-01-27 10:32:06 -08004214 if (isColorized() && !ambient) {
Selim Cinek7b9605b2017-01-19 17:36:00 -08004215 setTextViewColorPrimary(button, R.id.action0);
4216 } else if (mN.color != COLOR_DEFAULT) {
Adrian Roos487374f2017-01-11 15:48:14 -08004217 button.setTextColor(R.id.action0,
4218 ambient ? resolveAmbientColor() : resolveContrastColor());
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004219 }
Adrian Roosfe84e1f2015-11-04 15:55:39 -08004220 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004221 return button;
4222 }
4223
Joe Onoratocb109a02011-01-18 17:57:41 -08004224 /**
Selim Cinek981962e2016-07-20 20:41:58 -07004225 * Clears all color spans of a text
4226 * @param charSequence the input text
4227 * @return the same text but without color spans
4228 */
4229 private CharSequence clearColorSpans(CharSequence charSequence) {
4230 if (charSequence instanceof Spanned) {
4231 Spanned ss = (Spanned) charSequence;
4232 Object[] spans = ss.getSpans(0, ss.length(), Object.class);
4233 SpannableStringBuilder builder = new SpannableStringBuilder(ss.toString());
4234 for (Object span : spans) {
4235 Object resultSpan = span;
4236 if (resultSpan instanceof CharacterStyle) {
4237 resultSpan = ((CharacterStyle) span).getUnderlying();
4238 }
4239 if (resultSpan instanceof TextAppearanceSpan) {
4240 TextAppearanceSpan originalSpan = (TextAppearanceSpan) resultSpan;
4241 if (originalSpan.getTextColor() != null) {
4242 resultSpan = new TextAppearanceSpan(
4243 originalSpan.getFamily(),
4244 originalSpan.getTextStyle(),
4245 originalSpan.getTextSize(),
4246 null,
4247 originalSpan.getLinkTextColor());
4248 }
4249 } else if (resultSpan instanceof ForegroundColorSpan
4250 || (resultSpan instanceof BackgroundColorSpan)) {
4251 continue;
4252 } else {
4253 resultSpan = span;
4254 }
4255 builder.setSpan(resultSpan, ss.getSpanStart(span), ss.getSpanEnd(span),
4256 ss.getSpanFlags(span));
4257 }
4258 return builder;
4259 }
4260 return charSequence;
4261 }
4262
4263 /**
4264 * Ensures contrast on color spans against a background color. also returns the color of the
4265 * text if a span was found that spans over the whole text.
4266 *
4267 * @param charSequence the charSequence on which the spans are
4268 * @param background the background color to ensure the contrast against
4269 * @param outResultColor an array in which a color will be returned as the first element if
4270 * there exists a full length color span.
4271 * @return the contrasted charSequence
4272 */
4273 private CharSequence ensureColorSpanContrast(CharSequence charSequence, int background,
4274 ColorStateList[] outResultColor) {
4275 if (charSequence instanceof Spanned) {
4276 Spanned ss = (Spanned) charSequence;
4277 Object[] spans = ss.getSpans(0, ss.length(), Object.class);
4278 SpannableStringBuilder builder = new SpannableStringBuilder(ss.toString());
4279 for (Object span : spans) {
4280 Object resultSpan = span;
4281 int spanStart = ss.getSpanStart(span);
4282 int spanEnd = ss.getSpanEnd(span);
4283 boolean fullLength = (spanEnd - spanStart) == charSequence.length();
4284 if (resultSpan instanceof CharacterStyle) {
4285 resultSpan = ((CharacterStyle) span).getUnderlying();
4286 }
4287 if (resultSpan instanceof TextAppearanceSpan) {
4288 TextAppearanceSpan originalSpan = (TextAppearanceSpan) resultSpan;
4289 ColorStateList textColor = originalSpan.getTextColor();
4290 if (textColor != null) {
4291 int[] colors = textColor.getColors();
4292 int[] newColors = new int[colors.length];
4293 for (int i = 0; i < newColors.length; i++) {
4294 newColors[i] = NotificationColorUtil.ensureLargeTextContrast(
4295 colors[i], background);
4296 }
4297 textColor = new ColorStateList(textColor.getStates().clone(),
4298 newColors);
4299 resultSpan = new TextAppearanceSpan(
4300 originalSpan.getFamily(),
4301 originalSpan.getTextStyle(),
4302 originalSpan.getTextSize(),
4303 textColor,
4304 originalSpan.getLinkTextColor());
4305 if (fullLength) {
4306 outResultColor[0] = new ColorStateList(
4307 textColor.getStates().clone(), newColors);
4308 }
4309 }
4310 } else if (resultSpan instanceof ForegroundColorSpan) {
4311 ForegroundColorSpan originalSpan = (ForegroundColorSpan) resultSpan;
4312 int foregroundColor = originalSpan.getForegroundColor();
4313 foregroundColor = NotificationColorUtil.ensureLargeTextContrast(
4314 foregroundColor, background);
4315 resultSpan = new ForegroundColorSpan(foregroundColor);
4316 if (fullLength) {
4317 outResultColor[0] = ColorStateList.valueOf(foregroundColor);
4318 }
4319 } else {
4320 resultSpan = span;
4321 }
4322
4323 builder.setSpan(resultSpan, spanStart, spanEnd, ss.getSpanFlags(span));
4324 }
4325 return builder;
4326 }
4327 return charSequence;
4328 }
4329
4330 /**
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004331 * @return Whether we are currently building a notification from a legacy (an app that
Alan Viverette3cb07a462014-06-06 14:19:53 -07004332 * doesn't create material notifications by itself) app.
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004333 */
4334 private boolean isLegacy() {
Selim Cinek7b9605b2017-01-19 17:36:00 -08004335 if (!mIsLegacyInitialized) {
4336 mIsLegacy = mContext.getApplicationInfo().targetSdkVersion
4337 < Build.VERSION_CODES.LOLLIPOP;
4338 mIsLegacyInitialized = true;
4339 }
4340 return mIsLegacy;
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004341 }
4342
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004343 private CharSequence processLegacyText(CharSequence charSequence) {
Selim Cinek7b9605b2017-01-19 17:36:00 -08004344 if (isLegacy() || isColorized()) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004345 return getColorUtil().invertCharSequenceColors(charSequence);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004346 } else {
4347 return charSequence;
4348 }
4349 }
4350
Dan Sandler26e81cf2014-05-06 10:01:27 -04004351 /**
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004352 * Apply any necessariy colors to the small icon
Dan Sandler26e81cf2014-05-06 10:01:27 -04004353 */
Adrian Roos487374f2017-01-11 15:48:14 -08004354 private void processSmallIconColor(Icon smallIcon, RemoteViews contentView,
4355 boolean ambient) {
Selim Cinekea4bef72015-12-02 15:51:10 -08004356 boolean colorable = !isLegacy() || getColorUtil().isGrayscaleIcon(mContext, smallIcon);
Adrian Roos487374f2017-01-11 15:48:14 -08004357 int color = ambient ? resolveAmbientColor() : resolveContrastColor();
Selim Cinekea4bef72015-12-02 15:51:10 -08004358 if (colorable) {
Adrian Roos487374f2017-01-11 15:48:14 -08004359 contentView.setDrawableParameters(R.id.icon, false, -1, color,
Jorim Jaggi92df1f22014-12-16 19:44:41 +01004360 PorterDuff.Mode.SRC_ATOP, -1);
Selim Cinekea4bef72015-12-02 15:51:10 -08004361
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004362 }
Selim Cinekea4bef72015-12-02 15:51:10 -08004363 contentView.setInt(R.id.notification_header, "setOriginalIconColor",
Adrian Roos487374f2017-01-11 15:48:14 -08004364 colorable ? color : NotificationHeaderView.NO_COLOR);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004365 }
4366
Dan Sandler26e81cf2014-05-06 10:01:27 -04004367 /**
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004368 * Make the largeIcon dark if it's a fake smallIcon (that is,
Dan Sandler26e81cf2014-05-06 10:01:27 -04004369 * if it's grayscale).
4370 */
4371 // TODO: also check bounds, transparency, that sort of thing.
Dan Sandlerd63f9322015-05-06 15:18:49 -04004372 private void processLargeLegacyIcon(Icon largeIcon, RemoteViews contentView) {
4373 if (largeIcon != null && isLegacy()
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004374 && getColorUtil().isGrayscaleIcon(mContext, largeIcon)) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004375 // resolve color will fall back to the default when legacy
Adrian Roos4ff3b122016-02-01 12:26:13 -08004376 contentView.setDrawableParameters(R.id.icon, false, -1, resolveContrastColor(),
Dan Sandler190d58d2014-05-15 09:33:39 -04004377 PorterDuff.Mode.SRC_ATOP, -1);
Jorim Jaggi92df1f22014-12-16 19:44:41 +01004378 }
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004379 }
4380
Julia Reynolds10ee1fc2015-11-09 11:04:55 -05004381 private void sanitizeColor() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004382 if (mN.color != COLOR_DEFAULT) {
4383 mN.color |= 0xFF000000; // no alpha for custom colors
Jorim Jaggi74419312014-06-10 20:57:21 +02004384 }
Jorim Jaggi74419312014-06-10 20:57:21 +02004385 }
4386
Adrian Roos4ff3b122016-02-01 12:26:13 -08004387 int resolveContrastColor() {
4388 if (mCachedContrastColorIsFor == mN.color && mCachedContrastColor != COLOR_INVALID) {
4389 return mCachedContrastColor;
Dan Sandler26e81cf2014-05-06 10:01:27 -04004390 }
Adrian Roos4ff3b122016-02-01 12:26:13 -08004391 final int contrasted = NotificationColorUtil.resolveContrastColor(mContext, mN.color);
4392
4393 mCachedContrastColorIsFor = mN.color;
4394 return mCachedContrastColor = contrasted;
Dan Sandler26e81cf2014-05-06 10:01:27 -04004395 }
4396
Adrian Roos487374f2017-01-11 15:48:14 -08004397 int resolveAmbientColor() {
4398 if (mCachedAmbientColorIsFor == mN.color && mCachedAmbientColorIsFor != COLOR_INVALID) {
4399 return mCachedAmbientColor;
4400 }
4401 final int contrasted = NotificationColorUtil.resolveAmbientColor(mContext, mN.color);
4402
4403 mCachedAmbientColorIsFor = mN.color;
4404 return mCachedAmbientColor = contrasted;
4405 }
4406
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004407 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004408 * Apply the unstyled operations and return a new {@link Notification} object.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04004409 * @hide
Joe Onoratocb109a02011-01-18 17:57:41 -08004410 */
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04004411 public Notification buildUnstyled() {
Daniel Sandlera0a938c2012-03-15 08:42:37 -04004412 if (mActions.size() > 0) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004413 mN.actions = new Action[mActions.size()];
4414 mActions.toArray(mN.actions);
Daniel Sandlera0a938c2012-03-15 08:42:37 -04004415 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004416 if (!mPersonList.isEmpty()) {
4417 mN.extras.putStringArray(EXTRA_PEOPLE,
4418 mPersonList.toArray(new String[mPersonList.size()]));
Dan Sandler0bf2ed82013-12-21 23:33:41 -06004419 }
Selim Cinek247fa012016-02-18 09:50:48 -08004420 if (mN.bigContentView != null || mN.contentView != null
4421 || mN.headsUpContentView != null) {
4422 mN.extras.putBoolean(EXTRA_CONTAINS_CUSTOM_VIEW, true);
4423 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004424 return mN;
Joe Onorato46439ce2010-11-19 13:56:21 -08004425 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004426
Julia Reynolds3b848122016-02-26 10:45:32 -05004427 /**
4428 * Creates a Builder from an existing notification so further changes can be made.
4429 * @param context The context for your application / activity.
4430 * @param n The notification to create a Builder from.
4431 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004432 public static Notification.Builder recoverBuilder(Context context, Notification n) {
Christoph Studer4600f9b2014-07-22 22:44:43 +02004433 // Re-create notification context so we can access app resources.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004434 ApplicationInfo applicationInfo = n.extras.getParcelable(
4435 EXTRA_BUILDER_APPLICATION_INFO);
Christoph Studer4600f9b2014-07-22 22:44:43 +02004436 Context builderContext;
Julia Reynoldsda303542015-11-23 14:00:20 -05004437 if (applicationInfo != null) {
4438 try {
4439 builderContext = context.createApplicationContext(applicationInfo,
4440 Context.CONTEXT_RESTRICTED);
4441 } catch (NameNotFoundException e) {
4442 Log.e(TAG, "ApplicationInfo " + applicationInfo + " not found");
4443 builderContext = context; // try with our context
4444 }
4445 } else {
4446 builderContext = context; // try with given context
Christoph Studer4600f9b2014-07-22 22:44:43 +02004447 }
4448
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004449 return new Builder(builderContext, n);
Christoph Studer4600f9b2014-07-22 22:44:43 +02004450 }
4451
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004452 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004453 * @deprecated Use {@link #build()} instead.
4454 */
4455 @Deprecated
4456 public Notification getNotification() {
4457 return build();
4458 }
4459
4460 /**
4461 * Combine all of the options that have been set and return a new {@link Notification}
4462 * object.
4463 */
4464 public Notification build() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004465 // first, add any extras from the calling code
4466 if (mUserExtras != null) {
4467 mN.extras = getAllExtras();
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07004468 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004469
Selim Cinekb85f36fd2016-04-20 18:46:36 -07004470 mN.creationTime = System.currentTimeMillis();
4471
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004472 // lazy stuff from mContext; see comment in Builder(Context, Notification)
Julia Reynoldsda303542015-11-23 14:00:20 -05004473 Notification.addFieldsFromContext(mContext, mN);
Christoph Studer943aa672014-08-03 20:31:16 +02004474
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004475 buildUnstyled();
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004476
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004477 if (mStyle != null) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004478 mStyle.buildStyled(mN);
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004479 }
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004480
Adrian Roos5081c0d2016-02-26 16:04:19 -08004481 if (mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N
4482 && (mStyle == null || !mStyle.displayCustomViewInline())) {
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004483 if (mN.contentView == null) {
Julia Reynolds3b848122016-02-26 10:45:32 -05004484 mN.contentView = createContentView();
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004485 mN.extras.putInt(EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT,
4486 mN.contentView.getSequenceNumber());
4487 }
4488 if (mN.bigContentView == null) {
Julia Reynolds3b848122016-02-26 10:45:32 -05004489 mN.bigContentView = createBigContentView();
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004490 if (mN.bigContentView != null) {
4491 mN.extras.putInt(EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT,
4492 mN.bigContentView.getSequenceNumber());
4493 }
4494 }
4495 if (mN.headsUpContentView == null) {
Julia Reynolds3b848122016-02-26 10:45:32 -05004496 mN.headsUpContentView = createHeadsUpContentView();
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004497 if (mN.headsUpContentView != null) {
4498 mN.extras.putInt(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT,
4499 mN.headsUpContentView.getSequenceNumber());
4500 }
4501 }
4502 }
4503
Julia Reynolds4c0c2022016-02-02 15:11:59 -05004504 if ((mN.defaults & DEFAULT_LIGHTS) != 0) {
4505 mN.flags |= FLAG_SHOW_LIGHTS;
4506 }
4507
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004508 return mN;
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004509 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05004510
4511 /**
4512 * Apply this Builder to an existing {@link Notification} object.
4513 *
4514 * @hide
4515 */
4516 public Notification buildInto(Notification n) {
Daniel Sandler1a497d32013-04-18 14:52:45 -04004517 build().cloneInto(n, true);
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05004518 return n;
4519 }
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004520
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004521 /**
Adrian Roos184bfe022016-03-03 13:41:44 -08004522 * Removes RemoteViews that were created for compatibility from {@param n}, if they did not
4523 * change.
4524 *
4525 * @return {@param n}, if no stripping is needed, otherwise a stripped clone of {@param n}.
4526 *
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004527 * @hide
4528 */
Adrian Roos184bfe022016-03-03 13:41:44 -08004529 public static Notification maybeCloneStrippedForDelivery(Notification n) {
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004530 String templateClass = n.extras.getString(EXTRA_TEMPLATE);
Adrian Roos184bfe022016-03-03 13:41:44 -08004531
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004532 // Only strip views for known Styles because we won't know how to
4533 // re-create them otherwise.
Adrian Roos184bfe022016-03-03 13:41:44 -08004534 if (!TextUtils.isEmpty(templateClass)
4535 && getNotificationStyleClass(templateClass) == null) {
4536 return n;
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004537 }
Adrian Roos184bfe022016-03-03 13:41:44 -08004538
4539 // Only strip unmodified BuilderRemoteViews.
4540 boolean stripContentView = n.contentView instanceof BuilderRemoteViews &&
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004541 n.extras.getInt(EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT, -1) ==
Adrian Roos184bfe022016-03-03 13:41:44 -08004542 n.contentView.getSequenceNumber();
4543 boolean stripBigContentView = n.bigContentView instanceof BuilderRemoteViews &&
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004544 n.extras.getInt(EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT, -1) ==
Adrian Roos184bfe022016-03-03 13:41:44 -08004545 n.bigContentView.getSequenceNumber();
4546 boolean stripHeadsUpContentView = n.headsUpContentView instanceof BuilderRemoteViews &&
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004547 n.extras.getInt(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT, -1) ==
Adrian Roos184bfe022016-03-03 13:41:44 -08004548 n.headsUpContentView.getSequenceNumber();
4549
4550 // Nothing to do here, no need to clone.
4551 if (!stripContentView && !stripBigContentView && !stripHeadsUpContentView) {
4552 return n;
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004553 }
Adrian Roos184bfe022016-03-03 13:41:44 -08004554
4555 Notification clone = n.clone();
4556 if (stripContentView) {
4557 clone.contentView = null;
4558 clone.extras.remove(EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT);
4559 }
4560 if (stripBigContentView) {
4561 clone.bigContentView = null;
4562 clone.extras.remove(EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT);
4563 }
4564 if (stripHeadsUpContentView) {
4565 clone.headsUpContentView = null;
4566 clone.extras.remove(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT);
4567 }
4568 return clone;
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004569 }
4570
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004571 private int getBaseLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07004572 return R.layout.notification_template_material_base;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004573 }
4574
4575 private int getBigBaseLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07004576 return R.layout.notification_template_material_big_base;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004577 }
4578
4579 private int getBigPictureLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07004580 return R.layout.notification_template_material_big_picture;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004581 }
4582
4583 private int getBigTextLayoutResource() {
Jorim Jaggi445d3c02014-08-19 22:33:42 +02004584 return R.layout.notification_template_material_big_text;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004585 }
4586
4587 private int getInboxLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07004588 return R.layout.notification_template_material_inbox;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004589 }
4590
Adrian Roosc1a80b02016-04-05 14:54:55 -07004591 private int getMessagingLayoutResource() {
4592 return R.layout.notification_template_material_messaging;
4593 }
4594
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004595 private int getActionLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07004596 return R.layout.notification_material_action;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004597 }
4598
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004599 private int getEmphasizedActionLayoutResource() {
4600 return R.layout.notification_material_action_emphasized;
4601 }
4602
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004603 private int getActionTombstoneLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07004604 return R.layout.notification_material_action_tombstone;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004605 }
Selim Cinek7b9605b2017-01-19 17:36:00 -08004606
4607 private int getBackgroundColor() {
4608 if (isColorized()) {
4609 return mN.color;
4610 } else {
4611 return COLOR_DEFAULT;
4612 }
4613 }
4614
4615 private boolean isColorized() {
4616 return mN.isColorized();
4617 }
4618 }
4619
4620 /**
4621 * @return whether this notification is ongoing and can't be dismissed by the user.
4622 */
4623 private boolean isOngoing() {
4624 final int ongoingFlags = Notification.FLAG_FOREGROUND_SERVICE
4625 | Notification.FLAG_ONGOING_EVENT | Notification.FLAG_NO_CLEAR;
4626 return (flags & ongoingFlags) != 0;
4627 }
4628
4629 /**
Selim Cinek7d1009b2017-01-25 15:28:28 -08004630 * @return the style class of this notification
4631 * @hide
4632 */
4633 public Class<? extends Notification.Style> getNotificationStyle() {
4634 String templateClass = extras.getString(Notification.EXTRA_TEMPLATE);
4635
4636 if (!TextUtils.isEmpty(templateClass)) {
4637 return Notification.getNotificationStyleClass(templateClass);
4638 }
4639 return null;
4640 }
4641
4642 /**
Selim Cinek7b9605b2017-01-19 17:36:00 -08004643 * @return true if this notification is colorized. This also factors in wheather the
4644 * notification is ongoing.
4645 *
4646 * @hide
4647 */
4648 public boolean isColorized() {
4649 return extras.getBoolean(EXTRA_COLORIZED) && isOngoing();
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004650 }
4651
Selim Cinek279fa862016-06-14 10:57:25 -07004652 private boolean hasLargeIcon() {
4653 return mLargeIcon != null || largeIcon != null;
4654 }
4655
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004656 /**
Selim Cinekc2c0b042016-05-18 17:13:46 -07004657 * @return true if the notification will show the time; false otherwise
Selim Cinekb85f36fd2016-04-20 18:46:36 -07004658 * @hide
4659 */
Selim Cinekc2c0b042016-05-18 17:13:46 -07004660 public boolean showsTime() {
Selim Cinekb85f36fd2016-04-20 18:46:36 -07004661 return when != 0 && extras.getBoolean(EXTRA_SHOW_WHEN);
4662 }
4663
4664 /**
Selim Cinekc2c0b042016-05-18 17:13:46 -07004665 * @return true if the notification will show a chronometer; false otherwise
4666 * @hide
4667 */
4668 public boolean showsChronometer() {
4669 return when != 0 && extras.getBoolean(EXTRA_SHOW_CHRONOMETER);
4670 }
4671
4672 /**
Julia Reynolds4a02afb2016-12-13 13:39:52 -05004673 * @hide
4674 */
4675 @SystemApi
4676 public static Class<? extends Style> getNotificationStyleClass(String templateClass) {
4677 Class<? extends Style>[] classes = new Class[] {
4678 BigTextStyle.class, BigPictureStyle.class, InboxStyle.class, MediaStyle.class,
4679 DecoratedCustomViewStyle.class, DecoratedMediaCustomViewStyle.class,
4680 MessagingStyle.class };
4681 for (Class<? extends Style> innerClass : classes) {
4682 if (templateClass.equals(innerClass.getName())) {
4683 return innerClass;
4684 }
4685 }
4686 return null;
4687 }
4688
4689 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004690 * An object that can apply a rich notification style to a {@link Notification.Builder}
4691 * object.
4692 */
Griff Hazendfcb0802014-02-11 12:00:00 -08004693 public static abstract class Style {
Chris Wrend6297db2012-05-03 16:20:13 -04004694 private CharSequence mBigContentTitle;
Jorim Jaggi457a10d2014-09-08 16:18:23 +02004695
4696 /**
4697 * @hide
4698 */
4699 protected CharSequence mSummaryText = null;
4700
4701 /**
4702 * @hide
4703 */
4704 protected boolean mSummaryTextSet = false;
Chris Wrend6297db2012-05-03 16:20:13 -04004705
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004706 protected Builder mBuilder;
4707
Chris Wrend6297db2012-05-03 16:20:13 -04004708 /**
4709 * Overrides ContentTitle in the big form of the template.
4710 * This defaults to the value passed to setContentTitle().
4711 */
4712 protected void internalSetBigContentTitle(CharSequence title) {
4713 mBigContentTitle = title;
4714 }
4715
4716 /**
4717 * Set the first line of text after the detail section in the big form of the template.
4718 */
4719 protected void internalSetSummaryText(CharSequence cs) {
4720 mSummaryText = cs;
Daniel Sandler619738c2012-06-07 16:33:08 -04004721 mSummaryTextSet = true;
Chris Wrend6297db2012-05-03 16:20:13 -04004722 }
4723
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004724 public void setBuilder(Builder builder) {
4725 if (mBuilder != builder) {
4726 mBuilder = builder;
Daniel Sandlerc08dea22012-06-28 08:35:24 -07004727 if (mBuilder != null) {
4728 mBuilder.setStyle(this);
4729 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004730 }
4731 }
4732
Chris Wrend6297db2012-05-03 16:20:13 -04004733 protected void checkBuilder() {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004734 if (mBuilder == null) {
4735 throw new IllegalArgumentException("Style requires a valid Builder object");
4736 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004737 }
Chris Wrend6297db2012-05-03 16:20:13 -04004738
4739 protected RemoteViews getStandardView(int layoutId) {
4740 checkBuilder();
4741
Christoph Studer4600f9b2014-07-22 22:44:43 +02004742 // Nasty.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004743 CharSequence oldBuilderContentTitle =
4744 mBuilder.getAllExtras().getCharSequence(EXTRA_TITLE);
Chris Wrend6297db2012-05-03 16:20:13 -04004745 if (mBigContentTitle != null) {
4746 mBuilder.setContentTitle(mBigContentTitle);
4747 }
4748
Chris Wrend6297db2012-05-03 16:20:13 -04004749 RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(layoutId);
4750
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004751 mBuilder.getAllExtras().putCharSequence(EXTRA_TITLE, oldBuilderContentTitle);
Christoph Studer4600f9b2014-07-22 22:44:43 +02004752
Chris Wrend6297db2012-05-03 16:20:13 -04004753 if (mBigContentTitle != null && mBigContentTitle.equals("")) {
4754 contentView.setViewVisibility(R.id.line1, View.GONE);
Chris Wren67dc9a02012-05-16 01:03:20 -04004755 } else {
4756 contentView.setViewVisibility(R.id.line1, View.VISIBLE);
Chris Wrend6297db2012-05-03 16:20:13 -04004757 }
4758
Chris Wrend6297db2012-05-03 16:20:13 -04004759 return contentView;
4760 }
4761
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004762 /**
Selim Cinek7d1009b2017-01-25 15:28:28 -08004763 * Construct a Style-specific RemoteViews for the collapsed notification layout.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004764 * The default implementation has nothing additional to add.
Selim Cinek7d1009b2017-01-25 15:28:28 -08004765 *
4766 * @param increasedHeight true if this layout be created with an increased height.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004767 * @hide
4768 */
Selim Cinek7d1009b2017-01-25 15:28:28 -08004769 public RemoteViews makeContentView(boolean increasedHeight) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004770 return null;
4771 }
4772
4773 /**
4774 * Construct a Style-specific RemoteViews for the final big notification layout.
4775 * @hide
4776 */
4777 public RemoteViews makeBigContentView() {
4778 return null;
4779 }
4780
4781 /**
4782 * Construct a Style-specific RemoteViews for the final HUN layout.
4783 * @hide
4784 */
4785 public RemoteViews makeHeadsUpContentView() {
4786 return null;
4787 }
4788
4789 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004790 * Apply any style-specific extras to this notification before shipping it out.
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004791 * @hide
4792 */
4793 public void addExtras(Bundle extras) {
4794 if (mSummaryTextSet) {
4795 extras.putCharSequence(EXTRA_SUMMARY_TEXT, mSummaryText);
4796 }
4797 if (mBigContentTitle != null) {
4798 extras.putCharSequence(EXTRA_TITLE_BIG, mBigContentTitle);
4799 }
Chris Wren91ad5632013-06-05 15:05:57 -04004800 extras.putString(EXTRA_TEMPLATE, this.getClass().getName());
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004801 }
4802
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04004803 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004804 * Reconstruct the internal state of this Style object from extras.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04004805 * @hide
4806 */
Christoph Studer4600f9b2014-07-22 22:44:43 +02004807 protected void restoreFromExtras(Bundle extras) {
4808 if (extras.containsKey(EXTRA_SUMMARY_TEXT)) {
4809 mSummaryText = extras.getCharSequence(EXTRA_SUMMARY_TEXT);
4810 mSummaryTextSet = true;
4811 }
4812 if (extras.containsKey(EXTRA_TITLE_BIG)) {
4813 mBigContentTitle = extras.getCharSequence(EXTRA_TITLE_BIG);
4814 }
4815 }
4816
4817
4818 /**
4819 * @hide
4820 */
4821 public Notification buildStyled(Notification wip) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004822 addExtras(wip.extras);
Christoph Studer4600f9b2014-07-22 22:44:43 +02004823 return wip;
4824 }
4825
Daniel Sandler0ec46202015-06-24 01:27:05 -04004826 /**
4827 * @hide
4828 */
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07004829 public void purgeResources() {}
4830
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04004831 /**
4832 * Calls {@link android.app.Notification.Builder#build()} on the Builder this Style is
4833 * attached to.
4834 *
4835 * @return the fully constructed Notification.
4836 */
4837 public Notification build() {
4838 checkBuilder();
4839 return mBuilder.build();
4840 }
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004841
4842 /**
4843 * @hide
4844 * @return true if the style positions the progress bar on the second line; false if the
4845 * style hides the progress bar
4846 */
4847 protected boolean hasProgress() {
4848 return true;
4849 }
Selim Cinek03d0d652015-11-13 13:18:09 -05004850
4851 /**
4852 * @hide
4853 * @return Whether we should put the summary be put into the notification header
4854 */
4855 public boolean hasSummaryInHeader() {
4856 return true;
4857 }
Selim Cinek593610c2016-02-16 18:42:57 -08004858
4859 /**
4860 * @hide
4861 * @return Whether custom content views are displayed inline in the style
4862 */
4863 public boolean displayCustomViewInline() {
4864 return false;
4865 }
Joe Onorato46439ce2010-11-19 13:56:21 -08004866 }
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004867
4868 /**
Daniel Sandler4dfbe832012-04-11 14:51:46 -04004869 * Helper class for generating large-format notifications that include a large image attachment.
Joe Malin8d40d042012-11-05 11:36:40 -08004870 *
Robert Ly91c5ce32014-06-08 15:37:00 -07004871 * Here's how you'd set the <code>BigPictureStyle</code> on a notification:
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004872 * <pre class="prettyprint">
Robert Ly91c5ce32014-06-08 15:37:00 -07004873 * Notification notif = new Notification.Builder(mContext)
4874 * .setContentTitle(&quot;New photo from &quot; + sender.toString())
4875 * .setContentText(subject)
4876 * .setSmallIcon(R.drawable.new_post)
4877 * .setLargeIcon(aBitmap)
4878 * .setStyle(new Notification.BigPictureStyle()
4879 * .bigPicture(aBigBitmap))
4880 * .build();
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004881 * </pre>
Joe Malin8d40d042012-11-05 11:36:40 -08004882 *
Daniel Sandler4dfbe832012-04-11 14:51:46 -04004883 * @see Notification#bigContentView
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004884 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004885 public static class BigPictureStyle extends Style {
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004886 private Bitmap mPicture;
Dan Sandlerd63f9322015-05-06 15:18:49 -04004887 private Icon mBigLargeIcon;
Chris Wren3745a3d2012-05-22 15:11:52 -04004888 private boolean mBigLargeIconSet = false;
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004889
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004890 public BigPictureStyle() {
4891 }
4892
Adrian Roosf5faf9d2016-05-23 13:56:15 -07004893 /**
4894 * @deprecated use {@code BigPictureStyle()}.
4895 */
4896 @Deprecated
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004897 public BigPictureStyle(Builder builder) {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004898 setBuilder(builder);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004899 }
4900
Chris Wrend6297db2012-05-03 16:20:13 -04004901 /**
4902 * Overrides ContentTitle in the big form of the template.
4903 * This defaults to the value passed to setContentTitle().
4904 */
4905 public BigPictureStyle setBigContentTitle(CharSequence title) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04004906 internalSetBigContentTitle(safeCharSequence(title));
Chris Wrend6297db2012-05-03 16:20:13 -04004907 return this;
4908 }
4909
4910 /**
4911 * Set the first line of text after the detail section in the big form of the template.
4912 */
4913 public BigPictureStyle setSummaryText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04004914 internalSetSummaryText(safeCharSequence(cs));
Chris Wrend6297db2012-05-03 16:20:13 -04004915 return this;
4916 }
4917
Chris Wren0bd664d2012-08-01 13:56:56 -04004918 /**
4919 * Provide the bitmap to be used as the payload for the BigPicture notification.
4920 */
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004921 public BigPictureStyle bigPicture(Bitmap b) {
4922 mPicture = b;
4923 return this;
4924 }
4925
Chris Wren3745a3d2012-05-22 15:11:52 -04004926 /**
Chris Wren3745a3d2012-05-22 15:11:52 -04004927 * Override the large icon when the big notification is shown.
4928 */
4929 public BigPictureStyle bigLargeIcon(Bitmap b) {
Dan Sandlerd63f9322015-05-06 15:18:49 -04004930 return bigLargeIcon(b != null ? Icon.createWithBitmap(b) : null);
4931 }
4932
4933 /**
4934 * Override the large icon when the big notification is shown.
4935 */
4936 public BigPictureStyle bigLargeIcon(Icon icon) {
Chris Wren3745a3d2012-05-22 15:11:52 -04004937 mBigLargeIconSet = true;
Dan Sandlerd63f9322015-05-06 15:18:49 -04004938 mBigLargeIcon = icon;
Chris Wren3745a3d2012-05-22 15:11:52 -04004939 return this;
4940 }
4941
Riley Andrews0394a0c2015-11-03 23:36:52 -08004942 /** @hide */
4943 public static final int MIN_ASHMEM_BITMAP_SIZE = 128 * (1 << 10);
4944
Daniel Sandler0ec46202015-06-24 01:27:05 -04004945 /**
4946 * @hide
4947 */
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07004948 @Override
4949 public void purgeResources() {
4950 super.purgeResources();
Riley Andrews8cee7c12015-11-01 23:36:04 -08004951 if (mPicture != null &&
4952 mPicture.isMutable() &&
Riley Andrews0394a0c2015-11-03 23:36:52 -08004953 mPicture.getAllocationByteCount() >= MIN_ASHMEM_BITMAP_SIZE) {
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07004954 mPicture = mPicture.createAshmemBitmap();
4955 }
4956 if (mBigLargeIcon != null) {
4957 mBigLargeIcon.convertToAshmem();
4958 }
4959 }
Christoph Studer5c510ee2014-12-15 16:32:27 +01004960
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004961 /**
4962 * @hide
4963 */
4964 public RemoteViews makeBigContentView() {
4965 // Replace mN.mLargeIcon with mBigLargeIcon if mBigLargeIconSet
Christoph Studer5c510ee2014-12-15 16:32:27 +01004966 // This covers the following cases:
4967 // 1. mBigLargeIconSet -> mBigLargeIcon (null or non-null) applies, overrides
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004968 // mN.mLargeIcon
4969 // 2. !mBigLargeIconSet -> mN.mLargeIcon applies
Dan Sandlerd63f9322015-05-06 15:18:49 -04004970 Icon oldLargeIcon = null;
Selim Cineke99acb22016-08-04 12:55:48 -07004971 Bitmap largeIconLegacy = null;
Christoph Studer5c510ee2014-12-15 16:32:27 +01004972 if (mBigLargeIconSet) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004973 oldLargeIcon = mBuilder.mN.mLargeIcon;
4974 mBuilder.mN.mLargeIcon = mBigLargeIcon;
Selim Cineke99acb22016-08-04 12:55:48 -07004975 // The legacy largeIcon might not allow us to clear the image, as it's taken in
4976 // replacement if the other one is null. Because we're restoring these legacy icons
4977 // for old listeners, this is in general non-null.
4978 largeIconLegacy = mBuilder.mN.largeIcon;
4979 mBuilder.mN.largeIcon = null;
Christoph Studer5c510ee2014-12-15 16:32:27 +01004980 }
4981
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004982 RemoteViews contentView = getStandardView(mBuilder.getBigPictureLayoutResource());
Selim Cinek03d0d652015-11-13 13:18:09 -05004983 if (mSummaryTextSet) {
4984 contentView.setTextViewText(R.id.text, mBuilder.processLegacyText(mSummaryText));
Selim Cinek7b9605b2017-01-19 17:36:00 -08004985 mBuilder.setTextViewColorSecondary(contentView, R.id.text);
Selim Cinekc848c3a2016-01-13 15:27:30 -08004986 contentView.setViewVisibility(R.id.text, View.VISIBLE);
Selim Cinek03d0d652015-11-13 13:18:09 -05004987 }
Selim Cinek279fa862016-06-14 10:57:25 -07004988 mBuilder.setContentMinHeight(contentView, mBuilder.mN.hasLargeIcon());
Selim Cinek53e64a42015-11-16 10:40:56 -08004989
Christoph Studer5c510ee2014-12-15 16:32:27 +01004990 if (mBigLargeIconSet) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004991 mBuilder.mN.mLargeIcon = oldLargeIcon;
Selim Cineke99acb22016-08-04 12:55:48 -07004992 mBuilder.mN.largeIcon = largeIconLegacy;
Christoph Studer5c510ee2014-12-15 16:32:27 +01004993 }
4994
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004995 contentView.setImageViewBitmap(R.id.big_picture, mPicture);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004996 return contentView;
4997 }
4998
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004999 /**
5000 * @hide
5001 */
5002 public void addExtras(Bundle extras) {
5003 super.addExtras(extras);
5004
5005 if (mBigLargeIconSet) {
5006 extras.putParcelable(EXTRA_LARGE_ICON_BIG, mBigLargeIcon);
5007 }
5008 extras.putParcelable(EXTRA_PICTURE, mPicture);
5009 }
5010
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04005011 /**
5012 * @hide
5013 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005014 @Override
Christoph Studer4600f9b2014-07-22 22:44:43 +02005015 protected void restoreFromExtras(Bundle extras) {
5016 super.restoreFromExtras(extras);
5017
5018 if (extras.containsKey(EXTRA_LARGE_ICON_BIG)) {
Christoph Studer5c510ee2014-12-15 16:32:27 +01005019 mBigLargeIconSet = true;
Christoph Studer4600f9b2014-07-22 22:44:43 +02005020 mBigLargeIcon = extras.getParcelable(EXTRA_LARGE_ICON_BIG);
Chris Wren3745a3d2012-05-22 15:11:52 -04005021 }
Christoph Studer4600f9b2014-07-22 22:44:43 +02005022 mPicture = extras.getParcelable(EXTRA_PICTURE);
5023 }
Selim Cinek03d0d652015-11-13 13:18:09 -05005024
5025 /**
5026 * @hide
5027 */
5028 @Override
5029 public boolean hasSummaryInHeader() {
5030 return false;
5031 }
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005032 }
5033
5034 /**
Daniel Sandler4dfbe832012-04-11 14:51:46 -04005035 * Helper class for generating large-format notifications that include a lot of text.
Joe Malin8d40d042012-11-05 11:36:40 -08005036 *
Robert Ly91c5ce32014-06-08 15:37:00 -07005037 * Here's how you'd set the <code>BigTextStyle</code> on a notification:
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005038 * <pre class="prettyprint">
Robert Ly91c5ce32014-06-08 15:37:00 -07005039 * Notification notif = new Notification.Builder(mContext)
5040 * .setContentTitle(&quot;New mail from &quot; + sender.toString())
5041 * .setContentText(subject)
5042 * .setSmallIcon(R.drawable.new_mail)
5043 * .setLargeIcon(aBitmap)
5044 * .setStyle(new Notification.BigTextStyle()
5045 * .bigText(aVeryLongString))
5046 * .build();
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005047 * </pre>
Joe Malin8d40d042012-11-05 11:36:40 -08005048 *
Daniel Sandler4dfbe832012-04-11 14:51:46 -04005049 * @see Notification#bigContentView
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005050 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005051 public static class BigTextStyle extends Style {
Jorim Jaggi457a10d2014-09-08 16:18:23 +02005052
5053 private static final int MAX_LINES = 13;
Selim Cinek3a2c4b92015-12-17 17:01:17 -08005054 private static final int LINES_CONSUMED_BY_ACTIONS = 4;
Jorim Jaggi457a10d2014-09-08 16:18:23 +02005055
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005056 private CharSequence mBigText;
5057
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005058 public BigTextStyle() {
5059 }
5060
Adrian Roosf5faf9d2016-05-23 13:56:15 -07005061 /**
5062 * @deprecated use {@code BigTextStyle()}.
5063 */
5064 @Deprecated
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005065 public BigTextStyle(Builder builder) {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005066 setBuilder(builder);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005067 }
5068
Chris Wrend6297db2012-05-03 16:20:13 -04005069 /**
5070 * Overrides ContentTitle in the big form of the template.
5071 * This defaults to the value passed to setContentTitle().
5072 */
5073 public BigTextStyle setBigContentTitle(CharSequence title) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04005074 internalSetBigContentTitle(safeCharSequence(title));
Chris Wrend6297db2012-05-03 16:20:13 -04005075 return this;
5076 }
5077
5078 /**
5079 * Set the first line of text after the detail section in the big form of the template.
5080 */
5081 public BigTextStyle setSummaryText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04005082 internalSetSummaryText(safeCharSequence(cs));
Chris Wrend6297db2012-05-03 16:20:13 -04005083 return this;
5084 }
5085
Chris Wren0bd664d2012-08-01 13:56:56 -04005086 /**
5087 * Provide the longer text to be displayed in the big form of the
5088 * template in place of the content text.
5089 */
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005090 public BigTextStyle bigText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04005091 mBigText = safeCharSequence(cs);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005092 return this;
5093 }
5094
Daniel Sandlerf45564e2013-04-15 15:05:08 -04005095 /**
5096 * @hide
5097 */
5098 public void addExtras(Bundle extras) {
5099 super.addExtras(extras);
5100
Christoph Studer4600f9b2014-07-22 22:44:43 +02005101 extras.putCharSequence(EXTRA_BIG_TEXT, mBigText);
5102 }
5103
5104 /**
5105 * @hide
5106 */
5107 @Override
5108 protected void restoreFromExtras(Bundle extras) {
5109 super.restoreFromExtras(extras);
5110
5111 mBigText = extras.getCharSequence(EXTRA_BIG_TEXT);
Daniel Sandlerf45564e2013-04-15 15:05:08 -04005112 }
5113
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005114 /**
Selim Cinek7d1009b2017-01-25 15:28:28 -08005115 * @param increasedHeight true if this layout be created with an increased height.
5116 *
5117 * @hide
5118 */
5119 @Override
5120 public RemoteViews makeContentView(boolean increasedHeight) {
5121 if (increasedHeight) {
5122 ArrayList<Action> actions = mBuilder.mActions;
5123 mBuilder.mActions = new ArrayList<>();
5124 RemoteViews remoteViews = makeBigContentView();
5125 mBuilder.mActions = actions;
5126 return remoteViews;
5127 }
5128 return super.makeContentView(increasedHeight);
5129 }
5130
5131 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005132 * @hide
5133 */
5134 public RemoteViews makeBigContentView() {
Christoph Studer4600f9b2014-07-22 22:44:43 +02005135
5136 // Nasty
Selim Cinek75998782016-04-26 10:39:17 -07005137 CharSequence text = mBuilder.getAllExtras().getCharSequence(EXTRA_TEXT);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005138 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, null);
Daniel Sandler916ad912012-06-13 12:17:07 -04005139
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01005140 RemoteViews contentView = getStandardView(mBuilder.getBigTextLayoutResource());
Joe Malin8d40d042012-11-05 11:36:40 -08005141
Selim Cinek75998782016-04-26 10:39:17 -07005142 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, text);
Christoph Studer4600f9b2014-07-22 22:44:43 +02005143
Selim Cinek3a2c4b92015-12-17 17:01:17 -08005144 CharSequence bigTextText = mBuilder.processLegacyText(mBigText);
Selim Cinek75998782016-04-26 10:39:17 -07005145 if (TextUtils.isEmpty(bigTextText)) {
5146 // In case the bigtext is null / empty fall back to the normal text to avoid a weird
5147 // experience
5148 bigTextText = mBuilder.processLegacyText(text);
5149 }
Adrian Roosb1f427c2016-05-26 12:27:15 -07005150 applyBigTextContentView(mBuilder, contentView, bigTextText);
Selim Cinek4fb12d32015-11-19 18:10:48 -08005151
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005152 return contentView;
5153 }
5154
Adrian Roosb1f427c2016-05-26 12:27:15 -07005155 static void applyBigTextContentView(Builder builder,
5156 RemoteViews contentView, CharSequence bigTextText) {
5157 contentView.setTextViewText(R.id.big_text, bigTextText);
Selim Cinek7b9605b2017-01-19 17:36:00 -08005158 builder.setTextViewColorSecondary(contentView, R.id.big_text);
Adrian Roosb1f427c2016-05-26 12:27:15 -07005159 contentView.setViewVisibility(R.id.big_text,
5160 TextUtils.isEmpty(bigTextText) ? View.GONE : View.VISIBLE);
5161 contentView.setInt(R.id.big_text, "setMaxLines", calculateMaxLines(builder));
Selim Cinek279fa862016-06-14 10:57:25 -07005162 contentView.setBoolean(R.id.big_text, "setHasImage", builder.mN.hasLargeIcon());
Adrian Roosb1f427c2016-05-26 12:27:15 -07005163 }
5164
5165 private static int calculateMaxLines(Builder builder) {
Jorim Jaggi457a10d2014-09-08 16:18:23 +02005166 int lineCount = MAX_LINES;
Adrian Roosb1f427c2016-05-26 12:27:15 -07005167 boolean hasActions = builder.mActions.size() > 0;
Jorim Jaggi457a10d2014-09-08 16:18:23 +02005168 if (hasActions) {
5169 lineCount -= LINES_CONSUMED_BY_ACTIONS;
5170 }
Jorim Jaggi457a10d2014-09-08 16:18:23 +02005171 return lineCount;
5172 }
Daniel Sandlerf3b73432012-03-27 15:01:25 -04005173 }
Daniel Sandler879c5e02012-04-17 16:46:51 -04005174
5175 /**
Alex Hillsfc737de2016-03-23 17:33:02 -04005176 * Helper class for generating large-format notifications that include multiple back-and-forth
5177 * messages of varying types between any number of people.
5178 *
5179 * <br>
5180 * If the platform does not provide large-format notifications, this method has no effect. The
5181 * user will always see the normal notification view.
5182 * <br>
5183 * This class is a "rebuilder": It attaches to a Builder object and modifies its behavior, like
5184 * so:
5185 * <pre class="prettyprint">
5186 *
5187 * Notification noti = new Notification.Builder()
5188 * .setContentTitle(&quot;2 new messages wtih &quot; + sender.toString())
5189 * .setContentText(subject)
5190 * .setSmallIcon(R.drawable.new_message)
5191 * .setLargeIcon(aBitmap)
5192 * .setStyle(new Notification.MessagingStyle(resources.getString(R.string.reply_name))
5193 * .addMessage(messages[0].getText(), messages[0].getTime(), messages[0].getSender())
5194 * .addMessage(messages[1].getText(), messages[1].getTime(), messages[1].getSender()))
5195 * .build();
5196 * </pre>
5197 */
5198 public static class MessagingStyle extends Style {
5199
5200 /**
5201 * The maximum number of messages that will be retained in the Notification itself (the
5202 * number displayed is up to the platform).
5203 */
5204 public static final int MAXIMUM_RETAINED_MESSAGES = 25;
5205
5206 CharSequence mUserDisplayName;
5207 CharSequence mConversationTitle;
Alex Hillsd9b04d92016-04-11 16:38:16 -04005208 List<Message> mMessages = new ArrayList<>();
Adrian Roos437cd562017-01-18 15:47:03 -08005209 List<Message> mHistoricMessages = new ArrayList<>();
Alex Hillsfc737de2016-03-23 17:33:02 -04005210
5211 MessagingStyle() {
5212 }
5213
5214 /**
Alex Hillsfd590442016-10-07 09:52:44 -04005215 * @param userDisplayName Required - the name to be displayed for any replies sent by the
5216 * user before the posting app reposts the notification with those messages after they've
5217 * been actually sent and in previous messages sent by the user added in
Alex Hillsfc737de2016-03-23 17:33:02 -04005218 * {@link #addMessage(Notification.MessagingStyle.Message)}
5219 */
Alex Hillsfd590442016-10-07 09:52:44 -04005220 public MessagingStyle(@NonNull CharSequence userDisplayName) {
Alex Hillsfc737de2016-03-23 17:33:02 -04005221 mUserDisplayName = userDisplayName;
5222 }
5223
5224 /**
5225 * Returns the name to be displayed for any replies sent by the user
5226 */
5227 public CharSequence getUserDisplayName() {
5228 return mUserDisplayName;
5229 }
5230
5231 /**
Alex Hillsfc737de2016-03-23 17:33:02 -04005232 * Sets the title to be displayed on this conversation. This should only be used for
5233 * group messaging and left unset for one-on-one conversations.
5234 * @param conversationTitle
5235 * @return this object for method chaining.
5236 */
5237 public MessagingStyle setConversationTitle(CharSequence conversationTitle) {
5238 mConversationTitle = conversationTitle;
5239 return this;
5240 }
5241
5242 /**
5243 * Return the title to be displayed on this conversation. Can be <code>null</code> and
5244 * should be for one-on-one conversations
5245 */
5246 public CharSequence getConversationTitle() {
5247 return mConversationTitle;
5248 }
5249
5250 /**
5251 * Adds a message for display by this notification. Convenience call for a simple
5252 * {@link Message} in {@link #addMessage(Notification.MessagingStyle.Message)}.
5253 * @param text A {@link CharSequence} to be displayed as the message content
5254 * @param timestamp Time at which the message arrived
5255 * @param sender A {@link CharSequence} to be used for displaying the name of the
5256 * sender. Should be <code>null</code> for messages by the current user, in which case
5257 * the platform will insert {@link #getUserDisplayName()}.
5258 * Should be unique amongst all individuals in the conversation, and should be
5259 * consistent during re-posts of the notification.
5260 *
5261 * @see Message#Message(CharSequence, long, CharSequence)
5262 *
5263 * @return this object for method chaining
5264 */
5265 public MessagingStyle addMessage(CharSequence text, long timestamp, CharSequence sender) {
Adrian Roos437cd562017-01-18 15:47:03 -08005266 return addMessage(new Message(text, timestamp, sender));
Alex Hillsfc737de2016-03-23 17:33:02 -04005267 }
5268
5269 /**
5270 * Adds a {@link Message} for display in this notification.
Adrian Roos437cd562017-01-18 15:47:03 -08005271 *
5272 * <p>The messages should be added in chronologic order, i.e. the oldest first,
5273 * the newest last.
5274 *
Alex Hillsfc737de2016-03-23 17:33:02 -04005275 * @param message The {@link Message} to be displayed
5276 * @return this object for method chaining
5277 */
5278 public MessagingStyle addMessage(Message message) {
5279 mMessages.add(message);
5280 if (mMessages.size() > MAXIMUM_RETAINED_MESSAGES) {
5281 mMessages.remove(0);
5282 }
5283 return this;
5284 }
5285
5286 /**
Adrian Roos437cd562017-01-18 15:47:03 -08005287 * Adds a {@link Message} for historic context in this notification.
5288 *
5289 * <p>Messages should be added as historic if they are not the main subject of the
5290 * notification but may give context to a conversation. The system may choose to present
5291 * them only when relevant, e.g. when replying to a message through a {@link RemoteInput}.
5292 *
5293 * <p>The messages should be added in chronologic order, i.e. the oldest first,
5294 * the newest last.
5295 *
5296 * @param message The historic {@link Message} to be added
5297 * @return this object for method chaining
5298 */
5299 public MessagingStyle addHistoricMessage(Message message) {
5300 mHistoricMessages.add(message);
5301 if (mHistoricMessages.size() > MAXIMUM_RETAINED_MESSAGES) {
5302 mHistoricMessages.remove(0);
5303 }
5304 return this;
5305 }
5306
5307 /**
Alex Hillsfc737de2016-03-23 17:33:02 -04005308 * Gets the list of {@code Message} objects that represent the notification
5309 */
5310 public List<Message> getMessages() {
5311 return mMessages;
5312 }
5313
5314 /**
Adrian Roos437cd562017-01-18 15:47:03 -08005315 * Gets the list of historic {@code Message}s in the notification.
5316 */
5317 public List<Message> getHistoricMessages() {
5318 return mHistoricMessages;
5319 }
5320
5321 /**
Alex Hillsfc737de2016-03-23 17:33:02 -04005322 * @hide
5323 */
5324 @Override
5325 public void addExtras(Bundle extras) {
5326 super.addExtras(extras);
5327 if (mUserDisplayName != null) {
5328 extras.putCharSequence(EXTRA_SELF_DISPLAY_NAME, mUserDisplayName);
5329 }
5330 if (mConversationTitle != null) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04005331 extras.putCharSequence(EXTRA_CONVERSATION_TITLE, mConversationTitle);
Alex Hillsfc737de2016-03-23 17:33:02 -04005332 }
Alex Hillsd9b04d92016-04-11 16:38:16 -04005333 if (!mMessages.isEmpty()) { extras.putParcelableArray(EXTRA_MESSAGES,
5334 Message.getBundleArrayForMessages(mMessages));
Alex Hillsfc737de2016-03-23 17:33:02 -04005335 }
Adrian Roos437cd562017-01-18 15:47:03 -08005336 if (!mHistoricMessages.isEmpty()) { extras.putParcelableArray(EXTRA_HISTORIC_MESSAGES,
5337 Message.getBundleArrayForMessages(mHistoricMessages));
5338 }
Adrian Roos33fbd2c2016-05-27 15:35:28 -07005339
5340 fixTitleAndTextExtras(extras);
5341 }
5342
5343 private void fixTitleAndTextExtras(Bundle extras) {
5344 Message m = findLatestIncomingMessage();
5345 CharSequence text = (m == null) ? null : m.mText;
5346 CharSequence sender = m == null ? null
5347 : TextUtils.isEmpty(m.mSender) ? mUserDisplayName : m.mSender;
5348 CharSequence title;
5349 if (!TextUtils.isEmpty(mConversationTitle)) {
5350 if (!TextUtils.isEmpty(sender)) {
5351 BidiFormatter bidi = BidiFormatter.getInstance();
5352 title = mBuilder.mContext.getString(
5353 com.android.internal.R.string.notification_messaging_title_template,
5354 bidi.unicodeWrap(mConversationTitle), bidi.unicodeWrap(m.mSender));
5355 } else {
5356 title = mConversationTitle;
5357 }
5358 } else {
5359 title = sender;
5360 }
5361
5362 if (title != null) {
5363 extras.putCharSequence(EXTRA_TITLE, title);
5364 }
5365 if (text != null) {
5366 extras.putCharSequence(EXTRA_TEXT, text);
5367 }
Alex Hillsfc737de2016-03-23 17:33:02 -04005368 }
5369
5370 /**
5371 * @hide
5372 */
5373 @Override
5374 protected void restoreFromExtras(Bundle extras) {
5375 super.restoreFromExtras(extras);
5376
5377 mMessages.clear();
Adrian Roos437cd562017-01-18 15:47:03 -08005378 mHistoricMessages.clear();
Adrian Roos96b7e202016-05-17 13:50:38 -07005379 mUserDisplayName = extras.getCharSequence(EXTRA_SELF_DISPLAY_NAME);
5380 mConversationTitle = extras.getCharSequence(EXTRA_CONVERSATION_TITLE);
Adrian Roos437cd562017-01-18 15:47:03 -08005381 Parcelable[] messages = extras.getParcelableArray(EXTRA_MESSAGES);
5382 if (messages != null && messages instanceof Parcelable[]) {
5383 mMessages = Message.getMessagesFromBundleArray(messages);
5384 }
5385 Parcelable[] histMessages = extras.getParcelableArray(EXTRA_HISTORIC_MESSAGES);
5386 if (histMessages != null && histMessages instanceof Parcelable[]) {
5387 mHistoricMessages = Message.getMessagesFromBundleArray(histMessages);
Alex Hillsfc737de2016-03-23 17:33:02 -04005388 }
5389 }
5390
5391 /**
5392 * @hide
5393 */
Adrian Roosc1a80b02016-04-05 14:54:55 -07005394 @Override
Selim Cinek7d1009b2017-01-25 15:28:28 -08005395 public RemoteViews makeContentView(boolean increasedHeight) {
5396 if (!increasedHeight) {
5397 Message m = findLatestIncomingMessage();
5398 CharSequence title = mConversationTitle != null
5399 ? mConversationTitle
5400 : (m == null) ? null : m.mSender;
5401 CharSequence text = (m == null)
5402 ? null
5403 : mConversationTitle != null ? makeMessageLine(m, mBuilder) : m.mText;
Alex Hillsfc737de2016-03-23 17:33:02 -04005404
Selim Cinek7d1009b2017-01-25 15:28:28 -08005405 return mBuilder.applyStandardTemplate(mBuilder.getBaseLayoutResource(),
5406 mBuilder.mParams.reset().hasProgress(false).title(title).text(text));
5407 } else {
5408 ArrayList<Action> actions = mBuilder.mActions;
5409 mBuilder.mActions = new ArrayList<>();
5410 RemoteViews remoteViews = makeBigContentView();
5411 mBuilder.mActions = actions;
5412 return remoteViews;
5413 }
Adrian Roosc1a80b02016-04-05 14:54:55 -07005414 }
5415
5416 private Message findLatestIncomingMessage() {
5417 for (int i = mMessages.size() - 1; i >= 0; i--) {
5418 Message m = mMessages.get(i);
5419 // Incoming messages have a non-empty sender.
5420 if (!TextUtils.isEmpty(m.mSender)) {
5421 return m;
5422 }
5423 }
Adrian Roos33fbd2c2016-05-27 15:35:28 -07005424 if (!mMessages.isEmpty()) {
5425 // No incoming messages, fall back to outgoing message
5426 return mMessages.get(mMessages.size() - 1);
5427 }
Adrian Roosc1a80b02016-04-05 14:54:55 -07005428 return null;
5429 }
5430
5431 /**
5432 * @hide
5433 */
5434 @Override
5435 public RemoteViews makeBigContentView() {
5436 CharSequence title = !TextUtils.isEmpty(super.mBigContentTitle)
5437 ? super.mBigContentTitle
5438 : mConversationTitle;
5439 boolean hasTitle = !TextUtils.isEmpty(title);
5440
Adrian Roosfeafa052016-06-01 17:09:45 -07005441 if (mMessages.size() == 1) {
5442 // Special case for a single message: Use the big text style
5443 // so the collapsed and expanded versions match nicely.
5444 CharSequence bigTitle;
5445 CharSequence text;
5446 if (hasTitle) {
5447 bigTitle = title;
Selim Cinek7b9605b2017-01-19 17:36:00 -08005448 text = makeMessageLine(mMessages.get(0), mBuilder);
Adrian Roosfeafa052016-06-01 17:09:45 -07005449 } else {
5450 bigTitle = mMessages.get(0).mSender;
5451 text = mMessages.get(0).mText;
5452 }
Adrian Roosb1f427c2016-05-26 12:27:15 -07005453 RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(
5454 mBuilder.getBigTextLayoutResource(),
Adrian Roos70d7aa32017-01-11 15:39:06 -08005455 mBuilder.mParams.reset().hasProgress(false).title(bigTitle).text(null));
Adrian Roosb1f427c2016-05-26 12:27:15 -07005456 BigTextStyle.applyBigTextContentView(mBuilder, contentView, text);
5457 return contentView;
5458 }
5459
Adrian Roos48d746a2016-04-12 14:57:28 -07005460 RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(
Adrian Roosc1a80b02016-04-05 14:54:55 -07005461 mBuilder.getMessagingLayoutResource(),
Adrian Roos70d7aa32017-01-11 15:39:06 -08005462 mBuilder.mParams.reset().hasProgress(false).title(title).text(null));
Adrian Roosc1a80b02016-04-05 14:54:55 -07005463
5464 int[] rowIds = {R.id.inbox_text0, R.id.inbox_text1, R.id.inbox_text2, R.id.inbox_text3,
5465 R.id.inbox_text4, R.id.inbox_text5, R.id.inbox_text6};
5466
5467 // Make sure all rows are gone in case we reuse a view.
5468 for (int rowId : rowIds) {
5469 contentView.setViewVisibility(rowId, View.GONE);
5470 }
5471
5472 int i=0;
Adrian Roos2d5dbba2016-06-08 17:11:53 -07005473 contentView.setViewLayoutMarginBottomDimen(R.id.line1,
5474 hasTitle ? R.dimen.notification_messaging_spacing : 0);
Adrian Roosc1a80b02016-04-05 14:54:55 -07005475 contentView.setInt(R.id.notification_messaging, "setNumIndentLines",
Selim Cinek279fa862016-06-14 10:57:25 -07005476 !mBuilder.mN.hasLargeIcon() ? 0 : (hasTitle ? 1 : 2));
Adrian Roosc1a80b02016-04-05 14:54:55 -07005477
Adrian Roosfeafa052016-06-01 17:09:45 -07005478 int contractedChildId = View.NO_ID;
5479 Message contractedMessage = findLatestIncomingMessage();
Adrian Roos437cd562017-01-18 15:47:03 -08005480 int firstHistoricMessage = Math.max(0, mHistoricMessages.size()
5481 - (rowIds.length - mMessages.size()));
5482 while (firstHistoricMessage + i < mHistoricMessages.size() && i < rowIds.length) {
5483 Message m = mHistoricMessages.get(firstHistoricMessage + i);
5484 int rowId = rowIds[i];
5485
Selim Cinek7b9605b2017-01-19 17:36:00 -08005486 contentView.setTextViewText(rowId, makeMessageLine(m, mBuilder));
Adrian Roos437cd562017-01-18 15:47:03 -08005487
5488 if (contractedMessage == m) {
5489 contractedChildId = rowId;
5490 }
5491
5492 i++;
5493 }
5494
Adrian Roosc1a80b02016-04-05 14:54:55 -07005495 int firstMessage = Math.max(0, mMessages.size() - rowIds.length);
5496 while (firstMessage + i < mMessages.size() && i < rowIds.length) {
5497 Message m = mMessages.get(firstMessage + i);
5498 int rowId = rowIds[i];
5499
5500 contentView.setViewVisibility(rowId, View.VISIBLE);
Selim Cinek7b9605b2017-01-19 17:36:00 -08005501 contentView.setTextViewText(rowId, makeMessageLine(m, mBuilder));
5502 mBuilder.setTextViewColorSecondary(contentView, rowId);
Adrian Roosc1a80b02016-04-05 14:54:55 -07005503
Adrian Roosfeafa052016-06-01 17:09:45 -07005504 if (contractedMessage == m) {
5505 contractedChildId = rowId;
5506 }
5507
Adrian Roosc1a80b02016-04-05 14:54:55 -07005508 i++;
5509 }
Adrian Roos437cd562017-01-18 15:47:03 -08005510 // Clear the remaining views for reapply. Ensures that historic message views can
5511 // reliably be identified as being GONE and having non-null text.
5512 while (i < rowIds.length) {
5513 int rowId = rowIds[i];
5514 contentView.setTextViewText(rowId, null);
5515 i++;
5516 }
5517
Adrian Roosfeafa052016-06-01 17:09:45 -07005518 // Record this here to allow transformation between the contracted and expanded views.
5519 contentView.setInt(R.id.notification_messaging, "setContractedChildId",
5520 contractedChildId);
Alex Hillsfc737de2016-03-23 17:33:02 -04005521 return contentView;
5522 }
5523
Selim Cinek7b9605b2017-01-19 17:36:00 -08005524 private CharSequence makeMessageLine(Message m, Builder builder) {
Adrian Roosc1a80b02016-04-05 14:54:55 -07005525 BidiFormatter bidi = BidiFormatter.getInstance();
5526 SpannableStringBuilder sb = new SpannableStringBuilder();
Selim Cinek7b9605b2017-01-19 17:36:00 -08005527 boolean colorize = builder.isColorized();
Adrian Roosc1a80b02016-04-05 14:54:55 -07005528 if (TextUtils.isEmpty(m.mSender)) {
5529 CharSequence replyName = mUserDisplayName == null ? "" : mUserDisplayName;
5530 sb.append(bidi.unicodeWrap(replyName),
Selim Cinek7b9605b2017-01-19 17:36:00 -08005531 makeFontColorSpan(colorize
5532 ? builder.getPrimaryTextColor()
5533 : mBuilder.resolveContrastColor()),
Adrian Roosc1a80b02016-04-05 14:54:55 -07005534 0 /* flags */);
5535 } else {
5536 sb.append(bidi.unicodeWrap(m.mSender),
Selim Cinek7b9605b2017-01-19 17:36:00 -08005537 makeFontColorSpan(colorize
5538 ? builder.getPrimaryTextColor()
5539 : Color.BLACK),
Adrian Roosc1a80b02016-04-05 14:54:55 -07005540 0 /* flags */);
5541 }
5542 CharSequence text = m.mText == null ? "" : m.mText;
5543 sb.append(" ").append(bidi.unicodeWrap(text));
5544 return sb;
5545 }
5546
Adrian Roosdedd1df2016-04-26 16:38:47 -07005547 /**
5548 * @hide
5549 */
5550 @Override
5551 public RemoteViews makeHeadsUpContentView() {
5552 Message m = findLatestIncomingMessage();
5553 CharSequence title = mConversationTitle != null
5554 ? mConversationTitle
5555 : (m == null) ? null : m.mSender;
5556 CharSequence text = (m == null)
5557 ? null
Selim Cinek7b9605b2017-01-19 17:36:00 -08005558 : mConversationTitle != null ? makeMessageLine(m, mBuilder) : m.mText;
Adrian Roosdedd1df2016-04-26 16:38:47 -07005559
5560 return mBuilder.applyStandardTemplateWithActions(mBuilder.getBigBaseLayoutResource(),
Adrian Roos70d7aa32017-01-11 15:39:06 -08005561 mBuilder.mParams.reset().hasProgress(false).title(title).text(text));
Adrian Roosdedd1df2016-04-26 16:38:47 -07005562 }
5563
Adrian Roosc1a80b02016-04-05 14:54:55 -07005564 private static TextAppearanceSpan makeFontColorSpan(int color) {
5565 return new TextAppearanceSpan(null, 0, 0,
5566 ColorStateList.valueOf(color), null);
5567 }
5568
Alex Hillsd9b04d92016-04-11 16:38:16 -04005569 public static final class Message {
5570
5571 static final String KEY_TEXT = "text";
5572 static final String KEY_TIMESTAMP = "time";
5573 static final String KEY_SENDER = "sender";
5574 static final String KEY_DATA_MIME_TYPE = "type";
5575 static final String KEY_DATA_URI= "uri";
Alex Hillsfc737de2016-03-23 17:33:02 -04005576
5577 private final CharSequence mText;
5578 private final long mTimestamp;
5579 private final CharSequence mSender;
5580
5581 private String mDataMimeType;
5582 private Uri mDataUri;
5583
5584 /**
5585 * Constructor
5586 * @param text A {@link CharSequence} to be displayed as the message content
5587 * @param timestamp Time at which the message arrived
5588 * @param sender A {@link CharSequence} to be used for displaying the name of the
5589 * sender. Should be <code>null</code> for messages by the current user, in which case
5590 * the platform will insert {@link MessagingStyle#getUserDisplayName()}.
5591 * Should be unique amongst all individuals in the conversation, and should be
5592 * consistent during re-posts of the notification.
5593 */
5594 public Message(CharSequence text, long timestamp, CharSequence sender){
5595 mText = text;
5596 mTimestamp = timestamp;
5597 mSender = sender;
5598 }
5599
5600 /**
5601 * Sets a binary blob of data and an associated MIME type for a message. In the case
5602 * where the platform doesn't support the MIME type, the original text provided in the
5603 * constructor will be used.
5604 * @param dataMimeType The MIME type of the content. See
5605 * <a href="{@docRoot}notifications/messaging.html"> for the list of supported MIME
5606 * types on Android and Android Wear.
5607 * @param dataUri The uri containing the content whose type is given by the MIME type.
5608 * <p class="note">
5609 * <ol>
5610 * <li>Notification Listeners including the System UI need permission to access the
5611 * data the Uri points to. The recommended ways to do this are:</li>
5612 * <li>Store the data in your own ContentProvider, making sure that other apps have
5613 * the correct permission to access your provider. The preferred mechanism for
5614 * providing access is to use per-URI permissions which are temporary and only
5615 * grant access to the receiving application. An easy way to create a
5616 * ContentProvider like this is to use the FileProvider helper class.</li>
5617 * <li>Use the system MediaStore. The MediaStore is primarily aimed at video, audio
5618 * and image MIME types, however beginning with Android 3.0 (API level 11) it can
5619 * also store non-media types (see MediaStore.Files for more info). Files can be
5620 * inserted into the MediaStore using scanFile() after which a content:// style
5621 * Uri suitable for sharing is passed to the provided onScanCompleted() callback.
5622 * Note that once added to the system MediaStore the content is accessible to any
5623 * app on the device.</li>
5624 * </ol>
5625 * @return this object for method chaining
5626 */
5627 public Message setData(String dataMimeType, Uri dataUri) {
5628 mDataMimeType = dataMimeType;
5629 mDataUri = dataUri;
5630 return this;
5631 }
5632
Alex Hillsfc737de2016-03-23 17:33:02 -04005633 /**
5634 * Get the text to be used for this message, or the fallback text if a type and content
5635 * Uri have been set
5636 */
5637 public CharSequence getText() {
5638 return mText;
5639 }
5640
5641 /**
5642 * Get the time at which this message arrived
5643 */
5644 public long getTimestamp() {
5645 return mTimestamp;
5646 }
5647
5648 /**
5649 * Get the text used to display the contact's name in the messaging experience
5650 */
5651 public CharSequence getSender() {
5652 return mSender;
5653 }
5654
5655 /**
5656 * Get the MIME type of the data pointed to by the Uri
5657 */
5658 public String getDataMimeType() {
5659 return mDataMimeType;
5660 }
5661
5662 /**
5663 * Get the the Uri pointing to the content of the message. Can be null, in which case
5664 * {@see #getText()} is used.
5665 */
5666 public Uri getDataUri() {
5667 return mDataUri;
5668 }
5669
Alex Hillsd9b04d92016-04-11 16:38:16 -04005670 private Bundle toBundle() {
5671 Bundle bundle = new Bundle();
Alex Hillsfc737de2016-03-23 17:33:02 -04005672 if (mText != null) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04005673 bundle.putCharSequence(KEY_TEXT, mText);
Alex Hillsfc737de2016-03-23 17:33:02 -04005674 }
Alex Hillsd9b04d92016-04-11 16:38:16 -04005675 bundle.putLong(KEY_TIMESTAMP, mTimestamp);
Alex Hillsfc737de2016-03-23 17:33:02 -04005676 if (mSender != null) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04005677 bundle.putCharSequence(KEY_SENDER, mSender);
Alex Hillsfc737de2016-03-23 17:33:02 -04005678 }
5679 if (mDataMimeType != null) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04005680 bundle.putString(KEY_DATA_MIME_TYPE, mDataMimeType);
Alex Hillsfc737de2016-03-23 17:33:02 -04005681 }
5682 if (mDataUri != null) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04005683 bundle.putParcelable(KEY_DATA_URI, mDataUri);
Alex Hillsfc737de2016-03-23 17:33:02 -04005684 }
Alex Hillsd9b04d92016-04-11 16:38:16 -04005685 return bundle;
Alex Hillsfc737de2016-03-23 17:33:02 -04005686 }
5687
Alex Hillsd9b04d92016-04-11 16:38:16 -04005688 static Bundle[] getBundleArrayForMessages(List<Message> messages) {
5689 Bundle[] bundles = new Bundle[messages.size()];
5690 final int N = messages.size();
5691 for (int i = 0; i < N; i++) {
5692 bundles[i] = messages.get(i).toBundle();
5693 }
5694 return bundles;
5695 }
5696
Adrian Roosdedd1df2016-04-26 16:38:47 -07005697 static List<Message> getMessagesFromBundleArray(Parcelable[] bundles) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04005698 List<Message> messages = new ArrayList<>(bundles.length);
5699 for (int i = 0; i < bundles.length; i++) {
Adrian Roosdedd1df2016-04-26 16:38:47 -07005700 if (bundles[i] instanceof Bundle) {
5701 Message message = getMessageFromBundle((Bundle)bundles[i]);
5702 if (message != null) {
5703 messages.add(message);
5704 }
Alex Hillsd9b04d92016-04-11 16:38:16 -04005705 }
5706 }
5707 return messages;
5708 }
5709
5710 static Message getMessageFromBundle(Bundle bundle) {
5711 try {
Adrian Roosfbddd2c2016-05-13 12:57:20 -07005712 if (!bundle.containsKey(KEY_TEXT) || !bundle.containsKey(KEY_TIMESTAMP)) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04005713 return null;
5714 } else {
5715 Message message = new Message(bundle.getCharSequence(KEY_TEXT),
5716 bundle.getLong(KEY_TIMESTAMP), bundle.getCharSequence(KEY_SENDER));
5717 if (bundle.containsKey(KEY_DATA_MIME_TYPE) &&
5718 bundle.containsKey(KEY_DATA_URI)) {
5719
5720 message.setData(bundle.getString(KEY_DATA_MIME_TYPE),
5721 (Uri) bundle.getParcelable(KEY_DATA_URI));
Alex Hillsfc737de2016-03-23 17:33:02 -04005722 }
Alex Hillsd9b04d92016-04-11 16:38:16 -04005723 return message;
5724 }
5725 } catch (ClassCastException e) {
5726 return null;
5727 }
5728 }
Alex Hillsfc737de2016-03-23 17:33:02 -04005729 }
5730 }
5731
5732 /**
Daniel Sandler879c5e02012-04-17 16:46:51 -04005733 * Helper class for generating large-format notifications that include a list of (up to 5) strings.
Joe Malin8d40d042012-11-05 11:36:40 -08005734 *
Robert Ly91c5ce32014-06-08 15:37:00 -07005735 * Here's how you'd set the <code>InboxStyle</code> on a notification:
Daniel Sandler879c5e02012-04-17 16:46:51 -04005736 * <pre class="prettyprint">
Robert Ly91c5ce32014-06-08 15:37:00 -07005737 * Notification notif = new Notification.Builder(mContext)
5738 * .setContentTitle(&quot;5 New mails from &quot; + sender.toString())
5739 * .setContentText(subject)
5740 * .setSmallIcon(R.drawable.new_mail)
5741 * .setLargeIcon(aBitmap)
5742 * .setStyle(new Notification.InboxStyle()
5743 * .addLine(str1)
5744 * .addLine(str2)
5745 * .setContentTitle(&quot;&quot;)
5746 * .setSummaryText(&quot;+3 more&quot;))
5747 * .build();
Daniel Sandler879c5e02012-04-17 16:46:51 -04005748 * </pre>
Joe Malin8d40d042012-11-05 11:36:40 -08005749 *
Daniel Sandler879c5e02012-04-17 16:46:51 -04005750 * @see Notification#bigContentView
5751 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005752 public static class InboxStyle extends Style {
Daniel Sandler879c5e02012-04-17 16:46:51 -04005753 private ArrayList<CharSequence> mTexts = new ArrayList<CharSequence>(5);
5754
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005755 public InboxStyle() {
5756 }
5757
Adrian Roosf5faf9d2016-05-23 13:56:15 -07005758 /**
5759 * @deprecated use {@code InboxStyle()}.
5760 */
5761 @Deprecated
Daniel Sandler879c5e02012-04-17 16:46:51 -04005762 public InboxStyle(Builder builder) {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005763 setBuilder(builder);
Daniel Sandler879c5e02012-04-17 16:46:51 -04005764 }
5765
Chris Wrend6297db2012-05-03 16:20:13 -04005766 /**
5767 * Overrides ContentTitle in the big form of the template.
5768 * This defaults to the value passed to setContentTitle().
5769 */
5770 public InboxStyle setBigContentTitle(CharSequence title) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04005771 internalSetBigContentTitle(safeCharSequence(title));
Chris Wrend6297db2012-05-03 16:20:13 -04005772 return this;
5773 }
5774
5775 /**
5776 * Set the first line of text after the detail section in the big form of the template.
5777 */
5778 public InboxStyle setSummaryText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04005779 internalSetSummaryText(safeCharSequence(cs));
Chris Wrend6297db2012-05-03 16:20:13 -04005780 return this;
5781 }
5782
Chris Wren0bd664d2012-08-01 13:56:56 -04005783 /**
5784 * Append a line to the digest section of the Inbox notification.
5785 */
Daniel Sandler879c5e02012-04-17 16:46:51 -04005786 public InboxStyle addLine(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04005787 mTexts.add(safeCharSequence(cs));
Daniel Sandler879c5e02012-04-17 16:46:51 -04005788 return this;
5789 }
5790
Daniel Sandlerf45564e2013-04-15 15:05:08 -04005791 /**
5792 * @hide
5793 */
5794 public void addExtras(Bundle extras) {
5795 super.addExtras(extras);
Christoph Studer4600f9b2014-07-22 22:44:43 +02005796
Daniel Sandlerf45564e2013-04-15 15:05:08 -04005797 CharSequence[] a = new CharSequence[mTexts.size()];
5798 extras.putCharSequenceArray(EXTRA_TEXT_LINES, mTexts.toArray(a));
5799 }
5800
Christoph Studer4600f9b2014-07-22 22:44:43 +02005801 /**
5802 * @hide
5803 */
5804 @Override
5805 protected void restoreFromExtras(Bundle extras) {
5806 super.restoreFromExtras(extras);
5807
5808 mTexts.clear();
5809 if (extras.containsKey(EXTRA_TEXT_LINES)) {
5810 Collections.addAll(mTexts, extras.getCharSequenceArray(EXTRA_TEXT_LINES));
5811 }
5812 }
5813
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005814 /**
5815 * @hide
5816 */
5817 public RemoteViews makeBigContentView() {
Selim Cinekc848c3a2016-01-13 15:27:30 -08005818 // Remove the content text so it disappears unless you have a summary
Christoph Studer4600f9b2014-07-22 22:44:43 +02005819 // Nasty
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005820 CharSequence oldBuilderContentText = mBuilder.mN.extras.getCharSequence(EXTRA_TEXT);
5821 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, null);
Christoph Studer4600f9b2014-07-22 22:44:43 +02005822
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01005823 RemoteViews contentView = getStandardView(mBuilder.getInboxLayoutResource());
Daniel Sandler619738c2012-06-07 16:33:08 -04005824
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005825 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, oldBuilderContentText);
Christoph Studer4600f9b2014-07-22 22:44:43 +02005826
Chris Wrend6297db2012-05-03 16:20:13 -04005827 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 -04005828 R.id.inbox_text4, R.id.inbox_text5, R.id.inbox_text6};
Chris Wrend6297db2012-05-03 16:20:13 -04005829
Chris Wren4ed80d52012-05-17 09:30:03 -04005830 // Make sure all rows are gone in case we reuse a view.
5831 for (int rowId : rowIds) {
5832 contentView.setViewVisibility(rowId, View.GONE);
5833 }
5834
Daniel Sandler879c5e02012-04-17 16:46:51 -04005835 int i=0;
Selim Cinek07c80172016-04-21 16:40:47 -07005836 int topPadding = mBuilder.mContext.getResources().getDimensionPixelSize(
5837 R.dimen.notification_inbox_item_top_padding);
Selim Cinek247fa012016-02-18 09:50:48 -08005838 boolean first = true;
Selim Cinek07c80172016-04-21 16:40:47 -07005839 int onlyViewId = 0;
5840 int maxRows = rowIds.length;
5841 if (mBuilder.mActions.size() > 0) {
5842 maxRows--;
5843 }
5844 while (i < mTexts.size() && i < maxRows) {
Daniel Sandler879c5e02012-04-17 16:46:51 -04005845 CharSequence str = mTexts.get(i);
Selim Cinek07c80172016-04-21 16:40:47 -07005846 if (!TextUtils.isEmpty(str)) {
Daniel Sandler879c5e02012-04-17 16:46:51 -04005847 contentView.setViewVisibility(rowIds[i], View.VISIBLE);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01005848 contentView.setTextViewText(rowIds[i], mBuilder.processLegacyText(str));
Selim Cinek7b9605b2017-01-19 17:36:00 -08005849 mBuilder.setTextViewColorSecondary(contentView, rowIds[i]);
Selim Cinek07c80172016-04-21 16:40:47 -07005850 contentView.setViewPadding(rowIds[i], 0, topPadding, 0, 0);
Selim Cinek247fa012016-02-18 09:50:48 -08005851 handleInboxImageMargin(contentView, rowIds[i], first);
Selim Cinek07c80172016-04-21 16:40:47 -07005852 if (first) {
5853 onlyViewId = rowIds[i];
5854 } else {
5855 onlyViewId = 0;
5856 }
Selim Cinek247fa012016-02-18 09:50:48 -08005857 first = false;
Daniel Sandler879c5e02012-04-17 16:46:51 -04005858 }
5859 i++;
5860 }
Selim Cinek07c80172016-04-21 16:40:47 -07005861 if (onlyViewId != 0) {
5862 // We only have 1 entry, lets make it look like the normal Text of a Bigtext
5863 topPadding = mBuilder.mContext.getResources().getDimensionPixelSize(
5864 R.dimen.notification_text_margin_top);
5865 contentView.setViewPadding(onlyViewId, 0, topPadding, 0, 0);
5866 }
Selim Cinek1e0bf612015-11-20 15:57:26 -08005867
Daniel Sandler879c5e02012-04-17 16:46:51 -04005868 return contentView;
5869 }
Selim Cinek1e0bf612015-11-20 15:57:26 -08005870
Selim Cinek247fa012016-02-18 09:50:48 -08005871 private void handleInboxImageMargin(RemoteViews contentView, int id, boolean first) {
Selim Cinek1e0bf612015-11-20 15:57:26 -08005872 int endMargin = 0;
Selim Cinek247fa012016-02-18 09:50:48 -08005873 if (first) {
5874 final int max = mBuilder.mN.extras.getInt(EXTRA_PROGRESS_MAX, 0);
5875 final boolean ind = mBuilder.mN.extras.getBoolean(EXTRA_PROGRESS_INDETERMINATE);
5876 boolean hasProgress = max != 0 || ind;
Selim Cinek279fa862016-06-14 10:57:25 -07005877 if (mBuilder.mN.hasLargeIcon() && !hasProgress) {
Adrian Roos2d5dbba2016-06-08 17:11:53 -07005878 endMargin = R.dimen.notification_content_picture_margin;
Selim Cinek247fa012016-02-18 09:50:48 -08005879 }
Selim Cinek1e0bf612015-11-20 15:57:26 -08005880 }
Adrian Roos2d5dbba2016-06-08 17:11:53 -07005881 contentView.setViewLayoutMarginEndDimen(id, endMargin);
Selim Cinek1e0bf612015-11-20 15:57:26 -08005882 }
Daniel Sandler879c5e02012-04-17 16:46:51 -04005883 }
Dan Sandler842dd772014-05-15 09:36:47 -04005884
5885 /**
5886 * Notification style for media playback notifications.
5887 *
5888 * In the expanded form, {@link Notification#bigContentView}, up to 5
5889 * {@link Notification.Action}s specified with
Dan Sandler86647982015-05-13 23:41:13 -04005890 * {@link Notification.Builder#addAction(Action) addAction} will be
Dan Sandler842dd772014-05-15 09:36:47 -04005891 * shown as icon-only pushbuttons, suitable for transport controls. The Bitmap given to
5892 * {@link Notification.Builder#setLargeIcon(android.graphics.Bitmap) setLargeIcon()} will be
5893 * treated as album artwork.
5894 *
5895 * Unlike the other styles provided here, MediaStyle can also modify the standard-size
5896 * {@link Notification#contentView}; by providing action indices to
Christoph Studerfde6f4d2014-12-12 13:23:26 +01005897 * {@link #setShowActionsInCompactView(int...)} you can promote up to 3 actions to be displayed
Dan Sandler842dd772014-05-15 09:36:47 -04005898 * in the standard view alongside the usual content.
5899 *
Bryan Mawhinney6be8de32014-07-18 10:35:12 +01005900 * Notifications created with MediaStyle will have their category set to
5901 * {@link Notification#CATEGORY_TRANSPORT CATEGORY_TRANSPORT} unless you set a different
5902 * category using {@link Notification.Builder#setCategory(String) setCategory()}.
5903 *
Jeff Browndba34ba2014-06-24 20:46:03 -07005904 * Finally, if you attach a {@link android.media.session.MediaSession.Token} using
5905 * {@link android.app.Notification.MediaStyle#setMediaSession(MediaSession.Token)},
Dan Sandler842dd772014-05-15 09:36:47 -04005906 * the System UI can identify this as a notification representing an active media session
5907 * and respond accordingly (by showing album artwork in the lockscreen, for example).
5908 *
5909 * To use this style with your Notification, feed it to
5910 * {@link Notification.Builder#setStyle(android.app.Notification.Style)} like so:
5911 * <pre class="prettyprint">
5912 * Notification noti = new Notification.Builder()
5913 * .setSmallIcon(R.drawable.ic_stat_player)
Christoph Studere935fe92014-11-24 14:18:06 +01005914 * .setContentTitle(&quot;Track title&quot;)
5915 * .setContentText(&quot;Artist - Album&quot;)
5916 * .setLargeIcon(albumArtBitmap))
Bryan Mawhinney6be8de32014-07-18 10:35:12 +01005917 * .setStyle(<b>new Notification.MediaStyle()</b>
5918 * .setMediaSession(mySession))
Dan Sandler842dd772014-05-15 09:36:47 -04005919 * .build();
5920 * </pre>
5921 *
5922 * @see Notification#bigContentView
5923 */
5924 public static class MediaStyle extends Style {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02005925 static final int MAX_MEDIA_BUTTONS_IN_COMPACT = 3;
Dan Sandler842dd772014-05-15 09:36:47 -04005926 static final int MAX_MEDIA_BUTTONS = 5;
5927
5928 private int[] mActionsToShowInCompact = null;
Jeff Browndba34ba2014-06-24 20:46:03 -07005929 private MediaSession.Token mToken;
Dan Sandler842dd772014-05-15 09:36:47 -04005930
5931 public MediaStyle() {
5932 }
5933
Adrian Roosf5faf9d2016-05-23 13:56:15 -07005934 /**
5935 * @deprecated use {@code MediaStyle()}.
5936 */
5937 @Deprecated
Dan Sandler842dd772014-05-15 09:36:47 -04005938 public MediaStyle(Builder builder) {
5939 setBuilder(builder);
5940 }
5941
5942 /**
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02005943 * 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 -04005944 * notification view.
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02005945 *
5946 * @param actions the indices of the actions to show in the compact notification view
Dan Sandler842dd772014-05-15 09:36:47 -04005947 */
5948 public MediaStyle setShowActionsInCompactView(int...actions) {
5949 mActionsToShowInCompact = actions;
5950 return this;
5951 }
5952
5953 /**
Jeff Browndba34ba2014-06-24 20:46:03 -07005954 * Attach a {@link android.media.session.MediaSession.Token} to this Notification
5955 * to provide additional playback information and control to the SystemUI.
Dan Sandler842dd772014-05-15 09:36:47 -04005956 */
Jeff Browndba34ba2014-06-24 20:46:03 -07005957 public MediaStyle setMediaSession(MediaSession.Token token) {
Dan Sandler842dd772014-05-15 09:36:47 -04005958 mToken = token;
5959 return this;
5960 }
5961
Christoph Studer4600f9b2014-07-22 22:44:43 +02005962 /**
5963 * @hide
5964 */
Dan Sandler842dd772014-05-15 09:36:47 -04005965 @Override
5966 public Notification buildStyled(Notification wip) {
Christoph Studer4600f9b2014-07-22 22:44:43 +02005967 super.buildStyled(wip);
Bryan Mawhinney6be8de32014-07-18 10:35:12 +01005968 if (wip.category == null) {
5969 wip.category = Notification.CATEGORY_TRANSPORT;
5970 }
Dan Sandler842dd772014-05-15 09:36:47 -04005971 return wip;
5972 }
5973
Christoph Studer4600f9b2014-07-22 22:44:43 +02005974 /**
5975 * @hide
5976 */
5977 @Override
Selim Cinek7d1009b2017-01-25 15:28:28 -08005978 public RemoteViews makeContentView(boolean increasedHeight) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005979 return makeMediaContentView();
Christoph Studer4600f9b2014-07-22 22:44:43 +02005980 }
5981
5982 /**
5983 * @hide
5984 */
5985 @Override
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005986 public RemoteViews makeBigContentView() {
5987 return makeMediaBigContentView();
Christoph Studer4600f9b2014-07-22 22:44:43 +02005988 }
5989
Selim Cinekcc10bfb2016-02-10 16:24:21 -08005990 /**
5991 * @hide
5992 */
5993 @Override
5994 public RemoteViews makeHeadsUpContentView() {
5995 RemoteViews expanded = makeMediaBigContentView();
5996 return expanded != null ? expanded : makeMediaContentView();
5997 }
5998
Dan Sandler842dd772014-05-15 09:36:47 -04005999 /** @hide */
6000 @Override
6001 public void addExtras(Bundle extras) {
6002 super.addExtras(extras);
6003
6004 if (mToken != null) {
6005 extras.putParcelable(EXTRA_MEDIA_SESSION, mToken);
6006 }
Bryan Mawhinneye191f902014-07-22 12:50:09 +01006007 if (mActionsToShowInCompact != null) {
6008 extras.putIntArray(EXTRA_COMPACT_ACTIONS, mActionsToShowInCompact);
6009 }
Dan Sandler842dd772014-05-15 09:36:47 -04006010 }
6011
Christoph Studer4600f9b2014-07-22 22:44:43 +02006012 /**
6013 * @hide
6014 */
6015 @Override
6016 protected void restoreFromExtras(Bundle extras) {
6017 super.restoreFromExtras(extras);
6018
6019 if (extras.containsKey(EXTRA_MEDIA_SESSION)) {
6020 mToken = extras.getParcelable(EXTRA_MEDIA_SESSION);
6021 }
6022 if (extras.containsKey(EXTRA_COMPACT_ACTIONS)) {
6023 mActionsToShowInCompact = extras.getIntArray(EXTRA_COMPACT_ACTIONS);
6024 }
6025 }
6026
Selim Cinek5bf069a2015-11-10 19:14:27 -05006027 private RemoteViews generateMediaActionButton(Action action, int color) {
Dan Sandler842dd772014-05-15 09:36:47 -04006028 final boolean tombstone = (action.actionIntent == null);
Selim Cinekf33b1112015-07-15 17:45:11 -07006029 RemoteViews button = new BuilderRemoteViews(mBuilder.mContext.getApplicationInfo(),
Alan Viverette3cb07a462014-06-06 14:19:53 -07006030 R.layout.notification_material_media_action);
Dan Sandler68079d52015-07-22 10:45:30 -04006031 button.setImageViewIcon(R.id.action0, action.getIcon());
Selim Cinek5bf069a2015-11-10 19:14:27 -05006032 button.setDrawableParameters(R.id.action0, false, -1, color, PorterDuff.Mode.SRC_ATOP,
6033 -1);
Dan Sandler842dd772014-05-15 09:36:47 -04006034 if (!tombstone) {
6035 button.setOnClickPendingIntent(R.id.action0, action.actionIntent);
6036 }
6037 button.setContentDescription(R.id.action0, action.title);
6038 return button;
6039 }
6040
6041 private RemoteViews makeMediaContentView() {
6042 RemoteViews view = mBuilder.applyStandardTemplate(
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006043 R.layout.notification_template_material_media, false /* hasProgress */);
Dan Sandler842dd772014-05-15 09:36:47 -04006044
6045 final int numActions = mBuilder.mActions.size();
6046 final int N = mActionsToShowInCompact == null
6047 ? 0
6048 : Math.min(mActionsToShowInCompact.length, MAX_MEDIA_BUTTONS_IN_COMPACT);
6049 if (N > 0) {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006050 view.removeAllViews(com.android.internal.R.id.media_actions);
Dan Sandler842dd772014-05-15 09:36:47 -04006051 for (int i = 0; i < N; i++) {
6052 if (i >= numActions) {
6053 throw new IllegalArgumentException(String.format(
6054 "setShowActionsInCompactView: action %d out of bounds (max %d)",
6055 i, numActions - 1));
6056 }
6057
6058 final Action action = mBuilder.mActions.get(mActionsToShowInCompact[i]);
Selim Cinek5bf069a2015-11-10 19:14:27 -05006059 final RemoteViews button = generateMediaActionButton(action,
Adrian Roos4ff3b122016-02-01 12:26:13 -08006060 mBuilder.resolveContrastColor());
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006061 view.addView(com.android.internal.R.id.media_actions, button);
Dan Sandler842dd772014-05-15 09:36:47 -04006062 }
6063 }
Selim Cinekfdc738f2016-01-27 20:04:27 -08006064 handleImage(view);
6065 // handle the content margin
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006066 int endMargin = R.dimen.notification_content_margin_end;
Selim Cinek279fa862016-06-14 10:57:25 -07006067 if (mBuilder.mN.hasLargeIcon()) {
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006068 endMargin = R.dimen.notification_content_plus_picture_margin_end;
Selim Cinekfdc738f2016-01-27 20:04:27 -08006069 }
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006070 view.setViewLayoutMarginEndDimen(R.id.notification_main_column, endMargin);
Dan Sandler842dd772014-05-15 09:36:47 -04006071 return view;
6072 }
6073
6074 private RemoteViews makeMediaBigContentView() {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006075 final int actionCount = Math.min(mBuilder.mActions.size(), MAX_MEDIA_BUTTONS);
Selim Cinekcc10bfb2016-02-10 16:24:21 -08006076 // Dont add an expanded view if there is no more content to be revealed
6077 int actionsInCompact = mActionsToShowInCompact == null
6078 ? 0
6079 : Math.min(mActionsToShowInCompact.length, MAX_MEDIA_BUTTONS_IN_COMPACT);
Selim Cinek279fa862016-06-14 10:57:25 -07006080 if (!mBuilder.mN.hasLargeIcon() && actionCount <= actionsInCompact) {
Selim Cinekcc10bfb2016-02-10 16:24:21 -08006081 return null;
6082 }
Selim Cinek5bf069a2015-11-10 19:14:27 -05006083 RemoteViews big = mBuilder.applyStandardTemplate(
6084 R.layout.notification_template_material_big_media,
6085 false);
Dan Sandler842dd772014-05-15 09:36:47 -04006086
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006087 if (actionCount > 0) {
6088 big.removeAllViews(com.android.internal.R.id.media_actions);
6089 for (int i = 0; i < actionCount; i++) {
Selim Cinek5bf069a2015-11-10 19:14:27 -05006090 final RemoteViews button = generateMediaActionButton(mBuilder.mActions.get(i),
Adrian Roos4ff3b122016-02-01 12:26:13 -08006091 mBuilder.resolveContrastColor());
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006092 big.addView(com.android.internal.R.id.media_actions, button);
Dan Sandler842dd772014-05-15 09:36:47 -04006093 }
6094 }
Selim Cinek5bf069a2015-11-10 19:14:27 -05006095 handleImage(big);
Dan Sandler842dd772014-05-15 09:36:47 -04006096 return big;
6097 }
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006098
Selim Cinek5bf069a2015-11-10 19:14:27 -05006099 private void handleImage(RemoteViews contentView) {
Selim Cinek279fa862016-06-14 10:57:25 -07006100 if (mBuilder.mN.hasLargeIcon()) {
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006101 contentView.setViewLayoutMarginEndDimen(R.id.line1, 0);
6102 contentView.setViewLayoutMarginEndDimen(R.id.text, 0);
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006103 }
6104 }
6105
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02006106 /**
6107 * @hide
6108 */
6109 @Override
6110 protected boolean hasProgress() {
6111 return false;
6112 }
Dan Sandler842dd772014-05-15 09:36:47 -04006113 }
Griff Hazen61a9e862014-05-22 16:05:19 -07006114
Selim Cinek593610c2016-02-16 18:42:57 -08006115 /**
6116 * Notification style for custom views that are decorated by the system
6117 *
6118 * <p>Instead of providing a notification that is completely custom, a developer can set this
6119 * style and still obtain system decorations like the notification header with the expand
6120 * affordance and actions.
6121 *
6122 * <p>Use {@link android.app.Notification.Builder#setCustomContentView(RemoteViews)},
6123 * {@link android.app.Notification.Builder#setCustomBigContentView(RemoteViews)} and
6124 * {@link android.app.Notification.Builder#setCustomHeadsUpContentView(RemoteViews)} to set the
6125 * corresponding custom views to display.
6126 *
6127 * To use this style with your Notification, feed it to
6128 * {@link Notification.Builder#setStyle(android.app.Notification.Style)} like so:
6129 * <pre class="prettyprint">
6130 * Notification noti = new Notification.Builder()
6131 * .setSmallIcon(R.drawable.ic_stat_player)
6132 * .setLargeIcon(albumArtBitmap))
6133 * .setCustomContentView(contentView);
6134 * .setStyle(<b>new Notification.DecoratedCustomViewStyle()</b>)
6135 * .build();
6136 * </pre>
6137 */
6138 public static class DecoratedCustomViewStyle extends Style {
6139
6140 public DecoratedCustomViewStyle() {
6141 }
6142
Selim Cinek593610c2016-02-16 18:42:57 -08006143 /**
6144 * @hide
6145 */
6146 public boolean displayCustomViewInline() {
6147 return true;
6148 }
6149
6150 /**
6151 * @hide
6152 */
6153 @Override
Selim Cinek7d1009b2017-01-25 15:28:28 -08006154 public RemoteViews makeContentView(boolean increasedHeight) {
Selim Cinek593610c2016-02-16 18:42:57 -08006155 return makeStandardTemplateWithCustomContent(mBuilder.mN.contentView);
6156 }
6157
6158 /**
6159 * @hide
6160 */
6161 @Override
6162 public RemoteViews makeBigContentView() {
6163 return makeDecoratedBigContentView();
6164 }
6165
6166 /**
6167 * @hide
6168 */
6169 @Override
6170 public RemoteViews makeHeadsUpContentView() {
6171 return makeDecoratedHeadsUpContentView();
6172 }
6173
Selim Cinek593610c2016-02-16 18:42:57 -08006174 private RemoteViews makeDecoratedHeadsUpContentView() {
6175 RemoteViews headsUpContentView = mBuilder.mN.headsUpContentView == null
6176 ? mBuilder.mN.contentView
6177 : mBuilder.mN.headsUpContentView;
6178 if (mBuilder.mActions.size() == 0) {
6179 return makeStandardTemplateWithCustomContent(headsUpContentView);
6180 }
6181 RemoteViews remoteViews = mBuilder.applyStandardTemplateWithActions(
6182 mBuilder.getBigBaseLayoutResource());
Selim Cinek247fa012016-02-18 09:50:48 -08006183 buildIntoRemoteViewContent(remoteViews, headsUpContentView);
Selim Cinek593610c2016-02-16 18:42:57 -08006184 return remoteViews;
6185 }
6186
Selim Cinek593610c2016-02-16 18:42:57 -08006187 private RemoteViews makeStandardTemplateWithCustomContent(RemoteViews customContent) {
6188 RemoteViews remoteViews = mBuilder.applyStandardTemplate(
6189 mBuilder.getBaseLayoutResource());
Selim Cinek247fa012016-02-18 09:50:48 -08006190 buildIntoRemoteViewContent(remoteViews, customContent);
Selim Cinek593610c2016-02-16 18:42:57 -08006191 return remoteViews;
6192 }
6193
Selim Cinek593610c2016-02-16 18:42:57 -08006194 private RemoteViews makeDecoratedBigContentView() {
6195 RemoteViews bigContentView = mBuilder.mN.bigContentView == null
6196 ? mBuilder.mN.contentView
6197 : mBuilder.mN.bigContentView;
6198 if (mBuilder.mActions.size() == 0) {
6199 return makeStandardTemplateWithCustomContent(bigContentView);
6200 }
6201 RemoteViews remoteViews = mBuilder.applyStandardTemplateWithActions(
6202 mBuilder.getBigBaseLayoutResource());
Selim Cinek247fa012016-02-18 09:50:48 -08006203 buildIntoRemoteViewContent(remoteViews, bigContentView);
Selim Cinek593610c2016-02-16 18:42:57 -08006204 return remoteViews;
6205 }
Selim Cinek247fa012016-02-18 09:50:48 -08006206
6207 private void buildIntoRemoteViewContent(RemoteViews remoteViews,
6208 RemoteViews customContent) {
Adrian Roos5081c0d2016-02-26 16:04:19 -08006209 if (customContent != null) {
Selim Cinekf91017e2016-03-14 12:25:09 -07006210 // Need to clone customContent before adding, because otherwise it can no longer be
6211 // parceled independently of remoteViews.
Adrian Roos5081c0d2016-02-26 16:04:19 -08006212 customContent = customContent.clone();
Selim Cinekf91017e2016-03-14 12:25:09 -07006213 remoteViews.removeAllViews(R.id.notification_main_column);
6214 remoteViews.addView(R.id.notification_main_column, customContent);
Adrian Roos5081c0d2016-02-26 16:04:19 -08006215 }
Selim Cinek247fa012016-02-18 09:50:48 -08006216 // also update the end margin if there is an image
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006217 int endMargin = R.dimen.notification_content_margin_end;
Selim Cinek279fa862016-06-14 10:57:25 -07006218 if (mBuilder.mN.hasLargeIcon()) {
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006219 endMargin = R.dimen.notification_content_plus_picture_margin_end;
Selim Cinek247fa012016-02-18 09:50:48 -08006220 }
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006221 remoteViews.setViewLayoutMarginEndDimen(R.id.notification_main_column, endMargin);
Selim Cinek247fa012016-02-18 09:50:48 -08006222 }
Selim Cinek593610c2016-02-16 18:42:57 -08006223 }
6224
Selim Cinek03eb3b72016-02-18 10:39:45 -08006225 /**
6226 * Notification style for media custom views that are decorated by the system
6227 *
6228 * <p>Instead of providing a media notification that is completely custom, a developer can set
6229 * this style and still obtain system decorations like the notification header with the expand
6230 * affordance and actions.
6231 *
6232 * <p>Use {@link android.app.Notification.Builder#setCustomContentView(RemoteViews)},
6233 * {@link android.app.Notification.Builder#setCustomBigContentView(RemoteViews)} and
6234 * {@link android.app.Notification.Builder#setCustomHeadsUpContentView(RemoteViews)} to set the
6235 * corresponding custom views to display.
6236 *
6237 * To use this style with your Notification, feed it to
6238 * {@link Notification.Builder#setStyle(android.app.Notification.Style)} like so:
6239 * <pre class="prettyprint">
6240 * Notification noti = new Notification.Builder()
6241 * .setSmallIcon(R.drawable.ic_stat_player)
6242 * .setLargeIcon(albumArtBitmap))
6243 * .setCustomContentView(contentView);
6244 * .setStyle(<b>new Notification.DecoratedMediaCustomViewStyle()</b>
6245 * .setMediaSession(mySession))
6246 * .build();
6247 * </pre>
6248 *
6249 * @see android.app.Notification.DecoratedCustomViewStyle
6250 * @see android.app.Notification.MediaStyle
6251 */
6252 public static class DecoratedMediaCustomViewStyle extends MediaStyle {
6253
6254 public DecoratedMediaCustomViewStyle() {
6255 }
6256
Selim Cinek03eb3b72016-02-18 10:39:45 -08006257 /**
6258 * @hide
6259 */
6260 public boolean displayCustomViewInline() {
6261 return true;
6262 }
6263
6264 /**
6265 * @hide
6266 */
6267 @Override
Selim Cinek7d1009b2017-01-25 15:28:28 -08006268 public RemoteViews makeContentView(boolean increasedHeight) {
6269 RemoteViews remoteViews = super.makeContentView(false /* increasedHeight */);
Selim Cinek03eb3b72016-02-18 10:39:45 -08006270 return buildIntoRemoteView(remoteViews, R.id.notification_content_container,
6271 mBuilder.mN.contentView);
6272 }
6273
6274 /**
6275 * @hide
6276 */
6277 @Override
6278 public RemoteViews makeBigContentView() {
6279 RemoteViews customRemoteView = mBuilder.mN.bigContentView != null
6280 ? mBuilder.mN.bigContentView
6281 : mBuilder.mN.contentView;
6282 return makeBigContentViewWithCustomContent(customRemoteView);
6283 }
6284
6285 private RemoteViews makeBigContentViewWithCustomContent(RemoteViews customRemoteView) {
6286 RemoteViews remoteViews = super.makeBigContentView();
6287 if (remoteViews != null) {
6288 return buildIntoRemoteView(remoteViews, R.id.notification_main_column,
6289 customRemoteView);
6290 } else if (customRemoteView != mBuilder.mN.contentView){
Selim Cinek7d1009b2017-01-25 15:28:28 -08006291 remoteViews = super.makeContentView(false /* increasedHeight */);
Selim Cinek03eb3b72016-02-18 10:39:45 -08006292 return buildIntoRemoteView(remoteViews, R.id.notification_content_container,
6293 customRemoteView);
6294 } else {
6295 return null;
6296 }
6297 }
6298
6299 /**
6300 * @hide
6301 */
6302 @Override
6303 public RemoteViews makeHeadsUpContentView() {
6304 RemoteViews customRemoteView = mBuilder.mN.headsUpContentView != null
6305 ? mBuilder.mN.headsUpContentView
6306 : mBuilder.mN.contentView;
6307 return makeBigContentViewWithCustomContent(customRemoteView);
6308 }
6309
6310 private RemoteViews buildIntoRemoteView(RemoteViews remoteViews, int id,
6311 RemoteViews customContent) {
Adrian Roos5081c0d2016-02-26 16:04:19 -08006312 if (customContent != null) {
Selim Cinekf91017e2016-03-14 12:25:09 -07006313 // Need to clone customContent before adding, because otherwise it can no longer be
6314 // parceled independently of remoteViews.
Adrian Roos5081c0d2016-02-26 16:04:19 -08006315 customContent = customContent.clone();
Selim Cinekf91017e2016-03-14 12:25:09 -07006316 remoteViews.removeAllViews(id);
6317 remoteViews.addView(id, customContent);
Adrian Roos5081c0d2016-02-26 16:04:19 -08006318 }
Selim Cinek03eb3b72016-02-18 10:39:45 -08006319 return remoteViews;
6320 }
6321 }
6322
Christoph Studer4600f9b2014-07-22 22:44:43 +02006323 // When adding a new Style subclass here, don't forget to update
6324 // Builder.getNotificationStyleClass.
6325
Griff Hazen61a9e862014-05-22 16:05:19 -07006326 /**
6327 * Extender interface for use with {@link Builder#extend}. Extenders may be used to add
6328 * metadata or change options on a notification builder.
6329 */
6330 public interface Extender {
6331 /**
6332 * Apply this extender to a notification builder.
6333 * @param builder the builder to be modified.
6334 * @return the build object for chaining.
6335 */
6336 public Builder extend(Builder builder);
6337 }
6338
6339 /**
6340 * Helper class to add wearable extensions to notifications.
6341 * <p class="note"> See
6342 * <a href="{@docRoot}wear/notifications/creating.html">Creating Notifications
6343 * for Android Wear</a> for more information on how to use this class.
6344 * <p>
6345 * To create a notification with wearable extensions:
6346 * <ol>
6347 * <li>Create a {@link android.app.Notification.Builder}, setting any desired
6348 * properties.
6349 * <li>Create a {@link android.app.Notification.WearableExtender}.
6350 * <li>Set wearable-specific properties using the
6351 * {@code add} and {@code set} methods of {@link android.app.Notification.WearableExtender}.
6352 * <li>Call {@link android.app.Notification.Builder#extend} to apply the extensions to a
6353 * notification.
6354 * <li>Post the notification to the notification system with the
6355 * {@code NotificationManager.notify(...)} methods.
6356 * </ol>
6357 *
6358 * <pre class="prettyprint">
6359 * Notification notif = new Notification.Builder(mContext)
6360 * .setContentTitle(&quot;New mail from &quot; + sender.toString())
6361 * .setContentText(subject)
6362 * .setSmallIcon(R.drawable.new_mail)
6363 * .extend(new Notification.WearableExtender()
6364 * .setContentIcon(R.drawable.new_mail))
6365 * .build();
6366 * NotificationManager notificationManger =
6367 * (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
6368 * notificationManger.notify(0, notif);</pre>
6369 *
6370 * <p>Wearable extensions can be accessed on an existing notification by using the
6371 * {@code WearableExtender(Notification)} constructor,
6372 * and then using the {@code get} methods to access values.
6373 *
6374 * <pre class="prettyprint">
6375 * Notification.WearableExtender wearableExtender = new Notification.WearableExtender(
6376 * notification);
Griff Hazen14f57992014-05-26 09:07:14 -07006377 * List&lt;Notification&gt; pages = wearableExtender.getPages();</pre>
Griff Hazen61a9e862014-05-22 16:05:19 -07006378 */
6379 public static final class WearableExtender implements Extender {
6380 /**
6381 * Sentinel value for an action index that is unset.
6382 */
6383 public static final int UNSET_ACTION_INDEX = -1;
6384
6385 /**
6386 * Size value for use with {@link #setCustomSizePreset} to show this notification with
6387 * default sizing.
6388 * <p>For custom display notifications created using {@link #setDisplayIntent},
Paul Soulosaa4f4bf2015-08-04 11:59:45 -07006389 * the default is {@link #SIZE_MEDIUM}. All other notifications size automatically based
Griff Hazen61a9e862014-05-22 16:05:19 -07006390 * on their content.
6391 */
6392 public static final int SIZE_DEFAULT = 0;
6393
6394 /**
6395 * Size value for use with {@link #setCustomSizePreset} to show this notification
6396 * with an extra small size.
6397 * <p>This value is only applicable for custom display notifications created using
6398 * {@link #setDisplayIntent}.
6399 */
6400 public static final int SIZE_XSMALL = 1;
6401
6402 /**
6403 * Size value for use with {@link #setCustomSizePreset} to show this notification
6404 * with a small size.
6405 * <p>This value is only applicable for custom display notifications created using
6406 * {@link #setDisplayIntent}.
6407 */
6408 public static final int SIZE_SMALL = 2;
6409
6410 /**
6411 * Size value for use with {@link #setCustomSizePreset} to show this notification
6412 * with a medium size.
6413 * <p>This value is only applicable for custom display notifications created using
6414 * {@link #setDisplayIntent}.
6415 */
6416 public static final int SIZE_MEDIUM = 3;
6417
6418 /**
6419 * Size value for use with {@link #setCustomSizePreset} to show this notification
6420 * with a large size.
6421 * <p>This value is only applicable for custom display notifications created using
6422 * {@link #setDisplayIntent}.
6423 */
6424 public static final int SIZE_LARGE = 4;
6425
Griff Hazend5f11f92014-05-27 15:40:09 -07006426 /**
6427 * Size value for use with {@link #setCustomSizePreset} to show this notification
6428 * full screen.
6429 * <p>This value is only applicable for custom display notifications created using
6430 * {@link #setDisplayIntent}.
6431 */
6432 public static final int SIZE_FULL_SCREEN = 5;
6433
Griff Hazen5f2edfc2014-09-29 16:28:44 -07006434 /**
6435 * Sentinel value for use with {@link #setHintScreenTimeout} to keep the screen on for a
6436 * short amount of time when this notification is displayed on the screen. This
6437 * is the default value.
6438 */
6439 public static final int SCREEN_TIMEOUT_SHORT = 0;
6440
6441 /**
6442 * Sentinel value for use with {@link #setHintScreenTimeout} to keep the screen on
6443 * for a longer amount of time when this notification is displayed on the screen.
6444 */
6445 public static final int SCREEN_TIMEOUT_LONG = -1;
6446
Griff Hazen61a9e862014-05-22 16:05:19 -07006447 /** Notification extra which contains wearable extensions */
6448 private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS";
6449
Pete Gastaf6781d2014-10-07 15:17:05 -04006450 // Keys within EXTRA_WEARABLE_EXTENSIONS for wearable options.
Griff Hazen61a9e862014-05-22 16:05:19 -07006451 private static final String KEY_ACTIONS = "actions";
6452 private static final String KEY_FLAGS = "flags";
6453 private static final String KEY_DISPLAY_INTENT = "displayIntent";
6454 private static final String KEY_PAGES = "pages";
6455 private static final String KEY_BACKGROUND = "background";
6456 private static final String KEY_CONTENT_ICON = "contentIcon";
6457 private static final String KEY_CONTENT_ICON_GRAVITY = "contentIconGravity";
6458 private static final String KEY_CONTENT_ACTION_INDEX = "contentActionIndex";
6459 private static final String KEY_CUSTOM_SIZE_PRESET = "customSizePreset";
6460 private static final String KEY_CUSTOM_CONTENT_HEIGHT = "customContentHeight";
6461 private static final String KEY_GRAVITY = "gravity";
Griff Hazen5f2edfc2014-09-29 16:28:44 -07006462 private static final String KEY_HINT_SCREEN_TIMEOUT = "hintScreenTimeout";
Nadia Benbernou948627e2016-04-14 14:41:08 -04006463 private static final String KEY_DISMISSAL_ID = "dismissalId";
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04006464 private static final String KEY_BRIDGE_TAG = "bridgeTag";
Griff Hazen61a9e862014-05-22 16:05:19 -07006465
6466 // Flags bitwise-ored to mFlags
6467 private static final int FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE = 0x1;
6468 private static final int FLAG_HINT_HIDE_ICON = 1 << 1;
6469 private static final int FLAG_HINT_SHOW_BACKGROUND_ONLY = 1 << 2;
6470 private static final int FLAG_START_SCROLL_BOTTOM = 1 << 3;
Griff Hazen5f2edfc2014-09-29 16:28:44 -07006471 private static final int FLAG_HINT_AVOID_BACKGROUND_CLIPPING = 1 << 4;
Alex Hills4bcb06b2016-04-05 14:26:25 -04006472 private static final int FLAG_BIG_PICTURE_AMBIENT = 1 << 5;
Alex Hills9ab3a232016-04-05 14:54:56 -04006473 private static final int FLAG_HINT_CONTENT_INTENT_LAUNCHES_ACTIVITY = 1 << 6;
Griff Hazen61a9e862014-05-22 16:05:19 -07006474
6475 // Default value for flags integer
6476 private static final int DEFAULT_FLAGS = FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE;
6477
6478 private static final int DEFAULT_CONTENT_ICON_GRAVITY = Gravity.END;
6479 private static final int DEFAULT_GRAVITY = Gravity.BOTTOM;
6480
6481 private ArrayList<Action> mActions = new ArrayList<Action>();
6482 private int mFlags = DEFAULT_FLAGS;
6483 private PendingIntent mDisplayIntent;
6484 private ArrayList<Notification> mPages = new ArrayList<Notification>();
6485 private Bitmap mBackground;
6486 private int mContentIcon;
6487 private int mContentIconGravity = DEFAULT_CONTENT_ICON_GRAVITY;
6488 private int mContentActionIndex = UNSET_ACTION_INDEX;
6489 private int mCustomSizePreset = SIZE_DEFAULT;
6490 private int mCustomContentHeight;
6491 private int mGravity = DEFAULT_GRAVITY;
Griff Hazen5f2edfc2014-09-29 16:28:44 -07006492 private int mHintScreenTimeout;
Nadia Benbernou948627e2016-04-14 14:41:08 -04006493 private String mDismissalId;
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04006494 private String mBridgeTag;
Griff Hazen61a9e862014-05-22 16:05:19 -07006495
6496 /**
6497 * Create a {@link android.app.Notification.WearableExtender} with default
6498 * options.
6499 */
6500 public WearableExtender() {
6501 }
6502
6503 public WearableExtender(Notification notif) {
6504 Bundle wearableBundle = notif.extras.getBundle(EXTRA_WEARABLE_EXTENSIONS);
6505 if (wearableBundle != null) {
6506 List<Action> actions = wearableBundle.getParcelableArrayList(KEY_ACTIONS);
6507 if (actions != null) {
6508 mActions.addAll(actions);
6509 }
6510
6511 mFlags = wearableBundle.getInt(KEY_FLAGS, DEFAULT_FLAGS);
6512 mDisplayIntent = wearableBundle.getParcelable(KEY_DISPLAY_INTENT);
6513
6514 Notification[] pages = getNotificationArrayFromBundle(
6515 wearableBundle, KEY_PAGES);
6516 if (pages != null) {
6517 Collections.addAll(mPages, pages);
6518 }
6519
6520 mBackground = wearableBundle.getParcelable(KEY_BACKGROUND);
6521 mContentIcon = wearableBundle.getInt(KEY_CONTENT_ICON);
6522 mContentIconGravity = wearableBundle.getInt(KEY_CONTENT_ICON_GRAVITY,
6523 DEFAULT_CONTENT_ICON_GRAVITY);
6524 mContentActionIndex = wearableBundle.getInt(KEY_CONTENT_ACTION_INDEX,
6525 UNSET_ACTION_INDEX);
6526 mCustomSizePreset = wearableBundle.getInt(KEY_CUSTOM_SIZE_PRESET,
6527 SIZE_DEFAULT);
6528 mCustomContentHeight = wearableBundle.getInt(KEY_CUSTOM_CONTENT_HEIGHT);
6529 mGravity = wearableBundle.getInt(KEY_GRAVITY, DEFAULT_GRAVITY);
Griff Hazen5f2edfc2014-09-29 16:28:44 -07006530 mHintScreenTimeout = wearableBundle.getInt(KEY_HINT_SCREEN_TIMEOUT);
Nadia Benbernou948627e2016-04-14 14:41:08 -04006531 mDismissalId = wearableBundle.getString(KEY_DISMISSAL_ID);
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04006532 mBridgeTag = wearableBundle.getString(KEY_BRIDGE_TAG);
Griff Hazen61a9e862014-05-22 16:05:19 -07006533 }
6534 }
6535
6536 /**
6537 * Apply wearable extensions to a notification that is being built. This is typically
6538 * called by the {@link android.app.Notification.Builder#extend} method of
6539 * {@link android.app.Notification.Builder}.
6540 */
6541 @Override
6542 public Notification.Builder extend(Notification.Builder builder) {
6543 Bundle wearableBundle = new Bundle();
6544
6545 if (!mActions.isEmpty()) {
6546 wearableBundle.putParcelableArrayList(KEY_ACTIONS, mActions);
6547 }
6548 if (mFlags != DEFAULT_FLAGS) {
6549 wearableBundle.putInt(KEY_FLAGS, mFlags);
6550 }
6551 if (mDisplayIntent != null) {
6552 wearableBundle.putParcelable(KEY_DISPLAY_INTENT, mDisplayIntent);
6553 }
6554 if (!mPages.isEmpty()) {
6555 wearableBundle.putParcelableArray(KEY_PAGES, mPages.toArray(
6556 new Notification[mPages.size()]));
6557 }
6558 if (mBackground != null) {
6559 wearableBundle.putParcelable(KEY_BACKGROUND, mBackground);
6560 }
6561 if (mContentIcon != 0) {
6562 wearableBundle.putInt(KEY_CONTENT_ICON, mContentIcon);
6563 }
6564 if (mContentIconGravity != DEFAULT_CONTENT_ICON_GRAVITY) {
6565 wearableBundle.putInt(KEY_CONTENT_ICON_GRAVITY, mContentIconGravity);
6566 }
6567 if (mContentActionIndex != UNSET_ACTION_INDEX) {
6568 wearableBundle.putInt(KEY_CONTENT_ACTION_INDEX,
6569 mContentActionIndex);
6570 }
6571 if (mCustomSizePreset != SIZE_DEFAULT) {
6572 wearableBundle.putInt(KEY_CUSTOM_SIZE_PRESET, mCustomSizePreset);
6573 }
6574 if (mCustomContentHeight != 0) {
6575 wearableBundle.putInt(KEY_CUSTOM_CONTENT_HEIGHT, mCustomContentHeight);
6576 }
6577 if (mGravity != DEFAULT_GRAVITY) {
6578 wearableBundle.putInt(KEY_GRAVITY, mGravity);
6579 }
Griff Hazen5f2edfc2014-09-29 16:28:44 -07006580 if (mHintScreenTimeout != 0) {
6581 wearableBundle.putInt(KEY_HINT_SCREEN_TIMEOUT, mHintScreenTimeout);
6582 }
Nadia Benbernou948627e2016-04-14 14:41:08 -04006583 if (mDismissalId != null) {
6584 wearableBundle.putString(KEY_DISMISSAL_ID, mDismissalId);
6585 }
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04006586 if (mBridgeTag != null) {
6587 wearableBundle.putString(KEY_BRIDGE_TAG, mBridgeTag);
6588 }
Griff Hazen61a9e862014-05-22 16:05:19 -07006589
6590 builder.getExtras().putBundle(EXTRA_WEARABLE_EXTENSIONS, wearableBundle);
6591 return builder;
6592 }
6593
6594 @Override
6595 public WearableExtender clone() {
6596 WearableExtender that = new WearableExtender();
6597 that.mActions = new ArrayList<Action>(this.mActions);
6598 that.mFlags = this.mFlags;
6599 that.mDisplayIntent = this.mDisplayIntent;
6600 that.mPages = new ArrayList<Notification>(this.mPages);
6601 that.mBackground = this.mBackground;
6602 that.mContentIcon = this.mContentIcon;
6603 that.mContentIconGravity = this.mContentIconGravity;
6604 that.mContentActionIndex = this.mContentActionIndex;
6605 that.mCustomSizePreset = this.mCustomSizePreset;
6606 that.mCustomContentHeight = this.mCustomContentHeight;
6607 that.mGravity = this.mGravity;
Griff Hazen5f2edfc2014-09-29 16:28:44 -07006608 that.mHintScreenTimeout = this.mHintScreenTimeout;
Nadia Benbernou948627e2016-04-14 14:41:08 -04006609 that.mDismissalId = this.mDismissalId;
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04006610 that.mBridgeTag = this.mBridgeTag;
Griff Hazen61a9e862014-05-22 16:05:19 -07006611 return that;
6612 }
6613
6614 /**
6615 * Add a wearable action to this notification.
6616 *
6617 * <p>When wearable actions are added using this method, the set of actions that
6618 * show on a wearable device splits from devices that only show actions added
6619 * using {@link android.app.Notification.Builder#addAction}. This allows for customization
6620 * of which actions display on different devices.
6621 *
6622 * @param action the action to add to this notification
6623 * @return this object for method chaining
6624 * @see android.app.Notification.Action
6625 */
6626 public WearableExtender addAction(Action action) {
6627 mActions.add(action);
6628 return this;
6629 }
6630
6631 /**
6632 * Adds wearable actions to this notification.
6633 *
6634 * <p>When wearable actions are added using this method, the set of actions that
6635 * show on a wearable device splits from devices that only show actions added
6636 * using {@link android.app.Notification.Builder#addAction}. This allows for customization
6637 * of which actions display on different devices.
6638 *
6639 * @param actions the actions to add to this notification
6640 * @return this object for method chaining
6641 * @see android.app.Notification.Action
6642 */
6643 public WearableExtender addActions(List<Action> actions) {
6644 mActions.addAll(actions);
6645 return this;
6646 }
6647
6648 /**
6649 * Clear all wearable actions present on this builder.
6650 * @return this object for method chaining.
6651 * @see #addAction
6652 */
6653 public WearableExtender clearActions() {
6654 mActions.clear();
6655 return this;
6656 }
6657
6658 /**
6659 * Get the wearable actions present on this notification.
6660 */
6661 public List<Action> getActions() {
6662 return mActions;
6663 }
6664
6665 /**
6666 * Set an intent to launch inside of an activity view when displaying
Griff Hazen14f57992014-05-26 09:07:14 -07006667 * this notification. The {@link PendingIntent} provided should be for an activity.
6668 *
6669 * <pre class="prettyprint">
6670 * Intent displayIntent = new Intent(context, MyDisplayActivity.class);
6671 * PendingIntent displayPendingIntent = PendingIntent.getActivity(context,
6672 * 0, displayIntent, PendingIntent.FLAG_UPDATE_CURRENT);
6673 * Notification notif = new Notification.Builder(context)
6674 * .extend(new Notification.WearableExtender()
6675 * .setDisplayIntent(displayPendingIntent)
6676 * .setCustomSizePreset(Notification.WearableExtender.SIZE_MEDIUM))
6677 * .build();</pre>
6678 *
6679 * <p>The activity to launch needs to allow embedding, must be exported, and
Griff Hazen831ca9d2014-06-17 00:38:38 -07006680 * should have an empty task affinity. It is also recommended to use the device
6681 * default light theme.
Griff Hazen14f57992014-05-26 09:07:14 -07006682 *
6683 * <p>Example AndroidManifest.xml entry:
6684 * <pre class="prettyprint">
6685 * &lt;activity android:name=&quot;com.example.MyDisplayActivity&quot;
6686 * android:exported=&quot;true&quot;
6687 * android:allowEmbedded=&quot;true&quot;
Griff Hazen831ca9d2014-06-17 00:38:38 -07006688 * android:taskAffinity=&quot;&quot;
6689 * android:theme=&quot;@android:style/Theme.DeviceDefault.Light&quot; /&gt;</pre>
Griff Hazen61a9e862014-05-22 16:05:19 -07006690 *
6691 * @param intent the {@link PendingIntent} for an activity
6692 * @return this object for method chaining
6693 * @see android.app.Notification.WearableExtender#getDisplayIntent
6694 */
6695 public WearableExtender setDisplayIntent(PendingIntent intent) {
6696 mDisplayIntent = intent;
6697 return this;
6698 }
6699
6700 /**
6701 * Get the intent to launch inside of an activity view when displaying this
6702 * notification. This {@code PendingIntent} should be for an activity.
6703 */
6704 public PendingIntent getDisplayIntent() {
6705 return mDisplayIntent;
6706 }
6707
6708 /**
6709 * Add an additional page of content to display with this notification. The current
6710 * notification forms the first page, and pages added using this function form
6711 * subsequent pages. This field can be used to separate a notification into multiple
6712 * sections.
6713 *
6714 * @param page the notification to add as another page
6715 * @return this object for method chaining
6716 * @see android.app.Notification.WearableExtender#getPages
6717 */
6718 public WearableExtender addPage(Notification page) {
6719 mPages.add(page);
6720 return this;
6721 }
6722
6723 /**
6724 * Add additional pages of content to display with this notification. The current
6725 * notification forms the first page, and pages added using this function form
6726 * subsequent pages. This field can be used to separate a notification into multiple
6727 * sections.
6728 *
6729 * @param pages a list of notifications
6730 * @return this object for method chaining
6731 * @see android.app.Notification.WearableExtender#getPages
6732 */
6733 public WearableExtender addPages(List<Notification> pages) {
6734 mPages.addAll(pages);
6735 return this;
6736 }
6737
6738 /**
6739 * Clear all additional pages present on this builder.
6740 * @return this object for method chaining.
6741 * @see #addPage
6742 */
6743 public WearableExtender clearPages() {
6744 mPages.clear();
6745 return this;
6746 }
6747
6748 /**
6749 * Get the array of additional pages of content for displaying this notification. The
6750 * current notification forms the first page, and elements within this array form
6751 * subsequent pages. This field can be used to separate a notification into multiple
6752 * sections.
6753 * @return the pages for this notification
6754 */
6755 public List<Notification> getPages() {
6756 return mPages;
6757 }
6758
6759 /**
6760 * Set a background image to be displayed behind the notification content.
6761 * Contrary to the {@link android.app.Notification.BigPictureStyle}, this background
6762 * will work with any notification style.
6763 *
6764 * @param background the background bitmap
6765 * @return this object for method chaining
6766 * @see android.app.Notification.WearableExtender#getBackground
6767 */
6768 public WearableExtender setBackground(Bitmap background) {
6769 mBackground = background;
6770 return this;
6771 }
6772
6773 /**
6774 * Get a background image to be displayed behind the notification content.
6775 * Contrary to the {@link android.app.Notification.BigPictureStyle}, this background
6776 * will work with any notification style.
6777 *
6778 * @return the background image
6779 * @see android.app.Notification.WearableExtender#setBackground
6780 */
6781 public Bitmap getBackground() {
6782 return mBackground;
6783 }
6784
6785 /**
6786 * Set an icon that goes with the content of this notification.
6787 */
6788 public WearableExtender setContentIcon(int icon) {
6789 mContentIcon = icon;
6790 return this;
6791 }
6792
6793 /**
6794 * Get an icon that goes with the content of this notification.
6795 */
6796 public int getContentIcon() {
6797 return mContentIcon;
6798 }
6799
6800 /**
6801 * Set the gravity that the content icon should have within the notification display.
6802 * Supported values include {@link android.view.Gravity#START} and
6803 * {@link android.view.Gravity#END}. The default value is {@link android.view.Gravity#END}.
6804 * @see #setContentIcon
6805 */
6806 public WearableExtender setContentIconGravity(int contentIconGravity) {
6807 mContentIconGravity = contentIconGravity;
6808 return this;
6809 }
6810
6811 /**
6812 * Get the gravity that the content icon should have within the notification display.
6813 * Supported values include {@link android.view.Gravity#START} and
6814 * {@link android.view.Gravity#END}. The default value is {@link android.view.Gravity#END}.
6815 * @see #getContentIcon
6816 */
6817 public int getContentIconGravity() {
6818 return mContentIconGravity;
6819 }
6820
6821 /**
6822 * Set an action from this notification's actions to be clickable with the content of
Griff Hazen14f57992014-05-26 09:07:14 -07006823 * this notification. This action will no longer display separately from the
6824 * notification's content.
6825 *
Griff Hazenca48d352014-05-28 22:37:13 -07006826 * <p>For notifications with multiple pages, child pages can also have content actions
Griff Hazen14f57992014-05-26 09:07:14 -07006827 * set, although the list of available actions comes from the main notification and not
6828 * from the child page's notification.
6829 *
6830 * @param actionIndex The index of the action to hoist onto the current notification page.
6831 * If wearable actions were added to the main notification, this index
6832 * will apply to that list, otherwise it will apply to the regular
6833 * actions list.
Griff Hazen61a9e862014-05-22 16:05:19 -07006834 */
6835 public WearableExtender setContentAction(int actionIndex) {
6836 mContentActionIndex = actionIndex;
6837 return this;
6838 }
6839
6840 /**
Griff Hazenca48d352014-05-28 22:37:13 -07006841 * Get the index of the notification action, if any, that was specified as being clickable
6842 * with the content of this notification. This action will no longer display separately
Griff Hazen14f57992014-05-26 09:07:14 -07006843 * from the notification's content.
Griff Hazen61a9e862014-05-22 16:05:19 -07006844 *
Griff Hazenca48d352014-05-28 22:37:13 -07006845 * <p>For notifications with multiple pages, child pages can also have content actions
Griff Hazen14f57992014-05-26 09:07:14 -07006846 * set, although the list of available actions comes from the main notification and not
6847 * from the child page's notification.
6848 *
6849 * <p>If wearable specific actions were added to the main notification, this index will
6850 * apply to that list, otherwise it will apply to the regular actions list.
Griff Hazenca48d352014-05-28 22:37:13 -07006851 *
6852 * @return the action index or {@link #UNSET_ACTION_INDEX} if no action was selected.
Griff Hazen61a9e862014-05-22 16:05:19 -07006853 */
6854 public int getContentAction() {
6855 return mContentActionIndex;
6856 }
6857
6858 /**
6859 * Set the gravity that this notification should have within the available viewport space.
6860 * Supported values include {@link android.view.Gravity#TOP},
6861 * {@link android.view.Gravity#CENTER_VERTICAL} and {@link android.view.Gravity#BOTTOM}.
6862 * The default value is {@link android.view.Gravity#BOTTOM}.
6863 */
6864 public WearableExtender setGravity(int gravity) {
6865 mGravity = gravity;
6866 return this;
6867 }
6868
6869 /**
6870 * Get the gravity that this notification should have within the available viewport space.
6871 * Supported values include {@link android.view.Gravity#TOP},
6872 * {@link android.view.Gravity#CENTER_VERTICAL} and {@link android.view.Gravity#BOTTOM}.
6873 * The default value is {@link android.view.Gravity#BOTTOM}.
6874 */
6875 public int getGravity() {
6876 return mGravity;
6877 }
6878
6879 /**
6880 * Set the custom size preset for the display of this notification out of the available
6881 * presets found in {@link android.app.Notification.WearableExtender}, e.g.
6882 * {@link #SIZE_LARGE}.
6883 * <p>Some custom size presets are only applicable for custom display notifications created
6884 * using {@link android.app.Notification.WearableExtender#setDisplayIntent}. Check the
6885 * documentation for the preset in question. See also
6886 * {@link #setCustomContentHeight} and {@link #getCustomSizePreset}.
6887 */
6888 public WearableExtender setCustomSizePreset(int sizePreset) {
6889 mCustomSizePreset = sizePreset;
6890 return this;
6891 }
6892
6893 /**
6894 * Get the custom size preset for the display of this notification out of the available
6895 * presets found in {@link android.app.Notification.WearableExtender}, e.g.
6896 * {@link #SIZE_LARGE}.
6897 * <p>Some custom size presets are only applicable for custom display notifications created
6898 * using {@link #setDisplayIntent}. Check the documentation for the preset in question.
6899 * See also {@link #setCustomContentHeight} and {@link #setCustomSizePreset}.
6900 */
6901 public int getCustomSizePreset() {
6902 return mCustomSizePreset;
6903 }
6904
6905 /**
6906 * Set the custom height in pixels for the display of this notification's content.
6907 * <p>This option is only available for custom display notifications created
6908 * using {@link android.app.Notification.WearableExtender#setDisplayIntent}. See also
6909 * {@link android.app.Notification.WearableExtender#setCustomSizePreset} and
6910 * {@link #getCustomContentHeight}.
6911 */
6912 public WearableExtender setCustomContentHeight(int height) {
6913 mCustomContentHeight = height;
6914 return this;
6915 }
6916
6917 /**
6918 * Get the custom height in pixels for the display of this notification's content.
6919 * <p>This option is only available for custom display notifications created
6920 * using {@link #setDisplayIntent}. See also {@link #setCustomSizePreset} and
6921 * {@link #setCustomContentHeight}.
6922 */
6923 public int getCustomContentHeight() {
6924 return mCustomContentHeight;
6925 }
6926
6927 /**
6928 * Set whether the scrolling position for the contents of this notification should start
6929 * at the bottom of the contents instead of the top when the contents are too long to
6930 * display within the screen. Default is false (start scroll at the top).
6931 */
6932 public WearableExtender setStartScrollBottom(boolean startScrollBottom) {
6933 setFlag(FLAG_START_SCROLL_BOTTOM, startScrollBottom);
6934 return this;
6935 }
6936
6937 /**
6938 * Get whether the scrolling position for the contents of this notification should start
6939 * at the bottom of the contents instead of the top when the contents are too long to
6940 * display within the screen. Default is false (start scroll at the top).
6941 */
6942 public boolean getStartScrollBottom() {
6943 return (mFlags & FLAG_START_SCROLL_BOTTOM) != 0;
6944 }
6945
6946 /**
6947 * Set whether the content intent is available when the wearable device is not connected
6948 * to a companion device. The user can still trigger this intent when the wearable device
6949 * is offline, but a visual hint will indicate that the content intent may not be available.
6950 * Defaults to true.
6951 */
6952 public WearableExtender setContentIntentAvailableOffline(
6953 boolean contentIntentAvailableOffline) {
6954 setFlag(FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE, contentIntentAvailableOffline);
6955 return this;
6956 }
6957
6958 /**
6959 * Get whether the content intent is available when the wearable device is not connected
6960 * to a companion device. The user can still trigger this intent when the wearable device
6961 * is offline, but a visual hint will indicate that the content intent may not be available.
6962 * Defaults to true.
6963 */
6964 public boolean getContentIntentAvailableOffline() {
6965 return (mFlags & FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE) != 0;
6966 }
6967
6968 /**
6969 * Set a hint that this notification's icon should not be displayed.
6970 * @param hintHideIcon {@code true} to hide the icon, {@code false} otherwise.
6971 * @return this object for method chaining
6972 */
6973 public WearableExtender setHintHideIcon(boolean hintHideIcon) {
6974 setFlag(FLAG_HINT_HIDE_ICON, hintHideIcon);
6975 return this;
6976 }
6977
6978 /**
6979 * Get a hint that this notification's icon should not be displayed.
6980 * @return {@code true} if this icon should not be displayed, false otherwise.
6981 * The default value is {@code false} if this was never set.
6982 */
6983 public boolean getHintHideIcon() {
6984 return (mFlags & FLAG_HINT_HIDE_ICON) != 0;
6985 }
6986
6987 /**
6988 * Set a visual hint that only the background image of this notification should be
6989 * displayed, and other semantic content should be hidden. This hint is only applicable
6990 * to sub-pages added using {@link #addPage}.
6991 */
6992 public WearableExtender setHintShowBackgroundOnly(boolean hintShowBackgroundOnly) {
6993 setFlag(FLAG_HINT_SHOW_BACKGROUND_ONLY, hintShowBackgroundOnly);
6994 return this;
6995 }
6996
6997 /**
6998 * Get a visual hint that only the background image of this notification should be
6999 * displayed, and other semantic content should be hidden. This hint is only applicable
7000 * to sub-pages added using {@link android.app.Notification.WearableExtender#addPage}.
7001 */
7002 public boolean getHintShowBackgroundOnly() {
7003 return (mFlags & FLAG_HINT_SHOW_BACKGROUND_ONLY) != 0;
7004 }
7005
Griff Hazen5f2edfc2014-09-29 16:28:44 -07007006 /**
Griff Hazen9c5be4e2014-11-17 17:47:50 -08007007 * Set a hint that this notification's background should not be clipped if possible,
7008 * and should instead be resized to fully display on the screen, retaining the aspect
7009 * ratio of the image. This can be useful for images like barcodes or qr codes.
Griff Hazen5f2edfc2014-09-29 16:28:44 -07007010 * @param hintAvoidBackgroundClipping {@code true} to avoid clipping if possible.
7011 * @return this object for method chaining
7012 */
7013 public WearableExtender setHintAvoidBackgroundClipping(
7014 boolean hintAvoidBackgroundClipping) {
7015 setFlag(FLAG_HINT_AVOID_BACKGROUND_CLIPPING, hintAvoidBackgroundClipping);
7016 return this;
7017 }
7018
7019 /**
Griff Hazen9c5be4e2014-11-17 17:47:50 -08007020 * Get a hint that this notification's background should not be clipped if possible,
7021 * and should instead be resized to fully display on the screen, retaining the aspect
7022 * ratio of the image. This can be useful for images like barcodes or qr codes.
Griff Hazen5f2edfc2014-09-29 16:28:44 -07007023 * @return {@code true} if it's ok if the background is clipped on the screen, false
7024 * otherwise. The default value is {@code false} if this was never set.
7025 */
7026 public boolean getHintAvoidBackgroundClipping() {
7027 return (mFlags & FLAG_HINT_AVOID_BACKGROUND_CLIPPING) != 0;
7028 }
7029
7030 /**
7031 * Set a hint that the screen should remain on for at least this duration when
7032 * this notification is displayed on the screen.
7033 * @param timeout The requested screen timeout in milliseconds. Can also be either
7034 * {@link #SCREEN_TIMEOUT_SHORT} or {@link #SCREEN_TIMEOUT_LONG}.
7035 * @return this object for method chaining
7036 */
7037 public WearableExtender setHintScreenTimeout(int timeout) {
7038 mHintScreenTimeout = timeout;
7039 return this;
7040 }
7041
7042 /**
7043 * Get the duration, in milliseconds, that the screen should remain on for
7044 * when this notification is displayed.
7045 * @return the duration in milliseconds if > 0, or either one of the sentinel values
7046 * {@link #SCREEN_TIMEOUT_SHORT} or {@link #SCREEN_TIMEOUT_LONG}.
7047 */
7048 public int getHintScreenTimeout() {
7049 return mHintScreenTimeout;
7050 }
7051
Alex Hills9ab3a232016-04-05 14:54:56 -04007052 /**
Alex Hills4bcb06b2016-04-05 14:26:25 -04007053 * Set a hint that this notification's {@link BigPictureStyle} (if present) should be
7054 * converted to low-bit and displayed in ambient mode, especially useful for barcodes and
7055 * qr codes, as well as other simple black-and-white tickets.
7056 * @param hintAmbientBigPicture {@code true} to enable converstion and ambient.
7057 * @return this object for method chaining
7058 */
7059 public WearableExtender setHintAmbientBigPicture(boolean hintAmbientBigPicture) {
7060 setFlag(FLAG_BIG_PICTURE_AMBIENT, hintAmbientBigPicture);
7061 return this;
7062 }
7063
7064 /**
7065 * Get a hint that this notification's {@link BigPictureStyle} (if present) should be
7066 * converted to low-bit and displayed in ambient mode, especially useful for barcodes and
7067 * qr codes, as well as other simple black-and-white tickets.
7068 * @return {@code true} if it should be displayed in ambient, false otherwise
7069 * otherwise. The default value is {@code false} if this was never set.
7070 */
7071 public boolean getHintAmbientBigPicture() {
7072 return (mFlags & FLAG_BIG_PICTURE_AMBIENT) != 0;
7073 }
7074
7075 /**
Alex Hills9ab3a232016-04-05 14:54:56 -04007076 * Set a hint that this notification's content intent will launch an {@link Activity}
7077 * directly, telling the platform that it can generate the appropriate transitions.
7078 * @param hintContentIntentLaunchesActivity {@code true} if the content intent will launch
7079 * an activity and transitions should be generated, false otherwise.
7080 * @return this object for method chaining
7081 */
7082 public WearableExtender setHintContentIntentLaunchesActivity(
7083 boolean hintContentIntentLaunchesActivity) {
7084 setFlag(FLAG_HINT_CONTENT_INTENT_LAUNCHES_ACTIVITY, hintContentIntentLaunchesActivity);
7085 return this;
7086 }
7087
7088 /**
7089 * Get a hint that this notification's content intent will launch an {@link Activity}
7090 * directly, telling the platform that it can generate the appropriate transitions
7091 * @return {@code true} if the content intent will launch an activity and transitions should
7092 * be generated, false otherwise. The default value is {@code false} if this was never set.
7093 */
7094 public boolean getHintContentIntentLaunchesActivity() {
7095 return (mFlags & FLAG_HINT_CONTENT_INTENT_LAUNCHES_ACTIVITY) != 0;
7096 }
7097
Nadia Benbernou948627e2016-04-14 14:41:08 -04007098 /**
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04007099 * Sets the dismissal id for this notification. If a notification is posted with a
7100 * dismissal id, then when that notification is canceled, notifications on other wearables
7101 * and the paired Android phone having that same dismissal id will also be canceled. See
Nadia Benbernou948627e2016-04-14 14:41:08 -04007102 * <a href="{@docRoot}wear/notifications/index.html">Adding Wearable Features to
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04007103 * Notifications</a> for more information.
Nadia Benbernou948627e2016-04-14 14:41:08 -04007104 * @param dismissalId the dismissal id of the notification.
7105 * @return this object for method chaining
7106 */
7107 public WearableExtender setDismissalId(String dismissalId) {
7108 mDismissalId = dismissalId;
7109 return this;
7110 }
7111
7112 /**
7113 * Returns the dismissal id of the notification.
7114 * @return the dismissal id of the notification or null if it has not been set.
7115 */
7116 public String getDismissalId() {
7117 return mDismissalId;
7118 }
7119
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04007120 /**
7121 * Sets a bridge tag for this notification. A bridge tag can be set for notifications
7122 * posted from a phone to provide finer-grained control on what notifications are bridged
7123 * to wearables. See <a href="{@docRoot}wear/notifications/index.html">Adding Wearable
7124 * Features to Notifications</a> for more information.
7125 * @param bridgeTag the bridge tag of the notification.
7126 * @return this object for method chaining
7127 */
7128 public WearableExtender setBridgeTag(String bridgeTag) {
7129 mBridgeTag = bridgeTag;
7130 return this;
7131 }
7132
7133 /**
7134 * Returns the bridge tag of the notification.
7135 * @return the bridge tag or null if not present.
7136 */
7137 public String getBridgeTag() {
7138 return mBridgeTag;
7139 }
7140
Griff Hazen61a9e862014-05-22 16:05:19 -07007141 private void setFlag(int mask, boolean value) {
7142 if (value) {
7143 mFlags |= mask;
7144 } else {
7145 mFlags &= ~mask;
7146 }
7147 }
7148 }
7149
7150 /**
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08007151 * <p>Helper class to add Android Auto extensions to notifications. To create a notification
7152 * with car extensions:
7153 *
7154 * <ol>
7155 * <li>Create an {@link Notification.Builder}, setting any desired
7156 * properties.
7157 * <li>Create a {@link CarExtender}.
7158 * <li>Set car-specific properties using the {@code add} and {@code set} methods of
7159 * {@link CarExtender}.
7160 * <li>Call {@link Notification.Builder#extend(Notification.Extender)}
7161 * to apply the extensions to a notification.
7162 * </ol>
7163 *
7164 * <pre class="prettyprint">
7165 * Notification notification = new Notification.Builder(context)
7166 * ...
7167 * .extend(new CarExtender()
7168 * .set*(...))
7169 * .build();
7170 * </pre>
7171 *
7172 * <p>Car extensions can be accessed on an existing notification by using the
7173 * {@code CarExtender(Notification)} constructor, and then using the {@code get} methods
7174 * to access values.
7175 */
7176 public static final class CarExtender implements Extender {
7177 private static final String TAG = "CarExtender";
7178
7179 private static final String EXTRA_CAR_EXTENDER = "android.car.EXTENSIONS";
7180 private static final String EXTRA_LARGE_ICON = "large_icon";
7181 private static final String EXTRA_CONVERSATION = "car_conversation";
7182 private static final String EXTRA_COLOR = "app_color";
7183
7184 private Bitmap mLargeIcon;
7185 private UnreadConversation mUnreadConversation;
7186 private int mColor = Notification.COLOR_DEFAULT;
7187
7188 /**
7189 * Create a {@link CarExtender} with default options.
7190 */
7191 public CarExtender() {
7192 }
7193
7194 /**
7195 * Create a {@link CarExtender} from the CarExtender options of an existing Notification.
7196 *
7197 * @param notif The notification from which to copy options.
7198 */
7199 public CarExtender(Notification notif) {
7200 Bundle carBundle = notif.extras == null ?
7201 null : notif.extras.getBundle(EXTRA_CAR_EXTENDER);
7202 if (carBundle != null) {
7203 mLargeIcon = carBundle.getParcelable(EXTRA_LARGE_ICON);
7204 mColor = carBundle.getInt(EXTRA_COLOR, Notification.COLOR_DEFAULT);
7205
7206 Bundle b = carBundle.getBundle(EXTRA_CONVERSATION);
7207 mUnreadConversation = UnreadConversation.getUnreadConversationFromBundle(b);
7208 }
7209 }
7210
7211 /**
7212 * Apply car extensions to a notification that is being built. This is typically called by
7213 * the {@link Notification.Builder#extend(Notification.Extender)}
7214 * method of {@link Notification.Builder}.
7215 */
7216 @Override
7217 public Notification.Builder extend(Notification.Builder builder) {
7218 Bundle carExtensions = new Bundle();
7219
7220 if (mLargeIcon != null) {
7221 carExtensions.putParcelable(EXTRA_LARGE_ICON, mLargeIcon);
7222 }
7223 if (mColor != Notification.COLOR_DEFAULT) {
7224 carExtensions.putInt(EXTRA_COLOR, mColor);
7225 }
7226
7227 if (mUnreadConversation != null) {
7228 Bundle b = mUnreadConversation.getBundleForUnreadConversation();
7229 carExtensions.putBundle(EXTRA_CONVERSATION, b);
7230 }
7231
7232 builder.getExtras().putBundle(EXTRA_CAR_EXTENDER, carExtensions);
7233 return builder;
7234 }
7235
7236 /**
7237 * Sets the accent color to use when Android Auto presents the notification.
7238 *
7239 * Android Auto uses the color set with {@link Notification.Builder#setColor(int)}
7240 * to accent the displayed notification. However, not all colors are acceptable in an
7241 * automotive setting. This method can be used to override the color provided in the
7242 * notification in such a situation.
7243 */
Tor Norbye80756e32015-03-02 09:39:27 -08007244 public CarExtender setColor(@ColorInt int color) {
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08007245 mColor = color;
7246 return this;
7247 }
7248
7249 /**
7250 * Gets the accent color.
7251 *
Julia Reynoldsd9228f12015-10-20 10:37:27 -04007252 * @see #setColor
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08007253 */
Tor Norbye80756e32015-03-02 09:39:27 -08007254 @ColorInt
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08007255 public int getColor() {
7256 return mColor;
7257 }
7258
7259 /**
7260 * Sets the large icon of the car notification.
7261 *
7262 * If no large icon is set in the extender, Android Auto will display the icon
7263 * specified by {@link Notification.Builder#setLargeIcon(android.graphics.Bitmap)}
7264 *
7265 * @param largeIcon The large icon to use in the car notification.
7266 * @return This object for method chaining.
7267 */
7268 public CarExtender setLargeIcon(Bitmap largeIcon) {
7269 mLargeIcon = largeIcon;
7270 return this;
7271 }
7272
7273 /**
7274 * Gets the large icon used in this car notification, or null if no icon has been set.
7275 *
7276 * @return The large icon for the car notification.
7277 * @see CarExtender#setLargeIcon
7278 */
7279 public Bitmap getLargeIcon() {
7280 return mLargeIcon;
7281 }
7282
7283 /**
7284 * Sets the unread conversation in a message notification.
7285 *
7286 * @param unreadConversation The unread part of the conversation this notification conveys.
7287 * @return This object for method chaining.
7288 */
7289 public CarExtender setUnreadConversation(UnreadConversation unreadConversation) {
7290 mUnreadConversation = unreadConversation;
7291 return this;
7292 }
7293
7294 /**
7295 * Returns the unread conversation conveyed by this notification.
7296 * @see #setUnreadConversation(UnreadConversation)
7297 */
7298 public UnreadConversation getUnreadConversation() {
7299 return mUnreadConversation;
7300 }
7301
7302 /**
7303 * A class which holds the unread messages from a conversation.
7304 */
7305 public static class UnreadConversation {
7306 private static final String KEY_AUTHOR = "author";
7307 private static final String KEY_TEXT = "text";
7308 private static final String KEY_MESSAGES = "messages";
7309 private static final String KEY_REMOTE_INPUT = "remote_input";
7310 private static final String KEY_ON_REPLY = "on_reply";
7311 private static final String KEY_ON_READ = "on_read";
7312 private static final String KEY_PARTICIPANTS = "participants";
7313 private static final String KEY_TIMESTAMP = "timestamp";
7314
7315 private final String[] mMessages;
7316 private final RemoteInput mRemoteInput;
7317 private final PendingIntent mReplyPendingIntent;
7318 private final PendingIntent mReadPendingIntent;
7319 private final String[] mParticipants;
7320 private final long mLatestTimestamp;
7321
7322 UnreadConversation(String[] messages, RemoteInput remoteInput,
7323 PendingIntent replyPendingIntent, PendingIntent readPendingIntent,
7324 String[] participants, long latestTimestamp) {
7325 mMessages = messages;
7326 mRemoteInput = remoteInput;
7327 mReadPendingIntent = readPendingIntent;
7328 mReplyPendingIntent = replyPendingIntent;
7329 mParticipants = participants;
7330 mLatestTimestamp = latestTimestamp;
7331 }
7332
7333 /**
7334 * Gets the list of messages conveyed by this notification.
7335 */
7336 public String[] getMessages() {
7337 return mMessages;
7338 }
7339
7340 /**
7341 * Gets the remote input that will be used to convey the response to a message list, or
7342 * null if no such remote input exists.
7343 */
7344 public RemoteInput getRemoteInput() {
7345 return mRemoteInput;
7346 }
7347
7348 /**
7349 * Gets the pending intent that will be triggered when the user replies to this
7350 * notification.
7351 */
7352 public PendingIntent getReplyPendingIntent() {
7353 return mReplyPendingIntent;
7354 }
7355
7356 /**
7357 * Gets the pending intent that Android Auto will send after it reads aloud all messages
7358 * in this object's message list.
7359 */
7360 public PendingIntent getReadPendingIntent() {
7361 return mReadPendingIntent;
7362 }
7363
7364 /**
7365 * Gets the participants in the conversation.
7366 */
7367 public String[] getParticipants() {
7368 return mParticipants;
7369 }
7370
7371 /**
7372 * Gets the firs participant in the conversation.
7373 */
7374 public String getParticipant() {
7375 return mParticipants.length > 0 ? mParticipants[0] : null;
7376 }
7377
7378 /**
7379 * Gets the timestamp of the conversation.
7380 */
7381 public long getLatestTimestamp() {
7382 return mLatestTimestamp;
7383 }
7384
7385 Bundle getBundleForUnreadConversation() {
7386 Bundle b = new Bundle();
7387 String author = null;
7388 if (mParticipants != null && mParticipants.length > 1) {
7389 author = mParticipants[0];
7390 }
7391 Parcelable[] messages = new Parcelable[mMessages.length];
7392 for (int i = 0; i < messages.length; i++) {
7393 Bundle m = new Bundle();
7394 m.putString(KEY_TEXT, mMessages[i]);
7395 m.putString(KEY_AUTHOR, author);
7396 messages[i] = m;
7397 }
7398 b.putParcelableArray(KEY_MESSAGES, messages);
7399 if (mRemoteInput != null) {
7400 b.putParcelable(KEY_REMOTE_INPUT, mRemoteInput);
7401 }
7402 b.putParcelable(KEY_ON_REPLY, mReplyPendingIntent);
7403 b.putParcelable(KEY_ON_READ, mReadPendingIntent);
7404 b.putStringArray(KEY_PARTICIPANTS, mParticipants);
7405 b.putLong(KEY_TIMESTAMP, mLatestTimestamp);
7406 return b;
7407 }
7408
7409 static UnreadConversation getUnreadConversationFromBundle(Bundle b) {
7410 if (b == null) {
7411 return null;
7412 }
7413 Parcelable[] parcelableMessages = b.getParcelableArray(KEY_MESSAGES);
7414 String[] messages = null;
7415 if (parcelableMessages != null) {
7416 String[] tmp = new String[parcelableMessages.length];
7417 boolean success = true;
7418 for (int i = 0; i < tmp.length; i++) {
7419 if (!(parcelableMessages[i] instanceof Bundle)) {
7420 success = false;
7421 break;
7422 }
7423 tmp[i] = ((Bundle) parcelableMessages[i]).getString(KEY_TEXT);
7424 if (tmp[i] == null) {
7425 success = false;
7426 break;
7427 }
7428 }
7429 if (success) {
7430 messages = tmp;
7431 } else {
7432 return null;
7433 }
7434 }
7435
7436 PendingIntent onRead = b.getParcelable(KEY_ON_READ);
7437 PendingIntent onReply = b.getParcelable(KEY_ON_REPLY);
7438
7439 RemoteInput remoteInput = b.getParcelable(KEY_REMOTE_INPUT);
7440
7441 String[] participants = b.getStringArray(KEY_PARTICIPANTS);
7442 if (participants == null || participants.length != 1) {
7443 return null;
7444 }
7445
7446 return new UnreadConversation(messages,
7447 remoteInput,
7448 onReply,
7449 onRead,
7450 participants, b.getLong(KEY_TIMESTAMP));
7451 }
7452 };
7453
7454 /**
7455 * Builder class for {@link CarExtender.UnreadConversation} objects.
7456 */
7457 public static class Builder {
7458 private final List<String> mMessages = new ArrayList<String>();
7459 private final String mParticipant;
7460 private RemoteInput mRemoteInput;
7461 private PendingIntent mReadPendingIntent;
7462 private PendingIntent mReplyPendingIntent;
7463 private long mLatestTimestamp;
7464
7465 /**
7466 * Constructs a new builder for {@link CarExtender.UnreadConversation}.
7467 *
7468 * @param name The name of the other participant in the conversation.
7469 */
7470 public Builder(String name) {
7471 mParticipant = name;
7472 }
7473
7474 /**
7475 * Appends a new unread message to the list of messages for this conversation.
7476 *
7477 * The messages should be added from oldest to newest.
7478 *
7479 * @param message The text of the new unread message.
7480 * @return This object for method chaining.
7481 */
7482 public Builder addMessage(String message) {
7483 mMessages.add(message);
7484 return this;
7485 }
7486
7487 /**
7488 * Sets the pending intent and remote input which will convey the reply to this
7489 * notification.
7490 *
7491 * @param pendingIntent The pending intent which will be triggered on a reply.
7492 * @param remoteInput The remote input parcelable which will carry the reply.
7493 * @return This object for method chaining.
7494 *
7495 * @see CarExtender.UnreadConversation#getRemoteInput
7496 * @see CarExtender.UnreadConversation#getReplyPendingIntent
7497 */
7498 public Builder setReplyAction(
7499 PendingIntent pendingIntent, RemoteInput remoteInput) {
7500 mRemoteInput = remoteInput;
7501 mReplyPendingIntent = pendingIntent;
7502
7503 return this;
7504 }
7505
7506 /**
7507 * Sets the pending intent that will be sent once the messages in this notification
7508 * are read.
7509 *
7510 * @param pendingIntent The pending intent to use.
7511 * @return This object for method chaining.
7512 */
7513 public Builder setReadPendingIntent(PendingIntent pendingIntent) {
7514 mReadPendingIntent = pendingIntent;
7515 return this;
7516 }
7517
7518 /**
7519 * Sets the timestamp of the most recent message in an unread conversation.
7520 *
7521 * If a messaging notification has been posted by your application and has not
7522 * yet been cancelled, posting a later notification with the same id and tag
7523 * but without a newer timestamp may result in Android Auto not displaying a
7524 * heads up notification for the later notification.
7525 *
7526 * @param timestamp The timestamp of the most recent message in the conversation.
7527 * @return This object for method chaining.
7528 */
7529 public Builder setLatestTimestamp(long timestamp) {
7530 mLatestTimestamp = timestamp;
7531 return this;
7532 }
7533
7534 /**
7535 * Builds a new unread conversation object.
7536 *
7537 * @return The new unread conversation object.
7538 */
7539 public UnreadConversation build() {
7540 String[] messages = mMessages.toArray(new String[mMessages.size()]);
7541 String[] participants = { mParticipant };
7542 return new UnreadConversation(messages, mRemoteInput, mReplyPendingIntent,
7543 mReadPendingIntent, participants, mLatestTimestamp);
7544 }
7545 }
7546 }
7547
7548 /**
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08007549 * <p>Helper class to add Android TV extensions to notifications. To create a notification
7550 * with a TV extension:
7551 *
7552 * <ol>
7553 * <li>Create an {@link Notification.Builder}, setting any desired properties.
7554 * <li>Create a {@link TvExtender}.
7555 * <li>Set TV-specific properties using the {@code set} methods of
7556 * {@link TvExtender}.
7557 * <li>Call {@link Notification.Builder#extend(Notification.Extender)}
7558 * to apply the extension to a notification.
7559 * </ol>
7560 *
7561 * <pre class="prettyprint">
7562 * Notification notification = new Notification.Builder(context)
7563 * ...
7564 * .extend(new TvExtender()
7565 * .set*(...))
7566 * .build();
7567 * </pre>
7568 *
7569 * <p>TV extensions can be accessed on an existing notification by using the
7570 * {@code TvExtender(Notification)} constructor, and then using the {@code get} methods
7571 * to access values.
7572 *
7573 * @hide
7574 */
7575 @SystemApi
7576 public static final class TvExtender implements Extender {
7577 private static final String TAG = "TvExtender";
7578
7579 private static final String EXTRA_TV_EXTENDER = "android.tv.EXTENSIONS";
7580 private static final String EXTRA_FLAGS = "flags";
7581 private static final String EXTRA_CONTENT_INTENT = "content_intent";
7582 private static final String EXTRA_DELETE_INTENT = "delete_intent";
Dmitri Plotnikovb8a04ee2017-01-20 10:39:22 -08007583 private static final String EXTRA_CHANNEL_ID = "channel_id";
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08007584
7585 // Flags bitwise-ored to mFlags
7586 private static final int FLAG_AVAILABLE_ON_TV = 0x1;
7587
7588 private int mFlags;
Dmitri Plotnikovb8a04ee2017-01-20 10:39:22 -08007589 private String mChannelId;
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08007590 private PendingIntent mContentIntent;
7591 private PendingIntent mDeleteIntent;
7592
7593 /**
7594 * Create a {@link TvExtender} with default options.
7595 */
7596 public TvExtender() {
7597 mFlags = FLAG_AVAILABLE_ON_TV;
7598 }
7599
7600 /**
7601 * Create a {@link TvExtender} from the TvExtender options of an existing Notification.
7602 *
7603 * @param notif The notification from which to copy options.
7604 */
7605 public TvExtender(Notification notif) {
7606 Bundle bundle = notif.extras == null ?
7607 null : notif.extras.getBundle(EXTRA_TV_EXTENDER);
7608 if (bundle != null) {
7609 mFlags = bundle.getInt(EXTRA_FLAGS);
Dmitri Plotnikovb8a04ee2017-01-20 10:39:22 -08007610 mChannelId = bundle.getString(EXTRA_CHANNEL_ID);
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08007611 mContentIntent = bundle.getParcelable(EXTRA_CONTENT_INTENT);
7612 mDeleteIntent = bundle.getParcelable(EXTRA_DELETE_INTENT);
7613 }
7614 }
7615
7616 /**
7617 * Apply a TV extension to a notification that is being built. This is typically called by
7618 * the {@link Notification.Builder#extend(Notification.Extender)}
7619 * method of {@link Notification.Builder}.
7620 */
7621 @Override
7622 public Notification.Builder extend(Notification.Builder builder) {
7623 Bundle bundle = new Bundle();
7624
7625 bundle.putInt(EXTRA_FLAGS, mFlags);
Dmitri Plotnikovb8a04ee2017-01-20 10:39:22 -08007626 bundle.putString(EXTRA_CHANNEL_ID, mChannelId);
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08007627 if (mContentIntent != null) {
7628 bundle.putParcelable(EXTRA_CONTENT_INTENT, mContentIntent);
7629 }
7630
7631 if (mDeleteIntent != null) {
7632 bundle.putParcelable(EXTRA_DELETE_INTENT, mDeleteIntent);
7633 }
7634
7635 builder.getExtras().putBundle(EXTRA_TV_EXTENDER, bundle);
7636 return builder;
7637 }
7638
7639 /**
7640 * Returns true if this notification should be shown on TV. This method return true
7641 * if the notification was extended with a TvExtender.
7642 */
7643 public boolean isAvailableOnTv() {
7644 return (mFlags & FLAG_AVAILABLE_ON_TV) != 0;
7645 }
7646
7647 /**
Dmitri Plotnikovb8a04ee2017-01-20 10:39:22 -08007648 * Specifies the channel the notification should be delivered on when shown on TV.
7649 * It can be different from the channel that the notification is delivered to when
7650 * posting on a non-TV device.
7651 */
7652 public TvExtender setChannel(String channelId) {
7653 mChannelId = channelId;
7654 return this;
7655 }
7656
7657 /**
7658 * Returns the id of the channel this notification posts to on TV.
7659 */
7660 public String getChannel() {
7661 return mChannelId;
7662 }
7663
7664 /**
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08007665 * Supplies a {@link PendingIntent} to be sent when the notification is selected on TV.
7666 * If provided, it is used instead of the content intent specified
7667 * at the level of Notification.
7668 */
7669 public TvExtender setContentIntent(PendingIntent intent) {
7670 mContentIntent = intent;
7671 return this;
7672 }
7673
7674 /**
7675 * Returns the TV-specific content intent. If this method returns null, the
7676 * main content intent on the notification should be used.
7677 *
7678 * @see {@link Notification#contentIntent}
7679 */
7680 public PendingIntent getContentIntent() {
7681 return mContentIntent;
7682 }
7683
7684 /**
7685 * Supplies a {@link PendingIntent} to send when the notification is cleared explicitly
7686 * by the user on TV. If provided, it is used instead of the delete intent specified
7687 * at the level of Notification.
7688 */
7689 public TvExtender setDeleteIntent(PendingIntent intent) {
7690 mDeleteIntent = intent;
7691 return this;
7692 }
7693
7694 /**
7695 * Returns the TV-specific delete intent. If this method returns null, the
7696 * main delete intent on the notification should be used.
7697 *
7698 * @see {@link Notification#deleteIntent}
7699 */
7700 public PendingIntent getDeleteIntent() {
7701 return mDeleteIntent;
7702 }
7703 }
7704
7705 /**
Griff Hazen61a9e862014-05-22 16:05:19 -07007706 * Get an array of Notification objects from a parcelable array bundle field.
7707 * Update the bundle to have a typed array so fetches in the future don't need
7708 * to do an array copy.
7709 */
7710 private static Notification[] getNotificationArrayFromBundle(Bundle bundle, String key) {
7711 Parcelable[] array = bundle.getParcelableArray(key);
7712 if (array instanceof Notification[] || array == null) {
7713 return (Notification[]) array;
7714 }
7715 Notification[] typedArray = Arrays.copyOf(array, array.length,
7716 Notification[].class);
7717 bundle.putParcelableArray(key, typedArray);
7718 return typedArray;
7719 }
Christoph Studer4600f9b2014-07-22 22:44:43 +02007720
7721 private static class BuilderRemoteViews extends RemoteViews {
7722 public BuilderRemoteViews(Parcel parcel) {
7723 super(parcel);
7724 }
7725
Kenny Guy77320062014-08-27 21:37:15 +01007726 public BuilderRemoteViews(ApplicationInfo appInfo, int layoutId) {
7727 super(appInfo, layoutId);
Christoph Studer4600f9b2014-07-22 22:44:43 +02007728 }
7729
7730 @Override
7731 public BuilderRemoteViews clone() {
7732 Parcel p = Parcel.obtain();
7733 writeToParcel(p, 0);
7734 p.setDataPosition(0);
7735 BuilderRemoteViews brv = new BuilderRemoteViews(p);
7736 p.recycle();
7737 return brv;
7738 }
7739 }
Adrian Roos70d7aa32017-01-11 15:39:06 -08007740
7741 private static class StandardTemplateParams {
7742 boolean hasProgress = true;
7743 boolean ambient = false;
7744 CharSequence title;
7745 CharSequence text;
7746
7747 final StandardTemplateParams reset() {
7748 hasProgress = true;
7749 ambient = false;
7750 title = null;
7751 text = null;
7752 return this;
7753 }
7754
7755 final StandardTemplateParams hasProgress(boolean hasProgress) {
7756 this.hasProgress = hasProgress;
7757 return this;
7758 }
7759
7760 final StandardTemplateParams title(CharSequence title) {
7761 this.title = title;
7762 return this;
7763 }
7764
7765 final StandardTemplateParams text(CharSequence text) {
7766 this.text = text;
7767 return this;
7768 }
7769
7770 final StandardTemplateParams ambient(boolean ambient) {
7771 this.ambient = ambient;
7772 return this;
7773 }
7774
7775 final StandardTemplateParams fillTextsFrom(Builder b) {
7776 Bundle extras = b.mN.extras;
7777 title = b.processLegacyText(extras.getCharSequence(EXTRA_TITLE));
7778 text = b.processLegacyText(extras.getCharSequence(EXTRA_TEXT));
7779 return this;
7780 }
7781 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007782}