blob: 34d2039988e688cbbc4ca913de5ef7ba409d92cf [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.app;
18
Tor Norbye80756e32015-03-02 09:39:27 -080019import android.annotation.ColorInt;
Tor Norbye7b9c9122013-05-30 16:48:33 -070020import android.annotation.DrawableRes;
Tor Norbyed9273d62013-05-30 15:59:53 -070021import android.annotation.IntDef;
Alex Hillsfd590442016-10-07 09:52:44 -040022import android.annotation.NonNull;
Daniel Sandler01df1c62014-06-09 10:54:01 -040023import android.annotation.SdkConstant;
24import android.annotation.SdkConstant.SdkConstantType;
Julia Reynoldse46bb372016-03-17 11:05:58 -040025import android.annotation.SystemApi;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080026import android.content.Context;
27import android.content.Intent;
Kenny Guy77320062014-08-27 21:37:15 +010028import android.content.pm.ApplicationInfo;
Dan Sandler732bd6c2016-04-12 14:20:32 -040029import android.content.pm.PackageManager;
Christoph Studer4600f9b2014-07-22 22:44:43 +020030import android.content.pm.PackageManager.NameNotFoundException;
Jorim Jaggief72a192014-08-26 21:57:46 +020031import android.content.res.ColorStateList;
Joe Onoratoef1e7762010-09-17 18:38:38 -040032import android.graphics.Bitmap;
Kenny Guy8a0101b2014-05-08 23:34:12 +010033import android.graphics.Canvas;
Adrian Roosc1a80b02016-04-05 14:54:55 -070034import android.graphics.Color;
Jorim Jaggi5c2d8462014-03-21 17:37:00 +010035import android.graphics.PorterDuff;
Kenny Guy8a0101b2014-05-08 23:34:12 +010036import android.graphics.drawable.Drawable;
Dan Sandlerd63f9322015-05-06 15:18:49 -040037import android.graphics.drawable.Icon;
John Spurlockc0650f022014-07-19 13:22:39 -040038import android.media.AudioAttributes;
Jeff Sharkey098d5802012-04-26 17:30:34 -070039import android.media.AudioManager;
Jean-Michel Trivi2f7511f2016-11-28 15:40:27 -080040import android.media.PlayerBase;
Jeff Browndba34ba2014-06-24 20:46:03 -070041import android.media.session.MediaSession;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080042import android.net.Uri;
Daniel Sandlerdcbaf662013-04-26 16:23:09 -040043import android.os.BadParcelableException;
Christoph Studer239f8352014-08-25 15:13:18 +020044import android.os.Build;
Daniel Sandler2561b0b2012-02-13 21:04:12 -050045import android.os.Bundle;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080046import android.os.Parcel;
47import android.os.Parcelable;
Daniel Sandlera2985ed2012-04-03 16:42:00 -040048import android.os.SystemClock;
Selim Cinek6743c0b2017-01-18 18:24:01 -080049import android.os.SystemProperties;
Jeff Sharkey6d515712012-09-20 16:06:08 -070050import android.os.UserHandle;
Adrian Roosc1a80b02016-04-05 14:54:55 -070051import android.text.BidiFormatter;
Selim Cinek60a54252016-02-26 17:03:25 -080052import android.text.SpannableStringBuilder;
53import android.text.Spanned;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080054import android.text.TextUtils;
Selim Cinek60a54252016-02-26 17:03:25 -080055import android.text.style.AbsoluteSizeSpan;
Selim Cinek981962e2016-07-20 20:41:58 -070056import android.text.style.BackgroundColorSpan;
Selim Cinek89991a22016-03-07 19:51:54 -080057import android.text.style.CharacterStyle;
Selim Cinek981962e2016-07-20 20:41:58 -070058import android.text.style.ForegroundColorSpan;
Selim Cinek60a54252016-02-26 17:03:25 -080059import android.text.style.RelativeSizeSpan;
60import android.text.style.TextAppearanceSpan;
Svet Ganovddb94882016-06-23 19:55:24 -070061import android.util.ArraySet;
Daniel Sandlerdcbaf662013-04-26 16:23:09 -040062import android.util.Log;
Dan Sandler50128532015-12-08 15:42:41 -050063import android.util.SparseArray;
Griff Hazen61a9e862014-05-22 16:05:19 -070064import android.view.Gravity;
Selim Cinekea4bef72015-12-02 15:51:10 -080065import android.view.NotificationHeaderView;
Joe Onorato8595a3d2010-11-19 18:12:07 -080066import android.view.View;
Selim Cinek954cc232016-05-20 13:29:23 -070067import android.view.ViewGroup;
Jeff Sharkey1c400132011-08-05 14:50:13 -070068import android.widget.ProgressBar;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080069import android.widget.RemoteViews;
70
Griff Hazen959591e2014-05-15 22:26:18 -070071import com.android.internal.R;
Svet Ganovddb94882016-06-23 19:55:24 -070072import com.android.internal.util.ArrayUtils;
Griff Hazenc091ba82014-05-16 10:13:26 -070073import com.android.internal.util.NotificationColorUtil;
Griff Hazen959591e2014-05-15 22:26:18 -070074
Tor Norbyed9273d62013-05-30 15:59:53 -070075import java.lang.annotation.Retention;
76import java.lang.annotation.RetentionPolicy;
Christoph Studer4600f9b2014-07-22 22:44:43 +020077import java.lang.reflect.Constructor;
Daniel Sandler2561b0b2012-02-13 21:04:12 -050078import java.util.ArrayList;
Griff Hazen61a9e862014-05-22 16:05:19 -070079import java.util.Arrays;
Griff Hazen5cadc3b2014-05-20 09:55:39 -070080import java.util.Collections;
Griff Hazen61a9e862014-05-22 16:05:19 -070081import java.util.List;
Dan Sandler50128532015-12-08 15:42:41 -050082import java.util.Set;
Joe Onorato561d3852010-11-20 18:09:34 -080083
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080084/**
85 * A class that represents how a persistent notification is to be presented to
86 * the user using the {@link android.app.NotificationManager}.
87 *
Joe Onoratocb109a02011-01-18 17:57:41 -080088 * <p>The {@link Notification.Builder Notification.Builder} has been added to make it
89 * easier to construct Notifications.</p>
90 *
Joe Fernandez558459f2011-10-13 16:47:36 -070091 * <div class="special reference">
92 * <h3>Developer Guides</h3>
93 * <p>For a guide to creating notifications, read the
94 * <a href="{@docRoot}guide/topics/ui/notifiers/notifications.html">Status Bar Notifications</a>
95 * developer guide.</p>
96 * </div>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080097 */
98public class Notification implements Parcelable
99{
Daniel Sandlerdcbaf662013-04-26 16:23:09 -0400100 private static final String TAG = "Notification";
101
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800102 /**
Daniel Sandler01df1c62014-06-09 10:54:01 -0400103 * An activity that provides a user interface for adjusting notification preferences for its
104 * containing application. Optional but recommended for apps that post
105 * {@link android.app.Notification Notifications}.
106 */
107 @SdkConstant(SdkConstantType.INTENT_CATEGORY)
108 public static final String INTENT_CATEGORY_NOTIFICATION_PREFERENCES
109 = "android.intent.category.NOTIFICATION_PREFERENCES";
110
111 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800112 * Use all default values (where applicable).
113 */
114 public static final int DEFAULT_ALL = ~0;
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500115
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800116 /**
117 * Use the default notification sound. This will ignore any given
118 * {@link #sound}.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500119 *
Chris Wren47c20a12014-06-18 17:27:29 -0400120 * <p>
121 * A notification that is noisy is more likely to be presented as a heads-up notification.
122 * </p>
123 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800124 * @see #defaults
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500125 */
126
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800127 public static final int DEFAULT_SOUND = 1;
128
129 /**
130 * Use the default notification vibrate. This will ignore any given
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500131 * {@link #vibrate}. Using phone vibration requires the
Scott Mainb8b36452009-04-26 15:50:49 -0700132 * {@link android.Manifest.permission#VIBRATE VIBRATE} permission.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500133 *
Chris Wren47c20a12014-06-18 17:27:29 -0400134 * <p>
135 * A notification that vibrates is more likely to be presented as a heads-up notification.
136 * </p>
137 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800138 * @see #defaults
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500139 */
140
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800141 public static final int DEFAULT_VIBRATE = 2;
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500142
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800143 /**
144 * Use the default notification lights. This will ignore the
145 * {@link #FLAG_SHOW_LIGHTS} bit, and {@link #ledARGB}, {@link #ledOffMS}, or
146 * {@link #ledOnMS}.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500147 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800148 * @see #defaults
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500149 */
150
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800151 public static final int DEFAULT_LIGHTS = 4;
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500152
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800153 /**
Christoph Studer535ec612014-09-03 15:47:47 +0200154 * Maximum length of CharSequences accepted by Builder and friends.
155 *
156 * <p>
157 * Avoids spamming the system with overly large strings such as full e-mails.
158 */
159 private static final int MAX_CHARSEQUENCE_LENGTH = 5 * 1024;
160
161 /**
Adrian Roose458aa82015-12-08 16:17:19 -0800162 * Maximum entries of reply text that are accepted by Builder and friends.
163 */
164 private static final int MAX_REPLY_HISTORY = 5;
165
166 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500167 * A timestamp related to this notification, in milliseconds since the epoch.
Joe Malin8d40d042012-11-05 11:36:40 -0800168 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500169 * Default value: {@link System#currentTimeMillis() Now}.
170 *
171 * Choose a timestamp that will be most relevant to the user. For most finite events, this
172 * corresponds to the time the event happened (or will happen, in the case of events that have
173 * yet to occur but about which the user is being informed). Indefinite events should be
Joe Malin8d40d042012-11-05 11:36:40 -0800174 * timestamped according to when the activity began.
175 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500176 * Some examples:
Joe Malin8d40d042012-11-05 11:36:40 -0800177 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500178 * <ul>
179 * <li>Notification of a new chat message should be stamped when the message was received.</li>
180 * <li>Notification of an ongoing file download (with a progress bar, for example) should be stamped when the download started.</li>
181 * <li>Notification of a completed file download should be stamped when the download finished.</li>
182 * <li>Notification of an upcoming meeting should be stamped with the time the meeting will begin (that is, in the future).</li>
183 * <li>Notification of an ongoing stopwatch (increasing timer) should be stamped with the watch's start time.
184 * <li>Notification of an ongoing countdown timer should be stamped with the timer's end time.
Joe Malin8d40d042012-11-05 11:36:40 -0800185 * </ul>
186 *
Selim Cinek0ff1ce602016-04-05 18:27:16 -0700187 * For apps targeting {@link android.os.Build.VERSION_CODES#N} and above, this time is not shown
188 * anymore by default and must be opted into by using
189 * {@link android.app.Notification.Builder#setShowWhen(boolean)}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800190 */
191 public long when;
192
193 /**
Selim Cinekb85f36fd2016-04-20 18:46:36 -0700194 * The creation time of the notification
195 */
196 private long creationTime;
197
198 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800199 * The resource id of a drawable to use as the icon in the status bar.
Dan Sandler86647982015-05-13 23:41:13 -0400200 *
201 * @deprecated Use {@link Builder#setSmallIcon(Icon)} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800202 */
Dan Sandler86647982015-05-13 23:41:13 -0400203 @Deprecated
Tor Norbye7b9c9122013-05-30 16:48:33 -0700204 @DrawableRes
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800205 public int icon;
206
207 /**
Joe Onorato46439ce2010-11-19 13:56:21 -0800208 * If the icon in the status bar is to have more than one level, you can set this. Otherwise,
209 * leave it at its default value of 0.
210 *
211 * @see android.widget.ImageView#setImageLevel
Griff Hazen959591e2014-05-15 22:26:18 -0700212 * @see android.graphics.drawable.Drawable#setLevel
Joe Onorato46439ce2010-11-19 13:56:21 -0800213 */
214 public int iconLevel;
215
216 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500217 * The number of events that this notification represents. For example, in a new mail
218 * notification, this could be the number of unread messages.
Joe Malin8d40d042012-11-05 11:36:40 -0800219 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500220 * The system may or may not use this field to modify the appearance of the notification. For
221 * example, before {@link android.os.Build.VERSION_CODES#HONEYCOMB}, this number was
222 * superimposed over the icon in the status bar. Starting with
223 * {@link android.os.Build.VERSION_CODES#HONEYCOMB}, the template used by
224 * {@link Notification.Builder} has displayed the number in the expanded notification view.
Joe Malin8d40d042012-11-05 11:36:40 -0800225 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500226 * If the number is 0 or negative, it is never shown.
Selim Cinek0f9dd1e2016-04-05 17:03:40 -0700227 *
228 * @deprecated this number is not shown anymore
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800229 */
Aurimas Liutikas514c5ef2016-05-24 15:22:55 -0700230 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800231 public int number;
232
233 /**
234 * The intent to execute when the expanded status entry is clicked. If
235 * this is an activity, it must include the
236 * {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} flag, which requires
Scott Main7aee61f2011-02-08 11:25:01 -0800237 * that you take care of task management as described in the
238 * <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back
Dianne Hackborn6ceca582012-01-10 15:24:26 -0800239 * Stack</a> document. In particular, make sure to read the notification section
240 * <a href="{@docRoot}guide/topics/ui/notifiers/notifications.html#HandlingNotifications">Handling
241 * Notifications</a> for the correct ways to launch an application from a
242 * notification.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800243 */
244 public PendingIntent contentIntent;
245
246 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500247 * The intent to execute when the notification is explicitly dismissed by the user, either with
248 * the "Clear All" button or by swiping it away individually.
249 *
250 * This probably shouldn't be launching an activity since several of those will be sent
251 * at the same time.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800252 */
253 public PendingIntent deleteIntent;
254
255 /**
Dianne Hackborn170bae72010-09-03 15:14:28 -0700256 * An intent to launch instead of posting the notification to the status bar.
Joe Onoratocb109a02011-01-18 17:57:41 -0800257 *
Chris Wren47c20a12014-06-18 17:27:29 -0400258 * <p>
259 * The system UI may choose to display a heads-up notification, instead of
260 * launching this intent, while the user is using the device.
261 * </p>
262 *
Joe Onoratocb109a02011-01-18 17:57:41 -0800263 * @see Notification.Builder#setFullScreenIntent
Daniel Sandlere46cbd32010-06-17 10:35:26 -0400264 */
265 public PendingIntent fullScreenIntent;
266
267 /**
Dan Sandler5fcdf6e2014-07-18 11:31:15 -0400268 * Text that summarizes this notification for accessibility services.
269 *
270 * As of the L release, this text is no longer shown on screen, but it is still useful to
271 * accessibility services (where it serves as an audible announcement of the notification's
272 * appearance).
Joe Onoratoef1e7762010-09-17 18:38:38 -0400273 *
Joe Onorato46439ce2010-11-19 13:56:21 -0800274 * @see #tickerView
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800275 */
276 public CharSequence tickerText;
277
278 /**
Dan Sandler5fcdf6e2014-07-18 11:31:15 -0400279 * Formerly, a view showing the {@link #tickerText}.
280 *
281 * No longer displayed in the status bar as of API 21.
Joe Onoratoef1e7762010-09-17 18:38:38 -0400282 */
Dan Sandler5fcdf6e2014-07-18 11:31:15 -0400283 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -0800284 public RemoteViews tickerView;
Joe Onoratoef1e7762010-09-17 18:38:38 -0400285
286 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400287 * The view that will represent this notification in the notification list (which is pulled
288 * down from the status bar).
289 *
Julia Reynoldsd4ea7412016-02-17 14:00:56 -0500290 * As of N, this field may be null. The notification view is determined by the inputs
291 * to {@link Notification.Builder}; a custom RemoteViews can optionally be
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400292 * supplied with {@link Notification.Builder#setCustomContentView(RemoteViews)}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800293 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400294 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800295 public RemoteViews contentView;
296
Daniel Sandlera0a938c2012-03-15 08:42:37 -0400297 /**
Daniel Sandler4dfbe832012-04-11 14:51:46 -0400298 * A large-format version of {@link #contentView}, giving the Notification an
Daniel Sandlerf3b73432012-03-27 15:01:25 -0400299 * opportunity to show more detail. The system UI may choose to show this
300 * instead of the normal content view at its discretion.
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400301 *
Julia Reynoldsd4ea7412016-02-17 14:00:56 -0500302 * As of N, this field may be null. The expanded notification view is determined by the
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400303 * inputs to {@link Notification.Builder}; a custom RemoteViews can optionally be
304 * supplied with {@link Notification.Builder#setCustomBigContentView(RemoteViews)}.
Daniel Sandlerf3b73432012-03-27 15:01:25 -0400305 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400306 @Deprecated
Daniel Sandlerf3b73432012-03-27 15:01:25 -0400307 public RemoteViews bigContentView;
308
Chris Wren8fd39ec2014-02-27 17:43:26 -0500309
310 /**
Chris Wren47c20a12014-06-18 17:27:29 -0400311 * A medium-format version of {@link #contentView}, providing the Notification an
312 * opportunity to add action buttons to contentView. At its discretion, the system UI may
313 * choose to show this as a heads-up notification, which will pop up so the user can see
314 * it without leaving their current activity.
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400315 *
Julia Reynoldsd4ea7412016-02-17 14:00:56 -0500316 * As of N, this field may be null. The heads-up notification view is determined by the
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400317 * inputs to {@link Notification.Builder}; a custom RemoteViews can optionally be
318 * supplied with {@link Notification.Builder#setCustomHeadsUpContentView(RemoteViews)}.
Chris Wren8fd39ec2014-02-27 17:43:26 -0500319 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400320 @Deprecated
Chris Wren8fd39ec2014-02-27 17:43:26 -0500321 public RemoteViews headsUpContentView;
322
Daniel Sandlerf3b73432012-03-27 15:01:25 -0400323 /**
Dan Sandler86647982015-05-13 23:41:13 -0400324 * A large bitmap to be shown in the notification content area.
325 *
326 * @deprecated Use {@link Builder#setLargeIcon(Icon)} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800327 */
Dan Sandler86647982015-05-13 23:41:13 -0400328 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -0800329 public Bitmap largeIcon;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800330
331 /**
332 * The sound to play.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500333 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800334 * <p>
Chris Wren47c20a12014-06-18 17:27:29 -0400335 * A notification that is noisy is more likely to be presented as a heads-up notification.
336 * </p>
337 *
338 * <p>
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500339 * To play the default notification sound, see {@link #defaults}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800340 * </p>
341 */
342 public Uri sound;
343
344 /**
345 * Use this constant as the value for audioStreamType to request that
346 * the default stream type for notifications be used. Currently the
Jeff Sharkey098d5802012-04-26 17:30:34 -0700347 * default stream type is {@link AudioManager#STREAM_NOTIFICATION}.
John Spurlockc0650f022014-07-19 13:22:39 -0400348 *
349 * @deprecated Use {@link #audioAttributes} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800350 */
Jean-Michel Trivi81f871e2014-08-06 16:32:38 -0700351 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800352 public static final int STREAM_DEFAULT = -1;
353
354 /**
355 * The audio stream type to use when playing the sound.
356 * Should be one of the STREAM_ constants from
357 * {@link android.media.AudioManager}.
John Spurlockc0650f022014-07-19 13:22:39 -0400358 *
359 * @deprecated Use {@link #audioAttributes} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800360 */
Jean-Michel Trivi81f871e2014-08-06 16:32:38 -0700361 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800362 public int audioStreamType = STREAM_DEFAULT;
363
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800364 /**
John Spurlockc0650f022014-07-19 13:22:39 -0400365 * The default value of {@link #audioAttributes}.
366 */
367 public static final AudioAttributes AUDIO_ATTRIBUTES_DEFAULT = new AudioAttributes.Builder()
368 .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
369 .setUsage(AudioAttributes.USAGE_NOTIFICATION)
370 .build();
371
372 /**
373 * The {@link AudioAttributes audio attributes} to use when playing the sound.
374 */
375 public AudioAttributes audioAttributes = AUDIO_ATTRIBUTES_DEFAULT;
376
377 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500378 * The pattern with which to vibrate.
379 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800380 * <p>
381 * To vibrate the default pattern, see {@link #defaults}.
382 * </p>
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500383 *
Chris Wren47c20a12014-06-18 17:27:29 -0400384 * <p>
385 * A notification that vibrates is more likely to be presented as a heads-up notification.
386 * </p>
387 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800388 * @see android.os.Vibrator#vibrate(long[],int)
389 */
390 public long[] vibrate;
391
392 /**
393 * The color of the led. The hardware will do its best approximation.
394 *
395 * @see #FLAG_SHOW_LIGHTS
396 * @see #flags
397 */
Tor Norbye80756e32015-03-02 09:39:27 -0800398 @ColorInt
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800399 public int ledARGB;
400
401 /**
402 * The number of milliseconds for the LED to be on while it's flashing.
403 * The hardware will do its best approximation.
404 *
405 * @see #FLAG_SHOW_LIGHTS
406 * @see #flags
407 */
408 public int ledOnMS;
409
410 /**
411 * The number of milliseconds for the LED to be off while it's flashing.
412 * The hardware will do its best approximation.
413 *
414 * @see #FLAG_SHOW_LIGHTS
415 * @see #flags
416 */
417 public int ledOffMS;
418
419 /**
420 * Specifies which values should be taken from the defaults.
421 * <p>
422 * To set, OR the desired from {@link #DEFAULT_SOUND},
423 * {@link #DEFAULT_VIBRATE}, {@link #DEFAULT_LIGHTS}. For all default
424 * values, use {@link #DEFAULT_ALL}.
425 * </p>
426 */
427 public int defaults;
428
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800429 /**
430 * Bit to be bitwise-ored into the {@link #flags} field that should be
431 * set if you want the LED on for this notification.
432 * <ul>
433 * <li>To turn the LED off, pass 0 in the alpha channel for colorARGB
434 * or 0 for both ledOnMS and ledOffMS.</li>
435 * <li>To turn the LED on, pass 1 for ledOnMS and 0 for ledOffMS.</li>
436 * <li>To flash the LED, pass the number of milliseconds that it should
437 * be on and off to ledOnMS and ledOffMS.</li>
438 * </ul>
439 * <p>
440 * Since hardware varies, you are not guaranteed that any of the values
441 * you pass are honored exactly. Use the system defaults (TODO) if possible
442 * because they will be set to values that work on any given hardware.
443 * <p>
444 * The alpha channel must be set for forward compatibility.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500445 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800446 */
447 public static final int FLAG_SHOW_LIGHTS = 0x00000001;
448
449 /**
450 * Bit to be bitwise-ored into the {@link #flags} field that should be
451 * set if this notification is in reference to something that is ongoing,
452 * like a phone call. It should not be set if this notification is in
453 * reference to something that happened at a particular point in time,
454 * like a missed phone call.
455 */
456 public static final int FLAG_ONGOING_EVENT = 0x00000002;
457
458 /**
459 * Bit to be bitwise-ored into the {@link #flags} field that if set,
Scott Mainb8b36452009-04-26 15:50:49 -0700460 * the audio will be repeated until the notification is
461 * cancelled or the notification window is opened.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800462 */
463 public static final int FLAG_INSISTENT = 0x00000004;
464
465 /**
466 * Bit to be bitwise-ored into the {@link #flags} field that should be
Griff Hazen293977b2014-04-28 08:37:20 -0700467 * set if you would only like the sound, vibrate and ticker to be played
468 * if the notification was not already showing.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800469 */
470 public static final int FLAG_ONLY_ALERT_ONCE = 0x00000008;
471
472 /**
473 * Bit to be bitwise-ored into the {@link #flags} field that should be
474 * set if the notification should be canceled when it is clicked by the
Daniel Sandler8aa9ae62012-12-04 23:31:47 -0500475 * user.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800476 */
477 public static final int FLAG_AUTO_CANCEL = 0x00000010;
478
479 /**
480 * Bit to be bitwise-ored into the {@link #flags} field that should be
481 * set if the notification should not be canceled when the user clicks
482 * the Clear all button.
483 */
484 public static final int FLAG_NO_CLEAR = 0x00000020;
485
Dianne Hackbornd8a43f62009-08-17 23:33:56 -0700486 /**
487 * Bit to be bitwise-ored into the {@link #flags} field that should be
488 * set if this notification represents a currently running service. This
489 * will normally be set for you by {@link Service#startForeground}.
490 */
491 public static final int FLAG_FOREGROUND_SERVICE = 0x00000040;
492
Daniel Sandlere46cbd32010-06-17 10:35:26 -0400493 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500494 * Obsolete flag indicating high-priority notifications; use the priority field instead.
Joe Malin8d40d042012-11-05 11:36:40 -0800495 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500496 * @deprecated Use {@link #priority} with a positive value.
Daniel Sandlere46cbd32010-06-17 10:35:26 -0400497 */
Aurimas Liutikas514c5ef2016-05-24 15:22:55 -0700498 @Deprecated
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500499 public static final int FLAG_HIGH_PRIORITY = 0x00000080;
Daniel Sandlere46cbd32010-06-17 10:35:26 -0400500
Griff Hazendfcb0802014-02-11 12:00:00 -0800501 /**
502 * Bit to be bitswise-ored into the {@link #flags} field that should be
503 * set if this notification is relevant to the current device only
504 * and it is not recommended that it bridge to other devices.
505 */
506 public static final int FLAG_LOCAL_ONLY = 0x00000100;
507
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700508 /**
509 * Bit to be bitswise-ored into the {@link #flags} field that should be
510 * set if this notification is the group summary for a group of notifications.
511 * Grouped notifications may display in a cluster or stack on devices which
512 * support such rendering. Requires a group key also be set using {@link Builder#setGroup}.
513 */
514 public static final int FLAG_GROUP_SUMMARY = 0x00000200;
515
Julia Reynoldse46bb372016-03-17 11:05:58 -0400516 /**
517 * Bit to be bitswise-ored into the {@link #flags} field that should be
518 * set if this notification is the group summary for an auto-group of notifications.
519 *
520 * @hide
521 */
522 @SystemApi
523 public static final int FLAG_AUTOGROUP_SUMMARY = 0x00000400;
524
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800525 public int flags;
526
Tor Norbyed9273d62013-05-30 15:59:53 -0700527 /** @hide */
528 @IntDef({PRIORITY_DEFAULT,PRIORITY_LOW,PRIORITY_MIN,PRIORITY_HIGH,PRIORITY_MAX})
529 @Retention(RetentionPolicy.SOURCE)
530 public @interface Priority {}
531
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800532 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500533 * Default notification {@link #priority}. If your application does not prioritize its own
534 * notifications, use this value for all notifications.
535 */
536 public static final int PRIORITY_DEFAULT = 0;
537
538 /**
539 * Lower {@link #priority}, for items that are less important. The UI may choose to show these
540 * items smaller, or at a different position in the list, compared with your app's
541 * {@link #PRIORITY_DEFAULT} items.
542 */
543 public static final int PRIORITY_LOW = -1;
544
545 /**
546 * Lowest {@link #priority}; these items might not be shown to the user except under special
547 * circumstances, such as detailed notification logs.
548 */
549 public static final int PRIORITY_MIN = -2;
550
551 /**
552 * Higher {@link #priority}, for more important notifications or alerts. The UI may choose to
553 * show these items larger, or at a different position in notification lists, compared with
554 * your app's {@link #PRIORITY_DEFAULT} items.
555 */
556 public static final int PRIORITY_HIGH = 1;
557
558 /**
559 * Highest {@link #priority}, for your application's most important items that require the
560 * user's prompt attention or input.
561 */
562 public static final int PRIORITY_MAX = 2;
563
564 /**
565 * Relative priority for this notification.
Joe Malin8d40d042012-11-05 11:36:40 -0800566 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500567 * Priority is an indication of how much of the user's valuable attention should be consumed by
568 * this notification. Low-priority notifications may be hidden from the user in certain
569 * situations, while the user might be interrupted for a higher-priority notification. The
Daniel Sandler6738eee2012-11-16 12:03:32 -0500570 * system will make a determination about how to interpret this priority when presenting
571 * the notification.
Chris Wren47c20a12014-06-18 17:27:29 -0400572 *
573 * <p>
574 * A notification that is at least {@link #PRIORITY_HIGH} is more likely to be presented
575 * as a heads-up notification.
576 * </p>
577 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500578 */
Tor Norbyed9273d62013-05-30 15:59:53 -0700579 @Priority
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500580 public int priority;
Joe Malin8d40d042012-11-05 11:36:40 -0800581
Dan Sandler26e81cf2014-05-06 10:01:27 -0400582 /**
583 * Accent color (an ARGB integer like the constants in {@link android.graphics.Color})
584 * to be applied by the standard Style templates when presenting this notification.
585 *
586 * The current template design constructs a colorful header image by overlaying the
587 * {@link #icon} image (stenciled in white) atop a field of this color. Alpha components are
588 * ignored.
589 */
Tor Norbye80756e32015-03-02 09:39:27 -0800590 @ColorInt
Dan Sandler26e81cf2014-05-06 10:01:27 -0400591 public int color = COLOR_DEFAULT;
592
593 /**
594 * Special value of {@link #color} telling the system not to decorate this notification with
595 * any special color but instead use default colors when presenting this notification.
596 */
Tor Norbye80756e32015-03-02 09:39:27 -0800597 @ColorInt
Dan Sandler26e81cf2014-05-06 10:01:27 -0400598 public static final int COLOR_DEFAULT = 0; // AKA Color.TRANSPARENT
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600599
600 /**
Adrian Roos4ff3b122016-02-01 12:26:13 -0800601 * Special value of {@link #color} used as a place holder for an invalid color.
602 */
603 @ColorInt
604 private static final int COLOR_INVALID = 1;
605
606 /**
Adrian Roosc1a80b02016-04-05 14:54:55 -0700607 * Sphere of visibility of this notification, which affects how and when the SystemUI reveals
608 * the notification's presence and contents in untrusted situations (namely, on the secure
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600609 * lockscreen).
610 *
611 * The default level, {@link #VISIBILITY_PRIVATE}, behaves exactly as notifications have always
612 * done on Android: The notification's {@link #icon} and {@link #tickerText} (if available) are
613 * shown in all situations, but the contents are only available if the device is unlocked for
614 * the appropriate user.
615 *
616 * A more permissive policy can be expressed by {@link #VISIBILITY_PUBLIC}; such a notification
617 * can be read even in an "insecure" context (that is, above a secure lockscreen).
618 * To modify the public version of this notification—for example, to redact some portions—see
619 * {@link Builder#setPublicVersion(Notification)}.
620 *
621 * Finally, a notification can be made {@link #VISIBILITY_SECRET}, which will suppress its icon
622 * and ticker until the user has bypassed the lockscreen.
623 */
624 public int visibility;
625
Griff Hazenfc3922d2014-08-20 11:56:44 -0700626 /**
627 * Notification visibility: Show this notification in its entirety on all lockscreens.
628 *
629 * {@see #visibility}
630 */
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600631 public static final int VISIBILITY_PUBLIC = 1;
Griff Hazenfc3922d2014-08-20 11:56:44 -0700632
633 /**
634 * Notification visibility: Show this notification on all lockscreens, but conceal sensitive or
635 * private information on secure lockscreens.
636 *
637 * {@see #visibility}
638 */
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600639 public static final int VISIBILITY_PRIVATE = 0;
Griff Hazenfc3922d2014-08-20 11:56:44 -0700640
641 /**
642 * Notification visibility: Do not reveal any part of this notification on a secure lockscreen.
643 *
644 * {@see #visibility}
645 */
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600646 public static final int VISIBILITY_SECRET = -1;
647
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500648 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400649 * Notification category: incoming call (voice or video) or similar synchronous communication request.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500650 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400651 public static final String CATEGORY_CALL = "call";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500652
653 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400654 * Notification category: incoming direct message (SMS, instant message, etc.).
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500655 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400656 public static final String CATEGORY_MESSAGE = "msg";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500657
658 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400659 * Notification category: asynchronous bulk message (email).
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500660 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400661 public static final String CATEGORY_EMAIL = "email";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500662
663 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400664 * Notification category: calendar event.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500665 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400666 public static final String CATEGORY_EVENT = "event";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500667
668 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400669 * Notification category: promotion or advertisement.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500670 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400671 public static final String CATEGORY_PROMO = "promo";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500672
673 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400674 * Notification category: alarm or timer.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500675 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400676 public static final String CATEGORY_ALARM = "alarm";
677
678 /**
679 * Notification category: progress of a long-running background operation.
680 */
681 public static final String CATEGORY_PROGRESS = "progress";
682
683 /**
684 * Notification category: social network or sharing update.
685 */
686 public static final String CATEGORY_SOCIAL = "social";
687
688 /**
689 * Notification category: error in background operation or authentication status.
690 */
691 public static final String CATEGORY_ERROR = "err";
692
693 /**
694 * Notification category: media transport control for playback.
695 */
696 public static final String CATEGORY_TRANSPORT = "transport";
697
698 /**
699 * Notification category: system or device status update. Reserved for system use.
700 */
701 public static final String CATEGORY_SYSTEM = "sys";
702
703 /**
704 * Notification category: indication of running background service.
705 */
706 public static final String CATEGORY_SERVICE = "service";
707
708 /**
John Spurlock0a69c8c2014-03-21 13:30:57 -0400709 * Notification category: a specific, timely recommendation for a single thing.
710 * For example, a news app might want to recommend a news story it believes the user will
711 * want to read next.
712 */
713 public static final String CATEGORY_RECOMMENDATION = "recommendation";
714
715 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400716 * Notification category: ongoing information about device or contextual status.
717 */
718 public static final String CATEGORY_STATUS = "status";
719
720 /**
John Spurlock24d3dad2015-04-02 12:24:02 -0400721 * Notification category: user-scheduled reminder.
722 */
723 public static final String CATEGORY_REMINDER = "reminder";
724
725 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400726 * One of the predefined notification categories (see the <code>CATEGORY_*</code> constants)
727 * that best describes this Notification. May be used by the system for ranking and filtering.
728 */
729 public String category;
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500730
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700731 private String mGroupKey;
732
733 /**
734 * Get the key used to group this notification into a cluster or stack
735 * with other notifications on devices which support such rendering.
736 */
737 public String getGroup() {
738 return mGroupKey;
739 }
740
741 private String mSortKey;
742
743 /**
744 * Get a sort key that orders this notification among other notifications from the
745 * same package. This can be useful if an external sort was already applied and an app
746 * would like to preserve this. Notifications will be sorted lexicographically using this
747 * value, although providing different priorities in addition to providing sort key may
748 * cause this value to be ignored.
749 *
750 * <p>This sort key can also be used to order members of a notification group. See
751 * {@link Builder#setGroup}.
752 *
753 * @see String#compareTo(String)
754 */
755 public String getSortKey() {
756 return mSortKey;
757 }
758
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500759 /**
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400760 * Additional semantic data to be carried around with this Notification.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400761 * <p>
762 * The extras keys defined here are intended to capture the original inputs to {@link Builder}
763 * APIs, and are intended to be used by
764 * {@link android.service.notification.NotificationListenerService} implementations to extract
765 * detailed information from notification objects.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500766 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400767 public Bundle extras = new Bundle();
768
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400769 /**
Felipe Lemedd85da62016-06-28 11:29:54 -0700770 * All pending intents in the notification as the system needs to be able to access them but
771 * touching the extras bundle in the system process is not safe because the bundle may contain
Svet Ganovddb94882016-06-23 19:55:24 -0700772 * custom parcelable objects.
773 *
774 * @hide
775 */
Felipe Lemedd85da62016-06-28 11:29:54 -0700776 public ArraySet<PendingIntent> allPendingIntents;
Svet Ganovddb94882016-06-23 19:55:24 -0700777
778 /**
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400779 * {@link #extras} key: this is the title of the notification,
780 * as supplied to {@link Builder#setContentTitle(CharSequence)}.
781 */
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -0500782 public static final String EXTRA_TITLE = "android.title";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400783
784 /**
785 * {@link #extras} key: this is the title of the notification when shown in expanded form,
786 * e.g. as supplied to {@link BigTextStyle#setBigContentTitle(CharSequence)}.
787 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400788 public static final String EXTRA_TITLE_BIG = EXTRA_TITLE + ".big";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400789
790 /**
791 * {@link #extras} key: this is the main text payload, as supplied to
792 * {@link Builder#setContentText(CharSequence)}.
793 */
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -0500794 public static final String EXTRA_TEXT = "android.text";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400795
796 /**
797 * {@link #extras} key: this is a third line of text, as supplied to
798 * {@link Builder#setSubText(CharSequence)}.
799 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400800 public static final String EXTRA_SUB_TEXT = "android.subText";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400801
802 /**
Adrian Roose458aa82015-12-08 16:17:19 -0800803 * {@link #extras} key: this is the remote input history, as supplied to
804 * {@link Builder#setRemoteInputHistory(CharSequence[])}.
Adrian Roos005e7742016-04-13 15:02:20 -0700805 *
806 * Apps can fill this through {@link Builder#setRemoteInputHistory(CharSequence[])}
807 * with the most recent inputs that have been sent through a {@link RemoteInput} of this
808 * Notification and are expected to clear it once the it is no longer relevant (e.g. for chat
809 * notifications once the other party has responded).
810 *
811 * The extra with this key is of type CharSequence[] and contains the most recent entry at
812 * the 0 index, the second most recent at the 1 index, etc.
813 *
814 * @see Builder#setRemoteInputHistory(CharSequence[])
Adrian Roose458aa82015-12-08 16:17:19 -0800815 */
816 public static final String EXTRA_REMOTE_INPUT_HISTORY = "android.remoteInputHistory";
817
818 /**
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400819 * {@link #extras} key: this is a small piece of additional text as supplied to
820 * {@link Builder#setContentInfo(CharSequence)}.
821 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400822 public static final String EXTRA_INFO_TEXT = "android.infoText";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400823
824 /**
825 * {@link #extras} key: this is a line of summary information intended to be shown
826 * alongside expanded notifications, as supplied to (e.g.)
827 * {@link BigTextStyle#setSummaryText(CharSequence)}.
828 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400829 public static final String EXTRA_SUMMARY_TEXT = "android.summaryText";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400830
831 /**
Christoph Studer4600f9b2014-07-22 22:44:43 +0200832 * {@link #extras} key: this is the longer text shown in the big form of a
833 * {@link BigTextStyle} notification, as supplied to
834 * {@link BigTextStyle#bigText(CharSequence)}.
835 */
836 public static final String EXTRA_BIG_TEXT = "android.bigText";
837
838 /**
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400839 * {@link #extras} key: this is the resource ID of the notification's main small icon, as
840 * supplied to {@link Builder#setSmallIcon(int)}.
841 */
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -0500842 public static final String EXTRA_SMALL_ICON = "android.icon";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400843
844 /**
845 * {@link #extras} key: this is a bitmap to be used instead of the small icon when showing the
846 * notification payload, as
847 * supplied to {@link Builder#setLargeIcon(android.graphics.Bitmap)}.
848 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400849 public static final String EXTRA_LARGE_ICON = "android.largeIcon";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400850
851 /**
852 * {@link #extras} key: this is a bitmap to be used instead of the one from
853 * {@link Builder#setLargeIcon(android.graphics.Bitmap)} when the notification is
854 * shown in its expanded form, as supplied to
855 * {@link BigPictureStyle#bigLargeIcon(android.graphics.Bitmap)}.
856 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400857 public static final String EXTRA_LARGE_ICON_BIG = EXTRA_LARGE_ICON + ".big";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400858
859 /**
860 * {@link #extras} key: this is the progress value supplied to
861 * {@link Builder#setProgress(int, int, boolean)}.
862 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400863 public static final String EXTRA_PROGRESS = "android.progress";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400864
865 /**
866 * {@link #extras} key: this is the maximum value supplied to
867 * {@link Builder#setProgress(int, int, boolean)}.
868 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400869 public static final String EXTRA_PROGRESS_MAX = "android.progressMax";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400870
871 /**
872 * {@link #extras} key: whether the progress bar is indeterminate, supplied to
873 * {@link Builder#setProgress(int, int, boolean)}.
874 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400875 public static final String EXTRA_PROGRESS_INDETERMINATE = "android.progressIndeterminate";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400876
877 /**
878 * {@link #extras} key: whether {@link #when} should be shown as a count-up timer (specifically
879 * a {@link android.widget.Chronometer}) instead of a timestamp, as supplied to
880 * {@link Builder#setUsesChronometer(boolean)}.
881 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400882 public static final String EXTRA_SHOW_CHRONOMETER = "android.showChronometer";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400883
884 /**
Selim Cinek81c23aa2016-02-25 16:23:13 -0800885 * {@link #extras} key: whether the chronometer set on the notification should count down
886 * instead of counting up. Is only relevant if key {@link #EXTRA_SHOW_CHRONOMETER} is present.
Adrian Roos96b7e202016-05-17 13:50:38 -0700887 * This extra is a boolean. The default is false.
Selim Cinek81c23aa2016-02-25 16:23:13 -0800888 */
Adrian Roos96b7e202016-05-17 13:50:38 -0700889 public static final String EXTRA_CHRONOMETER_COUNT_DOWN = "android.chronometerCountDown";
Selim Cinek81c23aa2016-02-25 16:23:13 -0800890
891 /**
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400892 * {@link #extras} key: whether {@link #when} should be shown,
893 * as supplied to {@link Builder#setShowWhen(boolean)}.
894 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400895 public static final String EXTRA_SHOW_WHEN = "android.showWhen";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400896
897 /**
898 * {@link #extras} key: this is a bitmap to be shown in {@link BigPictureStyle} expanded
899 * notifications, supplied to {@link BigPictureStyle#bigPicture(android.graphics.Bitmap)}.
900 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400901 public static final String EXTRA_PICTURE = "android.picture";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400902
903 /**
904 * {@link #extras} key: An array of CharSequences to show in {@link InboxStyle} expanded
905 * notifications, each of which was supplied to {@link InboxStyle#addLine(CharSequence)}.
906 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400907 public static final String EXTRA_TEXT_LINES = "android.textLines";
Dan Sandler842dd772014-05-15 09:36:47 -0400908
909 /**
910 * {@link #extras} key: A string representing the name of the specific
911 * {@link android.app.Notification.Style} used to create this notification.
912 */
Chris Wren91ad5632013-06-05 15:05:57 -0400913 public static final String EXTRA_TEMPLATE = "android.template";
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400914
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400915 /**
Chris Wrene6c48932014-09-29 17:19:27 -0400916 * {@link #extras} key: A String array containing the people that this notification relates to,
917 * each of which was supplied to {@link Builder#addPerson(String)}.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400918 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400919 public static final String EXTRA_PEOPLE = "android.people";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500920
921 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400922 * Allow certain system-generated notifications to appear before the device is provisioned.
923 * Only available to notifications coming from the android package.
924 * @hide
925 */
926 public static final String EXTRA_ALLOW_DURING_SETUP = "android.allowDuringSetup";
927
928 /**
Jose Limae9e3b3b2014-05-18 23:44:50 -0700929 * {@link #extras} key: A
930 * {@link android.content.ContentUris content URI} pointing to an image that can be displayed
931 * in the background when the notification is selected. The URI must point to an image stream
932 * suitable for passing into
933 * {@link android.graphics.BitmapFactory#decodeStream(java.io.InputStream)
934 * BitmapFactory.decodeStream}; all other content types will be ignored. The content provider
935 * URI used for this purpose must require no permissions to read the image data.
936 */
937 public static final String EXTRA_BACKGROUND_IMAGE_URI = "android.backgroundImageUri";
938
939 /**
Dan Sandler842dd772014-05-15 09:36:47 -0400940 * {@link #extras} key: A
Jeff Browndba34ba2014-06-24 20:46:03 -0700941 * {@link android.media.session.MediaSession.Token} associated with a
Dan Sandler842dd772014-05-15 09:36:47 -0400942 * {@link android.app.Notification.MediaStyle} notification.
943 */
944 public static final String EXTRA_MEDIA_SESSION = "android.mediaSession";
945
946 /**
Bryan Mawhinneye191f902014-07-22 12:50:09 +0100947 * {@link #extras} key: the indices of actions to be shown in the compact view,
948 * as supplied to (e.g.) {@link MediaStyle#setShowActionsInCompactView(int...)}.
949 */
950 public static final String EXTRA_COMPACT_ACTIONS = "android.compactActions";
951
Christoph Studer943aa672014-08-03 20:31:16 +0200952 /**
Alex Hillsfc737de2016-03-23 17:33:02 -0400953 * {@link #extras} key: the username to be displayed for all messages sent by the user including
954 * direct replies
Adrian Roos96b7e202016-05-17 13:50:38 -0700955 * {@link android.app.Notification.MessagingStyle} notification. This extra is a
956 * {@link CharSequence}
Alex Hillsfc737de2016-03-23 17:33:02 -0400957 */
958 public static final String EXTRA_SELF_DISPLAY_NAME = "android.selfDisplayName";
959
960 /**
Adrian Roos96b7e202016-05-17 13:50:38 -0700961 * {@link #extras} key: a {@link CharSequence} to be displayed as the title to a conversation
Alex Hillsd9b04d92016-04-11 16:38:16 -0400962 * represented by a {@link android.app.Notification.MessagingStyle}
Alex Hillsfc737de2016-03-23 17:33:02 -0400963 */
Alex Hillsd9b04d92016-04-11 16:38:16 -0400964 public static final String EXTRA_CONVERSATION_TITLE = "android.conversationTitle";
Alex Hillsfc737de2016-03-23 17:33:02 -0400965
966 /**
967 * {@link #extras} key: an array of {@link android.app.Notification.MessagingStyle.Message}
968 * bundles provided by a
Adrian Roos96b7e202016-05-17 13:50:38 -0700969 * {@link android.app.Notification.MessagingStyle} notification. This extra is a parcelable
970 * array of bundles.
Alex Hillsfc737de2016-03-23 17:33:02 -0400971 */
972 public static final String EXTRA_MESSAGES = "android.messages";
973
974 /**
Adrian Roos437cd562017-01-18 15:47:03 -0800975 * {@link #extras} key: an array of
976 * {@link android.app.Notification.MessagingStyle#addHistoricMessage historic}
977 * {@link android.app.Notification.MessagingStyle.Message} bundles provided by a
978 * {@link android.app.Notification.MessagingStyle} notification. This extra is a parcelable
979 * array of bundles.
980 */
981 public static final String EXTRA_HISTORIC_MESSAGES = "android.messages.historic";
982
983 /**
Selim Cinek7b9605b2017-01-19 17:36:00 -0800984 * {@link #extras} key: whether the notification should be colorized as
985 * supplied to {@link Builder#setColorized(boolean)}}.
986 */
987 public static final String EXTRA_COLORIZED = "android.colorized";
988
989 /**
Kenny Guy8942bcd2014-09-08 21:09:47 +0100990 * {@link #extras} key: the user that built the notification.
991 *
992 * @hide
993 */
994 public static final String EXTRA_ORIGINATING_USERID = "android.originatingUserId";
995
996 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400997 * @hide
998 */
999 public static final String EXTRA_BUILDER_APPLICATION_INFO = "android.appInfo";
1000
Selim Cinek247fa012016-02-18 09:50:48 -08001001 /**
1002 * @hide
1003 */
1004 public static final String EXTRA_CONTAINS_CUSTOM_VIEW = "android.contains.customView";
1005
Shane Brennan472a3b32016-12-12 15:28:10 -08001006 /**
1007 * {@link #extras} key: the audio contents of this notification.
1008 *
1009 * This is for use when rendering the notification on an audio-focused interface;
1010 * the audio contents are a complete sound sample that contains the contents/body of the
1011 * notification. This may be used in substitute of a Text-to-Speech reading of the
1012 * notification. For example if the notification represents a voice message this should point
1013 * to the audio of that message.
1014 *
1015 * The data stored under this key should be a String representation of a Uri that contains the
1016 * audio contents in one of the following formats: WAV, PCM 16-bit, AMR-WB.
1017 *
1018 * This extra is unnecessary if you are using {@code MessagingStyle} since each {@code Message}
1019 * has a field for holding data URI. That field can be used for audio.
1020 * See {@code Message#setData}.
1021 *
1022 * Example usage:
1023 * <pre>
1024 * {@code
1025 * Notification.Builder myBuilder = (build your Notification as normal);
1026 * myBuilder.getExtras().putString(EXTRA_AUDIO_CONTENTS_URI, myAudioUri.toString());
1027 * }
1028 * </pre>
1029 */
1030 public static final String EXTRA_AUDIO_CONTENTS_URI = "android.audioContents";
1031
Dan Sandler80eaa592016-04-14 23:34:54 -04001032 /** @hide */
1033 @SystemApi
Dan Sandler732bd6c2016-04-12 14:20:32 -04001034 public static final String EXTRA_SUBSTITUTE_APP_NAME = "android.substName";
1035
Dan Sandlerd63f9322015-05-06 15:18:49 -04001036 private Icon mSmallIcon;
1037 private Icon mLargeIcon;
1038
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04001039 private String mChannelId;
Julia Reynolds2a128742016-11-28 14:29:25 -05001040 private long mTimeout;
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04001041
Chris Wren51c75102013-07-16 20:49:17 -04001042 /**
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001043 * Structure to encapsulate a named action that can be shown as part of this notification.
1044 * It must include an icon, a label, and a {@link PendingIntent} to be fired when the action is
1045 * selected by the user.
1046 * <p>
Griff Hazen959591e2014-05-15 22:26:18 -07001047 * Apps should use {@link Notification.Builder#addAction(int, CharSequence, PendingIntent)}
1048 * or {@link Notification.Builder#addAction(Notification.Action)}
1049 * to attach actions.
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001050 */
Daniel Sandlerea2a3172013-02-20 22:24:20 -05001051 public static class Action implements Parcelable {
Shane Brennan472a3b32016-12-12 15:28:10 -08001052 /**
1053 * {@link #extras} key: Keys to a {@link Parcelable} {@link ArrayList} of
1054 * {@link RemoteInput}s.
1055 *
1056 * This is intended for {@link RemoteInput}s that only accept data, meaning
1057 * {@link RemoteInput#getAllowFreeFormInput} is false, {@link RemoteInput#getChoices}
1058 * is null or empty, and {@link RemoteInput#getAllowedDataTypes is non-null and not
1059 * empty. These {@link RemoteInput}s will be ignored by devices that do not
1060 * support non-text-based {@link RemoteInput}s. See {@link Builder#build}.
1061 *
1062 * You can test if a RemoteInput matches these constraints using
1063 * {@link RemoteInput#isDataOnly}.
1064 */
1065 private static final String EXTRA_DATA_ONLY_INPUTS = "android.extra.DATA_ONLY_INPUTS";
1066
Griff Hazen959591e2014-05-15 22:26:18 -07001067 private final Bundle mExtras;
Dan Sandler86647982015-05-13 23:41:13 -04001068 private Icon mIcon;
Griff Hazen61a9e862014-05-22 16:05:19 -07001069 private final RemoteInput[] mRemoteInputs;
Alex Hills1f27f882017-01-12 11:24:46 -05001070 private boolean mAllowGeneratedReplies = true;
Griff Hazen959591e2014-05-15 22:26:18 -07001071
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001072 /**
1073 * Small icon representing the action.
Dan Sandler86647982015-05-13 23:41:13 -04001074 *
1075 * @deprecated Use {@link Action#getIcon()} instead.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001076 */
Dan Sandler86647982015-05-13 23:41:13 -04001077 @Deprecated
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001078 public int icon;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001079
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001080 /**
1081 * Title of the action.
1082 */
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001083 public CharSequence title;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001084
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001085 /**
1086 * Intent to send when the user invokes this action. May be null, in which case the action
1087 * may be rendered in a disabled presentation by the system UI.
1088 */
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001089 public PendingIntent actionIntent;
Griff Hazen959591e2014-05-15 22:26:18 -07001090
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001091 private Action(Parcel in) {
Dan Sandler86647982015-05-13 23:41:13 -04001092 if (in.readInt() != 0) {
1093 mIcon = Icon.CREATOR.createFromParcel(in);
Dan Sandler68079d52015-07-22 10:45:30 -04001094 if (mIcon.getType() == Icon.TYPE_RESOURCE) {
1095 icon = mIcon.getResId();
1096 }
Dan Sandler86647982015-05-13 23:41:13 -04001097 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001098 title = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
1099 if (in.readInt() == 1) {
1100 actionIntent = PendingIntent.CREATOR.createFromParcel(in);
1101 }
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001102 mExtras = Bundle.setDefusable(in.readBundle(), true);
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001103 mRemoteInputs = in.createTypedArray(RemoteInput.CREATOR);
Alex Hills42b0c4d2016-04-26 13:35:36 -04001104 mAllowGeneratedReplies = in.readInt() == 1;
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001105 }
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001106
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001107 /**
Dan Sandler86647982015-05-13 23:41:13 -04001108 * @deprecated Use {@link android.app.Notification.Action.Builder}.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001109 */
Dan Sandler86647982015-05-13 23:41:13 -04001110 @Deprecated
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001111 public Action(int icon, CharSequence title, PendingIntent intent) {
Alex Hills1f27f882017-01-12 11:24:46 -05001112 this(Icon.createWithResource("", icon), title, intent, new Bundle(), null, true);
Griff Hazen959591e2014-05-15 22:26:18 -07001113 }
1114
Adrian Roos7af53622016-10-12 13:44:05 -07001115 /** Keep in sync with {@link Notification.Action.Builder#Builder(Action)}! */
Dan Sandler86647982015-05-13 23:41:13 -04001116 private Action(Icon icon, CharSequence title, PendingIntent intent, Bundle extras,
Alex Hills42b0c4d2016-04-26 13:35:36 -04001117 RemoteInput[] remoteInputs, boolean allowGeneratedReplies) {
Dan Sandler86647982015-05-13 23:41:13 -04001118 this.mIcon = icon;
Gus Prevasf5bff46f2015-08-24 10:34:28 -04001119 if (icon != null && icon.getType() == Icon.TYPE_RESOURCE) {
1120 this.icon = icon.getResId();
1121 }
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001122 this.title = title;
1123 this.actionIntent = intent;
Griff Hazen959591e2014-05-15 22:26:18 -07001124 this.mExtras = extras != null ? extras : new Bundle();
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001125 this.mRemoteInputs = remoteInputs;
Alex Hills42b0c4d2016-04-26 13:35:36 -04001126 this.mAllowGeneratedReplies = allowGeneratedReplies;
Griff Hazen959591e2014-05-15 22:26:18 -07001127 }
1128
1129 /**
Dan Sandler86647982015-05-13 23:41:13 -04001130 * Return an icon representing the action.
1131 */
1132 public Icon getIcon() {
1133 if (mIcon == null && icon != 0) {
1134 // you snuck an icon in here without using the builder; let's try to keep it
1135 mIcon = Icon.createWithResource("", icon);
1136 }
1137 return mIcon;
1138 }
1139
1140 /**
Griff Hazen959591e2014-05-15 22:26:18 -07001141 * Get additional metadata carried around with this Action.
1142 */
1143 public Bundle getExtras() {
1144 return mExtras;
1145 }
1146
1147 /**
Alex Hills42b0c4d2016-04-26 13:35:36 -04001148 * Return whether the platform should automatically generate possible replies for this
1149 * {@link Action}
1150 */
1151 public boolean getAllowGeneratedReplies() {
1152 return mAllowGeneratedReplies;
1153 }
1154
1155 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001156 * Get the list of inputs to be collected from the user when this action is sent.
Shane Brennan472a3b32016-12-12 15:28:10 -08001157 * May return null if no remote inputs were added. Only returns inputs which accept
1158 * a text input. For inputs which only accept data use {@link #getDataOnlyRemoteInputs}.
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001159 */
1160 public RemoteInput[] getRemoteInputs() {
1161 return mRemoteInputs;
1162 }
1163
1164 /**
Shane Brennan472a3b32016-12-12 15:28:10 -08001165 * Get the list of inputs to be collected from the user that ONLY accept data when this
1166 * action is sent. These remote inputs are guaranteed to return true on a call to
1167 * {@link RemoteInput#isDataOnly}.
1168 *
1169 * May return null if no data-only remote inputs were added.
1170 *
1171 * This method exists so that legacy RemoteInput collectors that pre-date the addition
1172 * of non-textual RemoteInputs do not access these remote inputs.
1173 */
1174 public RemoteInput[] getDataOnlyRemoteInputs() {
1175 return (RemoteInput[]) mExtras.getParcelableArray(EXTRA_DATA_ONLY_INPUTS);
1176 }
1177
1178 /**
Griff Hazen959591e2014-05-15 22:26:18 -07001179 * Builder class for {@link Action} objects.
1180 */
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001181 public static final class Builder {
Dan Sandler86647982015-05-13 23:41:13 -04001182 private final Icon mIcon;
Griff Hazen959591e2014-05-15 22:26:18 -07001183 private final CharSequence mTitle;
1184 private final PendingIntent mIntent;
Alex Hills1f27f882017-01-12 11:24:46 -05001185 private boolean mAllowGeneratedReplies = true;
Griff Hazen959591e2014-05-15 22:26:18 -07001186 private final Bundle mExtras;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001187 private ArrayList<RemoteInput> mRemoteInputs;
Griff Hazen959591e2014-05-15 22:26:18 -07001188
1189 /**
1190 * Construct a new builder for {@link Action} object.
1191 * @param icon icon to show for this action
1192 * @param title the title of the action
1193 * @param intent the {@link PendingIntent} to fire when users trigger this action
1194 */
Dan Sandler86647982015-05-13 23:41:13 -04001195 @Deprecated
Griff Hazen959591e2014-05-15 22:26:18 -07001196 public Builder(int icon, CharSequence title, PendingIntent intent) {
Adrian Roos7af53622016-10-12 13:44:05 -07001197 this(Icon.createWithResource("", icon), title, intent);
Dan Sandler86647982015-05-13 23:41:13 -04001198 }
1199
1200 /**
1201 * Construct a new builder for {@link Action} object.
1202 * @param icon icon to show for this action
1203 * @param title the title of the action
1204 * @param intent the {@link PendingIntent} to fire when users trigger this action
1205 */
1206 public Builder(Icon icon, CharSequence title, PendingIntent intent) {
Alex Hills1f27f882017-01-12 11:24:46 -05001207 this(icon, title, intent, new Bundle(), null, true);
Griff Hazen959591e2014-05-15 22:26:18 -07001208 }
1209
1210 /**
1211 * Construct a new builder for {@link Action} object using the fields from an
1212 * {@link Action}.
1213 * @param action the action to read fields from.
1214 */
1215 public Builder(Action action) {
Adrian Roos7af53622016-10-12 13:44:05 -07001216 this(action.getIcon(), action.title, action.actionIntent,
1217 new Bundle(action.mExtras), action.getRemoteInputs(),
1218 action.getAllowGeneratedReplies());
Griff Hazen959591e2014-05-15 22:26:18 -07001219 }
1220
Dan Sandler86647982015-05-13 23:41:13 -04001221 private Builder(Icon icon, CharSequence title, PendingIntent intent, Bundle extras,
Adrian Roos7af53622016-10-12 13:44:05 -07001222 RemoteInput[] remoteInputs, boolean allowGeneratedReplies) {
Griff Hazen959591e2014-05-15 22:26:18 -07001223 mIcon = icon;
1224 mTitle = title;
1225 mIntent = intent;
1226 mExtras = extras;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001227 if (remoteInputs != null) {
1228 mRemoteInputs = new ArrayList<RemoteInput>(remoteInputs.length);
1229 Collections.addAll(mRemoteInputs, remoteInputs);
1230 }
Adrian Roos7af53622016-10-12 13:44:05 -07001231 mAllowGeneratedReplies = allowGeneratedReplies;
Griff Hazen959591e2014-05-15 22:26:18 -07001232 }
1233
1234 /**
1235 * Merge additional metadata into this builder.
1236 *
1237 * <p>Values within the Bundle will replace existing extras values in this Builder.
1238 *
1239 * @see Notification.Action#extras
1240 */
1241 public Builder addExtras(Bundle extras) {
1242 if (extras != null) {
1243 mExtras.putAll(extras);
1244 }
1245 return this;
1246 }
1247
1248 /**
1249 * Get the metadata Bundle used by this Builder.
1250 *
1251 * <p>The returned Bundle is shared with this Builder.
1252 */
1253 public Bundle getExtras() {
1254 return mExtras;
1255 }
1256
1257 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001258 * Add an input to be collected from the user when this action is sent.
1259 * Response values can be retrieved from the fired intent by using the
1260 * {@link RemoteInput#getResultsFromIntent} function.
1261 * @param remoteInput a {@link RemoteInput} to add to the action
1262 * @return this object for method chaining
1263 */
1264 public Builder addRemoteInput(RemoteInput remoteInput) {
1265 if (mRemoteInputs == null) {
1266 mRemoteInputs = new ArrayList<RemoteInput>();
1267 }
1268 mRemoteInputs.add(remoteInput);
1269 return this;
1270 }
1271
1272 /**
Alex Hills42b0c4d2016-04-26 13:35:36 -04001273 * Set whether the platform should automatically generate possible replies to add to
1274 * {@link RemoteInput#getChoices()}. If the {@link Action} doesn't have a
1275 * {@link RemoteInput}, this has no effect.
1276 * @param allowGeneratedReplies {@code true} to allow generated replies, {@code false}
1277 * otherwise
1278 * @return this object for method chaining
Alex Hills1f27f882017-01-12 11:24:46 -05001279 * The default value is {@code true}
Alex Hills42b0c4d2016-04-26 13:35:36 -04001280 */
1281 public Builder setAllowGeneratedReplies(boolean allowGeneratedReplies) {
1282 mAllowGeneratedReplies = allowGeneratedReplies;
1283 return this;
1284 }
1285
1286 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001287 * Apply an extender to this action builder. Extenders may be used to add
1288 * metadata or change options on this builder.
1289 */
Griff Hazen61a9e862014-05-22 16:05:19 -07001290 public Builder extend(Extender extender) {
1291 extender.extend(this);
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001292 return this;
1293 }
1294
1295 /**
Griff Hazen959591e2014-05-15 22:26:18 -07001296 * Combine all of the options that have been set and return a new {@link Action}
1297 * object.
1298 * @return the built action
1299 */
1300 public Action build() {
Shane Brennan472a3b32016-12-12 15:28:10 -08001301 ArrayList<RemoteInput> dataOnlyInputs = new ArrayList<>();
1302 RemoteInput[] previousDataInputs =
1303 (RemoteInput[]) mExtras.getParcelableArray(EXTRA_DATA_ONLY_INPUTS);
Felipe Lemedfd11962017-01-18 18:38:46 -08001304 if (previousDataInputs != null) {
Shane Brennan472a3b32016-12-12 15:28:10 -08001305 for (RemoteInput input : previousDataInputs) {
1306 dataOnlyInputs.add(input);
1307 }
1308 }
1309 List<RemoteInput> textInputs = new ArrayList<>();
1310 if (mRemoteInputs != null) {
1311 for (RemoteInput input : mRemoteInputs) {
1312 if (input.isDataOnly()) {
1313 dataOnlyInputs.add(input);
1314 } else {
1315 textInputs.add(input);
1316 }
1317 }
1318 }
1319 if (!dataOnlyInputs.isEmpty()) {
1320 RemoteInput[] dataInputsArr =
1321 dataOnlyInputs.toArray(new RemoteInput[dataOnlyInputs.size()]);
1322 mExtras.putParcelableArray(EXTRA_DATA_ONLY_INPUTS, dataInputsArr);
1323 }
1324 RemoteInput[] textInputsArr = textInputs.isEmpty()
1325 ? null : textInputs.toArray(new RemoteInput[textInputs.size()]);
1326 return new Action(mIcon, mTitle, mIntent, mExtras, textInputsArr,
Alex Hills42b0c4d2016-04-26 13:35:36 -04001327 mAllowGeneratedReplies);
Griff Hazen959591e2014-05-15 22:26:18 -07001328 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001329 }
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001330
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001331 @Override
1332 public Action clone() {
1333 return new Action(
Dan Sandler86647982015-05-13 23:41:13 -04001334 getIcon(),
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001335 title,
1336 actionIntent, // safe to alias
Julia Reynolds53c934c2016-09-16 14:03:43 -04001337 mExtras == null ? new Bundle() : new Bundle(mExtras),
Alex Hills42b0c4d2016-04-26 13:35:36 -04001338 getRemoteInputs(),
1339 getAllowGeneratedReplies());
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001340 }
1341 @Override
1342 public int describeContents() {
1343 return 0;
1344 }
1345 @Override
1346 public void writeToParcel(Parcel out, int flags) {
Dan Sandler86647982015-05-13 23:41:13 -04001347 final Icon ic = getIcon();
1348 if (ic != null) {
1349 out.writeInt(1);
1350 ic.writeToParcel(out, 0);
1351 } else {
1352 out.writeInt(0);
1353 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001354 TextUtils.writeToParcel(title, out, flags);
1355 if (actionIntent != null) {
1356 out.writeInt(1);
1357 actionIntent.writeToParcel(out, flags);
1358 } else {
1359 out.writeInt(0);
1360 }
Griff Hazen959591e2014-05-15 22:26:18 -07001361 out.writeBundle(mExtras);
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001362 out.writeTypedArray(mRemoteInputs, flags);
Alex Hills42b0c4d2016-04-26 13:35:36 -04001363 out.writeInt(mAllowGeneratedReplies ? 1 : 0);
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001364 }
Griff Hazen959591e2014-05-15 22:26:18 -07001365 public static final Parcelable.Creator<Action> CREATOR =
1366 new Parcelable.Creator<Action>() {
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001367 public Action createFromParcel(Parcel in) {
1368 return new Action(in);
1369 }
1370 public Action[] newArray(int size) {
1371 return new Action[size];
1372 }
1373 };
Griff Hazen61a9e862014-05-22 16:05:19 -07001374
1375 /**
1376 * Extender interface for use with {@link Builder#extend}. Extenders may be used to add
1377 * metadata or change options on an action builder.
1378 */
1379 public interface Extender {
1380 /**
1381 * Apply this extender to a notification action builder.
1382 * @param builder the builder to be modified.
1383 * @return the build object for chaining.
1384 */
1385 public Builder extend(Builder builder);
1386 }
1387
1388 /**
1389 * Wearable extender for notification actions. To add extensions to an action,
1390 * create a new {@link android.app.Notification.Action.WearableExtender} object using
1391 * the {@code WearableExtender()} constructor and apply it to a
1392 * {@link android.app.Notification.Action.Builder} using
1393 * {@link android.app.Notification.Action.Builder#extend}.
1394 *
1395 * <pre class="prettyprint">
1396 * Notification.Action action = new Notification.Action.Builder(
1397 * R.drawable.archive_all, "Archive all", actionIntent)
Griff Hazen14f57992014-05-26 09:07:14 -07001398 * .extend(new Notification.Action.WearableExtender()
Griff Hazen61a9e862014-05-22 16:05:19 -07001399 * .setAvailableOffline(false))
Griff Hazen14f57992014-05-26 09:07:14 -07001400 * .build();</pre>
Griff Hazen61a9e862014-05-22 16:05:19 -07001401 */
1402 public static final class WearableExtender implements Extender {
1403 /** Notification action extra which contains wearable extensions */
1404 private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS";
1405
Pete Gastaf6781d2014-10-07 15:17:05 -04001406 // Keys within EXTRA_WEARABLE_EXTENSIONS for wearable options.
Griff Hazen61a9e862014-05-22 16:05:19 -07001407 private static final String KEY_FLAGS = "flags";
Pete Gastaf6781d2014-10-07 15:17:05 -04001408 private static final String KEY_IN_PROGRESS_LABEL = "inProgressLabel";
1409 private static final String KEY_CONFIRM_LABEL = "confirmLabel";
1410 private static final String KEY_CANCEL_LABEL = "cancelLabel";
Griff Hazen61a9e862014-05-22 16:05:19 -07001411
1412 // Flags bitwise-ored to mFlags
1413 private static final int FLAG_AVAILABLE_OFFLINE = 0x1;
Alex Hills9ab3a232016-04-05 14:54:56 -04001414 private static final int FLAG_HINT_LAUNCHES_ACTIVITY = 1 << 1;
Alex Hills9f087612016-06-07 09:08:59 -04001415 private static final int FLAG_HINT_DISPLAY_INLINE = 1 << 2;
Griff Hazen61a9e862014-05-22 16:05:19 -07001416
1417 // Default value for flags integer
1418 private static final int DEFAULT_FLAGS = FLAG_AVAILABLE_OFFLINE;
1419
1420 private int mFlags = DEFAULT_FLAGS;
1421
Pete Gastaf6781d2014-10-07 15:17:05 -04001422 private CharSequence mInProgressLabel;
1423 private CharSequence mConfirmLabel;
1424 private CharSequence mCancelLabel;
1425
Griff Hazen61a9e862014-05-22 16:05:19 -07001426 /**
1427 * Create a {@link android.app.Notification.Action.WearableExtender} with default
1428 * options.
1429 */
1430 public WearableExtender() {
1431 }
1432
1433 /**
1434 * Create a {@link android.app.Notification.Action.WearableExtender} by reading
1435 * wearable options present in an existing notification action.
1436 * @param action the notification action to inspect.
1437 */
1438 public WearableExtender(Action action) {
1439 Bundle wearableBundle = action.getExtras().getBundle(EXTRA_WEARABLE_EXTENSIONS);
1440 if (wearableBundle != null) {
1441 mFlags = wearableBundle.getInt(KEY_FLAGS, DEFAULT_FLAGS);
Pete Gastaf6781d2014-10-07 15:17:05 -04001442 mInProgressLabel = wearableBundle.getCharSequence(KEY_IN_PROGRESS_LABEL);
1443 mConfirmLabel = wearableBundle.getCharSequence(KEY_CONFIRM_LABEL);
1444 mCancelLabel = wearableBundle.getCharSequence(KEY_CANCEL_LABEL);
Griff Hazen61a9e862014-05-22 16:05:19 -07001445 }
1446 }
1447
1448 /**
1449 * Apply wearable extensions to a notification action that is being built. This is
1450 * typically called by the {@link android.app.Notification.Action.Builder#extend}
1451 * method of {@link android.app.Notification.Action.Builder}.
1452 */
1453 @Override
1454 public Action.Builder extend(Action.Builder builder) {
1455 Bundle wearableBundle = new Bundle();
1456
1457 if (mFlags != DEFAULT_FLAGS) {
1458 wearableBundle.putInt(KEY_FLAGS, mFlags);
1459 }
Pete Gastaf6781d2014-10-07 15:17:05 -04001460 if (mInProgressLabel != null) {
1461 wearableBundle.putCharSequence(KEY_IN_PROGRESS_LABEL, mInProgressLabel);
1462 }
1463 if (mConfirmLabel != null) {
1464 wearableBundle.putCharSequence(KEY_CONFIRM_LABEL, mConfirmLabel);
1465 }
1466 if (mCancelLabel != null) {
1467 wearableBundle.putCharSequence(KEY_CANCEL_LABEL, mCancelLabel);
1468 }
Griff Hazen61a9e862014-05-22 16:05:19 -07001469
1470 builder.getExtras().putBundle(EXTRA_WEARABLE_EXTENSIONS, wearableBundle);
1471 return builder;
1472 }
1473
1474 @Override
1475 public WearableExtender clone() {
1476 WearableExtender that = new WearableExtender();
1477 that.mFlags = this.mFlags;
Pete Gastaf6781d2014-10-07 15:17:05 -04001478 that.mInProgressLabel = this.mInProgressLabel;
1479 that.mConfirmLabel = this.mConfirmLabel;
1480 that.mCancelLabel = this.mCancelLabel;
Griff Hazen61a9e862014-05-22 16:05:19 -07001481 return that;
1482 }
1483
1484 /**
1485 * Set whether this action is available when the wearable device is not connected to
1486 * a companion device. The user can still trigger this action when the wearable device is
1487 * offline, but a visual hint will indicate that the action may not be available.
1488 * Defaults to true.
1489 */
1490 public WearableExtender setAvailableOffline(boolean availableOffline) {
1491 setFlag(FLAG_AVAILABLE_OFFLINE, availableOffline);
1492 return this;
1493 }
1494
1495 /**
1496 * Get whether this action is available when the wearable device is not connected to
1497 * a companion device. The user can still trigger this action when the wearable device is
1498 * offline, but a visual hint will indicate that the action may not be available.
1499 * Defaults to true.
1500 */
1501 public boolean isAvailableOffline() {
1502 return (mFlags & FLAG_AVAILABLE_OFFLINE) != 0;
1503 }
1504
1505 private void setFlag(int mask, boolean value) {
1506 if (value) {
1507 mFlags |= mask;
1508 } else {
1509 mFlags &= ~mask;
1510 }
1511 }
Pete Gastaf6781d2014-10-07 15:17:05 -04001512
1513 /**
1514 * Set a label to display while the wearable is preparing to automatically execute the
1515 * action. This is usually a 'ing' verb ending in ellipsis like "Sending..."
1516 *
1517 * @param label the label to display while the action is being prepared to execute
1518 * @return this object for method chaining
1519 */
1520 public WearableExtender setInProgressLabel(CharSequence label) {
1521 mInProgressLabel = label;
1522 return this;
1523 }
1524
1525 /**
1526 * Get the label to display while the wearable is preparing to automatically execute
1527 * the action. This is usually a 'ing' verb ending in ellipsis like "Sending..."
1528 *
1529 * @return the label to display while the action is being prepared to execute
1530 */
1531 public CharSequence getInProgressLabel() {
1532 return mInProgressLabel;
1533 }
1534
1535 /**
1536 * Set a label to display to confirm that the action should be executed.
1537 * This is usually an imperative verb like "Send".
1538 *
1539 * @param label the label to confirm the action should be executed
1540 * @return this object for method chaining
1541 */
1542 public WearableExtender setConfirmLabel(CharSequence label) {
1543 mConfirmLabel = label;
1544 return this;
1545 }
1546
1547 /**
1548 * Get the label to display to confirm that the action should be executed.
1549 * This is usually an imperative verb like "Send".
1550 *
1551 * @return the label to confirm the action should be executed
1552 */
1553 public CharSequence getConfirmLabel() {
1554 return mConfirmLabel;
1555 }
1556
1557 /**
1558 * Set a label to display to cancel the action.
1559 * This is usually an imperative verb, like "Cancel".
1560 *
1561 * @param label the label to display to cancel the action
1562 * @return this object for method chaining
1563 */
1564 public WearableExtender setCancelLabel(CharSequence label) {
1565 mCancelLabel = label;
1566 return this;
1567 }
1568
1569 /**
1570 * Get the label to display to cancel the action.
1571 * This is usually an imperative verb like "Cancel".
1572 *
1573 * @return the label to display to cancel the action
1574 */
1575 public CharSequence getCancelLabel() {
1576 return mCancelLabel;
1577 }
Alex Hills9ab3a232016-04-05 14:54:56 -04001578
1579 /**
1580 * Set a hint that this Action will launch an {@link Activity} directly, telling the
1581 * platform that it can generate the appropriate transitions.
1582 * @param hintLaunchesActivity {@code true} if the content intent will launch
1583 * an activity and transitions should be generated, false otherwise.
1584 * @return this object for method chaining
1585 */
Alex Hills4ec3ff42016-04-12 11:36:18 -04001586 public WearableExtender setHintLaunchesActivity(
Alex Hills9ab3a232016-04-05 14:54:56 -04001587 boolean hintLaunchesActivity) {
1588 setFlag(FLAG_HINT_LAUNCHES_ACTIVITY, hintLaunchesActivity);
1589 return this;
1590 }
1591
1592 /**
1593 * Get a hint that this Action will launch an {@link Activity} directly, telling the
1594 * platform that it can generate the appropriate transitions
1595 * @return {@code true} if the content intent will launch an activity and transitions
1596 * should be generated, false otherwise. The default value is {@code false} if this was
1597 * never set.
1598 */
Alex Hills4ec3ff42016-04-12 11:36:18 -04001599 public boolean getHintLaunchesActivity() {
Alex Hills9ab3a232016-04-05 14:54:56 -04001600 return (mFlags & FLAG_HINT_LAUNCHES_ACTIVITY) != 0;
1601 }
Alex Hills9f087612016-06-07 09:08:59 -04001602
1603 /**
1604 * Set a hint that this Action should be displayed inline.
1605 *
1606 * @param hintDisplayInline {@code true} if action should be displayed inline, false
1607 * otherwise
1608 * @return this object for method chaining
1609 */
1610 public WearableExtender setHintDisplayActionInline(
1611 boolean hintDisplayInline) {
1612 setFlag(FLAG_HINT_DISPLAY_INLINE, hintDisplayInline);
1613 return this;
1614 }
1615
1616 /**
1617 * Get a hint that this Action should be displayed inline.
1618 *
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08001619 * @return {@code true} if the Action should be displayed inline, {@code false}
Alex Hills9f087612016-06-07 09:08:59 -04001620 * otherwise. The default value is {@code false} if this was never set.
1621 */
1622 public boolean getHintDisplayActionInline() {
1623 return (mFlags & FLAG_HINT_DISPLAY_INLINE) != 0;
1624 }
Griff Hazen61a9e862014-05-22 16:05:19 -07001625 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001626 }
1627
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001628 /**
1629 * Array of all {@link Action} structures attached to this notification by
1630 * {@link Builder#addAction(int, CharSequence, PendingIntent)}. Mostly useful for instances of
1631 * {@link android.service.notification.NotificationListenerService} that provide an alternative
1632 * interface for invoking actions.
1633 */
Daniel Sandlerea2a3172013-02-20 22:24:20 -05001634 public Action[] actions;
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001635
1636 /**
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001637 * Replacement version of this notification whose content will be shown
1638 * in an insecure context such as atop a secure keyguard. See {@link #visibility}
1639 * and {@link #VISIBILITY_PUBLIC}.
1640 */
1641 public Notification publicVersion;
1642
1643 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001644 * Constructs a Notification object with default values.
Joe Onorato46439ce2010-11-19 13:56:21 -08001645 * You might want to consider using {@link Builder} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001646 */
1647 public Notification()
1648 {
1649 this.when = System.currentTimeMillis();
Selim Cinekb85f36fd2016-04-20 18:46:36 -07001650 this.creationTime = System.currentTimeMillis();
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001651 this.priority = PRIORITY_DEFAULT;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001652 }
1653
1654 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001655 * @hide
1656 */
1657 public Notification(Context context, int icon, CharSequence tickerText, long when,
1658 CharSequence contentTitle, CharSequence contentText, Intent contentIntent)
1659 {
Chris Wren1ce4b6d2015-06-11 10:19:43 -04001660 new Builder(context)
1661 .setWhen(when)
1662 .setSmallIcon(icon)
1663 .setTicker(tickerText)
1664 .setContentTitle(contentTitle)
1665 .setContentText(contentText)
1666 .setContentIntent(PendingIntent.getActivity(context, 0, contentIntent, 0))
1667 .buildInto(this);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001668 }
1669
1670 /**
1671 * Constructs a Notification object with the information needed to
1672 * have a status bar icon without the standard expanded view.
1673 *
1674 * @param icon The resource id of the icon to put in the status bar.
1675 * @param tickerText The text that flows by in the status bar when the notification first
1676 * activates.
1677 * @param when The time to show in the time field. In the System.currentTimeMillis
1678 * timebase.
Joe Onorato46439ce2010-11-19 13:56:21 -08001679 *
1680 * @deprecated Use {@link Builder} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001681 */
Joe Onorato46439ce2010-11-19 13:56:21 -08001682 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001683 public Notification(int icon, CharSequence tickerText, long when)
1684 {
1685 this.icon = icon;
1686 this.tickerText = tickerText;
1687 this.when = when;
Selim Cinekb85f36fd2016-04-20 18:46:36 -07001688 this.creationTime = System.currentTimeMillis();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001689 }
1690
1691 /**
1692 * Unflatten the notification from a parcel.
1693 */
Svet Ganovddb94882016-06-23 19:55:24 -07001694 @SuppressWarnings("unchecked")
1695 public Notification(Parcel parcel) {
1696 // IMPORTANT: Add unmarshaling code in readFromParcel as the pending
1697 // intents in extras are always written as the last entry.
1698 readFromParcelImpl(parcel);
1699 // Must be read last!
Felipe Lemedd85da62016-06-28 11:29:54 -07001700 allPendingIntents = (ArraySet<PendingIntent>) parcel.readArraySet(null);
Svet Ganovddb94882016-06-23 19:55:24 -07001701 }
1702
1703 private void readFromParcelImpl(Parcel parcel)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001704 {
1705 int version = parcel.readInt();
1706
1707 when = parcel.readLong();
Selim Cinekb85f36fd2016-04-20 18:46:36 -07001708 creationTime = parcel.readLong();
Dan Sandler3936e7a2015-05-19 20:59:12 -04001709 if (parcel.readInt() != 0) {
1710 mSmallIcon = Icon.CREATOR.createFromParcel(parcel);
Dan Sandler4e787062015-06-17 15:09:48 -04001711 if (mSmallIcon.getType() == Icon.TYPE_RESOURCE) {
1712 icon = mSmallIcon.getResId();
1713 }
Dan Sandler3936e7a2015-05-19 20:59:12 -04001714 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001715 number = parcel.readInt();
1716 if (parcel.readInt() != 0) {
1717 contentIntent = PendingIntent.CREATOR.createFromParcel(parcel);
1718 }
1719 if (parcel.readInt() != 0) {
1720 deleteIntent = PendingIntent.CREATOR.createFromParcel(parcel);
1721 }
1722 if (parcel.readInt() != 0) {
1723 tickerText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
1724 }
1725 if (parcel.readInt() != 0) {
Joe Onorato46439ce2010-11-19 13:56:21 -08001726 tickerView = RemoteViews.CREATOR.createFromParcel(parcel);
Joe Onoratoef1e7762010-09-17 18:38:38 -04001727 }
1728 if (parcel.readInt() != 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001729 contentView = RemoteViews.CREATOR.createFromParcel(parcel);
1730 }
Joe Onorato561d3852010-11-20 18:09:34 -08001731 if (parcel.readInt() != 0) {
Dan Sandlerd63f9322015-05-06 15:18:49 -04001732 mLargeIcon = Icon.CREATOR.createFromParcel(parcel);
Joe Onorato561d3852010-11-20 18:09:34 -08001733 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001734 defaults = parcel.readInt();
1735 flags = parcel.readInt();
1736 if (parcel.readInt() != 0) {
1737 sound = Uri.CREATOR.createFromParcel(parcel);
1738 }
1739
1740 audioStreamType = parcel.readInt();
John Spurlockc0650f022014-07-19 13:22:39 -04001741 if (parcel.readInt() != 0) {
1742 audioAttributes = AudioAttributes.CREATOR.createFromParcel(parcel);
1743 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001744 vibrate = parcel.createLongArray();
1745 ledARGB = parcel.readInt();
1746 ledOnMS = parcel.readInt();
1747 ledOffMS = parcel.readInt();
1748 iconLevel = parcel.readInt();
Daniel Sandlere46cbd32010-06-17 10:35:26 -04001749
1750 if (parcel.readInt() != 0) {
1751 fullScreenIntent = PendingIntent.CREATOR.createFromParcel(parcel);
1752 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001753
1754 priority = parcel.readInt();
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001755
John Spurlockfd7f1e02014-03-18 16:41:57 -04001756 category = parcel.readString();
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001757
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001758 mGroupKey = parcel.readString();
1759
1760 mSortKey = parcel.readString();
1761
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001762 extras = Bundle.setDefusable(parcel.readBundle(), true); // may be null
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001763
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001764 actions = parcel.createTypedArray(Action.CREATOR); // may be null
1765
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001766 if (parcel.readInt() != 0) {
Daniel Sandlerf3b73432012-03-27 15:01:25 -04001767 bigContentView = RemoteViews.CREATOR.createFromParcel(parcel);
1768 }
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001769
Chris Wren8fd39ec2014-02-27 17:43:26 -05001770 if (parcel.readInt() != 0) {
1771 headsUpContentView = RemoteViews.CREATOR.createFromParcel(parcel);
1772 }
1773
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001774 visibility = parcel.readInt();
1775
1776 if (parcel.readInt() != 0) {
1777 publicVersion = Notification.CREATOR.createFromParcel(parcel);
1778 }
Dan Sandler26e81cf2014-05-06 10:01:27 -04001779
1780 color = parcel.readInt();
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04001781
1782 if (parcel.readInt() != 0) {
1783 mChannelId = parcel.readString();
1784 }
Julia Reynolds2a128742016-11-28 14:29:25 -05001785 mTimeout = parcel.readLong();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001786 }
1787
Andy Stadler110988c2010-12-03 14:29:16 -08001788 @Override
Joe Onorato18e69df2010-05-17 22:26:12 -07001789 public Notification clone() {
1790 Notification that = new Notification();
Daniel Sandler1a497d32013-04-18 14:52:45 -04001791 cloneInto(that, true);
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001792 return that;
1793 }
Joe Onorato18e69df2010-05-17 22:26:12 -07001794
Daniel Sandler1a497d32013-04-18 14:52:45 -04001795 /**
1796 * Copy all (or if heavy is false, all except Bitmaps and RemoteViews) members
1797 * of this into that.
1798 * @hide
1799 */
1800 public void cloneInto(Notification that, boolean heavy) {
Joe Onorato18e69df2010-05-17 22:26:12 -07001801 that.when = this.when;
Selim Cinekb85f36fd2016-04-20 18:46:36 -07001802 that.creationTime = this.creationTime;
Dan Sandlerd63f9322015-05-06 15:18:49 -04001803 that.mSmallIcon = this.mSmallIcon;
Joe Onorato18e69df2010-05-17 22:26:12 -07001804 that.number = this.number;
1805
1806 // PendingIntents are global, so there's no reason (or way) to clone them.
1807 that.contentIntent = this.contentIntent;
1808 that.deleteIntent = this.deleteIntent;
Daniel Sandlere46cbd32010-06-17 10:35:26 -04001809 that.fullScreenIntent = this.fullScreenIntent;
Joe Onorato18e69df2010-05-17 22:26:12 -07001810
1811 if (this.tickerText != null) {
1812 that.tickerText = this.tickerText.toString();
1813 }
Daniel Sandler1a497d32013-04-18 14:52:45 -04001814 if (heavy && this.tickerView != null) {
Joe Onorato46439ce2010-11-19 13:56:21 -08001815 that.tickerView = this.tickerView.clone();
Joe Onoratoef1e7762010-09-17 18:38:38 -04001816 }
Daniel Sandler1a497d32013-04-18 14:52:45 -04001817 if (heavy && this.contentView != null) {
Joe Onorato18e69df2010-05-17 22:26:12 -07001818 that.contentView = this.contentView.clone();
1819 }
Dan Sandlerd63f9322015-05-06 15:18:49 -04001820 if (heavy && this.mLargeIcon != null) {
1821 that.mLargeIcon = this.mLargeIcon;
Joe Onorato561d3852010-11-20 18:09:34 -08001822 }
Jozef BABJAKa8b91832011-02-22 08:05:08 +01001823 that.iconLevel = this.iconLevel;
Joe Onorato18e69df2010-05-17 22:26:12 -07001824 that.sound = this.sound; // android.net.Uri is immutable
1825 that.audioStreamType = this.audioStreamType;
John Spurlockc0650f022014-07-19 13:22:39 -04001826 if (this.audioAttributes != null) {
1827 that.audioAttributes = new AudioAttributes.Builder(this.audioAttributes).build();
1828 }
Joe Onorato18e69df2010-05-17 22:26:12 -07001829
1830 final long[] vibrate = this.vibrate;
1831 if (vibrate != null) {
1832 final int N = vibrate.length;
1833 final long[] vib = that.vibrate = new long[N];
1834 System.arraycopy(vibrate, 0, vib, 0, N);
1835 }
1836
1837 that.ledARGB = this.ledARGB;
1838 that.ledOnMS = this.ledOnMS;
1839 that.ledOffMS = this.ledOffMS;
1840 that.defaults = this.defaults;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001841
Joe Onorato18e69df2010-05-17 22:26:12 -07001842 that.flags = this.flags;
1843
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001844 that.priority = this.priority;
Joe Malin8d40d042012-11-05 11:36:40 -08001845
John Spurlockfd7f1e02014-03-18 16:41:57 -04001846 that.category = this.category;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001847
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001848 that.mGroupKey = this.mGroupKey;
1849
1850 that.mSortKey = this.mSortKey;
1851
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001852 if (this.extras != null) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04001853 try {
1854 that.extras = new Bundle(this.extras);
1855 // will unparcel
1856 that.extras.size();
1857 } catch (BadParcelableException e) {
1858 Log.e(TAG, "could not unparcel extras from notification: " + this, e);
1859 that.extras = null;
1860 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001861 }
1862
Felipe Lemedd85da62016-06-28 11:29:54 -07001863 if (!ArrayUtils.isEmpty(allPendingIntents)) {
1864 that.allPendingIntents = new ArraySet<>(allPendingIntents);
Svet Ganovddb94882016-06-23 19:55:24 -07001865 }
1866
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001867 if (this.actions != null) {
1868 that.actions = new Action[this.actions.length];
1869 for(int i=0; i<this.actions.length; i++) {
1870 that.actions[i] = this.actions[i].clone();
1871 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001872 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001873
Daniel Sandler1a497d32013-04-18 14:52:45 -04001874 if (heavy && this.bigContentView != null) {
Daniel Sandlerf3b73432012-03-27 15:01:25 -04001875 that.bigContentView = this.bigContentView.clone();
1876 }
Daniel Sandler1a497d32013-04-18 14:52:45 -04001877
Chris Wren8fd39ec2014-02-27 17:43:26 -05001878 if (heavy && this.headsUpContentView != null) {
1879 that.headsUpContentView = this.headsUpContentView.clone();
1880 }
1881
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001882 that.visibility = this.visibility;
1883
1884 if (this.publicVersion != null) {
1885 that.publicVersion = new Notification();
1886 this.publicVersion.cloneInto(that.publicVersion, heavy);
1887 }
1888
Dan Sandler26e81cf2014-05-06 10:01:27 -04001889 that.color = this.color;
1890
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04001891 that.mChannelId = this.mChannelId;
Julia Reynolds2a128742016-11-28 14:29:25 -05001892 that.mTimeout = this.mTimeout;
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04001893
Daniel Sandler1a497d32013-04-18 14:52:45 -04001894 if (!heavy) {
1895 that.lightenPayload(); // will clean out extras
1896 }
1897 }
1898
1899 /**
1900 * Removes heavyweight parts of the Notification object for archival or for sending to
1901 * listeners when the full contents are not necessary.
1902 * @hide
1903 */
1904 public final void lightenPayload() {
1905 tickerView = null;
1906 contentView = null;
1907 bigContentView = null;
Chris Wren8fd39ec2014-02-27 17:43:26 -05001908 headsUpContentView = null;
Dan Sandlerd63f9322015-05-06 15:18:49 -04001909 mLargeIcon = null;
Dan Sandler50128532015-12-08 15:42:41 -05001910 if (extras != null && !extras.isEmpty()) {
1911 final Set<String> keyset = extras.keySet();
1912 final int N = keyset.size();
1913 final String[] keys = keyset.toArray(new String[N]);
1914 for (int i=0; i<N; i++) {
1915 final String key = keys[i];
1916 final Object obj = extras.get(key);
1917 if (obj != null &&
1918 ( obj instanceof Parcelable
1919 || obj instanceof Parcelable[]
1920 || obj instanceof SparseArray
1921 || obj instanceof ArrayList)) {
1922 extras.remove(key);
1923 }
1924 }
Daniel Sandler1a497d32013-04-18 14:52:45 -04001925 }
Joe Onorato18e69df2010-05-17 22:26:12 -07001926 }
1927
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04001928 /**
1929 * Make sure this CharSequence is safe to put into a bundle, which basically
1930 * means it had better not be some custom Parcelable implementation.
1931 * @hide
1932 */
1933 public static CharSequence safeCharSequence(CharSequence cs) {
Christoph Studer535ec612014-09-03 15:47:47 +02001934 if (cs == null) return cs;
1935 if (cs.length() > MAX_CHARSEQUENCE_LENGTH) {
1936 cs = cs.subSequence(0, MAX_CHARSEQUENCE_LENGTH);
1937 }
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04001938 if (cs instanceof Parcelable) {
1939 Log.e(TAG, "warning: " + cs.getClass().getCanonicalName()
1940 + " instance is a custom Parcelable and not allowed in Notification");
1941 return cs.toString();
1942 }
Selim Cinek60a54252016-02-26 17:03:25 -08001943 return removeTextSizeSpans(cs);
1944 }
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04001945
Selim Cinek60a54252016-02-26 17:03:25 -08001946 private static CharSequence removeTextSizeSpans(CharSequence charSequence) {
1947 if (charSequence instanceof Spanned) {
1948 Spanned ss = (Spanned) charSequence;
1949 Object[] spans = ss.getSpans(0, ss.length(), Object.class);
1950 SpannableStringBuilder builder = new SpannableStringBuilder(ss.toString());
1951 for (Object span : spans) {
1952 Object resultSpan = span;
Selim Cinek89991a22016-03-07 19:51:54 -08001953 if (resultSpan instanceof CharacterStyle) {
1954 resultSpan = ((CharacterStyle) span).getUnderlying();
1955 }
1956 if (resultSpan instanceof TextAppearanceSpan) {
1957 TextAppearanceSpan originalSpan = (TextAppearanceSpan) resultSpan;
Selim Cinek60a54252016-02-26 17:03:25 -08001958 resultSpan = new TextAppearanceSpan(
1959 originalSpan.getFamily(),
1960 originalSpan.getTextStyle(),
1961 -1,
1962 originalSpan.getTextColor(),
1963 originalSpan.getLinkTextColor());
Selim Cinek89991a22016-03-07 19:51:54 -08001964 } else if (resultSpan instanceof RelativeSizeSpan
1965 || resultSpan instanceof AbsoluteSizeSpan) {
Selim Cinek60a54252016-02-26 17:03:25 -08001966 continue;
Selim Cinek89991a22016-03-07 19:51:54 -08001967 } else {
1968 resultSpan = span;
Selim Cinek60a54252016-02-26 17:03:25 -08001969 }
1970 builder.setSpan(resultSpan, ss.getSpanStart(span), ss.getSpanEnd(span),
1971 ss.getSpanFlags(span));
1972 }
1973 return builder;
1974 }
1975 return charSequence;
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04001976 }
1977
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001978 public int describeContents() {
1979 return 0;
1980 }
1981
1982 /**
Dan Sandler4e787062015-06-17 15:09:48 -04001983 * Flatten this notification into a parcel.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001984 */
Svet Ganovddb94882016-06-23 19:55:24 -07001985 public void writeToParcel(Parcel parcel, int flags) {
1986 // We need to mark all pending intents getting into the notification
1987 // system as being put there to later allow the notification ranker
1988 // to launch them and by doing so add the app to the battery saver white
1989 // list for a short period of time. The problem is that the system
1990 // cannot look into the extras as there may be parcelables there that
1991 // the platform does not know how to handle. To go around that we have
1992 // an explicit list of the pending intents in the extras bundle.
Felipe Lemedd85da62016-06-28 11:29:54 -07001993 final boolean collectPendingIntents = (allPendingIntents == null);
Svet Ganovddb94882016-06-23 19:55:24 -07001994 if (collectPendingIntents) {
1995 PendingIntent.setOnMarshaledListener(
1996 (PendingIntent intent, Parcel out, int outFlags) -> {
1997 if (parcel == out) {
Felipe Lemedd85da62016-06-28 11:29:54 -07001998 if (allPendingIntents == null) {
1999 allPendingIntents = new ArraySet<>();
Svet Ganovddb94882016-06-23 19:55:24 -07002000 }
Felipe Lemedd85da62016-06-28 11:29:54 -07002001 allPendingIntents.add(intent);
Svet Ganovddb94882016-06-23 19:55:24 -07002002 }
2003 });
2004 }
2005 try {
2006 // IMPORTANT: Add marshaling code in writeToParcelImpl as we
2007 // want to intercept all pending events written to the pacel.
2008 writeToParcelImpl(parcel, flags);
2009 // Must be written last!
Felipe Lemedd85da62016-06-28 11:29:54 -07002010 parcel.writeArraySet(allPendingIntents);
Svet Ganovddb94882016-06-23 19:55:24 -07002011 } finally {
2012 if (collectPendingIntents) {
2013 PendingIntent.setOnMarshaledListener(null);
2014 }
2015 }
2016 }
2017
2018 private void writeToParcelImpl(Parcel parcel, int flags) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002019 parcel.writeInt(1);
2020
2021 parcel.writeLong(when);
Selim Cinekb85f36fd2016-04-20 18:46:36 -07002022 parcel.writeLong(creationTime);
Dan Sandler4e787062015-06-17 15:09:48 -04002023 if (mSmallIcon == null && icon != 0) {
2024 // you snuck an icon in here without using the builder; let's try to keep it
2025 mSmallIcon = Icon.createWithResource("", icon);
2026 }
Dan Sandler3936e7a2015-05-19 20:59:12 -04002027 if (mSmallIcon != null) {
2028 parcel.writeInt(1);
2029 mSmallIcon.writeToParcel(parcel, 0);
2030 } else {
2031 parcel.writeInt(0);
2032 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002033 parcel.writeInt(number);
2034 if (contentIntent != null) {
2035 parcel.writeInt(1);
2036 contentIntent.writeToParcel(parcel, 0);
2037 } else {
2038 parcel.writeInt(0);
2039 }
2040 if (deleteIntent != null) {
2041 parcel.writeInt(1);
2042 deleteIntent.writeToParcel(parcel, 0);
2043 } else {
2044 parcel.writeInt(0);
2045 }
2046 if (tickerText != null) {
2047 parcel.writeInt(1);
2048 TextUtils.writeToParcel(tickerText, parcel, flags);
2049 } else {
2050 parcel.writeInt(0);
2051 }
Joe Onorato46439ce2010-11-19 13:56:21 -08002052 if (tickerView != null) {
Joe Onoratoef1e7762010-09-17 18:38:38 -04002053 parcel.writeInt(1);
Joe Onorato46439ce2010-11-19 13:56:21 -08002054 tickerView.writeToParcel(parcel, 0);
Joe Onoratoef1e7762010-09-17 18:38:38 -04002055 } else {
2056 parcel.writeInt(0);
2057 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002058 if (contentView != null) {
2059 parcel.writeInt(1);
2060 contentView.writeToParcel(parcel, 0);
2061 } else {
2062 parcel.writeInt(0);
2063 }
Selim Cinek279fa862016-06-14 10:57:25 -07002064 if (mLargeIcon == null && largeIcon != null) {
2065 // you snuck an icon in here without using the builder; let's try to keep it
2066 mLargeIcon = Icon.createWithBitmap(largeIcon);
2067 }
Dan Sandlerd63f9322015-05-06 15:18:49 -04002068 if (mLargeIcon != null) {
Joe Onorato561d3852010-11-20 18:09:34 -08002069 parcel.writeInt(1);
Dan Sandlerd63f9322015-05-06 15:18:49 -04002070 mLargeIcon.writeToParcel(parcel, 0);
Joe Onorato561d3852010-11-20 18:09:34 -08002071 } else {
2072 parcel.writeInt(0);
2073 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002074
2075 parcel.writeInt(defaults);
2076 parcel.writeInt(this.flags);
2077
2078 if (sound != null) {
2079 parcel.writeInt(1);
2080 sound.writeToParcel(parcel, 0);
2081 } else {
2082 parcel.writeInt(0);
2083 }
2084 parcel.writeInt(audioStreamType);
John Spurlockc0650f022014-07-19 13:22:39 -04002085
2086 if (audioAttributes != null) {
2087 parcel.writeInt(1);
2088 audioAttributes.writeToParcel(parcel, 0);
2089 } else {
2090 parcel.writeInt(0);
2091 }
2092
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002093 parcel.writeLongArray(vibrate);
2094 parcel.writeInt(ledARGB);
2095 parcel.writeInt(ledOnMS);
2096 parcel.writeInt(ledOffMS);
2097 parcel.writeInt(iconLevel);
Daniel Sandlere46cbd32010-06-17 10:35:26 -04002098
2099 if (fullScreenIntent != null) {
2100 parcel.writeInt(1);
2101 fullScreenIntent.writeToParcel(parcel, 0);
2102 } else {
2103 parcel.writeInt(0);
2104 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002105
2106 parcel.writeInt(priority);
Joe Malin8d40d042012-11-05 11:36:40 -08002107
John Spurlockfd7f1e02014-03-18 16:41:57 -04002108 parcel.writeString(category);
Joe Malin8d40d042012-11-05 11:36:40 -08002109
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002110 parcel.writeString(mGroupKey);
2111
2112 parcel.writeString(mSortKey);
2113
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002114 parcel.writeBundle(extras); // null ok
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002115
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002116 parcel.writeTypedArray(actions, 0); // null ok
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002117
Daniel Sandlerf3b73432012-03-27 15:01:25 -04002118 if (bigContentView != null) {
2119 parcel.writeInt(1);
2120 bigContentView.writeToParcel(parcel, 0);
2121 } else {
2122 parcel.writeInt(0);
2123 }
Dan Sandler0bf2ed82013-12-21 23:33:41 -06002124
Chris Wren8fd39ec2014-02-27 17:43:26 -05002125 if (headsUpContentView != null) {
2126 parcel.writeInt(1);
2127 headsUpContentView.writeToParcel(parcel, 0);
2128 } else {
2129 parcel.writeInt(0);
2130 }
2131
Dan Sandler0bf2ed82013-12-21 23:33:41 -06002132 parcel.writeInt(visibility);
2133
2134 if (publicVersion != null) {
2135 parcel.writeInt(1);
2136 publicVersion.writeToParcel(parcel, 0);
2137 } else {
2138 parcel.writeInt(0);
2139 }
Dan Sandler26e81cf2014-05-06 10:01:27 -04002140
2141 parcel.writeInt(color);
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04002142
2143 if (mChannelId != null) {
2144 parcel.writeInt(1);
2145 parcel.writeString(mChannelId);
2146 } else {
2147 parcel.writeInt(0);
2148 }
Julia Reynolds2a128742016-11-28 14:29:25 -05002149 parcel.writeLong(mTimeout);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002150 }
2151
2152 /**
2153 * Parcelable.Creator that instantiates Notification objects
2154 */
2155 public static final Parcelable.Creator<Notification> CREATOR
2156 = new Parcelable.Creator<Notification>()
2157 {
2158 public Notification createFromParcel(Parcel parcel)
2159 {
2160 return new Notification(parcel);
2161 }
2162
2163 public Notification[] newArray(int size)
2164 {
2165 return new Notification[size];
2166 }
2167 };
2168
2169 /**
2170 * Sets the {@link #contentView} field to be a view with the standard "Latest Event"
2171 * layout.
2172 *
2173 * <p>Uses the {@link #icon} and {@link #when} fields to set the icon and time fields
2174 * in the view.</p>
2175 * @param context The context for your application / activity.
2176 * @param contentTitle The title that goes in the expanded entry.
2177 * @param contentText The text that goes in the expanded entry.
2178 * @param contentIntent The intent to launch when the user clicks the expanded notification.
2179 * If this is an activity, it must include the
2180 * {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} flag, which requires
Scott Main7aee61f2011-02-08 11:25:01 -08002181 * that you take care of task management as described in the
2182 * <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back
2183 * Stack</a> document.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002184 *
Joe Onorato46439ce2010-11-19 13:56:21 -08002185 * @deprecated Use {@link Builder} instead.
Chris Wrena05db382015-06-24 15:18:34 -04002186 * @removed
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002187 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002188 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002189 public void setLatestEventInfo(Context context,
2190 CharSequence contentTitle, CharSequence contentText, PendingIntent contentIntent) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002191 if (context.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1){
2192 Log.e(TAG, "setLatestEventInfo() is deprecated and you should feel deprecated.",
2193 new Throwable());
2194 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002195
Selim Cinek4ac6f602016-06-13 15:47:03 -07002196 if (context.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N) {
2197 extras.putBoolean(EXTRA_SHOW_WHEN, true);
2198 }
2199
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002200 // ensure that any information already set directly is preserved
2201 final Notification.Builder builder = new Notification.Builder(context, this);
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002202
2203 // now apply the latestEventInfo fields
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002204 if (contentTitle != null) {
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002205 builder.setContentTitle(contentTitle);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002206 }
2207 if (contentText != null) {
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002208 builder.setContentText(contentText);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002209 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05002210 builder.setContentIntent(contentIntent);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002211
2212 builder.build(); // callers expect this notification to be ready to use
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002213 }
2214
Julia Reynoldsda303542015-11-23 14:00:20 -05002215 /**
2216 * @hide
2217 */
2218 public static void addFieldsFromContext(Context context, Notification notification) {
Jeff Sharkey012bc7b2016-04-11 16:30:27 -06002219 addFieldsFromContext(context.getApplicationInfo(), context.getUserId(), notification);
2220 }
2221
2222 /**
2223 * @hide
2224 */
2225 public static void addFieldsFromContext(ApplicationInfo ai, int userId,
2226 Notification notification) {
2227 notification.extras.putParcelable(EXTRA_BUILDER_APPLICATION_INFO, ai);
2228 notification.extras.putInt(EXTRA_ORIGINATING_USERID, userId);
Julia Reynoldsda303542015-11-23 14:00:20 -05002229 }
2230
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002231 @Override
2232 public String toString() {
2233 StringBuilder sb = new StringBuilder();
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002234 sb.append("Notification(pri=");
2235 sb.append(priority);
2236 sb.append(" contentView=");
Joe Onoratoc9596d62011-01-12 17:03:11 -08002237 if (contentView != null) {
2238 sb.append(contentView.getPackage());
2239 sb.append("/0x");
2240 sb.append(Integer.toHexString(contentView.getLayoutId()));
2241 } else {
2242 sb.append("null");
2243 }
2244 sb.append(" vibrate=");
Daniel Sandler6738eee2012-11-16 12:03:32 -05002245 if ((this.defaults & DEFAULT_VIBRATE) != 0) {
2246 sb.append("default");
2247 } else if (this.vibrate != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002248 int N = this.vibrate.length-1;
2249 sb.append("[");
2250 for (int i=0; i<N; i++) {
2251 sb.append(this.vibrate[i]);
2252 sb.append(',');
2253 }
Simon Schoar8cf97d92009-06-10 22:08:37 +02002254 if (N != -1) {
2255 sb.append(this.vibrate[N]);
2256 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002257 sb.append("]");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002258 } else {
2259 sb.append("null");
2260 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002261 sb.append(" sound=");
Daniel Sandler6738eee2012-11-16 12:03:32 -05002262 if ((this.defaults & DEFAULT_SOUND) != 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002263 sb.append("default");
Daniel Sandler6738eee2012-11-16 12:03:32 -05002264 } else if (this.sound != null) {
2265 sb.append(this.sound.toString());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002266 } else {
2267 sb.append("null");
2268 }
Chris Wren365b6d32015-07-16 10:39:26 -04002269 if (this.tickerText != null) {
2270 sb.append(" tick");
2271 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002272 sb.append(" defaults=0x");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002273 sb.append(Integer.toHexString(this.defaults));
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002274 sb.append(" flags=0x");
Daniel Sandlere46cbd32010-06-17 10:35:26 -04002275 sb.append(Integer.toHexString(this.flags));
Dan Sandler26e81cf2014-05-06 10:01:27 -04002276 sb.append(String.format(" color=0x%08x", this.color));
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002277 if (this.category != null) {
2278 sb.append(" category=");
2279 sb.append(this.category);
2280 }
2281 if (this.mGroupKey != null) {
2282 sb.append(" groupKey=");
2283 sb.append(this.mGroupKey);
2284 }
2285 if (this.mSortKey != null) {
2286 sb.append(" sortKey=");
2287 sb.append(this.mSortKey);
2288 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002289 if (actions != null) {
Dan Sandler1b718782014-07-18 12:43:45 -04002290 sb.append(" actions=");
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002291 sb.append(actions.length);
Dan Sandler1b718782014-07-18 12:43:45 -04002292 }
2293 sb.append(" vis=");
2294 sb.append(visibilityToString(this.visibility));
2295 if (this.publicVersion != null) {
2296 sb.append(" publicVersion=");
2297 sb.append(publicVersion.toString());
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002298 }
2299 sb.append(")");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002300 return sb.toString();
2301 }
Joe Onorato46439ce2010-11-19 13:56:21 -08002302
Dan Sandler1b718782014-07-18 12:43:45 -04002303 /**
2304 * {@hide}
2305 */
2306 public static String visibilityToString(int vis) {
2307 switch (vis) {
2308 case VISIBILITY_PRIVATE:
2309 return "PRIVATE";
2310 case VISIBILITY_PUBLIC:
2311 return "PUBLIC";
2312 case VISIBILITY_SECRET:
2313 return "SECRET";
2314 default:
2315 return "UNKNOWN(" + String.valueOf(vis) + ")";
2316 }
2317 }
2318
Joe Onoratocb109a02011-01-18 17:57:41 -08002319 /**
John Spurlock1d881a12015-03-18 19:21:54 -04002320 * {@hide}
2321 */
2322 public static String priorityToString(@Priority int pri) {
2323 switch (pri) {
2324 case PRIORITY_MIN:
2325 return "MIN";
2326 case PRIORITY_LOW:
2327 return "LOW";
2328 case PRIORITY_DEFAULT:
2329 return "DEFAULT";
2330 case PRIORITY_HIGH:
2331 return "HIGH";
2332 case PRIORITY_MAX:
2333 return "MAX";
2334 default:
2335 return "UNKNOWN(" + String.valueOf(pri) + ")";
2336 }
2337 }
2338
2339 /**
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04002340 * Returns the id of the channel this notification posts to.
2341 */
Julia Reynolds37856052016-11-11 09:20:07 -05002342 public String getChannel() {
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04002343 return mChannelId;
2344 }
2345
2346 /**
Julia Reynolds2a128742016-11-28 14:29:25 -05002347 * Returns the time at which this notification should be canceled, if it's not canceled already.
2348 */
2349 public long getTimeout() {
2350 return mTimeout;
2351 }
2352
2353 /**
Dan Sandlerd63f9322015-05-06 15:18:49 -04002354 * The small icon representing this notification in the status bar and content view.
2355 *
2356 * @return the small icon representing this notification.
2357 *
2358 * @see Builder#getSmallIcon()
2359 * @see Builder#setSmallIcon(Icon)
2360 */
2361 public Icon getSmallIcon() {
2362 return mSmallIcon;
2363 }
2364
2365 /**
2366 * Used when notifying to clean up legacy small icons.
2367 * @hide
2368 */
2369 public void setSmallIcon(Icon icon) {
2370 mSmallIcon = icon;
2371 }
2372
2373 /**
2374 * The large icon shown in this notification's content view.
2375 * @see Builder#getLargeIcon()
2376 * @see Builder#setLargeIcon(Icon)
2377 */
2378 public Icon getLargeIcon() {
2379 return mLargeIcon;
2380 }
2381
2382 /**
Christoph Studer4600f9b2014-07-22 22:44:43 +02002383 * @hide
2384 */
Christoph Studerc8db24b2014-07-25 17:50:30 +02002385 public boolean isGroupSummary() {
2386 return mGroupKey != null && (flags & FLAG_GROUP_SUMMARY) != 0;
2387 }
2388
2389 /**
2390 * @hide
2391 */
2392 public boolean isGroupChild() {
2393 return mGroupKey != null && (flags & FLAG_GROUP_SUMMARY) == 0;
2394 }
2395
2396 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002397 * Builder class for {@link Notification} objects.
Joe Malin8d40d042012-11-05 11:36:40 -08002398 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002399 * Provides a convenient way to set the various fields of a {@link Notification} and generate
Scott Main183bf112012-08-13 19:12:13 -07002400 * content views using the platform's notification layout template. If your app supports
2401 * versions of Android as old as API level 4, you can instead use
2402 * {@link android.support.v4.app.NotificationCompat.Builder NotificationCompat.Builder},
2403 * available in the <a href="{@docRoot}tools/extras/support-library.html">Android Support
2404 * library</a>.
Joe Malin8d40d042012-11-05 11:36:40 -08002405 *
Scott Main183bf112012-08-13 19:12:13 -07002406 * <p>Example:
Joe Malin8d40d042012-11-05 11:36:40 -08002407 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002408 * <pre class="prettyprint">
Scott Main183bf112012-08-13 19:12:13 -07002409 * Notification noti = new Notification.Builder(mContext)
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002410 * .setContentTitle(&quot;New mail from &quot; + sender.toString())
2411 * .setContentText(subject)
2412 * .setSmallIcon(R.drawable.new_mail)
2413 * .setLargeIcon(aBitmap)
Chris Wrenfbd96ba2012-05-01 12:03:58 -04002414 * .build();
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002415 * </pre>
Joe Onoratocb109a02011-01-18 17:57:41 -08002416 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002417 public static class Builder {
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05002418 /**
2419 * @hide
2420 */
2421 public static final String EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT =
2422 "android.rebuild.contentViewActionCount";
2423 /**
2424 * @hide
2425 */
2426 public static final String EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT
2427 = "android.rebuild.bigViewActionCount";
2428 /**
2429 * @hide
2430 */
2431 public static final String EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT
2432 = "android.rebuild.hudViewActionCount";
2433
Daniel Sandler602ad1c2012-06-12 16:06:27 -04002434 private static final int MAX_ACTION_BUTTONS = 3;
Daniel Sandler8680bf82012-05-15 16:52:52 -04002435
Selim Cinek6743c0b2017-01-18 18:24:01 -08002436 private static final boolean USE_ONLY_TITLE_IN_LOW_PRIORITY_SUMMARY =
2437 SystemProperties.getBoolean("notifications.only_title", true);
2438
Joe Onorato46439ce2010-11-19 13:56:21 -08002439 private Context mContext;
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002440 private Notification mN;
2441 private Bundle mUserExtras = new Bundle();
Chris Wrenfbd96ba2012-05-01 12:03:58 -04002442 private Style mStyle;
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002443 private ArrayList<Action> mActions = new ArrayList<Action>(MAX_ACTION_BUTTONS);
2444 private ArrayList<String> mPersonList = new ArrayList<String>();
2445 private NotificationColorUtil mColorUtil;
Selim Cinek7b9605b2017-01-19 17:36:00 -08002446 private boolean mIsLegacy;
2447 private boolean mIsLegacyInitialized;
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002448 private boolean mColorUtilInited = false;
Christoph Studer7ac80e62014-08-04 16:01:57 +02002449
2450 /**
Adrian Roos4ff3b122016-02-01 12:26:13 -08002451 * Caches a contrast-enhanced version of {@link #mCachedContrastColorIsFor}.
2452 */
2453 private int mCachedContrastColor = COLOR_INVALID;
2454 private int mCachedContrastColorIsFor = COLOR_INVALID;
Adrian Roos487374f2017-01-11 15:48:14 -08002455 /**
2456 * Caches a ambient version of {@link #mCachedContrastColorIsFor}.
2457 */
2458 private int mCachedAmbientColor = COLOR_INVALID;
2459 private int mCachedAmbientColorIsFor = COLOR_INVALID;
Adrian Roos4ff3b122016-02-01 12:26:13 -08002460
2461 /**
Adrian Roos70d7aa32017-01-11 15:39:06 -08002462 * Caches an instance of StandardTemplateParams. Note that this may have been used before,
2463 * so make sure to call {@link StandardTemplateParams#reset()} before using it.
2464 */
2465 StandardTemplateParams mParams = new StandardTemplateParams();
Selim Cinek7b9605b2017-01-19 17:36:00 -08002466 private int mTextColorsAreForBackground = COLOR_INVALID;
2467 private int mPrimaryTextColor = COLOR_INVALID;
2468 private int mSecondaryTextColor = COLOR_INVALID;
2469 private int mActionBarColor = COLOR_INVALID;
Adrian Roos70d7aa32017-01-11 15:39:06 -08002470
2471 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002472 * Constructs a new Builder with the defaults:
Joe Onoratocb109a02011-01-18 17:57:41 -08002473 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002474
2475 * <table>
2476 * <tr><th align=right>priority</th>
2477 * <td>{@link #PRIORITY_DEFAULT}</td></tr>
2478 * <tr><th align=right>when</th>
2479 * <td>now ({@link System#currentTimeMillis()})</td></tr>
2480 * <tr><th align=right>audio stream</th>
2481 * <td>{@link #STREAM_DEFAULT}</td></tr>
2482 * </table>
Joe Onoratocb109a02011-01-18 17:57:41 -08002483 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002484
2485 * @param context
2486 * A {@link Context} that will be used by the Builder to construct the
2487 * RemoteViews. The Context will not be held past the lifetime of this Builder
2488 * object.
Joe Onoratocb109a02011-01-18 17:57:41 -08002489 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002490 public Builder(Context context) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002491 this(context, null);
Joe Onorato46439ce2010-11-19 13:56:21 -08002492 }
2493
Joe Onoratocb109a02011-01-18 17:57:41 -08002494 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002495 * @hide
Christoph Studer4600f9b2014-07-22 22:44:43 +02002496 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002497 public Builder(Context context, Notification toAdopt) {
2498 mContext = context;
Christoph Studer4600f9b2014-07-22 22:44:43 +02002499
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002500 if (toAdopt == null) {
2501 mN = new Notification();
Selim Cinek0ff1ce602016-04-05 18:27:16 -07002502 if (context.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N) {
2503 mN.extras.putBoolean(EXTRA_SHOW_WHEN, true);
2504 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002505 mN.priority = PRIORITY_DEFAULT;
2506 mN.visibility = VISIBILITY_PRIVATE;
2507 } else {
2508 mN = toAdopt;
2509 if (mN.actions != null) {
2510 Collections.addAll(mActions, mN.actions);
Christoph Studer4600f9b2014-07-22 22:44:43 +02002511 }
2512
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002513 if (mN.extras.containsKey(EXTRA_PEOPLE)) {
2514 Collections.addAll(mPersonList, mN.extras.getStringArray(EXTRA_PEOPLE));
2515 }
2516
Selim Cinek4ac6f602016-06-13 15:47:03 -07002517 if (mN.getSmallIcon() == null && mN.icon != 0) {
2518 setSmallIcon(mN.icon);
2519 }
2520
2521 if (mN.getLargeIcon() == null && mN.largeIcon != null) {
2522 setLargeIcon(mN.largeIcon);
2523 }
2524
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002525 String templateClass = mN.extras.getString(EXTRA_TEMPLATE);
2526 if (!TextUtils.isEmpty(templateClass)) {
2527 final Class<? extends Style> styleClass
2528 = getNotificationStyleClass(templateClass);
2529 if (styleClass == null) {
2530 Log.d(TAG, "Unknown style class: " + templateClass);
2531 } else {
2532 try {
Adrian Roosc1a80b02016-04-05 14:54:55 -07002533 final Constructor<? extends Style> ctor =
2534 styleClass.getDeclaredConstructor();
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002535 ctor.setAccessible(true);
2536 final Style style = ctor.newInstance();
2537 style.restoreFromExtras(mN.extras);
2538
2539 if (style != null) {
2540 setStyle(style);
2541 }
2542 } catch (Throwable t) {
2543 Log.e(TAG, "Could not create Style", t);
2544 }
2545 }
2546 }
2547
2548 }
2549 }
2550
2551 private NotificationColorUtil getColorUtil() {
2552 if (!mColorUtilInited) {
2553 mColorUtilInited = true;
Selim Cinek7b9605b2017-01-19 17:36:00 -08002554 if (isLegacy() || isColorized()) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002555 mColorUtil = NotificationColorUtil.getInstance(mContext);
Christoph Studer4600f9b2014-07-22 22:44:43 +02002556 }
2557 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002558 return mColorUtil;
Christoph Studer4600f9b2014-07-22 22:44:43 +02002559 }
2560
2561 /**
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04002562 * Specifies the channel the notification should be delivered on.
2563 */
2564 public Builder setChannel(String channelId) {
2565 mN.mChannelId = channelId;
2566 return this;
2567 }
2568
2569 /**
Julia Reynolds2a128742016-11-28 14:29:25 -05002570 * Specifies the time at which this notification should be canceled, if it is not already
2571 * canceled.
2572 */
2573 public Builder setTimeout(long when) {
2574 mN.mTimeout = when;
2575 return this;
2576 }
2577
2578 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002579 * Add a timestamp pertaining to the notification (usually the time the event occurred).
Selim Cinek0ff1ce602016-04-05 18:27:16 -07002580 *
2581 * For apps targeting {@link android.os.Build.VERSION_CODES#N} and above, this time is not
2582 * shown anymore by default and must be opted into by using
2583 * {@link android.app.Notification.Builder#setShowWhen(boolean)}
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002584 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002585 * @see Notification#when
Joe Onoratocb109a02011-01-18 17:57:41 -08002586 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002587 public Builder setWhen(long when) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002588 mN.when = when;
Joe Onorato46439ce2010-11-19 13:56:21 -08002589 return this;
2590 }
2591
Joe Onoratocb109a02011-01-18 17:57:41 -08002592 /**
Griff Hazen50c11652014-05-16 09:46:31 -07002593 * Control whether the timestamp set with {@link #setWhen(long) setWhen} is shown
Daniel Sandler0c890492012-09-12 17:23:10 -07002594 * in the content view.
Selim Cinek0ff1ce602016-04-05 18:27:16 -07002595 * For apps targeting {@link android.os.Build.VERSION_CODES#N} and above, this defaults to
2596 * {@code false}. For earlier apps, the default is {@code true}.
Daniel Sandler0c890492012-09-12 17:23:10 -07002597 */
2598 public Builder setShowWhen(boolean show) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002599 mN.extras.putBoolean(EXTRA_SHOW_WHEN, show);
Daniel Sandler0c890492012-09-12 17:23:10 -07002600 return this;
2601 }
2602
2603 /**
Daniel Sandlerd33b8032012-05-10 11:41:48 -04002604 * Show the {@link Notification#when} field as a stopwatch.
Joe Malin8d40d042012-11-05 11:36:40 -08002605 *
2606 * Instead of presenting <code>when</code> as a timestamp, the notification will show an
Daniel Sandlerd33b8032012-05-10 11:41:48 -04002607 * automatically updating display of the minutes and seconds since <code>when</code>.
Daniel Sandlera2985ed2012-04-03 16:42:00 -04002608 *
Daniel Sandlerd33b8032012-05-10 11:41:48 -04002609 * Useful when showing an elapsed time (like an ongoing phone call).
2610 *
Selim Cinek81c23aa2016-02-25 16:23:13 -08002611 * The counter can also be set to count down to <code>when</code> when using
Adrian Roos96b7e202016-05-17 13:50:38 -07002612 * {@link #setChronometerCountDown(boolean)}.
Selim Cinek81c23aa2016-02-25 16:23:13 -08002613 *
Daniel Sandlerd33b8032012-05-10 11:41:48 -04002614 * @see android.widget.Chronometer
Daniel Sandlera2985ed2012-04-03 16:42:00 -04002615 * @see Notification#when
Adrian Roos96b7e202016-05-17 13:50:38 -07002616 * @see #setChronometerCountDown(boolean)
Daniel Sandlera2985ed2012-04-03 16:42:00 -04002617 */
2618 public Builder setUsesChronometer(boolean b) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002619 mN.extras.putBoolean(EXTRA_SHOW_CHRONOMETER, b);
Daniel Sandlera2985ed2012-04-03 16:42:00 -04002620 return this;
2621 }
2622
2623 /**
Selim Cinek81c23aa2016-02-25 16:23:13 -08002624 * Sets the Chronometer to count down instead of counting up.
2625 *
2626 * <p>This is only relevant if {@link #setUsesChronometer(boolean)} has been set to true.
2627 * If it isn't set the chronometer will count up.
2628 *
2629 * @see #setUsesChronometer(boolean)
2630 */
Adrian Roos96b7e202016-05-17 13:50:38 -07002631 public Builder setChronometerCountDown(boolean countDown) {
2632 mN.extras.putBoolean(EXTRA_CHRONOMETER_COUNT_DOWN, countDown);
Selim Cinek81c23aa2016-02-25 16:23:13 -08002633 return this;
2634 }
2635
2636 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002637 * Set the small icon resource, which will be used to represent the notification in the
2638 * status bar.
Joe Onoratocb109a02011-01-18 17:57:41 -08002639 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002640
2641 * The platform template for the expanded view will draw this icon in the left, unless a
2642 * {@link #setLargeIcon(Bitmap) large icon} has also been specified, in which case the small
2643 * icon will be moved to the right-hand side.
2644 *
2645
2646 * @param icon
2647 * A resource ID in the application's package of the drawable to use.
2648 * @see Notification#icon
Joe Onoratocb109a02011-01-18 17:57:41 -08002649 */
Tor Norbye7b9c9122013-05-30 16:48:33 -07002650 public Builder setSmallIcon(@DrawableRes int icon) {
Dan Sandlerd63f9322015-05-06 15:18:49 -04002651 return setSmallIcon(icon != 0
2652 ? Icon.createWithResource(mContext, icon)
2653 : null);
Joe Onorato46439ce2010-11-19 13:56:21 -08002654 }
2655
Joe Onoratocb109a02011-01-18 17:57:41 -08002656 /**
2657 * A variant of {@link #setSmallIcon(int) setSmallIcon(int)} that takes an additional
2658 * level parameter for when the icon is a {@link android.graphics.drawable.LevelListDrawable
2659 * LevelListDrawable}.
2660 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002661 * @param icon A resource ID in the application's package of the drawable to use.
Joe Onoratocb109a02011-01-18 17:57:41 -08002662 * @param level The level to use for the icon.
2663 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002664 * @see Notification#icon
2665 * @see Notification#iconLevel
Joe Onoratocb109a02011-01-18 17:57:41 -08002666 */
Tor Norbye7b9c9122013-05-30 16:48:33 -07002667 public Builder setSmallIcon(@DrawableRes int icon, int level) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002668 mN.iconLevel = level;
Dan Sandlerd63f9322015-05-06 15:18:49 -04002669 return setSmallIcon(icon);
2670 }
2671
2672 /**
2673 * Set the small icon, which will be used to represent the notification in the
2674 * status bar and content view (unless overriden there by a
2675 * {@link #setLargeIcon(Bitmap) large icon}).
2676 *
2677 * @param icon An Icon object to use.
2678 * @see Notification#icon
2679 */
2680 public Builder setSmallIcon(Icon icon) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002681 mN.setSmallIcon(icon);
2682 if (icon != null && icon.getType() == Icon.TYPE_RESOURCE) {
2683 mN.icon = icon.getResId();
2684 }
Joe Onorato46439ce2010-11-19 13:56:21 -08002685 return this;
2686 }
2687
Joe Onoratocb109a02011-01-18 17:57:41 -08002688 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002689 * Set the first line of text in the platform notification template.
Joe Onoratocb109a02011-01-18 17:57:41 -08002690 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002691 public Builder setContentTitle(CharSequence title) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002692 mN.extras.putCharSequence(EXTRA_TITLE, safeCharSequence(title));
Joe Onorato46439ce2010-11-19 13:56:21 -08002693 return this;
2694 }
2695
Joe Onoratocb109a02011-01-18 17:57:41 -08002696 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002697 * Set the second line of text in the platform notification template.
Joe Onoratocb109a02011-01-18 17:57:41 -08002698 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002699 public Builder setContentText(CharSequence text) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002700 mN.extras.putCharSequence(EXTRA_TEXT, safeCharSequence(text));
Joe Onorato46439ce2010-11-19 13:56:21 -08002701 return this;
2702 }
2703
Joe Onoratocb109a02011-01-18 17:57:41 -08002704 /**
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07002705 * This provides some additional information that is displayed in the notification. No
2706 * guarantees are given where exactly it is displayed.
2707 *
2708 * <p>This information should only be provided if it provides an essential
2709 * benefit to the understanding of the notification. The more text you provide the
2710 * less readable it becomes. For example, an email client should only provide the account
2711 * name here if more than one email account has been added.</p>
2712 *
2713 * <p>As of {@link android.os.Build.VERSION_CODES#N} this information is displayed in the
2714 * notification header area.
2715 *
2716 * On Android versions before {@link android.os.Build.VERSION_CODES#N}
2717 * this will be shown in the third line of text in the platform notification template.
2718 * You should not be using {@link #setProgress(int, int, boolean)} at the
2719 * same time on those versions; they occupy the same place.
2720 * </p>
Daniel Sandlerf3b73432012-03-27 15:01:25 -04002721 */
2722 public Builder setSubText(CharSequence text) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002723 mN.extras.putCharSequence(EXTRA_SUB_TEXT, safeCharSequence(text));
Daniel Sandlerf3b73432012-03-27 15:01:25 -04002724 return this;
2725 }
2726
2727 /**
Adrian Roose458aa82015-12-08 16:17:19 -08002728 * Set the remote input history.
2729 *
2730 * This should be set to the most recent inputs that have been sent
2731 * through a {@link RemoteInput} of this Notification and cleared once the it is no
2732 * longer relevant (e.g. for chat notifications once the other party has responded).
2733 *
2734 * The most recent input must be stored at the 0 index, the second most recent at the
2735 * 1 index, etc. Note that the system will limit both how far back the inputs will be shown
2736 * and how much of each individual input is shown.
2737 *
2738 * <p>Note: The reply text will only be shown on notifications that have least one action
2739 * with a {@code RemoteInput}.</p>
2740 */
2741 public Builder setRemoteInputHistory(CharSequence[] text) {
2742 if (text == null) {
2743 mN.extras.putCharSequenceArray(EXTRA_REMOTE_INPUT_HISTORY, null);
2744 } else {
2745 final int N = Math.min(MAX_REPLY_HISTORY, text.length);
2746 CharSequence[] safe = new CharSequence[N];
2747 for (int i = 0; i < N; i++) {
2748 safe[i] = safeCharSequence(text[i]);
2749 }
2750 mN.extras.putCharSequenceArray(EXTRA_REMOTE_INPUT_HISTORY, safe);
2751 }
2752 return this;
2753 }
2754
2755 /**
Joe Onoratocb109a02011-01-18 17:57:41 -08002756 * Set the large number at the right-hand side of the notification. This is
2757 * equivalent to setContentInfo, although it might show the number in a different
2758 * font size for readability.
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07002759 *
2760 * @deprecated this number is not shown anywhere anymore
Joe Onoratocb109a02011-01-18 17:57:41 -08002761 */
Aurimas Liutikas514c5ef2016-05-24 15:22:55 -07002762 @Deprecated
Joe Onorato8595a3d2010-11-19 18:12:07 -08002763 public Builder setNumber(int number) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002764 mN.number = number;
Joe Onorato8595a3d2010-11-19 18:12:07 -08002765 return this;
2766 }
2767
Joe Onoratocb109a02011-01-18 17:57:41 -08002768 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002769 * A small piece of additional information pertaining to this notification.
2770 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002771 * The platform template will draw this on the last line of the notification, at the far
2772 * right (to the right of a smallIcon if it has been placed there).
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07002773 *
2774 * @deprecated use {@link #setSubText(CharSequence)} instead to set a text in the header.
2775 * For legacy apps targeting a version below {@link android.os.Build.VERSION_CODES#N} this
2776 * field will still show up, but the subtext will take precedence.
Joe Onoratocb109a02011-01-18 17:57:41 -08002777 */
Aurimas Liutikas514c5ef2016-05-24 15:22:55 -07002778 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08002779 public Builder setContentInfo(CharSequence info) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002780 mN.extras.putCharSequence(EXTRA_INFO_TEXT, safeCharSequence(info));
Joe Onorato46439ce2010-11-19 13:56:21 -08002781 return this;
2782 }
2783
Joe Onoratocb109a02011-01-18 17:57:41 -08002784 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002785 * Set the progress this notification represents.
2786 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002787 * The platform template will represent this using a {@link ProgressBar}.
Jeff Sharkey1c400132011-08-05 14:50:13 -07002788 */
2789 public Builder setProgress(int max, int progress, boolean indeterminate) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002790 mN.extras.putInt(EXTRA_PROGRESS, progress);
2791 mN.extras.putInt(EXTRA_PROGRESS_MAX, max);
2792 mN.extras.putBoolean(EXTRA_PROGRESS_INDETERMINATE, indeterminate);
Jeff Sharkey1c400132011-08-05 14:50:13 -07002793 return this;
2794 }
2795
2796 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002797 * Supply a custom RemoteViews to use instead of the platform template.
2798 *
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002799 * Use {@link #setCustomContentView(RemoteViews)} instead.
Joe Onoratocb109a02011-01-18 17:57:41 -08002800 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002801 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08002802 public Builder setContent(RemoteViews views) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002803 return setCustomContentView(views);
2804 }
2805
2806 /**
2807 * Supply custom RemoteViews to use instead of the platform template.
2808 *
2809 * This will override the layout that would otherwise be constructed by this Builder
2810 * object.
2811 */
2812 public Builder setCustomContentView(RemoteViews contentView) {
2813 mN.contentView = contentView;
2814 return this;
2815 }
2816
2817 /**
2818 * Supply custom RemoteViews to use instead of the platform template in the expanded form.
2819 *
2820 * This will override the expanded layout that would otherwise be constructed by this
2821 * Builder object.
2822 */
2823 public Builder setCustomBigContentView(RemoteViews contentView) {
2824 mN.bigContentView = contentView;
2825 return this;
2826 }
2827
2828 /**
2829 * Supply custom RemoteViews to use instead of the platform template in the heads up dialog.
2830 *
2831 * This will override the heads-up layout that would otherwise be constructed by this
2832 * Builder object.
2833 */
2834 public Builder setCustomHeadsUpContentView(RemoteViews contentView) {
2835 mN.headsUpContentView = contentView;
Joe Onorato46439ce2010-11-19 13:56:21 -08002836 return this;
2837 }
2838
Joe Onoratocb109a02011-01-18 17:57:41 -08002839 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002840 * Supply a {@link PendingIntent} to be sent when the notification is clicked.
2841 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002842 * As of {@link android.os.Build.VERSION_CODES#HONEYCOMB}, if this field is unset and you
2843 * have specified a custom RemoteViews with {@link #setContent(RemoteViews)}, you can use
2844 * {@link RemoteViews#setOnClickPendingIntent RemoteViews.setOnClickPendingIntent(int,PendingIntent)}
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002845 * to assign PendingIntents to individual views in that custom layout (i.e., to create
Daniel Sandlerf3b73432012-03-27 15:01:25 -04002846 * clickable buttons inside the notification view).
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002847 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002848 * @see Notification#contentIntent Notification.contentIntent
Joe Onoratocb109a02011-01-18 17:57:41 -08002849 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002850 public Builder setContentIntent(PendingIntent intent) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002851 mN.contentIntent = intent;
Joe Onorato46439ce2010-11-19 13:56:21 -08002852 return this;
2853 }
2854
Joe Onoratocb109a02011-01-18 17:57:41 -08002855 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002856 * Supply a {@link PendingIntent} to send when the notification is cleared explicitly by the user.
2857 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002858 * @see Notification#deleteIntent
Joe Onoratocb109a02011-01-18 17:57:41 -08002859 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002860 public Builder setDeleteIntent(PendingIntent intent) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002861 mN.deleteIntent = intent;
Joe Onorato46439ce2010-11-19 13:56:21 -08002862 return this;
2863 }
2864
Joe Onoratocb109a02011-01-18 17:57:41 -08002865 /**
2866 * An intent to launch instead of posting the notification to the status bar.
2867 * Only for use with extremely high-priority notifications demanding the user's
2868 * <strong>immediate</strong> attention, such as an incoming phone call or
2869 * alarm clock that the user has explicitly set to a particular time.
2870 * If this facility is used for something else, please give the user an option
2871 * to turn it off and use a normal notification, as this can be extremely
2872 * disruptive.
2873 *
Chris Wren47c20a12014-06-18 17:27:29 -04002874 * <p>
2875 * The system UI may choose to display a heads-up notification, instead of
2876 * launching this intent, while the user is using the device.
2877 * </p>
2878 *
Joe Onoratocb109a02011-01-18 17:57:41 -08002879 * @param intent The pending intent to launch.
2880 * @param highPriority Passing true will cause this notification to be sent
2881 * even if other notifications are suppressed.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002882 *
2883 * @see Notification#fullScreenIntent
Joe Onoratocb109a02011-01-18 17:57:41 -08002884 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002885 public Builder setFullScreenIntent(PendingIntent intent, boolean highPriority) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002886 mN.fullScreenIntent = intent;
Joe Onorato46439ce2010-11-19 13:56:21 -08002887 setFlag(FLAG_HIGH_PRIORITY, highPriority);
2888 return this;
2889 }
2890
Joe Onoratocb109a02011-01-18 17:57:41 -08002891 /**
Dan Sandler5fcdf6e2014-07-18 11:31:15 -04002892 * Set the "ticker" text which is sent to accessibility services.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002893 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002894 * @see Notification#tickerText
Joe Onoratocb109a02011-01-18 17:57:41 -08002895 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002896 public Builder setTicker(CharSequence tickerText) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002897 mN.tickerText = safeCharSequence(tickerText);
Joe Onorato46439ce2010-11-19 13:56:21 -08002898 return this;
2899 }
2900
Joe Onoratocb109a02011-01-18 17:57:41 -08002901 /**
Dan Sandler5fcdf6e2014-07-18 11:31:15 -04002902 * Obsolete version of {@link #setTicker(CharSequence)}.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002903 *
Joe Onoratocb109a02011-01-18 17:57:41 -08002904 */
Dan Sandler5fcdf6e2014-07-18 11:31:15 -04002905 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08002906 public Builder setTicker(CharSequence tickerText, RemoteViews views) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002907 setTicker(tickerText);
2908 // views is ignored
Joe Onorato46439ce2010-11-19 13:56:21 -08002909 return this;
2910 }
2911
Joe Onoratocb109a02011-01-18 17:57:41 -08002912 /**
Dan Sandlerd63f9322015-05-06 15:18:49 -04002913 * Add a large icon to the notification content view.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002914 *
2915 * In the platform template, this image will be shown on the left of the notification view
Dan Sandlerd63f9322015-05-06 15:18:49 -04002916 * in place of the {@link #setSmallIcon(Icon) small icon} (which will be placed in a small
2917 * badge atop the large icon).
Dan Sandler08a04c12015-05-06 15:18:49 -04002918 */
Dan Sandlerd63f9322015-05-06 15:18:49 -04002919 public Builder setLargeIcon(Bitmap b) {
2920 return setLargeIcon(b != null ? Icon.createWithBitmap(b) : null);
2921 }
2922
2923 /**
2924 * Add a large icon to the notification content view.
2925 *
2926 * In the platform template, this image will be shown on the left of the notification view
2927 * in place of the {@link #setSmallIcon(Icon) small icon} (which will be placed in a small
2928 * badge atop the large icon).
2929 */
2930 public Builder setLargeIcon(Icon icon) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002931 mN.mLargeIcon = icon;
2932 mN.extras.putParcelable(EXTRA_LARGE_ICON, icon);
Joe Onorato46439ce2010-11-19 13:56:21 -08002933 return this;
2934 }
2935
Joe Onoratocb109a02011-01-18 17:57:41 -08002936 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002937 * Set the sound to play.
2938 *
John Spurlockc0650f022014-07-19 13:22:39 -04002939 * It will be played using the {@link #AUDIO_ATTRIBUTES_DEFAULT default audio attributes}
2940 * for notifications.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002941 *
2942 * @see Notification#sound
Joe Onoratocb109a02011-01-18 17:57:41 -08002943 */
Joe Onorato52f80cd2010-11-21 15:34:48 -08002944 public Builder setSound(Uri sound) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002945 mN.sound = sound;
2946 mN.audioAttributes = AUDIO_ATTRIBUTES_DEFAULT;
Joe Onorato52f80cd2010-11-21 15:34:48 -08002947 return this;
2948 }
2949
Joe Onoratocb109a02011-01-18 17:57:41 -08002950 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002951 * Set the sound to play, along with a specific stream on which to play it.
Joe Onoratocb109a02011-01-18 17:57:41 -08002952 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002953 * See {@link android.media.AudioManager} for the <code>STREAM_</code> constants.
2954 *
John Spurlockc0650f022014-07-19 13:22:39 -04002955 * @deprecated use {@link #setSound(Uri, AudioAttributes)} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002956 * @see Notification#sound
Joe Onoratocb109a02011-01-18 17:57:41 -08002957 */
Jean-Michel Trivi81f871e2014-08-06 16:32:38 -07002958 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08002959 public Builder setSound(Uri sound, int streamType) {
Jean-Michel Trivi2f7511f2016-11-28 15:40:27 -08002960 PlayerBase.deprecateStreamTypeForPlayback(streamType, "Notification", "setSound()");
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002961 mN.sound = sound;
2962 mN.audioStreamType = streamType;
Joe Onorato46439ce2010-11-19 13:56:21 -08002963 return this;
2964 }
2965
Joe Onoratocb109a02011-01-18 17:57:41 -08002966 /**
John Spurlockc0650f022014-07-19 13:22:39 -04002967 * Set the sound to play, along with specific {@link AudioAttributes audio attributes} to
2968 * use during playback.
2969 *
John Spurlockc0650f022014-07-19 13:22:39 -04002970 * @see Notification#sound
2971 */
2972 public Builder setSound(Uri sound, AudioAttributes audioAttributes) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002973 mN.sound = sound;
2974 mN.audioAttributes = audioAttributes;
John Spurlockc0650f022014-07-19 13:22:39 -04002975 return this;
2976 }
2977
2978 /**
Joe Onoratocb109a02011-01-18 17:57:41 -08002979 * Set the vibration pattern to use.
2980 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002981 * See {@link android.os.Vibrator#vibrate(long[], int)} for a discussion of the
2982 * <code>pattern</code> parameter.
2983 *
Chris Wren47c20a12014-06-18 17:27:29 -04002984 * <p>
2985 * A notification that vibrates is more likely to be presented as a heads-up notification.
2986 * </p>
2987 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002988 * @see Notification#vibrate
Joe Onoratocb109a02011-01-18 17:57:41 -08002989 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002990 public Builder setVibrate(long[] pattern) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002991 mN.vibrate = pattern;
Joe Onorato46439ce2010-11-19 13:56:21 -08002992 return this;
2993 }
2994
Joe Onoratocb109a02011-01-18 17:57:41 -08002995 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002996 * Set the desired color for the indicator LED on the device, as well as the
2997 * blink duty cycle (specified in milliseconds).
2998 *
2999
3000 * Not all devices will honor all (or even any) of these values.
3001 *
3002
3003 * @see Notification#ledARGB
3004 * @see Notification#ledOnMS
3005 * @see Notification#ledOffMS
Joe Onoratocb109a02011-01-18 17:57:41 -08003006 */
Tor Norbye80756e32015-03-02 09:39:27 -08003007 public Builder setLights(@ColorInt int argb, int onMs, int offMs) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003008 mN.ledARGB = argb;
3009 mN.ledOnMS = onMs;
3010 mN.ledOffMS = offMs;
Julia Reynolds10ee1fc2015-11-09 11:04:55 -05003011 if (onMs != 0 || offMs != 0) {
3012 mN.flags |= FLAG_SHOW_LIGHTS;
3013 }
Joe Onorato46439ce2010-11-19 13:56:21 -08003014 return this;
3015 }
3016
Joe Onoratocb109a02011-01-18 17:57:41 -08003017 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003018 * Set whether this is an "ongoing" notification.
Joe Onoratocb109a02011-01-18 17:57:41 -08003019 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003020
3021 * Ongoing notifications cannot be dismissed by the user, so your application or service
3022 * must take care of canceling them.
3023 *
3024
3025 * They are typically used to indicate a background task that the user is actively engaged
3026 * with (e.g., playing music) or is pending in some way and therefore occupying the device
3027 * (e.g., a file download, sync operation, active network connection).
3028 *
3029
3030 * @see Notification#FLAG_ONGOING_EVENT
3031 * @see Service#setForeground(boolean)
Joe Onoratocb109a02011-01-18 17:57:41 -08003032 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003033 public Builder setOngoing(boolean ongoing) {
3034 setFlag(FLAG_ONGOING_EVENT, ongoing);
3035 return this;
3036 }
3037
Joe Onoratocb109a02011-01-18 17:57:41 -08003038 /**
Selim Cinek7b9605b2017-01-19 17:36:00 -08003039 * Set whether this notification should be colorized. When set, the color set with
3040 * {@link #setColor(int)} will be used as the background color of this notification.
3041 * <p>
3042 * The coloring will only be applied if the notification is ongoing.
3043 * This should only be used for high priority ongoing tasks like navigation, an ongoing
3044 * call, or other similarly high-priority events for the user.
3045 *
3046 * @see Builder#setOngoing(boolean)
3047 * @see Builder#setColor(int)
3048 */
3049 public Builder setColorized(boolean colorize) {
3050 mN.extras.putBoolean(EXTRA_COLORIZED, colorize);
3051 return this;
3052 }
3053
3054 /**
Joe Onoratocb109a02011-01-18 17:57:41 -08003055 * Set this flag if you would only like the sound, vibrate
3056 * and ticker to be played if the notification is not already showing.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003057 *
3058 * @see Notification#FLAG_ONLY_ALERT_ONCE
Joe Onoratocb109a02011-01-18 17:57:41 -08003059 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003060 public Builder setOnlyAlertOnce(boolean onlyAlertOnce) {
3061 setFlag(FLAG_ONLY_ALERT_ONCE, onlyAlertOnce);
3062 return this;
3063 }
3064
Joe Onoratocb109a02011-01-18 17:57:41 -08003065 /**
Julia Reynolds04499532016-09-13 14:04:53 -04003066 * Make this notification automatically dismissed when the user touches it.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003067 *
3068 * @see Notification#FLAG_AUTO_CANCEL
Joe Onoratocb109a02011-01-18 17:57:41 -08003069 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003070 public Builder setAutoCancel(boolean autoCancel) {
Joe Onorato281d83f2011-01-04 17:13:10 -08003071 setFlag(FLAG_AUTO_CANCEL, autoCancel);
Joe Onorato46439ce2010-11-19 13:56:21 -08003072 return this;
3073 }
3074
Joe Onoratocb109a02011-01-18 17:57:41 -08003075 /**
Griff Hazendfcb0802014-02-11 12:00:00 -08003076 * Set whether or not this notification should not bridge to other devices.
3077 *
3078 * <p>Some notifications can be bridged to other devices for remote display.
3079 * This hint can be set to recommend this notification not be bridged.
3080 */
3081 public Builder setLocalOnly(boolean localOnly) {
3082 setFlag(FLAG_LOCAL_ONLY, localOnly);
3083 return this;
3084 }
3085
3086 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003087 * Set which notification properties will be inherited from system defaults.
Joe Onoratocb109a02011-01-18 17:57:41 -08003088 * <p>
3089 * The value should be one or more of the following fields combined with
3090 * bitwise-or:
3091 * {@link #DEFAULT_SOUND}, {@link #DEFAULT_VIBRATE}, {@link #DEFAULT_LIGHTS}.
3092 * <p>
3093 * For all default values, use {@link #DEFAULT_ALL}.
3094 */
Joe Onorato46439ce2010-11-19 13:56:21 -08003095 public Builder setDefaults(int defaults) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003096 mN.defaults = defaults;
Joe Onorato46439ce2010-11-19 13:56:21 -08003097 return this;
3098 }
3099
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003100 /**
3101 * Set the priority of this notification.
3102 *
3103 * @see Notification#priority
3104 */
Tor Norbyed9273d62013-05-30 15:59:53 -07003105 public Builder setPriority(@Priority int pri) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003106 mN.priority = pri;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003107 return this;
3108 }
Joe Malin8d40d042012-11-05 11:36:40 -08003109
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003110 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -04003111 * Set the notification category.
Joe Malin8d40d042012-11-05 11:36:40 -08003112 *
John Spurlockfd7f1e02014-03-18 16:41:57 -04003113 * @see Notification#category
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003114 */
John Spurlockfd7f1e02014-03-18 16:41:57 -04003115 public Builder setCategory(String category) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003116 mN.category = category;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003117 return this;
3118 }
3119
3120 /**
Chris Wrendde75302014-03-26 17:24:15 -04003121 * Add a person that is relevant to this notification.
3122 *
Chris Wrene6c48932014-09-29 17:19:27 -04003123 * <P>
3124 * Depending on user preferences, this annotation may allow the notification to pass
3125 * through interruption filters, and to appear more prominently in the user interface.
3126 * </P>
3127 *
3128 * <P>
3129 * The person should be specified by the {@code String} representation of a
3130 * {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI}.
3131 * </P>
3132 *
3133 * <P>The system will also attempt to resolve {@code mailto:} and {@code tel:} schema
3134 * URIs. The path part of these URIs must exist in the contacts database, in the
3135 * appropriate column, or the reference will be discarded as invalid. Telephone schema
3136 * URIs will be resolved by {@link android.provider.ContactsContract.PhoneLookup}.
3137 * </P>
3138 *
3139 * @param uri A URI for the person.
Chris Wrendde75302014-03-26 17:24:15 -04003140 * @see Notification#EXTRA_PEOPLE
3141 */
Chris Wrene6c48932014-09-29 17:19:27 -04003142 public Builder addPerson(String uri) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003143 mPersonList.add(uri);
Chris Wrendde75302014-03-26 17:24:15 -04003144 return this;
3145 }
3146
3147 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003148 * Set this notification to be part of a group of notifications sharing the same key.
3149 * Grouped notifications may display in a cluster or stack on devices which
3150 * support such rendering.
3151 *
3152 * <p>To make this notification the summary for its group, also call
3153 * {@link #setGroupSummary}. A sort order can be specified for group members by using
3154 * {@link #setSortKey}.
3155 * @param groupKey The group key of the group.
3156 * @return this object for method chaining
3157 */
3158 public Builder setGroup(String groupKey) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003159 mN.mGroupKey = groupKey;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003160 return this;
3161 }
3162
3163 /**
3164 * Set this notification to be the group summary for a group of notifications.
3165 * Grouped notifications may display in a cluster or stack on devices which
Julia Reynolds04499532016-09-13 14:04:53 -04003166 * support such rendering. If thereRequires a group key also be set using {@link #setGroup}.
3167 * The group summary may be suppressed if too few notifications are included in the group.
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003168 * @param isGroupSummary Whether this notification should be a group summary.
3169 * @return this object for method chaining
3170 */
3171 public Builder setGroupSummary(boolean isGroupSummary) {
3172 setFlag(FLAG_GROUP_SUMMARY, isGroupSummary);
3173 return this;
3174 }
3175
3176 /**
3177 * Set a sort key that orders this notification among other notifications from the
3178 * same package. This can be useful if an external sort was already applied and an app
3179 * would like to preserve this. Notifications will be sorted lexicographically using this
3180 * value, although providing different priorities in addition to providing sort key may
3181 * cause this value to be ignored.
3182 *
3183 * <p>This sort key can also be used to order members of a notification group. See
Griff Hazen9e1379f2014-05-20 12:50:51 -07003184 * {@link #setGroup}.
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003185 *
3186 * @see String#compareTo(String)
3187 */
3188 public Builder setSortKey(String sortKey) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003189 mN.mSortKey = sortKey;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003190 return this;
3191 }
3192
3193 /**
Griff Hazen720042b2014-02-24 15:46:56 -08003194 * Merge additional metadata into this notification.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003195 *
Griff Hazen720042b2014-02-24 15:46:56 -08003196 * <p>Values within the Bundle will replace existing extras values in this Builder.
3197 *
3198 * @see Notification#extras
3199 */
Griff Hazen959591e2014-05-15 22:26:18 -07003200 public Builder addExtras(Bundle extras) {
3201 if (extras != null) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003202 mUserExtras.putAll(extras);
Griff Hazen720042b2014-02-24 15:46:56 -08003203 }
3204 return this;
3205 }
3206
3207 /**
3208 * Set metadata for this notification.
3209 *
3210 * <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 -04003211 * current contents are copied into the Notification each time {@link #build()} is
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003212 * called.
3213 *
Griff Hazen720042b2014-02-24 15:46:56 -08003214 * <p>Replaces any existing extras values with those from the provided Bundle.
3215 * Use {@link #addExtras} to merge in metadata instead.
3216 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003217 * @see Notification#extras
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003218 */
Griff Hazen959591e2014-05-15 22:26:18 -07003219 public Builder setExtras(Bundle extras) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003220 if (extras != null) {
3221 mUserExtras = extras;
3222 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05003223 return this;
3224 }
3225
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003226 /**
Griff Hazen720042b2014-02-24 15:46:56 -08003227 * Get the current metadata Bundle used by this notification Builder.
3228 *
3229 * <p>The returned Bundle is shared with this Builder.
3230 *
3231 * <p>The current contents of this Bundle are copied into the Notification each time
3232 * {@link #build()} is called.
3233 *
3234 * @see Notification#extras
3235 */
3236 public Bundle getExtras() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003237 return mUserExtras;
3238 }
3239
3240 private Bundle getAllExtras() {
3241 final Bundle saveExtras = (Bundle) mUserExtras.clone();
3242 saveExtras.putAll(mN.extras);
3243 return saveExtras;
Griff Hazen720042b2014-02-24 15:46:56 -08003244 }
3245
3246 /**
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003247 * Add an action to this notification. Actions are typically displayed by
3248 * the system as a button adjacent to the notification content.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04003249 * <p>
3250 * Every action must have an icon (32dp square and matching the
3251 * <a href="{@docRoot}design/style/iconography.html#action-bar">Holo
3252 * Dark action bar</a> visual style), a textual label, and a {@link PendingIntent}.
3253 * <p>
3254 * A notification in its expanded form can display up to 3 actions, from left to right in
3255 * the order they were added. Actions will not be displayed when the notification is
3256 * collapsed, however, so be sure that any essential functions may be accessed by the user
3257 * in some other way (for example, in the Activity pointed to by {@link #contentIntent}).
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003258 *
3259 * @param icon Resource ID of a drawable that represents the action.
3260 * @param title Text describing the action.
3261 * @param intent PendingIntent to be fired when the action is invoked.
Dan Sandler86647982015-05-13 23:41:13 -04003262 *
3263 * @deprecated Use {@link #addAction(Action)} instead.
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003264 */
Dan Sandler86647982015-05-13 23:41:13 -04003265 @Deprecated
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003266 public Builder addAction(int icon, CharSequence title, PendingIntent intent) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04003267 mActions.add(new Action(icon, safeCharSequence(title), intent));
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003268 return this;
3269 }
3270
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003271 /**
Griff Hazen959591e2014-05-15 22:26:18 -07003272 * Add an action to this notification. Actions are typically displayed by
3273 * the system as a button adjacent to the notification content.
3274 * <p>
3275 * Every action must have an icon (32dp square and matching the
3276 * <a href="{@docRoot}design/style/iconography.html#action-bar">Holo
3277 * Dark action bar</a> visual style), a textual label, and a {@link PendingIntent}.
3278 * <p>
3279 * A notification in its expanded form can display up to 3 actions, from left to right in
3280 * the order they were added. Actions will not be displayed when the notification is
3281 * collapsed, however, so be sure that any essential functions may be accessed by the user
3282 * in some other way (for example, in the Activity pointed to by {@link #contentIntent}).
3283 *
3284 * @param action The action to add.
3285 */
3286 public Builder addAction(Action action) {
3287 mActions.add(action);
3288 return this;
3289 }
3290
3291 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003292 * Alter the complete list of actions attached to this notification.
3293 * @see #addAction(Action).
3294 *
3295 * @param actions
3296 * @return
3297 */
3298 public Builder setActions(Action... actions) {
3299 mActions.clear();
3300 for (int i = 0; i < actions.length; i++) {
3301 mActions.add(actions[i]);
3302 }
3303 return this;
3304 }
3305
3306 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003307 * Add a rich notification style to be applied at build time.
3308 *
3309 * @param style Object responsible for modifying the notification style.
3310 */
3311 public Builder setStyle(Style style) {
3312 if (mStyle != style) {
3313 mStyle = style;
Daniel Sandlerc08dea22012-06-28 08:35:24 -07003314 if (mStyle != null) {
3315 mStyle.setBuilder(this);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003316 mN.extras.putString(EXTRA_TEMPLATE, style.getClass().getName());
3317 } else {
3318 mN.extras.remove(EXTRA_TEMPLATE);
Daniel Sandlerc08dea22012-06-28 08:35:24 -07003319 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003320 }
3321 return this;
3322 }
3323
Dan Sandler0bf2ed82013-12-21 23:33:41 -06003324 /**
3325 * Specify the value of {@link #visibility}.
Griff Hazenb720abe2014-05-20 13:15:30 -07003326 *
Dan Sandler0bf2ed82013-12-21 23:33:41 -06003327 * @param visibility One of {@link #VISIBILITY_PRIVATE} (the default),
3328 * {@link #VISIBILITY_SECRET}, or {@link #VISIBILITY_PUBLIC}.
3329 *
3330 * @return The same Builder.
3331 */
3332 public Builder setVisibility(int visibility) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003333 mN.visibility = visibility;
Dan Sandler0bf2ed82013-12-21 23:33:41 -06003334 return this;
3335 }
3336
3337 /**
3338 * Supply a replacement Notification whose contents should be shown in insecure contexts
3339 * (i.e. atop the secure lockscreen). See {@link #visibility} and {@link #VISIBILITY_PUBLIC}.
3340 * @param n A replacement notification, presumably with some or all info redacted.
3341 * @return The same Builder.
3342 */
3343 public Builder setPublicVersion(Notification n) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003344 if (n != null) {
3345 mN.publicVersion = new Notification();
3346 n.cloneInto(mN.publicVersion, /*heavy=*/ true);
3347 } else {
3348 mN.publicVersion = null;
3349 }
Dan Sandler0bf2ed82013-12-21 23:33:41 -06003350 return this;
3351 }
3352
Griff Hazenb720abe2014-05-20 13:15:30 -07003353 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003354 * Apply an extender to this notification builder. Extenders may be used to add
3355 * metadata or change options on this builder.
3356 */
Griff Hazen61a9e862014-05-22 16:05:19 -07003357 public Builder extend(Extender extender) {
3358 extender.extend(this);
Griff Hazen5cadc3b2014-05-20 09:55:39 -07003359 return this;
3360 }
3361
Dan Sandler4e787062015-06-17 15:09:48 -04003362 /**
3363 * @hide
3364 */
Julia Reynoldse46bb372016-03-17 11:05:58 -04003365 public Builder setFlag(int mask, boolean value) {
Joe Onorato46439ce2010-11-19 13:56:21 -08003366 if (value) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003367 mN.flags |= mask;
Joe Onorato46439ce2010-11-19 13:56:21 -08003368 } else {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003369 mN.flags &= ~mask;
Joe Onorato46439ce2010-11-19 13:56:21 -08003370 }
Julia Reynoldse46bb372016-03-17 11:05:58 -04003371 return this;
Joe Onorato46439ce2010-11-19 13:56:21 -08003372 }
3373
Dan Sandler26e81cf2014-05-06 10:01:27 -04003374 /**
3375 * Sets {@link Notification#color}.
3376 *
3377 * @param argb The accent color to use
3378 *
3379 * @return The same Builder.
3380 */
Tor Norbye80756e32015-03-02 09:39:27 -08003381 public Builder setColor(@ColorInt int argb) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003382 mN.color = argb;
Julia Reynolds10ee1fc2015-11-09 11:04:55 -05003383 sanitizeColor();
Dan Sandler26e81cf2014-05-06 10:01:27 -04003384 return this;
3385 }
3386
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003387 private Drawable getProfileBadgeDrawable() {
Chris Wren66619a22016-05-12 16:42:37 -04003388 if (mContext.getUserId() == UserHandle.USER_SYSTEM) {
3389 // This user can never be a badged profile,
3390 // and also includes USER_ALL system notifications.
3391 return null;
3392 }
Christoph Studer7ac80e62014-08-04 16:01:57 +02003393 // Note: This assumes that the current user can read the profile badge of the
3394 // originating user.
Selim Cineke6ff9462016-01-15 15:07:06 -08003395 return mContext.getPackageManager().getUserBadgeForDensityNoBackground(
Julia Reynoldsda303542015-11-23 14:00:20 -05003396 new UserHandle(mContext.getUserId()), 0);
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003397 }
3398
3399 private Bitmap getProfileBadge() {
3400 Drawable badge = getProfileBadgeDrawable();
Kenny Guy8a0101b2014-05-08 23:34:12 +01003401 if (badge == null) {
3402 return null;
3403 }
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003404 final int size = mContext.getResources().getDimensionPixelSize(
3405 R.dimen.notification_badge_size);
3406 Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
Kenny Guy8a0101b2014-05-08 23:34:12 +01003407 Canvas canvas = new Canvas(bitmap);
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003408 badge.setBounds(0, 0, size, size);
Kenny Guy8a0101b2014-05-08 23:34:12 +01003409 badge.draw(canvas);
3410 return bitmap;
3411 }
3412
Selim Cinekc848c3a2016-01-13 15:27:30 -08003413 private void bindProfileBadge(RemoteViews contentView) {
Kenny Guy98193ea2014-07-24 19:54:37 +01003414 Bitmap profileBadge = getProfileBadge();
3415
Kenny Guy98193ea2014-07-24 19:54:37 +01003416 if (profileBadge != null) {
Selim Cinekc848c3a2016-01-13 15:27:30 -08003417 contentView.setImageViewBitmap(R.id.profile_badge, profileBadge);
3418 contentView.setViewVisibility(R.id.profile_badge, View.VISIBLE);
Selim Cinek7b9605b2017-01-19 17:36:00 -08003419 if (isColorized()) {
3420 contentView.setDrawableParameters(R.id.profile_badge, false, -1,
3421 getPrimaryTextColor(), PorterDuff.Mode.SRC_ATOP, -1);
3422 }
Kenny Guy98193ea2014-07-24 19:54:37 +01003423 }
Kenny Guy98193ea2014-07-24 19:54:37 +01003424 }
3425
Christoph Studerfe718432014-09-01 18:21:18 +02003426 private void resetStandardTemplate(RemoteViews contentView) {
Selim Cinekeaa29ca2015-11-23 13:51:13 -08003427 resetNotificationHeader(contentView);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003428 resetContentMargins(contentView);
Christoph Studerfe718432014-09-01 18:21:18 +02003429 contentView.setViewVisibility(R.id.right_icon, View.GONE);
Selim Cinek860b6da2015-12-16 19:02:19 -08003430 contentView.setViewVisibility(R.id.title, View.GONE);
Christoph Studerfe718432014-09-01 18:21:18 +02003431 contentView.setTextViewText(R.id.title, null);
Selim Cinek41598732016-01-11 16:58:37 -08003432 contentView.setViewVisibility(R.id.text, View.GONE);
Christoph Studerfe718432014-09-01 18:21:18 +02003433 contentView.setTextViewText(R.id.text, null);
Selim Cinek29603462015-11-17 19:04:39 -08003434 contentView.setViewVisibility(R.id.text_line_1, View.GONE);
Selim Cinek41598732016-01-11 16:58:37 -08003435 contentView.setTextViewText(R.id.text_line_1, null);
Christoph Studerfe718432014-09-01 18:21:18 +02003436 contentView.setViewVisibility(R.id.progress, View.GONE);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003437 }
3438
Selim Cinekeaa29ca2015-11-23 13:51:13 -08003439 /**
3440 * Resets the notification header to its original state
3441 */
3442 private void resetNotificationHeader(RemoteViews contentView) {
Adrian Roosc4337a32016-08-02 18:30:34 -07003443 // Small icon doesn't need to be reset, as it's always set. Resetting would prevent
3444 // re-using the drawable when the notification is updated.
Selim Cinek7b836392015-12-04 20:02:59 -08003445 contentView.setBoolean(R.id.notification_header, "setExpanded", false);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003446 contentView.setTextViewText(R.id.app_name_text, null);
Christoph Studerca1db712014-09-10 17:31:33 +02003447 contentView.setViewVisibility(R.id.chronometer, View.GONE);
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07003448 contentView.setViewVisibility(R.id.header_text, View.GONE);
Adrian Roos9dfb78f2016-06-30 15:43:44 -07003449 contentView.setTextViewText(R.id.header_text, null);
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07003450 contentView.setViewVisibility(R.id.header_text_divider, View.GONE);
Selim Cinek29603462015-11-17 19:04:39 -08003451 contentView.setViewVisibility(R.id.time_divider, View.GONE);
Selim Cinekb85f36fd2016-04-20 18:46:36 -07003452 contentView.setViewVisibility(R.id.time, View.GONE);
Selim Cinekc848c3a2016-01-13 15:27:30 -08003453 contentView.setImageViewIcon(R.id.profile_badge, null);
3454 contentView.setViewVisibility(R.id.profile_badge, View.GONE);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003455 }
3456
3457 private void resetContentMargins(RemoteViews contentView) {
Adrian Roos2d5dbba2016-06-08 17:11:53 -07003458 contentView.setViewLayoutMarginEndDimen(R.id.line1, 0);
3459 contentView.setViewLayoutMarginEndDimen(R.id.text, 0);
Christoph Studerfe718432014-09-01 18:21:18 +02003460 }
3461
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003462 private RemoteViews applyStandardTemplate(int resId) {
Adrian Roos70d7aa32017-01-11 15:39:06 -08003463 return applyStandardTemplate(resId, mParams.reset().fillTextsFrom(this));
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02003464 }
3465
3466 /**
3467 * @param hasProgress whether the progress bar should be shown and set
3468 */
3469 private RemoteViews applyStandardTemplate(int resId, boolean hasProgress) {
Adrian Roos70d7aa32017-01-11 15:39:06 -08003470 return applyStandardTemplate(resId, mParams.reset().hasProgress(hasProgress)
3471 .fillTextsFrom(this));
Adrian Roosc1a80b02016-04-05 14:54:55 -07003472 }
3473
Adrian Roos70d7aa32017-01-11 15:39:06 -08003474 private RemoteViews applyStandardTemplate(int resId, StandardTemplateParams p) {
Kenny Guy77320062014-08-27 21:37:15 +01003475 RemoteViews contentView = new BuilderRemoteViews(mContext.getApplicationInfo(), resId);
Dan Sandler539aad42014-08-04 00:43:39 -04003476
Christoph Studerfe718432014-09-01 18:21:18 +02003477 resetStandardTemplate(contentView);
3478
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003479 final Bundle ex = mN.extras;
Selim Cinek7b9605b2017-01-19 17:36:00 -08003480 updateBackgroundColor(contentView);
Adrian Roos487374f2017-01-11 15:48:14 -08003481 bindNotificationHeader(contentView, p.ambient);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003482 bindLargeIcon(contentView);
Adrian Roos70d7aa32017-01-11 15:39:06 -08003483 boolean showProgress = handleProgressBar(p.hasProgress, contentView, ex);
3484 if (p.title != null) {
Selim Cinek860b6da2015-12-16 19:02:19 -08003485 contentView.setViewVisibility(R.id.title, View.VISIBLE);
Adrian Roos70d7aa32017-01-11 15:39:06 -08003486 contentView.setTextViewText(R.id.title, p.title);
Selim Cinek7b9605b2017-01-19 17:36:00 -08003487 setTextViewColorPrimary(contentView, R.id.title);
Selim Cinek954cc232016-05-20 13:29:23 -07003488 contentView.setViewLayoutWidth(R.id.title, showProgress
3489 ? ViewGroup.LayoutParams.WRAP_CONTENT
3490 : ViewGroup.LayoutParams.MATCH_PARENT);
Joe Onorato561d3852010-11-20 18:09:34 -08003491 }
Adrian Roos70d7aa32017-01-11 15:39:06 -08003492 if (p.text != null) {
Selim Cinek41598732016-01-11 16:58:37 -08003493 int textId = showProgress ? com.android.internal.R.id.text_line_1
3494 : com.android.internal.R.id.text;
Adrian Roos70d7aa32017-01-11 15:39:06 -08003495 contentView.setTextViewText(textId, p.text);
Selim Cinek7b9605b2017-01-19 17:36:00 -08003496 setTextViewColorSecondary(contentView, textId);
Selim Cinek41598732016-01-11 16:58:37 -08003497 contentView.setViewVisibility(textId, View.VISIBLE);
Joe Onorato561d3852010-11-20 18:09:34 -08003498 }
Selim Cinekc848c3a2016-01-13 15:27:30 -08003499
Selim Cinek279fa862016-06-14 10:57:25 -07003500 setContentMinHeight(contentView, showProgress || mN.hasLargeIcon());
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003501
Selim Cinek29603462015-11-17 19:04:39 -08003502 return contentView;
3503 }
3504
Selim Cinek7b9605b2017-01-19 17:36:00 -08003505 private void setTextViewColorPrimary(RemoteViews contentView, int id) {
3506 ensureColors();
3507 contentView.setTextColor(id, mPrimaryTextColor);
3508 }
3509
3510 private int getPrimaryTextColor() {
3511 ensureColors();
3512 return mPrimaryTextColor;
3513 }
3514
3515 private int getActionBarColor() {
3516 ensureColors();
3517 return mActionBarColor;
3518 }
3519
3520 private void setTextViewColorSecondary(RemoteViews contentView, int id) {
3521 ensureColors();
3522 contentView.setTextColor(id, mSecondaryTextColor);
3523 }
3524
3525 private void ensureColors() {
3526 int backgroundColor = getBackgroundColor();
3527 if (mPrimaryTextColor == COLOR_INVALID
3528 || mSecondaryTextColor == COLOR_INVALID
3529 || mActionBarColor == COLOR_INVALID
3530 || mTextColorsAreForBackground != backgroundColor) {
3531 mTextColorsAreForBackground = backgroundColor;
3532 mPrimaryTextColor = NotificationColorUtil.resolvePrimaryColor(
3533 mContext, backgroundColor);
3534 mSecondaryTextColor = NotificationColorUtil.resolveSecondaryColor(
3535 mContext, backgroundColor);
3536 mActionBarColor = NotificationColorUtil.resolveActionBarColor(backgroundColor);
3537 }
3538 }
3539
3540 private void updateBackgroundColor(RemoteViews contentView) {
3541 if (isColorized()) {
3542 contentView.setInt(R.id.status_bar_latest_event_content, "setBackgroundColor",
3543 getBackgroundColor());
3544 } else {
3545 // Clear it!
3546 contentView.setInt(R.id.status_bar_latest_event_content, "setBackgroundResource",
3547 0);
3548 }
3549 }
3550
Selim Cinek860b6da2015-12-16 19:02:19 -08003551 /**
3552 * @param remoteView the remote view to update the minheight in
3553 * @param hasMinHeight does it have a mimHeight
3554 * @hide
3555 */
3556 void setContentMinHeight(RemoteViews remoteView, boolean hasMinHeight) {
3557 int minHeight = 0;
3558 if (hasMinHeight) {
3559 // we need to set the minHeight of the notification
3560 minHeight = mContext.getResources().getDimensionPixelSize(
3561 com.android.internal.R.dimen.notification_min_content_height);
3562 }
3563 remoteView.setInt(R.id.notification_main_column, "setMinimumHeight", minHeight);
3564 }
3565
Selim Cinek29603462015-11-17 19:04:39 -08003566 private boolean handleProgressBar(boolean hasProgress, RemoteViews contentView, Bundle ex) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003567 final int max = ex.getInt(EXTRA_PROGRESS_MAX, 0);
3568 final int progress = ex.getInt(EXTRA_PROGRESS, 0);
3569 final boolean ind = ex.getBoolean(EXTRA_PROGRESS_INDETERMINATE);
3570 if (hasProgress && (max != 0 || ind)) {
Selim Cinek29603462015-11-17 19:04:39 -08003571 contentView.setViewVisibility(com.android.internal.R.id.progress, View.VISIBLE);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003572 contentView.setProgressBar(
Selim Cinek29603462015-11-17 19:04:39 -08003573 R.id.progress, max, progress, ind);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003574 contentView.setProgressBackgroundTintList(
3575 R.id.progress, ColorStateList.valueOf(mContext.getColor(
3576 R.color.notification_progress_background_color)));
Selim Cinek29603462015-11-17 19:04:39 -08003577 if (mN.color != COLOR_DEFAULT) {
Adrian Roos4ff3b122016-02-01 12:26:13 -08003578 ColorStateList colorStateList = ColorStateList.valueOf(resolveContrastColor());
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003579 contentView.setProgressTintList(R.id.progress, colorStateList);
3580 contentView.setProgressIndeterminateTintList(R.id.progress, colorStateList);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003581 }
Selim Cinek29603462015-11-17 19:04:39 -08003582 return true;
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003583 } else {
3584 contentView.setViewVisibility(R.id.progress, View.GONE);
Selim Cinek29603462015-11-17 19:04:39 -08003585 return false;
Jeff Sharkey1c400132011-08-05 14:50:13 -07003586 }
Joe Onorato561d3852010-11-20 18:09:34 -08003587 }
3588
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003589 private void bindLargeIcon(RemoteViews contentView) {
Selim Cinek279fa862016-06-14 10:57:25 -07003590 if (mN.mLargeIcon == null && mN.largeIcon != null) {
3591 mN.mLargeIcon = Icon.createWithBitmap(mN.largeIcon);
3592 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003593 if (mN.mLargeIcon != null) {
3594 contentView.setViewVisibility(R.id.right_icon, View.VISIBLE);
3595 contentView.setImageViewIcon(R.id.right_icon, mN.mLargeIcon);
3596 processLargeLegacyIcon(mN.mLargeIcon, contentView);
Adrian Roos2d5dbba2016-06-08 17:11:53 -07003597 int endMargin = R.dimen.notification_content_picture_margin;
3598 contentView.setViewLayoutMarginEndDimen(R.id.line1, endMargin);
3599 contentView.setViewLayoutMarginEndDimen(R.id.text, endMargin);
3600 contentView.setViewLayoutMarginEndDimen(R.id.progress, endMargin);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003601 }
3602 }
3603
Adrian Roos487374f2017-01-11 15:48:14 -08003604 private void bindNotificationHeader(RemoteViews contentView, boolean ambient) {
3605 bindSmallIcon(contentView, ambient);
3606 bindHeaderAppName(contentView, ambient);
3607 if (!ambient) {
3608 // Ambient view does not have these
3609 bindHeaderText(contentView);
3610 bindHeaderChronometerAndTime(contentView);
3611 bindExpandButton(contentView);
3612 bindProfileBadge(contentView);
3613 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003614 }
3615
3616 private void bindExpandButton(RemoteViews contentView) {
Selim Cinek7b9605b2017-01-19 17:36:00 -08003617 int color = isColorized() ? getPrimaryTextColor() : resolveContrastColor();
3618 contentView.setDrawableParameters(R.id.expand_button, false, -1, color,
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003619 PorterDuff.Mode.SRC_ATOP, -1);
Selim Cinekea4bef72015-12-02 15:51:10 -08003620 contentView.setInt(R.id.notification_header, "setOriginalNotificationColor",
Selim Cinek7b9605b2017-01-19 17:36:00 -08003621 color);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003622 }
3623
3624 private void bindHeaderChronometerAndTime(RemoteViews contentView) {
3625 if (showsTimeOrChronometer()) {
Selim Cinek29603462015-11-17 19:04:39 -08003626 contentView.setViewVisibility(R.id.time_divider, View.VISIBLE);
Selim Cinek7b9605b2017-01-19 17:36:00 -08003627 setTextViewColorSecondary(contentView, R.id.time_divider);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003628 if (mN.extras.getBoolean(EXTRA_SHOW_CHRONOMETER)) {
3629 contentView.setViewVisibility(R.id.chronometer, View.VISIBLE);
3630 contentView.setLong(R.id.chronometer, "setBase",
3631 mN.when + (SystemClock.elapsedRealtime() - System.currentTimeMillis()));
3632 contentView.setBoolean(R.id.chronometer, "setStarted", true);
Adrian Roos96b7e202016-05-17 13:50:38 -07003633 boolean countsDown = mN.extras.getBoolean(EXTRA_CHRONOMETER_COUNT_DOWN);
Selim Cinekc3b752e2016-04-20 16:13:59 -07003634 contentView.setChronometerCountDown(R.id.chronometer, countsDown);
Selim Cinek7b9605b2017-01-19 17:36:00 -08003635 setTextViewColorSecondary(contentView, R.id.chronometer);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003636 } else {
3637 contentView.setViewVisibility(R.id.time, View.VISIBLE);
3638 contentView.setLong(R.id.time, "setTime", mN.when);
Selim Cinek7b9605b2017-01-19 17:36:00 -08003639 setTextViewColorSecondary(contentView, R.id.time);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003640 }
Selim Cinekb85f36fd2016-04-20 18:46:36 -07003641 } else {
3642 // We still want a time to be set but gone, such that we can show and hide it
3643 // on demand in case it's a child notification without anything in the header
3644 contentView.setLong(R.id.time, "setTime", mN.when != 0 ? mN.when : mN.creationTime);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003645 }
3646 }
3647
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07003648 private void bindHeaderText(RemoteViews contentView) {
3649 CharSequence headerText = mN.extras.getCharSequence(EXTRA_SUB_TEXT);
3650 if (headerText == null && mStyle != null && mStyle.mSummaryTextSet
Selim Cinek03d0d652015-11-13 13:18:09 -05003651 && mStyle.hasSummaryInHeader()) {
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07003652 headerText = mStyle.mSummaryText;
Selim Cinek03d0d652015-11-13 13:18:09 -05003653 }
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07003654 if (headerText == null
3655 && mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N
3656 && mN.extras.getCharSequence(EXTRA_INFO_TEXT) != null) {
3657 headerText = mN.extras.getCharSequence(EXTRA_INFO_TEXT);
3658 }
3659 if (headerText != null) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003660 // TODO: Remove the span entirely to only have the string with propper formating.
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07003661 contentView.setTextViewText(R.id.header_text, processLegacyText(headerText));
Selim Cinek7b9605b2017-01-19 17:36:00 -08003662 setTextViewColorSecondary(contentView, R.id.header_text);
Selim Cinek0f9dd1e2016-04-05 17:03:40 -07003663 contentView.setViewVisibility(R.id.header_text, View.VISIBLE);
3664 contentView.setViewVisibility(R.id.header_text_divider, View.VISIBLE);
Selim Cinek7b9605b2017-01-19 17:36:00 -08003665 setTextViewColorSecondary(contentView, R.id.header_text_divider);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003666 }
3667 }
3668
Adrian Rooseba05822016-04-22 17:09:27 -07003669 /**
3670 * @hide
3671 */
3672 public String loadHeaderAppName() {
Dan Sandler732bd6c2016-04-12 14:20:32 -04003673 CharSequence name = null;
3674 final PackageManager pm = mContext.getPackageManager();
3675 if (mN.extras.containsKey(EXTRA_SUBSTITUTE_APP_NAME)) {
3676 // only system packages which lump together a bunch of unrelated stuff
3677 // may substitute a different name to make the purpose of the
3678 // notification more clear. the correct package label should always
3679 // be accessible via SystemUI.
3680 final String pkg = mContext.getPackageName();
3681 final String subName = mN.extras.getString(EXTRA_SUBSTITUTE_APP_NAME);
3682 if (PackageManager.PERMISSION_GRANTED == pm.checkPermission(
3683 android.Manifest.permission.SUBSTITUTE_NOTIFICATION_APP_NAME, pkg)) {
3684 name = subName;
3685 } else {
3686 Log.w(TAG, "warning: pkg "
3687 + pkg + " attempting to substitute app name '" + subName
3688 + "' without holding perm "
3689 + android.Manifest.permission.SUBSTITUTE_NOTIFICATION_APP_NAME);
3690 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003691 }
Dan Sandler732bd6c2016-04-12 14:20:32 -04003692 if (TextUtils.isEmpty(name)) {
3693 name = pm.getApplicationLabel(mContext.getApplicationInfo());
3694 }
3695 if (TextUtils.isEmpty(name)) {
3696 // still nothing?
3697 return null;
3698 }
3699
3700 return String.valueOf(name);
3701 }
Adrian Roos487374f2017-01-11 15:48:14 -08003702 private void bindHeaderAppName(RemoteViews contentView, boolean ambient) {
Dan Sandler732bd6c2016-04-12 14:20:32 -04003703 contentView.setTextViewText(R.id.app_name_text, loadHeaderAppName());
Selim Cinek7b9605b2017-01-19 17:36:00 -08003704 if (isColorized()) {
3705 setTextViewColorPrimary(contentView, R.id.app_name_text);
3706 } else {
3707 contentView.setTextColor(R.id.app_name_text,
3708 ambient ? resolveAmbientColor() : resolveContrastColor());
3709 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003710 }
3711
Adrian Roos487374f2017-01-11 15:48:14 -08003712 private void bindSmallIcon(RemoteViews contentView, boolean ambient) {
Selim Cinek279fa862016-06-14 10:57:25 -07003713 if (mN.mSmallIcon == null && mN.icon != 0) {
3714 mN.mSmallIcon = Icon.createWithResource(mContext, mN.icon);
3715 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003716 contentView.setImageViewIcon(R.id.icon, mN.mSmallIcon);
Adrian Roos9b45a152016-06-28 13:32:29 -07003717 contentView.setDrawableParameters(R.id.icon, false /* targetBackground */,
3718 -1 /* alpha */, -1 /* colorFilter */, null /* mode */, mN.iconLevel);
Adrian Roos487374f2017-01-11 15:48:14 -08003719 processSmallIconColor(mN.mSmallIcon, contentView, ambient);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003720 }
3721
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003722 /**
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02003723 * @return true if the built notification will show the time or the chronometer; false
3724 * otherwise
3725 */
3726 private boolean showsTimeOrChronometer() {
Selim Cinekc2c0b042016-05-18 17:13:46 -07003727 return mN.showsTime() || mN.showsChronometer();
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02003728 }
3729
Christoph Studerfe718432014-09-01 18:21:18 +02003730 private void resetStandardTemplateWithActions(RemoteViews big) {
Adrian Roos4c1fcc82016-03-31 14:39:39 -07003731 // actions_container is only reset when there are no actions to avoid focus issues with
3732 // remote inputs.
Christoph Studerfe718432014-09-01 18:21:18 +02003733 big.setViewVisibility(R.id.actions, View.GONE);
Christoph Studerfe718432014-09-01 18:21:18 +02003734 big.removeAllViews(R.id.actions);
Adrian Roose458aa82015-12-08 16:17:19 -08003735
3736 big.setViewVisibility(R.id.notification_material_reply_container, View.GONE);
3737 big.setTextViewText(R.id.notification_material_reply_text_1, null);
3738
3739 big.setViewVisibility(R.id.notification_material_reply_text_2, View.GONE);
3740 big.setTextViewText(R.id.notification_material_reply_text_2, null);
3741 big.setViewVisibility(R.id.notification_material_reply_text_3, View.GONE);
3742 big.setTextViewText(R.id.notification_material_reply_text_3, null);
Adrian Roosf852a422016-06-03 13:33:43 -07003743
3744 big.setViewLayoutMarginBottomDimen(R.id.notification_action_list_margin_target, 0);
Christoph Studerfe718432014-09-01 18:21:18 +02003745 }
3746
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003747 private RemoteViews applyStandardTemplateWithActions(int layoutId) {
Adrian Roos70d7aa32017-01-11 15:39:06 -08003748 return applyStandardTemplateWithActions(layoutId, mParams.reset().fillTextsFrom(this));
Adrian Roos48d746a2016-04-12 14:57:28 -07003749 }
3750
Adrian Roos70d7aa32017-01-11 15:39:06 -08003751 private RemoteViews applyStandardTemplateWithActions(int layoutId,
3752 StandardTemplateParams p) {
3753 RemoteViews big = applyStandardTemplate(layoutId, p);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003754
Christoph Studerfe718432014-09-01 18:21:18 +02003755 resetStandardTemplateWithActions(big);
3756
Adrian Roose458aa82015-12-08 16:17:19 -08003757 boolean validRemoteInput = false;
3758
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003759 int N = mActions.size();
Adrian Roos487374f2017-01-11 15:48:14 -08003760 boolean emphazisedMode = mN.fullScreenIntent != null && !p.ambient;
Selim Cinek06e9e1f2016-07-08 17:14:16 -07003761 big.setBoolean(R.id.actions, "setEmphasizedMode", emphazisedMode);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003762 if (N > 0) {
Adrian Roos7052de52016-03-03 15:53:34 -08003763 big.setViewVisibility(R.id.actions_container, View.VISIBLE);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003764 big.setViewVisibility(R.id.actions, View.VISIBLE);
Adrian Roos487374f2017-01-11 15:48:14 -08003765 if (p.ambient) {
3766 big.setInt(R.id.actions, "setBackgroundColor", Color.TRANSPARENT);
Selim Cinek7b9605b2017-01-19 17:36:00 -08003767 } else if (isColorized()) {
3768 big.setInt(R.id.actions, "setBackgroundColor", getActionBarColor());
3769 } else {
3770 big.setInt(R.id.actions, "setBackgroundColor", mContext.getColor(
3771 R.color.notification_action_list));
Adrian Roos487374f2017-01-11 15:48:14 -08003772 }
Adrian Roosf852a422016-06-03 13:33:43 -07003773 big.setViewLayoutMarginBottomDimen(R.id.notification_action_list_margin_target,
3774 R.dimen.notification_action_list_height);
Daniel Sandler8680bf82012-05-15 16:52:52 -04003775 if (N>MAX_ACTION_BUTTONS) N=MAX_ACTION_BUTTONS;
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003776 for (int i=0; i<N; i++) {
Adrian Roose458aa82015-12-08 16:17:19 -08003777 Action action = mActions.get(i);
3778 validRemoteInput |= hasValidRemoteInput(action);
3779
Selim Cinek06e9e1f2016-07-08 17:14:16 -07003780 final RemoteViews button = generateActionButton(action, emphazisedMode,
Adrian Roos487374f2017-01-11 15:48:14 -08003781 i % 2 != 0, p.ambient);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003782 big.addView(R.id.actions, button);
3783 }
Adrian Roos4c1fcc82016-03-31 14:39:39 -07003784 } else {
3785 big.setViewVisibility(R.id.actions_container, View.GONE);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003786 }
Adrian Roose458aa82015-12-08 16:17:19 -08003787
3788 CharSequence[] replyText = mN.extras.getCharSequenceArray(EXTRA_REMOTE_INPUT_HISTORY);
Adrian Roos487374f2017-01-11 15:48:14 -08003789 if (!p.ambient && validRemoteInput && replyText != null
Adrian Roose458aa82015-12-08 16:17:19 -08003790 && replyText.length > 0 && !TextUtils.isEmpty(replyText[0])) {
3791 big.setViewVisibility(R.id.notification_material_reply_container, View.VISIBLE);
3792 big.setTextViewText(R.id.notification_material_reply_text_1, replyText[0]);
Selim Cinek7b9605b2017-01-19 17:36:00 -08003793 setTextViewColorSecondary(big, R.id.notification_material_reply_text_1);
Adrian Roose458aa82015-12-08 16:17:19 -08003794
3795 if (replyText.length > 1 && !TextUtils.isEmpty(replyText[1])) {
3796 big.setViewVisibility(R.id.notification_material_reply_text_2, View.VISIBLE);
3797 big.setTextViewText(R.id.notification_material_reply_text_2, replyText[1]);
Selim Cinek7b9605b2017-01-19 17:36:00 -08003798 setTextViewColorSecondary(big, R.id.notification_material_reply_text_2);
Adrian Roose458aa82015-12-08 16:17:19 -08003799
3800 if (replyText.length > 2 && !TextUtils.isEmpty(replyText[2])) {
3801 big.setViewVisibility(
3802 R.id.notification_material_reply_text_3, View.VISIBLE);
3803 big.setTextViewText(R.id.notification_material_reply_text_3, replyText[2]);
Selim Cinek7b9605b2017-01-19 17:36:00 -08003804 setTextViewColorSecondary(big, R.id.notification_material_reply_text_3);
Adrian Roose458aa82015-12-08 16:17:19 -08003805 }
3806 }
3807 }
3808
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003809 return big;
3810 }
3811
Adrian Roose458aa82015-12-08 16:17:19 -08003812 private boolean hasValidRemoteInput(Action action) {
3813 if (TextUtils.isEmpty(action.title) || action.actionIntent == null) {
3814 // Weird actions
3815 return false;
3816 }
3817
3818 RemoteInput[] remoteInputs = action.getRemoteInputs();
3819 if (remoteInputs == null) {
3820 return false;
3821 }
3822
3823 for (RemoteInput r : remoteInputs) {
3824 CharSequence[] choices = r.getChoices();
3825 if (r.getAllowFreeFormInput() || (choices != null && choices.length != 0)) {
3826 return true;
3827 }
3828 }
3829 return false;
3830 }
3831
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003832 /**
3833 * Construct a RemoteViews for the final 1U notification layout. In order:
3834 * 1. Custom contentView from the caller
3835 * 2. Style's proposed content view
3836 * 3. Standard template view
3837 */
Julia Reynolds3b848122016-02-26 10:45:32 -05003838 public RemoteViews createContentView() {
Selim Cinek593610c2016-02-16 18:42:57 -08003839 if (mN.contentView != null && (mStyle == null || !mStyle.displayCustomViewInline())) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003840 return mN.contentView;
3841 } else if (mStyle != null) {
3842 final RemoteViews styleView = mStyle.makeContentView();
3843 if (styleView != null) {
3844 return styleView;
3845 }
Joe Onorato46439ce2010-11-19 13:56:21 -08003846 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003847 return applyStandardTemplate(getBaseLayoutResource());
Joe Onorato46439ce2010-11-19 13:56:21 -08003848 }
3849
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003850 /**
3851 * Construct a RemoteViews for the final big notification layout.
3852 */
Julia Reynolds3b848122016-02-26 10:45:32 -05003853 public RemoteViews createBigContentView() {
Selim Cinek850a8542015-11-11 11:48:36 -05003854 RemoteViews result = null;
Selim Cinek593610c2016-02-16 18:42:57 -08003855 if (mN.bigContentView != null
3856 && (mStyle == null || !mStyle.displayCustomViewInline())) {
Julia Reynolds089e3e42015-11-18 09:59:57 -05003857 return mN.bigContentView;
3858 } else if (mStyle != null) {
Selim Cinek850a8542015-11-11 11:48:36 -05003859 result = mStyle.makeBigContentView();
Selim Cinek90dcf6d2015-11-18 20:24:13 -08003860 hideLine1Text(result);
Selim Cinekcc10bfb2016-02-10 16:24:21 -08003861 } else if (mActions.size() != 0) {
3862 result = applyStandardTemplateWithActions(getBigBaseLayoutResource());
Selim Cinek850a8542015-11-11 11:48:36 -05003863 }
Selim Cinek6743c0b2017-01-18 18:24:01 -08003864 makeHeaderExpanded(result);
Selim Cinek850a8542015-11-11 11:48:36 -05003865 return result;
3866 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003867
Selim Cinekeaa29ca2015-11-23 13:51:13 -08003868 /**
3869 * Construct a RemoteViews for the final notification header only
3870 *
3871 * @hide
3872 */
3873 public RemoteViews makeNotificationHeader() {
3874 RemoteViews header = new BuilderRemoteViews(mContext.getApplicationInfo(),
3875 R.layout.notification_template_header);
3876 resetNotificationHeader(header);
Adrian Roos487374f2017-01-11 15:48:14 -08003877 bindNotificationHeader(header, false /* ambient */);
Selim Cinekeaa29ca2015-11-23 13:51:13 -08003878 return header;
3879 }
3880
Adrian Roos487374f2017-01-11 15:48:14 -08003881 /**
3882 * Construct a RemoteViews for the ambient version of the notification.
3883 *
3884 * @hide
3885 */
3886 public RemoteViews makeAmbientNotification() {
3887 RemoteViews ambient = applyStandardTemplateWithActions(
3888 R.layout.notification_template_material_ambient,
3889 mParams.reset().fillTextsFrom(this).hasProgress(false).ambient(true));
3890 return ambient;
3891 }
3892
Selim Cinek29603462015-11-17 19:04:39 -08003893 private void hideLine1Text(RemoteViews result) {
Selim Cinekcc10bfb2016-02-10 16:24:21 -08003894 if (result != null) {
3895 result.setViewVisibility(R.id.text_line_1, View.GONE);
3896 }
Selim Cinek29603462015-11-17 19:04:39 -08003897 }
3898
Selim Cinek6743c0b2017-01-18 18:24:01 -08003899 /**
3900 * Adapt the Notification header if this view is used as an expanded view.
3901 *
3902 * @hide
3903 */
3904 public static void makeHeaderExpanded(RemoteViews result) {
Selim Cinekcc10bfb2016-02-10 16:24:21 -08003905 if (result != null) {
3906 result.setBoolean(R.id.notification_header, "setExpanded", true);
3907 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003908 }
3909
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003910 /**
3911 * Construct a RemoteViews for the final heads-up notification layout.
3912 */
Julia Reynolds3b848122016-02-26 10:45:32 -05003913 public RemoteViews createHeadsUpContentView() {
Selim Cinek593610c2016-02-16 18:42:57 -08003914 if (mN.headsUpContentView != null
3915 && (mStyle == null || !mStyle.displayCustomViewInline())) {
Julia Reynolds089e3e42015-11-18 09:59:57 -05003916 return mN.headsUpContentView;
3917 } else if (mStyle != null) {
3918 final RemoteViews styleView = mStyle.makeHeadsUpContentView();
3919 if (styleView != null) {
3920 return styleView;
3921 }
3922 } else if (mActions.size() == 0) {
3923 return null;
3924 }
3925
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003926 return applyStandardTemplateWithActions(getBigBaseLayoutResource());
Chris Wren8fd39ec2014-02-27 17:43:26 -05003927 }
3928
Selim Cinek624c02db2015-12-14 21:00:02 -08003929 /**
3930 * Construct a RemoteViews for the display in public contexts like on the lockscreen.
3931 *
3932 * @hide
3933 */
3934 public RemoteViews makePublicContentView() {
3935 if (mN.publicVersion != null) {
3936 final Builder builder = recoverBuilder(mContext, mN.publicVersion);
Julia Reynolds3b848122016-02-26 10:45:32 -05003937 return builder.createContentView();
Selim Cinek624c02db2015-12-14 21:00:02 -08003938 }
3939 Bundle savedBundle = mN.extras;
3940 Style style = mStyle;
3941 mStyle = null;
3942 Icon largeIcon = mN.mLargeIcon;
3943 mN.mLargeIcon = null;
Selim Cinek279fa862016-06-14 10:57:25 -07003944 Bitmap largeIconLegacy = mN.largeIcon;
3945 mN.largeIcon = null;
Selim Cinek624c02db2015-12-14 21:00:02 -08003946 Bundle publicExtras = new Bundle();
3947 publicExtras.putBoolean(EXTRA_SHOW_WHEN,
3948 savedBundle.getBoolean(EXTRA_SHOW_WHEN));
3949 publicExtras.putBoolean(EXTRA_SHOW_CHRONOMETER,
3950 savedBundle.getBoolean(EXTRA_SHOW_CHRONOMETER));
Adrian Roos96b7e202016-05-17 13:50:38 -07003951 publicExtras.putBoolean(EXTRA_CHRONOMETER_COUNT_DOWN,
3952 savedBundle.getBoolean(EXTRA_CHRONOMETER_COUNT_DOWN));
Selim Cinek624c02db2015-12-14 21:00:02 -08003953 publicExtras.putCharSequence(EXTRA_TITLE,
3954 mContext.getString(R.string.notification_hidden_text));
3955 mN.extras = publicExtras;
3956 final RemoteViews publicView = applyStandardTemplate(getBaseLayoutResource());
3957 mN.extras = savedBundle;
3958 mN.mLargeIcon = largeIcon;
Selim Cinek279fa862016-06-14 10:57:25 -07003959 mN.largeIcon = largeIconLegacy;
Selim Cinek624c02db2015-12-14 21:00:02 -08003960 mStyle = style;
3961 return publicView;
3962 }
3963
Selim Cinek6743c0b2017-01-18 18:24:01 -08003964 /**
3965 * Construct a content view for the display when low - priority
3966 *
3967 * @param useRegularSubtext uses the normal subtext set if there is one available. Otherwise
3968 * a new subtext is created consisting of the content of the
3969 * notification.
3970 * @hide
3971 */
3972 public RemoteViews makeLowPriorityContentView(boolean useRegularSubtext) {
3973 int color = mN.color;
3974 mN.color = COLOR_DEFAULT;
3975 CharSequence summary = mN.extras.getCharSequence(EXTRA_SUB_TEXT);
3976 if (!useRegularSubtext || TextUtils.isEmpty(summary)) {
3977 CharSequence newSummary = createSummaryText();
3978 if (!TextUtils.isEmpty(newSummary)) {
3979 mN.extras.putCharSequence(EXTRA_SUB_TEXT, newSummary);
3980 }
3981 }
3982 RemoteViews header = makeNotificationHeader();
3983 if (summary != null) {
3984 mN.extras.putCharSequence(EXTRA_SUB_TEXT, summary);
3985 } else {
3986 mN.extras.remove(EXTRA_SUB_TEXT);
3987 }
3988 mN.color = color;
3989 return header;
3990 }
Selim Cinek624c02db2015-12-14 21:00:02 -08003991
Selim Cinek6743c0b2017-01-18 18:24:01 -08003992 private CharSequence createSummaryText() {
3993 CharSequence titleText = mN.extras.getCharSequence(Notification.EXTRA_TITLE);
3994 if (USE_ONLY_TITLE_IN_LOW_PRIORITY_SUMMARY) {
3995 return titleText;
3996 }
3997 SpannableStringBuilder summary = new SpannableStringBuilder();
3998 if (titleText == null) {
3999 titleText = mN.extras.getCharSequence(Notification.EXTRA_TITLE_BIG);
4000 }
4001 BidiFormatter bidi = BidiFormatter.getInstance();
4002 if (titleText != null) {
4003 summary.append(bidi.unicodeWrap(titleText));
4004 }
4005 CharSequence contentText = mN.extras.getCharSequence(Notification.EXTRA_TEXT);
4006 if (titleText != null && contentText != null) {
4007 summary.append(bidi.unicodeWrap(mContext.getText(
4008 R.string.notification_header_divider_symbol_with_spaces)));
4009 }
4010 if (contentText != null) {
4011 summary.append(bidi.unicodeWrap(contentText));
4012 }
4013 return summary;
4014 }
Chris Wren8fd39ec2014-02-27 17:43:26 -05004015
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004016 private RemoteViews generateActionButton(Action action, boolean emphazisedMode,
Adrian Roos487374f2017-01-11 15:48:14 -08004017 boolean oddAction, boolean ambient) {
Daniel Sandler8680bf82012-05-15 16:52:52 -04004018 final boolean tombstone = (action.actionIntent == null);
Selim Cinekf33b1112015-07-15 17:45:11 -07004019 RemoteViews button = new BuilderRemoteViews(mContext.getApplicationInfo(),
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004020 emphazisedMode ? getEmphasizedActionLayoutResource()
4021 : tombstone ? getActionTombstoneLayoutResource()
4022 : getActionLayoutResource());
Daniel Sandler8680bf82012-05-15 16:52:52 -04004023 if (!tombstone) {
Daniel Sandlere5518842012-05-10 16:20:40 -04004024 button.setOnClickPendingIntent(R.id.action0, action.actionIntent);
Daniel Sandlere5518842012-05-10 16:20:40 -04004025 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004026 button.setContentDescription(R.id.action0, action.title);
Adrian Roosfe84e1f2015-11-04 15:55:39 -08004027 if (action.mRemoteInputs != null) {
4028 button.setRemoteInputs(R.id.action0, action.mRemoteInputs);
4029 }
Selim Cinek7b9605b2017-01-19 17:36:00 -08004030 // TODO: handle emphasized mode / actions right
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004031 if (emphazisedMode) {
Selim Cinek981962e2016-07-20 20:41:58 -07004032 // change the background bgColor
4033 int bgColor = mContext.getColor(oddAction ? R.color.notification_action_list
4034 : R.color.notification_action_list_dark);
4035 button.setDrawableParameters(R.id.button_holder, true, -1, bgColor,
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004036 PorterDuff.Mode.SRC_ATOP, -1);
Selim Cinek981962e2016-07-20 20:41:58 -07004037 CharSequence title = action.title;
4038 ColorStateList[] outResultColor = null;
4039 if (isLegacy()) {
4040 title = clearColorSpans(title);
4041 } else {
4042 outResultColor = new ColorStateList[1];
4043 title = ensureColorSpanContrast(title, bgColor, outResultColor);
4044 }
4045 button.setTextViewText(R.id.action0, title);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004046 setTextViewColorPrimary(button, R.id.action0);
Selim Cinek981962e2016-07-20 20:41:58 -07004047 if (outResultColor != null && outResultColor[0] != null) {
4048 // We need to set the text color as well since changing a text to uppercase
4049 // clears its spans.
4050 button.setTextColor(R.id.action0, outResultColor[0]);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004051 } else if (mN.color != COLOR_DEFAULT && !isColorized()) {
Selim Cinek981962e2016-07-20 20:41:58 -07004052 button.setTextColor(R.id.action0,resolveContrastColor());
4053 }
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004054 } else {
Selim Cinek981962e2016-07-20 20:41:58 -07004055 button.setTextViewText(R.id.action0, processLegacyText(action.title));
Selim Cinek7b9605b2017-01-19 17:36:00 -08004056 if (isColorized()) {
4057 setTextViewColorPrimary(button, R.id.action0);
4058 } else if (mN.color != COLOR_DEFAULT) {
Adrian Roos487374f2017-01-11 15:48:14 -08004059 button.setTextColor(R.id.action0,
4060 ambient ? resolveAmbientColor() : resolveContrastColor());
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004061 }
Adrian Roosfe84e1f2015-11-04 15:55:39 -08004062 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04004063 return button;
4064 }
4065
Joe Onoratocb109a02011-01-18 17:57:41 -08004066 /**
Selim Cinek981962e2016-07-20 20:41:58 -07004067 * Clears all color spans of a text
4068 * @param charSequence the input text
4069 * @return the same text but without color spans
4070 */
4071 private CharSequence clearColorSpans(CharSequence charSequence) {
4072 if (charSequence instanceof Spanned) {
4073 Spanned ss = (Spanned) charSequence;
4074 Object[] spans = ss.getSpans(0, ss.length(), Object.class);
4075 SpannableStringBuilder builder = new SpannableStringBuilder(ss.toString());
4076 for (Object span : spans) {
4077 Object resultSpan = span;
4078 if (resultSpan instanceof CharacterStyle) {
4079 resultSpan = ((CharacterStyle) span).getUnderlying();
4080 }
4081 if (resultSpan instanceof TextAppearanceSpan) {
4082 TextAppearanceSpan originalSpan = (TextAppearanceSpan) resultSpan;
4083 if (originalSpan.getTextColor() != null) {
4084 resultSpan = new TextAppearanceSpan(
4085 originalSpan.getFamily(),
4086 originalSpan.getTextStyle(),
4087 originalSpan.getTextSize(),
4088 null,
4089 originalSpan.getLinkTextColor());
4090 }
4091 } else if (resultSpan instanceof ForegroundColorSpan
4092 || (resultSpan instanceof BackgroundColorSpan)) {
4093 continue;
4094 } else {
4095 resultSpan = span;
4096 }
4097 builder.setSpan(resultSpan, ss.getSpanStart(span), ss.getSpanEnd(span),
4098 ss.getSpanFlags(span));
4099 }
4100 return builder;
4101 }
4102 return charSequence;
4103 }
4104
4105 /**
4106 * Ensures contrast on color spans against a background color. also returns the color of the
4107 * text if a span was found that spans over the whole text.
4108 *
4109 * @param charSequence the charSequence on which the spans are
4110 * @param background the background color to ensure the contrast against
4111 * @param outResultColor an array in which a color will be returned as the first element if
4112 * there exists a full length color span.
4113 * @return the contrasted charSequence
4114 */
4115 private CharSequence ensureColorSpanContrast(CharSequence charSequence, int background,
4116 ColorStateList[] outResultColor) {
4117 if (charSequence instanceof Spanned) {
4118 Spanned ss = (Spanned) charSequence;
4119 Object[] spans = ss.getSpans(0, ss.length(), Object.class);
4120 SpannableStringBuilder builder = new SpannableStringBuilder(ss.toString());
4121 for (Object span : spans) {
4122 Object resultSpan = span;
4123 int spanStart = ss.getSpanStart(span);
4124 int spanEnd = ss.getSpanEnd(span);
4125 boolean fullLength = (spanEnd - spanStart) == charSequence.length();
4126 if (resultSpan instanceof CharacterStyle) {
4127 resultSpan = ((CharacterStyle) span).getUnderlying();
4128 }
4129 if (resultSpan instanceof TextAppearanceSpan) {
4130 TextAppearanceSpan originalSpan = (TextAppearanceSpan) resultSpan;
4131 ColorStateList textColor = originalSpan.getTextColor();
4132 if (textColor != null) {
4133 int[] colors = textColor.getColors();
4134 int[] newColors = new int[colors.length];
4135 for (int i = 0; i < newColors.length; i++) {
4136 newColors[i] = NotificationColorUtil.ensureLargeTextContrast(
4137 colors[i], background);
4138 }
4139 textColor = new ColorStateList(textColor.getStates().clone(),
4140 newColors);
4141 resultSpan = new TextAppearanceSpan(
4142 originalSpan.getFamily(),
4143 originalSpan.getTextStyle(),
4144 originalSpan.getTextSize(),
4145 textColor,
4146 originalSpan.getLinkTextColor());
4147 if (fullLength) {
4148 outResultColor[0] = new ColorStateList(
4149 textColor.getStates().clone(), newColors);
4150 }
4151 }
4152 } else if (resultSpan instanceof ForegroundColorSpan) {
4153 ForegroundColorSpan originalSpan = (ForegroundColorSpan) resultSpan;
4154 int foregroundColor = originalSpan.getForegroundColor();
4155 foregroundColor = NotificationColorUtil.ensureLargeTextContrast(
4156 foregroundColor, background);
4157 resultSpan = new ForegroundColorSpan(foregroundColor);
4158 if (fullLength) {
4159 outResultColor[0] = ColorStateList.valueOf(foregroundColor);
4160 }
4161 } else {
4162 resultSpan = span;
4163 }
4164
4165 builder.setSpan(resultSpan, spanStart, spanEnd, ss.getSpanFlags(span));
4166 }
4167 return builder;
4168 }
4169 return charSequence;
4170 }
4171
4172 /**
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004173 * @return Whether we are currently building a notification from a legacy (an app that
Alan Viverette3cb07a462014-06-06 14:19:53 -07004174 * doesn't create material notifications by itself) app.
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004175 */
4176 private boolean isLegacy() {
Selim Cinek7b9605b2017-01-19 17:36:00 -08004177 if (!mIsLegacyInitialized) {
4178 mIsLegacy = mContext.getApplicationInfo().targetSdkVersion
4179 < Build.VERSION_CODES.LOLLIPOP;
4180 mIsLegacyInitialized = true;
4181 }
4182 return mIsLegacy;
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004183 }
4184
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004185 private CharSequence processLegacyText(CharSequence charSequence) {
Selim Cinek7b9605b2017-01-19 17:36:00 -08004186 if (isLegacy() || isColorized()) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004187 return getColorUtil().invertCharSequenceColors(charSequence);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004188 } else {
4189 return charSequence;
4190 }
4191 }
4192
Dan Sandler26e81cf2014-05-06 10:01:27 -04004193 /**
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004194 * Apply any necessariy colors to the small icon
Dan Sandler26e81cf2014-05-06 10:01:27 -04004195 */
Adrian Roos487374f2017-01-11 15:48:14 -08004196 private void processSmallIconColor(Icon smallIcon, RemoteViews contentView,
4197 boolean ambient) {
Selim Cinekea4bef72015-12-02 15:51:10 -08004198 boolean colorable = !isLegacy() || getColorUtil().isGrayscaleIcon(mContext, smallIcon);
Adrian Roos487374f2017-01-11 15:48:14 -08004199 int color = ambient ? resolveAmbientColor() : resolveContrastColor();
Selim Cinekea4bef72015-12-02 15:51:10 -08004200 if (colorable) {
Adrian Roos487374f2017-01-11 15:48:14 -08004201 contentView.setDrawableParameters(R.id.icon, false, -1, color,
Jorim Jaggi92df1f22014-12-16 19:44:41 +01004202 PorterDuff.Mode.SRC_ATOP, -1);
Selim Cinekea4bef72015-12-02 15:51:10 -08004203
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004204 }
Selim Cinekea4bef72015-12-02 15:51:10 -08004205 contentView.setInt(R.id.notification_header, "setOriginalIconColor",
Adrian Roos487374f2017-01-11 15:48:14 -08004206 colorable ? color : NotificationHeaderView.NO_COLOR);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004207 }
4208
Dan Sandler26e81cf2014-05-06 10:01:27 -04004209 /**
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004210 * Make the largeIcon dark if it's a fake smallIcon (that is,
Dan Sandler26e81cf2014-05-06 10:01:27 -04004211 * if it's grayscale).
4212 */
4213 // TODO: also check bounds, transparency, that sort of thing.
Dan Sandlerd63f9322015-05-06 15:18:49 -04004214 private void processLargeLegacyIcon(Icon largeIcon, RemoteViews contentView) {
4215 if (largeIcon != null && isLegacy()
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004216 && getColorUtil().isGrayscaleIcon(mContext, largeIcon)) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07004217 // resolve color will fall back to the default when legacy
Adrian Roos4ff3b122016-02-01 12:26:13 -08004218 contentView.setDrawableParameters(R.id.icon, false, -1, resolveContrastColor(),
Dan Sandler190d58d2014-05-15 09:33:39 -04004219 PorterDuff.Mode.SRC_ATOP, -1);
Jorim Jaggi92df1f22014-12-16 19:44:41 +01004220 }
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004221 }
4222
Julia Reynolds10ee1fc2015-11-09 11:04:55 -05004223 private void sanitizeColor() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004224 if (mN.color != COLOR_DEFAULT) {
4225 mN.color |= 0xFF000000; // no alpha for custom colors
Jorim Jaggi74419312014-06-10 20:57:21 +02004226 }
Jorim Jaggi74419312014-06-10 20:57:21 +02004227 }
4228
Adrian Roos4ff3b122016-02-01 12:26:13 -08004229 int resolveContrastColor() {
4230 if (mCachedContrastColorIsFor == mN.color && mCachedContrastColor != COLOR_INVALID) {
4231 return mCachedContrastColor;
Dan Sandler26e81cf2014-05-06 10:01:27 -04004232 }
Adrian Roos4ff3b122016-02-01 12:26:13 -08004233 final int contrasted = NotificationColorUtil.resolveContrastColor(mContext, mN.color);
4234
4235 mCachedContrastColorIsFor = mN.color;
4236 return mCachedContrastColor = contrasted;
Dan Sandler26e81cf2014-05-06 10:01:27 -04004237 }
4238
Adrian Roos487374f2017-01-11 15:48:14 -08004239 int resolveAmbientColor() {
4240 if (mCachedAmbientColorIsFor == mN.color && mCachedAmbientColorIsFor != COLOR_INVALID) {
4241 return mCachedAmbientColor;
4242 }
4243 final int contrasted = NotificationColorUtil.resolveAmbientColor(mContext, mN.color);
4244
4245 mCachedAmbientColorIsFor = mN.color;
4246 return mCachedAmbientColor = contrasted;
4247 }
4248
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004249 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004250 * Apply the unstyled operations and return a new {@link Notification} object.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04004251 * @hide
Joe Onoratocb109a02011-01-18 17:57:41 -08004252 */
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04004253 public Notification buildUnstyled() {
Daniel Sandlera0a938c2012-03-15 08:42:37 -04004254 if (mActions.size() > 0) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004255 mN.actions = new Action[mActions.size()];
4256 mActions.toArray(mN.actions);
Daniel Sandlera0a938c2012-03-15 08:42:37 -04004257 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004258 if (!mPersonList.isEmpty()) {
4259 mN.extras.putStringArray(EXTRA_PEOPLE,
4260 mPersonList.toArray(new String[mPersonList.size()]));
Dan Sandler0bf2ed82013-12-21 23:33:41 -06004261 }
Selim Cinek247fa012016-02-18 09:50:48 -08004262 if (mN.bigContentView != null || mN.contentView != null
4263 || mN.headsUpContentView != null) {
4264 mN.extras.putBoolean(EXTRA_CONTAINS_CUSTOM_VIEW, true);
4265 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004266 return mN;
Joe Onorato46439ce2010-11-19 13:56:21 -08004267 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004268
Julia Reynolds3b848122016-02-26 10:45:32 -05004269 /**
4270 * Creates a Builder from an existing notification so further changes can be made.
4271 * @param context The context for your application / activity.
4272 * @param n The notification to create a Builder from.
4273 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004274 public static Notification.Builder recoverBuilder(Context context, Notification n) {
Christoph Studer4600f9b2014-07-22 22:44:43 +02004275 // Re-create notification context so we can access app resources.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004276 ApplicationInfo applicationInfo = n.extras.getParcelable(
4277 EXTRA_BUILDER_APPLICATION_INFO);
Christoph Studer4600f9b2014-07-22 22:44:43 +02004278 Context builderContext;
Julia Reynoldsda303542015-11-23 14:00:20 -05004279 if (applicationInfo != null) {
4280 try {
4281 builderContext = context.createApplicationContext(applicationInfo,
4282 Context.CONTEXT_RESTRICTED);
4283 } catch (NameNotFoundException e) {
4284 Log.e(TAG, "ApplicationInfo " + applicationInfo + " not found");
4285 builderContext = context; // try with our context
4286 }
4287 } else {
4288 builderContext = context; // try with given context
Christoph Studer4600f9b2014-07-22 22:44:43 +02004289 }
4290
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004291 return new Builder(builderContext, n);
Christoph Studer4600f9b2014-07-22 22:44:43 +02004292 }
4293
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004294 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004295 * @deprecated Use {@link #build()} instead.
4296 */
4297 @Deprecated
4298 public Notification getNotification() {
4299 return build();
4300 }
4301
4302 /**
4303 * Combine all of the options that have been set and return a new {@link Notification}
4304 * object.
4305 */
4306 public Notification build() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004307 // first, add any extras from the calling code
4308 if (mUserExtras != null) {
4309 mN.extras = getAllExtras();
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07004310 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004311
Selim Cinekb85f36fd2016-04-20 18:46:36 -07004312 mN.creationTime = System.currentTimeMillis();
4313
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004314 // lazy stuff from mContext; see comment in Builder(Context, Notification)
Julia Reynoldsda303542015-11-23 14:00:20 -05004315 Notification.addFieldsFromContext(mContext, mN);
Christoph Studer943aa672014-08-03 20:31:16 +02004316
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004317 buildUnstyled();
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004318
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004319 if (mStyle != null) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004320 mStyle.buildStyled(mN);
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004321 }
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004322
Adrian Roos5081c0d2016-02-26 16:04:19 -08004323 if (mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N
4324 && (mStyle == null || !mStyle.displayCustomViewInline())) {
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004325 if (mN.contentView == null) {
Julia Reynolds3b848122016-02-26 10:45:32 -05004326 mN.contentView = createContentView();
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004327 mN.extras.putInt(EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT,
4328 mN.contentView.getSequenceNumber());
4329 }
4330 if (mN.bigContentView == null) {
Julia Reynolds3b848122016-02-26 10:45:32 -05004331 mN.bigContentView = createBigContentView();
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004332 if (mN.bigContentView != null) {
4333 mN.extras.putInt(EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT,
4334 mN.bigContentView.getSequenceNumber());
4335 }
4336 }
4337 if (mN.headsUpContentView == null) {
Julia Reynolds3b848122016-02-26 10:45:32 -05004338 mN.headsUpContentView = createHeadsUpContentView();
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004339 if (mN.headsUpContentView != null) {
4340 mN.extras.putInt(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT,
4341 mN.headsUpContentView.getSequenceNumber());
4342 }
4343 }
4344 }
4345
Julia Reynolds4c0c2022016-02-02 15:11:59 -05004346 if ((mN.defaults & DEFAULT_LIGHTS) != 0) {
4347 mN.flags |= FLAG_SHOW_LIGHTS;
4348 }
4349
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004350 return mN;
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004351 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05004352
4353 /**
4354 * Apply this Builder to an existing {@link Notification} object.
4355 *
4356 * @hide
4357 */
4358 public Notification buildInto(Notification n) {
Daniel Sandler1a497d32013-04-18 14:52:45 -04004359 build().cloneInto(n, true);
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05004360 return n;
4361 }
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004362
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004363 /**
Adrian Roos184bfe022016-03-03 13:41:44 -08004364 * Removes RemoteViews that were created for compatibility from {@param n}, if they did not
4365 * change.
4366 *
4367 * @return {@param n}, if no stripping is needed, otherwise a stripped clone of {@param n}.
4368 *
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004369 * @hide
4370 */
Adrian Roos184bfe022016-03-03 13:41:44 -08004371 public static Notification maybeCloneStrippedForDelivery(Notification n) {
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004372 String templateClass = n.extras.getString(EXTRA_TEMPLATE);
Adrian Roos184bfe022016-03-03 13:41:44 -08004373
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004374 // Only strip views for known Styles because we won't know how to
4375 // re-create them otherwise.
Adrian Roos184bfe022016-03-03 13:41:44 -08004376 if (!TextUtils.isEmpty(templateClass)
4377 && getNotificationStyleClass(templateClass) == null) {
4378 return n;
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004379 }
Adrian Roos184bfe022016-03-03 13:41:44 -08004380
4381 // Only strip unmodified BuilderRemoteViews.
4382 boolean stripContentView = n.contentView instanceof BuilderRemoteViews &&
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004383 n.extras.getInt(EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT, -1) ==
Adrian Roos184bfe022016-03-03 13:41:44 -08004384 n.contentView.getSequenceNumber();
4385 boolean stripBigContentView = n.bigContentView instanceof BuilderRemoteViews &&
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004386 n.extras.getInt(EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT, -1) ==
Adrian Roos184bfe022016-03-03 13:41:44 -08004387 n.bigContentView.getSequenceNumber();
4388 boolean stripHeadsUpContentView = n.headsUpContentView instanceof BuilderRemoteViews &&
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004389 n.extras.getInt(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT, -1) ==
Adrian Roos184bfe022016-03-03 13:41:44 -08004390 n.headsUpContentView.getSequenceNumber();
4391
4392 // Nothing to do here, no need to clone.
4393 if (!stripContentView && !stripBigContentView && !stripHeadsUpContentView) {
4394 return n;
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004395 }
Adrian Roos184bfe022016-03-03 13:41:44 -08004396
4397 Notification clone = n.clone();
4398 if (stripContentView) {
4399 clone.contentView = null;
4400 clone.extras.remove(EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT);
4401 }
4402 if (stripBigContentView) {
4403 clone.bigContentView = null;
4404 clone.extras.remove(EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT);
4405 }
4406 if (stripHeadsUpContentView) {
4407 clone.headsUpContentView = null;
4408 clone.extras.remove(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT);
4409 }
4410 return clone;
Julia Reynoldsd4ea7412016-02-17 14:00:56 -05004411 }
4412
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004413 private int getBaseLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07004414 return R.layout.notification_template_material_base;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004415 }
4416
4417 private int getBigBaseLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07004418 return R.layout.notification_template_material_big_base;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004419 }
4420
4421 private int getBigPictureLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07004422 return R.layout.notification_template_material_big_picture;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004423 }
4424
4425 private int getBigTextLayoutResource() {
Jorim Jaggi445d3c02014-08-19 22:33:42 +02004426 return R.layout.notification_template_material_big_text;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004427 }
4428
4429 private int getInboxLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07004430 return R.layout.notification_template_material_inbox;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004431 }
4432
Adrian Roosc1a80b02016-04-05 14:54:55 -07004433 private int getMessagingLayoutResource() {
4434 return R.layout.notification_template_material_messaging;
4435 }
4436
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004437 private int getActionLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07004438 return R.layout.notification_material_action;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004439 }
4440
Selim Cinek06e9e1f2016-07-08 17:14:16 -07004441 private int getEmphasizedActionLayoutResource() {
4442 return R.layout.notification_material_action_emphasized;
4443 }
4444
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004445 private int getActionTombstoneLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07004446 return R.layout.notification_material_action_tombstone;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004447 }
Selim Cinek7b9605b2017-01-19 17:36:00 -08004448
4449 private int getBackgroundColor() {
4450 if (isColorized()) {
4451 return mN.color;
4452 } else {
4453 return COLOR_DEFAULT;
4454 }
4455 }
4456
4457 private boolean isColorized() {
4458 return mN.isColorized();
4459 }
4460 }
4461
4462 /**
4463 * @return whether this notification is ongoing and can't be dismissed by the user.
4464 */
4465 private boolean isOngoing() {
4466 final int ongoingFlags = Notification.FLAG_FOREGROUND_SERVICE
4467 | Notification.FLAG_ONGOING_EVENT | Notification.FLAG_NO_CLEAR;
4468 return (flags & ongoingFlags) != 0;
4469 }
4470
4471 /**
4472 * @return true if this notification is colorized. This also factors in wheather the
4473 * notification is ongoing.
4474 *
4475 * @hide
4476 */
4477 public boolean isColorized() {
4478 return extras.getBoolean(EXTRA_COLORIZED) && isOngoing();
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004479 }
4480
Selim Cinek279fa862016-06-14 10:57:25 -07004481 private boolean hasLargeIcon() {
4482 return mLargeIcon != null || largeIcon != null;
4483 }
4484
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004485 /**
Selim Cinekc2c0b042016-05-18 17:13:46 -07004486 * @return true if the notification will show the time; false otherwise
Selim Cinekb85f36fd2016-04-20 18:46:36 -07004487 * @hide
4488 */
Selim Cinekc2c0b042016-05-18 17:13:46 -07004489 public boolean showsTime() {
Selim Cinekb85f36fd2016-04-20 18:46:36 -07004490 return when != 0 && extras.getBoolean(EXTRA_SHOW_WHEN);
4491 }
4492
4493 /**
Selim Cinekc2c0b042016-05-18 17:13:46 -07004494 * @return true if the notification will show a chronometer; false otherwise
4495 * @hide
4496 */
4497 public boolean showsChronometer() {
4498 return when != 0 && extras.getBoolean(EXTRA_SHOW_CHRONOMETER);
4499 }
4500
4501 /**
Julia Reynolds4a02afb2016-12-13 13:39:52 -05004502 * @hide
4503 */
4504 @SystemApi
4505 public static Class<? extends Style> getNotificationStyleClass(String templateClass) {
4506 Class<? extends Style>[] classes = new Class[] {
4507 BigTextStyle.class, BigPictureStyle.class, InboxStyle.class, MediaStyle.class,
4508 DecoratedCustomViewStyle.class, DecoratedMediaCustomViewStyle.class,
4509 MessagingStyle.class };
4510 for (Class<? extends Style> innerClass : classes) {
4511 if (templateClass.equals(innerClass.getName())) {
4512 return innerClass;
4513 }
4514 }
4515 return null;
4516 }
4517
4518 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004519 * An object that can apply a rich notification style to a {@link Notification.Builder}
4520 * object.
4521 */
Griff Hazendfcb0802014-02-11 12:00:00 -08004522 public static abstract class Style {
Chris Wrend6297db2012-05-03 16:20:13 -04004523 private CharSequence mBigContentTitle;
Jorim Jaggi457a10d2014-09-08 16:18:23 +02004524
4525 /**
4526 * @hide
4527 */
4528 protected CharSequence mSummaryText = null;
4529
4530 /**
4531 * @hide
4532 */
4533 protected boolean mSummaryTextSet = false;
Chris Wrend6297db2012-05-03 16:20:13 -04004534
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004535 protected Builder mBuilder;
4536
Chris Wrend6297db2012-05-03 16:20:13 -04004537 /**
4538 * Overrides ContentTitle in the big form of the template.
4539 * This defaults to the value passed to setContentTitle().
4540 */
4541 protected void internalSetBigContentTitle(CharSequence title) {
4542 mBigContentTitle = title;
4543 }
4544
4545 /**
4546 * Set the first line of text after the detail section in the big form of the template.
4547 */
4548 protected void internalSetSummaryText(CharSequence cs) {
4549 mSummaryText = cs;
Daniel Sandler619738c2012-06-07 16:33:08 -04004550 mSummaryTextSet = true;
Chris Wrend6297db2012-05-03 16:20:13 -04004551 }
4552
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004553 public void setBuilder(Builder builder) {
4554 if (mBuilder != builder) {
4555 mBuilder = builder;
Daniel Sandlerc08dea22012-06-28 08:35:24 -07004556 if (mBuilder != null) {
4557 mBuilder.setStyle(this);
4558 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004559 }
4560 }
4561
Chris Wrend6297db2012-05-03 16:20:13 -04004562 protected void checkBuilder() {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004563 if (mBuilder == null) {
4564 throw new IllegalArgumentException("Style requires a valid Builder object");
4565 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004566 }
Chris Wrend6297db2012-05-03 16:20:13 -04004567
4568 protected RemoteViews getStandardView(int layoutId) {
4569 checkBuilder();
4570
Christoph Studer4600f9b2014-07-22 22:44:43 +02004571 // Nasty.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004572 CharSequence oldBuilderContentTitle =
4573 mBuilder.getAllExtras().getCharSequence(EXTRA_TITLE);
Chris Wrend6297db2012-05-03 16:20:13 -04004574 if (mBigContentTitle != null) {
4575 mBuilder.setContentTitle(mBigContentTitle);
4576 }
4577
Chris Wrend6297db2012-05-03 16:20:13 -04004578 RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(layoutId);
4579
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004580 mBuilder.getAllExtras().putCharSequence(EXTRA_TITLE, oldBuilderContentTitle);
Christoph Studer4600f9b2014-07-22 22:44:43 +02004581
Chris Wrend6297db2012-05-03 16:20:13 -04004582 if (mBigContentTitle != null && mBigContentTitle.equals("")) {
4583 contentView.setViewVisibility(R.id.line1, View.GONE);
Chris Wren67dc9a02012-05-16 01:03:20 -04004584 } else {
4585 contentView.setViewVisibility(R.id.line1, View.VISIBLE);
Chris Wrend6297db2012-05-03 16:20:13 -04004586 }
4587
Chris Wrend6297db2012-05-03 16:20:13 -04004588 return contentView;
4589 }
4590
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004591 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004592 * Construct a Style-specific RemoteViews for the final 1U notification layout.
4593 * The default implementation has nothing additional to add.
4594 * @hide
4595 */
4596 public RemoteViews makeContentView() {
4597 return null;
4598 }
4599
4600 /**
4601 * Construct a Style-specific RemoteViews for the final big notification layout.
4602 * @hide
4603 */
4604 public RemoteViews makeBigContentView() {
4605 return null;
4606 }
4607
4608 /**
4609 * Construct a Style-specific RemoteViews for the final HUN layout.
4610 * @hide
4611 */
4612 public RemoteViews makeHeadsUpContentView() {
4613 return null;
4614 }
4615
4616 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004617 * Apply any style-specific extras to this notification before shipping it out.
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004618 * @hide
4619 */
4620 public void addExtras(Bundle extras) {
4621 if (mSummaryTextSet) {
4622 extras.putCharSequence(EXTRA_SUMMARY_TEXT, mSummaryText);
4623 }
4624 if (mBigContentTitle != null) {
4625 extras.putCharSequence(EXTRA_TITLE_BIG, mBigContentTitle);
4626 }
Chris Wren91ad5632013-06-05 15:05:57 -04004627 extras.putString(EXTRA_TEMPLATE, this.getClass().getName());
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004628 }
4629
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04004630 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004631 * Reconstruct the internal state of this Style object from extras.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04004632 * @hide
4633 */
Christoph Studer4600f9b2014-07-22 22:44:43 +02004634 protected void restoreFromExtras(Bundle extras) {
4635 if (extras.containsKey(EXTRA_SUMMARY_TEXT)) {
4636 mSummaryText = extras.getCharSequence(EXTRA_SUMMARY_TEXT);
4637 mSummaryTextSet = true;
4638 }
4639 if (extras.containsKey(EXTRA_TITLE_BIG)) {
4640 mBigContentTitle = extras.getCharSequence(EXTRA_TITLE_BIG);
4641 }
4642 }
4643
4644
4645 /**
4646 * @hide
4647 */
4648 public Notification buildStyled(Notification wip) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004649 addExtras(wip.extras);
Christoph Studer4600f9b2014-07-22 22:44:43 +02004650 return wip;
4651 }
4652
Daniel Sandler0ec46202015-06-24 01:27:05 -04004653 /**
4654 * @hide
4655 */
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07004656 public void purgeResources() {}
4657
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04004658 /**
4659 * Calls {@link android.app.Notification.Builder#build()} on the Builder this Style is
4660 * attached to.
4661 *
4662 * @return the fully constructed Notification.
4663 */
4664 public Notification build() {
4665 checkBuilder();
4666 return mBuilder.build();
4667 }
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004668
4669 /**
4670 * @hide
4671 * @return true if the style positions the progress bar on the second line; false if the
4672 * style hides the progress bar
4673 */
4674 protected boolean hasProgress() {
4675 return true;
4676 }
Selim Cinek03d0d652015-11-13 13:18:09 -05004677
4678 /**
4679 * @hide
4680 * @return Whether we should put the summary be put into the notification header
4681 */
4682 public boolean hasSummaryInHeader() {
4683 return true;
4684 }
Selim Cinek593610c2016-02-16 18:42:57 -08004685
4686 /**
4687 * @hide
4688 * @return Whether custom content views are displayed inline in the style
4689 */
4690 public boolean displayCustomViewInline() {
4691 return false;
4692 }
Joe Onorato46439ce2010-11-19 13:56:21 -08004693 }
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004694
4695 /**
Daniel Sandler4dfbe832012-04-11 14:51:46 -04004696 * Helper class for generating large-format notifications that include a large image attachment.
Joe Malin8d40d042012-11-05 11:36:40 -08004697 *
Robert Ly91c5ce32014-06-08 15:37:00 -07004698 * Here's how you'd set the <code>BigPictureStyle</code> on a notification:
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004699 * <pre class="prettyprint">
Robert Ly91c5ce32014-06-08 15:37:00 -07004700 * Notification notif = new Notification.Builder(mContext)
4701 * .setContentTitle(&quot;New photo from &quot; + sender.toString())
4702 * .setContentText(subject)
4703 * .setSmallIcon(R.drawable.new_post)
4704 * .setLargeIcon(aBitmap)
4705 * .setStyle(new Notification.BigPictureStyle()
4706 * .bigPicture(aBigBitmap))
4707 * .build();
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004708 * </pre>
Joe Malin8d40d042012-11-05 11:36:40 -08004709 *
Daniel Sandler4dfbe832012-04-11 14:51:46 -04004710 * @see Notification#bigContentView
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004711 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004712 public static class BigPictureStyle extends Style {
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004713 private Bitmap mPicture;
Dan Sandlerd63f9322015-05-06 15:18:49 -04004714 private Icon mBigLargeIcon;
Chris Wren3745a3d2012-05-22 15:11:52 -04004715 private boolean mBigLargeIconSet = false;
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004716
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004717 public BigPictureStyle() {
4718 }
4719
Adrian Roosf5faf9d2016-05-23 13:56:15 -07004720 /**
4721 * @deprecated use {@code BigPictureStyle()}.
4722 */
4723 @Deprecated
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004724 public BigPictureStyle(Builder builder) {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004725 setBuilder(builder);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004726 }
4727
Chris Wrend6297db2012-05-03 16:20:13 -04004728 /**
4729 * Overrides ContentTitle in the big form of the template.
4730 * This defaults to the value passed to setContentTitle().
4731 */
4732 public BigPictureStyle setBigContentTitle(CharSequence title) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04004733 internalSetBigContentTitle(safeCharSequence(title));
Chris Wrend6297db2012-05-03 16:20:13 -04004734 return this;
4735 }
4736
4737 /**
4738 * Set the first line of text after the detail section in the big form of the template.
4739 */
4740 public BigPictureStyle setSummaryText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04004741 internalSetSummaryText(safeCharSequence(cs));
Chris Wrend6297db2012-05-03 16:20:13 -04004742 return this;
4743 }
4744
Chris Wren0bd664d2012-08-01 13:56:56 -04004745 /**
4746 * Provide the bitmap to be used as the payload for the BigPicture notification.
4747 */
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004748 public BigPictureStyle bigPicture(Bitmap b) {
4749 mPicture = b;
4750 return this;
4751 }
4752
Chris Wren3745a3d2012-05-22 15:11:52 -04004753 /**
Chris Wren3745a3d2012-05-22 15:11:52 -04004754 * Override the large icon when the big notification is shown.
4755 */
4756 public BigPictureStyle bigLargeIcon(Bitmap b) {
Dan Sandlerd63f9322015-05-06 15:18:49 -04004757 return bigLargeIcon(b != null ? Icon.createWithBitmap(b) : null);
4758 }
4759
4760 /**
4761 * Override the large icon when the big notification is shown.
4762 */
4763 public BigPictureStyle bigLargeIcon(Icon icon) {
Chris Wren3745a3d2012-05-22 15:11:52 -04004764 mBigLargeIconSet = true;
Dan Sandlerd63f9322015-05-06 15:18:49 -04004765 mBigLargeIcon = icon;
Chris Wren3745a3d2012-05-22 15:11:52 -04004766 return this;
4767 }
4768
Riley Andrews0394a0c2015-11-03 23:36:52 -08004769 /** @hide */
4770 public static final int MIN_ASHMEM_BITMAP_SIZE = 128 * (1 << 10);
4771
Daniel Sandler0ec46202015-06-24 01:27:05 -04004772 /**
4773 * @hide
4774 */
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07004775 @Override
4776 public void purgeResources() {
4777 super.purgeResources();
Riley Andrews8cee7c12015-11-01 23:36:04 -08004778 if (mPicture != null &&
4779 mPicture.isMutable() &&
Riley Andrews0394a0c2015-11-03 23:36:52 -08004780 mPicture.getAllocationByteCount() >= MIN_ASHMEM_BITMAP_SIZE) {
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07004781 mPicture = mPicture.createAshmemBitmap();
4782 }
4783 if (mBigLargeIcon != null) {
4784 mBigLargeIcon.convertToAshmem();
4785 }
4786 }
Christoph Studer5c510ee2014-12-15 16:32:27 +01004787
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004788 /**
4789 * @hide
4790 */
4791 public RemoteViews makeBigContentView() {
4792 // Replace mN.mLargeIcon with mBigLargeIcon if mBigLargeIconSet
Christoph Studer5c510ee2014-12-15 16:32:27 +01004793 // This covers the following cases:
4794 // 1. mBigLargeIconSet -> mBigLargeIcon (null or non-null) applies, overrides
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004795 // mN.mLargeIcon
4796 // 2. !mBigLargeIconSet -> mN.mLargeIcon applies
Dan Sandlerd63f9322015-05-06 15:18:49 -04004797 Icon oldLargeIcon = null;
Selim Cineke99acb22016-08-04 12:55:48 -07004798 Bitmap largeIconLegacy = null;
Christoph Studer5c510ee2014-12-15 16:32:27 +01004799 if (mBigLargeIconSet) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004800 oldLargeIcon = mBuilder.mN.mLargeIcon;
4801 mBuilder.mN.mLargeIcon = mBigLargeIcon;
Selim Cineke99acb22016-08-04 12:55:48 -07004802 // The legacy largeIcon might not allow us to clear the image, as it's taken in
4803 // replacement if the other one is null. Because we're restoring these legacy icons
4804 // for old listeners, this is in general non-null.
4805 largeIconLegacy = mBuilder.mN.largeIcon;
4806 mBuilder.mN.largeIcon = null;
Christoph Studer5c510ee2014-12-15 16:32:27 +01004807 }
4808
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004809 RemoteViews contentView = getStandardView(mBuilder.getBigPictureLayoutResource());
Selim Cinek03d0d652015-11-13 13:18:09 -05004810 if (mSummaryTextSet) {
4811 contentView.setTextViewText(R.id.text, mBuilder.processLegacyText(mSummaryText));
Selim Cinek7b9605b2017-01-19 17:36:00 -08004812 mBuilder.setTextViewColorSecondary(contentView, R.id.text);
Selim Cinekc848c3a2016-01-13 15:27:30 -08004813 contentView.setViewVisibility(R.id.text, View.VISIBLE);
Selim Cinek03d0d652015-11-13 13:18:09 -05004814 }
Selim Cinek279fa862016-06-14 10:57:25 -07004815 mBuilder.setContentMinHeight(contentView, mBuilder.mN.hasLargeIcon());
Selim Cinek53e64a42015-11-16 10:40:56 -08004816
Christoph Studer5c510ee2014-12-15 16:32:27 +01004817 if (mBigLargeIconSet) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004818 mBuilder.mN.mLargeIcon = oldLargeIcon;
Selim Cineke99acb22016-08-04 12:55:48 -07004819 mBuilder.mN.largeIcon = largeIconLegacy;
Christoph Studer5c510ee2014-12-15 16:32:27 +01004820 }
4821
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004822 contentView.setImageViewBitmap(R.id.big_picture, mPicture);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004823 return contentView;
4824 }
4825
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004826 /**
4827 * @hide
4828 */
4829 public void addExtras(Bundle extras) {
4830 super.addExtras(extras);
4831
4832 if (mBigLargeIconSet) {
4833 extras.putParcelable(EXTRA_LARGE_ICON_BIG, mBigLargeIcon);
4834 }
4835 extras.putParcelable(EXTRA_PICTURE, mPicture);
4836 }
4837
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04004838 /**
4839 * @hide
4840 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004841 @Override
Christoph Studer4600f9b2014-07-22 22:44:43 +02004842 protected void restoreFromExtras(Bundle extras) {
4843 super.restoreFromExtras(extras);
4844
4845 if (extras.containsKey(EXTRA_LARGE_ICON_BIG)) {
Christoph Studer5c510ee2014-12-15 16:32:27 +01004846 mBigLargeIconSet = true;
Christoph Studer4600f9b2014-07-22 22:44:43 +02004847 mBigLargeIcon = extras.getParcelable(EXTRA_LARGE_ICON_BIG);
Chris Wren3745a3d2012-05-22 15:11:52 -04004848 }
Christoph Studer4600f9b2014-07-22 22:44:43 +02004849 mPicture = extras.getParcelable(EXTRA_PICTURE);
4850 }
Selim Cinek03d0d652015-11-13 13:18:09 -05004851
4852 /**
4853 * @hide
4854 */
4855 @Override
4856 public boolean hasSummaryInHeader() {
4857 return false;
4858 }
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004859 }
4860
4861 /**
Daniel Sandler4dfbe832012-04-11 14:51:46 -04004862 * Helper class for generating large-format notifications that include a lot of text.
Joe Malin8d40d042012-11-05 11:36:40 -08004863 *
Robert Ly91c5ce32014-06-08 15:37:00 -07004864 * Here's how you'd set the <code>BigTextStyle</code> on a notification:
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004865 * <pre class="prettyprint">
Robert Ly91c5ce32014-06-08 15:37:00 -07004866 * Notification notif = new Notification.Builder(mContext)
4867 * .setContentTitle(&quot;New mail from &quot; + sender.toString())
4868 * .setContentText(subject)
4869 * .setSmallIcon(R.drawable.new_mail)
4870 * .setLargeIcon(aBitmap)
4871 * .setStyle(new Notification.BigTextStyle()
4872 * .bigText(aVeryLongString))
4873 * .build();
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004874 * </pre>
Joe Malin8d40d042012-11-05 11:36:40 -08004875 *
Daniel Sandler4dfbe832012-04-11 14:51:46 -04004876 * @see Notification#bigContentView
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004877 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004878 public static class BigTextStyle extends Style {
Jorim Jaggi457a10d2014-09-08 16:18:23 +02004879
4880 private static final int MAX_LINES = 13;
Selim Cinek3a2c4b92015-12-17 17:01:17 -08004881 private static final int LINES_CONSUMED_BY_ACTIONS = 4;
Jorim Jaggi457a10d2014-09-08 16:18:23 +02004882
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004883 private CharSequence mBigText;
4884
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004885 public BigTextStyle() {
4886 }
4887
Adrian Roosf5faf9d2016-05-23 13:56:15 -07004888 /**
4889 * @deprecated use {@code BigTextStyle()}.
4890 */
4891 @Deprecated
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004892 public BigTextStyle(Builder builder) {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004893 setBuilder(builder);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004894 }
4895
Chris Wrend6297db2012-05-03 16:20:13 -04004896 /**
4897 * Overrides ContentTitle in the big form of the template.
4898 * This defaults to the value passed to setContentTitle().
4899 */
4900 public BigTextStyle setBigContentTitle(CharSequence title) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04004901 internalSetBigContentTitle(safeCharSequence(title));
Chris Wrend6297db2012-05-03 16:20:13 -04004902 return this;
4903 }
4904
4905 /**
4906 * Set the first line of text after the detail section in the big form of the template.
4907 */
4908 public BigTextStyle setSummaryText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04004909 internalSetSummaryText(safeCharSequence(cs));
Chris Wrend6297db2012-05-03 16:20:13 -04004910 return this;
4911 }
4912
Chris Wren0bd664d2012-08-01 13:56:56 -04004913 /**
4914 * Provide the longer text to be displayed in the big form of the
4915 * template in place of the content text.
4916 */
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004917 public BigTextStyle bigText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04004918 mBigText = safeCharSequence(cs);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004919 return this;
4920 }
4921
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004922 /**
4923 * @hide
4924 */
4925 public void addExtras(Bundle extras) {
4926 super.addExtras(extras);
4927
Christoph Studer4600f9b2014-07-22 22:44:43 +02004928 extras.putCharSequence(EXTRA_BIG_TEXT, mBigText);
4929 }
4930
4931 /**
4932 * @hide
4933 */
4934 @Override
4935 protected void restoreFromExtras(Bundle extras) {
4936 super.restoreFromExtras(extras);
4937
4938 mBigText = extras.getCharSequence(EXTRA_BIG_TEXT);
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004939 }
4940
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004941 /**
4942 * @hide
4943 */
4944 public RemoteViews makeBigContentView() {
Christoph Studer4600f9b2014-07-22 22:44:43 +02004945
4946 // Nasty
Selim Cinek75998782016-04-26 10:39:17 -07004947 CharSequence text = mBuilder.getAllExtras().getCharSequence(EXTRA_TEXT);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004948 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, null);
Daniel Sandler916ad912012-06-13 12:17:07 -04004949
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004950 RemoteViews contentView = getStandardView(mBuilder.getBigTextLayoutResource());
Joe Malin8d40d042012-11-05 11:36:40 -08004951
Selim Cinek75998782016-04-26 10:39:17 -07004952 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, text);
Christoph Studer4600f9b2014-07-22 22:44:43 +02004953
Selim Cinek3a2c4b92015-12-17 17:01:17 -08004954 CharSequence bigTextText = mBuilder.processLegacyText(mBigText);
Selim Cinek75998782016-04-26 10:39:17 -07004955 if (TextUtils.isEmpty(bigTextText)) {
4956 // In case the bigtext is null / empty fall back to the normal text to avoid a weird
4957 // experience
4958 bigTextText = mBuilder.processLegacyText(text);
4959 }
Adrian Roosb1f427c2016-05-26 12:27:15 -07004960 applyBigTextContentView(mBuilder, contentView, bigTextText);
Selim Cinek4fb12d32015-11-19 18:10:48 -08004961
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004962 return contentView;
4963 }
4964
Adrian Roosb1f427c2016-05-26 12:27:15 -07004965 static void applyBigTextContentView(Builder builder,
4966 RemoteViews contentView, CharSequence bigTextText) {
4967 contentView.setTextViewText(R.id.big_text, bigTextText);
Selim Cinek7b9605b2017-01-19 17:36:00 -08004968 builder.setTextViewColorSecondary(contentView, R.id.big_text);
Adrian Roosb1f427c2016-05-26 12:27:15 -07004969 contentView.setViewVisibility(R.id.big_text,
4970 TextUtils.isEmpty(bigTextText) ? View.GONE : View.VISIBLE);
4971 contentView.setInt(R.id.big_text, "setMaxLines", calculateMaxLines(builder));
Selim Cinek279fa862016-06-14 10:57:25 -07004972 contentView.setBoolean(R.id.big_text, "setHasImage", builder.mN.hasLargeIcon());
Adrian Roosb1f427c2016-05-26 12:27:15 -07004973 }
4974
4975 private static int calculateMaxLines(Builder builder) {
Jorim Jaggi457a10d2014-09-08 16:18:23 +02004976 int lineCount = MAX_LINES;
Adrian Roosb1f427c2016-05-26 12:27:15 -07004977 boolean hasActions = builder.mActions.size() > 0;
Jorim Jaggi457a10d2014-09-08 16:18:23 +02004978 if (hasActions) {
4979 lineCount -= LINES_CONSUMED_BY_ACTIONS;
4980 }
Jorim Jaggi457a10d2014-09-08 16:18:23 +02004981 return lineCount;
4982 }
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004983 }
Daniel Sandler879c5e02012-04-17 16:46:51 -04004984
4985 /**
Alex Hillsfc737de2016-03-23 17:33:02 -04004986 * Helper class for generating large-format notifications that include multiple back-and-forth
4987 * messages of varying types between any number of people.
4988 *
4989 * <br>
4990 * If the platform does not provide large-format notifications, this method has no effect. The
4991 * user will always see the normal notification view.
4992 * <br>
4993 * This class is a "rebuilder": It attaches to a Builder object and modifies its behavior, like
4994 * so:
4995 * <pre class="prettyprint">
4996 *
4997 * Notification noti = new Notification.Builder()
4998 * .setContentTitle(&quot;2 new messages wtih &quot; + sender.toString())
4999 * .setContentText(subject)
5000 * .setSmallIcon(R.drawable.new_message)
5001 * .setLargeIcon(aBitmap)
5002 * .setStyle(new Notification.MessagingStyle(resources.getString(R.string.reply_name))
5003 * .addMessage(messages[0].getText(), messages[0].getTime(), messages[0].getSender())
5004 * .addMessage(messages[1].getText(), messages[1].getTime(), messages[1].getSender()))
5005 * .build();
5006 * </pre>
5007 */
5008 public static class MessagingStyle extends Style {
5009
5010 /**
5011 * The maximum number of messages that will be retained in the Notification itself (the
5012 * number displayed is up to the platform).
5013 */
5014 public static final int MAXIMUM_RETAINED_MESSAGES = 25;
5015
5016 CharSequence mUserDisplayName;
5017 CharSequence mConversationTitle;
Alex Hillsd9b04d92016-04-11 16:38:16 -04005018 List<Message> mMessages = new ArrayList<>();
Adrian Roos437cd562017-01-18 15:47:03 -08005019 List<Message> mHistoricMessages = new ArrayList<>();
Alex Hillsfc737de2016-03-23 17:33:02 -04005020
5021 MessagingStyle() {
5022 }
5023
5024 /**
Alex Hillsfd590442016-10-07 09:52:44 -04005025 * @param userDisplayName Required - the name to be displayed for any replies sent by the
5026 * user before the posting app reposts the notification with those messages after they've
5027 * been actually sent and in previous messages sent by the user added in
Alex Hillsfc737de2016-03-23 17:33:02 -04005028 * {@link #addMessage(Notification.MessagingStyle.Message)}
5029 */
Alex Hillsfd590442016-10-07 09:52:44 -04005030 public MessagingStyle(@NonNull CharSequence userDisplayName) {
Alex Hillsfc737de2016-03-23 17:33:02 -04005031 mUserDisplayName = userDisplayName;
5032 }
5033
5034 /**
5035 * Returns the name to be displayed for any replies sent by the user
5036 */
5037 public CharSequence getUserDisplayName() {
5038 return mUserDisplayName;
5039 }
5040
5041 /**
Alex Hillsfc737de2016-03-23 17:33:02 -04005042 * Sets the title to be displayed on this conversation. This should only be used for
5043 * group messaging and left unset for one-on-one conversations.
5044 * @param conversationTitle
5045 * @return this object for method chaining.
5046 */
5047 public MessagingStyle setConversationTitle(CharSequence conversationTitle) {
5048 mConversationTitle = conversationTitle;
5049 return this;
5050 }
5051
5052 /**
5053 * Return the title to be displayed on this conversation. Can be <code>null</code> and
5054 * should be for one-on-one conversations
5055 */
5056 public CharSequence getConversationTitle() {
5057 return mConversationTitle;
5058 }
5059
5060 /**
5061 * Adds a message for display by this notification. Convenience call for a simple
5062 * {@link Message} in {@link #addMessage(Notification.MessagingStyle.Message)}.
5063 * @param text A {@link CharSequence} to be displayed as the message content
5064 * @param timestamp Time at which the message arrived
5065 * @param sender A {@link CharSequence} to be used for displaying the name of the
5066 * sender. Should be <code>null</code> for messages by the current user, in which case
5067 * the platform will insert {@link #getUserDisplayName()}.
5068 * Should be unique amongst all individuals in the conversation, and should be
5069 * consistent during re-posts of the notification.
5070 *
5071 * @see Message#Message(CharSequence, long, CharSequence)
5072 *
5073 * @return this object for method chaining
5074 */
5075 public MessagingStyle addMessage(CharSequence text, long timestamp, CharSequence sender) {
Adrian Roos437cd562017-01-18 15:47:03 -08005076 return addMessage(new Message(text, timestamp, sender));
Alex Hillsfc737de2016-03-23 17:33:02 -04005077 }
5078
5079 /**
5080 * Adds a {@link Message} for display in this notification.
Adrian Roos437cd562017-01-18 15:47:03 -08005081 *
5082 * <p>The messages should be added in chronologic order, i.e. the oldest first,
5083 * the newest last.
5084 *
Alex Hillsfc737de2016-03-23 17:33:02 -04005085 * @param message The {@link Message} to be displayed
5086 * @return this object for method chaining
5087 */
5088 public MessagingStyle addMessage(Message message) {
5089 mMessages.add(message);
5090 if (mMessages.size() > MAXIMUM_RETAINED_MESSAGES) {
5091 mMessages.remove(0);
5092 }
5093 return this;
5094 }
5095
5096 /**
Adrian Roos437cd562017-01-18 15:47:03 -08005097 * Adds a {@link Message} for historic context in this notification.
5098 *
5099 * <p>Messages should be added as historic if they are not the main subject of the
5100 * notification but may give context to a conversation. The system may choose to present
5101 * them only when relevant, e.g. when replying to a message through a {@link RemoteInput}.
5102 *
5103 * <p>The messages should be added in chronologic order, i.e. the oldest first,
5104 * the newest last.
5105 *
5106 * @param message The historic {@link Message} to be added
5107 * @return this object for method chaining
5108 */
5109 public MessagingStyle addHistoricMessage(Message message) {
5110 mHistoricMessages.add(message);
5111 if (mHistoricMessages.size() > MAXIMUM_RETAINED_MESSAGES) {
5112 mHistoricMessages.remove(0);
5113 }
5114 return this;
5115 }
5116
5117 /**
Alex Hillsfc737de2016-03-23 17:33:02 -04005118 * Gets the list of {@code Message} objects that represent the notification
5119 */
5120 public List<Message> getMessages() {
5121 return mMessages;
5122 }
5123
5124 /**
Adrian Roos437cd562017-01-18 15:47:03 -08005125 * Gets the list of historic {@code Message}s in the notification.
5126 */
5127 public List<Message> getHistoricMessages() {
5128 return mHistoricMessages;
5129 }
5130
5131 /**
Alex Hillsfc737de2016-03-23 17:33:02 -04005132 * @hide
5133 */
5134 @Override
5135 public void addExtras(Bundle extras) {
5136 super.addExtras(extras);
5137 if (mUserDisplayName != null) {
5138 extras.putCharSequence(EXTRA_SELF_DISPLAY_NAME, mUserDisplayName);
5139 }
5140 if (mConversationTitle != null) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04005141 extras.putCharSequence(EXTRA_CONVERSATION_TITLE, mConversationTitle);
Alex Hillsfc737de2016-03-23 17:33:02 -04005142 }
Alex Hillsd9b04d92016-04-11 16:38:16 -04005143 if (!mMessages.isEmpty()) { extras.putParcelableArray(EXTRA_MESSAGES,
5144 Message.getBundleArrayForMessages(mMessages));
Alex Hillsfc737de2016-03-23 17:33:02 -04005145 }
Adrian Roos437cd562017-01-18 15:47:03 -08005146 if (!mHistoricMessages.isEmpty()) { extras.putParcelableArray(EXTRA_HISTORIC_MESSAGES,
5147 Message.getBundleArrayForMessages(mHistoricMessages));
5148 }
Adrian Roos33fbd2c2016-05-27 15:35:28 -07005149
5150 fixTitleAndTextExtras(extras);
5151 }
5152
5153 private void fixTitleAndTextExtras(Bundle extras) {
5154 Message m = findLatestIncomingMessage();
5155 CharSequence text = (m == null) ? null : m.mText;
5156 CharSequence sender = m == null ? null
5157 : TextUtils.isEmpty(m.mSender) ? mUserDisplayName : m.mSender;
5158 CharSequence title;
5159 if (!TextUtils.isEmpty(mConversationTitle)) {
5160 if (!TextUtils.isEmpty(sender)) {
5161 BidiFormatter bidi = BidiFormatter.getInstance();
5162 title = mBuilder.mContext.getString(
5163 com.android.internal.R.string.notification_messaging_title_template,
5164 bidi.unicodeWrap(mConversationTitle), bidi.unicodeWrap(m.mSender));
5165 } else {
5166 title = mConversationTitle;
5167 }
5168 } else {
5169 title = sender;
5170 }
5171
5172 if (title != null) {
5173 extras.putCharSequence(EXTRA_TITLE, title);
5174 }
5175 if (text != null) {
5176 extras.putCharSequence(EXTRA_TEXT, text);
5177 }
Alex Hillsfc737de2016-03-23 17:33:02 -04005178 }
5179
5180 /**
5181 * @hide
5182 */
5183 @Override
5184 protected void restoreFromExtras(Bundle extras) {
5185 super.restoreFromExtras(extras);
5186
5187 mMessages.clear();
Adrian Roos437cd562017-01-18 15:47:03 -08005188 mHistoricMessages.clear();
Adrian Roos96b7e202016-05-17 13:50:38 -07005189 mUserDisplayName = extras.getCharSequence(EXTRA_SELF_DISPLAY_NAME);
5190 mConversationTitle = extras.getCharSequence(EXTRA_CONVERSATION_TITLE);
Adrian Roos437cd562017-01-18 15:47:03 -08005191 Parcelable[] messages = extras.getParcelableArray(EXTRA_MESSAGES);
5192 if (messages != null && messages instanceof Parcelable[]) {
5193 mMessages = Message.getMessagesFromBundleArray(messages);
5194 }
5195 Parcelable[] histMessages = extras.getParcelableArray(EXTRA_HISTORIC_MESSAGES);
5196 if (histMessages != null && histMessages instanceof Parcelable[]) {
5197 mHistoricMessages = Message.getMessagesFromBundleArray(histMessages);
Alex Hillsfc737de2016-03-23 17:33:02 -04005198 }
5199 }
5200
5201 /**
5202 * @hide
5203 */
Adrian Roosc1a80b02016-04-05 14:54:55 -07005204 @Override
5205 public RemoteViews makeContentView() {
5206 Message m = findLatestIncomingMessage();
5207 CharSequence title = mConversationTitle != null
5208 ? mConversationTitle
5209 : (m == null) ? null : m.mSender;
5210 CharSequence text = (m == null)
5211 ? null
Selim Cinek7b9605b2017-01-19 17:36:00 -08005212 : mConversationTitle != null ? makeMessageLine(m, mBuilder) : m.mText;
Alex Hillsfc737de2016-03-23 17:33:02 -04005213
Adrian Roos70d7aa32017-01-11 15:39:06 -08005214 return mBuilder.applyStandardTemplateWithActions(mBuilder.getBaseLayoutResource(),
5215 mBuilder.mParams.reset().hasProgress(false).title(title).text(text));
Adrian Roosc1a80b02016-04-05 14:54:55 -07005216 }
5217
5218 private Message findLatestIncomingMessage() {
5219 for (int i = mMessages.size() - 1; i >= 0; i--) {
5220 Message m = mMessages.get(i);
5221 // Incoming messages have a non-empty sender.
5222 if (!TextUtils.isEmpty(m.mSender)) {
5223 return m;
5224 }
5225 }
Adrian Roos33fbd2c2016-05-27 15:35:28 -07005226 if (!mMessages.isEmpty()) {
5227 // No incoming messages, fall back to outgoing message
5228 return mMessages.get(mMessages.size() - 1);
5229 }
Adrian Roosc1a80b02016-04-05 14:54:55 -07005230 return null;
5231 }
5232
5233 /**
5234 * @hide
5235 */
5236 @Override
5237 public RemoteViews makeBigContentView() {
5238 CharSequence title = !TextUtils.isEmpty(super.mBigContentTitle)
5239 ? super.mBigContentTitle
5240 : mConversationTitle;
5241 boolean hasTitle = !TextUtils.isEmpty(title);
5242
Adrian Roosfeafa052016-06-01 17:09:45 -07005243 if (mMessages.size() == 1) {
5244 // Special case for a single message: Use the big text style
5245 // so the collapsed and expanded versions match nicely.
5246 CharSequence bigTitle;
5247 CharSequence text;
5248 if (hasTitle) {
5249 bigTitle = title;
Selim Cinek7b9605b2017-01-19 17:36:00 -08005250 text = makeMessageLine(mMessages.get(0), mBuilder);
Adrian Roosfeafa052016-06-01 17:09:45 -07005251 } else {
5252 bigTitle = mMessages.get(0).mSender;
5253 text = mMessages.get(0).mText;
5254 }
Adrian Roosb1f427c2016-05-26 12:27:15 -07005255 RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(
5256 mBuilder.getBigTextLayoutResource(),
Adrian Roos70d7aa32017-01-11 15:39:06 -08005257 mBuilder.mParams.reset().hasProgress(false).title(bigTitle).text(null));
Adrian Roosb1f427c2016-05-26 12:27:15 -07005258 BigTextStyle.applyBigTextContentView(mBuilder, contentView, text);
5259 return contentView;
5260 }
5261
Adrian Roos48d746a2016-04-12 14:57:28 -07005262 RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(
Adrian Roosc1a80b02016-04-05 14:54:55 -07005263 mBuilder.getMessagingLayoutResource(),
Adrian Roos70d7aa32017-01-11 15:39:06 -08005264 mBuilder.mParams.reset().hasProgress(false).title(title).text(null));
Adrian Roosc1a80b02016-04-05 14:54:55 -07005265
5266 int[] rowIds = {R.id.inbox_text0, R.id.inbox_text1, R.id.inbox_text2, R.id.inbox_text3,
5267 R.id.inbox_text4, R.id.inbox_text5, R.id.inbox_text6};
5268
5269 // Make sure all rows are gone in case we reuse a view.
5270 for (int rowId : rowIds) {
5271 contentView.setViewVisibility(rowId, View.GONE);
5272 }
5273
5274 int i=0;
Adrian Roos2d5dbba2016-06-08 17:11:53 -07005275 contentView.setViewLayoutMarginBottomDimen(R.id.line1,
5276 hasTitle ? R.dimen.notification_messaging_spacing : 0);
Adrian Roosc1a80b02016-04-05 14:54:55 -07005277 contentView.setInt(R.id.notification_messaging, "setNumIndentLines",
Selim Cinek279fa862016-06-14 10:57:25 -07005278 !mBuilder.mN.hasLargeIcon() ? 0 : (hasTitle ? 1 : 2));
Adrian Roosc1a80b02016-04-05 14:54:55 -07005279
Adrian Roosfeafa052016-06-01 17:09:45 -07005280 int contractedChildId = View.NO_ID;
5281 Message contractedMessage = findLatestIncomingMessage();
Adrian Roos437cd562017-01-18 15:47:03 -08005282 int firstHistoricMessage = Math.max(0, mHistoricMessages.size()
5283 - (rowIds.length - mMessages.size()));
5284 while (firstHistoricMessage + i < mHistoricMessages.size() && i < rowIds.length) {
5285 Message m = mHistoricMessages.get(firstHistoricMessage + i);
5286 int rowId = rowIds[i];
5287
Selim Cinek7b9605b2017-01-19 17:36:00 -08005288 contentView.setTextViewText(rowId, makeMessageLine(m, mBuilder));
Adrian Roos437cd562017-01-18 15:47:03 -08005289
5290 if (contractedMessage == m) {
5291 contractedChildId = rowId;
5292 }
5293
5294 i++;
5295 }
5296
Adrian Roosc1a80b02016-04-05 14:54:55 -07005297 int firstMessage = Math.max(0, mMessages.size() - rowIds.length);
5298 while (firstMessage + i < mMessages.size() && i < rowIds.length) {
5299 Message m = mMessages.get(firstMessage + i);
5300 int rowId = rowIds[i];
5301
5302 contentView.setViewVisibility(rowId, View.VISIBLE);
Selim Cinek7b9605b2017-01-19 17:36:00 -08005303 contentView.setTextViewText(rowId, makeMessageLine(m, mBuilder));
5304 mBuilder.setTextViewColorSecondary(contentView, rowId);
Adrian Roosc1a80b02016-04-05 14:54:55 -07005305
Adrian Roosfeafa052016-06-01 17:09:45 -07005306 if (contractedMessage == m) {
5307 contractedChildId = rowId;
5308 }
5309
Adrian Roosc1a80b02016-04-05 14:54:55 -07005310 i++;
5311 }
Adrian Roos437cd562017-01-18 15:47:03 -08005312 // Clear the remaining views for reapply. Ensures that historic message views can
5313 // reliably be identified as being GONE and having non-null text.
5314 while (i < rowIds.length) {
5315 int rowId = rowIds[i];
5316 contentView.setTextViewText(rowId, null);
5317 i++;
5318 }
5319
Adrian Roosfeafa052016-06-01 17:09:45 -07005320 // Record this here to allow transformation between the contracted and expanded views.
5321 contentView.setInt(R.id.notification_messaging, "setContractedChildId",
5322 contractedChildId);
Alex Hillsfc737de2016-03-23 17:33:02 -04005323 return contentView;
5324 }
5325
Selim Cinek7b9605b2017-01-19 17:36:00 -08005326 private CharSequence makeMessageLine(Message m, Builder builder) {
Adrian Roosc1a80b02016-04-05 14:54:55 -07005327 BidiFormatter bidi = BidiFormatter.getInstance();
5328 SpannableStringBuilder sb = new SpannableStringBuilder();
Selim Cinek7b9605b2017-01-19 17:36:00 -08005329 boolean colorize = builder.isColorized();
Adrian Roosc1a80b02016-04-05 14:54:55 -07005330 if (TextUtils.isEmpty(m.mSender)) {
5331 CharSequence replyName = mUserDisplayName == null ? "" : mUserDisplayName;
5332 sb.append(bidi.unicodeWrap(replyName),
Selim Cinek7b9605b2017-01-19 17:36:00 -08005333 makeFontColorSpan(colorize
5334 ? builder.getPrimaryTextColor()
5335 : mBuilder.resolveContrastColor()),
Adrian Roosc1a80b02016-04-05 14:54:55 -07005336 0 /* flags */);
5337 } else {
5338 sb.append(bidi.unicodeWrap(m.mSender),
Selim Cinek7b9605b2017-01-19 17:36:00 -08005339 makeFontColorSpan(colorize
5340 ? builder.getPrimaryTextColor()
5341 : Color.BLACK),
Adrian Roosc1a80b02016-04-05 14:54:55 -07005342 0 /* flags */);
5343 }
5344 CharSequence text = m.mText == null ? "" : m.mText;
5345 sb.append(" ").append(bidi.unicodeWrap(text));
5346 return sb;
5347 }
5348
Adrian Roosdedd1df2016-04-26 16:38:47 -07005349 /**
5350 * @hide
5351 */
5352 @Override
5353 public RemoteViews makeHeadsUpContentView() {
5354 Message m = findLatestIncomingMessage();
5355 CharSequence title = mConversationTitle != null
5356 ? mConversationTitle
5357 : (m == null) ? null : m.mSender;
5358 CharSequence text = (m == null)
5359 ? null
Selim Cinek7b9605b2017-01-19 17:36:00 -08005360 : mConversationTitle != null ? makeMessageLine(m, mBuilder) : m.mText;
Adrian Roosdedd1df2016-04-26 16:38:47 -07005361
5362 return mBuilder.applyStandardTemplateWithActions(mBuilder.getBigBaseLayoutResource(),
Adrian Roos70d7aa32017-01-11 15:39:06 -08005363 mBuilder.mParams.reset().hasProgress(false).title(title).text(text));
Adrian Roosdedd1df2016-04-26 16:38:47 -07005364 }
5365
Adrian Roosc1a80b02016-04-05 14:54:55 -07005366 private static TextAppearanceSpan makeFontColorSpan(int color) {
5367 return new TextAppearanceSpan(null, 0, 0,
5368 ColorStateList.valueOf(color), null);
5369 }
5370
Alex Hillsd9b04d92016-04-11 16:38:16 -04005371 public static final class Message {
5372
5373 static final String KEY_TEXT = "text";
5374 static final String KEY_TIMESTAMP = "time";
5375 static final String KEY_SENDER = "sender";
5376 static final String KEY_DATA_MIME_TYPE = "type";
5377 static final String KEY_DATA_URI= "uri";
Alex Hillsfc737de2016-03-23 17:33:02 -04005378
5379 private final CharSequence mText;
5380 private final long mTimestamp;
5381 private final CharSequence mSender;
5382
5383 private String mDataMimeType;
5384 private Uri mDataUri;
5385
5386 /**
5387 * Constructor
5388 * @param text A {@link CharSequence} to be displayed as the message content
5389 * @param timestamp Time at which the message arrived
5390 * @param sender A {@link CharSequence} to be used for displaying the name of the
5391 * sender. Should be <code>null</code> for messages by the current user, in which case
5392 * the platform will insert {@link MessagingStyle#getUserDisplayName()}.
5393 * Should be unique amongst all individuals in the conversation, and should be
5394 * consistent during re-posts of the notification.
5395 */
5396 public Message(CharSequence text, long timestamp, CharSequence sender){
5397 mText = text;
5398 mTimestamp = timestamp;
5399 mSender = sender;
5400 }
5401
5402 /**
5403 * Sets a binary blob of data and an associated MIME type for a message. In the case
5404 * where the platform doesn't support the MIME type, the original text provided in the
5405 * constructor will be used.
5406 * @param dataMimeType The MIME type of the content. See
5407 * <a href="{@docRoot}notifications/messaging.html"> for the list of supported MIME
5408 * types on Android and Android Wear.
5409 * @param dataUri The uri containing the content whose type is given by the MIME type.
5410 * <p class="note">
5411 * <ol>
5412 * <li>Notification Listeners including the System UI need permission to access the
5413 * data the Uri points to. The recommended ways to do this are:</li>
5414 * <li>Store the data in your own ContentProvider, making sure that other apps have
5415 * the correct permission to access your provider. The preferred mechanism for
5416 * providing access is to use per-URI permissions which are temporary and only
5417 * grant access to the receiving application. An easy way to create a
5418 * ContentProvider like this is to use the FileProvider helper class.</li>
5419 * <li>Use the system MediaStore. The MediaStore is primarily aimed at video, audio
5420 * and image MIME types, however beginning with Android 3.0 (API level 11) it can
5421 * also store non-media types (see MediaStore.Files for more info). Files can be
5422 * inserted into the MediaStore using scanFile() after which a content:// style
5423 * Uri suitable for sharing is passed to the provided onScanCompleted() callback.
5424 * Note that once added to the system MediaStore the content is accessible to any
5425 * app on the device.</li>
5426 * </ol>
5427 * @return this object for method chaining
5428 */
5429 public Message setData(String dataMimeType, Uri dataUri) {
5430 mDataMimeType = dataMimeType;
5431 mDataUri = dataUri;
5432 return this;
5433 }
5434
Alex Hillsfc737de2016-03-23 17:33:02 -04005435 /**
5436 * Get the text to be used for this message, or the fallback text if a type and content
5437 * Uri have been set
5438 */
5439 public CharSequence getText() {
5440 return mText;
5441 }
5442
5443 /**
5444 * Get the time at which this message arrived
5445 */
5446 public long getTimestamp() {
5447 return mTimestamp;
5448 }
5449
5450 /**
5451 * Get the text used to display the contact's name in the messaging experience
5452 */
5453 public CharSequence getSender() {
5454 return mSender;
5455 }
5456
5457 /**
5458 * Get the MIME type of the data pointed to by the Uri
5459 */
5460 public String getDataMimeType() {
5461 return mDataMimeType;
5462 }
5463
5464 /**
5465 * Get the the Uri pointing to the content of the message. Can be null, in which case
5466 * {@see #getText()} is used.
5467 */
5468 public Uri getDataUri() {
5469 return mDataUri;
5470 }
5471
Alex Hillsd9b04d92016-04-11 16:38:16 -04005472 private Bundle toBundle() {
5473 Bundle bundle = new Bundle();
Alex Hillsfc737de2016-03-23 17:33:02 -04005474 if (mText != null) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04005475 bundle.putCharSequence(KEY_TEXT, mText);
Alex Hillsfc737de2016-03-23 17:33:02 -04005476 }
Alex Hillsd9b04d92016-04-11 16:38:16 -04005477 bundle.putLong(KEY_TIMESTAMP, mTimestamp);
Alex Hillsfc737de2016-03-23 17:33:02 -04005478 if (mSender != null) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04005479 bundle.putCharSequence(KEY_SENDER, mSender);
Alex Hillsfc737de2016-03-23 17:33:02 -04005480 }
5481 if (mDataMimeType != null) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04005482 bundle.putString(KEY_DATA_MIME_TYPE, mDataMimeType);
Alex Hillsfc737de2016-03-23 17:33:02 -04005483 }
5484 if (mDataUri != null) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04005485 bundle.putParcelable(KEY_DATA_URI, mDataUri);
Alex Hillsfc737de2016-03-23 17:33:02 -04005486 }
Alex Hillsd9b04d92016-04-11 16:38:16 -04005487 return bundle;
Alex Hillsfc737de2016-03-23 17:33:02 -04005488 }
5489
Alex Hillsd9b04d92016-04-11 16:38:16 -04005490 static Bundle[] getBundleArrayForMessages(List<Message> messages) {
5491 Bundle[] bundles = new Bundle[messages.size()];
5492 final int N = messages.size();
5493 for (int i = 0; i < N; i++) {
5494 bundles[i] = messages.get(i).toBundle();
5495 }
5496 return bundles;
5497 }
5498
Adrian Roosdedd1df2016-04-26 16:38:47 -07005499 static List<Message> getMessagesFromBundleArray(Parcelable[] bundles) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04005500 List<Message> messages = new ArrayList<>(bundles.length);
5501 for (int i = 0; i < bundles.length; i++) {
Adrian Roosdedd1df2016-04-26 16:38:47 -07005502 if (bundles[i] instanceof Bundle) {
5503 Message message = getMessageFromBundle((Bundle)bundles[i]);
5504 if (message != null) {
5505 messages.add(message);
5506 }
Alex Hillsd9b04d92016-04-11 16:38:16 -04005507 }
5508 }
5509 return messages;
5510 }
5511
5512 static Message getMessageFromBundle(Bundle bundle) {
5513 try {
Adrian Roosfbddd2c2016-05-13 12:57:20 -07005514 if (!bundle.containsKey(KEY_TEXT) || !bundle.containsKey(KEY_TIMESTAMP)) {
Alex Hillsd9b04d92016-04-11 16:38:16 -04005515 return null;
5516 } else {
5517 Message message = new Message(bundle.getCharSequence(KEY_TEXT),
5518 bundle.getLong(KEY_TIMESTAMP), bundle.getCharSequence(KEY_SENDER));
5519 if (bundle.containsKey(KEY_DATA_MIME_TYPE) &&
5520 bundle.containsKey(KEY_DATA_URI)) {
5521
5522 message.setData(bundle.getString(KEY_DATA_MIME_TYPE),
5523 (Uri) bundle.getParcelable(KEY_DATA_URI));
Alex Hillsfc737de2016-03-23 17:33:02 -04005524 }
Alex Hillsd9b04d92016-04-11 16:38:16 -04005525 return message;
5526 }
5527 } catch (ClassCastException e) {
5528 return null;
5529 }
5530 }
Alex Hillsfc737de2016-03-23 17:33:02 -04005531 }
5532 }
5533
5534 /**
Daniel Sandler879c5e02012-04-17 16:46:51 -04005535 * Helper class for generating large-format notifications that include a list of (up to 5) strings.
Joe Malin8d40d042012-11-05 11:36:40 -08005536 *
Robert Ly91c5ce32014-06-08 15:37:00 -07005537 * Here's how you'd set the <code>InboxStyle</code> on a notification:
Daniel Sandler879c5e02012-04-17 16:46:51 -04005538 * <pre class="prettyprint">
Robert Ly91c5ce32014-06-08 15:37:00 -07005539 * Notification notif = new Notification.Builder(mContext)
5540 * .setContentTitle(&quot;5 New mails from &quot; + sender.toString())
5541 * .setContentText(subject)
5542 * .setSmallIcon(R.drawable.new_mail)
5543 * .setLargeIcon(aBitmap)
5544 * .setStyle(new Notification.InboxStyle()
5545 * .addLine(str1)
5546 * .addLine(str2)
5547 * .setContentTitle(&quot;&quot;)
5548 * .setSummaryText(&quot;+3 more&quot;))
5549 * .build();
Daniel Sandler879c5e02012-04-17 16:46:51 -04005550 * </pre>
Joe Malin8d40d042012-11-05 11:36:40 -08005551 *
Daniel Sandler879c5e02012-04-17 16:46:51 -04005552 * @see Notification#bigContentView
5553 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005554 public static class InboxStyle extends Style {
Daniel Sandler879c5e02012-04-17 16:46:51 -04005555 private ArrayList<CharSequence> mTexts = new ArrayList<CharSequence>(5);
5556
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005557 public InboxStyle() {
5558 }
5559
Adrian Roosf5faf9d2016-05-23 13:56:15 -07005560 /**
5561 * @deprecated use {@code InboxStyle()}.
5562 */
5563 @Deprecated
Daniel Sandler879c5e02012-04-17 16:46:51 -04005564 public InboxStyle(Builder builder) {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04005565 setBuilder(builder);
Daniel Sandler879c5e02012-04-17 16:46:51 -04005566 }
5567
Chris Wrend6297db2012-05-03 16:20:13 -04005568 /**
5569 * Overrides ContentTitle in the big form of the template.
5570 * This defaults to the value passed to setContentTitle().
5571 */
5572 public InboxStyle setBigContentTitle(CharSequence title) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04005573 internalSetBigContentTitle(safeCharSequence(title));
Chris Wrend6297db2012-05-03 16:20:13 -04005574 return this;
5575 }
5576
5577 /**
5578 * Set the first line of text after the detail section in the big form of the template.
5579 */
5580 public InboxStyle setSummaryText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04005581 internalSetSummaryText(safeCharSequence(cs));
Chris Wrend6297db2012-05-03 16:20:13 -04005582 return this;
5583 }
5584
Chris Wren0bd664d2012-08-01 13:56:56 -04005585 /**
5586 * Append a line to the digest section of the Inbox notification.
5587 */
Daniel Sandler879c5e02012-04-17 16:46:51 -04005588 public InboxStyle addLine(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04005589 mTexts.add(safeCharSequence(cs));
Daniel Sandler879c5e02012-04-17 16:46:51 -04005590 return this;
5591 }
5592
Daniel Sandlerf45564e2013-04-15 15:05:08 -04005593 /**
5594 * @hide
5595 */
5596 public void addExtras(Bundle extras) {
5597 super.addExtras(extras);
Christoph Studer4600f9b2014-07-22 22:44:43 +02005598
Daniel Sandlerf45564e2013-04-15 15:05:08 -04005599 CharSequence[] a = new CharSequence[mTexts.size()];
5600 extras.putCharSequenceArray(EXTRA_TEXT_LINES, mTexts.toArray(a));
5601 }
5602
Christoph Studer4600f9b2014-07-22 22:44:43 +02005603 /**
5604 * @hide
5605 */
5606 @Override
5607 protected void restoreFromExtras(Bundle extras) {
5608 super.restoreFromExtras(extras);
5609
5610 mTexts.clear();
5611 if (extras.containsKey(EXTRA_TEXT_LINES)) {
5612 Collections.addAll(mTexts, extras.getCharSequenceArray(EXTRA_TEXT_LINES));
5613 }
5614 }
5615
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005616 /**
5617 * @hide
5618 */
5619 public RemoteViews makeBigContentView() {
Selim Cinekc848c3a2016-01-13 15:27:30 -08005620 // Remove the content text so it disappears unless you have a summary
Christoph Studer4600f9b2014-07-22 22:44:43 +02005621 // Nasty
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005622 CharSequence oldBuilderContentText = mBuilder.mN.extras.getCharSequence(EXTRA_TEXT);
5623 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, null);
Christoph Studer4600f9b2014-07-22 22:44:43 +02005624
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01005625 RemoteViews contentView = getStandardView(mBuilder.getInboxLayoutResource());
Daniel Sandler619738c2012-06-07 16:33:08 -04005626
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005627 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, oldBuilderContentText);
Christoph Studer4600f9b2014-07-22 22:44:43 +02005628
Chris Wrend6297db2012-05-03 16:20:13 -04005629 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 -04005630 R.id.inbox_text4, R.id.inbox_text5, R.id.inbox_text6};
Chris Wrend6297db2012-05-03 16:20:13 -04005631
Chris Wren4ed80d52012-05-17 09:30:03 -04005632 // Make sure all rows are gone in case we reuse a view.
5633 for (int rowId : rowIds) {
5634 contentView.setViewVisibility(rowId, View.GONE);
5635 }
5636
Daniel Sandler879c5e02012-04-17 16:46:51 -04005637 int i=0;
Selim Cinek07c80172016-04-21 16:40:47 -07005638 int topPadding = mBuilder.mContext.getResources().getDimensionPixelSize(
5639 R.dimen.notification_inbox_item_top_padding);
Selim Cinek247fa012016-02-18 09:50:48 -08005640 boolean first = true;
Selim Cinek07c80172016-04-21 16:40:47 -07005641 int onlyViewId = 0;
5642 int maxRows = rowIds.length;
5643 if (mBuilder.mActions.size() > 0) {
5644 maxRows--;
5645 }
5646 while (i < mTexts.size() && i < maxRows) {
Daniel Sandler879c5e02012-04-17 16:46:51 -04005647 CharSequence str = mTexts.get(i);
Selim Cinek07c80172016-04-21 16:40:47 -07005648 if (!TextUtils.isEmpty(str)) {
Daniel Sandler879c5e02012-04-17 16:46:51 -04005649 contentView.setViewVisibility(rowIds[i], View.VISIBLE);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01005650 contentView.setTextViewText(rowIds[i], mBuilder.processLegacyText(str));
Selim Cinek7b9605b2017-01-19 17:36:00 -08005651 mBuilder.setTextViewColorSecondary(contentView, rowIds[i]);
Selim Cinek07c80172016-04-21 16:40:47 -07005652 contentView.setViewPadding(rowIds[i], 0, topPadding, 0, 0);
Selim Cinek247fa012016-02-18 09:50:48 -08005653 handleInboxImageMargin(contentView, rowIds[i], first);
Selim Cinek07c80172016-04-21 16:40:47 -07005654 if (first) {
5655 onlyViewId = rowIds[i];
5656 } else {
5657 onlyViewId = 0;
5658 }
Selim Cinek247fa012016-02-18 09:50:48 -08005659 first = false;
Daniel Sandler879c5e02012-04-17 16:46:51 -04005660 }
5661 i++;
5662 }
Selim Cinek07c80172016-04-21 16:40:47 -07005663 if (onlyViewId != 0) {
5664 // We only have 1 entry, lets make it look like the normal Text of a Bigtext
5665 topPadding = mBuilder.mContext.getResources().getDimensionPixelSize(
5666 R.dimen.notification_text_margin_top);
5667 contentView.setViewPadding(onlyViewId, 0, topPadding, 0, 0);
5668 }
Selim Cinek1e0bf612015-11-20 15:57:26 -08005669
Daniel Sandler879c5e02012-04-17 16:46:51 -04005670 return contentView;
5671 }
Selim Cinek1e0bf612015-11-20 15:57:26 -08005672
Selim Cinek247fa012016-02-18 09:50:48 -08005673 private void handleInboxImageMargin(RemoteViews contentView, int id, boolean first) {
Selim Cinek1e0bf612015-11-20 15:57:26 -08005674 int endMargin = 0;
Selim Cinek247fa012016-02-18 09:50:48 -08005675 if (first) {
5676 final int max = mBuilder.mN.extras.getInt(EXTRA_PROGRESS_MAX, 0);
5677 final boolean ind = mBuilder.mN.extras.getBoolean(EXTRA_PROGRESS_INDETERMINATE);
5678 boolean hasProgress = max != 0 || ind;
Selim Cinek279fa862016-06-14 10:57:25 -07005679 if (mBuilder.mN.hasLargeIcon() && !hasProgress) {
Adrian Roos2d5dbba2016-06-08 17:11:53 -07005680 endMargin = R.dimen.notification_content_picture_margin;
Selim Cinek247fa012016-02-18 09:50:48 -08005681 }
Selim Cinek1e0bf612015-11-20 15:57:26 -08005682 }
Adrian Roos2d5dbba2016-06-08 17:11:53 -07005683 contentView.setViewLayoutMarginEndDimen(id, endMargin);
Selim Cinek1e0bf612015-11-20 15:57:26 -08005684 }
Daniel Sandler879c5e02012-04-17 16:46:51 -04005685 }
Dan Sandler842dd772014-05-15 09:36:47 -04005686
5687 /**
5688 * Notification style for media playback notifications.
5689 *
5690 * In the expanded form, {@link Notification#bigContentView}, up to 5
5691 * {@link Notification.Action}s specified with
Dan Sandler86647982015-05-13 23:41:13 -04005692 * {@link Notification.Builder#addAction(Action) addAction} will be
Dan Sandler842dd772014-05-15 09:36:47 -04005693 * shown as icon-only pushbuttons, suitable for transport controls. The Bitmap given to
5694 * {@link Notification.Builder#setLargeIcon(android.graphics.Bitmap) setLargeIcon()} will be
5695 * treated as album artwork.
5696 *
5697 * Unlike the other styles provided here, MediaStyle can also modify the standard-size
5698 * {@link Notification#contentView}; by providing action indices to
Christoph Studerfde6f4d2014-12-12 13:23:26 +01005699 * {@link #setShowActionsInCompactView(int...)} you can promote up to 3 actions to be displayed
Dan Sandler842dd772014-05-15 09:36:47 -04005700 * in the standard view alongside the usual content.
5701 *
Bryan Mawhinney6be8de32014-07-18 10:35:12 +01005702 * Notifications created with MediaStyle will have their category set to
5703 * {@link Notification#CATEGORY_TRANSPORT CATEGORY_TRANSPORT} unless you set a different
5704 * category using {@link Notification.Builder#setCategory(String) setCategory()}.
5705 *
Jeff Browndba34ba2014-06-24 20:46:03 -07005706 * Finally, if you attach a {@link android.media.session.MediaSession.Token} using
5707 * {@link android.app.Notification.MediaStyle#setMediaSession(MediaSession.Token)},
Dan Sandler842dd772014-05-15 09:36:47 -04005708 * the System UI can identify this as a notification representing an active media session
5709 * and respond accordingly (by showing album artwork in the lockscreen, for example).
5710 *
5711 * To use this style with your Notification, feed it to
5712 * {@link Notification.Builder#setStyle(android.app.Notification.Style)} like so:
5713 * <pre class="prettyprint">
5714 * Notification noti = new Notification.Builder()
5715 * .setSmallIcon(R.drawable.ic_stat_player)
Christoph Studere935fe92014-11-24 14:18:06 +01005716 * .setContentTitle(&quot;Track title&quot;)
5717 * .setContentText(&quot;Artist - Album&quot;)
5718 * .setLargeIcon(albumArtBitmap))
Bryan Mawhinney6be8de32014-07-18 10:35:12 +01005719 * .setStyle(<b>new Notification.MediaStyle()</b>
5720 * .setMediaSession(mySession))
Dan Sandler842dd772014-05-15 09:36:47 -04005721 * .build();
5722 * </pre>
5723 *
5724 * @see Notification#bigContentView
5725 */
5726 public static class MediaStyle extends Style {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02005727 static final int MAX_MEDIA_BUTTONS_IN_COMPACT = 3;
Dan Sandler842dd772014-05-15 09:36:47 -04005728 static final int MAX_MEDIA_BUTTONS = 5;
5729
5730 private int[] mActionsToShowInCompact = null;
Jeff Browndba34ba2014-06-24 20:46:03 -07005731 private MediaSession.Token mToken;
Dan Sandler842dd772014-05-15 09:36:47 -04005732
5733 public MediaStyle() {
5734 }
5735
Adrian Roosf5faf9d2016-05-23 13:56:15 -07005736 /**
5737 * @deprecated use {@code MediaStyle()}.
5738 */
5739 @Deprecated
Dan Sandler842dd772014-05-15 09:36:47 -04005740 public MediaStyle(Builder builder) {
5741 setBuilder(builder);
5742 }
5743
5744 /**
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02005745 * 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 -04005746 * notification view.
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02005747 *
5748 * @param actions the indices of the actions to show in the compact notification view
Dan Sandler842dd772014-05-15 09:36:47 -04005749 */
5750 public MediaStyle setShowActionsInCompactView(int...actions) {
5751 mActionsToShowInCompact = actions;
5752 return this;
5753 }
5754
5755 /**
Jeff Browndba34ba2014-06-24 20:46:03 -07005756 * Attach a {@link android.media.session.MediaSession.Token} to this Notification
5757 * to provide additional playback information and control to the SystemUI.
Dan Sandler842dd772014-05-15 09:36:47 -04005758 */
Jeff Browndba34ba2014-06-24 20:46:03 -07005759 public MediaStyle setMediaSession(MediaSession.Token token) {
Dan Sandler842dd772014-05-15 09:36:47 -04005760 mToken = token;
5761 return this;
5762 }
5763
Christoph Studer4600f9b2014-07-22 22:44:43 +02005764 /**
5765 * @hide
5766 */
Dan Sandler842dd772014-05-15 09:36:47 -04005767 @Override
5768 public Notification buildStyled(Notification wip) {
Christoph Studer4600f9b2014-07-22 22:44:43 +02005769 super.buildStyled(wip);
Bryan Mawhinney6be8de32014-07-18 10:35:12 +01005770 if (wip.category == null) {
5771 wip.category = Notification.CATEGORY_TRANSPORT;
5772 }
Dan Sandler842dd772014-05-15 09:36:47 -04005773 return wip;
5774 }
5775
Christoph Studer4600f9b2014-07-22 22:44:43 +02005776 /**
5777 * @hide
5778 */
5779 @Override
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005780 public RemoteViews makeContentView() {
5781 return makeMediaContentView();
Christoph Studer4600f9b2014-07-22 22:44:43 +02005782 }
5783
5784 /**
5785 * @hide
5786 */
5787 @Override
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005788 public RemoteViews makeBigContentView() {
5789 return makeMediaBigContentView();
Christoph Studer4600f9b2014-07-22 22:44:43 +02005790 }
5791
Selim Cinekcc10bfb2016-02-10 16:24:21 -08005792 /**
5793 * @hide
5794 */
5795 @Override
5796 public RemoteViews makeHeadsUpContentView() {
5797 RemoteViews expanded = makeMediaBigContentView();
5798 return expanded != null ? expanded : makeMediaContentView();
5799 }
5800
Dan Sandler842dd772014-05-15 09:36:47 -04005801 /** @hide */
5802 @Override
5803 public void addExtras(Bundle extras) {
5804 super.addExtras(extras);
5805
5806 if (mToken != null) {
5807 extras.putParcelable(EXTRA_MEDIA_SESSION, mToken);
5808 }
Bryan Mawhinneye191f902014-07-22 12:50:09 +01005809 if (mActionsToShowInCompact != null) {
5810 extras.putIntArray(EXTRA_COMPACT_ACTIONS, mActionsToShowInCompact);
5811 }
Dan Sandler842dd772014-05-15 09:36:47 -04005812 }
5813
Christoph Studer4600f9b2014-07-22 22:44:43 +02005814 /**
5815 * @hide
5816 */
5817 @Override
5818 protected void restoreFromExtras(Bundle extras) {
5819 super.restoreFromExtras(extras);
5820
5821 if (extras.containsKey(EXTRA_MEDIA_SESSION)) {
5822 mToken = extras.getParcelable(EXTRA_MEDIA_SESSION);
5823 }
5824 if (extras.containsKey(EXTRA_COMPACT_ACTIONS)) {
5825 mActionsToShowInCompact = extras.getIntArray(EXTRA_COMPACT_ACTIONS);
5826 }
5827 }
5828
Selim Cinek5bf069a2015-11-10 19:14:27 -05005829 private RemoteViews generateMediaActionButton(Action action, int color) {
Dan Sandler842dd772014-05-15 09:36:47 -04005830 final boolean tombstone = (action.actionIntent == null);
Selim Cinekf33b1112015-07-15 17:45:11 -07005831 RemoteViews button = new BuilderRemoteViews(mBuilder.mContext.getApplicationInfo(),
Alan Viverette3cb07a462014-06-06 14:19:53 -07005832 R.layout.notification_material_media_action);
Dan Sandler68079d52015-07-22 10:45:30 -04005833 button.setImageViewIcon(R.id.action0, action.getIcon());
Selim Cinek5bf069a2015-11-10 19:14:27 -05005834 button.setDrawableParameters(R.id.action0, false, -1, color, PorterDuff.Mode.SRC_ATOP,
5835 -1);
Dan Sandler842dd772014-05-15 09:36:47 -04005836 if (!tombstone) {
5837 button.setOnClickPendingIntent(R.id.action0, action.actionIntent);
5838 }
5839 button.setContentDescription(R.id.action0, action.title);
5840 return button;
5841 }
5842
5843 private RemoteViews makeMediaContentView() {
5844 RemoteViews view = mBuilder.applyStandardTemplate(
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02005845 R.layout.notification_template_material_media, false /* hasProgress */);
Dan Sandler842dd772014-05-15 09:36:47 -04005846
5847 final int numActions = mBuilder.mActions.size();
5848 final int N = mActionsToShowInCompact == null
5849 ? 0
5850 : Math.min(mActionsToShowInCompact.length, MAX_MEDIA_BUTTONS_IN_COMPACT);
5851 if (N > 0) {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02005852 view.removeAllViews(com.android.internal.R.id.media_actions);
Dan Sandler842dd772014-05-15 09:36:47 -04005853 for (int i = 0; i < N; i++) {
5854 if (i >= numActions) {
5855 throw new IllegalArgumentException(String.format(
5856 "setShowActionsInCompactView: action %d out of bounds (max %d)",
5857 i, numActions - 1));
5858 }
5859
5860 final Action action = mBuilder.mActions.get(mActionsToShowInCompact[i]);
Selim Cinek5bf069a2015-11-10 19:14:27 -05005861 final RemoteViews button = generateMediaActionButton(action,
Adrian Roos4ff3b122016-02-01 12:26:13 -08005862 mBuilder.resolveContrastColor());
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02005863 view.addView(com.android.internal.R.id.media_actions, button);
Dan Sandler842dd772014-05-15 09:36:47 -04005864 }
5865 }
Selim Cinekfdc738f2016-01-27 20:04:27 -08005866 handleImage(view);
5867 // handle the content margin
Adrian Roos2d5dbba2016-06-08 17:11:53 -07005868 int endMargin = R.dimen.notification_content_margin_end;
Selim Cinek279fa862016-06-14 10:57:25 -07005869 if (mBuilder.mN.hasLargeIcon()) {
Adrian Roos2d5dbba2016-06-08 17:11:53 -07005870 endMargin = R.dimen.notification_content_plus_picture_margin_end;
Selim Cinekfdc738f2016-01-27 20:04:27 -08005871 }
Adrian Roos2d5dbba2016-06-08 17:11:53 -07005872 view.setViewLayoutMarginEndDimen(R.id.notification_main_column, endMargin);
Dan Sandler842dd772014-05-15 09:36:47 -04005873 return view;
5874 }
5875
5876 private RemoteViews makeMediaBigContentView() {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02005877 final int actionCount = Math.min(mBuilder.mActions.size(), MAX_MEDIA_BUTTONS);
Selim Cinekcc10bfb2016-02-10 16:24:21 -08005878 // Dont add an expanded view if there is no more content to be revealed
5879 int actionsInCompact = mActionsToShowInCompact == null
5880 ? 0
5881 : Math.min(mActionsToShowInCompact.length, MAX_MEDIA_BUTTONS_IN_COMPACT);
Selim Cinek279fa862016-06-14 10:57:25 -07005882 if (!mBuilder.mN.hasLargeIcon() && actionCount <= actionsInCompact) {
Selim Cinekcc10bfb2016-02-10 16:24:21 -08005883 return null;
5884 }
Selim Cinek5bf069a2015-11-10 19:14:27 -05005885 RemoteViews big = mBuilder.applyStandardTemplate(
5886 R.layout.notification_template_material_big_media,
5887 false);
Dan Sandler842dd772014-05-15 09:36:47 -04005888
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02005889 if (actionCount > 0) {
5890 big.removeAllViews(com.android.internal.R.id.media_actions);
5891 for (int i = 0; i < actionCount; i++) {
Selim Cinek5bf069a2015-11-10 19:14:27 -05005892 final RemoteViews button = generateMediaActionButton(mBuilder.mActions.get(i),
Adrian Roos4ff3b122016-02-01 12:26:13 -08005893 mBuilder.resolveContrastColor());
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02005894 big.addView(com.android.internal.R.id.media_actions, button);
Dan Sandler842dd772014-05-15 09:36:47 -04005895 }
5896 }
Selim Cinek5bf069a2015-11-10 19:14:27 -05005897 handleImage(big);
Dan Sandler842dd772014-05-15 09:36:47 -04005898 return big;
5899 }
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02005900
Selim Cinek5bf069a2015-11-10 19:14:27 -05005901 private void handleImage(RemoteViews contentView) {
Selim Cinek279fa862016-06-14 10:57:25 -07005902 if (mBuilder.mN.hasLargeIcon()) {
Adrian Roos2d5dbba2016-06-08 17:11:53 -07005903 contentView.setViewLayoutMarginEndDimen(R.id.line1, 0);
5904 contentView.setViewLayoutMarginEndDimen(R.id.text, 0);
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02005905 }
5906 }
5907
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02005908 /**
5909 * @hide
5910 */
5911 @Override
5912 protected boolean hasProgress() {
5913 return false;
5914 }
Dan Sandler842dd772014-05-15 09:36:47 -04005915 }
Griff Hazen61a9e862014-05-22 16:05:19 -07005916
Selim Cinek593610c2016-02-16 18:42:57 -08005917 /**
5918 * Notification style for custom views that are decorated by the system
5919 *
5920 * <p>Instead of providing a notification that is completely custom, a developer can set this
5921 * style and still obtain system decorations like the notification header with the expand
5922 * affordance and actions.
5923 *
5924 * <p>Use {@link android.app.Notification.Builder#setCustomContentView(RemoteViews)},
5925 * {@link android.app.Notification.Builder#setCustomBigContentView(RemoteViews)} and
5926 * {@link android.app.Notification.Builder#setCustomHeadsUpContentView(RemoteViews)} to set the
5927 * corresponding custom views to display.
5928 *
5929 * To use this style with your Notification, feed it to
5930 * {@link Notification.Builder#setStyle(android.app.Notification.Style)} like so:
5931 * <pre class="prettyprint">
5932 * Notification noti = new Notification.Builder()
5933 * .setSmallIcon(R.drawable.ic_stat_player)
5934 * .setLargeIcon(albumArtBitmap))
5935 * .setCustomContentView(contentView);
5936 * .setStyle(<b>new Notification.DecoratedCustomViewStyle()</b>)
5937 * .build();
5938 * </pre>
5939 */
5940 public static class DecoratedCustomViewStyle extends Style {
5941
5942 public DecoratedCustomViewStyle() {
5943 }
5944
Selim Cinek593610c2016-02-16 18:42:57 -08005945 /**
5946 * @hide
5947 */
5948 public boolean displayCustomViewInline() {
5949 return true;
5950 }
5951
5952 /**
5953 * @hide
5954 */
5955 @Override
5956 public RemoteViews makeContentView() {
5957 return makeStandardTemplateWithCustomContent(mBuilder.mN.contentView);
5958 }
5959
5960 /**
5961 * @hide
5962 */
5963 @Override
5964 public RemoteViews makeBigContentView() {
5965 return makeDecoratedBigContentView();
5966 }
5967
5968 /**
5969 * @hide
5970 */
5971 @Override
5972 public RemoteViews makeHeadsUpContentView() {
5973 return makeDecoratedHeadsUpContentView();
5974 }
5975
Selim Cinek593610c2016-02-16 18:42:57 -08005976 private RemoteViews makeDecoratedHeadsUpContentView() {
5977 RemoteViews headsUpContentView = mBuilder.mN.headsUpContentView == null
5978 ? mBuilder.mN.contentView
5979 : mBuilder.mN.headsUpContentView;
5980 if (mBuilder.mActions.size() == 0) {
5981 return makeStandardTemplateWithCustomContent(headsUpContentView);
5982 }
5983 RemoteViews remoteViews = mBuilder.applyStandardTemplateWithActions(
5984 mBuilder.getBigBaseLayoutResource());
Selim Cinek247fa012016-02-18 09:50:48 -08005985 buildIntoRemoteViewContent(remoteViews, headsUpContentView);
Selim Cinek593610c2016-02-16 18:42:57 -08005986 return remoteViews;
5987 }
5988
Selim Cinek593610c2016-02-16 18:42:57 -08005989 private RemoteViews makeStandardTemplateWithCustomContent(RemoteViews customContent) {
5990 RemoteViews remoteViews = mBuilder.applyStandardTemplate(
5991 mBuilder.getBaseLayoutResource());
Selim Cinek247fa012016-02-18 09:50:48 -08005992 buildIntoRemoteViewContent(remoteViews, customContent);
Selim Cinek593610c2016-02-16 18:42:57 -08005993 return remoteViews;
5994 }
5995
Selim Cinek593610c2016-02-16 18:42:57 -08005996 private RemoteViews makeDecoratedBigContentView() {
5997 RemoteViews bigContentView = mBuilder.mN.bigContentView == null
5998 ? mBuilder.mN.contentView
5999 : mBuilder.mN.bigContentView;
6000 if (mBuilder.mActions.size() == 0) {
6001 return makeStandardTemplateWithCustomContent(bigContentView);
6002 }
6003 RemoteViews remoteViews = mBuilder.applyStandardTemplateWithActions(
6004 mBuilder.getBigBaseLayoutResource());
Selim Cinek247fa012016-02-18 09:50:48 -08006005 buildIntoRemoteViewContent(remoteViews, bigContentView);
Selim Cinek593610c2016-02-16 18:42:57 -08006006 return remoteViews;
6007 }
Selim Cinek247fa012016-02-18 09:50:48 -08006008
6009 private void buildIntoRemoteViewContent(RemoteViews remoteViews,
6010 RemoteViews customContent) {
Adrian Roos5081c0d2016-02-26 16:04:19 -08006011 if (customContent != null) {
Selim Cinekf91017e2016-03-14 12:25:09 -07006012 // Need to clone customContent before adding, because otherwise it can no longer be
6013 // parceled independently of remoteViews.
Adrian Roos5081c0d2016-02-26 16:04:19 -08006014 customContent = customContent.clone();
Selim Cinekf91017e2016-03-14 12:25:09 -07006015 remoteViews.removeAllViews(R.id.notification_main_column);
6016 remoteViews.addView(R.id.notification_main_column, customContent);
Adrian Roos5081c0d2016-02-26 16:04:19 -08006017 }
Selim Cinek247fa012016-02-18 09:50:48 -08006018 // also update the end margin if there is an image
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006019 int endMargin = R.dimen.notification_content_margin_end;
Selim Cinek279fa862016-06-14 10:57:25 -07006020 if (mBuilder.mN.hasLargeIcon()) {
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006021 endMargin = R.dimen.notification_content_plus_picture_margin_end;
Selim Cinek247fa012016-02-18 09:50:48 -08006022 }
Adrian Roos2d5dbba2016-06-08 17:11:53 -07006023 remoteViews.setViewLayoutMarginEndDimen(R.id.notification_main_column, endMargin);
Selim Cinek247fa012016-02-18 09:50:48 -08006024 }
Selim Cinek593610c2016-02-16 18:42:57 -08006025 }
6026
Selim Cinek03eb3b72016-02-18 10:39:45 -08006027 /**
6028 * Notification style for media custom views that are decorated by the system
6029 *
6030 * <p>Instead of providing a media notification that is completely custom, a developer can set
6031 * this style and still obtain system decorations like the notification header with the expand
6032 * affordance and actions.
6033 *
6034 * <p>Use {@link android.app.Notification.Builder#setCustomContentView(RemoteViews)},
6035 * {@link android.app.Notification.Builder#setCustomBigContentView(RemoteViews)} and
6036 * {@link android.app.Notification.Builder#setCustomHeadsUpContentView(RemoteViews)} to set the
6037 * corresponding custom views to display.
6038 *
6039 * To use this style with your Notification, feed it to
6040 * {@link Notification.Builder#setStyle(android.app.Notification.Style)} like so:
6041 * <pre class="prettyprint">
6042 * Notification noti = new Notification.Builder()
6043 * .setSmallIcon(R.drawable.ic_stat_player)
6044 * .setLargeIcon(albumArtBitmap))
6045 * .setCustomContentView(contentView);
6046 * .setStyle(<b>new Notification.DecoratedMediaCustomViewStyle()</b>
6047 * .setMediaSession(mySession))
6048 * .build();
6049 * </pre>
6050 *
6051 * @see android.app.Notification.DecoratedCustomViewStyle
6052 * @see android.app.Notification.MediaStyle
6053 */
6054 public static class DecoratedMediaCustomViewStyle extends MediaStyle {
6055
6056 public DecoratedMediaCustomViewStyle() {
6057 }
6058
Selim Cinek03eb3b72016-02-18 10:39:45 -08006059 /**
6060 * @hide
6061 */
6062 public boolean displayCustomViewInline() {
6063 return true;
6064 }
6065
6066 /**
6067 * @hide
6068 */
6069 @Override
6070 public RemoteViews makeContentView() {
6071 RemoteViews remoteViews = super.makeContentView();
6072 return buildIntoRemoteView(remoteViews, R.id.notification_content_container,
6073 mBuilder.mN.contentView);
6074 }
6075
6076 /**
6077 * @hide
6078 */
6079 @Override
6080 public RemoteViews makeBigContentView() {
6081 RemoteViews customRemoteView = mBuilder.mN.bigContentView != null
6082 ? mBuilder.mN.bigContentView
6083 : mBuilder.mN.contentView;
6084 return makeBigContentViewWithCustomContent(customRemoteView);
6085 }
6086
6087 private RemoteViews makeBigContentViewWithCustomContent(RemoteViews customRemoteView) {
6088 RemoteViews remoteViews = super.makeBigContentView();
6089 if (remoteViews != null) {
6090 return buildIntoRemoteView(remoteViews, R.id.notification_main_column,
6091 customRemoteView);
6092 } else if (customRemoteView != mBuilder.mN.contentView){
6093 remoteViews = super.makeContentView();
6094 return buildIntoRemoteView(remoteViews, R.id.notification_content_container,
6095 customRemoteView);
6096 } else {
6097 return null;
6098 }
6099 }
6100
6101 /**
6102 * @hide
6103 */
6104 @Override
6105 public RemoteViews makeHeadsUpContentView() {
6106 RemoteViews customRemoteView = mBuilder.mN.headsUpContentView != null
6107 ? mBuilder.mN.headsUpContentView
6108 : mBuilder.mN.contentView;
6109 return makeBigContentViewWithCustomContent(customRemoteView);
6110 }
6111
6112 private RemoteViews buildIntoRemoteView(RemoteViews remoteViews, int id,
6113 RemoteViews customContent) {
Adrian Roos5081c0d2016-02-26 16:04:19 -08006114 if (customContent != null) {
Selim Cinekf91017e2016-03-14 12:25:09 -07006115 // Need to clone customContent before adding, because otherwise it can no longer be
6116 // parceled independently of remoteViews.
Adrian Roos5081c0d2016-02-26 16:04:19 -08006117 customContent = customContent.clone();
Selim Cinekf91017e2016-03-14 12:25:09 -07006118 remoteViews.removeAllViews(id);
6119 remoteViews.addView(id, customContent);
Adrian Roos5081c0d2016-02-26 16:04:19 -08006120 }
Selim Cinek03eb3b72016-02-18 10:39:45 -08006121 return remoteViews;
6122 }
6123 }
6124
Christoph Studer4600f9b2014-07-22 22:44:43 +02006125 // When adding a new Style subclass here, don't forget to update
6126 // Builder.getNotificationStyleClass.
6127
Griff Hazen61a9e862014-05-22 16:05:19 -07006128 /**
6129 * Extender interface for use with {@link Builder#extend}. Extenders may be used to add
6130 * metadata or change options on a notification builder.
6131 */
6132 public interface Extender {
6133 /**
6134 * Apply this extender to a notification builder.
6135 * @param builder the builder to be modified.
6136 * @return the build object for chaining.
6137 */
6138 public Builder extend(Builder builder);
6139 }
6140
6141 /**
6142 * Helper class to add wearable extensions to notifications.
6143 * <p class="note"> See
6144 * <a href="{@docRoot}wear/notifications/creating.html">Creating Notifications
6145 * for Android Wear</a> for more information on how to use this class.
6146 * <p>
6147 * To create a notification with wearable extensions:
6148 * <ol>
6149 * <li>Create a {@link android.app.Notification.Builder}, setting any desired
6150 * properties.
6151 * <li>Create a {@link android.app.Notification.WearableExtender}.
6152 * <li>Set wearable-specific properties using the
6153 * {@code add} and {@code set} methods of {@link android.app.Notification.WearableExtender}.
6154 * <li>Call {@link android.app.Notification.Builder#extend} to apply the extensions to a
6155 * notification.
6156 * <li>Post the notification to the notification system with the
6157 * {@code NotificationManager.notify(...)} methods.
6158 * </ol>
6159 *
6160 * <pre class="prettyprint">
6161 * Notification notif = new Notification.Builder(mContext)
6162 * .setContentTitle(&quot;New mail from &quot; + sender.toString())
6163 * .setContentText(subject)
6164 * .setSmallIcon(R.drawable.new_mail)
6165 * .extend(new Notification.WearableExtender()
6166 * .setContentIcon(R.drawable.new_mail))
6167 * .build();
6168 * NotificationManager notificationManger =
6169 * (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
6170 * notificationManger.notify(0, notif);</pre>
6171 *
6172 * <p>Wearable extensions can be accessed on an existing notification by using the
6173 * {@code WearableExtender(Notification)} constructor,
6174 * and then using the {@code get} methods to access values.
6175 *
6176 * <pre class="prettyprint">
6177 * Notification.WearableExtender wearableExtender = new Notification.WearableExtender(
6178 * notification);
Griff Hazen14f57992014-05-26 09:07:14 -07006179 * List&lt;Notification&gt; pages = wearableExtender.getPages();</pre>
Griff Hazen61a9e862014-05-22 16:05:19 -07006180 */
6181 public static final class WearableExtender implements Extender {
6182 /**
6183 * Sentinel value for an action index that is unset.
6184 */
6185 public static final int UNSET_ACTION_INDEX = -1;
6186
6187 /**
6188 * Size value for use with {@link #setCustomSizePreset} to show this notification with
6189 * default sizing.
6190 * <p>For custom display notifications created using {@link #setDisplayIntent},
Paul Soulosaa4f4bf2015-08-04 11:59:45 -07006191 * the default is {@link #SIZE_MEDIUM}. All other notifications size automatically based
Griff Hazen61a9e862014-05-22 16:05:19 -07006192 * on their content.
6193 */
6194 public static final int SIZE_DEFAULT = 0;
6195
6196 /**
6197 * Size value for use with {@link #setCustomSizePreset} to show this notification
6198 * with an extra small size.
6199 * <p>This value is only applicable for custom display notifications created using
6200 * {@link #setDisplayIntent}.
6201 */
6202 public static final int SIZE_XSMALL = 1;
6203
6204 /**
6205 * Size value for use with {@link #setCustomSizePreset} to show this notification
6206 * with a small size.
6207 * <p>This value is only applicable for custom display notifications created using
6208 * {@link #setDisplayIntent}.
6209 */
6210 public static final int SIZE_SMALL = 2;
6211
6212 /**
6213 * Size value for use with {@link #setCustomSizePreset} to show this notification
6214 * with a medium size.
6215 * <p>This value is only applicable for custom display notifications created using
6216 * {@link #setDisplayIntent}.
6217 */
6218 public static final int SIZE_MEDIUM = 3;
6219
6220 /**
6221 * Size value for use with {@link #setCustomSizePreset} to show this notification
6222 * with a large size.
6223 * <p>This value is only applicable for custom display notifications created using
6224 * {@link #setDisplayIntent}.
6225 */
6226 public static final int SIZE_LARGE = 4;
6227
Griff Hazend5f11f92014-05-27 15:40:09 -07006228 /**
6229 * Size value for use with {@link #setCustomSizePreset} to show this notification
6230 * full screen.
6231 * <p>This value is only applicable for custom display notifications created using
6232 * {@link #setDisplayIntent}.
6233 */
6234 public static final int SIZE_FULL_SCREEN = 5;
6235
Griff Hazen5f2edfc2014-09-29 16:28:44 -07006236 /**
6237 * Sentinel value for use with {@link #setHintScreenTimeout} to keep the screen on for a
6238 * short amount of time when this notification is displayed on the screen. This
6239 * is the default value.
6240 */
6241 public static final int SCREEN_TIMEOUT_SHORT = 0;
6242
6243 /**
6244 * Sentinel value for use with {@link #setHintScreenTimeout} to keep the screen on
6245 * for a longer amount of time when this notification is displayed on the screen.
6246 */
6247 public static final int SCREEN_TIMEOUT_LONG = -1;
6248
Griff Hazen61a9e862014-05-22 16:05:19 -07006249 /** Notification extra which contains wearable extensions */
6250 private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS";
6251
Pete Gastaf6781d2014-10-07 15:17:05 -04006252 // Keys within EXTRA_WEARABLE_EXTENSIONS for wearable options.
Griff Hazen61a9e862014-05-22 16:05:19 -07006253 private static final String KEY_ACTIONS = "actions";
6254 private static final String KEY_FLAGS = "flags";
6255 private static final String KEY_DISPLAY_INTENT = "displayIntent";
6256 private static final String KEY_PAGES = "pages";
6257 private static final String KEY_BACKGROUND = "background";
6258 private static final String KEY_CONTENT_ICON = "contentIcon";
6259 private static final String KEY_CONTENT_ICON_GRAVITY = "contentIconGravity";
6260 private static final String KEY_CONTENT_ACTION_INDEX = "contentActionIndex";
6261 private static final String KEY_CUSTOM_SIZE_PRESET = "customSizePreset";
6262 private static final String KEY_CUSTOM_CONTENT_HEIGHT = "customContentHeight";
6263 private static final String KEY_GRAVITY = "gravity";
Griff Hazen5f2edfc2014-09-29 16:28:44 -07006264 private static final String KEY_HINT_SCREEN_TIMEOUT = "hintScreenTimeout";
Nadia Benbernou948627e2016-04-14 14:41:08 -04006265 private static final String KEY_DISMISSAL_ID = "dismissalId";
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04006266 private static final String KEY_BRIDGE_TAG = "bridgeTag";
Griff Hazen61a9e862014-05-22 16:05:19 -07006267
6268 // Flags bitwise-ored to mFlags
6269 private static final int FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE = 0x1;
6270 private static final int FLAG_HINT_HIDE_ICON = 1 << 1;
6271 private static final int FLAG_HINT_SHOW_BACKGROUND_ONLY = 1 << 2;
6272 private static final int FLAG_START_SCROLL_BOTTOM = 1 << 3;
Griff Hazen5f2edfc2014-09-29 16:28:44 -07006273 private static final int FLAG_HINT_AVOID_BACKGROUND_CLIPPING = 1 << 4;
Alex Hills4bcb06b2016-04-05 14:26:25 -04006274 private static final int FLAG_BIG_PICTURE_AMBIENT = 1 << 5;
Alex Hills9ab3a232016-04-05 14:54:56 -04006275 private static final int FLAG_HINT_CONTENT_INTENT_LAUNCHES_ACTIVITY = 1 << 6;
Griff Hazen61a9e862014-05-22 16:05:19 -07006276
6277 // Default value for flags integer
6278 private static final int DEFAULT_FLAGS = FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE;
6279
6280 private static final int DEFAULT_CONTENT_ICON_GRAVITY = Gravity.END;
6281 private static final int DEFAULT_GRAVITY = Gravity.BOTTOM;
6282
6283 private ArrayList<Action> mActions = new ArrayList<Action>();
6284 private int mFlags = DEFAULT_FLAGS;
6285 private PendingIntent mDisplayIntent;
6286 private ArrayList<Notification> mPages = new ArrayList<Notification>();
6287 private Bitmap mBackground;
6288 private int mContentIcon;
6289 private int mContentIconGravity = DEFAULT_CONTENT_ICON_GRAVITY;
6290 private int mContentActionIndex = UNSET_ACTION_INDEX;
6291 private int mCustomSizePreset = SIZE_DEFAULT;
6292 private int mCustomContentHeight;
6293 private int mGravity = DEFAULT_GRAVITY;
Griff Hazen5f2edfc2014-09-29 16:28:44 -07006294 private int mHintScreenTimeout;
Nadia Benbernou948627e2016-04-14 14:41:08 -04006295 private String mDismissalId;
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04006296 private String mBridgeTag;
Griff Hazen61a9e862014-05-22 16:05:19 -07006297
6298 /**
6299 * Create a {@link android.app.Notification.WearableExtender} with default
6300 * options.
6301 */
6302 public WearableExtender() {
6303 }
6304
6305 public WearableExtender(Notification notif) {
6306 Bundle wearableBundle = notif.extras.getBundle(EXTRA_WEARABLE_EXTENSIONS);
6307 if (wearableBundle != null) {
6308 List<Action> actions = wearableBundle.getParcelableArrayList(KEY_ACTIONS);
6309 if (actions != null) {
6310 mActions.addAll(actions);
6311 }
6312
6313 mFlags = wearableBundle.getInt(KEY_FLAGS, DEFAULT_FLAGS);
6314 mDisplayIntent = wearableBundle.getParcelable(KEY_DISPLAY_INTENT);
6315
6316 Notification[] pages = getNotificationArrayFromBundle(
6317 wearableBundle, KEY_PAGES);
6318 if (pages != null) {
6319 Collections.addAll(mPages, pages);
6320 }
6321
6322 mBackground = wearableBundle.getParcelable(KEY_BACKGROUND);
6323 mContentIcon = wearableBundle.getInt(KEY_CONTENT_ICON);
6324 mContentIconGravity = wearableBundle.getInt(KEY_CONTENT_ICON_GRAVITY,
6325 DEFAULT_CONTENT_ICON_GRAVITY);
6326 mContentActionIndex = wearableBundle.getInt(KEY_CONTENT_ACTION_INDEX,
6327 UNSET_ACTION_INDEX);
6328 mCustomSizePreset = wearableBundle.getInt(KEY_CUSTOM_SIZE_PRESET,
6329 SIZE_DEFAULT);
6330 mCustomContentHeight = wearableBundle.getInt(KEY_CUSTOM_CONTENT_HEIGHT);
6331 mGravity = wearableBundle.getInt(KEY_GRAVITY, DEFAULT_GRAVITY);
Griff Hazen5f2edfc2014-09-29 16:28:44 -07006332 mHintScreenTimeout = wearableBundle.getInt(KEY_HINT_SCREEN_TIMEOUT);
Nadia Benbernou948627e2016-04-14 14:41:08 -04006333 mDismissalId = wearableBundle.getString(KEY_DISMISSAL_ID);
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04006334 mBridgeTag = wearableBundle.getString(KEY_BRIDGE_TAG);
Griff Hazen61a9e862014-05-22 16:05:19 -07006335 }
6336 }
6337
6338 /**
6339 * Apply wearable extensions to a notification that is being built. This is typically
6340 * called by the {@link android.app.Notification.Builder#extend} method of
6341 * {@link android.app.Notification.Builder}.
6342 */
6343 @Override
6344 public Notification.Builder extend(Notification.Builder builder) {
6345 Bundle wearableBundle = new Bundle();
6346
6347 if (!mActions.isEmpty()) {
6348 wearableBundle.putParcelableArrayList(KEY_ACTIONS, mActions);
6349 }
6350 if (mFlags != DEFAULT_FLAGS) {
6351 wearableBundle.putInt(KEY_FLAGS, mFlags);
6352 }
6353 if (mDisplayIntent != null) {
6354 wearableBundle.putParcelable(KEY_DISPLAY_INTENT, mDisplayIntent);
6355 }
6356 if (!mPages.isEmpty()) {
6357 wearableBundle.putParcelableArray(KEY_PAGES, mPages.toArray(
6358 new Notification[mPages.size()]));
6359 }
6360 if (mBackground != null) {
6361 wearableBundle.putParcelable(KEY_BACKGROUND, mBackground);
6362 }
6363 if (mContentIcon != 0) {
6364 wearableBundle.putInt(KEY_CONTENT_ICON, mContentIcon);
6365 }
6366 if (mContentIconGravity != DEFAULT_CONTENT_ICON_GRAVITY) {
6367 wearableBundle.putInt(KEY_CONTENT_ICON_GRAVITY, mContentIconGravity);
6368 }
6369 if (mContentActionIndex != UNSET_ACTION_INDEX) {
6370 wearableBundle.putInt(KEY_CONTENT_ACTION_INDEX,
6371 mContentActionIndex);
6372 }
6373 if (mCustomSizePreset != SIZE_DEFAULT) {
6374 wearableBundle.putInt(KEY_CUSTOM_SIZE_PRESET, mCustomSizePreset);
6375 }
6376 if (mCustomContentHeight != 0) {
6377 wearableBundle.putInt(KEY_CUSTOM_CONTENT_HEIGHT, mCustomContentHeight);
6378 }
6379 if (mGravity != DEFAULT_GRAVITY) {
6380 wearableBundle.putInt(KEY_GRAVITY, mGravity);
6381 }
Griff Hazen5f2edfc2014-09-29 16:28:44 -07006382 if (mHintScreenTimeout != 0) {
6383 wearableBundle.putInt(KEY_HINT_SCREEN_TIMEOUT, mHintScreenTimeout);
6384 }
Nadia Benbernou948627e2016-04-14 14:41:08 -04006385 if (mDismissalId != null) {
6386 wearableBundle.putString(KEY_DISMISSAL_ID, mDismissalId);
6387 }
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04006388 if (mBridgeTag != null) {
6389 wearableBundle.putString(KEY_BRIDGE_TAG, mBridgeTag);
6390 }
Griff Hazen61a9e862014-05-22 16:05:19 -07006391
6392 builder.getExtras().putBundle(EXTRA_WEARABLE_EXTENSIONS, wearableBundle);
6393 return builder;
6394 }
6395
6396 @Override
6397 public WearableExtender clone() {
6398 WearableExtender that = new WearableExtender();
6399 that.mActions = new ArrayList<Action>(this.mActions);
6400 that.mFlags = this.mFlags;
6401 that.mDisplayIntent = this.mDisplayIntent;
6402 that.mPages = new ArrayList<Notification>(this.mPages);
6403 that.mBackground = this.mBackground;
6404 that.mContentIcon = this.mContentIcon;
6405 that.mContentIconGravity = this.mContentIconGravity;
6406 that.mContentActionIndex = this.mContentActionIndex;
6407 that.mCustomSizePreset = this.mCustomSizePreset;
6408 that.mCustomContentHeight = this.mCustomContentHeight;
6409 that.mGravity = this.mGravity;
Griff Hazen5f2edfc2014-09-29 16:28:44 -07006410 that.mHintScreenTimeout = this.mHintScreenTimeout;
Nadia Benbernou948627e2016-04-14 14:41:08 -04006411 that.mDismissalId = this.mDismissalId;
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04006412 that.mBridgeTag = this.mBridgeTag;
Griff Hazen61a9e862014-05-22 16:05:19 -07006413 return that;
6414 }
6415
6416 /**
6417 * Add a wearable action to this notification.
6418 *
6419 * <p>When wearable actions are added using this method, the set of actions that
6420 * show on a wearable device splits from devices that only show actions added
6421 * using {@link android.app.Notification.Builder#addAction}. This allows for customization
6422 * of which actions display on different devices.
6423 *
6424 * @param action the action to add to this notification
6425 * @return this object for method chaining
6426 * @see android.app.Notification.Action
6427 */
6428 public WearableExtender addAction(Action action) {
6429 mActions.add(action);
6430 return this;
6431 }
6432
6433 /**
6434 * Adds wearable actions to this notification.
6435 *
6436 * <p>When wearable actions are added using this method, the set of actions that
6437 * show on a wearable device splits from devices that only show actions added
6438 * using {@link android.app.Notification.Builder#addAction}. This allows for customization
6439 * of which actions display on different devices.
6440 *
6441 * @param actions the actions to add to this notification
6442 * @return this object for method chaining
6443 * @see android.app.Notification.Action
6444 */
6445 public WearableExtender addActions(List<Action> actions) {
6446 mActions.addAll(actions);
6447 return this;
6448 }
6449
6450 /**
6451 * Clear all wearable actions present on this builder.
6452 * @return this object for method chaining.
6453 * @see #addAction
6454 */
6455 public WearableExtender clearActions() {
6456 mActions.clear();
6457 return this;
6458 }
6459
6460 /**
6461 * Get the wearable actions present on this notification.
6462 */
6463 public List<Action> getActions() {
6464 return mActions;
6465 }
6466
6467 /**
6468 * Set an intent to launch inside of an activity view when displaying
Griff Hazen14f57992014-05-26 09:07:14 -07006469 * this notification. The {@link PendingIntent} provided should be for an activity.
6470 *
6471 * <pre class="prettyprint">
6472 * Intent displayIntent = new Intent(context, MyDisplayActivity.class);
6473 * PendingIntent displayPendingIntent = PendingIntent.getActivity(context,
6474 * 0, displayIntent, PendingIntent.FLAG_UPDATE_CURRENT);
6475 * Notification notif = new Notification.Builder(context)
6476 * .extend(new Notification.WearableExtender()
6477 * .setDisplayIntent(displayPendingIntent)
6478 * .setCustomSizePreset(Notification.WearableExtender.SIZE_MEDIUM))
6479 * .build();</pre>
6480 *
6481 * <p>The activity to launch needs to allow embedding, must be exported, and
Griff Hazen831ca9d2014-06-17 00:38:38 -07006482 * should have an empty task affinity. It is also recommended to use the device
6483 * default light theme.
Griff Hazen14f57992014-05-26 09:07:14 -07006484 *
6485 * <p>Example AndroidManifest.xml entry:
6486 * <pre class="prettyprint">
6487 * &lt;activity android:name=&quot;com.example.MyDisplayActivity&quot;
6488 * android:exported=&quot;true&quot;
6489 * android:allowEmbedded=&quot;true&quot;
Griff Hazen831ca9d2014-06-17 00:38:38 -07006490 * android:taskAffinity=&quot;&quot;
6491 * android:theme=&quot;@android:style/Theme.DeviceDefault.Light&quot; /&gt;</pre>
Griff Hazen61a9e862014-05-22 16:05:19 -07006492 *
6493 * @param intent the {@link PendingIntent} for an activity
6494 * @return this object for method chaining
6495 * @see android.app.Notification.WearableExtender#getDisplayIntent
6496 */
6497 public WearableExtender setDisplayIntent(PendingIntent intent) {
6498 mDisplayIntent = intent;
6499 return this;
6500 }
6501
6502 /**
6503 * Get the intent to launch inside of an activity view when displaying this
6504 * notification. This {@code PendingIntent} should be for an activity.
6505 */
6506 public PendingIntent getDisplayIntent() {
6507 return mDisplayIntent;
6508 }
6509
6510 /**
6511 * Add an additional page of content to display with this notification. The current
6512 * notification forms the first page, and pages added using this function form
6513 * subsequent pages. This field can be used to separate a notification into multiple
6514 * sections.
6515 *
6516 * @param page the notification to add as another page
6517 * @return this object for method chaining
6518 * @see android.app.Notification.WearableExtender#getPages
6519 */
6520 public WearableExtender addPage(Notification page) {
6521 mPages.add(page);
6522 return this;
6523 }
6524
6525 /**
6526 * Add additional pages of content to display with this notification. The current
6527 * notification forms the first page, and pages added using this function form
6528 * subsequent pages. This field can be used to separate a notification into multiple
6529 * sections.
6530 *
6531 * @param pages a list of notifications
6532 * @return this object for method chaining
6533 * @see android.app.Notification.WearableExtender#getPages
6534 */
6535 public WearableExtender addPages(List<Notification> pages) {
6536 mPages.addAll(pages);
6537 return this;
6538 }
6539
6540 /**
6541 * Clear all additional pages present on this builder.
6542 * @return this object for method chaining.
6543 * @see #addPage
6544 */
6545 public WearableExtender clearPages() {
6546 mPages.clear();
6547 return this;
6548 }
6549
6550 /**
6551 * Get the array of additional pages of content for displaying this notification. The
6552 * current notification forms the first page, and elements within this array form
6553 * subsequent pages. This field can be used to separate a notification into multiple
6554 * sections.
6555 * @return the pages for this notification
6556 */
6557 public List<Notification> getPages() {
6558 return mPages;
6559 }
6560
6561 /**
6562 * Set a background image to be displayed behind the notification content.
6563 * Contrary to the {@link android.app.Notification.BigPictureStyle}, this background
6564 * will work with any notification style.
6565 *
6566 * @param background the background bitmap
6567 * @return this object for method chaining
6568 * @see android.app.Notification.WearableExtender#getBackground
6569 */
6570 public WearableExtender setBackground(Bitmap background) {
6571 mBackground = background;
6572 return this;
6573 }
6574
6575 /**
6576 * Get a background image to be displayed behind the notification content.
6577 * Contrary to the {@link android.app.Notification.BigPictureStyle}, this background
6578 * will work with any notification style.
6579 *
6580 * @return the background image
6581 * @see android.app.Notification.WearableExtender#setBackground
6582 */
6583 public Bitmap getBackground() {
6584 return mBackground;
6585 }
6586
6587 /**
6588 * Set an icon that goes with the content of this notification.
6589 */
6590 public WearableExtender setContentIcon(int icon) {
6591 mContentIcon = icon;
6592 return this;
6593 }
6594
6595 /**
6596 * Get an icon that goes with the content of this notification.
6597 */
6598 public int getContentIcon() {
6599 return mContentIcon;
6600 }
6601
6602 /**
6603 * Set the gravity that the content icon should have within the notification display.
6604 * Supported values include {@link android.view.Gravity#START} and
6605 * {@link android.view.Gravity#END}. The default value is {@link android.view.Gravity#END}.
6606 * @see #setContentIcon
6607 */
6608 public WearableExtender setContentIconGravity(int contentIconGravity) {
6609 mContentIconGravity = contentIconGravity;
6610 return this;
6611 }
6612
6613 /**
6614 * Get the gravity that the content icon should have within the notification display.
6615 * Supported values include {@link android.view.Gravity#START} and
6616 * {@link android.view.Gravity#END}. The default value is {@link android.view.Gravity#END}.
6617 * @see #getContentIcon
6618 */
6619 public int getContentIconGravity() {
6620 return mContentIconGravity;
6621 }
6622
6623 /**
6624 * Set an action from this notification's actions to be clickable with the content of
Griff Hazen14f57992014-05-26 09:07:14 -07006625 * this notification. This action will no longer display separately from the
6626 * notification's content.
6627 *
Griff Hazenca48d352014-05-28 22:37:13 -07006628 * <p>For notifications with multiple pages, child pages can also have content actions
Griff Hazen14f57992014-05-26 09:07:14 -07006629 * set, although the list of available actions comes from the main notification and not
6630 * from the child page's notification.
6631 *
6632 * @param actionIndex The index of the action to hoist onto the current notification page.
6633 * If wearable actions were added to the main notification, this index
6634 * will apply to that list, otherwise it will apply to the regular
6635 * actions list.
Griff Hazen61a9e862014-05-22 16:05:19 -07006636 */
6637 public WearableExtender setContentAction(int actionIndex) {
6638 mContentActionIndex = actionIndex;
6639 return this;
6640 }
6641
6642 /**
Griff Hazenca48d352014-05-28 22:37:13 -07006643 * Get the index of the notification action, if any, that was specified as being clickable
6644 * with the content of this notification. This action will no longer display separately
Griff Hazen14f57992014-05-26 09:07:14 -07006645 * from the notification's content.
Griff Hazen61a9e862014-05-22 16:05:19 -07006646 *
Griff Hazenca48d352014-05-28 22:37:13 -07006647 * <p>For notifications with multiple pages, child pages can also have content actions
Griff Hazen14f57992014-05-26 09:07:14 -07006648 * set, although the list of available actions comes from the main notification and not
6649 * from the child page's notification.
6650 *
6651 * <p>If wearable specific actions were added to the main notification, this index will
6652 * apply to that list, otherwise it will apply to the regular actions list.
Griff Hazenca48d352014-05-28 22:37:13 -07006653 *
6654 * @return the action index or {@link #UNSET_ACTION_INDEX} if no action was selected.
Griff Hazen61a9e862014-05-22 16:05:19 -07006655 */
6656 public int getContentAction() {
6657 return mContentActionIndex;
6658 }
6659
6660 /**
6661 * Set the gravity that this notification should have within the available viewport space.
6662 * Supported values include {@link android.view.Gravity#TOP},
6663 * {@link android.view.Gravity#CENTER_VERTICAL} and {@link android.view.Gravity#BOTTOM}.
6664 * The default value is {@link android.view.Gravity#BOTTOM}.
6665 */
6666 public WearableExtender setGravity(int gravity) {
6667 mGravity = gravity;
6668 return this;
6669 }
6670
6671 /**
6672 * Get the gravity that this notification should have within the available viewport space.
6673 * Supported values include {@link android.view.Gravity#TOP},
6674 * {@link android.view.Gravity#CENTER_VERTICAL} and {@link android.view.Gravity#BOTTOM}.
6675 * The default value is {@link android.view.Gravity#BOTTOM}.
6676 */
6677 public int getGravity() {
6678 return mGravity;
6679 }
6680
6681 /**
6682 * Set the custom size preset for the display of this notification out of the available
6683 * presets found in {@link android.app.Notification.WearableExtender}, e.g.
6684 * {@link #SIZE_LARGE}.
6685 * <p>Some custom size presets are only applicable for custom display notifications created
6686 * using {@link android.app.Notification.WearableExtender#setDisplayIntent}. Check the
6687 * documentation for the preset in question. See also
6688 * {@link #setCustomContentHeight} and {@link #getCustomSizePreset}.
6689 */
6690 public WearableExtender setCustomSizePreset(int sizePreset) {
6691 mCustomSizePreset = sizePreset;
6692 return this;
6693 }
6694
6695 /**
6696 * Get the custom size preset for the display of this notification out of the available
6697 * presets found in {@link android.app.Notification.WearableExtender}, e.g.
6698 * {@link #SIZE_LARGE}.
6699 * <p>Some custom size presets are only applicable for custom display notifications created
6700 * using {@link #setDisplayIntent}. Check the documentation for the preset in question.
6701 * See also {@link #setCustomContentHeight} and {@link #setCustomSizePreset}.
6702 */
6703 public int getCustomSizePreset() {
6704 return mCustomSizePreset;
6705 }
6706
6707 /**
6708 * Set the custom height in pixels for the display of this notification's content.
6709 * <p>This option is only available for custom display notifications created
6710 * using {@link android.app.Notification.WearableExtender#setDisplayIntent}. See also
6711 * {@link android.app.Notification.WearableExtender#setCustomSizePreset} and
6712 * {@link #getCustomContentHeight}.
6713 */
6714 public WearableExtender setCustomContentHeight(int height) {
6715 mCustomContentHeight = height;
6716 return this;
6717 }
6718
6719 /**
6720 * Get the custom height in pixels for the display of this notification's content.
6721 * <p>This option is only available for custom display notifications created
6722 * using {@link #setDisplayIntent}. See also {@link #setCustomSizePreset} and
6723 * {@link #setCustomContentHeight}.
6724 */
6725 public int getCustomContentHeight() {
6726 return mCustomContentHeight;
6727 }
6728
6729 /**
6730 * Set whether the scrolling position for the contents of this notification should start
6731 * at the bottom of the contents instead of the top when the contents are too long to
6732 * display within the screen. Default is false (start scroll at the top).
6733 */
6734 public WearableExtender setStartScrollBottom(boolean startScrollBottom) {
6735 setFlag(FLAG_START_SCROLL_BOTTOM, startScrollBottom);
6736 return this;
6737 }
6738
6739 /**
6740 * Get whether the scrolling position for the contents of this notification should start
6741 * at the bottom of the contents instead of the top when the contents are too long to
6742 * display within the screen. Default is false (start scroll at the top).
6743 */
6744 public boolean getStartScrollBottom() {
6745 return (mFlags & FLAG_START_SCROLL_BOTTOM) != 0;
6746 }
6747
6748 /**
6749 * Set whether the content intent is available when the wearable device is not connected
6750 * to a companion device. The user can still trigger this intent when the wearable device
6751 * is offline, but a visual hint will indicate that the content intent may not be available.
6752 * Defaults to true.
6753 */
6754 public WearableExtender setContentIntentAvailableOffline(
6755 boolean contentIntentAvailableOffline) {
6756 setFlag(FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE, contentIntentAvailableOffline);
6757 return this;
6758 }
6759
6760 /**
6761 * Get whether the content intent is available when the wearable device is not connected
6762 * to a companion device. The user can still trigger this intent when the wearable device
6763 * is offline, but a visual hint will indicate that the content intent may not be available.
6764 * Defaults to true.
6765 */
6766 public boolean getContentIntentAvailableOffline() {
6767 return (mFlags & FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE) != 0;
6768 }
6769
6770 /**
6771 * Set a hint that this notification's icon should not be displayed.
6772 * @param hintHideIcon {@code true} to hide the icon, {@code false} otherwise.
6773 * @return this object for method chaining
6774 */
6775 public WearableExtender setHintHideIcon(boolean hintHideIcon) {
6776 setFlag(FLAG_HINT_HIDE_ICON, hintHideIcon);
6777 return this;
6778 }
6779
6780 /**
6781 * Get a hint that this notification's icon should not be displayed.
6782 * @return {@code true} if this icon should not be displayed, false otherwise.
6783 * The default value is {@code false} if this was never set.
6784 */
6785 public boolean getHintHideIcon() {
6786 return (mFlags & FLAG_HINT_HIDE_ICON) != 0;
6787 }
6788
6789 /**
6790 * Set a visual hint that only the background image of this notification should be
6791 * displayed, and other semantic content should be hidden. This hint is only applicable
6792 * to sub-pages added using {@link #addPage}.
6793 */
6794 public WearableExtender setHintShowBackgroundOnly(boolean hintShowBackgroundOnly) {
6795 setFlag(FLAG_HINT_SHOW_BACKGROUND_ONLY, hintShowBackgroundOnly);
6796 return this;
6797 }
6798
6799 /**
6800 * Get a visual hint that only the background image of this notification should be
6801 * displayed, and other semantic content should be hidden. This hint is only applicable
6802 * to sub-pages added using {@link android.app.Notification.WearableExtender#addPage}.
6803 */
6804 public boolean getHintShowBackgroundOnly() {
6805 return (mFlags & FLAG_HINT_SHOW_BACKGROUND_ONLY) != 0;
6806 }
6807
Griff Hazen5f2edfc2014-09-29 16:28:44 -07006808 /**
Griff Hazen9c5be4e2014-11-17 17:47:50 -08006809 * Set a hint that this notification's background should not be clipped if possible,
6810 * and should instead be resized to fully display on the screen, retaining the aspect
6811 * ratio of the image. This can be useful for images like barcodes or qr codes.
Griff Hazen5f2edfc2014-09-29 16:28:44 -07006812 * @param hintAvoidBackgroundClipping {@code true} to avoid clipping if possible.
6813 * @return this object for method chaining
6814 */
6815 public WearableExtender setHintAvoidBackgroundClipping(
6816 boolean hintAvoidBackgroundClipping) {
6817 setFlag(FLAG_HINT_AVOID_BACKGROUND_CLIPPING, hintAvoidBackgroundClipping);
6818 return this;
6819 }
6820
6821 /**
Griff Hazen9c5be4e2014-11-17 17:47:50 -08006822 * Get a hint that this notification's background should not be clipped if possible,
6823 * and should instead be resized to fully display on the screen, retaining the aspect
6824 * ratio of the image. This can be useful for images like barcodes or qr codes.
Griff Hazen5f2edfc2014-09-29 16:28:44 -07006825 * @return {@code true} if it's ok if the background is clipped on the screen, false
6826 * otherwise. The default value is {@code false} if this was never set.
6827 */
6828 public boolean getHintAvoidBackgroundClipping() {
6829 return (mFlags & FLAG_HINT_AVOID_BACKGROUND_CLIPPING) != 0;
6830 }
6831
6832 /**
6833 * Set a hint that the screen should remain on for at least this duration when
6834 * this notification is displayed on the screen.
6835 * @param timeout The requested screen timeout in milliseconds. Can also be either
6836 * {@link #SCREEN_TIMEOUT_SHORT} or {@link #SCREEN_TIMEOUT_LONG}.
6837 * @return this object for method chaining
6838 */
6839 public WearableExtender setHintScreenTimeout(int timeout) {
6840 mHintScreenTimeout = timeout;
6841 return this;
6842 }
6843
6844 /**
6845 * Get the duration, in milliseconds, that the screen should remain on for
6846 * when this notification is displayed.
6847 * @return the duration in milliseconds if > 0, or either one of the sentinel values
6848 * {@link #SCREEN_TIMEOUT_SHORT} or {@link #SCREEN_TIMEOUT_LONG}.
6849 */
6850 public int getHintScreenTimeout() {
6851 return mHintScreenTimeout;
6852 }
6853
Alex Hills9ab3a232016-04-05 14:54:56 -04006854 /**
Alex Hills4bcb06b2016-04-05 14:26:25 -04006855 * Set a hint that this notification's {@link BigPictureStyle} (if present) should be
6856 * converted to low-bit and displayed in ambient mode, especially useful for barcodes and
6857 * qr codes, as well as other simple black-and-white tickets.
6858 * @param hintAmbientBigPicture {@code true} to enable converstion and ambient.
6859 * @return this object for method chaining
6860 */
6861 public WearableExtender setHintAmbientBigPicture(boolean hintAmbientBigPicture) {
6862 setFlag(FLAG_BIG_PICTURE_AMBIENT, hintAmbientBigPicture);
6863 return this;
6864 }
6865
6866 /**
6867 * Get a hint that this notification's {@link BigPictureStyle} (if present) should be
6868 * converted to low-bit and displayed in ambient mode, especially useful for barcodes and
6869 * qr codes, as well as other simple black-and-white tickets.
6870 * @return {@code true} if it should be displayed in ambient, false otherwise
6871 * otherwise. The default value is {@code false} if this was never set.
6872 */
6873 public boolean getHintAmbientBigPicture() {
6874 return (mFlags & FLAG_BIG_PICTURE_AMBIENT) != 0;
6875 }
6876
6877 /**
Alex Hills9ab3a232016-04-05 14:54:56 -04006878 * Set a hint that this notification's content intent will launch an {@link Activity}
6879 * directly, telling the platform that it can generate the appropriate transitions.
6880 * @param hintContentIntentLaunchesActivity {@code true} if the content intent will launch
6881 * an activity and transitions should be generated, false otherwise.
6882 * @return this object for method chaining
6883 */
6884 public WearableExtender setHintContentIntentLaunchesActivity(
6885 boolean hintContentIntentLaunchesActivity) {
6886 setFlag(FLAG_HINT_CONTENT_INTENT_LAUNCHES_ACTIVITY, hintContentIntentLaunchesActivity);
6887 return this;
6888 }
6889
6890 /**
6891 * Get a hint that this notification's content intent will launch an {@link Activity}
6892 * directly, telling the platform that it can generate the appropriate transitions
6893 * @return {@code true} if the content intent will launch an activity and transitions should
6894 * be generated, false otherwise. The default value is {@code false} if this was never set.
6895 */
6896 public boolean getHintContentIntentLaunchesActivity() {
6897 return (mFlags & FLAG_HINT_CONTENT_INTENT_LAUNCHES_ACTIVITY) != 0;
6898 }
6899
Nadia Benbernou948627e2016-04-14 14:41:08 -04006900 /**
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04006901 * Sets the dismissal id for this notification. If a notification is posted with a
6902 * dismissal id, then when that notification is canceled, notifications on other wearables
6903 * and the paired Android phone having that same dismissal id will also be canceled. See
Nadia Benbernou948627e2016-04-14 14:41:08 -04006904 * <a href="{@docRoot}wear/notifications/index.html">Adding Wearable Features to
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04006905 * Notifications</a> for more information.
Nadia Benbernou948627e2016-04-14 14:41:08 -04006906 * @param dismissalId the dismissal id of the notification.
6907 * @return this object for method chaining
6908 */
6909 public WearableExtender setDismissalId(String dismissalId) {
6910 mDismissalId = dismissalId;
6911 return this;
6912 }
6913
6914 /**
6915 * Returns the dismissal id of the notification.
6916 * @return the dismissal id of the notification or null if it has not been set.
6917 */
6918 public String getDismissalId() {
6919 return mDismissalId;
6920 }
6921
Ariel Gertzenstein95ab5222016-09-02 17:00:16 -04006922 /**
6923 * Sets a bridge tag for this notification. A bridge tag can be set for notifications
6924 * posted from a phone to provide finer-grained control on what notifications are bridged
6925 * to wearables. See <a href="{@docRoot}wear/notifications/index.html">Adding Wearable
6926 * Features to Notifications</a> for more information.
6927 * @param bridgeTag the bridge tag of the notification.
6928 * @return this object for method chaining
6929 */
6930 public WearableExtender setBridgeTag(String bridgeTag) {
6931 mBridgeTag = bridgeTag;
6932 return this;
6933 }
6934
6935 /**
6936 * Returns the bridge tag of the notification.
6937 * @return the bridge tag or null if not present.
6938 */
6939 public String getBridgeTag() {
6940 return mBridgeTag;
6941 }
6942
Griff Hazen61a9e862014-05-22 16:05:19 -07006943 private void setFlag(int mask, boolean value) {
6944 if (value) {
6945 mFlags |= mask;
6946 } else {
6947 mFlags &= ~mask;
6948 }
6949 }
6950 }
6951
6952 /**
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08006953 * <p>Helper class to add Android Auto extensions to notifications. To create a notification
6954 * with car extensions:
6955 *
6956 * <ol>
6957 * <li>Create an {@link Notification.Builder}, setting any desired
6958 * properties.
6959 * <li>Create a {@link CarExtender}.
6960 * <li>Set car-specific properties using the {@code add} and {@code set} methods of
6961 * {@link CarExtender}.
6962 * <li>Call {@link Notification.Builder#extend(Notification.Extender)}
6963 * to apply the extensions to a notification.
6964 * </ol>
6965 *
6966 * <pre class="prettyprint">
6967 * Notification notification = new Notification.Builder(context)
6968 * ...
6969 * .extend(new CarExtender()
6970 * .set*(...))
6971 * .build();
6972 * </pre>
6973 *
6974 * <p>Car extensions can be accessed on an existing notification by using the
6975 * {@code CarExtender(Notification)} constructor, and then using the {@code get} methods
6976 * to access values.
6977 */
6978 public static final class CarExtender implements Extender {
6979 private static final String TAG = "CarExtender";
6980
6981 private static final String EXTRA_CAR_EXTENDER = "android.car.EXTENSIONS";
6982 private static final String EXTRA_LARGE_ICON = "large_icon";
6983 private static final String EXTRA_CONVERSATION = "car_conversation";
6984 private static final String EXTRA_COLOR = "app_color";
6985
6986 private Bitmap mLargeIcon;
6987 private UnreadConversation mUnreadConversation;
6988 private int mColor = Notification.COLOR_DEFAULT;
6989
6990 /**
6991 * Create a {@link CarExtender} with default options.
6992 */
6993 public CarExtender() {
6994 }
6995
6996 /**
6997 * Create a {@link CarExtender} from the CarExtender options of an existing Notification.
6998 *
6999 * @param notif The notification from which to copy options.
7000 */
7001 public CarExtender(Notification notif) {
7002 Bundle carBundle = notif.extras == null ?
7003 null : notif.extras.getBundle(EXTRA_CAR_EXTENDER);
7004 if (carBundle != null) {
7005 mLargeIcon = carBundle.getParcelable(EXTRA_LARGE_ICON);
7006 mColor = carBundle.getInt(EXTRA_COLOR, Notification.COLOR_DEFAULT);
7007
7008 Bundle b = carBundle.getBundle(EXTRA_CONVERSATION);
7009 mUnreadConversation = UnreadConversation.getUnreadConversationFromBundle(b);
7010 }
7011 }
7012
7013 /**
7014 * Apply car extensions to a notification that is being built. This is typically called by
7015 * the {@link Notification.Builder#extend(Notification.Extender)}
7016 * method of {@link Notification.Builder}.
7017 */
7018 @Override
7019 public Notification.Builder extend(Notification.Builder builder) {
7020 Bundle carExtensions = new Bundle();
7021
7022 if (mLargeIcon != null) {
7023 carExtensions.putParcelable(EXTRA_LARGE_ICON, mLargeIcon);
7024 }
7025 if (mColor != Notification.COLOR_DEFAULT) {
7026 carExtensions.putInt(EXTRA_COLOR, mColor);
7027 }
7028
7029 if (mUnreadConversation != null) {
7030 Bundle b = mUnreadConversation.getBundleForUnreadConversation();
7031 carExtensions.putBundle(EXTRA_CONVERSATION, b);
7032 }
7033
7034 builder.getExtras().putBundle(EXTRA_CAR_EXTENDER, carExtensions);
7035 return builder;
7036 }
7037
7038 /**
7039 * Sets the accent color to use when Android Auto presents the notification.
7040 *
7041 * Android Auto uses the color set with {@link Notification.Builder#setColor(int)}
7042 * to accent the displayed notification. However, not all colors are acceptable in an
7043 * automotive setting. This method can be used to override the color provided in the
7044 * notification in such a situation.
7045 */
Tor Norbye80756e32015-03-02 09:39:27 -08007046 public CarExtender setColor(@ColorInt int color) {
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08007047 mColor = color;
7048 return this;
7049 }
7050
7051 /**
7052 * Gets the accent color.
7053 *
Julia Reynoldsd9228f12015-10-20 10:37:27 -04007054 * @see #setColor
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08007055 */
Tor Norbye80756e32015-03-02 09:39:27 -08007056 @ColorInt
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08007057 public int getColor() {
7058 return mColor;
7059 }
7060
7061 /**
7062 * Sets the large icon of the car notification.
7063 *
7064 * If no large icon is set in the extender, Android Auto will display the icon
7065 * specified by {@link Notification.Builder#setLargeIcon(android.graphics.Bitmap)}
7066 *
7067 * @param largeIcon The large icon to use in the car notification.
7068 * @return This object for method chaining.
7069 */
7070 public CarExtender setLargeIcon(Bitmap largeIcon) {
7071 mLargeIcon = largeIcon;
7072 return this;
7073 }
7074
7075 /**
7076 * Gets the large icon used in this car notification, or null if no icon has been set.
7077 *
7078 * @return The large icon for the car notification.
7079 * @see CarExtender#setLargeIcon
7080 */
7081 public Bitmap getLargeIcon() {
7082 return mLargeIcon;
7083 }
7084
7085 /**
7086 * Sets the unread conversation in a message notification.
7087 *
7088 * @param unreadConversation The unread part of the conversation this notification conveys.
7089 * @return This object for method chaining.
7090 */
7091 public CarExtender setUnreadConversation(UnreadConversation unreadConversation) {
7092 mUnreadConversation = unreadConversation;
7093 return this;
7094 }
7095
7096 /**
7097 * Returns the unread conversation conveyed by this notification.
7098 * @see #setUnreadConversation(UnreadConversation)
7099 */
7100 public UnreadConversation getUnreadConversation() {
7101 return mUnreadConversation;
7102 }
7103
7104 /**
7105 * A class which holds the unread messages from a conversation.
7106 */
7107 public static class UnreadConversation {
7108 private static final String KEY_AUTHOR = "author";
7109 private static final String KEY_TEXT = "text";
7110 private static final String KEY_MESSAGES = "messages";
7111 private static final String KEY_REMOTE_INPUT = "remote_input";
7112 private static final String KEY_ON_REPLY = "on_reply";
7113 private static final String KEY_ON_READ = "on_read";
7114 private static final String KEY_PARTICIPANTS = "participants";
7115 private static final String KEY_TIMESTAMP = "timestamp";
7116
7117 private final String[] mMessages;
7118 private final RemoteInput mRemoteInput;
7119 private final PendingIntent mReplyPendingIntent;
7120 private final PendingIntent mReadPendingIntent;
7121 private final String[] mParticipants;
7122 private final long mLatestTimestamp;
7123
7124 UnreadConversation(String[] messages, RemoteInput remoteInput,
7125 PendingIntent replyPendingIntent, PendingIntent readPendingIntent,
7126 String[] participants, long latestTimestamp) {
7127 mMessages = messages;
7128 mRemoteInput = remoteInput;
7129 mReadPendingIntent = readPendingIntent;
7130 mReplyPendingIntent = replyPendingIntent;
7131 mParticipants = participants;
7132 mLatestTimestamp = latestTimestamp;
7133 }
7134
7135 /**
7136 * Gets the list of messages conveyed by this notification.
7137 */
7138 public String[] getMessages() {
7139 return mMessages;
7140 }
7141
7142 /**
7143 * Gets the remote input that will be used to convey the response to a message list, or
7144 * null if no such remote input exists.
7145 */
7146 public RemoteInput getRemoteInput() {
7147 return mRemoteInput;
7148 }
7149
7150 /**
7151 * Gets the pending intent that will be triggered when the user replies to this
7152 * notification.
7153 */
7154 public PendingIntent getReplyPendingIntent() {
7155 return mReplyPendingIntent;
7156 }
7157
7158 /**
7159 * Gets the pending intent that Android Auto will send after it reads aloud all messages
7160 * in this object's message list.
7161 */
7162 public PendingIntent getReadPendingIntent() {
7163 return mReadPendingIntent;
7164 }
7165
7166 /**
7167 * Gets the participants in the conversation.
7168 */
7169 public String[] getParticipants() {
7170 return mParticipants;
7171 }
7172
7173 /**
7174 * Gets the firs participant in the conversation.
7175 */
7176 public String getParticipant() {
7177 return mParticipants.length > 0 ? mParticipants[0] : null;
7178 }
7179
7180 /**
7181 * Gets the timestamp of the conversation.
7182 */
7183 public long getLatestTimestamp() {
7184 return mLatestTimestamp;
7185 }
7186
7187 Bundle getBundleForUnreadConversation() {
7188 Bundle b = new Bundle();
7189 String author = null;
7190 if (mParticipants != null && mParticipants.length > 1) {
7191 author = mParticipants[0];
7192 }
7193 Parcelable[] messages = new Parcelable[mMessages.length];
7194 for (int i = 0; i < messages.length; i++) {
7195 Bundle m = new Bundle();
7196 m.putString(KEY_TEXT, mMessages[i]);
7197 m.putString(KEY_AUTHOR, author);
7198 messages[i] = m;
7199 }
7200 b.putParcelableArray(KEY_MESSAGES, messages);
7201 if (mRemoteInput != null) {
7202 b.putParcelable(KEY_REMOTE_INPUT, mRemoteInput);
7203 }
7204 b.putParcelable(KEY_ON_REPLY, mReplyPendingIntent);
7205 b.putParcelable(KEY_ON_READ, mReadPendingIntent);
7206 b.putStringArray(KEY_PARTICIPANTS, mParticipants);
7207 b.putLong(KEY_TIMESTAMP, mLatestTimestamp);
7208 return b;
7209 }
7210
7211 static UnreadConversation getUnreadConversationFromBundle(Bundle b) {
7212 if (b == null) {
7213 return null;
7214 }
7215 Parcelable[] parcelableMessages = b.getParcelableArray(KEY_MESSAGES);
7216 String[] messages = null;
7217 if (parcelableMessages != null) {
7218 String[] tmp = new String[parcelableMessages.length];
7219 boolean success = true;
7220 for (int i = 0; i < tmp.length; i++) {
7221 if (!(parcelableMessages[i] instanceof Bundle)) {
7222 success = false;
7223 break;
7224 }
7225 tmp[i] = ((Bundle) parcelableMessages[i]).getString(KEY_TEXT);
7226 if (tmp[i] == null) {
7227 success = false;
7228 break;
7229 }
7230 }
7231 if (success) {
7232 messages = tmp;
7233 } else {
7234 return null;
7235 }
7236 }
7237
7238 PendingIntent onRead = b.getParcelable(KEY_ON_READ);
7239 PendingIntent onReply = b.getParcelable(KEY_ON_REPLY);
7240
7241 RemoteInput remoteInput = b.getParcelable(KEY_REMOTE_INPUT);
7242
7243 String[] participants = b.getStringArray(KEY_PARTICIPANTS);
7244 if (participants == null || participants.length != 1) {
7245 return null;
7246 }
7247
7248 return new UnreadConversation(messages,
7249 remoteInput,
7250 onReply,
7251 onRead,
7252 participants, b.getLong(KEY_TIMESTAMP));
7253 }
7254 };
7255
7256 /**
7257 * Builder class for {@link CarExtender.UnreadConversation} objects.
7258 */
7259 public static class Builder {
7260 private final List<String> mMessages = new ArrayList<String>();
7261 private final String mParticipant;
7262 private RemoteInput mRemoteInput;
7263 private PendingIntent mReadPendingIntent;
7264 private PendingIntent mReplyPendingIntent;
7265 private long mLatestTimestamp;
7266
7267 /**
7268 * Constructs a new builder for {@link CarExtender.UnreadConversation}.
7269 *
7270 * @param name The name of the other participant in the conversation.
7271 */
7272 public Builder(String name) {
7273 mParticipant = name;
7274 }
7275
7276 /**
7277 * Appends a new unread message to the list of messages for this conversation.
7278 *
7279 * The messages should be added from oldest to newest.
7280 *
7281 * @param message The text of the new unread message.
7282 * @return This object for method chaining.
7283 */
7284 public Builder addMessage(String message) {
7285 mMessages.add(message);
7286 return this;
7287 }
7288
7289 /**
7290 * Sets the pending intent and remote input which will convey the reply to this
7291 * notification.
7292 *
7293 * @param pendingIntent The pending intent which will be triggered on a reply.
7294 * @param remoteInput The remote input parcelable which will carry the reply.
7295 * @return This object for method chaining.
7296 *
7297 * @see CarExtender.UnreadConversation#getRemoteInput
7298 * @see CarExtender.UnreadConversation#getReplyPendingIntent
7299 */
7300 public Builder setReplyAction(
7301 PendingIntent pendingIntent, RemoteInput remoteInput) {
7302 mRemoteInput = remoteInput;
7303 mReplyPendingIntent = pendingIntent;
7304
7305 return this;
7306 }
7307
7308 /**
7309 * Sets the pending intent that will be sent once the messages in this notification
7310 * are read.
7311 *
7312 * @param pendingIntent The pending intent to use.
7313 * @return This object for method chaining.
7314 */
7315 public Builder setReadPendingIntent(PendingIntent pendingIntent) {
7316 mReadPendingIntent = pendingIntent;
7317 return this;
7318 }
7319
7320 /**
7321 * Sets the timestamp of the most recent message in an unread conversation.
7322 *
7323 * If a messaging notification has been posted by your application and has not
7324 * yet been cancelled, posting a later notification with the same id and tag
7325 * but without a newer timestamp may result in Android Auto not displaying a
7326 * heads up notification for the later notification.
7327 *
7328 * @param timestamp The timestamp of the most recent message in the conversation.
7329 * @return This object for method chaining.
7330 */
7331 public Builder setLatestTimestamp(long timestamp) {
7332 mLatestTimestamp = timestamp;
7333 return this;
7334 }
7335
7336 /**
7337 * Builds a new unread conversation object.
7338 *
7339 * @return The new unread conversation object.
7340 */
7341 public UnreadConversation build() {
7342 String[] messages = mMessages.toArray(new String[mMessages.size()]);
7343 String[] participants = { mParticipant };
7344 return new UnreadConversation(messages, mRemoteInput, mReplyPendingIntent,
7345 mReadPendingIntent, participants, mLatestTimestamp);
7346 }
7347 }
7348 }
7349
7350 /**
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08007351 * <p>Helper class to add Android TV extensions to notifications. To create a notification
7352 * with a TV extension:
7353 *
7354 * <ol>
7355 * <li>Create an {@link Notification.Builder}, setting any desired properties.
7356 * <li>Create a {@link TvExtender}.
7357 * <li>Set TV-specific properties using the {@code set} methods of
7358 * {@link TvExtender}.
7359 * <li>Call {@link Notification.Builder#extend(Notification.Extender)}
7360 * to apply the extension to a notification.
7361 * </ol>
7362 *
7363 * <pre class="prettyprint">
7364 * Notification notification = new Notification.Builder(context)
7365 * ...
7366 * .extend(new TvExtender()
7367 * .set*(...))
7368 * .build();
7369 * </pre>
7370 *
7371 * <p>TV extensions can be accessed on an existing notification by using the
7372 * {@code TvExtender(Notification)} constructor, and then using the {@code get} methods
7373 * to access values.
7374 *
7375 * @hide
7376 */
7377 @SystemApi
7378 public static final class TvExtender implements Extender {
7379 private static final String TAG = "TvExtender";
7380
7381 private static final String EXTRA_TV_EXTENDER = "android.tv.EXTENSIONS";
7382 private static final String EXTRA_FLAGS = "flags";
7383 private static final String EXTRA_CONTENT_INTENT = "content_intent";
7384 private static final String EXTRA_DELETE_INTENT = "delete_intent";
Dmitri Plotnikovb8a04ee2017-01-20 10:39:22 -08007385 private static final String EXTRA_CHANNEL_ID = "channel_id";
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08007386
7387 // Flags bitwise-ored to mFlags
7388 private static final int FLAG_AVAILABLE_ON_TV = 0x1;
7389
7390 private int mFlags;
Dmitri Plotnikovb8a04ee2017-01-20 10:39:22 -08007391 private String mChannelId;
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08007392 private PendingIntent mContentIntent;
7393 private PendingIntent mDeleteIntent;
7394
7395 /**
7396 * Create a {@link TvExtender} with default options.
7397 */
7398 public TvExtender() {
7399 mFlags = FLAG_AVAILABLE_ON_TV;
7400 }
7401
7402 /**
7403 * Create a {@link TvExtender} from the TvExtender options of an existing Notification.
7404 *
7405 * @param notif The notification from which to copy options.
7406 */
7407 public TvExtender(Notification notif) {
7408 Bundle bundle = notif.extras == null ?
7409 null : notif.extras.getBundle(EXTRA_TV_EXTENDER);
7410 if (bundle != null) {
7411 mFlags = bundle.getInt(EXTRA_FLAGS);
Dmitri Plotnikovb8a04ee2017-01-20 10:39:22 -08007412 mChannelId = bundle.getString(EXTRA_CHANNEL_ID);
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08007413 mContentIntent = bundle.getParcelable(EXTRA_CONTENT_INTENT);
7414 mDeleteIntent = bundle.getParcelable(EXTRA_DELETE_INTENT);
7415 }
7416 }
7417
7418 /**
7419 * Apply a TV extension to a notification that is being built. This is typically called by
7420 * the {@link Notification.Builder#extend(Notification.Extender)}
7421 * method of {@link Notification.Builder}.
7422 */
7423 @Override
7424 public Notification.Builder extend(Notification.Builder builder) {
7425 Bundle bundle = new Bundle();
7426
7427 bundle.putInt(EXTRA_FLAGS, mFlags);
Dmitri Plotnikovb8a04ee2017-01-20 10:39:22 -08007428 bundle.putString(EXTRA_CHANNEL_ID, mChannelId);
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08007429 if (mContentIntent != null) {
7430 bundle.putParcelable(EXTRA_CONTENT_INTENT, mContentIntent);
7431 }
7432
7433 if (mDeleteIntent != null) {
7434 bundle.putParcelable(EXTRA_DELETE_INTENT, mDeleteIntent);
7435 }
7436
7437 builder.getExtras().putBundle(EXTRA_TV_EXTENDER, bundle);
7438 return builder;
7439 }
7440
7441 /**
7442 * Returns true if this notification should be shown on TV. This method return true
7443 * if the notification was extended with a TvExtender.
7444 */
7445 public boolean isAvailableOnTv() {
7446 return (mFlags & FLAG_AVAILABLE_ON_TV) != 0;
7447 }
7448
7449 /**
Dmitri Plotnikovb8a04ee2017-01-20 10:39:22 -08007450 * Specifies the channel the notification should be delivered on when shown on TV.
7451 * It can be different from the channel that the notification is delivered to when
7452 * posting on a non-TV device.
7453 */
7454 public TvExtender setChannel(String channelId) {
7455 mChannelId = channelId;
7456 return this;
7457 }
7458
7459 /**
7460 * Returns the id of the channel this notification posts to on TV.
7461 */
7462 public String getChannel() {
7463 return mChannelId;
7464 }
7465
7466 /**
Dmitri Plotnikov9e9cfd92016-12-21 10:52:26 -08007467 * Supplies a {@link PendingIntent} to be sent when the notification is selected on TV.
7468 * If provided, it is used instead of the content intent specified
7469 * at the level of Notification.
7470 */
7471 public TvExtender setContentIntent(PendingIntent intent) {
7472 mContentIntent = intent;
7473 return this;
7474 }
7475
7476 /**
7477 * Returns the TV-specific content intent. If this method returns null, the
7478 * main content intent on the notification should be used.
7479 *
7480 * @see {@link Notification#contentIntent}
7481 */
7482 public PendingIntent getContentIntent() {
7483 return mContentIntent;
7484 }
7485
7486 /**
7487 * Supplies a {@link PendingIntent} to send when the notification is cleared explicitly
7488 * by the user on TV. If provided, it is used instead of the delete intent specified
7489 * at the level of Notification.
7490 */
7491 public TvExtender setDeleteIntent(PendingIntent intent) {
7492 mDeleteIntent = intent;
7493 return this;
7494 }
7495
7496 /**
7497 * Returns the TV-specific delete intent. If this method returns null, the
7498 * main delete intent on the notification should be used.
7499 *
7500 * @see {@link Notification#deleteIntent}
7501 */
7502 public PendingIntent getDeleteIntent() {
7503 return mDeleteIntent;
7504 }
7505 }
7506
7507 /**
Griff Hazen61a9e862014-05-22 16:05:19 -07007508 * Get an array of Notification objects from a parcelable array bundle field.
7509 * Update the bundle to have a typed array so fetches in the future don't need
7510 * to do an array copy.
7511 */
7512 private static Notification[] getNotificationArrayFromBundle(Bundle bundle, String key) {
7513 Parcelable[] array = bundle.getParcelableArray(key);
7514 if (array instanceof Notification[] || array == null) {
7515 return (Notification[]) array;
7516 }
7517 Notification[] typedArray = Arrays.copyOf(array, array.length,
7518 Notification[].class);
7519 bundle.putParcelableArray(key, typedArray);
7520 return typedArray;
7521 }
Christoph Studer4600f9b2014-07-22 22:44:43 +02007522
7523 private static class BuilderRemoteViews extends RemoteViews {
7524 public BuilderRemoteViews(Parcel parcel) {
7525 super(parcel);
7526 }
7527
Kenny Guy77320062014-08-27 21:37:15 +01007528 public BuilderRemoteViews(ApplicationInfo appInfo, int layoutId) {
7529 super(appInfo, layoutId);
Christoph Studer4600f9b2014-07-22 22:44:43 +02007530 }
7531
7532 @Override
7533 public BuilderRemoteViews clone() {
7534 Parcel p = Parcel.obtain();
7535 writeToParcel(p, 0);
7536 p.setDataPosition(0);
7537 BuilderRemoteViews brv = new BuilderRemoteViews(p);
7538 p.recycle();
7539 return brv;
7540 }
7541 }
Adrian Roos70d7aa32017-01-11 15:39:06 -08007542
7543 private static class StandardTemplateParams {
7544 boolean hasProgress = true;
7545 boolean ambient = false;
7546 CharSequence title;
7547 CharSequence text;
7548
7549 final StandardTemplateParams reset() {
7550 hasProgress = true;
7551 ambient = false;
7552 title = null;
7553 text = null;
7554 return this;
7555 }
7556
7557 final StandardTemplateParams hasProgress(boolean hasProgress) {
7558 this.hasProgress = hasProgress;
7559 return this;
7560 }
7561
7562 final StandardTemplateParams title(CharSequence title) {
7563 this.title = title;
7564 return this;
7565 }
7566
7567 final StandardTemplateParams text(CharSequence text) {
7568 this.text = text;
7569 return this;
7570 }
7571
7572 final StandardTemplateParams ambient(boolean ambient) {
7573 this.ambient = ambient;
7574 return this;
7575 }
7576
7577 final StandardTemplateParams fillTextsFrom(Builder b) {
7578 Bundle extras = b.mN.extras;
7579 title = b.processLegacyText(extras.getCharSequence(EXTRA_TITLE));
7580 text = b.processLegacyText(extras.getCharSequence(EXTRA_TEXT));
7581 return this;
7582 }
7583 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007584}