blob: 099a5fe9352e2bee95554b8cc19be70736a39b8c [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.app;
18
Tor Norbye80756e32015-03-02 09:39:27 -080019import android.annotation.ColorInt;
Tor Norbye7b9c9122013-05-30 16:48:33 -070020import android.annotation.DrawableRes;
Tor Norbyed9273d62013-05-30 15:59:53 -070021import android.annotation.IntDef;
Daniel Sandler01df1c62014-06-09 10:54:01 -040022import android.annotation.SdkConstant;
23import android.annotation.SdkConstant.SdkConstantType;
Julia Reynolds233a5f92015-10-19 13:51:23 -040024import android.annotation.SystemApi;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080025import android.content.Context;
26import android.content.Intent;
Kenny Guy77320062014-08-27 21:37:15 +010027import android.content.pm.ApplicationInfo;
Selim Cinek65b2e7c2015-10-26 14:11:31 -070028import android.content.pm.PackageManager;
Christoph Studer4600f9b2014-07-22 22:44:43 +020029import android.content.pm.PackageManager.NameNotFoundException;
Jorim Jaggief72a192014-08-26 21:57:46 +020030import android.content.res.ColorStateList;
Joe Onoratoef1e7762010-09-17 18:38:38 -040031import android.graphics.Bitmap;
Kenny Guy8a0101b2014-05-08 23:34:12 +010032import android.graphics.Canvas;
Jorim Jaggi5c2d8462014-03-21 17:37:00 +010033import android.graphics.PorterDuff;
Kenny Guy8a0101b2014-05-08 23:34:12 +010034import android.graphics.drawable.Drawable;
Dan Sandlerd63f9322015-05-06 15:18:49 -040035import android.graphics.drawable.Icon;
John Spurlockc0650f022014-07-19 13:22:39 -040036import android.media.AudioAttributes;
Jeff Sharkey098d5802012-04-26 17:30:34 -070037import android.media.AudioManager;
Jeff Browndba34ba2014-06-24 20:46:03 -070038import android.media.session.MediaSession;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080039import android.net.Uri;
Daniel Sandlerdcbaf662013-04-26 16:23:09 -040040import android.os.BadParcelableException;
Christoph Studer239f8352014-08-25 15:13:18 +020041import android.os.Build;
Daniel Sandler2561b0b2012-02-13 21:04:12 -050042import android.os.Bundle;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080043import android.os.Parcel;
44import android.os.Parcelable;
Daniel Sandlera2985ed2012-04-03 16:42:00 -040045import android.os.SystemClock;
Jeff Sharkey6d515712012-09-20 16:06:08 -070046import android.os.UserHandle;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080047import android.text.TextUtils;
Daniel Sandlerdcbaf662013-04-26 16:23:09 -040048import android.util.Log;
Daniel Sandler9f7936a2012-05-21 16:14:28 -040049import android.util.TypedValue;
Griff Hazen61a9e862014-05-22 16:05:19 -070050import android.view.Gravity;
Selim Cinekea4bef72015-12-02 15:51:10 -080051import android.view.NotificationHeaderView;
Joe Onorato8595a3d2010-11-19 18:12:07 -080052import android.view.View;
Jeff Sharkey1c400132011-08-05 14:50:13 -070053import android.widget.ProgressBar;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080054import android.widget.RemoteViews;
55
Griff Hazen959591e2014-05-15 22:26:18 -070056import com.android.internal.R;
Griff Hazenc091ba82014-05-16 10:13:26 -070057import com.android.internal.util.NotificationColorUtil;
Griff Hazen959591e2014-05-15 22:26:18 -070058
Tor Norbyed9273d62013-05-30 15:59:53 -070059import java.lang.annotation.Retention;
60import java.lang.annotation.RetentionPolicy;
Christoph Studer4600f9b2014-07-22 22:44:43 +020061import java.lang.reflect.Constructor;
Daniel Sandler2561b0b2012-02-13 21:04:12 -050062import java.util.ArrayList;
Griff Hazen61a9e862014-05-22 16:05:19 -070063import java.util.Arrays;
Griff Hazen5cadc3b2014-05-20 09:55:39 -070064import java.util.Collections;
Griff Hazen61a9e862014-05-22 16:05:19 -070065import java.util.List;
Julia Reynolds74303cf2015-10-16 11:37:55 -040066import java.util.Objects;
Joe Onorato561d3852010-11-20 18:09:34 -080067
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080068/**
69 * A class that represents how a persistent notification is to be presented to
70 * the user using the {@link android.app.NotificationManager}.
71 *
Joe Onoratocb109a02011-01-18 17:57:41 -080072 * <p>The {@link Notification.Builder Notification.Builder} has been added to make it
73 * easier to construct Notifications.</p>
74 *
Joe Fernandez558459f2011-10-13 16:47:36 -070075 * <div class="special reference">
76 * <h3>Developer Guides</h3>
77 * <p>For a guide to creating notifications, read the
78 * <a href="{@docRoot}guide/topics/ui/notifiers/notifications.html">Status Bar Notifications</a>
79 * developer guide.</p>
80 * </div>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080081 */
82public class Notification implements Parcelable
83{
Daniel Sandlerdcbaf662013-04-26 16:23:09 -040084 private static final String TAG = "Notification";
85
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080086 /**
Daniel Sandler01df1c62014-06-09 10:54:01 -040087 * An activity that provides a user interface for adjusting notification preferences for its
88 * containing application. Optional but recommended for apps that post
89 * {@link android.app.Notification Notifications}.
90 */
91 @SdkConstant(SdkConstantType.INTENT_CATEGORY)
92 public static final String INTENT_CATEGORY_NOTIFICATION_PREFERENCES
93 = "android.intent.category.NOTIFICATION_PREFERENCES";
94
95 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080096 * Use all default values (where applicable).
97 */
98 public static final int DEFAULT_ALL = ~0;
Daniel Sandler2561b0b2012-02-13 21:04:12 -050099
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800100 /**
101 * Use the default notification sound. This will ignore any given
102 * {@link #sound}.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500103 *
Chris Wren47c20a12014-06-18 17:27:29 -0400104 * <p>
105 * A notification that is noisy is more likely to be presented as a heads-up notification.
106 * </p>
107 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800108 * @see #defaults
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500109 */
110
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800111 public static final int DEFAULT_SOUND = 1;
112
113 /**
114 * Use the default notification vibrate. This will ignore any given
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500115 * {@link #vibrate}. Using phone vibration requires the
Scott Mainb8b36452009-04-26 15:50:49 -0700116 * {@link android.Manifest.permission#VIBRATE VIBRATE} permission.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500117 *
Chris Wren47c20a12014-06-18 17:27:29 -0400118 * <p>
119 * A notification that vibrates is more likely to be presented as a heads-up notification.
120 * </p>
121 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800122 * @see #defaults
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500123 */
124
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800125 public static final int DEFAULT_VIBRATE = 2;
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500126
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800127 /**
128 * Use the default notification lights. This will ignore the
129 * {@link #FLAG_SHOW_LIGHTS} bit, and {@link #ledARGB}, {@link #ledOffMS}, or
130 * {@link #ledOnMS}.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500131 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800132 * @see #defaults
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500133 */
134
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800135 public static final int DEFAULT_LIGHTS = 4;
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500136
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800137 /**
Christoph Studer535ec612014-09-03 15:47:47 +0200138 * Maximum length of CharSequences accepted by Builder and friends.
139 *
140 * <p>
141 * Avoids spamming the system with overly large strings such as full e-mails.
142 */
143 private static final int MAX_CHARSEQUENCE_LENGTH = 5 * 1024;
144
145 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500146 * A timestamp related to this notification, in milliseconds since the epoch.
Joe Malin8d40d042012-11-05 11:36:40 -0800147 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500148 * Default value: {@link System#currentTimeMillis() Now}.
149 *
150 * Choose a timestamp that will be most relevant to the user. For most finite events, this
151 * corresponds to the time the event happened (or will happen, in the case of events that have
152 * yet to occur but about which the user is being informed). Indefinite events should be
Joe Malin8d40d042012-11-05 11:36:40 -0800153 * timestamped according to when the activity began.
154 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500155 * Some examples:
Joe Malin8d40d042012-11-05 11:36:40 -0800156 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500157 * <ul>
158 * <li>Notification of a new chat message should be stamped when the message was received.</li>
159 * <li>Notification of an ongoing file download (with a progress bar, for example) should be stamped when the download started.</li>
160 * <li>Notification of a completed file download should be stamped when the download finished.</li>
161 * <li>Notification of an upcoming meeting should be stamped with the time the meeting will begin (that is, in the future).</li>
162 * <li>Notification of an ongoing stopwatch (increasing timer) should be stamped with the watch's start time.
163 * <li>Notification of an ongoing countdown timer should be stamped with the timer's end time.
Joe Malin8d40d042012-11-05 11:36:40 -0800164 * </ul>
165 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800166 */
167 public long when;
168
169 /**
170 * The resource id of a drawable to use as the icon in the status bar.
Dan Sandler86647982015-05-13 23:41:13 -0400171 *
172 * @deprecated Use {@link Builder#setSmallIcon(Icon)} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800173 */
Dan Sandler86647982015-05-13 23:41:13 -0400174 @Deprecated
Tor Norbye7b9c9122013-05-30 16:48:33 -0700175 @DrawableRes
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800176 public int icon;
177
178 /**
Joe Onorato46439ce2010-11-19 13:56:21 -0800179 * If the icon in the status bar is to have more than one level, you can set this. Otherwise,
180 * leave it at its default value of 0.
181 *
182 * @see android.widget.ImageView#setImageLevel
Griff Hazen959591e2014-05-15 22:26:18 -0700183 * @see android.graphics.drawable.Drawable#setLevel
Joe Onorato46439ce2010-11-19 13:56:21 -0800184 */
185 public int iconLevel;
186
187 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500188 * The number of events that this notification represents. For example, in a new mail
189 * notification, this could be the number of unread messages.
Joe Malin8d40d042012-11-05 11:36:40 -0800190 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500191 * The system may or may not use this field to modify the appearance of the notification. For
192 * example, before {@link android.os.Build.VERSION_CODES#HONEYCOMB}, this number was
193 * superimposed over the icon in the status bar. Starting with
194 * {@link android.os.Build.VERSION_CODES#HONEYCOMB}, the template used by
195 * {@link Notification.Builder} has displayed the number in the expanded notification view.
Joe Malin8d40d042012-11-05 11:36:40 -0800196 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500197 * If the number is 0 or negative, it is never shown.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800198 */
199 public int number;
200
201 /**
202 * The intent to execute when the expanded status entry is clicked. If
203 * this is an activity, it must include the
204 * {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} flag, which requires
Scott Main7aee61f2011-02-08 11:25:01 -0800205 * that you take care of task management as described in the
206 * <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back
Dianne Hackborn6ceca582012-01-10 15:24:26 -0800207 * Stack</a> document. In particular, make sure to read the notification section
208 * <a href="{@docRoot}guide/topics/ui/notifiers/notifications.html#HandlingNotifications">Handling
209 * Notifications</a> for the correct ways to launch an application from a
210 * notification.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800211 */
212 public PendingIntent contentIntent;
213
214 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500215 * The intent to execute when the notification is explicitly dismissed by the user, either with
216 * the "Clear All" button or by swiping it away individually.
217 *
218 * This probably shouldn't be launching an activity since several of those will be sent
219 * at the same time.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800220 */
221 public PendingIntent deleteIntent;
222
223 /**
Dianne Hackborn170bae72010-09-03 15:14:28 -0700224 * An intent to launch instead of posting the notification to the status bar.
Joe Onoratocb109a02011-01-18 17:57:41 -0800225 *
Chris Wren47c20a12014-06-18 17:27:29 -0400226 * <p>
227 * The system UI may choose to display a heads-up notification, instead of
228 * launching this intent, while the user is using the device.
229 * </p>
230 *
Joe Onoratocb109a02011-01-18 17:57:41 -0800231 * @see Notification.Builder#setFullScreenIntent
Daniel Sandlere46cbd32010-06-17 10:35:26 -0400232 */
233 public PendingIntent fullScreenIntent;
234
235 /**
Dan Sandler5fcdf6e2014-07-18 11:31:15 -0400236 * Text that summarizes this notification for accessibility services.
237 *
238 * As of the L release, this text is no longer shown on screen, but it is still useful to
239 * accessibility services (where it serves as an audible announcement of the notification's
240 * appearance).
Joe Onoratoef1e7762010-09-17 18:38:38 -0400241 *
Joe Onorato46439ce2010-11-19 13:56:21 -0800242 * @see #tickerView
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800243 */
244 public CharSequence tickerText;
245
246 /**
Dan Sandler5fcdf6e2014-07-18 11:31:15 -0400247 * Formerly, a view showing the {@link #tickerText}.
248 *
249 * No longer displayed in the status bar as of API 21.
Joe Onoratoef1e7762010-09-17 18:38:38 -0400250 */
Dan Sandler5fcdf6e2014-07-18 11:31:15 -0400251 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -0800252 public RemoteViews tickerView;
Joe Onoratoef1e7762010-09-17 18:38:38 -0400253
254 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400255 * The view that will represent this notification in the notification list (which is pulled
256 * down from the status bar).
257 *
258 * As of N, this field is not used. The notification view is determined by the inputs to
259 * {@link Notification.Builder}; a custom RemoteViews can optionally be
260 * supplied with {@link Notification.Builder#setCustomContentView(RemoteViews)}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800261 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400262 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800263 public RemoteViews contentView;
264
Daniel Sandlera0a938c2012-03-15 08:42:37 -0400265 /**
Daniel Sandler4dfbe832012-04-11 14:51:46 -0400266 * A large-format version of {@link #contentView}, giving the Notification an
Daniel Sandlerf3b73432012-03-27 15:01:25 -0400267 * opportunity to show more detail. The system UI may choose to show this
268 * instead of the normal content view at its discretion.
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400269 *
270 * As of N, this field is not used. The expanded notification view is determined by the
271 * inputs to {@link Notification.Builder}; a custom RemoteViews can optionally be
272 * supplied with {@link Notification.Builder#setCustomBigContentView(RemoteViews)}.
Daniel Sandlerf3b73432012-03-27 15:01:25 -0400273 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400274 @Deprecated
Daniel Sandlerf3b73432012-03-27 15:01:25 -0400275 public RemoteViews bigContentView;
276
Chris Wren8fd39ec2014-02-27 17:43:26 -0500277
278 /**
Chris Wren47c20a12014-06-18 17:27:29 -0400279 * A medium-format version of {@link #contentView}, providing the Notification an
280 * opportunity to add action buttons to contentView. At its discretion, the system UI may
281 * choose to show this as a heads-up notification, which will pop up so the user can see
282 * it without leaving their current activity.
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400283 *
284 * As of N, this field is not used. The heads-up notification view is determined by the
285 * inputs to {@link Notification.Builder}; a custom RemoteViews can optionally be
286 * supplied with {@link Notification.Builder#setCustomHeadsUpContentView(RemoteViews)}.
Chris Wren8fd39ec2014-02-27 17:43:26 -0500287 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400288 @Deprecated
Chris Wren8fd39ec2014-02-27 17:43:26 -0500289 public RemoteViews headsUpContentView;
290
Daniel Sandlerf3b73432012-03-27 15:01:25 -0400291 /**
Dan Sandler86647982015-05-13 23:41:13 -0400292 * A large bitmap to be shown in the notification content area.
293 *
294 * @deprecated Use {@link Builder#setLargeIcon(Icon)} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800295 */
Dan Sandler86647982015-05-13 23:41:13 -0400296 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -0800297 public Bitmap largeIcon;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800298
299 /**
300 * The sound to play.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500301 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800302 * <p>
Chris Wren47c20a12014-06-18 17:27:29 -0400303 * A notification that is noisy is more likely to be presented as a heads-up notification.
304 * </p>
305 *
306 * <p>
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500307 * To play the default notification sound, see {@link #defaults}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800308 * </p>
309 */
310 public Uri sound;
311
312 /**
313 * Use this constant as the value for audioStreamType to request that
314 * the default stream type for notifications be used. Currently the
Jeff Sharkey098d5802012-04-26 17:30:34 -0700315 * default stream type is {@link AudioManager#STREAM_NOTIFICATION}.
John Spurlockc0650f022014-07-19 13:22:39 -0400316 *
317 * @deprecated Use {@link #audioAttributes} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800318 */
Jean-Michel Trivi81f871e2014-08-06 16:32:38 -0700319 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800320 public static final int STREAM_DEFAULT = -1;
321
322 /**
323 * The audio stream type to use when playing the sound.
324 * Should be one of the STREAM_ constants from
325 * {@link android.media.AudioManager}.
John Spurlockc0650f022014-07-19 13:22:39 -0400326 *
327 * @deprecated Use {@link #audioAttributes} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800328 */
Jean-Michel Trivi81f871e2014-08-06 16:32:38 -0700329 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800330 public int audioStreamType = STREAM_DEFAULT;
331
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800332 /**
John Spurlockc0650f022014-07-19 13:22:39 -0400333 * The default value of {@link #audioAttributes}.
334 */
335 public static final AudioAttributes AUDIO_ATTRIBUTES_DEFAULT = new AudioAttributes.Builder()
336 .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
337 .setUsage(AudioAttributes.USAGE_NOTIFICATION)
338 .build();
339
340 /**
341 * The {@link AudioAttributes audio attributes} to use when playing the sound.
342 */
343 public AudioAttributes audioAttributes = AUDIO_ATTRIBUTES_DEFAULT;
344
345 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500346 * The pattern with which to vibrate.
347 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800348 * <p>
349 * To vibrate the default pattern, see {@link #defaults}.
350 * </p>
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500351 *
Chris Wren47c20a12014-06-18 17:27:29 -0400352 * <p>
353 * A notification that vibrates is more likely to be presented as a heads-up notification.
354 * </p>
355 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800356 * @see android.os.Vibrator#vibrate(long[],int)
357 */
358 public long[] vibrate;
359
360 /**
361 * The color of the led. The hardware will do its best approximation.
362 *
363 * @see #FLAG_SHOW_LIGHTS
364 * @see #flags
365 */
Tor Norbye80756e32015-03-02 09:39:27 -0800366 @ColorInt
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800367 public int ledARGB;
368
369 /**
370 * The number of milliseconds for the LED to be on while it's flashing.
371 * The hardware will do its best approximation.
372 *
373 * @see #FLAG_SHOW_LIGHTS
374 * @see #flags
375 */
376 public int ledOnMS;
377
378 /**
379 * The number of milliseconds for the LED to be off while it's flashing.
380 * The hardware will do its best approximation.
381 *
382 * @see #FLAG_SHOW_LIGHTS
383 * @see #flags
384 */
385 public int ledOffMS;
386
387 /**
388 * Specifies which values should be taken from the defaults.
389 * <p>
390 * To set, OR the desired from {@link #DEFAULT_SOUND},
391 * {@link #DEFAULT_VIBRATE}, {@link #DEFAULT_LIGHTS}. For all default
392 * values, use {@link #DEFAULT_ALL}.
393 * </p>
394 */
395 public int defaults;
396
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800397 /**
398 * Bit to be bitwise-ored into the {@link #flags} field that should be
399 * set if you want the LED on for this notification.
400 * <ul>
401 * <li>To turn the LED off, pass 0 in the alpha channel for colorARGB
402 * or 0 for both ledOnMS and ledOffMS.</li>
403 * <li>To turn the LED on, pass 1 for ledOnMS and 0 for ledOffMS.</li>
404 * <li>To flash the LED, pass the number of milliseconds that it should
405 * be on and off to ledOnMS and ledOffMS.</li>
406 * </ul>
407 * <p>
408 * Since hardware varies, you are not guaranteed that any of the values
409 * you pass are honored exactly. Use the system defaults (TODO) if possible
410 * because they will be set to values that work on any given hardware.
411 * <p>
412 * The alpha channel must be set for forward compatibility.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500413 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800414 */
415 public static final int FLAG_SHOW_LIGHTS = 0x00000001;
416
417 /**
418 * Bit to be bitwise-ored into the {@link #flags} field that should be
419 * set if this notification is in reference to something that is ongoing,
420 * like a phone call. It should not be set if this notification is in
421 * reference to something that happened at a particular point in time,
422 * like a missed phone call.
423 */
424 public static final int FLAG_ONGOING_EVENT = 0x00000002;
425
426 /**
427 * Bit to be bitwise-ored into the {@link #flags} field that if set,
Scott Mainb8b36452009-04-26 15:50:49 -0700428 * the audio will be repeated until the notification is
429 * cancelled or the notification window is opened.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800430 */
431 public static final int FLAG_INSISTENT = 0x00000004;
432
433 /**
434 * Bit to be bitwise-ored into the {@link #flags} field that should be
Griff Hazen293977b2014-04-28 08:37:20 -0700435 * set if you would only like the sound, vibrate and ticker to be played
436 * if the notification was not already showing.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800437 */
438 public static final int FLAG_ONLY_ALERT_ONCE = 0x00000008;
439
440 /**
441 * Bit to be bitwise-ored into the {@link #flags} field that should be
442 * set if the notification should be canceled when it is clicked by the
Daniel Sandler8aa9ae62012-12-04 23:31:47 -0500443 * user.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800444 */
445 public static final int FLAG_AUTO_CANCEL = 0x00000010;
446
447 /**
448 * Bit to be bitwise-ored into the {@link #flags} field that should be
449 * set if the notification should not be canceled when the user clicks
450 * the Clear all button.
451 */
452 public static final int FLAG_NO_CLEAR = 0x00000020;
453
Dianne Hackbornd8a43f62009-08-17 23:33:56 -0700454 /**
455 * Bit to be bitwise-ored into the {@link #flags} field that should be
456 * set if this notification represents a currently running service. This
457 * will normally be set for you by {@link Service#startForeground}.
458 */
459 public static final int FLAG_FOREGROUND_SERVICE = 0x00000040;
460
Daniel Sandlere46cbd32010-06-17 10:35:26 -0400461 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500462 * Obsolete flag indicating high-priority notifications; use the priority field instead.
Joe Malin8d40d042012-11-05 11:36:40 -0800463 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500464 * @deprecated Use {@link #priority} with a positive value.
Daniel Sandlere46cbd32010-06-17 10:35:26 -0400465 */
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500466 public static final int FLAG_HIGH_PRIORITY = 0x00000080;
Daniel Sandlere46cbd32010-06-17 10:35:26 -0400467
Griff Hazendfcb0802014-02-11 12:00:00 -0800468 /**
469 * Bit to be bitswise-ored into the {@link #flags} field that should be
470 * set if this notification is relevant to the current device only
471 * and it is not recommended that it bridge to other devices.
472 */
473 public static final int FLAG_LOCAL_ONLY = 0x00000100;
474
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700475 /**
476 * Bit to be bitswise-ored into the {@link #flags} field that should be
477 * set if this notification is the group summary for a group of notifications.
478 * Grouped notifications may display in a cluster or stack on devices which
479 * support such rendering. Requires a group key also be set using {@link Builder#setGroup}.
480 */
481 public static final int FLAG_GROUP_SUMMARY = 0x00000200;
482
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800483 public int flags;
484
Tor Norbyed9273d62013-05-30 15:59:53 -0700485 /** @hide */
486 @IntDef({PRIORITY_DEFAULT,PRIORITY_LOW,PRIORITY_MIN,PRIORITY_HIGH,PRIORITY_MAX})
487 @Retention(RetentionPolicy.SOURCE)
488 public @interface Priority {}
489
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800490 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500491 * Default notification {@link #priority}. If your application does not prioritize its own
492 * notifications, use this value for all notifications.
493 */
494 public static final int PRIORITY_DEFAULT = 0;
495
496 /**
497 * Lower {@link #priority}, for items that are less important. The UI may choose to show these
498 * items smaller, or at a different position in the list, compared with your app's
499 * {@link #PRIORITY_DEFAULT} items.
500 */
501 public static final int PRIORITY_LOW = -1;
502
503 /**
504 * Lowest {@link #priority}; these items might not be shown to the user except under special
505 * circumstances, such as detailed notification logs.
506 */
507 public static final int PRIORITY_MIN = -2;
508
509 /**
510 * Higher {@link #priority}, for more important notifications or alerts. The UI may choose to
511 * show these items larger, or at a different position in notification lists, compared with
512 * your app's {@link #PRIORITY_DEFAULT} items.
513 */
514 public static final int PRIORITY_HIGH = 1;
515
516 /**
517 * Highest {@link #priority}, for your application's most important items that require the
518 * user's prompt attention or input.
519 */
520 public static final int PRIORITY_MAX = 2;
521
522 /**
523 * Relative priority for this notification.
Joe Malin8d40d042012-11-05 11:36:40 -0800524 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500525 * Priority is an indication of how much of the user's valuable attention should be consumed by
526 * this notification. Low-priority notifications may be hidden from the user in certain
527 * situations, while the user might be interrupted for a higher-priority notification. The
Daniel Sandler6738eee2012-11-16 12:03:32 -0500528 * system will make a determination about how to interpret this priority when presenting
529 * the notification.
Chris Wren47c20a12014-06-18 17:27:29 -0400530 *
531 * <p>
532 * A notification that is at least {@link #PRIORITY_HIGH} is more likely to be presented
533 * as a heads-up notification.
534 * </p>
535 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500536 */
Tor Norbyed9273d62013-05-30 15:59:53 -0700537 @Priority
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500538 public int priority;
Joe Malin8d40d042012-11-05 11:36:40 -0800539
Dan Sandler26e81cf2014-05-06 10:01:27 -0400540 /**
541 * Accent color (an ARGB integer like the constants in {@link android.graphics.Color})
542 * to be applied by the standard Style templates when presenting this notification.
543 *
544 * The current template design constructs a colorful header image by overlaying the
545 * {@link #icon} image (stenciled in white) atop a field of this color. Alpha components are
546 * ignored.
547 */
Tor Norbye80756e32015-03-02 09:39:27 -0800548 @ColorInt
Dan Sandler26e81cf2014-05-06 10:01:27 -0400549 public int color = COLOR_DEFAULT;
550
551 /**
552 * Special value of {@link #color} telling the system not to decorate this notification with
553 * any special color but instead use default colors when presenting this notification.
554 */
Tor Norbye80756e32015-03-02 09:39:27 -0800555 @ColorInt
Dan Sandler26e81cf2014-05-06 10:01:27 -0400556 public static final int COLOR_DEFAULT = 0; // AKA Color.TRANSPARENT
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600557
558 /**
559 * Sphere of visibility of this notification, which affects how and when the SystemUI reveals
560 * the notification's presence and contents in untrusted situations (namely, on the secure
561 * lockscreen).
562 *
563 * The default level, {@link #VISIBILITY_PRIVATE}, behaves exactly as notifications have always
564 * done on Android: The notification's {@link #icon} and {@link #tickerText} (if available) are
565 * shown in all situations, but the contents are only available if the device is unlocked for
566 * the appropriate user.
567 *
568 * A more permissive policy can be expressed by {@link #VISIBILITY_PUBLIC}; such a notification
569 * can be read even in an "insecure" context (that is, above a secure lockscreen).
570 * To modify the public version of this notification—for example, to redact some portions—see
571 * {@link Builder#setPublicVersion(Notification)}.
572 *
573 * Finally, a notification can be made {@link #VISIBILITY_SECRET}, which will suppress its icon
574 * and ticker until the user has bypassed the lockscreen.
575 */
576 public int visibility;
577
Griff Hazenfc3922d2014-08-20 11:56:44 -0700578 /**
579 * Notification visibility: Show this notification in its entirety on all lockscreens.
580 *
581 * {@see #visibility}
582 */
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600583 public static final int VISIBILITY_PUBLIC = 1;
Griff Hazenfc3922d2014-08-20 11:56:44 -0700584
585 /**
586 * Notification visibility: Show this notification on all lockscreens, but conceal sensitive or
587 * private information on secure lockscreens.
588 *
589 * {@see #visibility}
590 */
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600591 public static final int VISIBILITY_PRIVATE = 0;
Griff Hazenfc3922d2014-08-20 11:56:44 -0700592
593 /**
594 * Notification visibility: Do not reveal any part of this notification on a secure lockscreen.
595 *
596 * {@see #visibility}
597 */
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600598 public static final int VISIBILITY_SECRET = -1;
599
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500600 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400601 * Notification category: incoming call (voice or video) or similar synchronous communication request.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500602 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400603 public static final String CATEGORY_CALL = "call";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500604
605 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400606 * Notification category: incoming direct message (SMS, instant message, etc.).
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500607 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400608 public static final String CATEGORY_MESSAGE = "msg";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500609
610 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400611 * Notification category: asynchronous bulk message (email).
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500612 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400613 public static final String CATEGORY_EMAIL = "email";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500614
615 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400616 * Notification category: calendar event.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500617 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400618 public static final String CATEGORY_EVENT = "event";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500619
620 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400621 * Notification category: promotion or advertisement.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500622 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400623 public static final String CATEGORY_PROMO = "promo";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500624
625 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400626 * Notification category: alarm or timer.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500627 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400628 public static final String CATEGORY_ALARM = "alarm";
629
630 /**
631 * Notification category: progress of a long-running background operation.
632 */
633 public static final String CATEGORY_PROGRESS = "progress";
634
635 /**
636 * Notification category: social network or sharing update.
637 */
638 public static final String CATEGORY_SOCIAL = "social";
639
640 /**
641 * Notification category: error in background operation or authentication status.
642 */
643 public static final String CATEGORY_ERROR = "err";
644
645 /**
646 * Notification category: media transport control for playback.
647 */
648 public static final String CATEGORY_TRANSPORT = "transport";
649
650 /**
651 * Notification category: system or device status update. Reserved for system use.
652 */
653 public static final String CATEGORY_SYSTEM = "sys";
654
655 /**
656 * Notification category: indication of running background service.
657 */
658 public static final String CATEGORY_SERVICE = "service";
659
660 /**
John Spurlock0a69c8c2014-03-21 13:30:57 -0400661 * Notification category: a specific, timely recommendation for a single thing.
662 * For example, a news app might want to recommend a news story it believes the user will
663 * want to read next.
664 */
665 public static final String CATEGORY_RECOMMENDATION = "recommendation";
666
667 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400668 * Notification category: ongoing information about device or contextual status.
669 */
670 public static final String CATEGORY_STATUS = "status";
671
672 /**
John Spurlock24d3dad2015-04-02 12:24:02 -0400673 * Notification category: user-scheduled reminder.
674 */
675 public static final String CATEGORY_REMINDER = "reminder";
676
677 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400678 * One of the predefined notification categories (see the <code>CATEGORY_*</code> constants)
679 * that best describes this Notification. May be used by the system for ranking and filtering.
680 */
681 public String category;
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500682
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700683 private String mGroupKey;
684
685 /**
686 * Get the key used to group this notification into a cluster or stack
687 * with other notifications on devices which support such rendering.
688 */
689 public String getGroup() {
690 return mGroupKey;
691 }
692
693 private String mSortKey;
694
695 /**
696 * Get a sort key that orders this notification among other notifications from the
697 * same package. This can be useful if an external sort was already applied and an app
698 * would like to preserve this. Notifications will be sorted lexicographically using this
699 * value, although providing different priorities in addition to providing sort key may
700 * cause this value to be ignored.
701 *
702 * <p>This sort key can also be used to order members of a notification group. See
703 * {@link Builder#setGroup}.
704 *
705 * @see String#compareTo(String)
706 */
707 public String getSortKey() {
708 return mSortKey;
709 }
710
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500711 /**
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400712 * Additional semantic data to be carried around with this Notification.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400713 * <p>
714 * The extras keys defined here are intended to capture the original inputs to {@link Builder}
715 * APIs, and are intended to be used by
716 * {@link android.service.notification.NotificationListenerService} implementations to extract
717 * detailed information from notification objects.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500718 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400719 public Bundle extras = new Bundle();
720
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400721 /**
722 * {@link #extras} key: this is the title of the notification,
723 * as supplied to {@link Builder#setContentTitle(CharSequence)}.
724 */
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -0500725 public static final String EXTRA_TITLE = "android.title";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400726
727 /**
728 * {@link #extras} key: this is the title of the notification when shown in expanded form,
729 * e.g. as supplied to {@link BigTextStyle#setBigContentTitle(CharSequence)}.
730 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400731 public static final String EXTRA_TITLE_BIG = EXTRA_TITLE + ".big";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400732
733 /**
734 * {@link #extras} key: this is the main text payload, as supplied to
735 * {@link Builder#setContentText(CharSequence)}.
736 */
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -0500737 public static final String EXTRA_TEXT = "android.text";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400738
739 /**
740 * {@link #extras} key: this is a third line of text, as supplied to
741 * {@link Builder#setSubText(CharSequence)}.
742 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400743 public static final String EXTRA_SUB_TEXT = "android.subText";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400744
745 /**
746 * {@link #extras} key: this is a small piece of additional text as supplied to
747 * {@link Builder#setContentInfo(CharSequence)}.
748 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400749 public static final String EXTRA_INFO_TEXT = "android.infoText";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400750
751 /**
752 * {@link #extras} key: this is a line of summary information intended to be shown
753 * alongside expanded notifications, as supplied to (e.g.)
754 * {@link BigTextStyle#setSummaryText(CharSequence)}.
755 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400756 public static final String EXTRA_SUMMARY_TEXT = "android.summaryText";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400757
758 /**
Christoph Studer4600f9b2014-07-22 22:44:43 +0200759 * {@link #extras} key: this is the longer text shown in the big form of a
760 * {@link BigTextStyle} notification, as supplied to
761 * {@link BigTextStyle#bigText(CharSequence)}.
762 */
763 public static final String EXTRA_BIG_TEXT = "android.bigText";
764
765 /**
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400766 * {@link #extras} key: this is the resource ID of the notification's main small icon, as
767 * supplied to {@link Builder#setSmallIcon(int)}.
768 */
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -0500769 public static final String EXTRA_SMALL_ICON = "android.icon";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400770
771 /**
772 * {@link #extras} key: this is a bitmap to be used instead of the small icon when showing the
773 * notification payload, as
774 * supplied to {@link Builder#setLargeIcon(android.graphics.Bitmap)}.
775 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400776 public static final String EXTRA_LARGE_ICON = "android.largeIcon";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400777
778 /**
779 * {@link #extras} key: this is a bitmap to be used instead of the one from
780 * {@link Builder#setLargeIcon(android.graphics.Bitmap)} when the notification is
781 * shown in its expanded form, as supplied to
782 * {@link BigPictureStyle#bigLargeIcon(android.graphics.Bitmap)}.
783 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400784 public static final String EXTRA_LARGE_ICON_BIG = EXTRA_LARGE_ICON + ".big";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400785
786 /**
787 * {@link #extras} key: this is the progress value supplied to
788 * {@link Builder#setProgress(int, int, boolean)}.
789 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400790 public static final String EXTRA_PROGRESS = "android.progress";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400791
792 /**
793 * {@link #extras} key: this is the maximum value supplied to
794 * {@link Builder#setProgress(int, int, boolean)}.
795 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400796 public static final String EXTRA_PROGRESS_MAX = "android.progressMax";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400797
798 /**
799 * {@link #extras} key: whether the progress bar is indeterminate, supplied to
800 * {@link Builder#setProgress(int, int, boolean)}.
801 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400802 public static final String EXTRA_PROGRESS_INDETERMINATE = "android.progressIndeterminate";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400803
804 /**
805 * {@link #extras} key: whether {@link #when} should be shown as a count-up timer (specifically
806 * a {@link android.widget.Chronometer}) instead of a timestamp, as supplied to
807 * {@link Builder#setUsesChronometer(boolean)}.
808 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400809 public static final String EXTRA_SHOW_CHRONOMETER = "android.showChronometer";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400810
811 /**
812 * {@link #extras} key: whether {@link #when} should be shown,
813 * as supplied to {@link Builder#setShowWhen(boolean)}.
814 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400815 public static final String EXTRA_SHOW_WHEN = "android.showWhen";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400816
817 /**
818 * {@link #extras} key: this is a bitmap to be shown in {@link BigPictureStyle} expanded
819 * notifications, supplied to {@link BigPictureStyle#bigPicture(android.graphics.Bitmap)}.
820 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400821 public static final String EXTRA_PICTURE = "android.picture";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400822
823 /**
824 * {@link #extras} key: An array of CharSequences to show in {@link InboxStyle} expanded
825 * notifications, each of which was supplied to {@link InboxStyle#addLine(CharSequence)}.
826 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400827 public static final String EXTRA_TEXT_LINES = "android.textLines";
Dan Sandler842dd772014-05-15 09:36:47 -0400828
829 /**
830 * {@link #extras} key: A string representing the name of the specific
831 * {@link android.app.Notification.Style} used to create this notification.
832 */
Chris Wren91ad5632013-06-05 15:05:57 -0400833 public static final String EXTRA_TEMPLATE = "android.template";
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400834
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400835 /**
Chris Wrene6c48932014-09-29 17:19:27 -0400836 * {@link #extras} key: A String array containing the people that this notification relates to,
837 * each of which was supplied to {@link Builder#addPerson(String)}.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400838 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400839 public static final String EXTRA_PEOPLE = "android.people";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500840
841 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400842 * Allow certain system-generated notifications to appear before the device is provisioned.
843 * Only available to notifications coming from the android package.
844 * @hide
845 */
846 public static final String EXTRA_ALLOW_DURING_SETUP = "android.allowDuringSetup";
847
848 /**
Jose Limae9e3b3b2014-05-18 23:44:50 -0700849 * {@link #extras} key: A
850 * {@link android.content.ContentUris content URI} pointing to an image that can be displayed
851 * in the background when the notification is selected. The URI must point to an image stream
852 * suitable for passing into
853 * {@link android.graphics.BitmapFactory#decodeStream(java.io.InputStream)
854 * BitmapFactory.decodeStream}; all other content types will be ignored. The content provider
855 * URI used for this purpose must require no permissions to read the image data.
856 */
857 public static final String EXTRA_BACKGROUND_IMAGE_URI = "android.backgroundImageUri";
858
859 /**
Dan Sandler842dd772014-05-15 09:36:47 -0400860 * {@link #extras} key: A
Jeff Browndba34ba2014-06-24 20:46:03 -0700861 * {@link android.media.session.MediaSession.Token} associated with a
Dan Sandler842dd772014-05-15 09:36:47 -0400862 * {@link android.app.Notification.MediaStyle} notification.
863 */
864 public static final String EXTRA_MEDIA_SESSION = "android.mediaSession";
865
866 /**
Bryan Mawhinneye191f902014-07-22 12:50:09 +0100867 * {@link #extras} key: the indices of actions to be shown in the compact view,
868 * as supplied to (e.g.) {@link MediaStyle#setShowActionsInCompactView(int...)}.
869 */
870 public static final String EXTRA_COMPACT_ACTIONS = "android.compactActions";
871
Christoph Studer943aa672014-08-03 20:31:16 +0200872 /**
Kenny Guy8942bcd2014-09-08 21:09:47 +0100873 * {@link #extras} key: the user that built the notification.
874 *
875 * @hide
876 */
877 public static final String EXTRA_ORIGINATING_USERID = "android.originatingUserId";
878
879 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400880 * @hide
881 */
882 public static final String EXTRA_BUILDER_APPLICATION_INFO = "android.appInfo";
883
Dan Sandlerd63f9322015-05-06 15:18:49 -0400884 private Icon mSmallIcon;
885 private Icon mLargeIcon;
886
Chris Wren51c75102013-07-16 20:49:17 -0400887 /**
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400888 * Structure to encapsulate a named action that can be shown as part of this notification.
889 * It must include an icon, a label, and a {@link PendingIntent} to be fired when the action is
890 * selected by the user.
891 * <p>
Griff Hazen959591e2014-05-15 22:26:18 -0700892 * Apps should use {@link Notification.Builder#addAction(int, CharSequence, PendingIntent)}
893 * or {@link Notification.Builder#addAction(Notification.Action)}
894 * to attach actions.
Daniel Sandlera0a938c2012-03-15 08:42:37 -0400895 */
Daniel Sandlerea2a3172013-02-20 22:24:20 -0500896 public static class Action implements Parcelable {
Griff Hazen959591e2014-05-15 22:26:18 -0700897 private final Bundle mExtras;
Dan Sandler86647982015-05-13 23:41:13 -0400898 private Icon mIcon;
Griff Hazen61a9e862014-05-22 16:05:19 -0700899 private final RemoteInput[] mRemoteInputs;
Griff Hazen959591e2014-05-15 22:26:18 -0700900
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400901 /**
902 * Small icon representing the action.
Dan Sandler86647982015-05-13 23:41:13 -0400903 *
904 * @deprecated Use {@link Action#getIcon()} instead.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400905 */
Dan Sandler86647982015-05-13 23:41:13 -0400906 @Deprecated
Daniel Sandlera0a938c2012-03-15 08:42:37 -0400907 public int icon;
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700908
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400909 /**
910 * Title of the action.
911 */
Daniel Sandlera0a938c2012-03-15 08:42:37 -0400912 public CharSequence title;
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700913
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400914 /**
915 * Intent to send when the user invokes this action. May be null, in which case the action
916 * may be rendered in a disabled presentation by the system UI.
917 */
Daniel Sandlera0a938c2012-03-15 08:42:37 -0400918 public PendingIntent actionIntent;
Griff Hazen959591e2014-05-15 22:26:18 -0700919
Daniel Sandlera0a938c2012-03-15 08:42:37 -0400920 private Action(Parcel in) {
Dan Sandler86647982015-05-13 23:41:13 -0400921 if (in.readInt() != 0) {
922 mIcon = Icon.CREATOR.createFromParcel(in);
Dan Sandler68079d52015-07-22 10:45:30 -0400923 if (mIcon.getType() == Icon.TYPE_RESOURCE) {
924 icon = mIcon.getResId();
925 }
Dan Sandler86647982015-05-13 23:41:13 -0400926 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -0400927 title = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
928 if (in.readInt() == 1) {
929 actionIntent = PendingIntent.CREATOR.createFromParcel(in);
930 }
Griff Hazen959591e2014-05-15 22:26:18 -0700931 mExtras = in.readBundle();
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700932 mRemoteInputs = in.createTypedArray(RemoteInput.CREATOR);
Daniel Sandlera0a938c2012-03-15 08:42:37 -0400933 }
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700934
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400935 /**
Dan Sandler86647982015-05-13 23:41:13 -0400936 * @deprecated Use {@link android.app.Notification.Action.Builder}.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400937 */
Dan Sandler86647982015-05-13 23:41:13 -0400938 @Deprecated
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400939 public Action(int icon, CharSequence title, PendingIntent intent) {
Dan Sandler86647982015-05-13 23:41:13 -0400940 this(Icon.createWithResource("", icon), title, intent, new Bundle(), null);
Griff Hazen959591e2014-05-15 22:26:18 -0700941 }
942
Dan Sandler86647982015-05-13 23:41:13 -0400943 private Action(Icon icon, CharSequence title, PendingIntent intent, Bundle extras,
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700944 RemoteInput[] remoteInputs) {
Dan Sandler86647982015-05-13 23:41:13 -0400945 this.mIcon = icon;
Gus Prevasf5bff46f2015-08-24 10:34:28 -0400946 if (icon != null && icon.getType() == Icon.TYPE_RESOURCE) {
947 this.icon = icon.getResId();
948 }
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400949 this.title = title;
950 this.actionIntent = intent;
Griff Hazen959591e2014-05-15 22:26:18 -0700951 this.mExtras = extras != null ? extras : new Bundle();
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700952 this.mRemoteInputs = remoteInputs;
Griff Hazen959591e2014-05-15 22:26:18 -0700953 }
954
955 /**
Dan Sandler86647982015-05-13 23:41:13 -0400956 * Return an icon representing the action.
957 */
958 public Icon getIcon() {
959 if (mIcon == null && icon != 0) {
960 // you snuck an icon in here without using the builder; let's try to keep it
961 mIcon = Icon.createWithResource("", icon);
962 }
963 return mIcon;
964 }
965
966 /**
Griff Hazen959591e2014-05-15 22:26:18 -0700967 * Get additional metadata carried around with this Action.
968 */
969 public Bundle getExtras() {
970 return mExtras;
971 }
972
973 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700974 * Get the list of inputs to be collected from the user when this action is sent.
975 * May return null if no remote inputs were added.
976 */
977 public RemoteInput[] getRemoteInputs() {
978 return mRemoteInputs;
979 }
980
981 /**
Griff Hazen959591e2014-05-15 22:26:18 -0700982 * Builder class for {@link Action} objects.
983 */
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700984 public static final class Builder {
Dan Sandler86647982015-05-13 23:41:13 -0400985 private final Icon mIcon;
Griff Hazen959591e2014-05-15 22:26:18 -0700986 private final CharSequence mTitle;
987 private final PendingIntent mIntent;
988 private final Bundle mExtras;
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700989 private ArrayList<RemoteInput> mRemoteInputs;
Griff Hazen959591e2014-05-15 22:26:18 -0700990
991 /**
992 * Construct a new builder for {@link Action} object.
993 * @param icon icon to show for this action
994 * @param title the title of the action
995 * @param intent the {@link PendingIntent} to fire when users trigger this action
996 */
Dan Sandler86647982015-05-13 23:41:13 -0400997 @Deprecated
Griff Hazen959591e2014-05-15 22:26:18 -0700998 public Builder(int icon, CharSequence title, PendingIntent intent) {
Dan Sandler86647982015-05-13 23:41:13 -0400999 this(Icon.createWithResource("", icon), title, intent, new Bundle(), null);
1000 }
1001
1002 /**
1003 * Construct a new builder for {@link Action} object.
1004 * @param icon icon to show for this action
1005 * @param title the title of the action
1006 * @param intent the {@link PendingIntent} to fire when users trigger this action
1007 */
1008 public Builder(Icon icon, CharSequence title, PendingIntent intent) {
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001009 this(icon, title, intent, new Bundle(), null);
Griff Hazen959591e2014-05-15 22:26:18 -07001010 }
1011
1012 /**
1013 * Construct a new builder for {@link Action} object using the fields from an
1014 * {@link Action}.
1015 * @param action the action to read fields from.
1016 */
1017 public Builder(Action action) {
Dan Sandler86647982015-05-13 23:41:13 -04001018 this(action.getIcon(), action.title, action.actionIntent, new Bundle(action.mExtras),
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001019 action.getRemoteInputs());
Griff Hazen959591e2014-05-15 22:26:18 -07001020 }
1021
Dan Sandler86647982015-05-13 23:41:13 -04001022 private Builder(Icon icon, CharSequence title, PendingIntent intent, Bundle extras,
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001023 RemoteInput[] remoteInputs) {
Griff Hazen959591e2014-05-15 22:26:18 -07001024 mIcon = icon;
1025 mTitle = title;
1026 mIntent = intent;
1027 mExtras = extras;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001028 if (remoteInputs != null) {
1029 mRemoteInputs = new ArrayList<RemoteInput>(remoteInputs.length);
1030 Collections.addAll(mRemoteInputs, remoteInputs);
1031 }
Griff Hazen959591e2014-05-15 22:26:18 -07001032 }
1033
1034 /**
1035 * Merge additional metadata into this builder.
1036 *
1037 * <p>Values within the Bundle will replace existing extras values in this Builder.
1038 *
1039 * @see Notification.Action#extras
1040 */
1041 public Builder addExtras(Bundle extras) {
1042 if (extras != null) {
1043 mExtras.putAll(extras);
1044 }
1045 return this;
1046 }
1047
1048 /**
1049 * Get the metadata Bundle used by this Builder.
1050 *
1051 * <p>The returned Bundle is shared with this Builder.
1052 */
1053 public Bundle getExtras() {
1054 return mExtras;
1055 }
1056
1057 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001058 * Add an input to be collected from the user when this action is sent.
1059 * Response values can be retrieved from the fired intent by using the
1060 * {@link RemoteInput#getResultsFromIntent} function.
1061 * @param remoteInput a {@link RemoteInput} to add to the action
1062 * @return this object for method chaining
1063 */
1064 public Builder addRemoteInput(RemoteInput remoteInput) {
1065 if (mRemoteInputs == null) {
1066 mRemoteInputs = new ArrayList<RemoteInput>();
1067 }
1068 mRemoteInputs.add(remoteInput);
1069 return this;
1070 }
1071
1072 /**
1073 * Apply an extender to this action builder. Extenders may be used to add
1074 * metadata or change options on this builder.
1075 */
Griff Hazen61a9e862014-05-22 16:05:19 -07001076 public Builder extend(Extender extender) {
1077 extender.extend(this);
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001078 return this;
1079 }
1080
1081 /**
Griff Hazen959591e2014-05-15 22:26:18 -07001082 * Combine all of the options that have been set and return a new {@link Action}
1083 * object.
1084 * @return the built action
1085 */
1086 public Action build() {
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001087 RemoteInput[] remoteInputs = mRemoteInputs != null
1088 ? mRemoteInputs.toArray(new RemoteInput[mRemoteInputs.size()]) : null;
1089 return new Action(mIcon, mTitle, mIntent, mExtras, remoteInputs);
Griff Hazen959591e2014-05-15 22:26:18 -07001090 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001091 }
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001092
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001093 @Override
1094 public Action clone() {
1095 return new Action(
Dan Sandler86647982015-05-13 23:41:13 -04001096 getIcon(),
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001097 title,
1098 actionIntent, // safe to alias
1099 new Bundle(mExtras),
1100 getRemoteInputs());
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001101 }
1102 @Override
1103 public int describeContents() {
1104 return 0;
1105 }
1106 @Override
1107 public void writeToParcel(Parcel out, int flags) {
Dan Sandler86647982015-05-13 23:41:13 -04001108 final Icon ic = getIcon();
1109 if (ic != null) {
1110 out.writeInt(1);
1111 ic.writeToParcel(out, 0);
1112 } else {
1113 out.writeInt(0);
1114 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001115 TextUtils.writeToParcel(title, out, flags);
1116 if (actionIntent != null) {
1117 out.writeInt(1);
1118 actionIntent.writeToParcel(out, flags);
1119 } else {
1120 out.writeInt(0);
1121 }
Griff Hazen959591e2014-05-15 22:26:18 -07001122 out.writeBundle(mExtras);
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001123 out.writeTypedArray(mRemoteInputs, flags);
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001124 }
Griff Hazen959591e2014-05-15 22:26:18 -07001125 public static final Parcelable.Creator<Action> CREATOR =
1126 new Parcelable.Creator<Action>() {
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001127 public Action createFromParcel(Parcel in) {
1128 return new Action(in);
1129 }
1130 public Action[] newArray(int size) {
1131 return new Action[size];
1132 }
1133 };
Griff Hazen61a9e862014-05-22 16:05:19 -07001134
1135 /**
1136 * Extender interface for use with {@link Builder#extend}. Extenders may be used to add
1137 * metadata or change options on an action builder.
1138 */
1139 public interface Extender {
1140 /**
1141 * Apply this extender to a notification action builder.
1142 * @param builder the builder to be modified.
1143 * @return the build object for chaining.
1144 */
1145 public Builder extend(Builder builder);
1146 }
1147
1148 /**
1149 * Wearable extender for notification actions. To add extensions to an action,
1150 * create a new {@link android.app.Notification.Action.WearableExtender} object using
1151 * the {@code WearableExtender()} constructor and apply it to a
1152 * {@link android.app.Notification.Action.Builder} using
1153 * {@link android.app.Notification.Action.Builder#extend}.
1154 *
1155 * <pre class="prettyprint">
1156 * Notification.Action action = new Notification.Action.Builder(
1157 * R.drawable.archive_all, "Archive all", actionIntent)
Griff Hazen14f57992014-05-26 09:07:14 -07001158 * .extend(new Notification.Action.WearableExtender()
Griff Hazen61a9e862014-05-22 16:05:19 -07001159 * .setAvailableOffline(false))
Griff Hazen14f57992014-05-26 09:07:14 -07001160 * .build();</pre>
Griff Hazen61a9e862014-05-22 16:05:19 -07001161 */
1162 public static final class WearableExtender implements Extender {
1163 /** Notification action extra which contains wearable extensions */
1164 private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS";
1165
Pete Gastaf6781d2014-10-07 15:17:05 -04001166 // Keys within EXTRA_WEARABLE_EXTENSIONS for wearable options.
Griff Hazen61a9e862014-05-22 16:05:19 -07001167 private static final String KEY_FLAGS = "flags";
Pete Gastaf6781d2014-10-07 15:17:05 -04001168 private static final String KEY_IN_PROGRESS_LABEL = "inProgressLabel";
1169 private static final String KEY_CONFIRM_LABEL = "confirmLabel";
1170 private static final String KEY_CANCEL_LABEL = "cancelLabel";
Griff Hazen61a9e862014-05-22 16:05:19 -07001171
1172 // Flags bitwise-ored to mFlags
1173 private static final int FLAG_AVAILABLE_OFFLINE = 0x1;
1174
1175 // Default value for flags integer
1176 private static final int DEFAULT_FLAGS = FLAG_AVAILABLE_OFFLINE;
1177
1178 private int mFlags = DEFAULT_FLAGS;
1179
Pete Gastaf6781d2014-10-07 15:17:05 -04001180 private CharSequence mInProgressLabel;
1181 private CharSequence mConfirmLabel;
1182 private CharSequence mCancelLabel;
1183
Griff Hazen61a9e862014-05-22 16:05:19 -07001184 /**
1185 * Create a {@link android.app.Notification.Action.WearableExtender} with default
1186 * options.
1187 */
1188 public WearableExtender() {
1189 }
1190
1191 /**
1192 * Create a {@link android.app.Notification.Action.WearableExtender} by reading
1193 * wearable options present in an existing notification action.
1194 * @param action the notification action to inspect.
1195 */
1196 public WearableExtender(Action action) {
1197 Bundle wearableBundle = action.getExtras().getBundle(EXTRA_WEARABLE_EXTENSIONS);
1198 if (wearableBundle != null) {
1199 mFlags = wearableBundle.getInt(KEY_FLAGS, DEFAULT_FLAGS);
Pete Gastaf6781d2014-10-07 15:17:05 -04001200 mInProgressLabel = wearableBundle.getCharSequence(KEY_IN_PROGRESS_LABEL);
1201 mConfirmLabel = wearableBundle.getCharSequence(KEY_CONFIRM_LABEL);
1202 mCancelLabel = wearableBundle.getCharSequence(KEY_CANCEL_LABEL);
Griff Hazen61a9e862014-05-22 16:05:19 -07001203 }
1204 }
1205
1206 /**
1207 * Apply wearable extensions to a notification action that is being built. This is
1208 * typically called by the {@link android.app.Notification.Action.Builder#extend}
1209 * method of {@link android.app.Notification.Action.Builder}.
1210 */
1211 @Override
1212 public Action.Builder extend(Action.Builder builder) {
1213 Bundle wearableBundle = new Bundle();
1214
1215 if (mFlags != DEFAULT_FLAGS) {
1216 wearableBundle.putInt(KEY_FLAGS, mFlags);
1217 }
Pete Gastaf6781d2014-10-07 15:17:05 -04001218 if (mInProgressLabel != null) {
1219 wearableBundle.putCharSequence(KEY_IN_PROGRESS_LABEL, mInProgressLabel);
1220 }
1221 if (mConfirmLabel != null) {
1222 wearableBundle.putCharSequence(KEY_CONFIRM_LABEL, mConfirmLabel);
1223 }
1224 if (mCancelLabel != null) {
1225 wearableBundle.putCharSequence(KEY_CANCEL_LABEL, mCancelLabel);
1226 }
Griff Hazen61a9e862014-05-22 16:05:19 -07001227
1228 builder.getExtras().putBundle(EXTRA_WEARABLE_EXTENSIONS, wearableBundle);
1229 return builder;
1230 }
1231
1232 @Override
1233 public WearableExtender clone() {
1234 WearableExtender that = new WearableExtender();
1235 that.mFlags = this.mFlags;
Pete Gastaf6781d2014-10-07 15:17:05 -04001236 that.mInProgressLabel = this.mInProgressLabel;
1237 that.mConfirmLabel = this.mConfirmLabel;
1238 that.mCancelLabel = this.mCancelLabel;
Griff Hazen61a9e862014-05-22 16:05:19 -07001239 return that;
1240 }
1241
1242 /**
1243 * Set whether this action is available when the wearable device is not connected to
1244 * a companion device. The user can still trigger this action when the wearable device is
1245 * offline, but a visual hint will indicate that the action may not be available.
1246 * Defaults to true.
1247 */
1248 public WearableExtender setAvailableOffline(boolean availableOffline) {
1249 setFlag(FLAG_AVAILABLE_OFFLINE, availableOffline);
1250 return this;
1251 }
1252
1253 /**
1254 * Get whether this action is available when the wearable device is not connected to
1255 * a companion device. The user can still trigger this action when the wearable device is
1256 * offline, but a visual hint will indicate that the action may not be available.
1257 * Defaults to true.
1258 */
1259 public boolean isAvailableOffline() {
1260 return (mFlags & FLAG_AVAILABLE_OFFLINE) != 0;
1261 }
1262
1263 private void setFlag(int mask, boolean value) {
1264 if (value) {
1265 mFlags |= mask;
1266 } else {
1267 mFlags &= ~mask;
1268 }
1269 }
Pete Gastaf6781d2014-10-07 15:17:05 -04001270
1271 /**
1272 * Set a label to display while the wearable is preparing to automatically execute the
1273 * action. This is usually a 'ing' verb ending in ellipsis like "Sending..."
1274 *
1275 * @param label the label to display while the action is being prepared to execute
1276 * @return this object for method chaining
1277 */
1278 public WearableExtender setInProgressLabel(CharSequence label) {
1279 mInProgressLabel = label;
1280 return this;
1281 }
1282
1283 /**
1284 * Get the label to display while the wearable is preparing to automatically execute
1285 * the action. This is usually a 'ing' verb ending in ellipsis like "Sending..."
1286 *
1287 * @return the label to display while the action is being prepared to execute
1288 */
1289 public CharSequence getInProgressLabel() {
1290 return mInProgressLabel;
1291 }
1292
1293 /**
1294 * Set a label to display to confirm that the action should be executed.
1295 * This is usually an imperative verb like "Send".
1296 *
1297 * @param label the label to confirm the action should be executed
1298 * @return this object for method chaining
1299 */
1300 public WearableExtender setConfirmLabel(CharSequence label) {
1301 mConfirmLabel = label;
1302 return this;
1303 }
1304
1305 /**
1306 * Get the label to display to confirm that the action should be executed.
1307 * This is usually an imperative verb like "Send".
1308 *
1309 * @return the label to confirm the action should be executed
1310 */
1311 public CharSequence getConfirmLabel() {
1312 return mConfirmLabel;
1313 }
1314
1315 /**
1316 * Set a label to display to cancel the action.
1317 * This is usually an imperative verb, like "Cancel".
1318 *
1319 * @param label the label to display to cancel the action
1320 * @return this object for method chaining
1321 */
1322 public WearableExtender setCancelLabel(CharSequence label) {
1323 mCancelLabel = label;
1324 return this;
1325 }
1326
1327 /**
1328 * Get the label to display to cancel the action.
1329 * This is usually an imperative verb like "Cancel".
1330 *
1331 * @return the label to display to cancel the action
1332 */
1333 public CharSequence getCancelLabel() {
1334 return mCancelLabel;
1335 }
Griff Hazen61a9e862014-05-22 16:05:19 -07001336 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001337 }
1338
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001339 /**
1340 * Array of all {@link Action} structures attached to this notification by
1341 * {@link Builder#addAction(int, CharSequence, PendingIntent)}. Mostly useful for instances of
1342 * {@link android.service.notification.NotificationListenerService} that provide an alternative
1343 * interface for invoking actions.
1344 */
Daniel Sandlerea2a3172013-02-20 22:24:20 -05001345 public Action[] actions;
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001346
1347 /**
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001348 * Replacement version of this notification whose content will be shown
1349 * in an insecure context such as atop a secure keyguard. See {@link #visibility}
1350 * and {@link #VISIBILITY_PUBLIC}.
1351 */
1352 public Notification publicVersion;
1353
1354 /**
Julia Reynolds74303cf2015-10-16 11:37:55 -04001355 * Structure to encapsulate a topic that is shown in Notification settings.
1356 * It must include an id and label.
1357 */
1358 public static class Topic implements Parcelable {
1359 private final String id;
1360 private final CharSequence label;
1361
1362 public Topic(String id, CharSequence label) {
1363 this.id = id;
1364 this.label = safeCharSequence(label);
1365 }
1366
1367 private Topic(Parcel in) {
1368 if (in.readInt() != 0) {
1369 id = in.readString();
1370 } else {
1371 id = null;
1372 }
1373 label = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
1374 }
1375
1376 public String getId() {
1377 return id;
1378 }
1379
1380 public CharSequence getLabel() {
1381 return label;
1382 }
1383
1384 @Override
1385 public String toString() {
1386 return new StringBuilder(Topic.class.getSimpleName()).append('[')
1387 .append("id=").append(id)
1388 .append(",label=").append(label)
1389 .append(']').toString();
1390 }
1391
1392 @Override
1393 public boolean equals(Object o) {
1394 if (!(o instanceof Topic)) return false;
1395 if (o == this) return true;
1396 final Topic other = (Topic) o;
1397 return Objects.equals(other.id, id)
1398 && Objects.equals(other.label, label);
1399 }
1400
1401 @Override
1402 public int hashCode() {
1403 return Objects.hash(id, label);
1404 }
1405
1406 @Override
1407 public Topic clone() {
1408 return new Topic(id, label);
1409 }
1410
1411 @Override
1412 public int describeContents() {
1413 return 0;
1414 }
1415
1416 @Override
1417 public void writeToParcel(Parcel out, int flags) {
1418 if (id != null) {
1419 out.writeInt(1);
1420 out.writeString(id);
1421 } else {
1422 out.writeInt(0);
1423 }
1424 TextUtils.writeToParcel(label, out, flags);
1425 }
1426 public static final Parcelable.Creator<Topic> CREATOR =
1427 new Parcelable.Creator<Topic>() {
1428 public Topic createFromParcel(Parcel in) {
1429 return new Topic(in);
1430 }
1431 public Topic[] newArray(int size) {
1432 return new Topic[size];
1433 }
1434 };
1435 }
1436
Julia Reynolds233a5f92015-10-19 13:51:23 -04001437 @SystemApi
1438 public static final String TOPIC_DEFAULT = "system_default_topic";
1439
Julia Reynolds054c5dc2015-11-17 15:36:30 -05001440 private Topic topic;
Julia Reynolds74303cf2015-10-16 11:37:55 -04001441
Julia Reynolds054c5dc2015-11-17 15:36:30 -05001442 public Topic getTopic() {
1443 return topic;
Julia Reynolds74303cf2015-10-16 11:37:55 -04001444 }
1445
1446 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001447 * Constructs a Notification object with default values.
Joe Onorato46439ce2010-11-19 13:56:21 -08001448 * You might want to consider using {@link Builder} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001449 */
1450 public Notification()
1451 {
1452 this.when = System.currentTimeMillis();
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001453 this.priority = PRIORITY_DEFAULT;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001454 }
1455
1456 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001457 * @hide
1458 */
1459 public Notification(Context context, int icon, CharSequence tickerText, long when,
1460 CharSequence contentTitle, CharSequence contentText, Intent contentIntent)
1461 {
Chris Wren1ce4b6d2015-06-11 10:19:43 -04001462 new Builder(context)
1463 .setWhen(when)
1464 .setSmallIcon(icon)
1465 .setTicker(tickerText)
1466 .setContentTitle(contentTitle)
1467 .setContentText(contentText)
1468 .setContentIntent(PendingIntent.getActivity(context, 0, contentIntent, 0))
1469 .buildInto(this);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001470 }
1471
1472 /**
1473 * Constructs a Notification object with the information needed to
1474 * have a status bar icon without the standard expanded view.
1475 *
1476 * @param icon The resource id of the icon to put in the status bar.
1477 * @param tickerText The text that flows by in the status bar when the notification first
1478 * activates.
1479 * @param when The time to show in the time field. In the System.currentTimeMillis
1480 * timebase.
Joe Onorato46439ce2010-11-19 13:56:21 -08001481 *
1482 * @deprecated Use {@link Builder} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001483 */
Joe Onorato46439ce2010-11-19 13:56:21 -08001484 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001485 public Notification(int icon, CharSequence tickerText, long when)
1486 {
1487 this.icon = icon;
1488 this.tickerText = tickerText;
1489 this.when = when;
1490 }
1491
1492 /**
1493 * Unflatten the notification from a parcel.
1494 */
1495 public Notification(Parcel parcel)
1496 {
1497 int version = parcel.readInt();
1498
1499 when = parcel.readLong();
Dan Sandler3936e7a2015-05-19 20:59:12 -04001500 if (parcel.readInt() != 0) {
1501 mSmallIcon = Icon.CREATOR.createFromParcel(parcel);
Dan Sandler4e787062015-06-17 15:09:48 -04001502 if (mSmallIcon.getType() == Icon.TYPE_RESOURCE) {
1503 icon = mSmallIcon.getResId();
1504 }
Dan Sandler3936e7a2015-05-19 20:59:12 -04001505 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001506 number = parcel.readInt();
1507 if (parcel.readInt() != 0) {
1508 contentIntent = PendingIntent.CREATOR.createFromParcel(parcel);
1509 }
1510 if (parcel.readInt() != 0) {
1511 deleteIntent = PendingIntent.CREATOR.createFromParcel(parcel);
1512 }
1513 if (parcel.readInt() != 0) {
1514 tickerText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
1515 }
1516 if (parcel.readInt() != 0) {
Joe Onorato46439ce2010-11-19 13:56:21 -08001517 tickerView = RemoteViews.CREATOR.createFromParcel(parcel);
Joe Onoratoef1e7762010-09-17 18:38:38 -04001518 }
1519 if (parcel.readInt() != 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001520 contentView = RemoteViews.CREATOR.createFromParcel(parcel);
1521 }
Joe Onorato561d3852010-11-20 18:09:34 -08001522 if (parcel.readInt() != 0) {
Dan Sandlerd63f9322015-05-06 15:18:49 -04001523 mLargeIcon = Icon.CREATOR.createFromParcel(parcel);
Joe Onorato561d3852010-11-20 18:09:34 -08001524 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001525 defaults = parcel.readInt();
1526 flags = parcel.readInt();
1527 if (parcel.readInt() != 0) {
1528 sound = Uri.CREATOR.createFromParcel(parcel);
1529 }
1530
1531 audioStreamType = parcel.readInt();
John Spurlockc0650f022014-07-19 13:22:39 -04001532 if (parcel.readInt() != 0) {
1533 audioAttributes = AudioAttributes.CREATOR.createFromParcel(parcel);
1534 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001535 vibrate = parcel.createLongArray();
1536 ledARGB = parcel.readInt();
1537 ledOnMS = parcel.readInt();
1538 ledOffMS = parcel.readInt();
1539 iconLevel = parcel.readInt();
Daniel Sandlere46cbd32010-06-17 10:35:26 -04001540
1541 if (parcel.readInt() != 0) {
1542 fullScreenIntent = PendingIntent.CREATOR.createFromParcel(parcel);
1543 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001544
1545 priority = parcel.readInt();
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001546
John Spurlockfd7f1e02014-03-18 16:41:57 -04001547 category = parcel.readString();
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001548
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001549 mGroupKey = parcel.readString();
1550
1551 mSortKey = parcel.readString();
1552
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001553 extras = parcel.readBundle(); // may be null
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001554
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001555 actions = parcel.createTypedArray(Action.CREATOR); // may be null
1556
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001557 if (parcel.readInt() != 0) {
Daniel Sandlerf3b73432012-03-27 15:01:25 -04001558 bigContentView = RemoteViews.CREATOR.createFromParcel(parcel);
1559 }
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001560
Chris Wren8fd39ec2014-02-27 17:43:26 -05001561 if (parcel.readInt() != 0) {
1562 headsUpContentView = RemoteViews.CREATOR.createFromParcel(parcel);
1563 }
1564
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001565 visibility = parcel.readInt();
1566
1567 if (parcel.readInt() != 0) {
1568 publicVersion = Notification.CREATOR.createFromParcel(parcel);
1569 }
Dan Sandler26e81cf2014-05-06 10:01:27 -04001570
1571 color = parcel.readInt();
Julia Reynolds74303cf2015-10-16 11:37:55 -04001572
Julia Reynolds054c5dc2015-11-17 15:36:30 -05001573 if (parcel.readInt() != 0) {
1574 topic = Topic.CREATOR.createFromParcel(parcel);
1575 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001576 }
1577
Andy Stadler110988c2010-12-03 14:29:16 -08001578 @Override
Joe Onorato18e69df2010-05-17 22:26:12 -07001579 public Notification clone() {
1580 Notification that = new Notification();
Daniel Sandler1a497d32013-04-18 14:52:45 -04001581 cloneInto(that, true);
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001582 return that;
1583 }
Joe Onorato18e69df2010-05-17 22:26:12 -07001584
Daniel Sandler1a497d32013-04-18 14:52:45 -04001585 /**
1586 * Copy all (or if heavy is false, all except Bitmaps and RemoteViews) members
1587 * of this into that.
1588 * @hide
1589 */
1590 public void cloneInto(Notification that, boolean heavy) {
Joe Onorato18e69df2010-05-17 22:26:12 -07001591 that.when = this.when;
Dan Sandlerd63f9322015-05-06 15:18:49 -04001592 that.mSmallIcon = this.mSmallIcon;
Joe Onorato18e69df2010-05-17 22:26:12 -07001593 that.number = this.number;
1594
1595 // PendingIntents are global, so there's no reason (or way) to clone them.
1596 that.contentIntent = this.contentIntent;
1597 that.deleteIntent = this.deleteIntent;
Daniel Sandlere46cbd32010-06-17 10:35:26 -04001598 that.fullScreenIntent = this.fullScreenIntent;
Joe Onorato18e69df2010-05-17 22:26:12 -07001599
1600 if (this.tickerText != null) {
1601 that.tickerText = this.tickerText.toString();
1602 }
Daniel Sandler1a497d32013-04-18 14:52:45 -04001603 if (heavy && this.tickerView != null) {
Joe Onorato46439ce2010-11-19 13:56:21 -08001604 that.tickerView = this.tickerView.clone();
Joe Onoratoef1e7762010-09-17 18:38:38 -04001605 }
Daniel Sandler1a497d32013-04-18 14:52:45 -04001606 if (heavy && this.contentView != null) {
Joe Onorato18e69df2010-05-17 22:26:12 -07001607 that.contentView = this.contentView.clone();
1608 }
Dan Sandlerd63f9322015-05-06 15:18:49 -04001609 if (heavy && this.mLargeIcon != null) {
1610 that.mLargeIcon = this.mLargeIcon;
Joe Onorato561d3852010-11-20 18:09:34 -08001611 }
Jozef BABJAKa8b91832011-02-22 08:05:08 +01001612 that.iconLevel = this.iconLevel;
Joe Onorato18e69df2010-05-17 22:26:12 -07001613 that.sound = this.sound; // android.net.Uri is immutable
1614 that.audioStreamType = this.audioStreamType;
John Spurlockc0650f022014-07-19 13:22:39 -04001615 if (this.audioAttributes != null) {
1616 that.audioAttributes = new AudioAttributes.Builder(this.audioAttributes).build();
1617 }
Joe Onorato18e69df2010-05-17 22:26:12 -07001618
1619 final long[] vibrate = this.vibrate;
1620 if (vibrate != null) {
1621 final int N = vibrate.length;
1622 final long[] vib = that.vibrate = new long[N];
1623 System.arraycopy(vibrate, 0, vib, 0, N);
1624 }
1625
1626 that.ledARGB = this.ledARGB;
1627 that.ledOnMS = this.ledOnMS;
1628 that.ledOffMS = this.ledOffMS;
1629 that.defaults = this.defaults;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001630
Joe Onorato18e69df2010-05-17 22:26:12 -07001631 that.flags = this.flags;
1632
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001633 that.priority = this.priority;
Joe Malin8d40d042012-11-05 11:36:40 -08001634
John Spurlockfd7f1e02014-03-18 16:41:57 -04001635 that.category = this.category;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001636
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001637 that.mGroupKey = this.mGroupKey;
1638
1639 that.mSortKey = this.mSortKey;
1640
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001641 if (this.extras != null) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04001642 try {
1643 that.extras = new Bundle(this.extras);
1644 // will unparcel
1645 that.extras.size();
1646 } catch (BadParcelableException e) {
1647 Log.e(TAG, "could not unparcel extras from notification: " + this, e);
1648 that.extras = null;
1649 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001650 }
1651
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001652 if (this.actions != null) {
1653 that.actions = new Action[this.actions.length];
1654 for(int i=0; i<this.actions.length; i++) {
1655 that.actions[i] = this.actions[i].clone();
1656 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001657 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001658
Daniel Sandler1a497d32013-04-18 14:52:45 -04001659 if (heavy && this.bigContentView != null) {
Daniel Sandlerf3b73432012-03-27 15:01:25 -04001660 that.bigContentView = this.bigContentView.clone();
1661 }
Daniel Sandler1a497d32013-04-18 14:52:45 -04001662
Chris Wren8fd39ec2014-02-27 17:43:26 -05001663 if (heavy && this.headsUpContentView != null) {
1664 that.headsUpContentView = this.headsUpContentView.clone();
1665 }
1666
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001667 that.visibility = this.visibility;
1668
1669 if (this.publicVersion != null) {
1670 that.publicVersion = new Notification();
1671 this.publicVersion.cloneInto(that.publicVersion, heavy);
1672 }
1673
Dan Sandler26e81cf2014-05-06 10:01:27 -04001674 that.color = this.color;
1675
Julia Reynolds054c5dc2015-11-17 15:36:30 -05001676 if (this.topic != null) {
1677 that.topic = this.topic.clone();
Julia Reynolds74303cf2015-10-16 11:37:55 -04001678 }
1679
Daniel Sandler1a497d32013-04-18 14:52:45 -04001680 if (!heavy) {
1681 that.lightenPayload(); // will clean out extras
1682 }
1683 }
1684
1685 /**
1686 * Removes heavyweight parts of the Notification object for archival or for sending to
1687 * listeners when the full contents are not necessary.
1688 * @hide
1689 */
1690 public final void lightenPayload() {
1691 tickerView = null;
1692 contentView = null;
1693 bigContentView = null;
Chris Wren8fd39ec2014-02-27 17:43:26 -05001694 headsUpContentView = null;
Dan Sandlerd63f9322015-05-06 15:18:49 -04001695 mLargeIcon = null;
Daniel Sandler1a497d32013-04-18 14:52:45 -04001696 if (extras != null) {
1697 extras.remove(Notification.EXTRA_LARGE_ICON);
1698 extras.remove(Notification.EXTRA_LARGE_ICON_BIG);
1699 extras.remove(Notification.EXTRA_PICTURE);
Christoph Studer223f44e2014-09-02 14:59:32 +02001700 extras.remove(Notification.EXTRA_BIG_TEXT);
Daniel Sandler1a497d32013-04-18 14:52:45 -04001701 }
Joe Onorato18e69df2010-05-17 22:26:12 -07001702 }
1703
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04001704 /**
1705 * Make sure this CharSequence is safe to put into a bundle, which basically
1706 * means it had better not be some custom Parcelable implementation.
1707 * @hide
1708 */
1709 public static CharSequence safeCharSequence(CharSequence cs) {
Christoph Studer535ec612014-09-03 15:47:47 +02001710 if (cs == null) return cs;
1711 if (cs.length() > MAX_CHARSEQUENCE_LENGTH) {
1712 cs = cs.subSequence(0, MAX_CHARSEQUENCE_LENGTH);
1713 }
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04001714 if (cs instanceof Parcelable) {
1715 Log.e(TAG, "warning: " + cs.getClass().getCanonicalName()
1716 + " instance is a custom Parcelable and not allowed in Notification");
1717 return cs.toString();
1718 }
1719
1720 return cs;
1721 }
1722
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001723 public int describeContents() {
1724 return 0;
1725 }
1726
1727 /**
Dan Sandler4e787062015-06-17 15:09:48 -04001728 * Flatten this notification into a parcel.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001729 */
1730 public void writeToParcel(Parcel parcel, int flags)
1731 {
1732 parcel.writeInt(1);
1733
1734 parcel.writeLong(when);
Dan Sandler4e787062015-06-17 15:09:48 -04001735 if (mSmallIcon == null && icon != 0) {
1736 // you snuck an icon in here without using the builder; let's try to keep it
1737 mSmallIcon = Icon.createWithResource("", icon);
1738 }
Dan Sandler3936e7a2015-05-19 20:59:12 -04001739 if (mSmallIcon != null) {
1740 parcel.writeInt(1);
1741 mSmallIcon.writeToParcel(parcel, 0);
1742 } else {
1743 parcel.writeInt(0);
1744 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001745 parcel.writeInt(number);
1746 if (contentIntent != null) {
1747 parcel.writeInt(1);
1748 contentIntent.writeToParcel(parcel, 0);
1749 } else {
1750 parcel.writeInt(0);
1751 }
1752 if (deleteIntent != null) {
1753 parcel.writeInt(1);
1754 deleteIntent.writeToParcel(parcel, 0);
1755 } else {
1756 parcel.writeInt(0);
1757 }
1758 if (tickerText != null) {
1759 parcel.writeInt(1);
1760 TextUtils.writeToParcel(tickerText, parcel, flags);
1761 } else {
1762 parcel.writeInt(0);
1763 }
Joe Onorato46439ce2010-11-19 13:56:21 -08001764 if (tickerView != null) {
Joe Onoratoef1e7762010-09-17 18:38:38 -04001765 parcel.writeInt(1);
Joe Onorato46439ce2010-11-19 13:56:21 -08001766 tickerView.writeToParcel(parcel, 0);
Joe Onoratoef1e7762010-09-17 18:38:38 -04001767 } else {
1768 parcel.writeInt(0);
1769 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001770 if (contentView != null) {
1771 parcel.writeInt(1);
1772 contentView.writeToParcel(parcel, 0);
1773 } else {
1774 parcel.writeInt(0);
1775 }
Dan Sandlerd63f9322015-05-06 15:18:49 -04001776 if (mLargeIcon != null) {
Joe Onorato561d3852010-11-20 18:09:34 -08001777 parcel.writeInt(1);
Dan Sandlerd63f9322015-05-06 15:18:49 -04001778 mLargeIcon.writeToParcel(parcel, 0);
Joe Onorato561d3852010-11-20 18:09:34 -08001779 } else {
1780 parcel.writeInt(0);
1781 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001782
1783 parcel.writeInt(defaults);
1784 parcel.writeInt(this.flags);
1785
1786 if (sound != null) {
1787 parcel.writeInt(1);
1788 sound.writeToParcel(parcel, 0);
1789 } else {
1790 parcel.writeInt(0);
1791 }
1792 parcel.writeInt(audioStreamType);
John Spurlockc0650f022014-07-19 13:22:39 -04001793
1794 if (audioAttributes != null) {
1795 parcel.writeInt(1);
1796 audioAttributes.writeToParcel(parcel, 0);
1797 } else {
1798 parcel.writeInt(0);
1799 }
1800
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001801 parcel.writeLongArray(vibrate);
1802 parcel.writeInt(ledARGB);
1803 parcel.writeInt(ledOnMS);
1804 parcel.writeInt(ledOffMS);
1805 parcel.writeInt(iconLevel);
Daniel Sandlere46cbd32010-06-17 10:35:26 -04001806
1807 if (fullScreenIntent != null) {
1808 parcel.writeInt(1);
1809 fullScreenIntent.writeToParcel(parcel, 0);
1810 } else {
1811 parcel.writeInt(0);
1812 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001813
1814 parcel.writeInt(priority);
Joe Malin8d40d042012-11-05 11:36:40 -08001815
John Spurlockfd7f1e02014-03-18 16:41:57 -04001816 parcel.writeString(category);
Joe Malin8d40d042012-11-05 11:36:40 -08001817
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001818 parcel.writeString(mGroupKey);
1819
1820 parcel.writeString(mSortKey);
1821
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001822 parcel.writeBundle(extras); // null ok
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001823
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001824 parcel.writeTypedArray(actions, 0); // null ok
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001825
Daniel Sandlerf3b73432012-03-27 15:01:25 -04001826 if (bigContentView != null) {
1827 parcel.writeInt(1);
1828 bigContentView.writeToParcel(parcel, 0);
1829 } else {
1830 parcel.writeInt(0);
1831 }
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001832
Chris Wren8fd39ec2014-02-27 17:43:26 -05001833 if (headsUpContentView != null) {
1834 parcel.writeInt(1);
1835 headsUpContentView.writeToParcel(parcel, 0);
1836 } else {
1837 parcel.writeInt(0);
1838 }
1839
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001840 parcel.writeInt(visibility);
1841
1842 if (publicVersion != null) {
1843 parcel.writeInt(1);
1844 publicVersion.writeToParcel(parcel, 0);
1845 } else {
1846 parcel.writeInt(0);
1847 }
Dan Sandler26e81cf2014-05-06 10:01:27 -04001848
1849 parcel.writeInt(color);
Julia Reynolds74303cf2015-10-16 11:37:55 -04001850
Julia Reynolds054c5dc2015-11-17 15:36:30 -05001851 if (topic != null) {
1852 parcel.writeInt(1);
1853 topic.writeToParcel(parcel, 0);
1854 } else {
1855 parcel.writeInt(0);
1856 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001857 }
1858
1859 /**
1860 * Parcelable.Creator that instantiates Notification objects
1861 */
1862 public static final Parcelable.Creator<Notification> CREATOR
1863 = new Parcelable.Creator<Notification>()
1864 {
1865 public Notification createFromParcel(Parcel parcel)
1866 {
1867 return new Notification(parcel);
1868 }
1869
1870 public Notification[] newArray(int size)
1871 {
1872 return new Notification[size];
1873 }
1874 };
1875
1876 /**
1877 * Sets the {@link #contentView} field to be a view with the standard "Latest Event"
1878 * layout.
1879 *
1880 * <p>Uses the {@link #icon} and {@link #when} fields to set the icon and time fields
1881 * in the view.</p>
1882 * @param context The context for your application / activity.
1883 * @param contentTitle The title that goes in the expanded entry.
1884 * @param contentText The text that goes in the expanded entry.
1885 * @param contentIntent The intent to launch when the user clicks the expanded notification.
1886 * If this is an activity, it must include the
1887 * {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} flag, which requires
Scott Main7aee61f2011-02-08 11:25:01 -08001888 * that you take care of task management as described in the
1889 * <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back
1890 * Stack</a> document.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001891 *
Joe Onorato46439ce2010-11-19 13:56:21 -08001892 * @deprecated Use {@link Builder} instead.
Chris Wrena05db382015-06-24 15:18:34 -04001893 * @removed
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001894 */
Joe Onorato46439ce2010-11-19 13:56:21 -08001895 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001896 public void setLatestEventInfo(Context context,
1897 CharSequence contentTitle, CharSequence contentText, PendingIntent contentIntent) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04001898 if (context.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1){
1899 Log.e(TAG, "setLatestEventInfo() is deprecated and you should feel deprecated.",
1900 new Throwable());
1901 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001902
Julia Reynoldsd9228f12015-10-20 10:37:27 -04001903 // ensure that any information already set directly is preserved
1904 final Notification.Builder builder = new Notification.Builder(context, this);
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001905
1906 // now apply the latestEventInfo fields
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001907 if (contentTitle != null) {
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001908 builder.setContentTitle(contentTitle);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001909 }
1910 if (contentText != null) {
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001911 builder.setContentText(contentText);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001912 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001913 builder.setContentIntent(contentIntent);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04001914
1915 builder.build(); // callers expect this notification to be ready to use
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001916 }
1917
Julia Reynoldsda303542015-11-23 14:00:20 -05001918 /**
1919 * @hide
1920 */
1921 public static void addFieldsFromContext(Context context, Notification notification) {
1922 if (notification.extras.getParcelable(EXTRA_BUILDER_APPLICATION_INFO) == null) {
1923 notification.extras.putParcelable(EXTRA_BUILDER_APPLICATION_INFO,
1924 context.getApplicationInfo());
1925 }
1926 if (!notification.extras.containsKey(EXTRA_ORIGINATING_USERID)) {
1927 notification.extras.putInt(EXTRA_ORIGINATING_USERID, context.getUserId());
1928 }
1929 }
1930
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001931 @Override
1932 public String toString() {
1933 StringBuilder sb = new StringBuilder();
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001934 sb.append("Notification(pri=");
1935 sb.append(priority);
1936 sb.append(" contentView=");
Joe Onoratoc9596d62011-01-12 17:03:11 -08001937 if (contentView != null) {
1938 sb.append(contentView.getPackage());
1939 sb.append("/0x");
1940 sb.append(Integer.toHexString(contentView.getLayoutId()));
1941 } else {
1942 sb.append("null");
1943 }
1944 sb.append(" vibrate=");
Daniel Sandler6738eee2012-11-16 12:03:32 -05001945 if ((this.defaults & DEFAULT_VIBRATE) != 0) {
1946 sb.append("default");
1947 } else if (this.vibrate != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001948 int N = this.vibrate.length-1;
1949 sb.append("[");
1950 for (int i=0; i<N; i++) {
1951 sb.append(this.vibrate[i]);
1952 sb.append(',');
1953 }
Simon Schoar8cf97d92009-06-10 22:08:37 +02001954 if (N != -1) {
1955 sb.append(this.vibrate[N]);
1956 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001957 sb.append("]");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001958 } else {
1959 sb.append("null");
1960 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001961 sb.append(" sound=");
Daniel Sandler6738eee2012-11-16 12:03:32 -05001962 if ((this.defaults & DEFAULT_SOUND) != 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001963 sb.append("default");
Daniel Sandler6738eee2012-11-16 12:03:32 -05001964 } else if (this.sound != null) {
1965 sb.append(this.sound.toString());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001966 } else {
1967 sb.append("null");
1968 }
Chris Wren365b6d32015-07-16 10:39:26 -04001969 if (this.tickerText != null) {
1970 sb.append(" tick");
1971 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001972 sb.append(" defaults=0x");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001973 sb.append(Integer.toHexString(this.defaults));
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001974 sb.append(" flags=0x");
Daniel Sandlere46cbd32010-06-17 10:35:26 -04001975 sb.append(Integer.toHexString(this.flags));
Dan Sandler26e81cf2014-05-06 10:01:27 -04001976 sb.append(String.format(" color=0x%08x", this.color));
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001977 if (this.category != null) {
1978 sb.append(" category=");
1979 sb.append(this.category);
1980 }
1981 if (this.mGroupKey != null) {
1982 sb.append(" groupKey=");
1983 sb.append(this.mGroupKey);
1984 }
1985 if (this.mSortKey != null) {
1986 sb.append(" sortKey=");
1987 sb.append(this.mSortKey);
1988 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001989 if (actions != null) {
Dan Sandler1b718782014-07-18 12:43:45 -04001990 sb.append(" actions=");
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001991 sb.append(actions.length);
Dan Sandler1b718782014-07-18 12:43:45 -04001992 }
1993 sb.append(" vis=");
1994 sb.append(visibilityToString(this.visibility));
1995 if (this.publicVersion != null) {
1996 sb.append(" publicVersion=");
1997 sb.append(publicVersion.toString());
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001998 }
Julia Reynolds054c5dc2015-11-17 15:36:30 -05001999 if (topic != null) {
2000 sb.append("topic=");
2001 sb.append(topic.toString());
Julia Reynolds74303cf2015-10-16 11:37:55 -04002002 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002003 sb.append(")");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002004 return sb.toString();
2005 }
Joe Onorato46439ce2010-11-19 13:56:21 -08002006
Dan Sandler1b718782014-07-18 12:43:45 -04002007 /**
2008 * {@hide}
2009 */
2010 public static String visibilityToString(int vis) {
2011 switch (vis) {
2012 case VISIBILITY_PRIVATE:
2013 return "PRIVATE";
2014 case VISIBILITY_PUBLIC:
2015 return "PUBLIC";
2016 case VISIBILITY_SECRET:
2017 return "SECRET";
2018 default:
2019 return "UNKNOWN(" + String.valueOf(vis) + ")";
2020 }
2021 }
2022
Joe Onoratocb109a02011-01-18 17:57:41 -08002023 /**
John Spurlock1d881a12015-03-18 19:21:54 -04002024 * {@hide}
2025 */
2026 public static String priorityToString(@Priority int pri) {
2027 switch (pri) {
2028 case PRIORITY_MIN:
2029 return "MIN";
2030 case PRIORITY_LOW:
2031 return "LOW";
2032 case PRIORITY_DEFAULT:
2033 return "DEFAULT";
2034 case PRIORITY_HIGH:
2035 return "HIGH";
2036 case PRIORITY_MAX:
2037 return "MAX";
2038 default:
2039 return "UNKNOWN(" + String.valueOf(pri) + ")";
2040 }
2041 }
2042
2043 /**
Dan Sandlerd63f9322015-05-06 15:18:49 -04002044 * The small icon representing this notification in the status bar and content view.
2045 *
2046 * @return the small icon representing this notification.
2047 *
2048 * @see Builder#getSmallIcon()
2049 * @see Builder#setSmallIcon(Icon)
2050 */
2051 public Icon getSmallIcon() {
2052 return mSmallIcon;
2053 }
2054
2055 /**
2056 * Used when notifying to clean up legacy small icons.
2057 * @hide
2058 */
2059 public void setSmallIcon(Icon icon) {
2060 mSmallIcon = icon;
2061 }
2062
2063 /**
2064 * The large icon shown in this notification's content view.
2065 * @see Builder#getLargeIcon()
2066 * @see Builder#setLargeIcon(Icon)
2067 */
2068 public Icon getLargeIcon() {
2069 return mLargeIcon;
2070 }
2071
2072 /**
Christoph Studer4600f9b2014-07-22 22:44:43 +02002073 * @hide
2074 */
Christoph Studerc8db24b2014-07-25 17:50:30 +02002075 public boolean isGroupSummary() {
2076 return mGroupKey != null && (flags & FLAG_GROUP_SUMMARY) != 0;
2077 }
2078
2079 /**
2080 * @hide
2081 */
2082 public boolean isGroupChild() {
2083 return mGroupKey != null && (flags & FLAG_GROUP_SUMMARY) == 0;
2084 }
2085
2086 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002087 * Builder class for {@link Notification} objects.
Joe Malin8d40d042012-11-05 11:36:40 -08002088 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002089 * Provides a convenient way to set the various fields of a {@link Notification} and generate
Scott Main183bf112012-08-13 19:12:13 -07002090 * content views using the platform's notification layout template. If your app supports
2091 * versions of Android as old as API level 4, you can instead use
2092 * {@link android.support.v4.app.NotificationCompat.Builder NotificationCompat.Builder},
2093 * available in the <a href="{@docRoot}tools/extras/support-library.html">Android Support
2094 * library</a>.
Joe Malin8d40d042012-11-05 11:36:40 -08002095 *
Scott Main183bf112012-08-13 19:12:13 -07002096 * <p>Example:
Joe Malin8d40d042012-11-05 11:36:40 -08002097 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002098 * <pre class="prettyprint">
Scott Main183bf112012-08-13 19:12:13 -07002099 * Notification noti = new Notification.Builder(mContext)
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002100 * .setContentTitle(&quot;New mail from &quot; + sender.toString())
2101 * .setContentText(subject)
2102 * .setSmallIcon(R.drawable.new_mail)
2103 * .setLargeIcon(aBitmap)
Chris Wrenfbd96ba2012-05-01 12:03:58 -04002104 * .build();
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002105 * </pre>
Joe Onoratocb109a02011-01-18 17:57:41 -08002106 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002107 public static class Builder {
Daniel Sandler602ad1c2012-06-12 16:06:27 -04002108 private static final int MAX_ACTION_BUTTONS = 3;
Jorim Jaggi445d3c02014-08-19 22:33:42 +02002109 private static final float LARGE_TEXT_SCALE = 1.3f;
Daniel Sandler8680bf82012-05-15 16:52:52 -04002110
Joe Onorato46439ce2010-11-19 13:56:21 -08002111 private Context mContext;
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002112 private Notification mN;
2113 private Bundle mUserExtras = new Bundle();
Chris Wrenfbd96ba2012-05-01 12:03:58 -04002114 private Style mStyle;
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002115 private ArrayList<Action> mActions = new ArrayList<Action>(MAX_ACTION_BUTTONS);
2116 private ArrayList<String> mPersonList = new ArrayList<String>();
2117 private NotificationColorUtil mColorUtil;
2118 private boolean mColorUtilInited = false;
Christoph Studer7ac80e62014-08-04 16:01:57 +02002119
2120 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002121 * Constructs a new Builder with the defaults:
Joe Onoratocb109a02011-01-18 17:57:41 -08002122 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002123
2124 * <table>
2125 * <tr><th align=right>priority</th>
2126 * <td>{@link #PRIORITY_DEFAULT}</td></tr>
2127 * <tr><th align=right>when</th>
2128 * <td>now ({@link System#currentTimeMillis()})</td></tr>
2129 * <tr><th align=right>audio stream</th>
2130 * <td>{@link #STREAM_DEFAULT}</td></tr>
2131 * </table>
Joe Onoratocb109a02011-01-18 17:57:41 -08002132 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002133
2134 * @param context
2135 * A {@link Context} that will be used by the Builder to construct the
2136 * RemoteViews. The Context will not be held past the lifetime of this Builder
2137 * object.
Joe Onoratocb109a02011-01-18 17:57:41 -08002138 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002139 public Builder(Context context) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002140 this(context, null);
Joe Onorato46439ce2010-11-19 13:56:21 -08002141 }
2142
Joe Onoratocb109a02011-01-18 17:57:41 -08002143 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002144 * @hide
Christoph Studer4600f9b2014-07-22 22:44:43 +02002145 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002146 public Builder(Context context, Notification toAdopt) {
2147 mContext = context;
Christoph Studer4600f9b2014-07-22 22:44:43 +02002148
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002149 if (toAdopt == null) {
2150 mN = new Notification();
2151 mN.extras.putBoolean(EXTRA_SHOW_WHEN, true);
2152 mN.priority = PRIORITY_DEFAULT;
2153 mN.visibility = VISIBILITY_PRIVATE;
2154 } else {
2155 mN = toAdopt;
2156 if (mN.actions != null) {
2157 Collections.addAll(mActions, mN.actions);
Christoph Studer4600f9b2014-07-22 22:44:43 +02002158 }
2159
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002160 if (mN.extras.containsKey(EXTRA_PEOPLE)) {
2161 Collections.addAll(mPersonList, mN.extras.getStringArray(EXTRA_PEOPLE));
2162 }
2163
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002164 String templateClass = mN.extras.getString(EXTRA_TEMPLATE);
2165 if (!TextUtils.isEmpty(templateClass)) {
2166 final Class<? extends Style> styleClass
2167 = getNotificationStyleClass(templateClass);
2168 if (styleClass == null) {
2169 Log.d(TAG, "Unknown style class: " + templateClass);
2170 } else {
2171 try {
2172 final Constructor<? extends Style> ctor = styleClass.getConstructor();
2173 ctor.setAccessible(true);
2174 final Style style = ctor.newInstance();
2175 style.restoreFromExtras(mN.extras);
2176
2177 if (style != null) {
2178 setStyle(style);
2179 }
2180 } catch (Throwable t) {
2181 Log.e(TAG, "Could not create Style", t);
2182 }
2183 }
2184 }
2185
2186 }
2187 }
2188
2189 private NotificationColorUtil getColorUtil() {
2190 if (!mColorUtilInited) {
2191 mColorUtilInited = true;
2192 if (mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.LOLLIPOP) {
2193 mColorUtil = NotificationColorUtil.getInstance(mContext);
Christoph Studer4600f9b2014-07-22 22:44:43 +02002194 }
2195 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002196 return mColorUtil;
Christoph Studer4600f9b2014-07-22 22:44:43 +02002197 }
2198
2199 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002200 * Add a timestamp pertaining to the notification (usually the time the event occurred).
Daniel Sandler0c890492012-09-12 17:23:10 -07002201 * It will be shown in the notification content view by default; use
Griff Hazen50c11652014-05-16 09:46:31 -07002202 * {@link #setShowWhen(boolean) setShowWhen} to control this.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002203 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002204 * @see Notification#when
Joe Onoratocb109a02011-01-18 17:57:41 -08002205 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002206 public Builder setWhen(long when) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002207 mN.when = when;
Joe Onorato46439ce2010-11-19 13:56:21 -08002208 return this;
2209 }
2210
Joe Onoratocb109a02011-01-18 17:57:41 -08002211 /**
Griff Hazen50c11652014-05-16 09:46:31 -07002212 * Control whether the timestamp set with {@link #setWhen(long) setWhen} is shown
Daniel Sandler0c890492012-09-12 17:23:10 -07002213 * in the content view.
2214 */
2215 public Builder setShowWhen(boolean show) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002216 mN.extras.putBoolean(EXTRA_SHOW_WHEN, show);
Daniel Sandler0c890492012-09-12 17:23:10 -07002217 return this;
2218 }
2219
2220 /**
Daniel Sandlerd33b8032012-05-10 11:41:48 -04002221 * Show the {@link Notification#when} field as a stopwatch.
Joe Malin8d40d042012-11-05 11:36:40 -08002222 *
2223 * Instead of presenting <code>when</code> as a timestamp, the notification will show an
Daniel Sandlerd33b8032012-05-10 11:41:48 -04002224 * automatically updating display of the minutes and seconds since <code>when</code>.
Daniel Sandlera2985ed2012-04-03 16:42:00 -04002225 *
Daniel Sandlerd33b8032012-05-10 11:41:48 -04002226 * Useful when showing an elapsed time (like an ongoing phone call).
2227 *
2228 * @see android.widget.Chronometer
Daniel Sandlera2985ed2012-04-03 16:42:00 -04002229 * @see Notification#when
2230 */
2231 public Builder setUsesChronometer(boolean b) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002232 mN.extras.putBoolean(EXTRA_SHOW_CHRONOMETER, b);
Daniel Sandlera2985ed2012-04-03 16:42:00 -04002233 return this;
2234 }
2235
2236 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002237 * Set the small icon resource, which will be used to represent the notification in the
2238 * status bar.
Joe Onoratocb109a02011-01-18 17:57:41 -08002239 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002240
2241 * The platform template for the expanded view will draw this icon in the left, unless a
2242 * {@link #setLargeIcon(Bitmap) large icon} has also been specified, in which case the small
2243 * icon will be moved to the right-hand side.
2244 *
2245
2246 * @param icon
2247 * A resource ID in the application's package of the drawable to use.
2248 * @see Notification#icon
Joe Onoratocb109a02011-01-18 17:57:41 -08002249 */
Tor Norbye7b9c9122013-05-30 16:48:33 -07002250 public Builder setSmallIcon(@DrawableRes int icon) {
Dan Sandlerd63f9322015-05-06 15:18:49 -04002251 return setSmallIcon(icon != 0
2252 ? Icon.createWithResource(mContext, icon)
2253 : null);
Joe Onorato46439ce2010-11-19 13:56:21 -08002254 }
2255
Joe Onoratocb109a02011-01-18 17:57:41 -08002256 /**
2257 * A variant of {@link #setSmallIcon(int) setSmallIcon(int)} that takes an additional
2258 * level parameter for when the icon is a {@link android.graphics.drawable.LevelListDrawable
2259 * LevelListDrawable}.
2260 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002261 * @param icon A resource ID in the application's package of the drawable to use.
Joe Onoratocb109a02011-01-18 17:57:41 -08002262 * @param level The level to use for the icon.
2263 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002264 * @see Notification#icon
2265 * @see Notification#iconLevel
Joe Onoratocb109a02011-01-18 17:57:41 -08002266 */
Tor Norbye7b9c9122013-05-30 16:48:33 -07002267 public Builder setSmallIcon(@DrawableRes int icon, int level) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002268 mN.iconLevel = level;
Dan Sandlerd63f9322015-05-06 15:18:49 -04002269 return setSmallIcon(icon);
2270 }
2271
2272 /**
2273 * Set the small icon, which will be used to represent the notification in the
2274 * status bar and content view (unless overriden there by a
2275 * {@link #setLargeIcon(Bitmap) large icon}).
2276 *
2277 * @param icon An Icon object to use.
2278 * @see Notification#icon
2279 */
2280 public Builder setSmallIcon(Icon icon) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002281 mN.setSmallIcon(icon);
2282 if (icon != null && icon.getType() == Icon.TYPE_RESOURCE) {
2283 mN.icon = icon.getResId();
2284 }
Joe Onorato46439ce2010-11-19 13:56:21 -08002285 return this;
2286 }
2287
Joe Onoratocb109a02011-01-18 17:57:41 -08002288 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002289 * Set the first line of text in the platform notification template.
Joe Onoratocb109a02011-01-18 17:57:41 -08002290 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002291 public Builder setContentTitle(CharSequence title) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002292 mN.extras.putCharSequence(EXTRA_TITLE, safeCharSequence(title));
Joe Onorato46439ce2010-11-19 13:56:21 -08002293 return this;
2294 }
2295
Joe Onoratocb109a02011-01-18 17:57:41 -08002296 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002297 * Set the second line of text in the platform notification template.
Joe Onoratocb109a02011-01-18 17:57:41 -08002298 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002299 public Builder setContentText(CharSequence text) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002300 mN.extras.putCharSequence(EXTRA_TEXT, safeCharSequence(text));
Joe Onorato46439ce2010-11-19 13:56:21 -08002301 return this;
2302 }
2303
Joe Onoratocb109a02011-01-18 17:57:41 -08002304 /**
Joe Malin8d40d042012-11-05 11:36:40 -08002305 * Set the third line of text in the platform notification template.
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04002306 * Don't use if you're also using {@link #setProgress(int, int, boolean)}; they occupy the
2307 * same location in the standard template.
Daniel Sandlerf3b73432012-03-27 15:01:25 -04002308 */
2309 public Builder setSubText(CharSequence text) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002310 mN.extras.putCharSequence(EXTRA_SUB_TEXT, safeCharSequence(text));
Daniel Sandlerf3b73432012-03-27 15:01:25 -04002311 return this;
2312 }
2313
2314 /**
Joe Onoratocb109a02011-01-18 17:57:41 -08002315 * Set the large number at the right-hand side of the notification. This is
2316 * equivalent to setContentInfo, although it might show the number in a different
2317 * font size for readability.
2318 */
Joe Onorato8595a3d2010-11-19 18:12:07 -08002319 public Builder setNumber(int number) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002320 mN.number = number;
Joe Onorato8595a3d2010-11-19 18:12:07 -08002321 return this;
2322 }
2323
Joe Onoratocb109a02011-01-18 17:57:41 -08002324 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002325 * A small piece of additional information pertaining to this notification.
2326 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002327 * The platform template will draw this on the last line of the notification, at the far
2328 * right (to the right of a smallIcon if it has been placed there).
Joe Onoratocb109a02011-01-18 17:57:41 -08002329 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002330 public Builder setContentInfo(CharSequence info) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002331 mN.extras.putCharSequence(EXTRA_INFO_TEXT, safeCharSequence(info));
Joe Onorato46439ce2010-11-19 13:56:21 -08002332 return this;
2333 }
2334
Joe Onoratocb109a02011-01-18 17:57:41 -08002335 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002336 * Set the progress this notification represents.
2337 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002338 * The platform template will represent this using a {@link ProgressBar}.
Jeff Sharkey1c400132011-08-05 14:50:13 -07002339 */
2340 public Builder setProgress(int max, int progress, boolean indeterminate) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002341 mN.extras.putInt(EXTRA_PROGRESS, progress);
2342 mN.extras.putInt(EXTRA_PROGRESS_MAX, max);
2343 mN.extras.putBoolean(EXTRA_PROGRESS_INDETERMINATE, indeterminate);
Jeff Sharkey1c400132011-08-05 14:50:13 -07002344 return this;
2345 }
2346
2347 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002348 * Supply a custom RemoteViews to use instead of the platform template.
2349 *
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002350 * Use {@link #setCustomContentView(RemoteViews)} instead.
Joe Onoratocb109a02011-01-18 17:57:41 -08002351 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002352 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08002353 public Builder setContent(RemoteViews views) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002354 return setCustomContentView(views);
2355 }
2356
2357 /**
2358 * Supply custom RemoteViews to use instead of the platform template.
2359 *
2360 * This will override the layout that would otherwise be constructed by this Builder
2361 * object.
2362 */
2363 public Builder setCustomContentView(RemoteViews contentView) {
2364 mN.contentView = contentView;
2365 return this;
2366 }
2367
2368 /**
2369 * Supply custom RemoteViews to use instead of the platform template in the expanded form.
2370 *
2371 * This will override the expanded layout that would otherwise be constructed by this
2372 * Builder object.
2373 */
2374 public Builder setCustomBigContentView(RemoteViews contentView) {
2375 mN.bigContentView = contentView;
2376 return this;
2377 }
2378
2379 /**
2380 * Supply custom RemoteViews to use instead of the platform template in the heads up dialog.
2381 *
2382 * This will override the heads-up layout that would otherwise be constructed by this
2383 * Builder object.
2384 */
2385 public Builder setCustomHeadsUpContentView(RemoteViews contentView) {
2386 mN.headsUpContentView = contentView;
Joe Onorato46439ce2010-11-19 13:56:21 -08002387 return this;
2388 }
2389
Joe Onoratocb109a02011-01-18 17:57:41 -08002390 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002391 * Supply a {@link PendingIntent} to be sent when the notification is clicked.
2392 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002393 * As of {@link android.os.Build.VERSION_CODES#HONEYCOMB}, if this field is unset and you
2394 * have specified a custom RemoteViews with {@link #setContent(RemoteViews)}, you can use
2395 * {@link RemoteViews#setOnClickPendingIntent RemoteViews.setOnClickPendingIntent(int,PendingIntent)}
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002396 * to assign PendingIntents to individual views in that custom layout (i.e., to create
Daniel Sandlerf3b73432012-03-27 15:01:25 -04002397 * clickable buttons inside the notification view).
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002398 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002399 * @see Notification#contentIntent Notification.contentIntent
Joe Onoratocb109a02011-01-18 17:57:41 -08002400 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002401 public Builder setContentIntent(PendingIntent intent) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002402 mN.contentIntent = intent;
Joe Onorato46439ce2010-11-19 13:56:21 -08002403 return this;
2404 }
2405
Joe Onoratocb109a02011-01-18 17:57:41 -08002406 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002407 * Supply a {@link PendingIntent} to send when the notification is cleared explicitly by the user.
2408 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002409 * @see Notification#deleteIntent
Joe Onoratocb109a02011-01-18 17:57:41 -08002410 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002411 public Builder setDeleteIntent(PendingIntent intent) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002412 mN.deleteIntent = intent;
Joe Onorato46439ce2010-11-19 13:56:21 -08002413 return this;
2414 }
2415
Joe Onoratocb109a02011-01-18 17:57:41 -08002416 /**
2417 * An intent to launch instead of posting the notification to the status bar.
2418 * Only for use with extremely high-priority notifications demanding the user's
2419 * <strong>immediate</strong> attention, such as an incoming phone call or
2420 * alarm clock that the user has explicitly set to a particular time.
2421 * If this facility is used for something else, please give the user an option
2422 * to turn it off and use a normal notification, as this can be extremely
2423 * disruptive.
2424 *
Chris Wren47c20a12014-06-18 17:27:29 -04002425 * <p>
2426 * The system UI may choose to display a heads-up notification, instead of
2427 * launching this intent, while the user is using the device.
2428 * </p>
2429 *
Joe Onoratocb109a02011-01-18 17:57:41 -08002430 * @param intent The pending intent to launch.
2431 * @param highPriority Passing true will cause this notification to be sent
2432 * even if other notifications are suppressed.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002433 *
2434 * @see Notification#fullScreenIntent
Joe Onoratocb109a02011-01-18 17:57:41 -08002435 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002436 public Builder setFullScreenIntent(PendingIntent intent, boolean highPriority) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002437 mN.fullScreenIntent = intent;
Joe Onorato46439ce2010-11-19 13:56:21 -08002438 setFlag(FLAG_HIGH_PRIORITY, highPriority);
2439 return this;
2440 }
2441
Joe Onoratocb109a02011-01-18 17:57:41 -08002442 /**
Dan Sandler5fcdf6e2014-07-18 11:31:15 -04002443 * Set the "ticker" text which is sent to accessibility services.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002444 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002445 * @see Notification#tickerText
Joe Onoratocb109a02011-01-18 17:57:41 -08002446 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002447 public Builder setTicker(CharSequence tickerText) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002448 mN.tickerText = safeCharSequence(tickerText);
Joe Onorato46439ce2010-11-19 13:56:21 -08002449 return this;
2450 }
2451
Joe Onoratocb109a02011-01-18 17:57:41 -08002452 /**
Dan Sandler5fcdf6e2014-07-18 11:31:15 -04002453 * Obsolete version of {@link #setTicker(CharSequence)}.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002454 *
Joe Onoratocb109a02011-01-18 17:57:41 -08002455 */
Dan Sandler5fcdf6e2014-07-18 11:31:15 -04002456 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08002457 public Builder setTicker(CharSequence tickerText, RemoteViews views) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002458 setTicker(tickerText);
2459 // views is ignored
Joe Onorato46439ce2010-11-19 13:56:21 -08002460 return this;
2461 }
2462
Joe Onoratocb109a02011-01-18 17:57:41 -08002463 /**
Dan Sandlerd63f9322015-05-06 15:18:49 -04002464 * Add a large icon to the notification content view.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002465 *
2466 * In the platform template, this image will be shown on the left of the notification view
Dan Sandlerd63f9322015-05-06 15:18:49 -04002467 * in place of the {@link #setSmallIcon(Icon) small icon} (which will be placed in a small
2468 * badge atop the large icon).
Dan Sandler08a04c12015-05-06 15:18:49 -04002469 */
Dan Sandlerd63f9322015-05-06 15:18:49 -04002470 public Builder setLargeIcon(Bitmap b) {
2471 return setLargeIcon(b != null ? Icon.createWithBitmap(b) : null);
2472 }
2473
2474 /**
2475 * Add a large icon to the notification content view.
2476 *
2477 * In the platform template, this image will be shown on the left of the notification view
2478 * in place of the {@link #setSmallIcon(Icon) small icon} (which will be placed in a small
2479 * badge atop the large icon).
2480 */
2481 public Builder setLargeIcon(Icon icon) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002482 mN.mLargeIcon = icon;
2483 mN.extras.putParcelable(EXTRA_LARGE_ICON, icon);
Joe Onorato46439ce2010-11-19 13:56:21 -08002484 return this;
2485 }
2486
Joe Onoratocb109a02011-01-18 17:57:41 -08002487 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002488 * Set the sound to play.
2489 *
John Spurlockc0650f022014-07-19 13:22:39 -04002490 * It will be played using the {@link #AUDIO_ATTRIBUTES_DEFAULT default audio attributes}
2491 * for notifications.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002492 *
Chris Wren47c20a12014-06-18 17:27:29 -04002493 * <p>
2494 * A notification that is noisy is more likely to be presented as a heads-up notification.
2495 * </p>
2496 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002497 * @see Notification#sound
Joe Onoratocb109a02011-01-18 17:57:41 -08002498 */
Joe Onorato52f80cd2010-11-21 15:34:48 -08002499 public Builder setSound(Uri sound) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002500 mN.sound = sound;
2501 mN.audioAttributes = AUDIO_ATTRIBUTES_DEFAULT;
Joe Onorato52f80cd2010-11-21 15:34:48 -08002502 return this;
2503 }
2504
Joe Onoratocb109a02011-01-18 17:57:41 -08002505 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002506 * Set the sound to play, along with a specific stream on which to play it.
Joe Onoratocb109a02011-01-18 17:57:41 -08002507 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002508 * See {@link android.media.AudioManager} for the <code>STREAM_</code> constants.
2509 *
Chris Wren47c20a12014-06-18 17:27:29 -04002510 * <p>
2511 * A notification that is noisy is more likely to be presented as a heads-up notification.
2512 * </p>
John Spurlockc0650f022014-07-19 13:22:39 -04002513 * @deprecated use {@link #setSound(Uri, AudioAttributes)} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002514 * @see Notification#sound
Joe Onoratocb109a02011-01-18 17:57:41 -08002515 */
Jean-Michel Trivi81f871e2014-08-06 16:32:38 -07002516 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08002517 public Builder setSound(Uri sound, int streamType) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002518 mN.sound = sound;
2519 mN.audioStreamType = streamType;
Joe Onorato46439ce2010-11-19 13:56:21 -08002520 return this;
2521 }
2522
Joe Onoratocb109a02011-01-18 17:57:41 -08002523 /**
John Spurlockc0650f022014-07-19 13:22:39 -04002524 * Set the sound to play, along with specific {@link AudioAttributes audio attributes} to
2525 * use during playback.
2526 *
2527 * <p>
2528 * A notification that is noisy is more likely to be presented as a heads-up notification.
2529 * </p>
2530 *
2531 * @see Notification#sound
2532 */
2533 public Builder setSound(Uri sound, AudioAttributes audioAttributes) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002534 mN.sound = sound;
2535 mN.audioAttributes = audioAttributes;
John Spurlockc0650f022014-07-19 13:22:39 -04002536 return this;
2537 }
2538
2539 /**
Joe Onoratocb109a02011-01-18 17:57:41 -08002540 * Set the vibration pattern to use.
2541 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002542 * See {@link android.os.Vibrator#vibrate(long[], int)} for a discussion of the
2543 * <code>pattern</code> parameter.
2544 *
Chris Wren47c20a12014-06-18 17:27:29 -04002545 * <p>
2546 * A notification that vibrates is more likely to be presented as a heads-up notification.
2547 * </p>
2548 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002549 * @see Notification#vibrate
Joe Onoratocb109a02011-01-18 17:57:41 -08002550 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002551 public Builder setVibrate(long[] pattern) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002552 mN.vibrate = pattern;
Joe Onorato46439ce2010-11-19 13:56:21 -08002553 return this;
2554 }
2555
Joe Onoratocb109a02011-01-18 17:57:41 -08002556 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002557 * Set the desired color for the indicator LED on the device, as well as the
2558 * blink duty cycle (specified in milliseconds).
2559 *
2560
2561 * Not all devices will honor all (or even any) of these values.
2562 *
2563
2564 * @see Notification#ledARGB
2565 * @see Notification#ledOnMS
2566 * @see Notification#ledOffMS
Joe Onoratocb109a02011-01-18 17:57:41 -08002567 */
Tor Norbye80756e32015-03-02 09:39:27 -08002568 public Builder setLights(@ColorInt int argb, int onMs, int offMs) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002569 mN.ledARGB = argb;
2570 mN.ledOnMS = onMs;
2571 mN.ledOffMS = offMs;
Julia Reynolds10ee1fc2015-11-09 11:04:55 -05002572 if (onMs != 0 || offMs != 0) {
2573 mN.flags |= FLAG_SHOW_LIGHTS;
2574 }
2575 if ((mN.defaults & DEFAULT_LIGHTS) != 0) {
2576 mN.flags |= FLAG_SHOW_LIGHTS;
2577 }
Joe Onorato46439ce2010-11-19 13:56:21 -08002578 return this;
2579 }
2580
Joe Onoratocb109a02011-01-18 17:57:41 -08002581 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002582 * Set whether this is an "ongoing" notification.
Joe Onoratocb109a02011-01-18 17:57:41 -08002583 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002584
2585 * Ongoing notifications cannot be dismissed by the user, so your application or service
2586 * must take care of canceling them.
2587 *
2588
2589 * They are typically used to indicate a background task that the user is actively engaged
2590 * with (e.g., playing music) or is pending in some way and therefore occupying the device
2591 * (e.g., a file download, sync operation, active network connection).
2592 *
2593
2594 * @see Notification#FLAG_ONGOING_EVENT
2595 * @see Service#setForeground(boolean)
Joe Onoratocb109a02011-01-18 17:57:41 -08002596 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002597 public Builder setOngoing(boolean ongoing) {
2598 setFlag(FLAG_ONGOING_EVENT, ongoing);
2599 return this;
2600 }
2601
Joe Onoratocb109a02011-01-18 17:57:41 -08002602 /**
2603 * Set this flag if you would only like the sound, vibrate
2604 * and ticker to be played if the notification is not already showing.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002605 *
2606 * @see Notification#FLAG_ONLY_ALERT_ONCE
Joe Onoratocb109a02011-01-18 17:57:41 -08002607 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002608 public Builder setOnlyAlertOnce(boolean onlyAlertOnce) {
2609 setFlag(FLAG_ONLY_ALERT_ONCE, onlyAlertOnce);
2610 return this;
2611 }
2612
Joe Onoratocb109a02011-01-18 17:57:41 -08002613 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002614 * Make this notification automatically dismissed when the user touches it. The
2615 * PendingIntent set with {@link #setDeleteIntent} will be sent when this happens.
2616 *
2617 * @see Notification#FLAG_AUTO_CANCEL
Joe Onoratocb109a02011-01-18 17:57:41 -08002618 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002619 public Builder setAutoCancel(boolean autoCancel) {
Joe Onorato281d83f2011-01-04 17:13:10 -08002620 setFlag(FLAG_AUTO_CANCEL, autoCancel);
Joe Onorato46439ce2010-11-19 13:56:21 -08002621 return this;
2622 }
2623
Joe Onoratocb109a02011-01-18 17:57:41 -08002624 /**
Griff Hazendfcb0802014-02-11 12:00:00 -08002625 * Set whether or not this notification should not bridge to other devices.
2626 *
2627 * <p>Some notifications can be bridged to other devices for remote display.
2628 * This hint can be set to recommend this notification not be bridged.
2629 */
2630 public Builder setLocalOnly(boolean localOnly) {
2631 setFlag(FLAG_LOCAL_ONLY, localOnly);
2632 return this;
2633 }
2634
2635 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002636 * Set which notification properties will be inherited from system defaults.
Joe Onoratocb109a02011-01-18 17:57:41 -08002637 * <p>
2638 * The value should be one or more of the following fields combined with
2639 * bitwise-or:
2640 * {@link #DEFAULT_SOUND}, {@link #DEFAULT_VIBRATE}, {@link #DEFAULT_LIGHTS}.
2641 * <p>
2642 * For all default values, use {@link #DEFAULT_ALL}.
2643 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002644 public Builder setDefaults(int defaults) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002645 mN.defaults = defaults;
Joe Onorato46439ce2010-11-19 13:56:21 -08002646 return this;
2647 }
2648
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002649 /**
2650 * Set the priority of this notification.
2651 *
2652 * @see Notification#priority
2653 */
Tor Norbyed9273d62013-05-30 15:59:53 -07002654 public Builder setPriority(@Priority int pri) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002655 mN.priority = pri;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002656 return this;
2657 }
Joe Malin8d40d042012-11-05 11:36:40 -08002658
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002659 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -04002660 * Set the notification category.
Joe Malin8d40d042012-11-05 11:36:40 -08002661 *
John Spurlockfd7f1e02014-03-18 16:41:57 -04002662 * @see Notification#category
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002663 */
John Spurlockfd7f1e02014-03-18 16:41:57 -04002664 public Builder setCategory(String category) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002665 mN.category = category;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002666 return this;
2667 }
2668
2669 /**
Chris Wrendde75302014-03-26 17:24:15 -04002670 * Add a person that is relevant to this notification.
2671 *
Chris Wrene6c48932014-09-29 17:19:27 -04002672 * <P>
2673 * Depending on user preferences, this annotation may allow the notification to pass
2674 * through interruption filters, and to appear more prominently in the user interface.
2675 * </P>
2676 *
2677 * <P>
2678 * The person should be specified by the {@code String} representation of a
2679 * {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI}.
2680 * </P>
2681 *
2682 * <P>The system will also attempt to resolve {@code mailto:} and {@code tel:} schema
2683 * URIs. The path part of these URIs must exist in the contacts database, in the
2684 * appropriate column, or the reference will be discarded as invalid. Telephone schema
2685 * URIs will be resolved by {@link android.provider.ContactsContract.PhoneLookup}.
2686 * </P>
2687 *
2688 * @param uri A URI for the person.
Chris Wrendde75302014-03-26 17:24:15 -04002689 * @see Notification#EXTRA_PEOPLE
2690 */
Chris Wrene6c48932014-09-29 17:19:27 -04002691 public Builder addPerson(String uri) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002692 mPersonList.add(uri);
Chris Wrendde75302014-03-26 17:24:15 -04002693 return this;
2694 }
2695
2696 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002697 * Set this notification to be part of a group of notifications sharing the same key.
2698 * Grouped notifications may display in a cluster or stack on devices which
2699 * support such rendering.
2700 *
2701 * <p>To make this notification the summary for its group, also call
2702 * {@link #setGroupSummary}. A sort order can be specified for group members by using
2703 * {@link #setSortKey}.
2704 * @param groupKey The group key of the group.
2705 * @return this object for method chaining
2706 */
2707 public Builder setGroup(String groupKey) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002708 mN.mGroupKey = groupKey;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002709 return this;
2710 }
2711
2712 /**
2713 * Set this notification to be the group summary for a group of notifications.
2714 * Grouped notifications may display in a cluster or stack on devices which
2715 * support such rendering. Requires a group key also be set using {@link #setGroup}.
2716 * @param isGroupSummary Whether this notification should be a group summary.
2717 * @return this object for method chaining
2718 */
2719 public Builder setGroupSummary(boolean isGroupSummary) {
2720 setFlag(FLAG_GROUP_SUMMARY, isGroupSummary);
2721 return this;
2722 }
2723
2724 /**
2725 * Set a sort key that orders this notification among other notifications from the
2726 * same package. This can be useful if an external sort was already applied and an app
2727 * would like to preserve this. Notifications will be sorted lexicographically using this
2728 * value, although providing different priorities in addition to providing sort key may
2729 * cause this value to be ignored.
2730 *
2731 * <p>This sort key can also be used to order members of a notification group. See
Griff Hazen9e1379f2014-05-20 12:50:51 -07002732 * {@link #setGroup}.
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002733 *
2734 * @see String#compareTo(String)
2735 */
2736 public Builder setSortKey(String sortKey) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002737 mN.mSortKey = sortKey;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002738 return this;
2739 }
2740
2741 /**
Griff Hazen720042b2014-02-24 15:46:56 -08002742 * Merge additional metadata into this notification.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002743 *
Griff Hazen720042b2014-02-24 15:46:56 -08002744 * <p>Values within the Bundle will replace existing extras values in this Builder.
2745 *
2746 * @see Notification#extras
2747 */
Griff Hazen959591e2014-05-15 22:26:18 -07002748 public Builder addExtras(Bundle extras) {
2749 if (extras != null) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002750 mUserExtras.putAll(extras);
Griff Hazen720042b2014-02-24 15:46:56 -08002751 }
2752 return this;
2753 }
2754
2755 /**
2756 * Set metadata for this notification.
2757 *
2758 * <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 -04002759 * current contents are copied into the Notification each time {@link #build()} is
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002760 * called.
2761 *
Griff Hazen720042b2014-02-24 15:46:56 -08002762 * <p>Replaces any existing extras values with those from the provided Bundle.
2763 * Use {@link #addExtras} to merge in metadata instead.
2764 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002765 * @see Notification#extras
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002766 */
Griff Hazen959591e2014-05-15 22:26:18 -07002767 public Builder setExtras(Bundle extras) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002768 if (extras != null) {
2769 mUserExtras = extras;
2770 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002771 return this;
2772 }
2773
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002774 /**
Griff Hazen720042b2014-02-24 15:46:56 -08002775 * Get the current metadata Bundle used by this notification Builder.
2776 *
2777 * <p>The returned Bundle is shared with this Builder.
2778 *
2779 * <p>The current contents of this Bundle are copied into the Notification each time
2780 * {@link #build()} is called.
2781 *
2782 * @see Notification#extras
2783 */
2784 public Bundle getExtras() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002785 return mUserExtras;
2786 }
2787
2788 private Bundle getAllExtras() {
2789 final Bundle saveExtras = (Bundle) mUserExtras.clone();
2790 saveExtras.putAll(mN.extras);
2791 return saveExtras;
Griff Hazen720042b2014-02-24 15:46:56 -08002792 }
2793
2794 /**
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002795 * Add an action to this notification. Actions are typically displayed by
2796 * the system as a button adjacent to the notification content.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04002797 * <p>
2798 * Every action must have an icon (32dp square and matching the
2799 * <a href="{@docRoot}design/style/iconography.html#action-bar">Holo
2800 * Dark action bar</a> visual style), a textual label, and a {@link PendingIntent}.
2801 * <p>
2802 * A notification in its expanded form can display up to 3 actions, from left to right in
2803 * the order they were added. Actions will not be displayed when the notification is
2804 * collapsed, however, so be sure that any essential functions may be accessed by the user
2805 * in some other way (for example, in the Activity pointed to by {@link #contentIntent}).
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002806 *
2807 * @param icon Resource ID of a drawable that represents the action.
2808 * @param title Text describing the action.
2809 * @param intent PendingIntent to be fired when the action is invoked.
Dan Sandler86647982015-05-13 23:41:13 -04002810 *
2811 * @deprecated Use {@link #addAction(Action)} instead.
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002812 */
Dan Sandler86647982015-05-13 23:41:13 -04002813 @Deprecated
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002814 public Builder addAction(int icon, CharSequence title, PendingIntent intent) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04002815 mActions.add(new Action(icon, safeCharSequence(title), intent));
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002816 return this;
2817 }
2818
Chris Wrenfbd96ba2012-05-01 12:03:58 -04002819 /**
Griff Hazen959591e2014-05-15 22:26:18 -07002820 * Add an action to this notification. Actions are typically displayed by
2821 * the system as a button adjacent to the notification content.
2822 * <p>
2823 * Every action must have an icon (32dp square and matching the
2824 * <a href="{@docRoot}design/style/iconography.html#action-bar">Holo
2825 * Dark action bar</a> visual style), a textual label, and a {@link PendingIntent}.
2826 * <p>
2827 * A notification in its expanded form can display up to 3 actions, from left to right in
2828 * the order they were added. Actions will not be displayed when the notification is
2829 * collapsed, however, so be sure that any essential functions may be accessed by the user
2830 * in some other way (for example, in the Activity pointed to by {@link #contentIntent}).
2831 *
2832 * @param action The action to add.
2833 */
2834 public Builder addAction(Action action) {
2835 mActions.add(action);
2836 return this;
2837 }
2838
2839 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002840 * Alter the complete list of actions attached to this notification.
2841 * @see #addAction(Action).
2842 *
2843 * @param actions
2844 * @return
2845 */
2846 public Builder setActions(Action... actions) {
2847 mActions.clear();
2848 for (int i = 0; i < actions.length; i++) {
2849 mActions.add(actions[i]);
2850 }
2851 return this;
2852 }
2853
2854 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04002855 * Add a rich notification style to be applied at build time.
2856 *
2857 * @param style Object responsible for modifying the notification style.
2858 */
2859 public Builder setStyle(Style style) {
2860 if (mStyle != style) {
2861 mStyle = style;
Daniel Sandlerc08dea22012-06-28 08:35:24 -07002862 if (mStyle != null) {
2863 mStyle.setBuilder(this);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002864 mN.extras.putString(EXTRA_TEMPLATE, style.getClass().getName());
2865 } else {
2866 mN.extras.remove(EXTRA_TEMPLATE);
Daniel Sandlerc08dea22012-06-28 08:35:24 -07002867 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04002868 }
2869 return this;
2870 }
2871
Dan Sandler0bf2ed82013-12-21 23:33:41 -06002872 /**
2873 * Specify the value of {@link #visibility}.
Griff Hazenb720abe2014-05-20 13:15:30 -07002874 *
Dan Sandler0bf2ed82013-12-21 23:33:41 -06002875 * @param visibility One of {@link #VISIBILITY_PRIVATE} (the default),
2876 * {@link #VISIBILITY_SECRET}, or {@link #VISIBILITY_PUBLIC}.
2877 *
2878 * @return The same Builder.
2879 */
2880 public Builder setVisibility(int visibility) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002881 mN.visibility = visibility;
Dan Sandler0bf2ed82013-12-21 23:33:41 -06002882 return this;
2883 }
2884
2885 /**
2886 * Supply a replacement Notification whose contents should be shown in insecure contexts
2887 * (i.e. atop the secure lockscreen). See {@link #visibility} and {@link #VISIBILITY_PUBLIC}.
2888 * @param n A replacement notification, presumably with some or all info redacted.
2889 * @return The same Builder.
2890 */
2891 public Builder setPublicVersion(Notification n) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002892 if (n != null) {
2893 mN.publicVersion = new Notification();
2894 n.cloneInto(mN.publicVersion, /*heavy=*/ true);
2895 } else {
2896 mN.publicVersion = null;
2897 }
Dan Sandler0bf2ed82013-12-21 23:33:41 -06002898 return this;
2899 }
2900
Griff Hazenb720abe2014-05-20 13:15:30 -07002901 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002902 * Apply an extender to this notification builder. Extenders may be used to add
2903 * metadata or change options on this builder.
2904 */
Griff Hazen61a9e862014-05-22 16:05:19 -07002905 public Builder extend(Extender extender) {
2906 extender.extend(this);
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002907 return this;
2908 }
2909
Dan Sandler4e787062015-06-17 15:09:48 -04002910 /**
2911 * @hide
2912 */
2913 public void setFlag(int mask, boolean value) {
Joe Onorato46439ce2010-11-19 13:56:21 -08002914 if (value) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002915 mN.flags |= mask;
Joe Onorato46439ce2010-11-19 13:56:21 -08002916 } else {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002917 mN.flags &= ~mask;
Joe Onorato46439ce2010-11-19 13:56:21 -08002918 }
2919 }
2920
Dan Sandler26e81cf2014-05-06 10:01:27 -04002921 /**
2922 * Sets {@link Notification#color}.
2923 *
2924 * @param argb The accent color to use
2925 *
2926 * @return The same Builder.
2927 */
Tor Norbye80756e32015-03-02 09:39:27 -08002928 public Builder setColor(@ColorInt int argb) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002929 mN.color = argb;
Julia Reynolds10ee1fc2015-11-09 11:04:55 -05002930 sanitizeColor();
Dan Sandler26e81cf2014-05-06 10:01:27 -04002931 return this;
2932 }
2933
Julia Reynolds74303cf2015-10-16 11:37:55 -04002934 /**
Julia Reynolds054c5dc2015-11-17 15:36:30 -05002935 * Sets the topic of this notification. Topics are typically displayed in Notification
Julia Reynolds74303cf2015-10-16 11:37:55 -04002936 * settings.
2937 * <p>
2938 * Every topic must have an id and a textual label.
2939 *
2940 * @param topic The topic to add.
2941 */
Julia Reynolds054c5dc2015-11-17 15:36:30 -05002942 public Builder setTopic(Topic topic) {
2943 mN.topic = topic;
Julia Reynolds74303cf2015-10-16 11:37:55 -04002944 return this;
2945 }
2946
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02002947 private Drawable getProfileBadgeDrawable() {
Christoph Studer7ac80e62014-08-04 16:01:57 +02002948 // Note: This assumes that the current user can read the profile badge of the
2949 // originating user.
Svetoslavc7d62f02014-09-04 15:39:54 -07002950 return mContext.getPackageManager().getUserBadgeForDensity(
Julia Reynoldsda303542015-11-23 14:00:20 -05002951 new UserHandle(mContext.getUserId()), 0);
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02002952 }
2953
2954 private Bitmap getProfileBadge() {
2955 Drawable badge = getProfileBadgeDrawable();
Kenny Guy8a0101b2014-05-08 23:34:12 +01002956 if (badge == null) {
2957 return null;
2958 }
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02002959 final int size = mContext.getResources().getDimensionPixelSize(
2960 R.dimen.notification_badge_size);
2961 Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
Kenny Guy8a0101b2014-05-08 23:34:12 +01002962 Canvas canvas = new Canvas(bitmap);
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02002963 badge.setBounds(0, 0, size, size);
Kenny Guy8a0101b2014-05-08 23:34:12 +01002964 badge.draw(canvas);
2965 return bitmap;
2966 }
2967
Kenny Guy98193ea2014-07-24 19:54:37 +01002968 private boolean addProfileBadge(RemoteViews contentView, int resId) {
2969 Bitmap profileBadge = getProfileBadge();
2970
2971 contentView.setViewVisibility(R.id.profile_badge_large_template, View.GONE);
Kenny Guy98193ea2014-07-24 19:54:37 +01002972 contentView.setViewVisibility(R.id.profile_badge_line3, View.GONE);
2973
2974 if (profileBadge != null) {
2975 contentView.setImageViewBitmap(resId, profileBadge);
2976 contentView.setViewVisibility(resId, View.VISIBLE);
2977
2978 // Make sure Line 3 is visible. As badge will be here if there
2979 // is no text to display.
2980 if (resId == R.id.profile_badge_line3) {
2981 contentView.setViewVisibility(R.id.line3, View.VISIBLE);
2982 }
2983 return true;
2984 }
2985 return false;
2986 }
2987
Christoph Studerfe718432014-09-01 18:21:18 +02002988 private void resetStandardTemplate(RemoteViews contentView) {
Selim Cinekeaa29ca2015-11-23 13:51:13 -08002989 resetNotificationHeader(contentView);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07002990 resetContentMargins(contentView);
Christoph Studerfe718432014-09-01 18:21:18 +02002991 contentView.setViewVisibility(R.id.right_icon, View.GONE);
Christoph Studerfe718432014-09-01 18:21:18 +02002992 contentView.setTextViewText(R.id.title, null);
2993 contentView.setTextViewText(R.id.text, null);
Christoph Studerfe718432014-09-01 18:21:18 +02002994 contentView.setViewVisibility(R.id.line3, View.GONE);
Selim Cinek29603462015-11-17 19:04:39 -08002995 contentView.setViewVisibility(R.id.text_line_1, View.GONE);
Christoph Studerfe718432014-09-01 18:21:18 +02002996 contentView.setViewVisibility(R.id.progress, View.GONE);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07002997 }
2998
Selim Cinekeaa29ca2015-11-23 13:51:13 -08002999 /**
3000 * Resets the notification header to its original state
3001 */
3002 private void resetNotificationHeader(RemoteViews contentView) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003003 contentView.setImageViewResource(R.id.icon, 0);
Selim Cinek7b836392015-12-04 20:02:59 -08003004 contentView.setBoolean(R.id.notification_header, "setExpanded", false);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003005 contentView.setTextViewText(R.id.app_name_text, null);
Christoph Studerca1db712014-09-10 17:31:33 +02003006 contentView.setViewVisibility(R.id.chronometer, View.GONE);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003007 contentView.setViewVisibility(R.id.header_sub_text, View.GONE);
Selim Cinek29603462015-11-17 19:04:39 -08003008 contentView.setViewVisibility(R.id.header_content_info, View.GONE);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003009 contentView.setViewVisibility(R.id.number_of_children, View.GONE);
Selim Cinek29603462015-11-17 19:04:39 -08003010 contentView.setViewVisibility(R.id.sub_text_divider, View.GONE);
3011 contentView.setViewVisibility(R.id.content_info_divider, View.GONE);
3012 contentView.setViewVisibility(R.id.time_divider, View.GONE);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003013 }
3014
3015 private void resetContentMargins(RemoteViews contentView) {
3016 contentView.setViewLayoutMarginEnd(R.id.line1, 0);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003017 contentView.setViewLayoutMarginEnd(R.id.line3, 0);
Christoph Studerfe718432014-09-01 18:21:18 +02003018 }
3019
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003020 private RemoteViews applyStandardTemplate(int resId) {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02003021 return applyStandardTemplate(resId, true /* hasProgress */);
3022 }
3023
3024 /**
3025 * @param hasProgress whether the progress bar should be shown and set
3026 */
3027 private RemoteViews applyStandardTemplate(int resId, boolean hasProgress) {
Kenny Guy77320062014-08-27 21:37:15 +01003028 RemoteViews contentView = new BuilderRemoteViews(mContext.getApplicationInfo(), resId);
Dan Sandler539aad42014-08-04 00:43:39 -04003029
Christoph Studerfe718432014-09-01 18:21:18 +02003030 resetStandardTemplate(contentView);
3031
Daniel Sandler9f7936a2012-05-21 16:14:28 -04003032 boolean showLine3 = false;
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003033 final Bundle ex = mN.extras;
Dan Sandler190d58d2014-05-15 09:33:39 -04003034
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003035 bindNotificationHeader(contentView);
3036 bindLargeIcon(contentView);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003037 if (ex.getCharSequence(EXTRA_TITLE) != null) {
3038 contentView.setTextViewText(R.id.title,
3039 processLegacyText(ex.getCharSequence(EXTRA_TITLE)));
Joe Onorato561d3852010-11-20 18:09:34 -08003040 }
Selim Cinek29603462015-11-17 19:04:39 -08003041 boolean showProgress = handleProgressBar(hasProgress, contentView, ex);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003042 if (ex.getCharSequence(EXTRA_TEXT) != null) {
Selim Cinek29603462015-11-17 19:04:39 -08003043 contentView.setTextViewText(showProgress ? R.id.text_line_1 : R.id.text,
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003044 processLegacyText(ex.getCharSequence(EXTRA_TEXT)));
Selim Cinek29603462015-11-17 19:04:39 -08003045 if (showProgress) {
3046 contentView.setViewVisibility(R.id.text_line_1, View.VISIBLE);
Joe Onorato059a2f82011-01-04 10:27:01 -08003047 }
Selim Cinek29603462015-11-17 19:04:39 -08003048 showLine3 = !showProgress;
Joe Onorato561d3852010-11-20 18:09:34 -08003049 }
Selim Cinek29603462015-11-17 19:04:39 -08003050 // We want to add badge to first line of text.
3051 if (addProfileBadge(contentView, R.id.profile_badge_line3)) {
3052 showLine3 = true;
3053 }
3054 // Note getStandardView may hide line 3 again.
3055 contentView.setViewVisibility(R.id.line3, showLine3 ? View.VISIBLE : View.GONE);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003056
Selim Cinek29603462015-11-17 19:04:39 -08003057 return contentView;
3058 }
3059
3060 private boolean handleProgressBar(boolean hasProgress, RemoteViews contentView, Bundle ex) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003061 final int max = ex.getInt(EXTRA_PROGRESS_MAX, 0);
3062 final int progress = ex.getInt(EXTRA_PROGRESS, 0);
3063 final boolean ind = ex.getBoolean(EXTRA_PROGRESS_INDETERMINATE);
3064 if (hasProgress && (max != 0 || ind)) {
Selim Cinek29603462015-11-17 19:04:39 -08003065 contentView.setViewVisibility(com.android.internal.R.id.progress, View.VISIBLE);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003066 contentView.setProgressBar(
Selim Cinek29603462015-11-17 19:04:39 -08003067 R.id.progress, max, progress, ind);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003068 contentView.setProgressBackgroundTintList(
3069 R.id.progress, ColorStateList.valueOf(mContext.getColor(
3070 R.color.notification_progress_background_color)));
Selim Cinek29603462015-11-17 19:04:39 -08003071 if (mN.color != COLOR_DEFAULT) {
3072 ColorStateList colorStateList = ColorStateList.valueOf(mN.color);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003073 contentView.setProgressTintList(R.id.progress, colorStateList);
3074 contentView.setProgressIndeterminateTintList(R.id.progress, colorStateList);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003075 }
Selim Cinek29603462015-11-17 19:04:39 -08003076 return true;
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003077 } else {
3078 contentView.setViewVisibility(R.id.progress, View.GONE);
Selim Cinek29603462015-11-17 19:04:39 -08003079 return false;
Jeff Sharkey1c400132011-08-05 14:50:13 -07003080 }
Joe Onorato561d3852010-11-20 18:09:34 -08003081 }
3082
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003083 private void bindLargeIcon(RemoteViews contentView) {
3084 if (mN.mLargeIcon != null) {
3085 contentView.setViewVisibility(R.id.right_icon, View.VISIBLE);
3086 contentView.setImageViewIcon(R.id.right_icon, mN.mLargeIcon);
3087 processLargeLegacyIcon(mN.mLargeIcon, contentView);
3088 int endMargin = mContext.getResources().getDimensionPixelSize(
3089 R.dimen.notification_content_picture_margin);
3090 contentView.setViewLayoutMarginEnd(R.id.line1, endMargin);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003091 contentView.setViewLayoutMarginEnd(R.id.line3, endMargin);
Selim Cinek29603462015-11-17 19:04:39 -08003092 contentView.setViewLayoutMarginEnd(R.id.progress, endMargin);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003093 }
3094 }
3095
3096 private void bindNotificationHeader(RemoteViews contentView) {
3097 bindSmallIcon(contentView);
Selim Cinekeaa29ca2015-11-23 13:51:13 -08003098 bindChildCountColor(contentView);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003099 bindHeaderAppName(contentView);
Selim Cinek03d0d652015-11-13 13:18:09 -05003100 bindHeaderSubText(contentView);
Selim Cinek29603462015-11-17 19:04:39 -08003101 bindContentInfo(contentView);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003102 bindHeaderChronometerAndTime(contentView);
3103 bindExpandButton(contentView);
3104 }
3105
Selim Cinekeaa29ca2015-11-23 13:51:13 -08003106 private void bindChildCountColor(RemoteViews contentView) {
3107 contentView.setTextColor(R.id.number_of_children, resolveColor());
3108 }
3109
Selim Cinek29603462015-11-17 19:04:39 -08003110 private void bindContentInfo(RemoteViews contentView) {
3111 boolean visible = false;
3112 if (mN.extras.getCharSequence(EXTRA_INFO_TEXT) != null) {
3113 contentView.setTextViewText(R.id.header_content_info,
3114 processLegacyText(mN.extras.getCharSequence(EXTRA_INFO_TEXT)));
3115 contentView.setViewVisibility(R.id.header_content_info, View.VISIBLE);
3116 visible = true;
3117 } else if (mN.number > 0) {
3118 final int tooBig = mContext.getResources().getInteger(
3119 R.integer.status_bar_notification_info_maxnum);
3120 if (mN.number > tooBig) {
3121 contentView.setTextViewText(R.id.header_content_info, processLegacyText(
3122 mContext.getResources().getString(
3123 R.string.status_bar_notification_info_overflow)));
3124 } else {
3125 contentView.setTextViewText(R.id.header_content_info,
3126 processLegacyText(String.valueOf(mN.number)));
3127 }
3128 contentView.setViewVisibility(R.id.header_content_info, View.VISIBLE);
3129 visible = true;
3130 }
3131 if (visible) {
3132 contentView.setViewVisibility(R.id.content_info_divider, View.VISIBLE);
3133 }
3134 }
3135
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003136 private void bindExpandButton(RemoteViews contentView) {
3137 contentView.setDrawableParameters(R.id.expand_button, false, -1, resolveColor(),
3138 PorterDuff.Mode.SRC_ATOP, -1);
Selim Cinekea4bef72015-12-02 15:51:10 -08003139 contentView.setInt(R.id.notification_header, "setOriginalNotificationColor",
3140 resolveColor());
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003141 }
3142
3143 private void bindHeaderChronometerAndTime(RemoteViews contentView) {
3144 if (showsTimeOrChronometer()) {
Selim Cinek29603462015-11-17 19:04:39 -08003145 contentView.setViewVisibility(R.id.time_divider, View.VISIBLE);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003146 if (mN.extras.getBoolean(EXTRA_SHOW_CHRONOMETER)) {
3147 contentView.setViewVisibility(R.id.chronometer, View.VISIBLE);
3148 contentView.setLong(R.id.chronometer, "setBase",
3149 mN.when + (SystemClock.elapsedRealtime() - System.currentTimeMillis()));
3150 contentView.setBoolean(R.id.chronometer, "setStarted", true);
3151 } else {
3152 contentView.setViewVisibility(R.id.time, View.VISIBLE);
3153 contentView.setLong(R.id.time, "setTime", mN.when);
3154 }
3155 }
3156 }
3157
Selim Cinek03d0d652015-11-13 13:18:09 -05003158 private void bindHeaderSubText(RemoteViews contentView) {
3159 CharSequence subText = mN.extras.getCharSequence(EXTRA_SUB_TEXT);
3160 if (subText == null && mStyle != null && mStyle.mSummaryTextSet
3161 && mStyle.hasSummaryInHeader()) {
3162 subText = mStyle.mSummaryText;
3163 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003164 if (subText != null) {
3165 // TODO: Remove the span entirely to only have the string with propper formating.
3166 contentView.setTextViewText(R.id.header_sub_text, processLegacyText(subText));
3167 contentView.setViewVisibility(R.id.header_sub_text, View.VISIBLE);
Selim Cinek29603462015-11-17 19:04:39 -08003168 contentView.setViewVisibility(R.id.sub_text_divider, View.VISIBLE);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003169 }
3170 }
3171
3172 private void bindHeaderAppName(RemoteViews contentView) {
3173 PackageManager packageManager = mContext.getPackageManager();
3174 ApplicationInfo info = null;
3175 try {
3176 info = packageManager.getApplicationInfo(mContext.getApplicationInfo().packageName,
3177 0);
3178 } catch (final NameNotFoundException e) {
3179 return;
3180 }
3181 CharSequence appName = info != null ? packageManager.getApplicationLabel(info)
3182 : null;
3183 if (TextUtils.isEmpty(appName)) {
3184 return;
3185 }
3186 contentView.setTextViewText(R.id.app_name_text, appName);
3187 }
3188
3189 private void bindSmallIcon(RemoteViews contentView) {
3190 contentView.setImageViewIcon(R.id.icon, mN.mSmallIcon);
3191 processSmallIconColor(mN.mSmallIcon, contentView);
3192 }
3193
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003194 /**
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02003195 * @return true if the built notification will show the time or the chronometer; false
3196 * otherwise
3197 */
3198 private boolean showsTimeOrChronometer() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003199 return mN.when != 0 && mN.extras.getBoolean(EXTRA_SHOW_WHEN);
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02003200 }
3201
Christoph Studerfe718432014-09-01 18:21:18 +02003202 private void resetStandardTemplateWithActions(RemoteViews big) {
3203 big.setViewVisibility(R.id.actions, View.GONE);
Christoph Studerfe718432014-09-01 18:21:18 +02003204 big.removeAllViews(R.id.actions);
3205 }
3206
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003207 private RemoteViews applyStandardTemplateWithActions(int layoutId) {
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003208 RemoteViews big = applyStandardTemplate(layoutId);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003209
Christoph Studerfe718432014-09-01 18:21:18 +02003210 resetStandardTemplateWithActions(big);
3211
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003212 int N = mActions.size();
3213 if (N > 0) {
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003214 big.setViewVisibility(R.id.actions, View.VISIBLE);
Daniel Sandler8680bf82012-05-15 16:52:52 -04003215 if (N>MAX_ACTION_BUTTONS) N=MAX_ACTION_BUTTONS;
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003216 for (int i=0; i<N; i++) {
3217 final RemoteViews button = generateActionButton(mActions.get(i));
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003218 big.addView(R.id.actions, button);
3219 }
3220 }
3221 return big;
3222 }
3223
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003224 /**
3225 * Construct a RemoteViews for the final 1U notification layout. In order:
3226 * 1. Custom contentView from the caller
3227 * 2. Style's proposed content view
3228 * 3. Standard template view
3229 */
3230 public RemoteViews makeContentView() {
3231 if (mN.contentView != null) {
3232 return mN.contentView;
3233 } else if (mStyle != null) {
3234 final RemoteViews styleView = mStyle.makeContentView();
3235 if (styleView != null) {
3236 return styleView;
3237 }
Joe Onorato46439ce2010-11-19 13:56:21 -08003238 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003239 return applyStandardTemplate(getBaseLayoutResource());
Joe Onorato46439ce2010-11-19 13:56:21 -08003240 }
3241
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003242 /**
3243 * Construct a RemoteViews for the final big notification layout.
3244 */
3245 public RemoteViews makeBigContentView() {
Selim Cinek850a8542015-11-11 11:48:36 -05003246 RemoteViews result = null;
Julia Reynolds089e3e42015-11-18 09:59:57 -05003247 if (mN.bigContentView != null) {
3248 return mN.bigContentView;
3249 } else if (mStyle != null) {
Selim Cinek850a8542015-11-11 11:48:36 -05003250 result = mStyle.makeBigContentView();
Julia Reynolds089e3e42015-11-18 09:59:57 -05003251 } else if (mActions.size() == 0) {
3252 return null;
3253 }
Selim Cinek850a8542015-11-11 11:48:36 -05003254 if (result == null) {
3255 result = applyStandardTemplateWithActions(getBigBaseLayoutResource());
Selim Cinek90dcf6d2015-11-18 20:24:13 -08003256 } else {
3257 hideLine1Text(result);
Selim Cinek850a8542015-11-11 11:48:36 -05003258 }
3259 adaptNotificationHeaderForBigContentView(result);
3260 return result;
3261 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003262
Selim Cinekeaa29ca2015-11-23 13:51:13 -08003263 /**
3264 * Construct a RemoteViews for the final notification header only
3265 *
3266 * @hide
3267 */
3268 public RemoteViews makeNotificationHeader() {
3269 RemoteViews header = new BuilderRemoteViews(mContext.getApplicationInfo(),
3270 R.layout.notification_template_header);
3271 resetNotificationHeader(header);
3272 bindNotificationHeader(header);
3273 return header;
3274 }
3275
Selim Cinek29603462015-11-17 19:04:39 -08003276 private void hideLine1Text(RemoteViews result) {
3277 result.setViewVisibility(R.id.text_line_1, View.GONE);
3278 }
3279
Selim Cinek850a8542015-11-11 11:48:36 -05003280 private void adaptNotificationHeaderForBigContentView(RemoteViews result) {
Selim Cinek7b836392015-12-04 20:02:59 -08003281 result.setBoolean(R.id.notification_header, "setExpanded", true);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003282 }
3283
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003284 /**
3285 * Construct a RemoteViews for the final heads-up notification layout.
3286 */
3287 public RemoteViews makeHeadsUpContentView() {
Julia Reynolds089e3e42015-11-18 09:59:57 -05003288 if (mN.headsUpContentView != null) {
3289 return mN.headsUpContentView;
3290 } else if (mStyle != null) {
3291 final RemoteViews styleView = mStyle.makeHeadsUpContentView();
3292 if (styleView != null) {
3293 return styleView;
3294 }
3295 } else if (mActions.size() == 0) {
3296 return null;
3297 }
3298
Chris Wren8fd39ec2014-02-27 17:43:26 -05003299
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003300 return applyStandardTemplateWithActions(getBigBaseLayoutResource());
Chris Wren8fd39ec2014-02-27 17:43:26 -05003301 }
3302
3303
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003304 private RemoteViews generateActionButton(Action action) {
Daniel Sandler8680bf82012-05-15 16:52:52 -04003305 final boolean tombstone = (action.actionIntent == null);
Selim Cinekf33b1112015-07-15 17:45:11 -07003306 RemoteViews button = new BuilderRemoteViews(mContext.getApplicationInfo(),
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003307 tombstone ? getActionTombstoneLayoutResource()
3308 : getActionLayoutResource());
Dan Sandler68079d52015-07-22 10:45:30 -04003309 final Icon ai = action.getIcon();
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003310 button.setTextViewText(R.id.action0, processLegacyText(action.title));
Daniel Sandler8680bf82012-05-15 16:52:52 -04003311 if (!tombstone) {
Daniel Sandlere5518842012-05-10 16:20:40 -04003312 button.setOnClickPendingIntent(R.id.action0, action.actionIntent);
Daniel Sandlere5518842012-05-10 16:20:40 -04003313 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003314 button.setContentDescription(R.id.action0, action.title);
Adrian Roosfe84e1f2015-11-04 15:55:39 -08003315 if (action.mRemoteInputs != null) {
3316 button.setRemoteInputs(R.id.action0, action.mRemoteInputs);
3317 }
3318 if (mN.color != COLOR_DEFAULT) {
3319 button.setTextColor(R.id.action0, mN.color);
3320 }
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003321 processLegacyAction(action, button);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003322 return button;
3323 }
3324
Joe Onoratocb109a02011-01-18 17:57:41 -08003325 /**
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003326 * @return Whether we are currently building a notification from a legacy (an app that
Alan Viverette3cb07a462014-06-06 14:19:53 -07003327 * doesn't create material notifications by itself) app.
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003328 */
3329 private boolean isLegacy() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003330 return getColorUtil() != null;
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003331 }
3332
3333 private void processLegacyAction(Action action, RemoteViews button) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003334 if (!isLegacy() || getColorUtil().isGrayscaleIcon(mContext, action.getIcon())) {
Christoph Studer239f8352014-08-25 15:13:18 +02003335 button.setTextViewCompoundDrawablesRelativeColorFilter(R.id.action0, 0,
Alan Viverette4a357cd2015-03-18 18:37:18 -07003336 mContext.getColor(R.color.notification_action_color_filter),
Christoph Studer239f8352014-08-25 15:13:18 +02003337 PorterDuff.Mode.MULTIPLY);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003338 }
3339 }
3340
3341 private CharSequence processLegacyText(CharSequence charSequence) {
3342 if (isLegacy()) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003343 return getColorUtil().invertCharSequenceColors(charSequence);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003344 } else {
3345 return charSequence;
3346 }
3347 }
3348
Dan Sandler26e81cf2014-05-06 10:01:27 -04003349 /**
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003350 * Apply any necessariy colors to the small icon
Dan Sandler26e81cf2014-05-06 10:01:27 -04003351 */
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003352 private void processSmallIconColor(Icon smallIcon, RemoteViews contentView) {
Selim Cinekea4bef72015-12-02 15:51:10 -08003353 boolean colorable = !isLegacy() || getColorUtil().isGrayscaleIcon(mContext, smallIcon);
3354 if (colorable) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003355 contentView.setDrawableParameters(R.id.icon, false, -1, resolveColor(),
Jorim Jaggi92df1f22014-12-16 19:44:41 +01003356 PorterDuff.Mode.SRC_ATOP, -1);
Selim Cinekea4bef72015-12-02 15:51:10 -08003357
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003358 }
Selim Cinekea4bef72015-12-02 15:51:10 -08003359 contentView.setInt(R.id.notification_header, "setOriginalIconColor",
3360 colorable ? resolveColor() : NotificationHeaderView.NO_COLOR);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003361 }
3362
Dan Sandler26e81cf2014-05-06 10:01:27 -04003363 /**
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003364 * Make the largeIcon dark if it's a fake smallIcon (that is,
Dan Sandler26e81cf2014-05-06 10:01:27 -04003365 * if it's grayscale).
3366 */
3367 // TODO: also check bounds, transparency, that sort of thing.
Dan Sandlerd63f9322015-05-06 15:18:49 -04003368 private void processLargeLegacyIcon(Icon largeIcon, RemoteViews contentView) {
3369 if (largeIcon != null && isLegacy()
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003370 && getColorUtil().isGrayscaleIcon(mContext, largeIcon)) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003371 // resolve color will fall back to the default when legacy
3372 contentView.setDrawableParameters(R.id.icon, false, -1, resolveColor(),
Dan Sandler190d58d2014-05-15 09:33:39 -04003373 PorterDuff.Mode.SRC_ATOP, -1);
Jorim Jaggi92df1f22014-12-16 19:44:41 +01003374 }
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003375 }
3376
Julia Reynolds10ee1fc2015-11-09 11:04:55 -05003377 private void sanitizeColor() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003378 if (mN.color != COLOR_DEFAULT) {
3379 mN.color |= 0xFF000000; // no alpha for custom colors
Jorim Jaggi74419312014-06-10 20:57:21 +02003380 }
Jorim Jaggi74419312014-06-10 20:57:21 +02003381 }
3382
Selim Cinek5bf069a2015-11-10 19:14:27 -05003383 int resolveColor() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003384 if (mN.color == COLOR_DEFAULT) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003385 return mContext.getColor(R.color.notification_icon_default_color);
Dan Sandler26e81cf2014-05-06 10:01:27 -04003386 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003387 return mN.color;
Dan Sandler26e81cf2014-05-06 10:01:27 -04003388 }
3389
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003390 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003391 * Apply the unstyled operations and return a new {@link Notification} object.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04003392 * @hide
Joe Onoratocb109a02011-01-18 17:57:41 -08003393 */
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04003394 public Notification buildUnstyled() {
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003395 if (mActions.size() > 0) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003396 mN.actions = new Action[mActions.size()];
3397 mActions.toArray(mN.actions);
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003398 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003399 if (!mPersonList.isEmpty()) {
3400 mN.extras.putStringArray(EXTRA_PEOPLE,
3401 mPersonList.toArray(new String[mPersonList.size()]));
Dan Sandler0bf2ed82013-12-21 23:33:41 -06003402 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003403 return mN;
Joe Onorato46439ce2010-11-19 13:56:21 -08003404 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003405
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003406 public static Notification.Builder recoverBuilder(Context context, Notification n) {
Christoph Studer4600f9b2014-07-22 22:44:43 +02003407 // Re-create notification context so we can access app resources.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003408 ApplicationInfo applicationInfo = n.extras.getParcelable(
3409 EXTRA_BUILDER_APPLICATION_INFO);
Christoph Studer4600f9b2014-07-22 22:44:43 +02003410 Context builderContext;
Julia Reynoldsda303542015-11-23 14:00:20 -05003411 if (applicationInfo != null) {
3412 try {
3413 builderContext = context.createApplicationContext(applicationInfo,
3414 Context.CONTEXT_RESTRICTED);
3415 } catch (NameNotFoundException e) {
3416 Log.e(TAG, "ApplicationInfo " + applicationInfo + " not found");
3417 builderContext = context; // try with our context
3418 }
3419 } else {
3420 builderContext = context; // try with given context
Christoph Studer4600f9b2014-07-22 22:44:43 +02003421 }
3422
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003423 return new Builder(builderContext, n);
Christoph Studer4600f9b2014-07-22 22:44:43 +02003424 }
3425
3426 private static Class<? extends Style> getNotificationStyleClass(String templateClass) {
3427 Class<? extends Style>[] classes = new Class[]{
3428 BigTextStyle.class, BigPictureStyle.class, InboxStyle.class, MediaStyle.class};
3429 for (Class<? extends Style> innerClass : classes) {
3430 if (templateClass.equals(innerClass.getName())) {
3431 return innerClass;
3432 }
3433 }
3434 return null;
3435 }
3436
3437 private void setBuilderContentView(Notification n, RemoteViews contentView) {
3438 n.contentView = contentView;
Christoph Studer4600f9b2014-07-22 22:44:43 +02003439 }
3440
3441 private void setBuilderBigContentView(Notification n, RemoteViews bigContentView) {
3442 n.bigContentView = bigContentView;
Christoph Studer4600f9b2014-07-22 22:44:43 +02003443 }
3444
3445 private void setBuilderHeadsUpContentView(Notification n,
3446 RemoteViews headsUpContentView) {
3447 n.headsUpContentView = headsUpContentView;
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003448 }
3449
3450 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003451 * @deprecated Use {@link #build()} instead.
3452 */
3453 @Deprecated
3454 public Notification getNotification() {
3455 return build();
3456 }
3457
3458 /**
3459 * Combine all of the options that have been set and return a new {@link Notification}
3460 * object.
3461 */
3462 public Notification build() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003463 // first, add any extras from the calling code
3464 if (mUserExtras != null) {
3465 mN.extras = getAllExtras();
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07003466 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003467
3468 // lazy stuff from mContext; see comment in Builder(Context, Notification)
Julia Reynoldsda303542015-11-23 14:00:20 -05003469 Notification.addFieldsFromContext(mContext, mN);
Christoph Studer943aa672014-08-03 20:31:16 +02003470
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003471 buildUnstyled();
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003472
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003473 if (mStyle != null) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003474 mStyle.buildStyled(mN);
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003475 }
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003476
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003477 return mN;
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003478 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05003479
3480 /**
3481 * Apply this Builder to an existing {@link Notification} object.
3482 *
3483 * @hide
3484 */
3485 public Notification buildInto(Notification n) {
Daniel Sandler1a497d32013-04-18 14:52:45 -04003486 build().cloneInto(n, true);
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05003487 return n;
3488 }
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003489
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003490 private int getBaseLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07003491 return R.layout.notification_template_material_base;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003492 }
3493
3494 private int getBigBaseLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07003495 return R.layout.notification_template_material_big_base;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003496 }
3497
3498 private int getBigPictureLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07003499 return R.layout.notification_template_material_big_picture;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003500 }
3501
3502 private int getBigTextLayoutResource() {
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003503 return R.layout.notification_template_material_big_text;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003504 }
3505
3506 private int getInboxLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07003507 return R.layout.notification_template_material_inbox;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003508 }
3509
3510 private int getActionLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07003511 return R.layout.notification_material_action;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003512 }
3513
3514 private int getActionTombstoneLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07003515 return R.layout.notification_material_action_tombstone;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003516 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003517 }
3518
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003519 /**
3520 * An object that can apply a rich notification style to a {@link Notification.Builder}
3521 * object.
3522 */
Griff Hazendfcb0802014-02-11 12:00:00 -08003523 public static abstract class Style {
Chris Wrend6297db2012-05-03 16:20:13 -04003524 private CharSequence mBigContentTitle;
Jorim Jaggi457a10d2014-09-08 16:18:23 +02003525
3526 /**
3527 * @hide
3528 */
3529 protected CharSequence mSummaryText = null;
3530
3531 /**
3532 * @hide
3533 */
3534 protected boolean mSummaryTextSet = false;
Chris Wrend6297db2012-05-03 16:20:13 -04003535
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003536 protected Builder mBuilder;
3537
Chris Wrend6297db2012-05-03 16:20:13 -04003538 /**
3539 * Overrides ContentTitle in the big form of the template.
3540 * This defaults to the value passed to setContentTitle().
3541 */
3542 protected void internalSetBigContentTitle(CharSequence title) {
3543 mBigContentTitle = title;
3544 }
3545
3546 /**
3547 * Set the first line of text after the detail section in the big form of the template.
3548 */
3549 protected void internalSetSummaryText(CharSequence cs) {
3550 mSummaryText = cs;
Daniel Sandler619738c2012-06-07 16:33:08 -04003551 mSummaryTextSet = true;
Chris Wrend6297db2012-05-03 16:20:13 -04003552 }
3553
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003554 public void setBuilder(Builder builder) {
3555 if (mBuilder != builder) {
3556 mBuilder = builder;
Daniel Sandlerc08dea22012-06-28 08:35:24 -07003557 if (mBuilder != null) {
3558 mBuilder.setStyle(this);
3559 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003560 }
3561 }
3562
Chris Wrend6297db2012-05-03 16:20:13 -04003563 protected void checkBuilder() {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003564 if (mBuilder == null) {
3565 throw new IllegalArgumentException("Style requires a valid Builder object");
3566 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003567 }
Chris Wrend6297db2012-05-03 16:20:13 -04003568
3569 protected RemoteViews getStandardView(int layoutId) {
3570 checkBuilder();
3571
Christoph Studer4600f9b2014-07-22 22:44:43 +02003572 // Nasty.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003573 CharSequence oldBuilderContentTitle =
3574 mBuilder.getAllExtras().getCharSequence(EXTRA_TITLE);
Chris Wrend6297db2012-05-03 16:20:13 -04003575 if (mBigContentTitle != null) {
3576 mBuilder.setContentTitle(mBigContentTitle);
3577 }
3578
Chris Wrend6297db2012-05-03 16:20:13 -04003579 RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(layoutId);
3580
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003581 mBuilder.getAllExtras().putCharSequence(EXTRA_TITLE, oldBuilderContentTitle);
Christoph Studer4600f9b2014-07-22 22:44:43 +02003582
Chris Wrend6297db2012-05-03 16:20:13 -04003583 if (mBigContentTitle != null && mBigContentTitle.equals("")) {
3584 contentView.setViewVisibility(R.id.line1, View.GONE);
Chris Wren67dc9a02012-05-16 01:03:20 -04003585 } else {
3586 contentView.setViewVisibility(R.id.line1, View.VISIBLE);
Chris Wrend6297db2012-05-03 16:20:13 -04003587 }
3588
Selim Cinek9d9fc6e2015-11-12 15:49:14 -05003589 // Clear text in case we use the line to show the profile badge.
3590 contentView.setTextViewText(com.android.internal.R.id.text, "");
3591 contentView.setViewVisibility(com.android.internal.R.id.line3, View.GONE);
3592
Chris Wrend6297db2012-05-03 16:20:13 -04003593 return contentView;
3594 }
3595
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003596 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003597 * Construct a Style-specific RemoteViews for the final 1U notification layout.
3598 * The default implementation has nothing additional to add.
3599 * @hide
3600 */
3601 public RemoteViews makeContentView() {
3602 return null;
3603 }
3604
3605 /**
3606 * Construct a Style-specific RemoteViews for the final big notification layout.
3607 * @hide
3608 */
3609 public RemoteViews makeBigContentView() {
3610 return null;
3611 }
3612
3613 /**
3614 * Construct a Style-specific RemoteViews for the final HUN layout.
3615 * @hide
3616 */
3617 public RemoteViews makeHeadsUpContentView() {
3618 return null;
3619 }
3620
3621 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003622 * Apply any style-specific extras to this notification before shipping it out.
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003623 * @hide
3624 */
3625 public void addExtras(Bundle extras) {
3626 if (mSummaryTextSet) {
3627 extras.putCharSequence(EXTRA_SUMMARY_TEXT, mSummaryText);
3628 }
3629 if (mBigContentTitle != null) {
3630 extras.putCharSequence(EXTRA_TITLE_BIG, mBigContentTitle);
3631 }
Chris Wren91ad5632013-06-05 15:05:57 -04003632 extras.putString(EXTRA_TEMPLATE, this.getClass().getName());
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003633 }
3634
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04003635 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003636 * Reconstruct the internal state of this Style object from extras.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04003637 * @hide
3638 */
Christoph Studer4600f9b2014-07-22 22:44:43 +02003639 protected void restoreFromExtras(Bundle extras) {
3640 if (extras.containsKey(EXTRA_SUMMARY_TEXT)) {
3641 mSummaryText = extras.getCharSequence(EXTRA_SUMMARY_TEXT);
3642 mSummaryTextSet = true;
3643 }
3644 if (extras.containsKey(EXTRA_TITLE_BIG)) {
3645 mBigContentTitle = extras.getCharSequence(EXTRA_TITLE_BIG);
3646 }
3647 }
3648
3649
3650 /**
3651 * @hide
3652 */
3653 public Notification buildStyled(Notification wip) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003654 addExtras(wip.extras);
Christoph Studer4600f9b2014-07-22 22:44:43 +02003655 return wip;
3656 }
3657
Daniel Sandler0ec46202015-06-24 01:27:05 -04003658 /**
3659 * @hide
3660 */
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07003661 public void purgeResources() {}
3662
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04003663 /**
3664 * Calls {@link android.app.Notification.Builder#build()} on the Builder this Style is
3665 * attached to.
3666 *
3667 * @return the fully constructed Notification.
3668 */
3669 public Notification build() {
3670 checkBuilder();
3671 return mBuilder.build();
3672 }
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02003673
3674 /**
3675 * @hide
3676 * @return true if the style positions the progress bar on the second line; false if the
3677 * style hides the progress bar
3678 */
3679 protected boolean hasProgress() {
3680 return true;
3681 }
Selim Cinek03d0d652015-11-13 13:18:09 -05003682
3683 /**
3684 * @hide
3685 * @return Whether we should put the summary be put into the notification header
3686 */
3687 public boolean hasSummaryInHeader() {
3688 return true;
3689 }
Joe Onorato46439ce2010-11-19 13:56:21 -08003690 }
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003691
3692 /**
Daniel Sandler4dfbe832012-04-11 14:51:46 -04003693 * Helper class for generating large-format notifications that include a large image attachment.
Joe Malin8d40d042012-11-05 11:36:40 -08003694 *
Robert Ly91c5ce32014-06-08 15:37:00 -07003695 * Here's how you'd set the <code>BigPictureStyle</code> on a notification:
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003696 * <pre class="prettyprint">
Robert Ly91c5ce32014-06-08 15:37:00 -07003697 * Notification notif = new Notification.Builder(mContext)
3698 * .setContentTitle(&quot;New photo from &quot; + sender.toString())
3699 * .setContentText(subject)
3700 * .setSmallIcon(R.drawable.new_post)
3701 * .setLargeIcon(aBitmap)
3702 * .setStyle(new Notification.BigPictureStyle()
3703 * .bigPicture(aBigBitmap))
3704 * .build();
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003705 * </pre>
Joe Malin8d40d042012-11-05 11:36:40 -08003706 *
Daniel Sandler4dfbe832012-04-11 14:51:46 -04003707 * @see Notification#bigContentView
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003708 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003709 public static class BigPictureStyle extends Style {
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003710 private Bitmap mPicture;
Dan Sandlerd63f9322015-05-06 15:18:49 -04003711 private Icon mBigLargeIcon;
Chris Wren3745a3d2012-05-22 15:11:52 -04003712 private boolean mBigLargeIconSet = false;
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003713
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003714 public BigPictureStyle() {
3715 }
3716
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003717 public BigPictureStyle(Builder builder) {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003718 setBuilder(builder);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003719 }
3720
Chris Wrend6297db2012-05-03 16:20:13 -04003721 /**
3722 * Overrides ContentTitle in the big form of the template.
3723 * This defaults to the value passed to setContentTitle().
3724 */
3725 public BigPictureStyle setBigContentTitle(CharSequence title) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04003726 internalSetBigContentTitle(safeCharSequence(title));
Chris Wrend6297db2012-05-03 16:20:13 -04003727 return this;
3728 }
3729
3730 /**
3731 * Set the first line of text after the detail section in the big form of the template.
3732 */
3733 public BigPictureStyle setSummaryText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04003734 internalSetSummaryText(safeCharSequence(cs));
Chris Wrend6297db2012-05-03 16:20:13 -04003735 return this;
3736 }
3737
Chris Wren0bd664d2012-08-01 13:56:56 -04003738 /**
3739 * Provide the bitmap to be used as the payload for the BigPicture notification.
3740 */
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003741 public BigPictureStyle bigPicture(Bitmap b) {
3742 mPicture = b;
3743 return this;
3744 }
3745
Chris Wren3745a3d2012-05-22 15:11:52 -04003746 /**
Chris Wren3745a3d2012-05-22 15:11:52 -04003747 * Override the large icon when the big notification is shown.
3748 */
3749 public BigPictureStyle bigLargeIcon(Bitmap b) {
Dan Sandlerd63f9322015-05-06 15:18:49 -04003750 return bigLargeIcon(b != null ? Icon.createWithBitmap(b) : null);
3751 }
3752
3753 /**
3754 * Override the large icon when the big notification is shown.
3755 */
3756 public BigPictureStyle bigLargeIcon(Icon icon) {
Chris Wren3745a3d2012-05-22 15:11:52 -04003757 mBigLargeIconSet = true;
Dan Sandlerd63f9322015-05-06 15:18:49 -04003758 mBigLargeIcon = icon;
Chris Wren3745a3d2012-05-22 15:11:52 -04003759 return this;
3760 }
3761
Riley Andrews0394a0c2015-11-03 23:36:52 -08003762 /** @hide */
3763 public static final int MIN_ASHMEM_BITMAP_SIZE = 128 * (1 << 10);
3764
Daniel Sandler0ec46202015-06-24 01:27:05 -04003765 /**
3766 * @hide
3767 */
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07003768 @Override
3769 public void purgeResources() {
3770 super.purgeResources();
Riley Andrews8cee7c12015-11-01 23:36:04 -08003771 if (mPicture != null &&
3772 mPicture.isMutable() &&
Riley Andrews0394a0c2015-11-03 23:36:52 -08003773 mPicture.getAllocationByteCount() >= MIN_ASHMEM_BITMAP_SIZE) {
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07003774 mPicture = mPicture.createAshmemBitmap();
3775 }
3776 if (mBigLargeIcon != null) {
3777 mBigLargeIcon.convertToAshmem();
3778 }
3779 }
Christoph Studer5c510ee2014-12-15 16:32:27 +01003780
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003781 /**
3782 * @hide
3783 */
3784 public RemoteViews makeBigContentView() {
3785 // Replace mN.mLargeIcon with mBigLargeIcon if mBigLargeIconSet
Christoph Studer5c510ee2014-12-15 16:32:27 +01003786 // This covers the following cases:
3787 // 1. mBigLargeIconSet -> mBigLargeIcon (null or non-null) applies, overrides
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003788 // mN.mLargeIcon
3789 // 2. !mBigLargeIconSet -> mN.mLargeIcon applies
Dan Sandlerd63f9322015-05-06 15:18:49 -04003790 Icon oldLargeIcon = null;
Christoph Studer5c510ee2014-12-15 16:32:27 +01003791 if (mBigLargeIconSet) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003792 oldLargeIcon = mBuilder.mN.mLargeIcon;
3793 mBuilder.mN.mLargeIcon = mBigLargeIcon;
Christoph Studer5c510ee2014-12-15 16:32:27 +01003794 }
3795
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003796 RemoteViews contentView = getStandardView(mBuilder.getBigPictureLayoutResource());
Selim Cinek03d0d652015-11-13 13:18:09 -05003797 if (mSummaryTextSet) {
3798 contentView.setTextViewText(R.id.text, mBuilder.processLegacyText(mSummaryText));
3799 contentView.setViewVisibility(R.id.line3, View.VISIBLE);
3800 }
Selim Cinek53e64a42015-11-16 10:40:56 -08003801 int imageMinHeight = mBuilder.mContext.getResources().getDimensionPixelSize(
3802 R.dimen.notification_big_picture_content_min_height_with_picture);
3803 // We need to make space for the right image, so we're enforcing a minheight if there
3804 // is a picture.
3805 int minHeight = (mBuilder.mN.mLargeIcon == null) ? 0 : imageMinHeight;
3806 contentView.setInt(R.id.notification_main_column, "setMinimumHeight", minHeight);
3807
Christoph Studer5c510ee2014-12-15 16:32:27 +01003808 if (mBigLargeIconSet) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003809 mBuilder.mN.mLargeIcon = oldLargeIcon;
Christoph Studer5c510ee2014-12-15 16:32:27 +01003810 }
3811
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003812 contentView.setImageViewBitmap(R.id.big_picture, mPicture);
3813
Selim Cinek29603462015-11-17 19:04:39 -08003814 mBuilder.addProfileBadge(contentView, R.id.profile_badge_line3);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003815 return contentView;
3816 }
3817
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003818 /**
3819 * @hide
3820 */
3821 public void addExtras(Bundle extras) {
3822 super.addExtras(extras);
3823
3824 if (mBigLargeIconSet) {
3825 extras.putParcelable(EXTRA_LARGE_ICON_BIG, mBigLargeIcon);
3826 }
3827 extras.putParcelable(EXTRA_PICTURE, mPicture);
3828 }
3829
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04003830 /**
3831 * @hide
3832 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003833 @Override
Christoph Studer4600f9b2014-07-22 22:44:43 +02003834 protected void restoreFromExtras(Bundle extras) {
3835 super.restoreFromExtras(extras);
3836
3837 if (extras.containsKey(EXTRA_LARGE_ICON_BIG)) {
Christoph Studer5c510ee2014-12-15 16:32:27 +01003838 mBigLargeIconSet = true;
Christoph Studer4600f9b2014-07-22 22:44:43 +02003839 mBigLargeIcon = extras.getParcelable(EXTRA_LARGE_ICON_BIG);
Chris Wren3745a3d2012-05-22 15:11:52 -04003840 }
Christoph Studer4600f9b2014-07-22 22:44:43 +02003841 mPicture = extras.getParcelable(EXTRA_PICTURE);
3842 }
Selim Cinek03d0d652015-11-13 13:18:09 -05003843
3844 /**
3845 * @hide
3846 */
3847 @Override
3848 public boolean hasSummaryInHeader() {
3849 return false;
3850 }
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003851 }
3852
3853 /**
Daniel Sandler4dfbe832012-04-11 14:51:46 -04003854 * Helper class for generating large-format notifications that include a lot of text.
Joe Malin8d40d042012-11-05 11:36:40 -08003855 *
Robert Ly91c5ce32014-06-08 15:37:00 -07003856 * Here's how you'd set the <code>BigTextStyle</code> on a notification:
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003857 * <pre class="prettyprint">
Robert Ly91c5ce32014-06-08 15:37:00 -07003858 * Notification notif = new Notification.Builder(mContext)
3859 * .setContentTitle(&quot;New mail from &quot; + sender.toString())
3860 * .setContentText(subject)
3861 * .setSmallIcon(R.drawable.new_mail)
3862 * .setLargeIcon(aBitmap)
3863 * .setStyle(new Notification.BigTextStyle()
3864 * .bigText(aVeryLongString))
3865 * .build();
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003866 * </pre>
Joe Malin8d40d042012-11-05 11:36:40 -08003867 *
Daniel Sandler4dfbe832012-04-11 14:51:46 -04003868 * @see Notification#bigContentView
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003869 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003870 public static class BigTextStyle extends Style {
Jorim Jaggi457a10d2014-09-08 16:18:23 +02003871
3872 private static final int MAX_LINES = 13;
3873 private static final int LINES_CONSUMED_BY_ACTIONS = 3;
3874 private static final int LINES_CONSUMED_BY_SUMMARY = 2;
3875
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003876 private CharSequence mBigText;
3877
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003878 public BigTextStyle() {
3879 }
3880
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003881 public BigTextStyle(Builder builder) {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003882 setBuilder(builder);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003883 }
3884
Chris Wrend6297db2012-05-03 16:20:13 -04003885 /**
3886 * Overrides ContentTitle in the big form of the template.
3887 * This defaults to the value passed to setContentTitle().
3888 */
3889 public BigTextStyle setBigContentTitle(CharSequence title) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04003890 internalSetBigContentTitle(safeCharSequence(title));
Chris Wrend6297db2012-05-03 16:20:13 -04003891 return this;
3892 }
3893
3894 /**
3895 * Set the first line of text after the detail section in the big form of the template.
3896 */
3897 public BigTextStyle setSummaryText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04003898 internalSetSummaryText(safeCharSequence(cs));
Chris Wrend6297db2012-05-03 16:20:13 -04003899 return this;
3900 }
3901
Chris Wren0bd664d2012-08-01 13:56:56 -04003902 /**
3903 * Provide the longer text to be displayed in the big form of the
3904 * template in place of the content text.
3905 */
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003906 public BigTextStyle bigText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04003907 mBigText = safeCharSequence(cs);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003908 return this;
3909 }
3910
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003911 /**
3912 * @hide
3913 */
3914 public void addExtras(Bundle extras) {
3915 super.addExtras(extras);
3916
Christoph Studer4600f9b2014-07-22 22:44:43 +02003917 extras.putCharSequence(EXTRA_BIG_TEXT, mBigText);
3918 }
3919
3920 /**
3921 * @hide
3922 */
3923 @Override
3924 protected void restoreFromExtras(Bundle extras) {
3925 super.restoreFromExtras(extras);
3926
3927 mBigText = extras.getCharSequence(EXTRA_BIG_TEXT);
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003928 }
3929
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003930 /**
3931 * @hide
3932 */
3933 public RemoteViews makeBigContentView() {
Christoph Studer4600f9b2014-07-22 22:44:43 +02003934
3935 // Nasty
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003936 CharSequence oldBuilderContentText =
3937 mBuilder.getAllExtras().getCharSequence(EXTRA_TEXT);
3938 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, null);
Daniel Sandler916ad912012-06-13 12:17:07 -04003939
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003940 RemoteViews contentView = getStandardView(mBuilder.getBigTextLayoutResource());
Joe Malin8d40d042012-11-05 11:36:40 -08003941
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003942 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, oldBuilderContentText);
Christoph Studer4600f9b2014-07-22 22:44:43 +02003943
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003944 contentView.setTextViewText(R.id.big_text, mBuilder.processLegacyText(mBigText));
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003945 contentView.setViewVisibility(R.id.big_text, View.VISIBLE);
Jorim Jaggi457a10d2014-09-08 16:18:23 +02003946 contentView.setInt(R.id.big_text, "setMaxLines", calculateMaxLines());
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003947
Kenny Guy98193ea2014-07-24 19:54:37 +01003948 mBuilder.addProfileBadge(contentView, R.id.profile_badge_large_template);
3949
Selim Cinek4fb12d32015-11-19 18:10:48 -08003950 contentView.setBoolean(R.id.big_text, "setHasImage", mBuilder.mN.mLargeIcon != null);
3951
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003952 return contentView;
3953 }
3954
Jorim Jaggi457a10d2014-09-08 16:18:23 +02003955 private int calculateMaxLines() {
3956 int lineCount = MAX_LINES;
3957 boolean hasActions = mBuilder.mActions.size() > 0;
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003958 boolean hasSummary = (mSummaryTextSet ? mSummaryText
3959 : mBuilder.getAllExtras().getCharSequence(EXTRA_SUB_TEXT)) != null;
Jorim Jaggi457a10d2014-09-08 16:18:23 +02003960 if (hasActions) {
3961 lineCount -= LINES_CONSUMED_BY_ACTIONS;
3962 }
3963 if (hasSummary) {
3964 lineCount -= LINES_CONSUMED_BY_SUMMARY;
3965 }
Jorim Jaggi457a10d2014-09-08 16:18:23 +02003966 return lineCount;
3967 }
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003968 }
Daniel Sandler879c5e02012-04-17 16:46:51 -04003969
3970 /**
3971 * Helper class for generating large-format notifications that include a list of (up to 5) strings.
Joe Malin8d40d042012-11-05 11:36:40 -08003972 *
Robert Ly91c5ce32014-06-08 15:37:00 -07003973 * Here's how you'd set the <code>InboxStyle</code> on a notification:
Daniel Sandler879c5e02012-04-17 16:46:51 -04003974 * <pre class="prettyprint">
Robert Ly91c5ce32014-06-08 15:37:00 -07003975 * Notification notif = new Notification.Builder(mContext)
3976 * .setContentTitle(&quot;5 New mails from &quot; + sender.toString())
3977 * .setContentText(subject)
3978 * .setSmallIcon(R.drawable.new_mail)
3979 * .setLargeIcon(aBitmap)
3980 * .setStyle(new Notification.InboxStyle()
3981 * .addLine(str1)
3982 * .addLine(str2)
3983 * .setContentTitle(&quot;&quot;)
3984 * .setSummaryText(&quot;+3 more&quot;))
3985 * .build();
Daniel Sandler879c5e02012-04-17 16:46:51 -04003986 * </pre>
Joe Malin8d40d042012-11-05 11:36:40 -08003987 *
Daniel Sandler879c5e02012-04-17 16:46:51 -04003988 * @see Notification#bigContentView
3989 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003990 public static class InboxStyle extends Style {
Daniel Sandler879c5e02012-04-17 16:46:51 -04003991 private ArrayList<CharSequence> mTexts = new ArrayList<CharSequence>(5);
3992
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003993 public InboxStyle() {
3994 }
3995
Daniel Sandler879c5e02012-04-17 16:46:51 -04003996 public InboxStyle(Builder builder) {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003997 setBuilder(builder);
Daniel Sandler879c5e02012-04-17 16:46:51 -04003998 }
3999
Chris Wrend6297db2012-05-03 16:20:13 -04004000 /**
4001 * Overrides ContentTitle in the big form of the template.
4002 * This defaults to the value passed to setContentTitle().
4003 */
4004 public InboxStyle setBigContentTitle(CharSequence title) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04004005 internalSetBigContentTitle(safeCharSequence(title));
Chris Wrend6297db2012-05-03 16:20:13 -04004006 return this;
4007 }
4008
4009 /**
4010 * Set the first line of text after the detail section in the big form of the template.
4011 */
4012 public InboxStyle setSummaryText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04004013 internalSetSummaryText(safeCharSequence(cs));
Chris Wrend6297db2012-05-03 16:20:13 -04004014 return this;
4015 }
4016
Chris Wren0bd664d2012-08-01 13:56:56 -04004017 /**
4018 * Append a line to the digest section of the Inbox notification.
4019 */
Daniel Sandler879c5e02012-04-17 16:46:51 -04004020 public InboxStyle addLine(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04004021 mTexts.add(safeCharSequence(cs));
Daniel Sandler879c5e02012-04-17 16:46:51 -04004022 return this;
4023 }
4024
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004025 /**
4026 * @hide
4027 */
4028 public void addExtras(Bundle extras) {
4029 super.addExtras(extras);
Christoph Studer4600f9b2014-07-22 22:44:43 +02004030
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004031 CharSequence[] a = new CharSequence[mTexts.size()];
4032 extras.putCharSequenceArray(EXTRA_TEXT_LINES, mTexts.toArray(a));
4033 }
4034
Christoph Studer4600f9b2014-07-22 22:44:43 +02004035 /**
4036 * @hide
4037 */
4038 @Override
4039 protected void restoreFromExtras(Bundle extras) {
4040 super.restoreFromExtras(extras);
4041
4042 mTexts.clear();
4043 if (extras.containsKey(EXTRA_TEXT_LINES)) {
4044 Collections.addAll(mTexts, extras.getCharSequenceArray(EXTRA_TEXT_LINES));
4045 }
4046 }
4047
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004048 /**
4049 * @hide
4050 */
4051 public RemoteViews makeBigContentView() {
Daniel Sandler619738c2012-06-07 16:33:08 -04004052 // Remove the content text so line3 disappears unless you have a summary
Christoph Studer4600f9b2014-07-22 22:44:43 +02004053 // Nasty
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004054 CharSequence oldBuilderContentText = mBuilder.mN.extras.getCharSequence(EXTRA_TEXT);
4055 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, null);
Christoph Studer4600f9b2014-07-22 22:44:43 +02004056
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004057 RemoteViews contentView = getStandardView(mBuilder.getInboxLayoutResource());
Daniel Sandler619738c2012-06-07 16:33:08 -04004058
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004059 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, oldBuilderContentText);
Christoph Studer4600f9b2014-07-22 22:44:43 +02004060
Chris Wrend6297db2012-05-03 16:20:13 -04004061 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 -04004062 R.id.inbox_text4, R.id.inbox_text5, R.id.inbox_text6};
Chris Wrend6297db2012-05-03 16:20:13 -04004063
Chris Wren4ed80d52012-05-17 09:30:03 -04004064 // Make sure all rows are gone in case we reuse a view.
4065 for (int rowId : rowIds) {
4066 contentView.setViewVisibility(rowId, View.GONE);
4067 }
4068
Jorim Jaggi445d3c02014-08-19 22:33:42 +02004069 final boolean largeText =
4070 mBuilder.mContext.getResources().getConfiguration().fontScale > 1f;
4071 final float subTextSize = mBuilder.mContext.getResources().getDimensionPixelSize(
4072 R.dimen.notification_subtext_size);
Daniel Sandler879c5e02012-04-17 16:46:51 -04004073 int i=0;
Selim Cinek9d9fc6e2015-11-12 15:49:14 -05004074 final float density = mBuilder.mContext.getResources().getDisplayMetrics().density;
4075 int topPadding = (int) (5 * density);
4076 int bottomPadding = (int) (13 * density);
Daniel Sandler879c5e02012-04-17 16:46:51 -04004077 while (i < mTexts.size() && i < rowIds.length) {
4078 CharSequence str = mTexts.get(i);
4079 if (str != null && !str.equals("")) {
4080 contentView.setViewVisibility(rowIds[i], View.VISIBLE);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004081 contentView.setTextViewText(rowIds[i], mBuilder.processLegacyText(str));
Jorim Jaggi445d3c02014-08-19 22:33:42 +02004082 if (largeText) {
4083 contentView.setTextViewTextSize(rowIds[i], TypedValue.COMPLEX_UNIT_PX,
4084 subTextSize);
4085 }
Selim Cinek9d9fc6e2015-11-12 15:49:14 -05004086 contentView.setViewPadding(rowIds[i], 0, topPadding, 0,
4087 i == rowIds.length - 1 || i == mTexts.size() - 1 ? bottomPadding : 0);
Daniel Sandler879c5e02012-04-17 16:46:51 -04004088 }
4089 i++;
4090 }
Kenny Guy98193ea2014-07-24 19:54:37 +01004091 mBuilder.addProfileBadge(contentView, R.id.profile_badge_large_template);
4092
Selim Cinek1e0bf612015-11-20 15:57:26 -08004093 handleInboxImageMargin(contentView, rowIds[0]);
4094
Daniel Sandler879c5e02012-04-17 16:46:51 -04004095 return contentView;
4096 }
Selim Cinek1e0bf612015-11-20 15:57:26 -08004097
4098 private void handleInboxImageMargin(RemoteViews contentView, int id) {
4099 final int max = mBuilder.mN.extras.getInt(EXTRA_PROGRESS_MAX, 0);
4100 final boolean ind = mBuilder.mN.extras.getBoolean(EXTRA_PROGRESS_INDETERMINATE);
4101 boolean hasProgress = max != 0 || ind;
4102 int endMargin = 0;
4103 if (mTexts.size() > 0 && mBuilder.mN.mLargeIcon != null && !hasProgress) {
4104 endMargin = mBuilder.mContext.getResources().getDimensionPixelSize(
4105 R.dimen.notification_content_picture_margin);
4106 }
4107 contentView.setViewLayoutMarginEnd(id, endMargin);
4108 }
Daniel Sandler879c5e02012-04-17 16:46:51 -04004109 }
Dan Sandler842dd772014-05-15 09:36:47 -04004110
4111 /**
4112 * Notification style for media playback notifications.
4113 *
4114 * In the expanded form, {@link Notification#bigContentView}, up to 5
4115 * {@link Notification.Action}s specified with
Dan Sandler86647982015-05-13 23:41:13 -04004116 * {@link Notification.Builder#addAction(Action) addAction} will be
Dan Sandler842dd772014-05-15 09:36:47 -04004117 * shown as icon-only pushbuttons, suitable for transport controls. The Bitmap given to
4118 * {@link Notification.Builder#setLargeIcon(android.graphics.Bitmap) setLargeIcon()} will be
4119 * treated as album artwork.
4120 *
4121 * Unlike the other styles provided here, MediaStyle can also modify the standard-size
4122 * {@link Notification#contentView}; by providing action indices to
Christoph Studerfde6f4d2014-12-12 13:23:26 +01004123 * {@link #setShowActionsInCompactView(int...)} you can promote up to 3 actions to be displayed
Dan Sandler842dd772014-05-15 09:36:47 -04004124 * in the standard view alongside the usual content.
4125 *
Bryan Mawhinney6be8de32014-07-18 10:35:12 +01004126 * Notifications created with MediaStyle will have their category set to
4127 * {@link Notification#CATEGORY_TRANSPORT CATEGORY_TRANSPORT} unless you set a different
4128 * category using {@link Notification.Builder#setCategory(String) setCategory()}.
4129 *
Jeff Browndba34ba2014-06-24 20:46:03 -07004130 * Finally, if you attach a {@link android.media.session.MediaSession.Token} using
4131 * {@link android.app.Notification.MediaStyle#setMediaSession(MediaSession.Token)},
Dan Sandler842dd772014-05-15 09:36:47 -04004132 * the System UI can identify this as a notification representing an active media session
4133 * and respond accordingly (by showing album artwork in the lockscreen, for example).
4134 *
4135 * To use this style with your Notification, feed it to
4136 * {@link Notification.Builder#setStyle(android.app.Notification.Style)} like so:
4137 * <pre class="prettyprint">
4138 * Notification noti = new Notification.Builder()
4139 * .setSmallIcon(R.drawable.ic_stat_player)
Christoph Studere935fe92014-11-24 14:18:06 +01004140 * .setContentTitle(&quot;Track title&quot;)
4141 * .setContentText(&quot;Artist - Album&quot;)
4142 * .setLargeIcon(albumArtBitmap))
Bryan Mawhinney6be8de32014-07-18 10:35:12 +01004143 * .setStyle(<b>new Notification.MediaStyle()</b>
4144 * .setMediaSession(mySession))
Dan Sandler842dd772014-05-15 09:36:47 -04004145 * .build();
4146 * </pre>
4147 *
4148 * @see Notification#bigContentView
4149 */
4150 public static class MediaStyle extends Style {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004151 static final int MAX_MEDIA_BUTTONS_IN_COMPACT = 3;
Dan Sandler842dd772014-05-15 09:36:47 -04004152 static final int MAX_MEDIA_BUTTONS = 5;
4153
4154 private int[] mActionsToShowInCompact = null;
Jeff Browndba34ba2014-06-24 20:46:03 -07004155 private MediaSession.Token mToken;
Dan Sandler842dd772014-05-15 09:36:47 -04004156
4157 public MediaStyle() {
4158 }
4159
4160 public MediaStyle(Builder builder) {
4161 setBuilder(builder);
4162 }
4163
4164 /**
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004165 * 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 -04004166 * notification view.
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004167 *
4168 * @param actions the indices of the actions to show in the compact notification view
Dan Sandler842dd772014-05-15 09:36:47 -04004169 */
4170 public MediaStyle setShowActionsInCompactView(int...actions) {
4171 mActionsToShowInCompact = actions;
4172 return this;
4173 }
4174
4175 /**
Jeff Browndba34ba2014-06-24 20:46:03 -07004176 * Attach a {@link android.media.session.MediaSession.Token} to this Notification
4177 * to provide additional playback information and control to the SystemUI.
Dan Sandler842dd772014-05-15 09:36:47 -04004178 */
Jeff Browndba34ba2014-06-24 20:46:03 -07004179 public MediaStyle setMediaSession(MediaSession.Token token) {
Dan Sandler842dd772014-05-15 09:36:47 -04004180 mToken = token;
4181 return this;
4182 }
4183
Christoph Studer4600f9b2014-07-22 22:44:43 +02004184 /**
4185 * @hide
4186 */
Dan Sandler842dd772014-05-15 09:36:47 -04004187 @Override
4188 public Notification buildStyled(Notification wip) {
Christoph Studer4600f9b2014-07-22 22:44:43 +02004189 super.buildStyled(wip);
Bryan Mawhinney6be8de32014-07-18 10:35:12 +01004190 if (wip.category == null) {
4191 wip.category = Notification.CATEGORY_TRANSPORT;
4192 }
Dan Sandler842dd772014-05-15 09:36:47 -04004193 return wip;
4194 }
4195
Christoph Studer4600f9b2014-07-22 22:44:43 +02004196 /**
4197 * @hide
4198 */
4199 @Override
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004200 public RemoteViews makeContentView() {
4201 return makeMediaContentView();
Christoph Studer4600f9b2014-07-22 22:44:43 +02004202 }
4203
4204 /**
4205 * @hide
4206 */
4207 @Override
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004208 public RemoteViews makeBigContentView() {
4209 return makeMediaBigContentView();
Christoph Studer4600f9b2014-07-22 22:44:43 +02004210 }
4211
Dan Sandler842dd772014-05-15 09:36:47 -04004212 /** @hide */
4213 @Override
4214 public void addExtras(Bundle extras) {
4215 super.addExtras(extras);
4216
4217 if (mToken != null) {
4218 extras.putParcelable(EXTRA_MEDIA_SESSION, mToken);
4219 }
Bryan Mawhinneye191f902014-07-22 12:50:09 +01004220 if (mActionsToShowInCompact != null) {
4221 extras.putIntArray(EXTRA_COMPACT_ACTIONS, mActionsToShowInCompact);
4222 }
Dan Sandler842dd772014-05-15 09:36:47 -04004223 }
4224
Christoph Studer4600f9b2014-07-22 22:44:43 +02004225 /**
4226 * @hide
4227 */
4228 @Override
4229 protected void restoreFromExtras(Bundle extras) {
4230 super.restoreFromExtras(extras);
4231
4232 if (extras.containsKey(EXTRA_MEDIA_SESSION)) {
4233 mToken = extras.getParcelable(EXTRA_MEDIA_SESSION);
4234 }
4235 if (extras.containsKey(EXTRA_COMPACT_ACTIONS)) {
4236 mActionsToShowInCompact = extras.getIntArray(EXTRA_COMPACT_ACTIONS);
4237 }
4238 }
4239
Selim Cinek5bf069a2015-11-10 19:14:27 -05004240 private RemoteViews generateMediaActionButton(Action action, int color) {
Dan Sandler842dd772014-05-15 09:36:47 -04004241 final boolean tombstone = (action.actionIntent == null);
Selim Cinekf33b1112015-07-15 17:45:11 -07004242 RemoteViews button = new BuilderRemoteViews(mBuilder.mContext.getApplicationInfo(),
Alan Viverette3cb07a462014-06-06 14:19:53 -07004243 R.layout.notification_material_media_action);
Dan Sandler68079d52015-07-22 10:45:30 -04004244 button.setImageViewIcon(R.id.action0, action.getIcon());
Selim Cinek5bf069a2015-11-10 19:14:27 -05004245 button.setDrawableParameters(R.id.action0, false, -1, color, PorterDuff.Mode.SRC_ATOP,
4246 -1);
Dan Sandler842dd772014-05-15 09:36:47 -04004247 if (!tombstone) {
4248 button.setOnClickPendingIntent(R.id.action0, action.actionIntent);
4249 }
4250 button.setContentDescription(R.id.action0, action.title);
4251 return button;
4252 }
4253
4254 private RemoteViews makeMediaContentView() {
4255 RemoteViews view = mBuilder.applyStandardTemplate(
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004256 R.layout.notification_template_material_media, false /* hasProgress */);
Dan Sandler842dd772014-05-15 09:36:47 -04004257
4258 final int numActions = mBuilder.mActions.size();
4259 final int N = mActionsToShowInCompact == null
4260 ? 0
4261 : Math.min(mActionsToShowInCompact.length, MAX_MEDIA_BUTTONS_IN_COMPACT);
4262 if (N > 0) {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004263 view.removeAllViews(com.android.internal.R.id.media_actions);
Dan Sandler842dd772014-05-15 09:36:47 -04004264 for (int i = 0; i < N; i++) {
4265 if (i >= numActions) {
4266 throw new IllegalArgumentException(String.format(
4267 "setShowActionsInCompactView: action %d out of bounds (max %d)",
4268 i, numActions - 1));
4269 }
4270
4271 final Action action = mBuilder.mActions.get(mActionsToShowInCompact[i]);
Selim Cinek5bf069a2015-11-10 19:14:27 -05004272 final RemoteViews button = generateMediaActionButton(action,
4273 mBuilder.resolveColor());
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004274 view.addView(com.android.internal.R.id.media_actions, button);
Dan Sandler842dd772014-05-15 09:36:47 -04004275 }
4276 }
Selim Cinek5bf069a2015-11-10 19:14:27 -05004277 handleImage(view /* addPaddingToMainColumn */);
Dan Sandler842dd772014-05-15 09:36:47 -04004278 return view;
4279 }
4280
4281 private RemoteViews makeMediaBigContentView() {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004282 final int actionCount = Math.min(mBuilder.mActions.size(), MAX_MEDIA_BUTTONS);
Selim Cinek5bf069a2015-11-10 19:14:27 -05004283 RemoteViews big = mBuilder.applyStandardTemplate(
4284 R.layout.notification_template_material_big_media,
4285 false);
Dan Sandler842dd772014-05-15 09:36:47 -04004286
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004287 if (actionCount > 0) {
4288 big.removeAllViews(com.android.internal.R.id.media_actions);
4289 for (int i = 0; i < actionCount; i++) {
Selim Cinek5bf069a2015-11-10 19:14:27 -05004290 final RemoteViews button = generateMediaActionButton(mBuilder.mActions.get(i),
4291 mBuilder.resolveColor());
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004292 big.addView(com.android.internal.R.id.media_actions, button);
Dan Sandler842dd772014-05-15 09:36:47 -04004293 }
4294 }
Selim Cinek5bf069a2015-11-10 19:14:27 -05004295 handleImage(big);
Dan Sandler842dd772014-05-15 09:36:47 -04004296 return big;
4297 }
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004298
Selim Cinek5bf069a2015-11-10 19:14:27 -05004299 private void handleImage(RemoteViews contentView) {
4300 if (mBuilder.mN.mLargeIcon != null) {
4301 contentView.setViewLayoutMarginEnd(R.id.line1, 0);
Selim Cinek5bf069a2015-11-10 19:14:27 -05004302 contentView.setViewLayoutMarginEnd(R.id.line3, 0);
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004303 }
4304 }
4305
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004306 /**
4307 * @hide
4308 */
4309 @Override
4310 protected boolean hasProgress() {
4311 return false;
4312 }
Dan Sandler842dd772014-05-15 09:36:47 -04004313 }
Griff Hazen61a9e862014-05-22 16:05:19 -07004314
Christoph Studer4600f9b2014-07-22 22:44:43 +02004315 // When adding a new Style subclass here, don't forget to update
4316 // Builder.getNotificationStyleClass.
4317
Griff Hazen61a9e862014-05-22 16:05:19 -07004318 /**
4319 * Extender interface for use with {@link Builder#extend}. Extenders may be used to add
4320 * metadata or change options on a notification builder.
4321 */
4322 public interface Extender {
4323 /**
4324 * Apply this extender to a notification builder.
4325 * @param builder the builder to be modified.
4326 * @return the build object for chaining.
4327 */
4328 public Builder extend(Builder builder);
4329 }
4330
4331 /**
4332 * Helper class to add wearable extensions to notifications.
4333 * <p class="note"> See
4334 * <a href="{@docRoot}wear/notifications/creating.html">Creating Notifications
4335 * for Android Wear</a> for more information on how to use this class.
4336 * <p>
4337 * To create a notification with wearable extensions:
4338 * <ol>
4339 * <li>Create a {@link android.app.Notification.Builder}, setting any desired
4340 * properties.
4341 * <li>Create a {@link android.app.Notification.WearableExtender}.
4342 * <li>Set wearable-specific properties using the
4343 * {@code add} and {@code set} methods of {@link android.app.Notification.WearableExtender}.
4344 * <li>Call {@link android.app.Notification.Builder#extend} to apply the extensions to a
4345 * notification.
4346 * <li>Post the notification to the notification system with the
4347 * {@code NotificationManager.notify(...)} methods.
4348 * </ol>
4349 *
4350 * <pre class="prettyprint">
4351 * Notification notif = new Notification.Builder(mContext)
4352 * .setContentTitle(&quot;New mail from &quot; + sender.toString())
4353 * .setContentText(subject)
4354 * .setSmallIcon(R.drawable.new_mail)
4355 * .extend(new Notification.WearableExtender()
4356 * .setContentIcon(R.drawable.new_mail))
4357 * .build();
4358 * NotificationManager notificationManger =
4359 * (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
4360 * notificationManger.notify(0, notif);</pre>
4361 *
4362 * <p>Wearable extensions can be accessed on an existing notification by using the
4363 * {@code WearableExtender(Notification)} constructor,
4364 * and then using the {@code get} methods to access values.
4365 *
4366 * <pre class="prettyprint">
4367 * Notification.WearableExtender wearableExtender = new Notification.WearableExtender(
4368 * notification);
Griff Hazen14f57992014-05-26 09:07:14 -07004369 * List&lt;Notification&gt; pages = wearableExtender.getPages();</pre>
Griff Hazen61a9e862014-05-22 16:05:19 -07004370 */
4371 public static final class WearableExtender implements Extender {
4372 /**
4373 * Sentinel value for an action index that is unset.
4374 */
4375 public static final int UNSET_ACTION_INDEX = -1;
4376
4377 /**
4378 * Size value for use with {@link #setCustomSizePreset} to show this notification with
4379 * default sizing.
4380 * <p>For custom display notifications created using {@link #setDisplayIntent},
Paul Soulosaa4f4bf2015-08-04 11:59:45 -07004381 * the default is {@link #SIZE_MEDIUM}. All other notifications size automatically based
Griff Hazen61a9e862014-05-22 16:05:19 -07004382 * on their content.
4383 */
4384 public static final int SIZE_DEFAULT = 0;
4385
4386 /**
4387 * Size value for use with {@link #setCustomSizePreset} to show this notification
4388 * with an extra small size.
4389 * <p>This value is only applicable for custom display notifications created using
4390 * {@link #setDisplayIntent}.
4391 */
4392 public static final int SIZE_XSMALL = 1;
4393
4394 /**
4395 * Size value for use with {@link #setCustomSizePreset} to show this notification
4396 * with a small size.
4397 * <p>This value is only applicable for custom display notifications created using
4398 * {@link #setDisplayIntent}.
4399 */
4400 public static final int SIZE_SMALL = 2;
4401
4402 /**
4403 * Size value for use with {@link #setCustomSizePreset} to show this notification
4404 * with a medium size.
4405 * <p>This value is only applicable for custom display notifications created using
4406 * {@link #setDisplayIntent}.
4407 */
4408 public static final int SIZE_MEDIUM = 3;
4409
4410 /**
4411 * Size value for use with {@link #setCustomSizePreset} to show this notification
4412 * with a large size.
4413 * <p>This value is only applicable for custom display notifications created using
4414 * {@link #setDisplayIntent}.
4415 */
4416 public static final int SIZE_LARGE = 4;
4417
Griff Hazend5f11f92014-05-27 15:40:09 -07004418 /**
4419 * Size value for use with {@link #setCustomSizePreset} to show this notification
4420 * full screen.
4421 * <p>This value is only applicable for custom display notifications created using
4422 * {@link #setDisplayIntent}.
4423 */
4424 public static final int SIZE_FULL_SCREEN = 5;
4425
Griff Hazen5f2edfc2014-09-29 16:28:44 -07004426 /**
4427 * Sentinel value for use with {@link #setHintScreenTimeout} to keep the screen on for a
4428 * short amount of time when this notification is displayed on the screen. This
4429 * is the default value.
4430 */
4431 public static final int SCREEN_TIMEOUT_SHORT = 0;
4432
4433 /**
4434 * Sentinel value for use with {@link #setHintScreenTimeout} to keep the screen on
4435 * for a longer amount of time when this notification is displayed on the screen.
4436 */
4437 public static final int SCREEN_TIMEOUT_LONG = -1;
4438
Griff Hazen61a9e862014-05-22 16:05:19 -07004439 /** Notification extra which contains wearable extensions */
4440 private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS";
4441
Pete Gastaf6781d2014-10-07 15:17:05 -04004442 // Keys within EXTRA_WEARABLE_EXTENSIONS for wearable options.
Griff Hazen61a9e862014-05-22 16:05:19 -07004443 private static final String KEY_ACTIONS = "actions";
4444 private static final String KEY_FLAGS = "flags";
4445 private static final String KEY_DISPLAY_INTENT = "displayIntent";
4446 private static final String KEY_PAGES = "pages";
4447 private static final String KEY_BACKGROUND = "background";
4448 private static final String KEY_CONTENT_ICON = "contentIcon";
4449 private static final String KEY_CONTENT_ICON_GRAVITY = "contentIconGravity";
4450 private static final String KEY_CONTENT_ACTION_INDEX = "contentActionIndex";
4451 private static final String KEY_CUSTOM_SIZE_PRESET = "customSizePreset";
4452 private static final String KEY_CUSTOM_CONTENT_HEIGHT = "customContentHeight";
4453 private static final String KEY_GRAVITY = "gravity";
Griff Hazen5f2edfc2014-09-29 16:28:44 -07004454 private static final String KEY_HINT_SCREEN_TIMEOUT = "hintScreenTimeout";
Griff Hazen61a9e862014-05-22 16:05:19 -07004455
4456 // Flags bitwise-ored to mFlags
4457 private static final int FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE = 0x1;
4458 private static final int FLAG_HINT_HIDE_ICON = 1 << 1;
4459 private static final int FLAG_HINT_SHOW_BACKGROUND_ONLY = 1 << 2;
4460 private static final int FLAG_START_SCROLL_BOTTOM = 1 << 3;
Griff Hazen5f2edfc2014-09-29 16:28:44 -07004461 private static final int FLAG_HINT_AVOID_BACKGROUND_CLIPPING = 1 << 4;
Griff Hazen61a9e862014-05-22 16:05:19 -07004462
4463 // Default value for flags integer
4464 private static final int DEFAULT_FLAGS = FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE;
4465
4466 private static final int DEFAULT_CONTENT_ICON_GRAVITY = Gravity.END;
4467 private static final int DEFAULT_GRAVITY = Gravity.BOTTOM;
4468
4469 private ArrayList<Action> mActions = new ArrayList<Action>();
4470 private int mFlags = DEFAULT_FLAGS;
4471 private PendingIntent mDisplayIntent;
4472 private ArrayList<Notification> mPages = new ArrayList<Notification>();
4473 private Bitmap mBackground;
4474 private int mContentIcon;
4475 private int mContentIconGravity = DEFAULT_CONTENT_ICON_GRAVITY;
4476 private int mContentActionIndex = UNSET_ACTION_INDEX;
4477 private int mCustomSizePreset = SIZE_DEFAULT;
4478 private int mCustomContentHeight;
4479 private int mGravity = DEFAULT_GRAVITY;
Griff Hazen5f2edfc2014-09-29 16:28:44 -07004480 private int mHintScreenTimeout;
Griff Hazen61a9e862014-05-22 16:05:19 -07004481
4482 /**
4483 * Create a {@link android.app.Notification.WearableExtender} with default
4484 * options.
4485 */
4486 public WearableExtender() {
4487 }
4488
4489 public WearableExtender(Notification notif) {
4490 Bundle wearableBundle = notif.extras.getBundle(EXTRA_WEARABLE_EXTENSIONS);
4491 if (wearableBundle != null) {
4492 List<Action> actions = wearableBundle.getParcelableArrayList(KEY_ACTIONS);
4493 if (actions != null) {
4494 mActions.addAll(actions);
4495 }
4496
4497 mFlags = wearableBundle.getInt(KEY_FLAGS, DEFAULT_FLAGS);
4498 mDisplayIntent = wearableBundle.getParcelable(KEY_DISPLAY_INTENT);
4499
4500 Notification[] pages = getNotificationArrayFromBundle(
4501 wearableBundle, KEY_PAGES);
4502 if (pages != null) {
4503 Collections.addAll(mPages, pages);
4504 }
4505
4506 mBackground = wearableBundle.getParcelable(KEY_BACKGROUND);
4507 mContentIcon = wearableBundle.getInt(KEY_CONTENT_ICON);
4508 mContentIconGravity = wearableBundle.getInt(KEY_CONTENT_ICON_GRAVITY,
4509 DEFAULT_CONTENT_ICON_GRAVITY);
4510 mContentActionIndex = wearableBundle.getInt(KEY_CONTENT_ACTION_INDEX,
4511 UNSET_ACTION_INDEX);
4512 mCustomSizePreset = wearableBundle.getInt(KEY_CUSTOM_SIZE_PRESET,
4513 SIZE_DEFAULT);
4514 mCustomContentHeight = wearableBundle.getInt(KEY_CUSTOM_CONTENT_HEIGHT);
4515 mGravity = wearableBundle.getInt(KEY_GRAVITY, DEFAULT_GRAVITY);
Griff Hazen5f2edfc2014-09-29 16:28:44 -07004516 mHintScreenTimeout = wearableBundle.getInt(KEY_HINT_SCREEN_TIMEOUT);
Griff Hazen61a9e862014-05-22 16:05:19 -07004517 }
4518 }
4519
4520 /**
4521 * Apply wearable extensions to a notification that is being built. This is typically
4522 * called by the {@link android.app.Notification.Builder#extend} method of
4523 * {@link android.app.Notification.Builder}.
4524 */
4525 @Override
4526 public Notification.Builder extend(Notification.Builder builder) {
4527 Bundle wearableBundle = new Bundle();
4528
4529 if (!mActions.isEmpty()) {
4530 wearableBundle.putParcelableArrayList(KEY_ACTIONS, mActions);
4531 }
4532 if (mFlags != DEFAULT_FLAGS) {
4533 wearableBundle.putInt(KEY_FLAGS, mFlags);
4534 }
4535 if (mDisplayIntent != null) {
4536 wearableBundle.putParcelable(KEY_DISPLAY_INTENT, mDisplayIntent);
4537 }
4538 if (!mPages.isEmpty()) {
4539 wearableBundle.putParcelableArray(KEY_PAGES, mPages.toArray(
4540 new Notification[mPages.size()]));
4541 }
4542 if (mBackground != null) {
4543 wearableBundle.putParcelable(KEY_BACKGROUND, mBackground);
4544 }
4545 if (mContentIcon != 0) {
4546 wearableBundle.putInt(KEY_CONTENT_ICON, mContentIcon);
4547 }
4548 if (mContentIconGravity != DEFAULT_CONTENT_ICON_GRAVITY) {
4549 wearableBundle.putInt(KEY_CONTENT_ICON_GRAVITY, mContentIconGravity);
4550 }
4551 if (mContentActionIndex != UNSET_ACTION_INDEX) {
4552 wearableBundle.putInt(KEY_CONTENT_ACTION_INDEX,
4553 mContentActionIndex);
4554 }
4555 if (mCustomSizePreset != SIZE_DEFAULT) {
4556 wearableBundle.putInt(KEY_CUSTOM_SIZE_PRESET, mCustomSizePreset);
4557 }
4558 if (mCustomContentHeight != 0) {
4559 wearableBundle.putInt(KEY_CUSTOM_CONTENT_HEIGHT, mCustomContentHeight);
4560 }
4561 if (mGravity != DEFAULT_GRAVITY) {
4562 wearableBundle.putInt(KEY_GRAVITY, mGravity);
4563 }
Griff Hazen5f2edfc2014-09-29 16:28:44 -07004564 if (mHintScreenTimeout != 0) {
4565 wearableBundle.putInt(KEY_HINT_SCREEN_TIMEOUT, mHintScreenTimeout);
4566 }
Griff Hazen61a9e862014-05-22 16:05:19 -07004567
4568 builder.getExtras().putBundle(EXTRA_WEARABLE_EXTENSIONS, wearableBundle);
4569 return builder;
4570 }
4571
4572 @Override
4573 public WearableExtender clone() {
4574 WearableExtender that = new WearableExtender();
4575 that.mActions = new ArrayList<Action>(this.mActions);
4576 that.mFlags = this.mFlags;
4577 that.mDisplayIntent = this.mDisplayIntent;
4578 that.mPages = new ArrayList<Notification>(this.mPages);
4579 that.mBackground = this.mBackground;
4580 that.mContentIcon = this.mContentIcon;
4581 that.mContentIconGravity = this.mContentIconGravity;
4582 that.mContentActionIndex = this.mContentActionIndex;
4583 that.mCustomSizePreset = this.mCustomSizePreset;
4584 that.mCustomContentHeight = this.mCustomContentHeight;
4585 that.mGravity = this.mGravity;
Griff Hazen5f2edfc2014-09-29 16:28:44 -07004586 that.mHintScreenTimeout = this.mHintScreenTimeout;
Griff Hazen61a9e862014-05-22 16:05:19 -07004587 return that;
4588 }
4589
4590 /**
4591 * Add a wearable action to this notification.
4592 *
4593 * <p>When wearable actions are added using this method, the set of actions that
4594 * show on a wearable device splits from devices that only show actions added
4595 * using {@link android.app.Notification.Builder#addAction}. This allows for customization
4596 * of which actions display on different devices.
4597 *
4598 * @param action the action to add to this notification
4599 * @return this object for method chaining
4600 * @see android.app.Notification.Action
4601 */
4602 public WearableExtender addAction(Action action) {
4603 mActions.add(action);
4604 return this;
4605 }
4606
4607 /**
4608 * Adds wearable actions to this notification.
4609 *
4610 * <p>When wearable actions are added using this method, the set of actions that
4611 * show on a wearable device splits from devices that only show actions added
4612 * using {@link android.app.Notification.Builder#addAction}. This allows for customization
4613 * of which actions display on different devices.
4614 *
4615 * @param actions the actions to add to this notification
4616 * @return this object for method chaining
4617 * @see android.app.Notification.Action
4618 */
4619 public WearableExtender addActions(List<Action> actions) {
4620 mActions.addAll(actions);
4621 return this;
4622 }
4623
4624 /**
4625 * Clear all wearable actions present on this builder.
4626 * @return this object for method chaining.
4627 * @see #addAction
4628 */
4629 public WearableExtender clearActions() {
4630 mActions.clear();
4631 return this;
4632 }
4633
4634 /**
4635 * Get the wearable actions present on this notification.
4636 */
4637 public List<Action> getActions() {
4638 return mActions;
4639 }
4640
4641 /**
4642 * Set an intent to launch inside of an activity view when displaying
Griff Hazen14f57992014-05-26 09:07:14 -07004643 * this notification. The {@link PendingIntent} provided should be for an activity.
4644 *
4645 * <pre class="prettyprint">
4646 * Intent displayIntent = new Intent(context, MyDisplayActivity.class);
4647 * PendingIntent displayPendingIntent = PendingIntent.getActivity(context,
4648 * 0, displayIntent, PendingIntent.FLAG_UPDATE_CURRENT);
4649 * Notification notif = new Notification.Builder(context)
4650 * .extend(new Notification.WearableExtender()
4651 * .setDisplayIntent(displayPendingIntent)
4652 * .setCustomSizePreset(Notification.WearableExtender.SIZE_MEDIUM))
4653 * .build();</pre>
4654 *
4655 * <p>The activity to launch needs to allow embedding, must be exported, and
Griff Hazen831ca9d2014-06-17 00:38:38 -07004656 * should have an empty task affinity. It is also recommended to use the device
4657 * default light theme.
Griff Hazen14f57992014-05-26 09:07:14 -07004658 *
4659 * <p>Example AndroidManifest.xml entry:
4660 * <pre class="prettyprint">
4661 * &lt;activity android:name=&quot;com.example.MyDisplayActivity&quot;
4662 * android:exported=&quot;true&quot;
4663 * android:allowEmbedded=&quot;true&quot;
Griff Hazen831ca9d2014-06-17 00:38:38 -07004664 * android:taskAffinity=&quot;&quot;
4665 * android:theme=&quot;@android:style/Theme.DeviceDefault.Light&quot; /&gt;</pre>
Griff Hazen61a9e862014-05-22 16:05:19 -07004666 *
4667 * @param intent the {@link PendingIntent} for an activity
4668 * @return this object for method chaining
4669 * @see android.app.Notification.WearableExtender#getDisplayIntent
4670 */
4671 public WearableExtender setDisplayIntent(PendingIntent intent) {
4672 mDisplayIntent = intent;
4673 return this;
4674 }
4675
4676 /**
4677 * Get the intent to launch inside of an activity view when displaying this
4678 * notification. This {@code PendingIntent} should be for an activity.
4679 */
4680 public PendingIntent getDisplayIntent() {
4681 return mDisplayIntent;
4682 }
4683
4684 /**
4685 * Add an additional page of content to display with this notification. The current
4686 * notification forms the first page, and pages added using this function form
4687 * subsequent pages. This field can be used to separate a notification into multiple
4688 * sections.
4689 *
4690 * @param page the notification to add as another page
4691 * @return this object for method chaining
4692 * @see android.app.Notification.WearableExtender#getPages
4693 */
4694 public WearableExtender addPage(Notification page) {
4695 mPages.add(page);
4696 return this;
4697 }
4698
4699 /**
4700 * Add additional pages of content to display with this notification. The current
4701 * notification forms the first page, and pages added using this function form
4702 * subsequent pages. This field can be used to separate a notification into multiple
4703 * sections.
4704 *
4705 * @param pages a list of notifications
4706 * @return this object for method chaining
4707 * @see android.app.Notification.WearableExtender#getPages
4708 */
4709 public WearableExtender addPages(List<Notification> pages) {
4710 mPages.addAll(pages);
4711 return this;
4712 }
4713
4714 /**
4715 * Clear all additional pages present on this builder.
4716 * @return this object for method chaining.
4717 * @see #addPage
4718 */
4719 public WearableExtender clearPages() {
4720 mPages.clear();
4721 return this;
4722 }
4723
4724 /**
4725 * Get the array of additional pages of content for displaying this notification. The
4726 * current notification forms the first page, and elements within this array form
4727 * subsequent pages. This field can be used to separate a notification into multiple
4728 * sections.
4729 * @return the pages for this notification
4730 */
4731 public List<Notification> getPages() {
4732 return mPages;
4733 }
4734
4735 /**
4736 * Set a background image to be displayed behind the notification content.
4737 * Contrary to the {@link android.app.Notification.BigPictureStyle}, this background
4738 * will work with any notification style.
4739 *
4740 * @param background the background bitmap
4741 * @return this object for method chaining
4742 * @see android.app.Notification.WearableExtender#getBackground
4743 */
4744 public WearableExtender setBackground(Bitmap background) {
4745 mBackground = background;
4746 return this;
4747 }
4748
4749 /**
4750 * Get a background image to be displayed behind the notification content.
4751 * Contrary to the {@link android.app.Notification.BigPictureStyle}, this background
4752 * will work with any notification style.
4753 *
4754 * @return the background image
4755 * @see android.app.Notification.WearableExtender#setBackground
4756 */
4757 public Bitmap getBackground() {
4758 return mBackground;
4759 }
4760
4761 /**
4762 * Set an icon that goes with the content of this notification.
4763 */
4764 public WearableExtender setContentIcon(int icon) {
4765 mContentIcon = icon;
4766 return this;
4767 }
4768
4769 /**
4770 * Get an icon that goes with the content of this notification.
4771 */
4772 public int getContentIcon() {
4773 return mContentIcon;
4774 }
4775
4776 /**
4777 * Set the gravity that the content icon should have within the notification display.
4778 * Supported values include {@link android.view.Gravity#START} and
4779 * {@link android.view.Gravity#END}. The default value is {@link android.view.Gravity#END}.
4780 * @see #setContentIcon
4781 */
4782 public WearableExtender setContentIconGravity(int contentIconGravity) {
4783 mContentIconGravity = contentIconGravity;
4784 return this;
4785 }
4786
4787 /**
4788 * Get the gravity that the content icon should have within the notification display.
4789 * Supported values include {@link android.view.Gravity#START} and
4790 * {@link android.view.Gravity#END}. The default value is {@link android.view.Gravity#END}.
4791 * @see #getContentIcon
4792 */
4793 public int getContentIconGravity() {
4794 return mContentIconGravity;
4795 }
4796
4797 /**
4798 * Set an action from this notification's actions to be clickable with the content of
Griff Hazen14f57992014-05-26 09:07:14 -07004799 * this notification. This action will no longer display separately from the
4800 * notification's content.
4801 *
Griff Hazenca48d352014-05-28 22:37:13 -07004802 * <p>For notifications with multiple pages, child pages can also have content actions
Griff Hazen14f57992014-05-26 09:07:14 -07004803 * set, although the list of available actions comes from the main notification and not
4804 * from the child page's notification.
4805 *
4806 * @param actionIndex The index of the action to hoist onto the current notification page.
4807 * If wearable actions were added to the main notification, this index
4808 * will apply to that list, otherwise it will apply to the regular
4809 * actions list.
Griff Hazen61a9e862014-05-22 16:05:19 -07004810 */
4811 public WearableExtender setContentAction(int actionIndex) {
4812 mContentActionIndex = actionIndex;
4813 return this;
4814 }
4815
4816 /**
Griff Hazenca48d352014-05-28 22:37:13 -07004817 * Get the index of the notification action, if any, that was specified as being clickable
4818 * with the content of this notification. This action will no longer display separately
Griff Hazen14f57992014-05-26 09:07:14 -07004819 * from the notification's content.
Griff Hazen61a9e862014-05-22 16:05:19 -07004820 *
Griff Hazenca48d352014-05-28 22:37:13 -07004821 * <p>For notifications with multiple pages, child pages can also have content actions
Griff Hazen14f57992014-05-26 09:07:14 -07004822 * set, although the list of available actions comes from the main notification and not
4823 * from the child page's notification.
4824 *
4825 * <p>If wearable specific actions were added to the main notification, this index will
4826 * apply to that list, otherwise it will apply to the regular actions list.
Griff Hazenca48d352014-05-28 22:37:13 -07004827 *
4828 * @return the action index or {@link #UNSET_ACTION_INDEX} if no action was selected.
Griff Hazen61a9e862014-05-22 16:05:19 -07004829 */
4830 public int getContentAction() {
4831 return mContentActionIndex;
4832 }
4833
4834 /**
4835 * Set the gravity that this notification should have within the available viewport space.
4836 * Supported values include {@link android.view.Gravity#TOP},
4837 * {@link android.view.Gravity#CENTER_VERTICAL} and {@link android.view.Gravity#BOTTOM}.
4838 * The default value is {@link android.view.Gravity#BOTTOM}.
4839 */
4840 public WearableExtender setGravity(int gravity) {
4841 mGravity = gravity;
4842 return this;
4843 }
4844
4845 /**
4846 * Get the gravity that this notification should have within the available viewport space.
4847 * Supported values include {@link android.view.Gravity#TOP},
4848 * {@link android.view.Gravity#CENTER_VERTICAL} and {@link android.view.Gravity#BOTTOM}.
4849 * The default value is {@link android.view.Gravity#BOTTOM}.
4850 */
4851 public int getGravity() {
4852 return mGravity;
4853 }
4854
4855 /**
4856 * Set the custom size preset for the display of this notification out of the available
4857 * presets found in {@link android.app.Notification.WearableExtender}, e.g.
4858 * {@link #SIZE_LARGE}.
4859 * <p>Some custom size presets are only applicable for custom display notifications created
4860 * using {@link android.app.Notification.WearableExtender#setDisplayIntent}. Check the
4861 * documentation for the preset in question. See also
4862 * {@link #setCustomContentHeight} and {@link #getCustomSizePreset}.
4863 */
4864 public WearableExtender setCustomSizePreset(int sizePreset) {
4865 mCustomSizePreset = sizePreset;
4866 return this;
4867 }
4868
4869 /**
4870 * Get the custom size preset for the display of this notification out of the available
4871 * presets found in {@link android.app.Notification.WearableExtender}, e.g.
4872 * {@link #SIZE_LARGE}.
4873 * <p>Some custom size presets are only applicable for custom display notifications created
4874 * using {@link #setDisplayIntent}. Check the documentation for the preset in question.
4875 * See also {@link #setCustomContentHeight} and {@link #setCustomSizePreset}.
4876 */
4877 public int getCustomSizePreset() {
4878 return mCustomSizePreset;
4879 }
4880
4881 /**
4882 * Set the custom height in pixels for the display of this notification's content.
4883 * <p>This option is only available for custom display notifications created
4884 * using {@link android.app.Notification.WearableExtender#setDisplayIntent}. See also
4885 * {@link android.app.Notification.WearableExtender#setCustomSizePreset} and
4886 * {@link #getCustomContentHeight}.
4887 */
4888 public WearableExtender setCustomContentHeight(int height) {
4889 mCustomContentHeight = height;
4890 return this;
4891 }
4892
4893 /**
4894 * Get the custom height in pixels for the display of this notification's content.
4895 * <p>This option is only available for custom display notifications created
4896 * using {@link #setDisplayIntent}. See also {@link #setCustomSizePreset} and
4897 * {@link #setCustomContentHeight}.
4898 */
4899 public int getCustomContentHeight() {
4900 return mCustomContentHeight;
4901 }
4902
4903 /**
4904 * Set whether the scrolling position for the contents of this notification should start
4905 * at the bottom of the contents instead of the top when the contents are too long to
4906 * display within the screen. Default is false (start scroll at the top).
4907 */
4908 public WearableExtender setStartScrollBottom(boolean startScrollBottom) {
4909 setFlag(FLAG_START_SCROLL_BOTTOM, startScrollBottom);
4910 return this;
4911 }
4912
4913 /**
4914 * Get whether the scrolling position for the contents of this notification should start
4915 * at the bottom of the contents instead of the top when the contents are too long to
4916 * display within the screen. Default is false (start scroll at the top).
4917 */
4918 public boolean getStartScrollBottom() {
4919 return (mFlags & FLAG_START_SCROLL_BOTTOM) != 0;
4920 }
4921
4922 /**
4923 * Set whether the content intent is available when the wearable device is not connected
4924 * to a companion device. The user can still trigger this intent when the wearable device
4925 * is offline, but a visual hint will indicate that the content intent may not be available.
4926 * Defaults to true.
4927 */
4928 public WearableExtender setContentIntentAvailableOffline(
4929 boolean contentIntentAvailableOffline) {
4930 setFlag(FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE, contentIntentAvailableOffline);
4931 return this;
4932 }
4933
4934 /**
4935 * Get whether the content intent is available when the wearable device is not connected
4936 * to a companion device. The user can still trigger this intent when the wearable device
4937 * is offline, but a visual hint will indicate that the content intent may not be available.
4938 * Defaults to true.
4939 */
4940 public boolean getContentIntentAvailableOffline() {
4941 return (mFlags & FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE) != 0;
4942 }
4943
4944 /**
4945 * Set a hint that this notification's icon should not be displayed.
4946 * @param hintHideIcon {@code true} to hide the icon, {@code false} otherwise.
4947 * @return this object for method chaining
4948 */
4949 public WearableExtender setHintHideIcon(boolean hintHideIcon) {
4950 setFlag(FLAG_HINT_HIDE_ICON, hintHideIcon);
4951 return this;
4952 }
4953
4954 /**
4955 * Get a hint that this notification's icon should not be displayed.
4956 * @return {@code true} if this icon should not be displayed, false otherwise.
4957 * The default value is {@code false} if this was never set.
4958 */
4959 public boolean getHintHideIcon() {
4960 return (mFlags & FLAG_HINT_HIDE_ICON) != 0;
4961 }
4962
4963 /**
4964 * Set a visual hint that only the background image of this notification should be
4965 * displayed, and other semantic content should be hidden. This hint is only applicable
4966 * to sub-pages added using {@link #addPage}.
4967 */
4968 public WearableExtender setHintShowBackgroundOnly(boolean hintShowBackgroundOnly) {
4969 setFlag(FLAG_HINT_SHOW_BACKGROUND_ONLY, hintShowBackgroundOnly);
4970 return this;
4971 }
4972
4973 /**
4974 * Get a visual hint that only the background image of this notification should be
4975 * displayed, and other semantic content should be hidden. This hint is only applicable
4976 * to sub-pages added using {@link android.app.Notification.WearableExtender#addPage}.
4977 */
4978 public boolean getHintShowBackgroundOnly() {
4979 return (mFlags & FLAG_HINT_SHOW_BACKGROUND_ONLY) != 0;
4980 }
4981
Griff Hazen5f2edfc2014-09-29 16:28:44 -07004982 /**
Griff Hazen9c5be4e2014-11-17 17:47:50 -08004983 * Set a hint that this notification's background should not be clipped if possible,
4984 * and should instead be resized to fully display on the screen, retaining the aspect
4985 * ratio of the image. This can be useful for images like barcodes or qr codes.
Griff Hazen5f2edfc2014-09-29 16:28:44 -07004986 * @param hintAvoidBackgroundClipping {@code true} to avoid clipping if possible.
4987 * @return this object for method chaining
4988 */
4989 public WearableExtender setHintAvoidBackgroundClipping(
4990 boolean hintAvoidBackgroundClipping) {
4991 setFlag(FLAG_HINT_AVOID_BACKGROUND_CLIPPING, hintAvoidBackgroundClipping);
4992 return this;
4993 }
4994
4995 /**
Griff Hazen9c5be4e2014-11-17 17:47:50 -08004996 * Get a hint that this notification's background should not be clipped if possible,
4997 * and should instead be resized to fully display on the screen, retaining the aspect
4998 * ratio of the image. This can be useful for images like barcodes or qr codes.
Griff Hazen5f2edfc2014-09-29 16:28:44 -07004999 * @return {@code true} if it's ok if the background is clipped on the screen, false
5000 * otherwise. The default value is {@code false} if this was never set.
5001 */
5002 public boolean getHintAvoidBackgroundClipping() {
5003 return (mFlags & FLAG_HINT_AVOID_BACKGROUND_CLIPPING) != 0;
5004 }
5005
5006 /**
5007 * Set a hint that the screen should remain on for at least this duration when
5008 * this notification is displayed on the screen.
5009 * @param timeout The requested screen timeout in milliseconds. Can also be either
5010 * {@link #SCREEN_TIMEOUT_SHORT} or {@link #SCREEN_TIMEOUT_LONG}.
5011 * @return this object for method chaining
5012 */
5013 public WearableExtender setHintScreenTimeout(int timeout) {
5014 mHintScreenTimeout = timeout;
5015 return this;
5016 }
5017
5018 /**
5019 * Get the duration, in milliseconds, that the screen should remain on for
5020 * when this notification is displayed.
5021 * @return the duration in milliseconds if > 0, or either one of the sentinel values
5022 * {@link #SCREEN_TIMEOUT_SHORT} or {@link #SCREEN_TIMEOUT_LONG}.
5023 */
5024 public int getHintScreenTimeout() {
5025 return mHintScreenTimeout;
5026 }
5027
Griff Hazen61a9e862014-05-22 16:05:19 -07005028 private void setFlag(int mask, boolean value) {
5029 if (value) {
5030 mFlags |= mask;
5031 } else {
5032 mFlags &= ~mask;
5033 }
5034 }
5035 }
5036
5037 /**
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08005038 * <p>Helper class to add Android Auto extensions to notifications. To create a notification
5039 * with car extensions:
5040 *
5041 * <ol>
5042 * <li>Create an {@link Notification.Builder}, setting any desired
5043 * properties.
5044 * <li>Create a {@link CarExtender}.
5045 * <li>Set car-specific properties using the {@code add} and {@code set} methods of
5046 * {@link CarExtender}.
5047 * <li>Call {@link Notification.Builder#extend(Notification.Extender)}
5048 * to apply the extensions to a notification.
5049 * </ol>
5050 *
5051 * <pre class="prettyprint">
5052 * Notification notification = new Notification.Builder(context)
5053 * ...
5054 * .extend(new CarExtender()
5055 * .set*(...))
5056 * .build();
5057 * </pre>
5058 *
5059 * <p>Car extensions can be accessed on an existing notification by using the
5060 * {@code CarExtender(Notification)} constructor, and then using the {@code get} methods
5061 * to access values.
5062 */
5063 public static final class CarExtender implements Extender {
5064 private static final String TAG = "CarExtender";
5065
5066 private static final String EXTRA_CAR_EXTENDER = "android.car.EXTENSIONS";
5067 private static final String EXTRA_LARGE_ICON = "large_icon";
5068 private static final String EXTRA_CONVERSATION = "car_conversation";
5069 private static final String EXTRA_COLOR = "app_color";
5070
5071 private Bitmap mLargeIcon;
5072 private UnreadConversation mUnreadConversation;
5073 private int mColor = Notification.COLOR_DEFAULT;
5074
5075 /**
5076 * Create a {@link CarExtender} with default options.
5077 */
5078 public CarExtender() {
5079 }
5080
5081 /**
5082 * Create a {@link CarExtender} from the CarExtender options of an existing Notification.
5083 *
5084 * @param notif The notification from which to copy options.
5085 */
5086 public CarExtender(Notification notif) {
5087 Bundle carBundle = notif.extras == null ?
5088 null : notif.extras.getBundle(EXTRA_CAR_EXTENDER);
5089 if (carBundle != null) {
5090 mLargeIcon = carBundle.getParcelable(EXTRA_LARGE_ICON);
5091 mColor = carBundle.getInt(EXTRA_COLOR, Notification.COLOR_DEFAULT);
5092
5093 Bundle b = carBundle.getBundle(EXTRA_CONVERSATION);
5094 mUnreadConversation = UnreadConversation.getUnreadConversationFromBundle(b);
5095 }
5096 }
5097
5098 /**
5099 * Apply car extensions to a notification that is being built. This is typically called by
5100 * the {@link Notification.Builder#extend(Notification.Extender)}
5101 * method of {@link Notification.Builder}.
5102 */
5103 @Override
5104 public Notification.Builder extend(Notification.Builder builder) {
5105 Bundle carExtensions = new Bundle();
5106
5107 if (mLargeIcon != null) {
5108 carExtensions.putParcelable(EXTRA_LARGE_ICON, mLargeIcon);
5109 }
5110 if (mColor != Notification.COLOR_DEFAULT) {
5111 carExtensions.putInt(EXTRA_COLOR, mColor);
5112 }
5113
5114 if (mUnreadConversation != null) {
5115 Bundle b = mUnreadConversation.getBundleForUnreadConversation();
5116 carExtensions.putBundle(EXTRA_CONVERSATION, b);
5117 }
5118
5119 builder.getExtras().putBundle(EXTRA_CAR_EXTENDER, carExtensions);
5120 return builder;
5121 }
5122
5123 /**
5124 * Sets the accent color to use when Android Auto presents the notification.
5125 *
5126 * Android Auto uses the color set with {@link Notification.Builder#setColor(int)}
5127 * to accent the displayed notification. However, not all colors are acceptable in an
5128 * automotive setting. This method can be used to override the color provided in the
5129 * notification in such a situation.
5130 */
Tor Norbye80756e32015-03-02 09:39:27 -08005131 public CarExtender setColor(@ColorInt int color) {
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08005132 mColor = color;
5133 return this;
5134 }
5135
5136 /**
5137 * Gets the accent color.
5138 *
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005139 * @see #setColor
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08005140 */
Tor Norbye80756e32015-03-02 09:39:27 -08005141 @ColorInt
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08005142 public int getColor() {
5143 return mColor;
5144 }
5145
5146 /**
5147 * Sets the large icon of the car notification.
5148 *
5149 * If no large icon is set in the extender, Android Auto will display the icon
5150 * specified by {@link Notification.Builder#setLargeIcon(android.graphics.Bitmap)}
5151 *
5152 * @param largeIcon The large icon to use in the car notification.
5153 * @return This object for method chaining.
5154 */
5155 public CarExtender setLargeIcon(Bitmap largeIcon) {
5156 mLargeIcon = largeIcon;
5157 return this;
5158 }
5159
5160 /**
5161 * Gets the large icon used in this car notification, or null if no icon has been set.
5162 *
5163 * @return The large icon for the car notification.
5164 * @see CarExtender#setLargeIcon
5165 */
5166 public Bitmap getLargeIcon() {
5167 return mLargeIcon;
5168 }
5169
5170 /**
5171 * Sets the unread conversation in a message notification.
5172 *
5173 * @param unreadConversation The unread part of the conversation this notification conveys.
5174 * @return This object for method chaining.
5175 */
5176 public CarExtender setUnreadConversation(UnreadConversation unreadConversation) {
5177 mUnreadConversation = unreadConversation;
5178 return this;
5179 }
5180
5181 /**
5182 * Returns the unread conversation conveyed by this notification.
5183 * @see #setUnreadConversation(UnreadConversation)
5184 */
5185 public UnreadConversation getUnreadConversation() {
5186 return mUnreadConversation;
5187 }
5188
5189 /**
5190 * A class which holds the unread messages from a conversation.
5191 */
5192 public static class UnreadConversation {
5193 private static final String KEY_AUTHOR = "author";
5194 private static final String KEY_TEXT = "text";
5195 private static final String KEY_MESSAGES = "messages";
5196 private static final String KEY_REMOTE_INPUT = "remote_input";
5197 private static final String KEY_ON_REPLY = "on_reply";
5198 private static final String KEY_ON_READ = "on_read";
5199 private static final String KEY_PARTICIPANTS = "participants";
5200 private static final String KEY_TIMESTAMP = "timestamp";
5201
5202 private final String[] mMessages;
5203 private final RemoteInput mRemoteInput;
5204 private final PendingIntent mReplyPendingIntent;
5205 private final PendingIntent mReadPendingIntent;
5206 private final String[] mParticipants;
5207 private final long mLatestTimestamp;
5208
5209 UnreadConversation(String[] messages, RemoteInput remoteInput,
5210 PendingIntent replyPendingIntent, PendingIntent readPendingIntent,
5211 String[] participants, long latestTimestamp) {
5212 mMessages = messages;
5213 mRemoteInput = remoteInput;
5214 mReadPendingIntent = readPendingIntent;
5215 mReplyPendingIntent = replyPendingIntent;
5216 mParticipants = participants;
5217 mLatestTimestamp = latestTimestamp;
5218 }
5219
5220 /**
5221 * Gets the list of messages conveyed by this notification.
5222 */
5223 public String[] getMessages() {
5224 return mMessages;
5225 }
5226
5227 /**
5228 * Gets the remote input that will be used to convey the response to a message list, or
5229 * null if no such remote input exists.
5230 */
5231 public RemoteInput getRemoteInput() {
5232 return mRemoteInput;
5233 }
5234
5235 /**
5236 * Gets the pending intent that will be triggered when the user replies to this
5237 * notification.
5238 */
5239 public PendingIntent getReplyPendingIntent() {
5240 return mReplyPendingIntent;
5241 }
5242
5243 /**
5244 * Gets the pending intent that Android Auto will send after it reads aloud all messages
5245 * in this object's message list.
5246 */
5247 public PendingIntent getReadPendingIntent() {
5248 return mReadPendingIntent;
5249 }
5250
5251 /**
5252 * Gets the participants in the conversation.
5253 */
5254 public String[] getParticipants() {
5255 return mParticipants;
5256 }
5257
5258 /**
5259 * Gets the firs participant in the conversation.
5260 */
5261 public String getParticipant() {
5262 return mParticipants.length > 0 ? mParticipants[0] : null;
5263 }
5264
5265 /**
5266 * Gets the timestamp of the conversation.
5267 */
5268 public long getLatestTimestamp() {
5269 return mLatestTimestamp;
5270 }
5271
5272 Bundle getBundleForUnreadConversation() {
5273 Bundle b = new Bundle();
5274 String author = null;
5275 if (mParticipants != null && mParticipants.length > 1) {
5276 author = mParticipants[0];
5277 }
5278 Parcelable[] messages = new Parcelable[mMessages.length];
5279 for (int i = 0; i < messages.length; i++) {
5280 Bundle m = new Bundle();
5281 m.putString(KEY_TEXT, mMessages[i]);
5282 m.putString(KEY_AUTHOR, author);
5283 messages[i] = m;
5284 }
5285 b.putParcelableArray(KEY_MESSAGES, messages);
5286 if (mRemoteInput != null) {
5287 b.putParcelable(KEY_REMOTE_INPUT, mRemoteInput);
5288 }
5289 b.putParcelable(KEY_ON_REPLY, mReplyPendingIntent);
5290 b.putParcelable(KEY_ON_READ, mReadPendingIntent);
5291 b.putStringArray(KEY_PARTICIPANTS, mParticipants);
5292 b.putLong(KEY_TIMESTAMP, mLatestTimestamp);
5293 return b;
5294 }
5295
5296 static UnreadConversation getUnreadConversationFromBundle(Bundle b) {
5297 if (b == null) {
5298 return null;
5299 }
5300 Parcelable[] parcelableMessages = b.getParcelableArray(KEY_MESSAGES);
5301 String[] messages = null;
5302 if (parcelableMessages != null) {
5303 String[] tmp = new String[parcelableMessages.length];
5304 boolean success = true;
5305 for (int i = 0; i < tmp.length; i++) {
5306 if (!(parcelableMessages[i] instanceof Bundle)) {
5307 success = false;
5308 break;
5309 }
5310 tmp[i] = ((Bundle) parcelableMessages[i]).getString(KEY_TEXT);
5311 if (tmp[i] == null) {
5312 success = false;
5313 break;
5314 }
5315 }
5316 if (success) {
5317 messages = tmp;
5318 } else {
5319 return null;
5320 }
5321 }
5322
5323 PendingIntent onRead = b.getParcelable(KEY_ON_READ);
5324 PendingIntent onReply = b.getParcelable(KEY_ON_REPLY);
5325
5326 RemoteInput remoteInput = b.getParcelable(KEY_REMOTE_INPUT);
5327
5328 String[] participants = b.getStringArray(KEY_PARTICIPANTS);
5329 if (participants == null || participants.length != 1) {
5330 return null;
5331 }
5332
5333 return new UnreadConversation(messages,
5334 remoteInput,
5335 onReply,
5336 onRead,
5337 participants, b.getLong(KEY_TIMESTAMP));
5338 }
5339 };
5340
5341 /**
5342 * Builder class for {@link CarExtender.UnreadConversation} objects.
5343 */
5344 public static class Builder {
5345 private final List<String> mMessages = new ArrayList<String>();
5346 private final String mParticipant;
5347 private RemoteInput mRemoteInput;
5348 private PendingIntent mReadPendingIntent;
5349 private PendingIntent mReplyPendingIntent;
5350 private long mLatestTimestamp;
5351
5352 /**
5353 * Constructs a new builder for {@link CarExtender.UnreadConversation}.
5354 *
5355 * @param name The name of the other participant in the conversation.
5356 */
5357 public Builder(String name) {
5358 mParticipant = name;
5359 }
5360
5361 /**
5362 * Appends a new unread message to the list of messages for this conversation.
5363 *
5364 * The messages should be added from oldest to newest.
5365 *
5366 * @param message The text of the new unread message.
5367 * @return This object for method chaining.
5368 */
5369 public Builder addMessage(String message) {
5370 mMessages.add(message);
5371 return this;
5372 }
5373
5374 /**
5375 * Sets the pending intent and remote input which will convey the reply to this
5376 * notification.
5377 *
5378 * @param pendingIntent The pending intent which will be triggered on a reply.
5379 * @param remoteInput The remote input parcelable which will carry the reply.
5380 * @return This object for method chaining.
5381 *
5382 * @see CarExtender.UnreadConversation#getRemoteInput
5383 * @see CarExtender.UnreadConversation#getReplyPendingIntent
5384 */
5385 public Builder setReplyAction(
5386 PendingIntent pendingIntent, RemoteInput remoteInput) {
5387 mRemoteInput = remoteInput;
5388 mReplyPendingIntent = pendingIntent;
5389
5390 return this;
5391 }
5392
5393 /**
5394 * Sets the pending intent that will be sent once the messages in this notification
5395 * are read.
5396 *
5397 * @param pendingIntent The pending intent to use.
5398 * @return This object for method chaining.
5399 */
5400 public Builder setReadPendingIntent(PendingIntent pendingIntent) {
5401 mReadPendingIntent = pendingIntent;
5402 return this;
5403 }
5404
5405 /**
5406 * Sets the timestamp of the most recent message in an unread conversation.
5407 *
5408 * If a messaging notification has been posted by your application and has not
5409 * yet been cancelled, posting a later notification with the same id and tag
5410 * but without a newer timestamp may result in Android Auto not displaying a
5411 * heads up notification for the later notification.
5412 *
5413 * @param timestamp The timestamp of the most recent message in the conversation.
5414 * @return This object for method chaining.
5415 */
5416 public Builder setLatestTimestamp(long timestamp) {
5417 mLatestTimestamp = timestamp;
5418 return this;
5419 }
5420
5421 /**
5422 * Builds a new unread conversation object.
5423 *
5424 * @return The new unread conversation object.
5425 */
5426 public UnreadConversation build() {
5427 String[] messages = mMessages.toArray(new String[mMessages.size()]);
5428 String[] participants = { mParticipant };
5429 return new UnreadConversation(messages, mRemoteInput, mReplyPendingIntent,
5430 mReadPendingIntent, participants, mLatestTimestamp);
5431 }
5432 }
5433 }
5434
5435 /**
Griff Hazen61a9e862014-05-22 16:05:19 -07005436 * Get an array of Notification objects from a parcelable array bundle field.
5437 * Update the bundle to have a typed array so fetches in the future don't need
5438 * to do an array copy.
5439 */
5440 private static Notification[] getNotificationArrayFromBundle(Bundle bundle, String key) {
5441 Parcelable[] array = bundle.getParcelableArray(key);
5442 if (array instanceof Notification[] || array == null) {
5443 return (Notification[]) array;
5444 }
5445 Notification[] typedArray = Arrays.copyOf(array, array.length,
5446 Notification[].class);
5447 bundle.putParcelableArray(key, typedArray);
5448 return typedArray;
5449 }
Christoph Studer4600f9b2014-07-22 22:44:43 +02005450
5451 private static class BuilderRemoteViews extends RemoteViews {
5452 public BuilderRemoteViews(Parcel parcel) {
5453 super(parcel);
5454 }
5455
Kenny Guy77320062014-08-27 21:37:15 +01005456 public BuilderRemoteViews(ApplicationInfo appInfo, int layoutId) {
5457 super(appInfo, layoutId);
Christoph Studer4600f9b2014-07-22 22:44:43 +02005458 }
5459
5460 @Override
5461 public BuilderRemoteViews clone() {
5462 Parcel p = Parcel.obtain();
5463 writeToParcel(p, 0);
5464 p.setDataPosition(0);
5465 BuilderRemoteViews brv = new BuilderRemoteViews(p);
5466 p.recycle();
5467 return brv;
5468 }
5469 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005470}